blinking-in-the-light
blinking-in-the-light

Reputation: 147

Rails from a high-level view: performing calculations on a model value between view and controller

This must be a common need but I can't seem to find a definitive answer on the most rubyesque way. I need to create a fairly complex algorithm to dynamically calculate course grades in a rails 4.1 app.

Specifically, I have a model, "course", and whenever an instance of it is displayed in the view, I want to dynamically calculate the current grade (a decimal value, calculated from many course.field values) and display it as a letter value using a switch/case. My assumption was that I could do this in the controller (but it almost seems like it's complex enough to warrant it's own -- module? In C++ I would create a class). At the same time, since it is created dynamically, it seemed like bad form to create a current_grade field for it in the model, so it's not one I can pass back and forth as one of the allowable params (that I know of-- can one pass a variable in the params that is not represented in the db?).

In my initial research I see suggestions of hidden_field_tags and helper_methods and all_helpers and modules and global modules and more. Under time pressure, I dread beginning down the wrong path. Which is the better approach? Or a good high level doc for reference?

As an example, here is one view in which I would like to calculate current grade, compare it to desired grade, and display accordingly.

# index.html.erb
<% @courses.each do |course| %>
  <li>
    <%= my_algorithm_to_calculate_curr_grade(many course.fields used to caluculate) 
    <= course.desired_grade ? "set li to <Color: red>" : "set li to <Color: green>" %>   
    <%= course.course_name %>     
    Current Calculation: <%= display_results_of_previous_calculation %>   
    (Goal: <%= course.desired_grade %>)   
    <%= link_to 'Show', course %>   
    <%= link_to 'Edit', edit_course_path(course) %>   
    <%= link_to 'Drop Course Without Penalty', course, method: :delete, data: { confirm: 'Are you sure?' } %>
  </li>        
<% end %>

Upvotes: 0

Views: 502

Answers (1)

messanjah
messanjah

Reputation: 9278

It's hard to tell from your question if course.fields are attributes of Course or different model(s). If all the fields are Course attributes, I would put it as an instance method on Course.

class Course < ActiveRecord::Base
  def calculated_grade
    # fun algorithm
  end
end

If course.fields need to be loaded from the database, I'd probably go with a Plain Old Ruby Object (PORO), maybe call it CourseGradeCalculator (put it in app/models, why not? It's business logic)

class CourseGradeCalculator
  attr_reader :course, :fields, :grade
  def initialize(course, fields)
    @course = course
    @fields = fields
    @grade = calculate_grade
  end

  private

  def calculate_grade
    # fun algorithm
  end
end

# controller
@course = Course.preload(:fields).find(params[:id]

# view
CourseGradeCalculator.new(@course, @course.fields)

Upvotes: 1

Related Questions