undefined method `users' for nil:NilClass while deleting association - ruby-on-rails

I have
class Clot < ActiveRecord::Base
has_many :memberships
has_many :users, through: :memberships
end
and same for users
I have follow button with action
def follow
#clot = Clot.find(params[:id])
#clot.users << current_user
redirect_to #clot
end
Its working fine but i want to create unfollow button
i wrote
= button_to 'unollow', unfollow_clot_path(#clot), :method => :delete
with
def unfollow
#clot.users.destroy
end
My routes looks like
resources :clots do
member do
post :follow
delete :unfollow
end
end
but i am getting error. please tell me answer and also tell me right way if i am doing it wrong.
Thanks in advance

When you say this, it will call controller method unfollow, and in params will pass clot id. So you have to find that clot object with that id
= button_to 'unollow', unfollow_clot_path(#clot), :method => :delete
def method_name
#clot = Clot.find(params[:id])
end
But When you will do
#clot.users
It will return array of user for that clot instance. And you can't call destroy method on array object. So first will return you the first found object,So you can write like
#clot.users.first.destory!
Or you can also use destory_all method. To destroy all objects

Related

Action Controller exception undefined method `merge' for xxx:String

I'm doing simple Reddit like site. I'm trying to add button to reporting posts. I create report model, using button_to i try to post data to report controller to create it but i received NoMethodError in ReportsController#create undefined method merge' for "post_id":String
model/report.rb
class Report < ApplicationRecord
belongs_to :reporting_user, class_name: 'Author'
has_one :post
end
report_controller.rb
class ReportsController < ApplicationController
def create
report = Report.new(report_params)
flash[:notice] = if report.save
'Raported'
else
report.errors.full_messages.join('. ')
end
end
def report_params
params.require(:post).merge(reporting_user: current_author.id)
end
end
and button in view
= button_to "Report", reports_path, method: :post, params: {post: post}
What cause that problem?
edit:
params
=> <ActionController::Parameters {"authenticity_token"=>"sX0DQfM0rp97q8i16LGZfXPoSJNx15Hk4mmP35uFVh52bzVa30ei/Bxk/Bm40gnFmd2NvFEqj+Wze8ted66kig==", "post"=>"1", "controller"=>"reports", "action"=>"create"} permitted: false>
To start with you want to use belongs_to and not has_one.
class Report < ApplicationRecord
belongs_to :reporting_user, class_name: 'Author'
belongs_to :post
end
This correctly places the post_id foreign key column on reports. Using has_one places the fk column on posts which won't work.
And a generally superior solution would be to make reports a nested resource:
# /config/routes.rb
resources :posts do
resources :reports, only: [:create]
end
# app/controller/reports_controller.rb
class ReportsController
before_action :set_post
# POST /posts/:post_id/reports
def create
#report = #post.reports.new(reporting_user: current_author)
if #report.save
flash[:notice] = 'Reported'
else
flash[:notice] = report.errors.full_messages.join('. ')
end
redirect_to #post
end
private
def set_post
#post = Post.find(params[:post_id])
end
end
This lets you simplify the button to just:
= button_to "Report", post_reports_path(post), method: :post
Since the post_id is part of the path we don't need to send any additional params.
If you do want to let the user pass additional info through a form in the future a better way to create/update resources with params and session data is by passing a block:
#report = #post.reports.new(report_params) do |r|
r.reporting_user = current_user
end
ActionController::Parameters#require returns the value of the required key in the params. Usually this would be an object passed back from a form. In this example require would return {name: "Francesco", age: 22, role: "admin"} and merge would work.
Your view is sending back parameters that Rails is formatting into {post: 'string'}. We would need to see your view code to determine what exactly needs to change.
Update: From the new code posted we can see that the parameter sent back is "post"=>"1". Normally we would be expecting post: {id: 1, ...}.
Update: The button in the view would need the params key updated to something ala params: {post: {id: post.id}} EDIT: I agree that params: {report: { post_id: post}} is a better format.
The problem seems to be in report_params. When you call params.require(:post), it fetches :post from params -> the result is string. And you are calling merge on this string.
I'd recommend change in view:
= button_to "Report", reports_path, method: :post, params: { report: { post_id: post} }
then in controller:
def report_params
params.require(:report).permit(:post_id).merge(reporting_user_id: current_author.id)
end
Note, that I changed also the naming according to conventions: model_id for id of the model, model or model itself.

Rails 4 Nested Resources Error with 3 models

