groupdate 5.2.4 → 6.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2b6c83f192d439e35025586a076762413d80daab15b85e81561404bdf078acad
4
- data.tar.gz: 382dd612bbd1db8e131345981576b7a6c61c525d8a991fb8a3fc60678ce8d6ef
3
+ metadata.gz: 12cfc48ae509ac20872022dd07429dd7bd8f24a5bfd81dcb16242f3519cb5385
4
+ data.tar.gz: 3079f700f956f141184fbc5bc8244ad90380804cea9f15fef51300985380d13e
5
5
  SHA512:
6
- metadata.gz: 2d33022921907669f28eed522daec36e84552aabd41a411ff556923c0029cc72a003d30cd9cbeecba27e3d795fc279f71ca05a01cbc8c5cf6a0bd7256a478f72
7
- data.tar.gz: 87c778a1972590cc6609a3c5a096fef6dde88c70ab663b5ebac820de89ec356359f802ea1d8ba38280378e877caabb1974e1d6a81c689eef92e6d7accf2f356a
6
+ metadata.gz: 64b4e317731bc0032a914e4f07ca751ee276e41b7a30a2113bff9d9f2437196916c68e5f1f699c2e107861d0022b712b32315132cd0f530bb7caad5f41955e45
7
+ data.tar.gz: ea085f2e6985f4df40f591333935b88f8c59b2be87943f39241254dbbc8ac18cc1aaa2b54030880a804117ecdc0451a65463b07ea758d3fc93d99697a11a5711
data/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ ## 6.0.1 (2022-01-16)
2
+
3
+ - Fixed incorrect results (error before 6.0) with `includes` with Active Record 6.1+
4
+
5
+ ## 6.0.0 (2022-01-15)
6
+
7
+ - Raise `ActiveRecord::UnknownAttributeReference` for non-attribute arguments
8
+ - Raise `ArgumentError` for ranges with string bounds
9
+ - Added `n` option for Redshift
10
+ - Changed SQL to return dates instead of times for day, week, month, quarter, and year
11
+ - Removed `dates` option
12
+ - Dropped support for Ruby < 2.6 and Rails < 5.2
13
+
1
14
  ## 5.2.4 (2021-12-15)
2
15
 
