Al D
Al D

Reputation: 667

Rails assign session variable

Hi I'm hacking around Rails the last 8 months so don't truly understand a number of things. I've seen and used helper methods that set the current_user etc. and am now trying to replicate something similar.

I'm trying to set a global value in the application controller for the current company the user is in. I know I'm doing it wrong but I can't quite figure out how to solve it or even if I'm approaching it correctly. I want it so that when the user clicks on a company, the global variable is set to the id of the company they are in. Then in any other sub-model, if I want info on the company, I use the global variable to retrieve the company object with that id.

The code that's causing the problem is in my navbar in application.html.erb

<li><%= link_to "Company", company_path, :method => :get %></li>

This works when I'm using the companies controller etc. But when I try use any other controller I'm getting the error

ActionController::UrlGenerationError in Employees#index

No route matches {:action=>"show", :controller=>"companies"} missing required keys: [:id] 

which as I understand it means it can't render the url because no company id parameter is being passed?

I was trying to hack a helper method I used in another project (for getting the current_user) but have realised that it uses the session to extract the user.id to return a user object. Here's my attempt at the helper method in application_controller.rb

def current_company
  @company = Company.find_by_id(params[:id])
end

I'm not able to get the current company from the session so my method above is useless unless the company id is being passed as a parameter.

I've thought about passing the company id on every sub-model method but

  1. I'm not 100% sure how to do this
  2. It doesn't sound very efficient

So my question is am I approaching this correctly? What's the optimal way to do it? Can I create a helper method that stores a global company id variable that gets set once a user accesses a company and can then be retrieved by other models?

I probably haven't explained it too well so let me know if you need clarification or more info. Thanks for looking.

Edit 1

Made the changes suggested by Ruby Racer and now I have:

application.html.erb

 <%unless current_page?(root_path)||current_page?(companies_path)||current_company.nil? %>

   <li><%= link_to "Company", company_path, :method => :get %></li>

