groupdate 5.1.0 → 5.2.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: 163a969c3eb174890b78b4512c6a6631e02722a43f1fbcc30926f3e48cdba461
4
- data.tar.gz: 256ee01a6e991c7513390e8cdf6d27b3e4458018e0980d63e9506bccfc6923d8
3
+ metadata.gz: 448e7901aa2448bca591568110960048d18ac0f21faa5c3cae566fc4a6de30c4
4
+ data.tar.gz: ba242e41103ae9d2542ff0509de1f454f58142957f0da190482769a42a152887
5
5
  SHA512:
6
- metadata.gz: 37093986846259dc59a17187af24097a2ed9d881de6a48ff9a394883e5a936a569fd758daf820cc9444e69212d29548ab21a816f7fac0cba1b7e502ac2d99198
7
- data.tar.gz: d1639375454ed0d1d33600226287a484ae7001c3759446b2c196dd24f1e003f3fce7f0c8f2253d90819bc280260bedfed2fa6f622f24ad46c590c05739177df2
6
+ metadata.gz: 91b008f8ee0d9ad419ec042ee2f417b621fba09793b223705a2898fe21083a09527885ead3be2d3b8003427473d1f996938098c9bd7139bcb9bffdad2810734c
7
+ data.tar.gz: d4eb493bdf4b7f40eda2733003263bee49e62f9f69fb6239bec118d1768ce0630c21244e819147ca6b5b1556a9b5e125ed69748e2ee69d3c325f31a2740a6ec5
@@ -1,3 +1,13 @@
1
+ ## 5.2.1 (2020-09-09)
2
+
3
+ - Improved error message for invalid ranges
4
+ - Fixed bug with date string ranges
5
+
6
+ ## 5.2.0 (2020-09-07)
7
+
8
+ - Added warning for non-attribute argument
9
+ - Added support for beginless and endless ranges in `range` option
10
+
1
11
  ## 5.1.0 (2020-07-30)
2
12
 
3
13
  - Added `n` option to minute and second for custom durations
data/README.md CHANGED
@@ -60,7 +60,7 @@ and
60
60
  - day_of_year
61
61
  - month_of_year
62
62
 
