Rails error: "comparison of User with User failed" - ruby-on-rails

I'm getting the following error in my rails app:
comparison of User with User failed
The relevant section of my controller looks like this:
class AssessmentsController < ApplicationController
before_filter :authenticate_user!
respond_to :html, :xml, :js, :pdf
def index
#user = current_user
#account = Account.find(#user.account_id)
#assessments = Assessment.all
respond_with #assessments
end
The relevant section of my view looks like this:
<%= form_for(#account) do |a| %>
<%= a.fields_for :users, #account.users.build do |u| %>
....
<%= a.submit "Sign-up", :class => "button", :disable_with => "Saving..." %>
<% end %>
<h1>Current users</h1>
<% for #user in #account.users.sort! { |b,a| a.id <=> b.id } %>
<%= render :partial => 'user' %>
<% end %>
The error seems to be originating around the for #user in #account.users.sort! section according to the error model, but removing it seems to be the addition of the #account.users.build in the fields_for section that creates it (but I need this as I want the user to be able to create a new user for that account. Can someone enlighten me to what is generating this?

The error is indeed occurring on that line, because ActiveRecord models don't implement comparables by default. So when you say #account.users.sort!, the sort bombs out since it has no way to compare users with users.
There's two things you can do here:
Implement the comparison operator for your user model. Check out this link for a blog post on how to do it, but it'd be something like:
class User < ActiveRecord::Base
def <=>(other)
self.name <=> other.name
end
end
Tell the sort directly what comparison to use, like this:
#account.users.sort! {|a, b| a.name <=> b.name}

Related

Dynamically inserting multiple nested form_fields in Rails using UJS

I've been following a pretty standard UJS model for a lot of my AJAX and it works pretty well - however there's one situation where I'm not sure exactly what the best practice is. I have a model with a has_many relationship say:
class User < ActiveRecord::Base
has_many :aliases
accepts_nested_attributes_for :aliases, :allow_destroy => true
end
class Alias < ActiveRecord::Base
belongs_to :User
end
Now, in my NEW view for a User object, I'd like the user to be able to click a button and add as many aliases as they like - each time they press the button it fires off a remote request to a controller action that looks like this:
def new_ajax
#alias = Alias.new({})
respond_to do |format|
format.js
end
end
This replies with my new_ajax.js.erb template which looks like this:
$('#aliases-container').append('<%= j(render 'ajax_new')%>');
Which in turn renders a template which looks like this:
<%=simple_fields_for #alias do |f|%>
<%= f.input :name %>
<% end %>
This all works and I get my Alias form field rendered - but the problem is the ID and the NAME attribute are not set correctly in the nested fields. The ID and the Alias should be numbered in sequence to provide for proper association building in the create method and for error handling. Does anyone have a solution to this problem?
The problem you have is the base problem with adding new nested fields.
The simple answer is to use child_index: Time.now.to_i for your fields_for; however, you've got to invoke it within a form_for object so that Rails creates the names etc correctly. Sounds complicated but it's quite simple when you get your head around it:
#app/controllers/users__controller.rb
class UsersController < ApplicationController
respond_to :js, :html, only: :new
def new
#user = User.new
#user.aliases.build if request.xhr?
respond_with #user
end
end
#app/views/users/new.html.erb
<%= render "form", locals: {user: #user} %>
#app/views/users/_form.html.erb
<%= form_for user do |f| %>
<%= f.fields_for :aliases, child_index: Time.now.to_i do |a| %>
<%= a.text_field :name %>
<% end %>
<% end %>
#app/views/users/new.js.erb
var data = $("<%=j render "users/ajax_new", locals: {user: #user} %>").html();
$('#aliases-container').append(data);
You can see a more rudimentary implementation I wrote back in 2013:
Rails accepts_nested_attributes_for with f.fields_for and AJAX

Undefined method 'to_key' error using form_for

I am getting the following error when trying to use form_for in my Rails application:
undefined method `to_key' for #<Table::ActiveRecord_Relation:0x8a09ca8>
My config/routes.rb is:
root 'welcome#index'
post 'foo', as: 'foo', to: 'welcome#index'
The controller is:
class WelcomeController < ApplicationController
def index
#tables = Table.all
end
def test
#tables = Table.all
end
end
And the welcome/index.html.erb view is:
<p>
<%= form_for #tables, :url => foo_path do |t| %>
<%= t.text_area :name %>
<% end %>
</p>
I've tried to do the url workaround that had been suggested in the documentation, but I'm still getting the same error.
Does anyone know what I am doing wrong? I would like to understand this bug a bit more so I can better deal with it.
As per your code, index is returning a collection. However your view tries to define a form for it. This is unlikely going to be succeed.
Form is for an object, not for collections.
Perhaps you can do something like
def new
#table = Table.new
end
and in new.html.erb
<%= form_for #table do |f| %>
...
<% end %>
And if you would like to stick with index.html.erb with a form. Then you have to edit your routes for index action and also in controller it should be for creating a new object.
def index
#table = Table.new
end
Hope it helps!
I see your code have 3 not true things
As RESFUL standard then:
index action always go through with get action so in route file you should define again same that:
root "wellcome#index"
get "foo", to: "wellcome#index", as: :foo
form_for usually use with model object but not collect as you use #tables, if model object not save into database form_for using to create 1 object to database, otherwise form_for using update that object
if you want create form at index action you can follow me:
def index
#tables = Table.all
#table = Table.new
end
index.html.erb file
<%= form_for #table do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
you need create tables_controller to process request from form send to server. you run: rails g controller tables
In table_controller.rb you write same as:
def create
#table = Table.new table_params
if #table.save
redirect_to root_path, notice: "success"
else
redirect_to root_path, alert: "fail"
end
end
private
def table_params
params.require(:table).permit :name
end
so that. end. Have nice day!

Rails - updating div w/ Ajax and :remote => true

I've followed this Railscast on submitting a form via Ajax and updating a div without reloading the page, but I'm having trouble with one portion of it.
Ryan has $("#products").html("<%= escape_javascript(render(#products)) %>"); in an index.js.erb file to update the #products div when the form is submitted. I'm struggling to understand whether an instance variable like #products is relevant for my situation, or if I can simply substitute it for a URL.
I'm trying to do the same thing as Ryan in this screencast, but instead of display search results I just want to display the updated value.
In show.html.erb I have:
<% #project.project_todos.order("created_at DESC").where(:status => false).each do |todo|%>
<%= form_for todo, :remote => true, :"data-replace" => "#dueon" do |f| %>
<%= f.text_field :due %>
<%= f.submit :class => "primary", :value => "Add" %>
<% end %>
<div id="dueon">
<%= render "duedate", :todo => todo %>
</div>
<% end %>
The partial _duedate.html.erb has one line in it: <%= todo.due %>
So in my index.js.erb I currently have this: $("#dueon").html("<%= escape_javascript(render("_duedate")) %>"); but it's clearly not working. Do I have to use a variable here in place of the _duedate? And if so, how would I set this up in the controller? I mean what does the variable have represent?
Also, for what it's worth, the partial is rendering correctly and displaying the todo.due value...it's just not updating when I submit the form.
ProjectsController:
def show
#project = Project.find(params[:id])
# Display the form to create a new todo
#project_todo = ProjectTodo.new
respond_to do |format|
format.html # show.html.erb
format.json { render :json => #project }
end
end
Try this
in you controller action , (say sample_action)
def sample_action
#todos = #your code
respond_to do |format|
format.js
end
end
and you have a sample_action.js.erb
$("#dueon").html("<%= raw escape_javascript(render(:partial => 'duedate')) %>")
then inside the partial, you have access to the new #todos instance variable
HTH
I will answer you separately as I believe your entire setup should be little change (IMO, this might not be the case)
I think you should have a todos controller with a project belongs to it,
--- models ----------------
class Project < ActiveRecord::Base
has_many :todos
end
class Todo < ActiveRecord::Base
belongs_to :project
end
---- routes ---------------
resources :projects do
resources :todos do
end
end
---- controllers ----------
class ProjectsController < ApplicationController
end
class TodosController < ApplicationController
def new
#project = Project.find(params[:project_id])
#todos = #project.todos.build
end
end
in your view (views/todos.html.erb)
<%= #project.name %>
<%= form_for([#Project, #todos]) do |f| %>
#todo form code
<% end%>
As per the relation, project has many todos, its always clear to show the project details in the todo add screen, rather than allowing users to add new todos from project screen.
and again, this is my personal view, feel free to ask any questions :)

Is there a way to do this in Rails without mass assignment?

Members create votes that both belong to them and to another model, Issues. Currently I'm doing this with a hidden form and passing the appropriate parameters. Here's the code on the issues index view:
<%= form_for(#vote) do |f| %>
<%= f.hidden_field "issue_id", :value => issue.id %>
<%= f.hidden_field "member_id", :value => session[:member_id] %>
<%= f.hidden_field "type", :value => :Upvote %>
<%= f.label issue.upvotes_count(issue.id) %>
<%= submit_tag "Up", :class => 'up-vote' %>
<% end %>
This doesn't seem ideal as it leaves issue_id and member_id open to mass assignment. Is there a better way to do this with a button_to tag or something?
Here's the controller code:
class VotesController < ApplicationController
#GET
def new
#vote = Vote.new
end
# POST
def create
#vote = Vote.new(params[:vote])
#vote.member_id = current_member
if #vote.save
redirect_to issues_path
else
redirect_to issues_path, notice: "you must be logged in to vote"
end
end
end
and
class IssuesController < ApplicationController
# GET
def index
#issues = Issue.find(:all)
#vote = Vote.new
end
# GET
def show
#issue = Issue.find(params[:id])
respond_to do |format|
format.html
format.js
end
end
end
Use scope in the controller:
#issue = Issue.find(params[:issue_id])
#vote = #issue.votes.new(params[:vote])
#vote.save
and do not pass member_id and issue_id to hidden fields.
If you have proper nested RESTful routes you should be able to get params[:issue_id] directly.
If issue and member_id are available before you vote.save! in the controller, you can set them manually there.
Normally you get values like member_id from current_user in the controller rather than passing it via form parameters. How you have it currently does expose you to mass-assignment.
Do members have to login before voting? If so, then you don't need to include member_id as a hidden field because you can grab current_user in the controller and this will provide good protection since there wouldn't be any advantage for a member to hack issue_id or type.

How can I only display an add review form if a user hasn't already submited a review?

Hi I'm a super beginner and working on my first app, I have a table of venues and users can add reviews to the venues. I would like to be able to hide the review form to users once they have submitted a review, to stop them from submitting more.
This is what I have now:
add review form on venue show page
<% if reviewed? %>
<%= form_for [#venue, #review], :class => 'rating_ballot' do |f| %>
<%= f.label("value_1", content_tag(:span, '1'), {:class=>"rating", :id=>"1"}) %>
<%= radio_button_tag("review[rating]", 1, :class => 'rating_button') %>
<%= f.label("value_2", content_tag(:span, '2'), {:class=>"rating", :id=>"2"}) %>
<%= radio_button_tag("review[rating]", 2, :class => 'rating_button') %>
<%= f.label("value_3", content_tag(:span, '3'), {:class=>"rating", :id=>"3"}) %>
<%= radio_button_tag("review[rating]", 3, :class => 'rating_button') %>
<%= f.label("value_4", content_tag(:span, '4'), {:class=>"rating", :id=>"4"}) %>
<%= radio_button_tag("review[rating]", 4, :class => 'rating_button') %>
<%= f.label("value_5", content_tag(:span, '5'), {:class=>"rating", :id=>"5"}) %>
<%= radio_button_tag("review[rating]", 5, :class => 'rating_button') %> <br>
<p>title: <br>
<%= f.text_field :title %></p><br>
<%= submit_tag %>
<% end %>
<% end %>
application controller
class ApplicationController < ActionController::Base
helper :all # include all helpers, all the time
protect_from_forgery # See ActionController::RequestForgeryProtection for details
helper_method :reviewed?
protected
def reviewed?
true
end
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end
I cant figure out what the reviewed? helper should be to allow me to do this, any help is greatly appreciated!
edit
I've added the has_reviewed helper to the application controller, it now shows this error:
Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
Extracted source (around line #79):
76: <%= render :partial => 'reviews/review', :collection => #venue.reviews %>
77: </div>
78:
79: <% if reviewed? %>
application controller
class ApplicationController < ActionController::Base
helper :all # include all helpers, all the time
protect_from_forgery # See ActionController::RequestForgeryProtection for details
helper_method :current_user
helper_method :has_reviewed
helper_method :reviewed?
protected
def reviewed?
Review.has_reviewed(#current_user.id, venue.id)
end
def has_reviewed
!Review.where(:user_id=>user,:venue_id=>venue).blank?
end
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end
another edit
I've changed the reviewed? helper method to:
def reviewed?
if current_user
Review.has_reviewed(#current_user.id, #venue.id)
else
nil
end
end
but it gives undefined method `has_reviewed' for # error
schema
A venue has many reviews
A user has many reviews
A review belongs to a user and a venue
the routes looks like this:
App::Application.routes.draw do
resources :sessions
resources :users
resources :venues do
resources :reviews
end
end
Presuming you make the change as suggested by Wes, the reviewed? method would look in the database to see if this user has made a review.
The reviews table will need to have a column for the user that made the review and the venue it reviewed.
So the code would look something like this...
EDITED to reflect schema recently added
In the controller
def reviewed?
if current_user
#current_user.has_reviewed(#venue.id)
else
nil
end
end
In the User model...
class User < ...
has_many :reviews
def has_reviewed(venueid)
reviews.exists?(:venue_id => venueid)
end
...
end
Basically I think the has_reviewed is better off in the User model as the user has_many reviews, and then it can check if the user has reviewed the given venue.
I am presuming that the Model Review has a foreign key to venue called venue_id, as a Review belongs_to a Venue and that would be the standard thing.
<% unless reviewed? %>
[...]
<% end %>

Resources