class TimespanRange < DelegateDecorator attr_accessor :unit, :range def initialize range, unit = :minutes range = (0..60) if range.min == nil || range.max == nil super(range, except: %w{to_s to_str}) @range = Timespan.new between: range @unit = unit.to_s.pluralize.to_sym end def to_str to_s end def to_s range.min.nil? ? 'no timespan range' : "#{range.min} to #{range.max} #{unit}" end end class DurationRange < DelegateDecorator attr_accessor :unit, :range def initialize range, unit = :minutes range = (0..60) if range.min == nil || range.max == nil super(range, except: %w{to_s to_str}) unit = unit.to_s.pluralize.to_sym unless allowed_unit? unit raise ArgumentError, "Unit #{unit} not valid, only: #{allowed_units} are valid" end @unit = unit @range = range end def allowed_unit? unit allowed_units.include? unit.to_sym end def allowed_units [:seconds, :minutes, :hours, :days, :weeks, :months, :years] end def to_str to_s end def to_s range.min.nil? ? 'no duration range' : "#{range.min} to #{range.max} #{unit}" end def __evolve_to_duration_range__ self end def mongoize {:from => range.min.to_i, :to => range.max.to_i} end def between? duration obj = case duration when Duration duration else Duration.new duration end obj.total >= min && obj.total <= max end class << self # See http://mongoid.org/en/mongoid/docs/upgrading.html # Serialize a Hash (with DurationRange keys) or a DurationRange to # a BSON serializable type. # # @param [Timespan, Hash, Integer, String] value # @return [Hash] Timespan in seconds def mongoize object mongoized = case object when DurationRange then object.mongoize when Hash object when Range object.send(:seconds).mongoize else object end # puts "mongoized: #{mongoized} - Hash" mongoized end # Deserialize a Timespan given the hash stored by Mongodb # # @param [Hash] Timespan as hash # @return [Timespan] deserialized Timespan def demongoize(object) return if !object demongoized = case object when Hash object.__evolve_to_duration_range__ else raise "Unable to demongoize DurationRange from: #{object}" end # puts "demongoized: #{demongoized} - #{demongoized.class}" demongoized end # Converts the object that was supplied to a criteria and converts it # into a database friendly form. def evolve(object) object.__evolve_to_duration_range__.mongoize end protected def parse duration if duration.kind_of? Numeric return Duration.new duration else case duration when Timespan duration.duration when Duration duration when Hash Duration.new duration when Time duration.to_i when DateTime, Date duration.to_time.to_i when String Duration.new parse_duration(duration) else raise ArgumentError, "Unsupported duration type: #{duration.inspect} of class #{duration.class}" end end end end end class LongDurationRange < DurationRange def allowed_units [:days, :weeks, :months, :years] end end class ShortDurationRange < DurationRange def allowed_units [:seconds, :minutes, :hours] end end class Range [:seconds, :minutes, :hours, :days, :weeks, :months, :years].each do |unit| define_method unit do |type = :duration| timerange = Range.new self.min.send(unit), self.max.send(unit) type == :timespan ? TimespanRange.new(timerange, unit) : DurationRange.new(timerange, unit) end end end