63
- Use it anywhere you can use `group`. Works with `count`, `sum`, `minimum`, `maximum`, and `average`. For `median`, check out [ActiveMedian](https://github.com/ankane/active_median).
63
+ Use it anywhere you can use `group`. Works with `count`, `sum`, `minimum`, `maximum`, and `average`. For `median` and `percentile`, check out [ActiveMedian](https://github.com/ankane/active_median).
64
64
 
65
65
  ### Time Zones
66
66
 
@@ -47,7 +47,6 @@ module Groupdate
47
47
  # TODO better messages
48
48
  raise ArgumentError, "Unrecognized time zone" unless time_zone
49
49
  raise ArgumentError, "Unrecognized :week_start option" unless week_start
50
- raise ArgumentError, "Cannot use endless range for :range option" if options[:range].is_a?(Range) && !options[:range].end
51
50
  raise ArgumentError, ":day_start must be between 0 and 24" if (day_start / 3600) < 0 || (day_start / 3600) >= 24
52
51
  end
53
52
 
@@ -3,6 +3,9 @@ module Groupdate
3
3
  attr_reader :period, :column, :day_start, :week_start, :n_seconds
4
4
 
5
5
  def initialize(relation, column:, period:, time_zone:, time_range:, week_start:, day_start:, n_seconds:)
6
+ # very important
7
+ validate_column(column)
8
+
6
9
  @relation = relation
7
10
  @column = resolve_column(relation, column)
8
11
  @period = period
@@ -188,13 +191,30 @@ module Groupdate
188
191
 
189
192
  def where_clause
190
193
  if @time_range.is_a?(Range)
191
- op = @time_range.exclude_end? ? "<" : "<="
192
- ["#{column} >= ? AND #{column} #{op} ?", @time_range.first, @time_range.last]
194
+ if @time_range.end
195
+ op = @time_range.exclude_end? ? "<" : "<="
196
+ if @time_range.begin
197
+ ["#{column} >= ? AND #{column} #{op} ?", @time_range.begin, @time_range.end]
198
+ else
199
+ ["#{column} #{op} ?", @time_range.end]
200
+ end
201
+ else
202
+ ["#{column} >= ?", @time_range.begin]
203
+ end
193
204
  else
194
205
  ["#{column} IS NOT NULL"]
195
206
  end
196
207
  end
197
208
 
209
+ # basic version of Active Record disallow_raw_sql!
210
+ # symbol = column (safe), Arel node = SQL (safe), other = untrusted
211
+ def validate_column(column)
212
+ # matches table.column and column
213
+ unless column.is_a?(Symbol) || column.is_a?(Arel::Nodes::SqlLiteral) || /\A\w+(\.\w+)?\z/i.match(column.to_s)
214
+ warn "[groupdate] Non-attribute argument: #{column}. Use Arel.sql() for known-safe values. This will raise an error in Groupdate 6"
215
+ end
216
+ end
217
+
198
218
  # resolves eagerly
199
219
  # need to convert both where_clause (easy)
200
220
  # and group_clause (not easy) if want to avoid this
@@ -11,7 +11,6 @@ module Groupdate
11
11
  @day_start = day_start
12
12
  @n_seconds = n_seconds
13
13
  @options = options
14
- @round_time = {}
15
14
  @week_start_key = Groupdate::Magic::DAYS[@week_start] if @week_start
16
15
  end
17
16
 
@@ -142,11 +141,35 @@ module Groupdate
142
141
  def time_range
143
142
  @time_range ||= begin
144
143
  time_range = options[:range]
145
- if time_range.is_a?(Range) && time_range.first.is_a?(Date)
146
- # convert range of dates to range of times
147
- last = time_range.last.in_time_zone(time_zone)
148
- last += 1.day unless time_range.exclude_end?
149
- time_range = Range.new(time_range.first.in_time_zone(time_zone), last, true)
144
+
145
+ if time_range.is_a?(Range)
146
+ # check types
147
+ [time_range.begin, time_range.end].each do |v|
148
+ case v
149
+ when nil, Date, Time
150
+ # 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
+ else
156
+ raise ArgumentError, "Range bounds should be Date or Time, not #{v.class.name}"
157
+ end
158
+ end
159
+
160
+ start = time_range.begin
161
+ start = start.in_time_zone(time_zone) if start
162
+
163
+ exclude_end = time_range.exclude_end?
164
+
165
+ finish = time_range.end
166
+ finish = finish.in_time_zone(time_zone) if finish
167
+ if time_range.end.is_a?(Date) && !exclude_end
168
+ finish += 1.day
169
+ exclude_end = true
170
+ end
171
+
172
+ time_range = Range.new(start, finish, exclude_end)
150
173
  elsif !time_range && options[:last]
151
174
  if period == :quarter
152
175
  step = 3.months
@@ -200,7 +223,7 @@ module Groupdate
200
223
  else
201
224
  time_range = self.time_range
202
225
  time_range =
203
- if time_range.is_a?(Range)
226
+ if time_range.is_a?(Range) && time_range.begin && time_range.end
204
227
  time_range
205
228
  else
206
229
  # use first and last values
@@ -211,11 +234,23 @@ module Groupdate
211
234
  data.keys.sort
212
235
  end
213
236
 
214
- tr = sorted_keys.first..sorted_keys.last
215
- if options[:current] == false && sorted_keys.any? && round_time(now) >= tr.last
216
- tr = tr.first...round_time(now)
237
+ if time_range.is_a?(Range)
238
+ if sorted_keys.any?
239
+ if time_range.begin
240
+ time_range.begin..sorted_keys.last
241
+ else
242
+ Range.new(sorted_keys.first, time_range.end, time_range.exclude_end?)
243
+ end
244
+ else
245
+ nil..nil
246
+ end
247
+ else
248
+ tr = sorted_keys.first..sorted_keys.last
249
+ if options[:current] == false && sorted_keys.any? && round_time(now) >= tr.last
250
+ tr = tr.first...round_time(now)
251
+ end
252
+ tr
217
253
  end
218
- tr
219
254
  end
220
255
 
221
256
  if time_range.begin
@@ -1,3 +1,3 @@
1
1
  module Groupdate
2
- VERSION = "5.1.0"
2
+ VERSION = "5.2.1"
3
3
  end
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.1.0
4
+ version: 5.2.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: 2020-07-31 00:00:00.000000000 Z
11
+ date: 2020-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport