Topic: How to move this from view to controller?

Hi,

I've got these models:
- User             has_many :assignments
- Scenario         has_many :assignments
- Milestone        has_many :assignments
- Assignment       belongs_to all others smile

Now in the index action for the assignments I need to display all scenarios + assignment for each scenario *if* such assignment exists.

Now it works like this:

class Scenario < ActiveRecord::Base
  def assignment_for(milestone)   
    self.assignments.select {|a| a.milestone_id == milestone.id }.first
  end
end

controller:
def index
  @milestones = Milestone.find(:all)
  @scenarios = Scenario.find(:all)
  @users = User.find(:all, :order => :login)
 
  if params[:milestone].nil?
    @current_milestone = @milestones.find {|milestone| milestone.current?}
  else
    @current_milestone = @milestones.find {|milestone| milestone.id == params[:milestone].to_i}
  end
 
  @assignments = Assignment.find(:all, :conditions => ["milestone_id = ?", @current_milestone.id])

  respond_to do |format|
    format.html # index.rhtml
    format.xml  { render :xml => @assignments.to_xml }
  end
end


_scenario.rhtml (partial rendered for each scenario in index action of assignment controller) :
  <% assignment = scenario.assignment_for(@current_milestone) -%> 
  <% if assignment.nil? -%>
  <%= render :partial => 'new_assignment', :locals => {:scenario => scenario} %>
  <% else -%>
  <%= render :partial => 'existing_assignment', :locals => {:scenario => scenario, :assignment => assignment} %>
  <% end -%>

How can I refactor it and move the code to controller? Additionally, current code doesn't allow me i.e. to easily get similar results in xml format.

Thank you in advance

Re: How to move this from view to controller?

Without understanding too much of what you're trying to do as I can't see the result I'm going off purely my own best practices.  When I find myself writing conditionals in a view I start throwing things into helpers.  Given the complexity of the relationship you have it may even be a decent idea to create your own block helper to make your views descriptive of what is really happening.  For more info on block helpers and generally drying up your views you can check out Rails Casts as well as the many articles written by Bruce Williams on the subject.

If art interprets our dreams, the computer executes them in the guise of programs.

-SICP (Abelson, Sussman)

Re: How to move this from view to controller?

I forgot to subscribe to this topic and missed your reply smile

The problem is with this part:

class Scenario < ActiveRecord::Base
  def assignment_for(milestone)   
    self.assignments.select {|a| a.milestone_id == milestone.id }.first
  end
end

and then in the view in _scenario.rhtml partial:
<% assignment = scenario.assignment_for(@current_milestone) -%>
few other lines using assignment object

I'd like to move this code somehow to controller, so I could just fetch it once and then display as html, xml, csv or whatever.

The problem is that I don't want to display assignments only (this would be easy), but *all* scenarios and assignments to them *if* such assignment exists. Thus I've got assignment_for method for scenario called for each scenario in my view.

But using this method in the view makes it impossible to display it as i.e. xml or csv.

Now I get these logs when calling assignment index method:

Processing AssignmentsController#index (for 127.0.0.1 at 2007-07-16 22:04:58) [GET]
  Session ID: 862cf90d4eb93a9c72446ef9fc699371
  Parameters: {"action"=>"index", "controller"=>"assignments"}
  User Load (0.000000)   SELECT * FROM users WHERE (users."id" = 1) LIMIT 1
  Milestone Load (0.000000)   SELECT * FROM milestones ORDER BY name
  Scenario Load (0.000000)   SELECT * FROM scenarios ORDER BY name
  User Load (0.000000)   SELECT * FROM users ORDER BY login
  Assignment Load (0.000000)   SELECT * FROM assignments WHERE (milestone_id = 3)
Rendering actionindexlayoutfalsecontent_typetext/html within layouts/application
Rendering assignments/index
  Assignment Load (0.000000)   SELECT * FROM assignments WHERE (assignments.scenario_id = 1)
  User Load (0.000000)   SELECT * FROM users WHERE (users."id" = 2)