3
16
  - Simplified queries for Active Record 7 and MySQL
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013-2021 Andrew Kane
1
+ Copyright (c) 2013-2022 Andrew Kane
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -22,7 +22,7 @@ Supports PostgreSQL, MySQL, and Redshift, plus arrays and hashes (and limited su
22
22
  Add this line to your application’s Gemfile:
23
23
 
24
24
  ```ruby
25
- gem 'groupdate'
25
+ gem "groupdate"
26
26
  ```
27
27
 
28
28
  For MySQL and SQLite, also follow [these instructions](#additional-instructions).
@@ -210,19 +210,6 @@ If grouping on date columns which don’t need time zone conversion, use:
210
210
  User.group_by_week(:created_on, time_zone: false).count
211
211
  ```
212
212
 
213
- ### User Input
214
-
215
- If passing user input as the column, be sure to sanitize it first [like you must](https://rails-sqli.org/) with `group`.
216
-
217
- ```ruby
218
- column = params[:column]
219
-
220
- # check against permitted columns
221
- raise "Unpermitted column" unless ["column_a", "column_b"].include?(column)
222
-
223
- User.group_by_day(column).count
224
- ```
225
-
226
213
  ### Default Scopes
227
214
 
228
215
  If you use Postgres and have a default scope that uses `order`, you may get a `column must appear in the GROUP BY clause` error (just like with Active Record’s `group` method). Remove the `order` scope with:
@@ -252,7 +239,7 @@ users.group_by_day(series: true) { |u| u.created_at }
252
239
  Count
253
240
 
254
241
  ```ruby
255
- users.group_by_day { |u| u.created_at }.map { |k, v| [k, v.count] }.to_h
242
+ users.group_by_day { |u| u.created_at }.to_h { |k, v| [k, v.count] }
256
243
  ```
257
244
 
258
245
  ## Additional Instructions
@@ -289,13 +276,15 @@ Groupdate.time_zone = false
289
276
 
290
277
  ## Upgrading
291
278
 
292
- ### 5.0
279
+ ### 6.0
280
+
281
+ Groupdate 6.0 protects against unsafe input by default. For non-attribute arguments, use:
293
282
 
294
- Groupdate 5.0 brings a number of improvements. Here are a few to be aware of:
283
+ ```ruby
284
+ User.group_by_day(Arel.sql(known_safe_value)).count
285
+ ```
295
286
 
296
- - The `week_start` option is now supported for SQLite
297
- - The `day_start` option is now consistent between Active Record and enumerable
298
- - Deprecated positional arguments for time zone and range have been removed
287
+ Also, the `dates` option has been removed.
299
288
 
300
289
  ## History
301
290
 
@@ -4,11 +4,8 @@ module Groupdate
4
4
  attr_reader :period, :column, :day_start, :week_start, :n_seconds
5
5
 
6
6
  def initialize(relation, column:, period:, time_zone:, time_range:, week_start:, day_start:, n_seconds:)
7
- # very important
8
- column = validate_column(column)
9
-
10
7
  @relation = relation
11
- @column = resolve_column(relation, column)
8
+ @column = column
12
9
  @period = period
13
10
  @time_zone = time_zone
14
11
  @time_range = time_range
@@ -49,28 +46,6 @@ module Groupdate
49
46
  ["#{column} IS NOT NULL"]
50
47
  end
51
48
  end
52
-
53
- # basic version of Active Record disallow_raw_sql!
54
- # symbol = column (safe), Arel node = SQL (safe), other = untrusted
55
- # matches table.column and column
56
- def validate_column(column)
57
- unless column.is_a?(Symbol) || column.is_a?(Arel::Nodes::SqlLiteral)
58
- column = column.to_s
59
- unless /\A\w+(\.\w+)?\z/i.match(column)
60
- warn "[groupdate] Non-attribute argument: #{column}. Use Arel.sql() for known-safe values. This will raise an error in Groupdate 6"
61
- end
62
- end
63
- column
64
- end
65
-
66
- # resolves eagerly
67
- # need to convert both where_clause (easy)
68
- # and group_clause (not easy) if want to avoid this
69
- def resolve_column(relation, column)
70
- node = relation.send(:relation).send(:arel_columns, [column]).first
71
- node = Arel::Nodes::SqlLiteral.new(node) if node.is_a?(String)
72
- relation.connection.visitor.accept(node, Arel::Collectors::SQLString.new).value
73
- end
74
49
  end
75
50
  end
76
51
  end
@@ -20,9 +20,21 @@ module Groupdate
20
20
  when :month_of_year
21
21
  ["MONTH(#{day_start_column})", time_zone, day_start]
22
22
  when :week
23
- ["CONVERT_TZ(DATE_FORMAT(#{day_start_column} - INTERVAL ((? + DAYOFWEEK(#{day_start_column})) % 7) DAY, '%Y-%m-%d 00:00:00') + INTERVAL ? second, ?, '+00:00')", time_zone, day_start, 12 - week_start, time_zone, day_start, day_start, time_zone]
23
+ ["CAST(DATE_FORMAT(#{day_start_column} - INTERVAL ((? + DAYOFWEEK(#{day_start_column})) % 7) DAY, '%Y-%m-%d') AS DATE)", time_zone, day_start, 12 - week_start, time_zone, day_start]
24
24
  when :quarter
25
- ["CONVERT_TZ(DATE_FORMAT(DATE(CONCAT(YEAR(#{day_start_column}), '-', LPAD(1 + 3 * (QUARTER(#{day_start_column}) - 1), 2, '00'), '-01')), '%Y-%m-%d %H:%i:%S') + INTERVAL ? second, ?, '+00:00')", time_zone, day_start, time_zone, day_start, day_start, time_zone]
25
+ ["CAST(CONCAT(YEAR(#{day_start_column}), '-', LPAD(1 + 3 * (QUARTER(#{day_start_column}) - 1), 2, '00'), '-01') AS DATE)", time_zone, day_start, time_zone, day_start]
26
+ when :day, :month, :year
27
+ format =
28
+ case period
29
+ when :day
30
+ "%Y-%m-%d"
31
+ when :month
32
+ "%Y-%m-01"
33
+ else # year
34
+ "%Y-01-01"
35
+ end
36
+
37
+ ["CAST(DATE_FORMAT(#{day_start_column}, ?) AS DATE)", time_zone, day_start, format]
26
38
  when :custom
27
39
  ["FROM_UNIXTIME((UNIX_TIMESTAMP(#{column}) DIV ?) * ?)", n_seconds, n_seconds]
28
40
  else
@@ -32,14 +44,8 @@ module Groupdate
32
44
  "%Y-%m-%d %H:%i:%S"
33
45
  when :minute
34
46
  "%Y-%m-%d %H:%i:00"
35
- when :hour
47
+ else # hour
36
48
  "%Y-%m-%d %H:00:00"
37
- when :day
38
- "%Y-%m-%d 00:00:00"
39
- when :month
40
- "%Y-%m-01 00:00:00"
41
- else # year
42
- "%Y-01-01 00:00:00"
43
49
  end
44
50
 
45
51
  ["CONVERT_TZ(DATE_FORMAT(#{day_start_column}, ?) + INTERVAL ? second, ?, '+00:00')", time_zone, day_start, format, day_start, time_zone]
@@ -21,16 +21,18 @@ module Groupdate
21
21
  when :month_of_year
22
22
  ["EXTRACT(MONTH FROM #{day_start_column})::integer", time_zone, day_start_interval]
23
23
  when :week
24
- ["(DATE_TRUNC('day', #{day_start_column} - INTERVAL '1 day' * ((? + EXTRACT(DOW FROM #{day_start_column})::integer) % 7)) + INTERVAL ?) AT TIME ZONE ?", time_zone, day_start_interval, 13 - week_start, time_zone, day_start_interval, day_start_interval, time_zone]
24
+ ["(DATE_TRUNC('day', #{day_start_column} - INTERVAL '1 day' * ((? + EXTRACT(DOW FROM #{day_start_column})::integer) % 7)) + INTERVAL ?)::date", time_zone, day_start_interval, 13 - week_start, time_zone, day_start_interval, day_start_interval]
25
25
  when :custom
26
- ["TO_TIMESTAMP(FLOOR(EXTRACT(EPOCH FROM #{column}::timestamptz) / ?) * ?)", n_seconds, n_seconds]
27
- else
28
- if day_start == 0
29
- # prettier
30
- ["DATE_TRUNC(?, #{day_start_column}) AT TIME ZONE ?", period, time_zone, day_start_interval, time_zone]
26
+ if @relation.connection.adapter_name == "Redshift"
27
+ ["TIMESTAMP 'epoch' + (FLOOR(EXTRACT(EPOCH FROM #{column}::timestamp) / ?) * ?) * INTERVAL '1 second'", n_seconds, n_seconds]
31
28
  else
32
- ["(DATE_TRUNC(?, #{day_start_column}) + INTERVAL ?) AT TIME ZONE ?", period, time_zone, day_start_interval, day_start_interval, time_zone]
29
+ ["TO_TIMESTAMP(FLOOR(EXTRACT(EPOCH FROM #{column}::timestamptz) / ?) * ?)", n_seconds, n_seconds]
33
30
  end
31
+ when :day, :month, :quarter, :year
32
+ ["DATE_TRUNC(?, #{day_start_column})::date", period, time_zone, day_start_interval]
33
+ else
34
+ # day start is always 0 for seconds, minute, hour
35
+ ["DATE_TRUNC(?, #{day_start_column}) AT TIME ZONE ?", period, time_zone, day_start_interval, time_zone]
34
36
  end
35
37
 
36
38
  clean_group_clause(@relation.send(:sanitize_sql_array, query))
@@ -7,7 +7,7 @@ module Groupdate
7
7
 
8
8
  query =
9
9
  if period == :week
10
- ["strftime('%Y-%m-%d 00:00:00 UTC', #{column}, '-6 days', ?)", "weekday #{(week_start + 1) % 7}"]
10
+ ["strftime('%Y-%m-%d', #{column}, '-6 days', ?)", "weekday #{(week_start + 1) % 7}"]
11
11
  elsif period == :custom
12
12
  ["datetime((strftime('%s', #{column}) / ?) * ?, 'unixepoch')", n_seconds, n_seconds]
13
13
  else
@@ -32,13 +32,13 @@ module Groupdate
32
32
  when :hour
33
33
  "%Y-%m-%d %H:00:00 UTC"
34
34
  when :day
35
- "%Y-%m-%d 00:00:00 UTC"
35
+ "%Y-%m-%d"
36
36
  when :month
37
- "%Y-%m-01 00:00:00 UTC"
37
+ "%Y-%m-01"
38
38
  when :quarter
39
39
  raise Groupdate::Error, "Quarter not supported for SQLite"
40
40
  else # year
41
- "%Y-01-01 00:00:00 UTC"
41
+ "%Y-01-01"
42
42
  end
43
43
 
44
44
  ["strftime(?, #{column})", format]
@@ -22,7 +22,7 @@ module Groupdate
22
22
  end
23
23
 
24
24
  def validate_keywords
25
- known_keywords = [:time_zone, :dates, :series, :format, :locale, :range, :reverse]
25
+ known_keywords = [:time_zone, :series, :format, :locale, :range, :reverse]
26
26
 
27
27
  if %i[week day_of_week].include?(period)
28
28
  known_keywords << :week_start
@@ -145,6 +145,16 @@ module Groupdate
145
145
  lambda { |k| k.to_i }
146
146
  when :day_of_week
147
147
  lambda { |k| (k.to_i - 1 - week_start) % 7 }
148
+ when :day, :week, :month, :quarter, :year
149
+ # TODO keep as date
150
+ if day_start != 0
151
+ day_start_hour = day_start / 3600
152
+ day_start_min = (day_start % 3600) / 60
153
+ day_start_sec = (day_start % 3600) % 60
154
+ lambda { |k| k.in_time_zone(time_zone).change(hour: day_start_hour, min: day_start_min, sec: day_start_sec) }
155
+ else
156
+ lambda { |k| k.in_time_zone(time_zone) }
157
+ end
148
158
  else
149
159
  utc = ActiveSupport::TimeZone["UTC"]
150
160
  lambda { |k| (k.is_a?(String) || !k.respond_to?(:to_time) ? utc.parse(k.to_s) : k.to_time).in_time_zone(time_zone) }
@@ -193,11 +203,15 @@ module Groupdate
193
203
  adapter = Groupdate.adapters[adapter_name]
194
204
  raise Groupdate::Error, "Connection adapter not supported: #{adapter_name}" unless adapter
195
205
 
206
+ # very important
207
+ column = validate_column(field)
208
+ column = resolve_column(relation, column)
209
+
196
210
  # generate ActiveRecord relation
197
211
  relation =
198
212
  adapter.new(
199
213
  relation,
200
- column: field,
214
+ column: column,
201
215
  period: magic.period,
202
216
  time_zone: magic.time_zone,
203
217
  time_range: magic.time_range,
@@ -213,6 +227,30 @@ module Groupdate
213
227
  relation
214
228
  end
215
229
 
230
+ class << self
231
+ # basic version of Active Record disallow_raw_sql!
232
+ # symbol = column (safe), Arel node = SQL (safe), other = untrusted
233
+ # matches table.column and column
234
+ def validate_column(column)
235
+ unless column.is_a?(Symbol) || column.is_a?(Arel::Nodes::SqlLiteral)
236
+ column = column.to_s
237
+ unless /\A\w+(\.\w+)?\z/i.match(column)
238
+ raise ActiveRecord::UnknownAttributeReference, "Query method called with non-attribute argument(s): #{column.inspect}. Use Arel.sql() for known-safe values."
239
+ end
240
+ end
241
+ column
242
+ end
243
+
244
+ # resolves eagerly
245
+ # need to convert both where_clause (easy)
246
+ # and group_clause (not easy) if want to avoid this
247
+ def resolve_column(relation, column)
248
+ node = relation.send(:relation).send(:arel_columns, [column]).first
249
+ node = Arel::Nodes::SqlLiteral.new(node) if node.is_a?(String)
250
+ relation.connection.visitor.accept(node, Arel::Collectors::SQLString.new).value
251
+ end
252
+ end
253
+
216
254
  # allow any options to keep flexible for future
217
255
  def self.process_result(relation, result, **options)
218
256
  relation.groupdate_values.reverse.each do |gv|
@@ -9,6 +9,9 @@ module Groupdate
9
9
  end
10
10
 
11
11
  def calculate(*args, &block)
12
+ # prevent calculate from being called twice
13
+ return super if ActiveRecord::VERSION::STRING.to_f >= 6.1 && has_include?(args[1])
14
+
12
15
  default_value = [:count, :sum].include?(args[0]) ? 0 : nil
13
16
  Groupdate.process_result(self, super, default_value: default_value)
14
17
  end
@@ -2,8 +2,6 @@ module Groupdate
2
2
  class SeriesBuilder
3
3
  attr_reader :period, :time_zone, :day_start, :week_start, :n_seconds, :options
4
4
 
5
- CHECK_PERIODS = [:day, :week, :month, :quarter, :year]
6
-
7
5
  def initialize(period:, time_zone:, day_start:, week_start:, n_seconds:, **options)
8
6
  @period = period
9
7
  @time_zone = time_zone
@@ -23,41 +21,12 @@ module Groupdate
23
21
  verified_data[k] = data.delete(k)
24
22
  end
25
23
 
26
- # this is a fun one
27
- # PostgreSQL and Ruby both return the 2nd hour when converting/parsing a backward DST change
28
- # Other databases and Active Support return the 1st hour (as expected)
29
- # Active Support good: ActiveSupport::TimeZone["America/Los_Angeles"].parse("2013-11-03 01:00:00")
30
- # MySQL good: SELECT CONVERT_TZ('2013-11-03 01:00:00', 'America/Los_Angeles', 'Etc/UTC');
31
- # Ruby not good: Time.parse("2013-11-03 01:00:00")
32
- # PostgreSQL not good: SELECT '2013-11-03 01:00:00'::timestamp AT TIME ZONE 'America/Los_Angeles';
33
- # we need to account for this here
34
- if series_default && CHECK_PERIODS.include?(period)
35
- data.each do |k, v|
36
- key = multiple_groups ? k[group_index] : k
37
- # TODO only do this for PostgreSQL
38
- # this may mask some inconsistent time zone errors
39
- # but not sure there's a better approach
40
- if key.hour == (key - 1.hour).hour && series.include?(key - 1.hour)
41
- key -= 1.hour
42
- if multiple_groups
43
- k[group_index] = key
44
- else
45
- k = key
46
- end
47
- verified_data[k] = v
48
- elsif key != round_time(key)
49
- # only need to show what database returned since it will cast in Ruby time zone
50
- raise Groupdate::Error, "Database and Ruby have inconsistent time zone info. Database returned #{key}"
51
- end
52
- end
53
- end
54
-
55
24
  unless entire_series?(series_default)
56
25
  series = series.select { |k| verified_data[k] }
57
26
  end
58
27
 
59
28
  value = 0
60
- result = Hash[series.map do |k|
29
+ result = series.to_h do |k|
61
30
  value = verified_data[k] || (@options[:carry_forward] && value) || default_value
62
31
  key =
63
32
  if multiple_groups
@@ -67,7 +36,7 @@ module Groupdate
67
36
  end
68
37
 
69
38
  [key, value]
70
- end]
39
+ end
71
40
 
72
41
  result
73
42
  end
@@ -81,7 +50,7 @@ module Groupdate
81
50
 
82
51
  if day_start != 0
83
52
  # apply day_start to a time object that's not affected by DST
84
- time = change_zone.call(time, utc)
53
+ time = time.change(zone: utc)
85
54
  time -= day_start.seconds
86
55
  end
87
56
 
@@ -121,23 +90,12 @@ module Groupdate
121
90
 
122
91
  if day_start != 0 && time.is_a?(Time)
123
92
  time += day_start.seconds
124
- time = change_zone.call(time, time_zone)
93
+ time = time.change(zone: time_zone)
125
94
  end
126
95
 
127
96
  time
128
97
  end
129
98
 
130
- def change_zone
131
- @change_zone ||= begin
132
- if ActiveSupport::VERSION::STRING >= "5.2"
133
- ->(time, zone) { time.change(zone: zone) }
134
- else
135
- # TODO make more efficient
136
- ->(time, zone) { zone.parse(time.strftime("%Y-%m-%d %H:%M:%S")) }
137
- end
138
- end
139
- end
140
-
141
99
  def time_range
142
100
  @time_range ||= begin
143
101
  time_range = options[:range]
@@ -148,10 +106,6 @@ module Groupdate
148
106
  case v
149
107
  when nil, Date, Time
150
108
  # good
151
- when String
152
- # TODO raise error in Groupdate 6
153
- warn "[groupdate] Range bounds should be Date or Time, not #{v.class.name}. This will raise an error in Groupdate 6"
154
- break
155
109
  else
156
110
  raise ArgumentError, "Range bounds should be Date or Time, not #{v.class.name}"
157
111
  end
@@ -289,7 +243,6 @@ module Groupdate
289
243
  def key_format
290
244
  @key_format ||= begin
291
245
  locale = options[:locale] || I18n.locale
292
- use_dates = options.key?(:dates) ? options[:dates] : Groupdate.dates
293
246
 
294
247
  if options[:format]
295
248
  if options[:format].respond_to?(:call)
@@ -312,7 +265,7 @@ module Groupdate
312
265
  I18n.localize(key, format: options[:format], locale: locale)
313
266
  end
314
267
  end
315
- elsif [:day, :week, :month, :quarter, :year].include?(period) && use_dates
268
+ elsif [:day, :week, :month, :quarter, :year].include?(period)
316
269
  lambda { |k| k.to_date }
317
270
  else
318
271
  lambda { |k| k }
@@ -1,3 +1,3 @@
1
1
  module Groupdate
2
- VERSION = "5.2.4"
2
+ VERSION = "6.0.1"
3
3
  end
data/lib/groupdate.rb CHANGED
@@ -12,7 +12,6 @@ require "groupdate/version"
12
12
  require "groupdate/adapters/base_adapter"
13
13
  require "groupdate/adapters/mysql_adapter"
14
14
  require "groupdate/adapters/postgresql_adapter"
15
- require "groupdate/adapters/redshift_adapter"
16
15
  require "groupdate/adapters/sqlite_adapter"
17
16
 
18
17
  module Groupdate
@@ -21,10 +20,9 @@ module Groupdate
21
20
  PERIODS = [:second, :minute, :hour, :day, :week, :month, :quarter, :year, :day_of_week, :hour_of_day, :minute_of_hour, :day_of_month, :day_of_year, :month_of_year]
22
21
  METHODS = PERIODS.map { |v| :"group_by_#{v}" } + [:group_by_period]
23
22
 
24
- mattr_accessor :week_start, :day_start, :time_zone, :dates
23
+ mattr_accessor :week_start, :day_start, :time_zone
25
24
  self.week_start = :sunday
26
25
  self.day_start = 0
27
- self.dates = true
28
26
 
29
27
  # api for gems like ActiveMedian
30
28
  def self.process_result(relation, result, **options)
@@ -46,8 +44,7 @@ module Groupdate
46
44
  end
47
45
 
48
46
  Groupdate.register_adapter ["Mysql2", "Mysql2Spatial", "Mysql2Rgeo"], Groupdate::Adapters::MySQLAdapter
49
- Groupdate.register_adapter ["PostgreSQL", "PostGIS"], Groupdate::Adapters::PostgreSQLAdapter
50
- Groupdate.register_adapter "Redshift", Groupdate::Adapters::RedshiftAdapter
47
+ Groupdate.register_adapter ["PostgreSQL", "PostGIS", "Redshift"], Groupdate::Adapters::PostgreSQLAdapter
51
48
  Groupdate.register_adapter "SQLite", Groupdate::Adapters::SQLiteAdapter
52
49
 
53
50
  require "groupdate/enumerable"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: groupdate
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.4
4
+ version: 6.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-16 00:00:00.000000000 Z
11
+ date: 2022-01-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '5'
19
+ version: '5.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '5'
26
+ version: '5.2'
27
27
  description:
28
28
  email: andrew@ankane.org
29
29
  executables: []
@@ -39,7 +39,6 @@ files:
39
39
  - lib/groupdate/adapters/base_adapter.rb
40
40
  - lib/groupdate/adapters/mysql_adapter.rb
41
41
  - lib/groupdate/adapters/postgresql_adapter.rb
42
- - lib/groupdate/adapters/redshift_adapter.rb
43
42
  - lib/groupdate/adapters/sqlite_adapter.rb
44
43
  - lib/groupdate/enumerable.rb
45
44
  - lib/groupdate/magic.rb
@@ -59,7 +58,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
59
58
  requirements:
60
59
  - - ">="
61
60
  - !ruby/object:Gem::Version
62
- version: '2.4'
61
+ version: '2.6'
63
62
  required_rubygems_version: !ruby/object:Gem::Requirement
64
63
  requirements:
65
64
  - - ">="
@@ -1,39 +0,0 @@
1
- module Groupdate
2
- module Adapters
3
- class RedshiftAdapter < BaseAdapter
4
- def group_clause
5
- time_zone = @time_zone.tzinfo.name
6
- day_start_column = "CONVERT_TIMEZONE(?, #{column}::timestamp) - INTERVAL ?"
7
- day_start_interval = "#{day_start} second"
8
-
9
- query =
10
- case period
11
- when :minute_of_hour
12
- ["EXTRACT(MINUTE from #{day_start_column})::integer", time_zone, day_start_interval]
13
- when :hour_of_day
14
- ["EXTRACT(HOUR from #{day_start_column})::integer", time_zone, day_start_interval]
15
- when :day_of_week
16
- ["EXTRACT(DOW from #{day_start_column})::integer", time_zone, day_start_interval]
17
- when :day_of_month
18
- ["EXTRACT(DAY from #{day_start_column})::integer", time_zone, day_start_interval]
19
- when :day_of_year
20
- ["EXTRACT(DOY from #{day_start_column})::integer", time_zone, day_start_interval]
21
- when :month_of_year
22
- ["EXTRACT(MONTH from #{day_start_column})::integer", time_zone, day_start_interval]
23
- when :week # start on Sunday, not Redshift default Monday
24
- # Redshift does not return timezone information; it
25
- # always says it is in UTC time, so we must convert
26
- # back to UTC to play properly with the rest of Groupdate.
27
- week_start_interval = "#{week_start} day"
28
- ["CONVERT_TIMEZONE(?, 'Etc/UTC', DATE_TRUNC('week', #{day_start_column} - INTERVAL ?) + INTERVAL ? + INTERVAL ?)::timestamp", time_zone, time_zone, day_start_interval, week_start_interval, week_start_interval, day_start_interval]
29
- when :custom
30
- raise Groupdate::Error, "Not implemented yet"
31
- else
32
- ["CONVERT_TIMEZONE(?, 'Etc/UTC', DATE_TRUNC(?, #{day_start_column}) + INTERVAL ?)::timestamp", time_zone, period, time_zone, day_start_interval, day_start_interval]
33
- end
34
-
35
- @relation.send(:sanitize_sql_array, query)
36
- end
37
- end
38
- end
39
- end