Error: ActiveRecord::RecordNotFound Couldn't find Option with 'id'=
This is happening when I add a link to the options show.html.erb to get all the registrations for that option. In order to get the event id and the option id, I'm adding the following to the show method in the OptionsController:
#event = Event.find(params[:event_id])
#option = Option.find(params[:option_id])
This is the link I'm adding to the show.html.erb file:
link_to "Registrations", event_option_registrations_path(#option)
This is how my 3 models look: Event, option and registrations
event.rb:
class Event < ActiveRecord::Base
has_many :options, dependent: :destroy
end
option.rb:
class Option < ActiveRecord::Base
belongs_to :event
has_many :registrations
end
routes.rb:
resources :events do
resources :options do
resources :registrations
end
Route for Registrations:
event_option_registrations_path /events/:event_id/options/:option_id/registrations(.:format)
registrations#index
Error: ActiveRecord::RecordNotFound Couldn't find Option with 'id'=
This error message is saying that, it can't find the option with id = nil when you do this:
#option = Option.find(params[:option_id])
which means, your params[:option_id] is nil in this case.
You should put a print statement in your controller like this:
def your_action
# these are for debugging
puts params.inspect
puts params[:option_id]
#event = Event.find(params[:event_id])
#option = Option.find(params[:option_id])
end
Then you will be able to see what you are getting inside your params hash. Then, you can grab the correct attribute and then do the rest of the work. Hope this helps you debug the issue and solve your problem.
Update
change this:
#option = Option.find(params[:option_id])
To:
#option = Option.find(params[:id])
Because, in your params hash, you don't have a option_id key, but you have a id key which refers to the id of the option.

Finding specific database entries when using nested attributes in RoR

I'm using nested attributes in my Ruby on Rails app (4.0.2) such that survey has_many questions and accepts_nested_attributes_for questions and question belongs_to survey.
The problem that I have run into is that when I only want to look at the questions that belong to a particular survey I get an "undefined method "id" for nil:NilClass"-error. When on the other hand I look at all the questions the index action works without problems.
My questions controller index action:
def index
#questions = Question.where(:survey_id => #survey.id).all # if instead I use #questions = Question.all it works fine, but is not what I want.
##questions = #survey.questions.all
#surveys = Survey.all
#survey = Survey.first
end
My surveys/index.html.erb page:
<%= link_to("questions", { :controller => 'questions', :survey_id => survey.id }, :class => 'btn btn-xs') do %>
<%= glyph 'th-list' %>
<%- end -%>
My Question model:
class Question < ActiveRecord::Base
belongs_to :survey
scope :sorted, lambda { order("questions.created_at ASC")}
end
I also use a before_action that I call find_survey which looks like this:
def find_survey
# If in each action calling this method (find_survey) has :survey_id sent
if params[:survey_id]
# We will then go to the database and look for (and find) :survey_id and set that to #survey.
#survey = Survey.find(params[:survey_id])
end
end
Could anybody point me in the right direction?
The undefined method on NilClass is because your instance variable #survey is nil, and when you're calling Question.where(:survey_id => #survey.id).all you're getting that error.
If you're associations are set up right you should be able to run #survey.questions and not have to perform the search on Questions. That's part of ActiveRecord rails magic. You SHOULDN'T have to call #survey.questions.all because that will return all intances of the Question class, leave that off and you should be good to go.
As far as why this isn't working for you now, it may just be an order thing — you're calling #survey before you define it on the line below.
Based on the error you posted, #survey is likely nil in this line:
#questions = Question.where(:survey_id => #survey.id).all
So maybe your "before_action" is not being called?

Rails 4: 'redirect_to current_thing' undefined error

I have a model “Thing,” each of which has_many “Comments,” each of which in turn has_many “Votes.” I want to be able to vote on comments on the Thing show page. This is what I have so far:
Comments Controller:
def votecomment
#comment = Comment.find(params[:id])
Vote.create!(voteable_id: params[:id], voteable_type: 'Comment')
redirect_to current_thing
end
Things View:
<%= link_to “Vote”, vote_comment_path(:id => comment.id), method: :post %>
Routes:
post 'comments/:id/vote' => 'comments#vote', as: 'vote_comment'
But I'm getting back this error:
NameError in CommentsController#votecomment
undefined local variable or method `current_thing' for #<CommentsController:0x007f98efa69c00>
I tried moving the method to the Things controller, but I got the exact same type of error.
What am I doing wrong?
Assuming you have the following relation in comment.rb
belongs_to :thing
You can access the thing object of a comment using #comment.thing. Since redirect_to accepts objects, you can do
redirect_to #comment.thing
You have to understand that nothing is called current_thing if you are familiar with devise and you see ex current_user this is a method in the gem not a populated method with each model you create.
So if you want something like that add method to your application_controller or even application helper to get current_thing
def current_thing
Thing.find() --> or whatever the way you get that current thing.
end

Neo4J Gem - Saving undeclared relationships

I am trying to create a realtionship between two nodes as described here
https://github.com/neo4jrb/neo4j/wiki/Neo4j-v3-Declared-Relationships
from_node.create_rel("FRIENDS", to_node)
I am getting an undefined method for create_rel
What am I doing wrong? I am trying to create a Q+A system inside another model. So both Questions and Answers are treated as models right now.
I'm getting a undefined methodcreate_rel' for #
event.rb
has_many :out, :event_questions
event_question.rb
has_one :in, :events
has_many :out, :event_answers
def create_questions_of(from_node,to_node)
from_node.create_rel("questions_of", to_node)
end
event_answer.rb
has_one :in, :event_questions
event_questions_controller.rb
def new
#is this needed
end
def create
#event_question = EventQuestion.new(event_question_params)
if #event_question.save
#event = Event.find(params[:id])
#event_question.update(admin: current_user.facebook_id)
#event_question.create_questions_of(self,#event)
redirect_to #event
else
redirect_to #event
end
end
private
def event_question_params
params.require(:event_question).permit(:question)
end
I have my new question sitting inside the event's index page since I wanted to list all the questions on the event after. I don't even need a new method in my controller right? I also don't really know how I would obtain the event that my question form is sitting on. Is that accessible through params?
UPDATE
Did you mean this
def create_questions_of(to_node)
self.create_rel("questions_of", to_node)
end
and
#event_question.create_questions_of(#event)
So I think I need to change my routes as well and nest questions inside to create
events/123/questions/
Then I can grab events_id and use find
UPDATE #2
events_controller.rb
def show
#event = Event.find(params[:id])
#event_question = EventQuestion.new
end
event.rb
has_many :out, :event_questions, type: 'questions_of'
event_question.rb
has_one :in, :events, origin: :event_questions
events/show.html.erb
<%= form_for [:event, #event_question] do |f| %>
#form stuff
<% end %>
event_questions_controller.rb
def create
#event_question = EventQuestion.new(event_question_params)
if #event_question.save
#event = Event.find(params[:event_id])
#event_question.update(admin: current_user.facebook_id)
#event_question.events << #event
redirect_to #event
else
redirect_to :back
end
end
routes.rb
resources :events do
resources :event_questions, only: [:create, :destroy]
end
create_rel worked fine when I tested it just now. Is it saying undefined method 'create_rel' for nil:NilClass? If so, it means that your from_node variable doesn't actually have a node set. Make sure your objects are what you think they are.
The better question here: why do you want to do this? When you create an undeclared relationship, you have to write your own Cypher queries whenever you want to use it. If it's part of your code and you are using it regularly, it should probably have has_many associations in your models. create_rel really only exists to provide interoperability with nodes that don't have models.
As for your other question, you don't need a new action unless there's a route and a view that corresponds with it. If you're loading the form for a new question on your index page, that's fine. If your URL is something like http://127.0.0.1:3000/events/123/questions/, then you can get the Event ID in params[:event_id]. Run the rake routes command from your project's directory and it'll spit out lots of information that includes the parameter names.
Finally, when you use self in #event_question.create_questions_of(self,#event), you're going to get the controller. If you want it to refer to the #event_question, just remove that first argument from create_questions_of and use self from within the method.
Edit: Part 2
You're getting the undefined method because self in #event_question.create_questions_of(self,#event) is the controller. You're trying to send #event_question to itself, I think. Don't do that, just call self from within create_questions_of and you'll get current EventQuestion.
You use ActiveRel if you want callbacks, validations, properties, etc,... If you just want a simple relationships, just setup the has_many associations in each model, omit rel_class, and either set them both to the same type or set origin on one.
class Event
include Neo4j::ActiveNode
has_many :in, :event_questions, type: 'questions_of'
end
class EventQuestion
include Neo4j::ActiveNode
has_many :out, :events, origin: :event_questions
end
origin says, "Look for this association in the reciprocal model and use the type it defines." It lets you not have to worry about synchronizing the type between associations.
After that, you can do #event_question.events << #event and it'll create a new relationship for you.

Resources