Module: ArInterval::RelationMethods

Extended by:
ActiveSupport::Concern
Defined in:
lib/activerecord_time_intervals/relation_methods.rb

Instance Method Summary collapse

Instance Method Details

#in_intervals_of(step, range:, column:) ⇒ Object

Raises:

  • (ArgumentError)


7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/activerecord_time_intervals/relation_methods.rb', line 7

def in_intervals_of(step, range:, column:)
  raise ArgumentError, "PostgreSQL only" unless klass.connection.adapter_name =~ /postg/i
  raise ArgumentError, "column required" if column.nil?

  secs = Integer(step.respond_to?(:to_i) ? step.to_i : step)
  col  = column.to_s

  raise ArgumentError, "step must be positive" if secs <= 0

  startq = klass.connection.quote(range.begin)
  endq   = klass.connection.quote(range.end)

  interval_literal = klass.connection.quote("#{secs} seconds")
  interval_expr    = "#{interval_literal}::interval"
  stop_expr = range.exclude_end? ? "(#{endq}::timestamptz - #{interval_expr})" : "#{endq}::timestamptz"

  table_quoted = klass.connection.quote_table_name(klass.table_name)
  col_quoted   = klass.connection.quote_column_name(col)
  primary_key  = klass.primary_key

  base_relation = self.except(:order, :limit, :offset).unscope(:select)
  lateral_relation = base_relation
                      .where("#{table_quoted}.#{col_quoted} = series.ts")
                      .order(nil)
                      .limit(1)

  series_sql = "    SELECT series.ts AS interval_ts, matched.*\n    FROM generate_series(\#{startq}::timestamptz, \#{stop_expr}, \#{interval_expr}) AS series(ts)\n    LEFT JOIN LATERAL (\n      \#{lateral_relation.to_sql}\n    ) AS matched ON TRUE\n    ORDER BY interval_ts\n  SQL\n\n  klass.find_by_sql(series_sql).map do |record|\n    if primary_key && record.read_attribute(primary_key).nil?\n      nil\n    else\n      record\n    end\n  end\nend\n"