Rendered assignments/_update_options (0.00000)
Rendered assignments/_existing_assignment (0.03100)
Rendered assignments/_form (0.00000)
Rendered assignments/_scenario (0.03100)
  Assignment Load (0.000000)   SELECT * FROM assignments WHERE (assignments.scenario_id = 2)
  User Load (0.032000)   SELECT * FROM users WHERE (users."id" = 2)
Rendered assignments/_update_options (0.00000)
Rendered assignments/_existing_assignment (0.03200)
Rendered assignments/_form (0.00000)
Rendered assignments/_scenario (0.04700)
  Assignment Load (0.000000)   SELECT * FROM assignments WHERE (assignments.scenario_id = 3)
  User Load (0.000000)   SELECT * FROM users WHERE (users."id" = 1)
Rendered assignments/_update_options (0.01600)
Rendered assignments/_existing_assignment (0.01600)
Rendered assignments/_form (0.00000)
Rendered assignments/_scenario (0.03200)
  Assignment Load (0.000000)   SELECT * FROM assignments WHERE (assignments.scenario_id = 4)
  User Load (0.000000)   SELECT * FROM users WHERE (users."id" = 2)
Rendered assignments/_update_options (0.01600)
Rendered assignments/_existing_assignment (0.01600)
Rendered assignments/_form (0.01500)
Rendered assignments/_scenario (0.04600)
  Assignment Load (0.000000)   SELECT * FROM assignments WHERE (assignments.scenario_id = 5)
Rendered assignments/_new_assignment (0.00000)
Rendered assignments/_form (0.00000)
Rendered assignments/_scenario (0.01600)
  Assignment Load (0.015000)   SELECT * FROM assignments WHERE (assignments.scenario_id = 6)
  User Load (0.000000)   SELECT * FROM users WHERE (users."id" = 2)
Rendered assignments/_update_options (0.00000)
Rendered assignments/_existing_assignment (0.03100)
Rendered assignments/_form (0.01600)
Rendered assignments/_scenario (0.09400)
  Assignment Load (0.000000)   SELECT * FROM assignments WHERE (assignments.scenario_id = 7)
  User Load (0.000000)   SELECT * FROM users WHERE (users."id" = 2)
Rendered assignments/_update_options (0.00000)
Rendered assignments/_existing_assignment (0.01600)
Rendered assignments/_form (0.18700)
Rendered assignments/_scenario (0.21900)
  Assignment Load (0.000000)   SELECT * FROM assignments WHERE (assignments.scenario_id = 8)
  User Load (0.000000)   SELECT * FROM users WHERE (users."id" = 1)
Rendered assignments/_update_options (0.00000)
Rendered assignments/_existing_assignment (0.01500)
Rendered assignments/_form (0.00000)
Rendered assignments/_scenario (0.03100)
  Assignment Load (0.000000)   SELECT * FROM assignments WHERE (assignments.scenario_id = 9)
  User Load (0.000000)   SELECT * FROM users WHERE (users."id" = 2)
Rendered assignments/_update_options (0.00000)
Rendered assignments/_existing_assignment (0.01600)
Rendered assignments/_form (0.00000)
Rendered assignments/_scenario (0.03200)
Rendered assignments/_sidebar (0.00000)
Completed in 0.73500 (1 reqs/sec) | Rendering: 0.51600 (70%) | DB: 0.04700 (6%) | 200 OK [http://localhost/assignments]

As you can see when rendering _scenario.rhtml partial it fetches assignment every time.

Additionaly I've got "assignment.user.full_name", where "assignment" object is retrived using "scenario.assignment_for(milestone)", which causes the same users to be fetched maany times. It would be nice to fix this somehow as well.

[EDIT]
I've modified my Scenario model and modified

has_many :assignments

to
has_many :assignments,  :include => :user

which seems to help with loading users objects every time.

Last edited by g0nzo (2007-07-16 16:58:40)

Re: How to move this from view to controller?

I think I've found the solution.
I've just added "attr_accessor :assignment" to Scenario model and I'm just doing a small loop in the controller:

@scenarios.each {|scenario| scenario.assignment = scenario.assignment_for(@current_milestone)}

This way I could move scenario.assignment_for(milestone) calls from the view to the controller. Maybe it's something obvious, but I've never used attr_accessor before in any of my models smile