lib/groupdate/series.rb in groupdate-2.1.1 vs lib/groupdate/series.rb in groupdate-2.2.0
- old
+ new
@@ -1,189 +1,27 @@
module Groupdate
class Series
- attr_accessor :relation
+ attr_accessor :magic, :relation
- def initialize(relation, field, column, time_zone, time_range, week_start, day_start, group_index, options)
+ def initialize(magic, relation)
+ @magic = magic
@relation = relation
- @field = field
- @column = column
- @time_zone = time_zone
- @time_range = time_range
- @week_start = week_start
- @day_start = day_start
- @group_index = group_index
- @options = options
end
- def perform(method, *args, &block)
- utc = ActiveSupport::TimeZone["UTC"]
-
- time_range = @time_range
- if !time_range.is_a?(Range) and @options[:last]
- step = 1.send(@field) if 1.respond_to?(@field)
- if step
- now = Time.now
- time_range = round_time(now - (@options[:last].to_i - 1).send(@field))..now
- end
- end
-
- relation =
- if time_range.is_a?(Range)
- # doesn't matter whether we include the end of a ... range - it will be excluded later
- @relation.where("#{@column} >= ? AND #{@column} <= ?", time_range.first, time_range.last)
- else
- @relation.where("#{@column} IS NOT NULL")
- end
-
- # undo reverse since we do not want this to appear in the query
- reverse = relation.reverse_order_value
- if reverse
- relation = relation.reverse_order
- end
- order = relation.order_values.first
- if order.is_a?(String)
- parts = order.split(" ")
- reverse_order = (parts.size == 2 && parts[0] == @field && parts[1].to_s.downcase == "desc")
- reverse = !reverse if reverse_order
- end
-
- multiple_groups = relation.group_values.size > 1
-
- cast_method =
- case @field
- when "day_of_week", "hour_of_day"
- lambda{|k| k.to_i }
- else
- lambda{|k| (k.is_a?(String) ? utc.parse(k) : k.to_time).in_time_zone(@time_zone) }
- end
-
- count =
- begin
- Hash[ relation.send(method, *args, &block).map{|k, v| [multiple_groups ? k[0...@group_index] + [cast_method.call(k[@group_index])] + k[(@group_index + 1)..-1] : cast_method.call(k), v] } ]
- rescue NoMethodError
- raise "Be sure to install time zone support - https://github.com/ankane/groupdate#for-mysql"
- end
-
- series =
- case @field
- when "day_of_week"
- 0..6
- when "hour_of_day"
- 0..23
- else
- time_range =
- if time_range.is_a?(Range)
- time_range
- else
- # use first and last values
- sorted_keys =
- if multiple_groups
- count.keys.map{|k| k[@group_index] }.sort
- else
- count.keys.sort
- end
- sorted_keys.first..sorted_keys.last
- end
-
- if time_range.first
- series = [round_time(time_range.first)]
-
- step = 1.send(@field)
-
- while time_range.cover?(series.last + step)
- series << series.last + step
- end
-
- if multiple_groups
- keys = count.keys.map{|k| k[0...@group_index] + k[(@group_index + 1)..-1] }.uniq
- series = series.reverse if reverse
- keys.flat_map do |k|
- series.map{|s| k[0...@group_index] + [s] + k[@group_index..-1] }
- end
- else
- series
- end
- else
- []
- end
- end
-
- # reversed above if multiple groups
- if !multiple_groups and reverse
- series = series.to_a.reverse
- end
-
- key_format =
- if @options[:format]
- if @options[:format].respond_to?(:call)
- @options[:format]
- else
- sunday = @time_zone.parse("2014-03-02 00:00:00")
- lambda do |key|
- case @field
- when "hour_of_day"
- key = sunday + key.hours + @day_start.hours
- when "day_of_week"
- key = sunday + key.days
- end
- key.strftime(@options[:format].to_s)
- end
- end
- else
- lambda{|k| k }
- end
-
- Hash[series.map do |k|
- [multiple_groups ? k[0...@group_index] + [key_format.call(k[@group_index])] + k[(@group_index + 1)..-1] : key_format.call(k), count[k] || 0]
- end]
- end
-
- def round_time(time)
- time = time.to_time.in_time_zone(@time_zone) - @day_start.hours
-
- time =
- case @field
- when "second"
- time.change(:usec => 0)
- when "minute"
- time.change(:sec => 0)
- when "hour"
- time.change(:min => 0)
- when "day"
- time.beginning_of_day
- when "week"
- # same logic as MySQL group
- weekday = (time.wday - 1) % 7
- (time - ((7 - @week_start + weekday) % 7).days).midnight
- when "month"
- time.beginning_of_month
- else # year
- time.beginning_of_year
- end
-
- time + @day_start.hours
- end
-
- def clone
- Groupdate::Series.new(@relation, @field, @column, @time_zone, @time_range, @week_start, @day_start, @group_index, @options)
- end
-
# clone to prevent modifying original variables
def method_missing(method, *args, &block)
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation/calculations.rb
if ActiveRecord::Calculations.method_defined?(method)
- clone.perform(method, *args, &block)
+ magic.perform(relation, method, *args, &block)
elsif @relation.respond_to?(method)
- series = clone
- series.relation = @relation.send(method, *args, &block)
- series
+ Groupdate::Series.new(magic, relation.send(method, *args, &block))
else
super
end
end
def respond_to?(method, include_all = false)
- ActiveRecord::Calculations.method_defined?(method) || @relation.respond_to?(method) || super
+ ActiveRecord::Calculations.method_defined?(method) || relation.respond_to?(method) || super
end
- end # Series
+ end
end