该任务允许用户以不同的单位查看,创建,编辑记录,即以米和英尺为单位的高度,以米/秒为单位的速度,结,公里/小时,英里/小时。
我已经阅读了很多关于值对象的内容,composed_of
以及为什么我们不应该使用它并使用serialize
来代替下面的解决方案。
对我来说,这似乎是一个复杂的解决方案。 你能指点一下我能为它重构和指导吗?
app/models/weather.rb
class Weather < ActiveRecord::Base
attr_accessor :altitude_unit, :wind_speed_unit
attr_reader :altitude_in_units, :wind_speed_in_units
belongs_to :weatherable, polymorphic: true
before_save :set_altitude, :set_wind_speed
validates_presence_of :actual_on, :wind_direction
validate :altitude_present?
validate :wind_speed_present?
validates_numericality_of :altitude, greater_than_or_equal_to: 0, allow_nil: true
validates_numericality_of :altitude_in_units, greater_than_or_equal_to: 0, allow_nil: true
validates_numericality_of :wind_speed, greater_than_or_equal_to: 0, allow_nil: true
validates_numericality_of :wind_speed_in_units, greater_than_or_equal_to: 0, allow_nil: true
validates_numericality_of :wind_direction, greater_than_or_equal_to: 0, less_than: 360, allow_nil: true
serialize :altitude, Distance
serialize :wind_speed, Velocity
def wind_speed_in_units=(value)
@wind_speed_in_units = value_from_param(value)
end
def altitude_in_units=(value)
@altitude_in_units = value_from_param(value)
end
private
def value_from_param(value)
return nil if value.is_a?(String) && value.empty?
value
end
def altitude_present?
return if altitude.present? || altitude_in_units.present?
errors.add :altitude, :blank
end
def wind_speed_present?
return if wind_speed.present? || wind_speed_in_units.present?
errors.add :wind_speed, :blank
end
def set_altitude
return if altitude_in_units.blank? || altitude_unit.blank?
self.altitude = Distance.new(altitude_in_units, altitude_unit)
end
def set_wind_speed
return if wind_speed_in_units.blank? || wind_speed_unit.blank?
self.wind_speed = Velocity.new(wind_speed_in_units, wind_speed_unit)
end
end
app/model/distance.rb
class Distance < DelegateClass(BigDecimal)
FT_IN_M = 3.280839895
def self.load(distance)
new(distance) unless distance.nil?
end
def self.dump(obj)
obj.dump
end
def initialize(distance, unit = 'm')
value = convert_from(BigDecimal.new(distance), unit)
super(value)
end
def dump
@delegate_dc_obj
end
def convert_to(unit)
method = "to_#{unit}"
raise ArgumentError, "Unsupported unit #{unit}" unless respond_to? method
send method
end
def convert_from(val, unit)
method = "from_#{unit}"
raise ArgumentError, "Unsupported unit #{unit}" unless respond_to? method
send method, val
end
def to_m
@delegate_dc_obj
end
def to_ft
@delegate_dc_obj * FT_IN_M
end
def from_m(val)
val
end
def from_ft(val)
val / FT_IN_M
end
end
app/models/velocity.rb
与距离几乎相同。