This is not displaying the link in the navbar, I presume because current_company is nil (the other two unless statements were fine before I added current_company.nil?

I'm setting the current_company in

companies_controller.rb

before_action :set_company, only: [:show, :edit, :update, :destroy, :company_home]

def company_home
  current_company = @company
  respond_with(@company)
end

application_controller.rb

def current_company=(company)
  session[:current_company] = company.id
  puts "The current_company has been assigned"
  puts params.inspect
end

def current_company
  @company = Company.find_by_id(session[:current_company])
  puts "The current_company helper has been called"
  puts @company
  puts params.inspect
end

Am I doing something wrong?

Edit 2

I have no idea why this isn't working. After the above edits, it appears as though the session[:company_id] is not being assigned so the current_company helper method is returning nil. I've tried printing the session paramaters puts session.inspect and can't find any company_id information. Anyone any idea why it isn't assigning the value?

Edit 3

Can't for the life of me figure out what's going wrong. I've tried multiple things including moving the current_company = @company into the set_company method in companies_controller.rb which now looks like this:

def company_home
  puts "Test the current company"
  puts "#{@company.id} #{@company.name}"
  puts params.inspect
end

 private
  def set_company
    @company = Company.find_by_id(params[:id])
    if @company.nil?||current_user.organisation_id != @company.organisation.id
    flash[:alert] = "Stop poking around you nosey parker"
    redirect_to root_path
    else
    current_company = @company
    end      
  end

The company_home method is being given a company object (I can see this in the console output below) but the current_company assignment is just not happening. Here's the console output for reference

Started GET "/company_home/1" for 80.55.210.105 at 2014-12-19 10:26:49 +0000                                                                                                      
Processing by CompaniesController#company_home as HTML                                                                                                                            
  Parameters: {"authenticity_token"=>"gfdhjfgjhoFFHGHGFHJGhjkdgkhjgdjhHGLKJGJHpDQs6yNjONwSyTrdgjhgdjgjf=", "id"=>"1"}                                                                                   
  User Load (0.5ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = 6  ORDER BY "users"."id" ASC LIMIT 1                                                                    
  Company Load (0.3ms)  SELECT  "companies".* FROM "companies"  WHERE "companies"."id" = 1 LIMIT 1                                                                                
  Organisation Load (0.3ms)  SELECT  "organisations".* FROM "organisations"  WHERE "organisations"."id" = $1 LIMIT 1  [["id", 6]]                                                 
Test the current company                                                                                                                                                          
1 Cine                                                                                                                                                                            
{"_method"=>"get", "authenticity_token"=>"gfdhjfgjhoFFHGHGFHJGhjkdgkhjgdjhHGLKJGJHpDQs6yNjONwSyTrdgjhgdjgjf=", "controller"=>"companies", "action"=>"company_home", "id"=>"1"}                          
  Rendered companies/company_home.html.erb within layouts/application (0.1ms)                                                                                                     
  Company Load (0.6ms)  SELECT  "companies".* FROM "companies"  WHERE "companies"."id" IS NULL LIMIT 1                                                                            
The current_company helper has been called                                                                                                                                        

{"_method"=>"get", "authenticity_token"=>"gfdhjfgjhoFFHGHGFHJGhjkdgkhjgdjhHGLKJGJHpDQs6yNjONwSyTrdgjhgdjgjf=", "controller"=>"companies", "action"=>"company_home", "id"=>"1"}                          
  CACHE (0.0ms)  SELECT  "organisations".* FROM "organisations"  WHERE "organisations"."id" = $1 LIMIT 1  [["id", 6]]                                                             
Completed 200 OK in 280ms (Views: 274.0ms | ActiveRecord: 1.7ms)

As per above, under the line The current_company helper has been called, there's a blank line where puts @company should be outputting something. This means the current_company method is returning nothing.

Also, in the company_home method in the companies_controller, if I change puts "#{@company.id} #{@company.name}" to puts "#{current_company.id} #{current_company.name}" an error gets thrown.

Has anyone any idea why the def current_company=(company) isn't assigning a session parameter? Thanks

Final Edit

I've no idea why, but it appears the problem related to this:

def current_company=(company)
  session[:current_company] = company.id
  puts "The current_company has been assigned"
  puts params.inspect
end

It looks as though this never gets called. I don't understand why as I've never used something like this before.

I'll put my fix in an answer.

Upvotes: 4

Views: 14815

Answers (2)

Al D
Al D

Reputation: 667

Instead of using a helper method to assign a value or search for a company, I've assigned a session variable in the set_company method in companies_controller.rb. This can then be accessed around the application

companies_controller.rb

 private
    def set_company
       @company = Company.find_by_id(params[:id])

       if @company.nil?||current_user.organisation_id != @company.organisation.id
          redirect_to root_path
       else
          session[:current_company] = @company
          current_company = @company
       end      
    end

Upvotes: 0

Ruby Racer
Ruby Racer

Reputation: 5740

Ok, you need to do two things.

First thing, you need to assign your company.id to a session variable

def current_company=(company)
    session[:company_id]=company.id
end

Second, your helper method for current_company will be as follows:

def current_company
    Company.find_by_id(session[:company_id])
end

This can be nil if there is no session[:company_id] or if it corresponds to no company. That's ok...

Next, it is quite unlikely to get it working without an id, if you use /companies both for your index and your show actions.

Now, for your first task. Setting the variable:

controller:companies_controller.rb

def show
    # assuming you have a before_action, something like set_company, no need to redo it
    current_company=@company # this will set the session variable
end

If you want your navbar to lead to your current_company, you will need to write:

<% unless current_company.nil? %>
    <li><%= link_to "Company", current_company, :method => :get %></li>
<% end %>

I don't know what you want to do if there is no current company, so I just leave it out.

Upvotes: 6

Related Questions