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