Rails first_or_create! creating null values in database table - mysql

I'm using first_or_create to populate a table with a list of email subscribers (called members). The code is as follows:
def community_members=(members)
self.members = members.split(",").map do |member|
Member.where(email: member.strip, community_id: self.id).first_or_create! unless member.strip == nil
end
end
Everything works fine, except that when I add additional emails to the same community, the table turns the "community_id" column for all previous rows to NULL.
Here's the server log:
Member Load (0.2ms) SELECT "members".* FROM "members" WHERE "members"."email" = $1 AND "members"."community_id" = $2 ORDER BY "members"."id" ASC LIMIT 1 [["email", "lisa#holy.com"], ["community_id", 1]]
SQL (0.3ms) INSERT INTO "members" ("email", "community_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["email", "lisa#holy.com"], ["community_id", 1], ["created_at", "2015-04-30 16:14:25.930012"], ["updated_at", "2015-04-30 16:14:25.930012"]]
Member Load (0.2ms) SELECT "members".* FROM "members" WHERE "members"."community_id" = $1 [["community_id", 1]]
SQL (0.4ms) UPDATE "members" SET "community_id" = NULL WHERE "members"."community_id" = $1 AND "members"."id" = 30 [["community_id", 1]]
(0.3ms) COMMIT
The first "Member" load does exactly what it's supposed to do. But for some reason it always ends with the second Member load that goes in and sets all "community_id" fields to NULL.
Right now I call :community_member from a form on a community page:
<%= form_for(#community) do |f| %>
<%= f.text_area :community_members, class:"form-control input-lg", placeholder:"Please add your list of comma separated member email addresses here" %>
<%= f.submit "Save", class: "btn btn-lg btn-green btn-block pad-top" %>
<% end %>
Seems like I'm missing something obvious here. Any ideas? Thank you.

You're going to want to find by the unique attribute, email, and create by community name, I think.
If that's the case, you'll have to do something like:
Member.where(email: member.strip).first_or_create(community: self) unless...
If you have records with non-unique emails, you'll have to redesign your associations.
class Subscriber
#this should have the email attribute
has_many :community_subscribers
has_many :communities, through: :community_subscribers
end
class CommunitySubscriber
#this is a 'bridge' table
belongs_to :subscriber
belongs_to :community
end
class Community
has_many :community_subscribers
has_may :subscribers, through: :community_subscribers
#I suggest new method and arg names
#Using self will keep the query within the scope of the community you are working on
#This also allows for creation of Subscriber records if you insist placing that here
#are you sure you want to map instead of just iterating the split list of emails?
def new_subscribers(emails)
emails.split(",").map do |email|
clean_email = email.strip
subscriber = Subscriber.where(email: clean_email).find_or_create unless clean_email.blank?
self.community_subscribers.where(subscriber: subscriber).first_or_create unless subscriber.blank?
end
end
end
Docs:
http://apidock.com/rails/v3.2.1/ActiveRecord/Relation/first_or_create
http://guides.rubyonrails.org/v3.2.13/active_record_querying.html#first_or_create

Related

Include parent attributes when searching via query

I have a User Model that has_many Job Applications.
Everything works great, but when I try to search Job Applications by the User's first_name I get the error below.
Error
SQLite3::SQLException: no such column: first_name: SELECT "job_applications"
From my understanding, I need to include the User attributes in the Job Application query.
How can I accomplish this?
View (job application)
<%= form_for :search, :html => {:method => :get, :id => 'search'} do |f| %>
<%= text_field_tag :terms, params[:terms], :type => 'search' %>
<% end %>
Controller (job application)
def index
#job = Job.find(params[:job_id])
#job_applications = #job.job_applications.search(params[:terms])
end
Model (job application)
def self.search(terms)
terms ||= ""
conditions = terms.split(" ").map do |term|
term = term.strip.gsub("'","''")
### I am having an issue here...
### how do i include the user attributes in this query
"first_name like '%#{term}%'"
end
where(conditions.join " OR ")
end
You have to join job_applications table with users table.
# job_applications.rb
def self.search(terms)
terms ||= ""
conditions = terms.split(" ").map do |term|
term = term.strip.gsub("'","''")
"users.first_name like :term"
end
joins(:user).where(conditions.join " OR ")
end
Avoid passing raw user's inputs into your queries directly to avoid sql injection. Use rails' built-in filters or sanitize it yourself.
def self.search(terms)
terms ||= ""
term_args = []
conditions = terms.split(" ").map do |term|
term = term.strip.gsub("'","''")
term_args << "%#{term}%"
"users.first_name like ?"
end
joins(:user).where(conditions.join(' OR '), term_args)
end

Rails cannot update column value

I have created Rails(version 3.2) application with mysql database. I have table Message with column content. I can update the following data using rails console but I can't using run same code from seeds.rb.
data: "Join **** Audio / Video Meeting. This is an online meeting by ****, the community marketplace to find products and services in your neighbourhood. http://*****.dev:3000/conferences/80"
I have following code in my seeds.rb file
all_messages = Message.all
all_messages.each do |message|
message_content = message.content
if message_content.present? && message_content[/\/(.*)\/(.*)\/conferences/,2].present?
message_content.slice! (message_content[/\/(.*)\/(.*)\/conferences/,2]+'/')
if message.update_attributes!(content: message_content)
puts message.content
else
puts "nothing"
end
end
end
It does't show any errors, but data is not updated in db and not shown any errors.
This is my model file
class Message < ActiveRecord::Base
attr_accessible :content
after_save :update_conversation_read_status
belongs_to :sender, :class_name => "Person"
belongs_to :conversation
has_one :request
validates_presence_of :sender_id
validates_presence_of :content
def update_conversation_read_status
conversation.update_attribute(:last_message_at, created_at)
conversation.participations.each do |p|
last_at = p.person.eql?(sender) ? :last_sent_at : :last_received_at
p.update_attributes({ :is_read => p.person.eql?(sender), last_at => created_at})
end
end
end
Note: content is text type in db.

undefined method `user_revisers_path' "NoMethodError in Revisers#new "

I got this weird error I'm assuming this comes from the routes.rb part of my app. Im trying to give the chance for a user to become a reviser when they enter a form. a user can only become a reviser once so its a has_one reviser on user.rb model Thanks!
routes.rb:
Rails.application.routes.draw do
root 'pages#home'
devise_for :users ,
:path => '' ,
:path_names => { :sign_in => 'login', :sign_out => 'logout', :edit => 'profile' },
:controllers => { :omniauth_callbacks => 'omniauth_callbacks',
:registrations => 'registrations'
}
resources :users, only: [:index, :show] do
resource :reviser
end
revisers_controller:
class RevisersController < ApplicationController
before_action :set_reviser, only: [:show, :edit, :update]
before_action :authenticate_user!, except: [:show]
def index
#reviser = current_user.reviser
end
def show
end
def new
#reviser = current_user.build_reviser(params[:reviser])
#user = User.find(params[:user_id])
end
def create
#reviser = current_user.reviser.build(reviser_params)
if #reviser.save
redirect_to #reviser,notice: "saved...."
else
render :new
end
end
def edit
set_reviser
end
def update
set_reviser
if #reviser.update(reviser_params)
redirect_to #reviser, notice: "updated.."
else
render :edit
end
end
private
def set_reviser
#reviser = Reviser.find(params[:id])
end
def reviser_params
params.require(:reviser).permit(:description, :average_start, :average_end, :max_pages, :price_per, :active)
end
end
new.html
<%= form_for [current_user, #reviser] do |f| %>
<div class="row">
<div class="div.col-md-4 select">
<div class="form-group">
<label>dsd</label>
<%= f.input :description, label: false, class: 'controls',:input_html => { :id => 'description' } %>
</div>
</div>
</div>
<%= f.submit "Become Adviser", class: "btn btn-large btn-primary" %>
<% end %>
Error log:
Rendered revisers/_form.html.erb (14.7ms)
Rendered revisers/new.html.erb within layouts/application (15.9ms)
Completed 500 Internal Server Error in 48ms (ActiveRecord: 1.1ms)
ActionView::Template::Error (undefined method `user_revisers_path' for #<#<Class:0x007faf35f0cc60>:0x007faf35ee7e60>):
5: <div class="panel-body">
6: <div class="container">
7:
8: <%= form_for [current_user, #reviser] do |f| %>
9:
10: <div class="row">
11: <div class="div.col-md-4 select">
app/views/revisers/_form.html.erb:8:in `_app_views_revisers__form_html_erb___2471522092853631188_70195335070980'
app/views/revisers/new.html.erb:1:in `_app_views_revisers_new_html_erb___1451348464463745171_70195280947160'
Rake routes:
Prefix Verb URI Pattern Controller#Action
root GET / pages#home
new_user_session GET /login(.:format) devise/sessions#new
user_session POST /login(.:format) devise/sessions#create
destroy_user_session DELETE /logout(.:format) devise/sessions#destroy
user_omniauth_authorize GET|POST /auth/:provider(.:format) omniauth_callbacks#passthru {:provider=>/facebook/}
user_omniauth_callback GET|POST /auth/:action/callback(.:format) omniauth_callbacks#(?-mix:facebook)
user_password POST /password(.:format) devise/passwords#create
new_user_password GET /password/new(.:format) devise/passwords#new
edit_user_password GET /password/edit(.:format) devise/passwords#edit
PATCH /password(.:format) devise/passwords#update
PUT /password(.:format) devise/passwords#update
cancel_user_registration GET /cancel(.:format) registrations#cancel
user_registration POST / registrations#create
new_user_registration GET /sign_up(.:format) registrations#new
edit_user_registration GET /profile(.:format) registrations#edit
PATCH / registrations#update
PUT / registrations#update
DELETE / registrations#destroy
user_confirmation POST /confirmation(.:format) devise/confirmations#create
new_user_confirmation GET /confirmation/new(.:format) devise/confirmations#new
GET /confirmation(.:format) devise/confirmations#show
user_reviser POST /users/:user_id/reviser(.:format) revisers#create
new_user_reviser GET /users/:user_id/reviser/new(.:format) revisers#new
edit_user_reviser GET /users/:user_id/reviser/edit(.:format) revisers#edit
GET /users/:user_id/reviser(.:format) revisers#show
PATCH /users/:user_id/reviser(.:format) revisers#update
PUT /users/:user_id/reviser(.:format) revisers#update
DELETE /users/:user_id/reviser(.:format) revisers#destroy
users GET /users(.:format) users#index
user GET /users/:id(.:format) users#show
photos GET /photos(.:format) photos#index
POST /photos(.:format) photos#create
new_photo GET /photos/new(.:format) photos#new
edit_photo GET /photos/:id/edit(.:format) photos#edit
photo GET /photos/:id(.:format) photos#show
PATCH /photos/:id(.:format) photos#update
PUT /photos/:id(.:format) photos#update
DELETE /photos/:id(.:format) photos#destroy
pages GET /pages(.:format) pages#index
POST /pages(.:format) pages#create
new_page GET /pages/new(.:format) pages#new
edit_page GET /pages/:id/edit(.:format) pages#edit
page GET /pages/:id(.:format) pages#show
PATCH /pages/:id(.:format) pages#update
PUT /pages/:id(.:format) pages#update
DELETE /pages/:id(.:format) pages#destroy
autocomplete_user_country_suggestions GET /suggestions/autocomplete_user_country(.:format) suggestions#autocomplete_user_country
autocomplete_user_city_suggestions GET /suggestions/autocomplete_user_city(.:format) suggestions#autocomplete_user_city
autocomplete_user_school_suggestions GET /suggestions/autocomplete_user_school(.:format) suggestions#autocomplete_user_school
autocomplete_user_major_suggestions GET /suggestions/autocomplete_user_major(.:format) suggestions#autocomplete_user_major
suggestions GET /suggestions(.:format) suggestions#index
POST /suggestions(.:format) suggestions#create
new_suggestion GET /suggestions/new(.:format) suggestions#new
edit_suggestion GET /suggestions/:id/edit(.:format) suggestions#edit
suggestion GET /suggestions/:id(.:format) suggestions#show
PATCH /suggestions/:id(.:format) suggestions#update
PUT /suggestions/:id(.:format) suggestions#update
DELETE /suggestions/:id(.:format) suggestions#destroy
conversation_messages GET /conversations/:conversation_id/messages(.:format) messages#index
POST /conversations/:conversation_id/messages(.:format) messages#create
conversations GET /conversations(.:format) conversations#index
POST /conversations(.:format) conversations#create
post_comments GET /posts/:post_id/comments(.:format) comments#index
POST /posts/:post_id/comments(.:format) comments#create
new_post_comment GET /posts/:post_id/comments/new(.:format) comments#new
edit_post_comment GET /posts/:post_id/comments/:id/edit(.:format) comments#edit
post_comment GET /posts/:post_id/comments/:id(.:format) comments#show
PATCH /posts/:post_id/comments/:id(.:format) comments#update
PUT /posts/:post_id/comments/:id(.:format) comments#update
DELETE /posts/:post_id/comments/:id(.:format) comments#destroy
post_places GET /posts/:post_id/places(.:format) places#index
POST /posts/:post_id/places(.:format) places#create
new_post_place GET /posts/:post_id/places/new(.:format) places#new
edit_post_place GET /posts/:post_id/places/:id/edit(.:format) places#edit
post_place GET /posts/:post_id/places/:id(.:format) places#show
PATCH /posts/:post_id/places/:id(.:format) places#update
PUT /posts/:post_id/places/:id(.:format) places#update
DELETE /posts/:post_id/places/:id(.:format) places#destroy
posts GET /posts(.:format) posts#index
POST /posts(.:format) posts#create
new_post GET /posts/new(.:format) posts#new
edit_post GET /posts/:id/edit(.:format) posts#edit
post GET /posts/:id(.:format) posts#show
PATCH /posts/:id(.:format) posts#update
PUT /posts/:id(.:format) posts#update
DELETE /posts/:id(.:format) posts#destroy
You have a singular resource definition for your :reviser route. This makes sense for what you're trying to do, however the route generated by form_for [current_user, #reviser] will try to generate a route with both a :user_id, and an :id to identify your reviser.
So, the :id field isn't defined in your case since resource :reviser doesn't create an :id.
The solution is to explicitly set the url in your form_for to the route your want:
It should be something along the lines of:
form_for #reviser, url: edit_user_reviser_path(user_id: current_user.id)
The bigger issue with your code is that you're still directly referencing reviser by id in your controller. Under your current route settings, this will return an error on the Reviser.find() method. However, more importantly this presents a security concern since a malicious user could send an id param in the PATCH request to hijack and update a record that belongs to another user.
A simple solution is to just reference the reviser object directly on current_user.
def set_reviser
#reviser = current_user.reviser
end

Ruby on Rails updating specific column based on another table value

I am working with a Ruby on Rails test site that is basically a carbon copy of our actual site. Upon updating user data, I have noticed in both test and the live environment that the user table has both department_id and department_name in it, rather than just joining on department_id and always pulling the info from the department table. The existing code updates department_id in the users table when someone switches departments, but the department_name is not updated. How this was never realized I'm not sure, because there are a few places that pull department_name from the users table directly where I've manually updated the incorrect department_name fields. What do I need to do in the users controller to get it to update the department_name in the user table based on what is in the department table for that department_id? (I know I could recreate the pages to join on the department_id and pull the name from the dept table instead, but I really don't feel like rewriting a bunch of different pages).
users_controller.rb update method
def update
#user = User.find(params[:id])
email_changed = #user.email != params[:user][:email]
#need to set user's department_name so it is updated in users table
#user.update_without_password(params[:user])
successfully_updated = true
if successfully_updated
flash[:notice] = "Profile was successfully updated"
redirect_to #user
else
render "edit"
end
end
Form Control that allows department change in users/_form.html.erb
<% if current_user.is_admin? %>
<%= f.association :department, label: false, :collection =>
#departments, :prompt => "Select Department" %>
...
Model file
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me,
:username, :first_name, :middle_name, :last_name, :suffix, :department_id,
:department_name
belongs_to :department
...
In your User model:
before_save do
self.department_name = department.name if department_id_changed?
end

Mongoid embedded documents and Rails strong parameters not working

I have a 1-N relationship in Mongoid/Rails:
class Company
include Mongoid::Document
field :name, type: String
embeds_many :people, class_name: 'Person'
end
class Person
include Mongoid::Document
field :first_name, type: String
embedded_in :company, class_name: 'Company', inverse_of: 'people'
end
Now I can successfully create a Company as follows in the console; for example:
> c = Company.new(name: 'GLG', :people => [{first_name: 'Jake'}]) # OK!
> c.people # OK!
Then I have a JSON API controller to update a company, along the lines of:
# PUT /api/companies/:id
def update
if Company.update(company_params)
# ... render JSON
else
# ... render error
end
end
private
def company_params
params.require(:company).permit(:name, :people => [:first_name])
end
Now, when the PUT request comes in from the frontend, the company_params is always missing the :people attribute. Rails log says:
Parameters: {"id"=>"5436fbc64a616b5240050000", "name"=>"GLG", "people"=>[{"first_name"=>"Jake"}], "company"=>{"name"=>"GLG"}}
I don't get an "Unpermitted parameters" warning. I've tried every conceivable way of permitting the people field and it still doesn't get included.
params.require(:company).permit!
Results in the same. What am I doing wrong?
You have to accept nested_attributes on assignment
class Company
include Mongoid::Document
field :name, type: String
embeds_many :people, class_name: 'Person'
accepts_nested_attributes_for :people
end