# Copyright (C) 2011 AMEE UK Ltd. - http://www.amee.com # Released as Open Source Software under the BSD 3-Clause license. See LICENSE.txt for details. # :title: Class: AMEE::Db::Calculation module AMEE module Db # This class represents a database record for a calculation performed using # the AMEE:DataAbstraction::OngoingCalculation class. This class stores # the primary calculation level attributes such as the calculation # calculation_type, profile_uid and profile_item_uid. # # The values and attributes of specific calculation terms are stored via the # related class AMEE::Db::Term. # # This class is typically used by proxy, via the find, # find_by_type, #save, #delete, and # #get_db_calculation methods associated with the # AMEE:DataAbstraction::OngoingCalculation class. # class Calculation < ActiveRecord::Base has_many :terms, :class_name => "AMEE::Db::Term", :dependent => :destroy validates_presence_of :calculation_type validates_format_of :profile_item_uid, :with => /\A([A-Z0-9]{12})\z/, :allow_nil => true, :allow_blank => true validates_format_of :profile_uid, :with => /\A([A-Z0-9]{12})\z/, :allow_nil => true, :allow_blank => true before_save :validate_calculation_type # Standardize the calculation_type attribute to String # format. Called using before filter prior to record saving to ensure # string serialization. # def validate_calculation_type self.calculation_type = calculation_type.to_s end # Convenience method for returning the calculation_type attribute # of self in canonical symbol form. # def type calculation_type.to_sym end # Returns the subset of all instance attributes which should be editable via # mass update methods and which should be included in hash representations of # self, i.e. those passed in explcitly as data rather than added by # ActiveRecord (e.g. id, created_at, etc.). # def primary_calculation_attributes attributes.keys.reject {|attr| ['id','created_at','updated_at'].include? attr } end # Update the attributes of self and those of any related terms, # according to the passed options hash. Any associated terms which # are not represented in options are deleted. # # Term attributes provided in options should be keyed with the # term label and include a sub-hash with keys represent one or more of # :value, :unit and :per_unit. E.g., # # options = { :profile_item_uid => "W93UEY573U4E8", # :mass => { :value => 23 }, # :distance => { :value => 1400, # :unit => }} # # my_calculation.update_calculation!(options) # def update_calculation!(options) primary_calculation_attributes.each do |attr| if options.keys.include? attr.to_sym update_calculation_attribute!(attr,options.delete(attr.to_sym),false) end end save! options.each_pair do |attribute,value| add_or_update_term!(attribute,value) end delete_unspecified_terms(options) reload end # Update the attribute of self represented by the label # key with the value of value. By default, # the #save! method is called, in turn calling the class # validations. # # Specify that the record should not be saved by passing false # as the final argument. # def update_calculation_attribute!(key,value,save=true) # use attr_accessor (via #send) method rather than # #update_attribute so that validations are performed # send("#{key}=", (value.nil? ? nil : value.to_s)) save! if save end # Add, or update an existing, associated term represented by the label # label and value, unit and/or per_unit attributes defined by # data. The data argument should be a hash with keys # represent one or more of :value, :unit and :per_unit. E.g., # # data = { :value => 1400, # :unit => } # # my_calculation.add_or_update_term!(:distance, data) # # This method is called as part of the #update_calculation! # method # def add_or_update_term!(label,data) term = Term.find_or_initialize_by_calculation_id_and_label(id,label.to_s) term.update_attributes!(data) end # Delete all of the terms which are not explicitly referenced in the # options hash. # # This method is called as part of the #update_calculation! # method # def delete_unspecified_terms(options) terms.each do |term| Term.delete(term.id) unless options.keys.include? term.label.to_sym end end # Returns a Hash representation of self including a only # the data explicitly passed in (those added by ActiveRecord - # created, updated, id - are ignored) as well # as sub-hashes for all associated terms. E.g., # # my_calculation.to_hash #=> { :profile_uid => "EYR758EY36WY", # :profile_item_uid => "W83URT48DY3W", # :type => { :value => 'car' }, # :distance => { :value => 1600, # :unit => }, # :co2 => { :value => 234.1, # :unit => }} # # This method can be used to initialize instances of the class # OngoingCalculation by providing the hashed options for any of # the choose... methods. # def to_hash hash = {} terms.each { |term| hash.merge!(term.to_hash) } [ :profile_item_uid, :profile_uid ].each do |attr| hash[attr] = self.send(attr) end return hash end end end end