Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails - Where should I calculate derived attributes?

Noob question for Ruby on Rails- here's an example of my situation: If I have model circle and attribute radius, where do I do the calculations for circumference? Would this be in the model or the controller, and how might it look? circumference would need to be accessible in my views.

Also, would I be correct in thinking that I don't need to make circumference an attribute that's part of my model/database since it can be derived from a user-input radius?

like image 435
Nick Avatar asked Mar 22 '12 18:03

Nick


People also ask

How do you find derived attributes?

If an attribute's value can be derived from the values of other attributes, then the attribute is derivable, and is said to be a derived attribute. For example, if we have an attribute for birth date then age is derivable. Derived attributes are shown with a dotted lined oval.

What is derive attribute?

Derived attribute − Derived attributes are the attributes that do not exist in the physical database, but their values are derived from other attributes present in the database. For example, average_salary in a department should not be saved directly in the database, instead it can be derived.

What are attributes in Rails?

The Rails Attributes API is used under-the-hood to type cast attributes for ActiveRecord models. When you query for a model that has a datetime column in the database and the Ruby object that gets pulled out has a DateTime field – that's the Attributes API at work.


1 Answers

The logic for calculating a derived attribute absolutely belongs in the model. The circumference is a property of the circle itself, not a concern of how you're presenting it in the web interface.

In order to access the circumference from anywhere, just define a method on the class, such as the following:

require 'mathn'
class Circle < ActiveRecord::Base
  # assume `radius` column exists in the database

  def circumference
    Math::PI * 2 * radius
  end
end

Since it's pretty cheap computationally to calculate the circumference, you can just calculate it as needed. If it were something that involved more complex calculation that you didn't want to run multiple times, you could memoize it as follows:

def circumference
  @circumference ||= Math::PI * 2 * radius
end

That would set the @circumference instance variable the first time the method is called, then return the result of the first calculation on every subsequent call. If you were doing that, you'd need to make sure to set @circumference to nil when the radius changed to make sure it's accurate.

def radius=(value)
  @circumference = nil
  super(value)
end
like image 169
Emily Avatar answered Sep 30 '22 01:09

Emily