1

I have three entities: user, contact & engagement. user that has_many: contacts. contact belongs_to: user. A user makes an engagement with a contact. I have also that an engagement belongs_to: contact. In my views/contacts/show.html.erb I want to show a particular contact's page and let a user register an engagement with the contact by filling an engagement form. I want the engagement that is created on the contact's page to be associated with that particular contact. So I show a contact:

resources :contacts
resources :engagements, only: [:create, :edit, :destroy]

class ContactsController < ApplicationController
      include ApplicationHelper
       def show
        @contact = Contact.find(params[:id])
        set_current_contact @contact.id  #pass the particular id to helper
      end
    end

Define method in helper:

module ApplicationHelper
  def set_current_contact(contact_id)
    @current_contact = Contact.find_by(id: contact_id) 
  end

  def the_current_contact
    @current_contact   #create instance variable for the other helper
  end
end

The key thing I am trying to do is to make the engagements controller 'know' for which contact the user is registering an engagement. i.e. pass @contact to EngagementsController

class EngagementsController < ApplicationController
  def create
    @engagement = the_current_contact.engagements.build(engagement_params) 
  end
end

I get the error:

undefined method `set_current_contact' for #<EngagementsController:0x007f2c2c24f360>

First problem is that I do not understand why the controller can not access a method from the ApplicationHelper? I did not mean to ask two somehow different questions but the second issue is whether using the helper in this way is the right approach. I understand that HTTP is a stateless protocol and in this case helpers become useful fort passing instance variables. I have searched for similar posts and found related Rails: Set a common instance variable across several controller actions but though it recommends helpers as the solution it does not explain in particular how to use the helper.

EDIT: I have added the missing include ApplicationHelper in the EngagementsController. Now the error is:

wrong number of arguments (given 0, expected 1)
Extracted source (around line #13):

  end

  13 def set_current_contact(contact_id)
  14   @current_contact = Contact.find_by(id: contact_id)
  15 end
1
  • You forgot to include ApplicationHelper in your EngagementsController. And even when you fix this, you are correct in your assumption that it will not work as you expect :) Commented Dec 10, 2016 at 20:05

2 Answers 2

2

You can't share data between two requests using an instance variable, not even between two requests to the same controller. Rails creates a new controller instance for every request. Even so, your app may be behind a load balancer, and no one can guarantee that the second request is even served by the same server.

The best way to achieve what you want is to pass the contact_id as a parameter to the EngagementsController#create action. Or else use session data.

Sign up to request clarification or add additional context in comments.

2 Comments

@lulian Yes, it would be helpful to be able to pass the contact_id to EngagementsController. I can pass Contact.all and Contact.first and so on in EngagementsController but how do I get the particular Contact.find([params: id]). How does the EngagementsController access that id? The contacts controller can access it via get 'contacts/:id' which is easily used in contacts#show, destroy, etc. How can engagements controller know that the current page is e.g. contacts/23 and thus attach the engagement to contact with id 23?
@lulian You suggested also using session data. This can work to get the current user id via session[:user_id] since users log in. But contacts do not log in. Can I store a contact id on a session? What if a user logs in permanently and creates cookies then it would be not a session but cookies.signed[:user_id] and the id might have to be captured differently.
1

I found a way of making the contact_id accesible to the EngagementsController without having to pass variables from ContactsController to EngagementsController via a helper. The nested resources:

  resources :contacts do
    resources :engagements 
  end

generates the path:

POST   /contacts/:contact_id/engagements(.:format)  engagements#create

Thus the contact_id of the engagement is made accessible to the EngagementsController:

def create
    @contact = Contact.find(params[:contact_id])
    @engagement = @contact.engagements.build(engagement_params)
end

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.