groupdate2 4.1.5 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1a798d7e082e32adf30f0fd07348f8fec6369006
4
- data.tar.gz: 6fc8861fc493d524bc29b8be51ef030887d38dd4
3
+ metadata.gz: 7dfbcb0e43b31bb8e60084798e7b36b30fe7fb90
4
+ data.tar.gz: 0ac168fda29a6df9f1e28272ef80ee11c0029459
5
5
  SHA512:
6
- metadata.gz: 3c3459a9aa602d466dac261078dcd3dda23481e57b9cf1cf644699353f18e03af3924d0fda5f45887a6fad83d715ea7a0fc07f5772b30b0d4c34159fa0092bd3
7
- data.tar.gz: fe47222def4bec06307c1930aff1c81b2bc6dd2819e9413d4df7e766345834d80271fa899e06a3f6655b53337e3864c68a32b2351f99432760f46731ec7b6c7d
6
+ metadata.gz: b272dfa52234edaad18d44b78b129106b53a44ed79e577f593684428ba7b80972d78a48d99715ba52e0ecafab1e716b8cd318fd657293066ee91ad5f26f5c1cd
7
+ data.tar.gz: 53d31a3bd69f26d2cdb32617e8741c25a66991b29d1bca1544ec494991007b0ea5ed6b54040d0df49a32372958c647dd81975f54591c4da5856ee49bc7dd8456
@@ -1,31 +1,55 @@
1
- ## 4.1.2
1
+ ## 5.0.0 (2020-02-18)
2
+
3
+ - Added support for `week_start` for SQLite
4
+ - Added support for full weekday names
5
+ - Made `day_start` behavior consistent between Active Record and enumerable
6
+ - Made `last` option extend to end of current period
7
+ - Raise error when `day_start` and `week_start` passed to unsupported methods
8
+ - The `day_start` option no longer applies to shorter periods
9
+ - Fixed `inconsistent time zone info` errors around DST with MySQL and PostgreSQL
10
+ - Improved performance of `format` option
11
+ - Removed deprecated positional arguments for time zone and range
12
+ - Dropped support for `mysql` gem (last release was 2013)
13
+
14
+ ## 4.3.0 (2019-12-26)
15
+
16
+ - Fixed error with empty results in Ruby 2.7
17
+ - Fixed deprecation warnings in Ruby 2.7
18
+ - Deprecated positional arguments for time zone and range
19
+
20
+ ## 4.2.0 (2019-10-28)
21
+
22
+ - Added `day_of_year`
23
+ - Dropped support for Rails 4.2
24
+
25
+ ## 4.1.2 (2019-05-26)
2
26
 
3
27
  - Fixed error with empty data and `current: false`
4
28
  - Fixed error in time zone check for Rails < 5.2
5
29
  - Prevent infinite loop with endless ranges
6
30
 
7
- ## 4.1.1
31
+ ## 4.1.1 (2018-12-11)
8
32
 
9
33
  - Made column resolution consistent with `group`
10
34
  - Added support for `alias_attribute`
11
35
 
12
- ## 4.1.0
36
+ ## 4.1.0 (2018-11-04)
13
37
 
14
38
  - Many performance improvements
15
39
  - Added check for consistent time zone info
16
40
  - Fixed error message for invalid queries with MySQL and SQLite
17
41
  - Fixed issue with enumerable methods ignoring nils
18
42
 
19
- ## 4.0.2
43
+ ## 4.0.2 (2018-10-15)
20
44
 
21
45
  - Make `current` option work without `last`
22
46
  - Fixed default value for `maximum`, `minimum`, and `average` (periods with no results now return `nil` instead of `0`, pass `default_value: 0` for previous behavior)
23
47
 
24
- ## 4.0.1
48
+ ## 4.0.1 (2018-05-03)
25
49
 
26
50
  - Fixed incorrect range with `last` option near time change
27
51
 
28
- ## 4.0.0
52
+ ## 4.0.0 (2018-02-21)
29
53
 
30
54
  - Custom calculation methods are supported by default - `groupdate_calculation_methods` is no longer needed
31
55
 
@@ -37,37 +61,37 @@ Breaking changes
37
61
  - `week_start` now affects `day_of_week`
38
62
  - Removed support for `reverse_order` (was never supported in Rails 5)
39
63
 
40
- ## 3.2.1
64
+ ## 3.2.1 (2018-02-21)
41
65
 
42
66
  - Added `minute_of_hour`
43
67
  - Added support for `unscoped`
44
68
 
45
- ## 3.2.0
69
+ ## 3.2.0 (2017-01-30)
46
70
 
47
71
  - Added limited support for SQLite
48
72
 
49
- ## 3.1.1
73
+ ## 3.1.1 (2016-10-25)
50
74
 
51
75
  - Fixed `current: false`
52
76
  - Fixed `last` with `group_by_quarter`
53
77
  - Raise `ArgumentError` when `last` option is not supported
54
78
 
55
- ## 3.1.0
79
+ ## 3.1.0 (2016-10-22)
56
80
 
57
81
  - Better support for date columns with `time_zone: false`
58
82
  - Better date range handling for `range` option
59
83
 
60
- ## 3.0.2
84
+ ## 3.0.2 (2016-08-09)
61
85
 
62
86
  - Fixed `group_by_period` with associations
63
87
  - Fixed `week_start` option for enumerables
64
88
 
65
- ## 3.0.1
89
+ ## 3.0.1 (2016-07-13)
66
90
 
67
91
  - Added support for Redshift
68
92
  - Fix for infinite loop in certain cases for Rails 5
69
93
 
70
- ## 3.0.0
94
+ ## 3.0.0 (2016-05-30)
71
95
 
72
96
  Breaking changes
73
97
 
@@ -75,16 +99,16 @@ Breaking changes
75
99
  - Array and hash methods no longer return the entire series by default. Use `series: true` for the previous behavior.
76
100
  - The `series: false` option now returns the correct types and order, and plays nicely with other options.
77
101
 
78
- ## 2.5.3
102
+ ## 2.5.3 (2016-04-28)
79
103
 
80
104
  - All tests green with `mysql` gem
81
105
  - Added support for decimal day start
82
106
 
83
- ## 2.5.2
107
+ ## 2.5.2 (2016-02-16)
84
108
 
85
109
  - Added `dates` option to return dates for day, week, month, quarter, and year
86
110
 
87
- ## 2.5.1
111
+ ## 2.5.1 (2016-02-03)
88
112
 
89
113
  - Added `group_by_quarter`
90
114
  - Added `default_value` option
@@ -92,13 +116,13 @@ Breaking changes
92
116
  - Raise `ArgumentError` if no field specified
93
117
  - Added support for ActiveRecord 5 beta
94
118
 
95
- ## 2.5.0
119
+ ## 2.5.0 (2015-09-29)
96
120
 
97
121
  - Added `group_by_period` method
98
122
  - Added `current` option
99
123
  - Raise `ArgumentError` if no block given to enumerable
100
124
 
101
- ## 2.4.0
125
+ ## 2.4.0 (2014-12-28)
102
126
 
103
127
  - Added localization
104
128
  - Added `carry_forward` option
@@ -106,77 +130,77 @@ Breaking changes
106
130
  - Fixed issue w/ Brasilia Summer Time
107
131
  - Fixed issues w/ ActiveRecord 4.2
108
132
 
109
- ## 2.3.0
133
+ ## 2.3.0 (2014-08-31)
110
134
 
111
135
  - Raise error when ActiveRecord::Base.default_timezone is not `:utc`
112
136
  - Added `day_of_month`
113
137
  - Added `month_of_year`
114
138
  - Do not quote column name
115
139
 
116
- ## 2.2.1
140
+ ## 2.2.1 (2014-06-23)
117
141
 
118
142
  - Fixed ActiveRecord 3 associations
119
143
 
120
- ## 2.2.0
144
+ ## 2.2.0 (2014-06-22)
121
145
 
122
146
  - Added support for arrays and hashes
123
147
 
124
- ## 2.1.1
148
+ ## 2.1.1 (2014-05-17)
125
149
 
126
150
  - Fixed format option with multiple groups
127
151
  - Better error message if time zone support is missing for MySQL
128
152
 
129
- ## 2.1.0
153
+ ## 2.1.0 (2014-03-16)
130
154
 
131
155
  - Added last option
132
156
  - Added format option
133
157
 
134
- ## 2.0.4
158
+ ## 2.0.4 (2014-03-12)
135
159
 
136
160
  - Added multiple groups
137
161
  - Added order
138
162
  - Subsequent methods no longer modify relation
139
163
 
140
- ## 2.0.3
164
+ ## 2.0.3 (2014-03-11)
141
165
 
142
166
  - Implemented respond_to?
143
167
 
144
- ## 2.0.2
168
+ ## 2.0.2 (2014-03-11)
145
169
 
146
170
  - where, joins, and includes no longer need to be before the group_by method
147
171
 
148
- ## 2.0.1
172
+ ## 2.0.1 (2014-03-07)
149
173
 
150
174
  - Use time zone instead of UTC for results
151
175
 
152
- ## 2.0.0
176
+ ## 2.0.0 (2014-03-07)
153
177
 
154
178
  - Returns entire series by default
155
179
  - Added day_start option
156
180
  - Better interface
157
181
 
158
- ## 1.0.5
182
+ ## 1.0.5 (2014-03-06)
159
183
 
160
184
  - Added global time_zone option
161
185
 
162
- ## 1.0.4
186
+ ## 1.0.4 (2013-07-20)
163
187
 
164
188
  - Added global week_start option
165
189
  - Fixed bug with NULL values and series
166
190
 
167
- ## 1.0.3
191
+ ## 1.0.3 (2013-07-05)
168
192
 
169
193
  - Fixed deprecation warning when used with will_paginate
170
194
  - Fixed bug with DateTime series
171
195
 
172
- ## 1.0.2
196
+ ## 1.0.2 (2013-06-10)
173
197
 
174
198
  - Added :start option for custom week start for group_by_week
175
199
 
176
- ## 1.0.1
200
+ ## 1.0.1 (2013-06-03)
177
201
 
178
202
  - Fixed series for Rails < 3.2 and MySQL
179
203
 
180
- ## 1.0.0
204
+ ## 1.0.0 (2013-05-15)
181
205
 
182
206
  - First major release
@@ -57,7 +57,7 @@ brew services start mysql
57
57
 
58
58
  # create databases
59
59
  createdb groupdate_test
60
- mysql -u root -e "create database groupdate_test"
60
+ mysqladmin create groupdate_test
61
61
  mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql
62
62
 
63
63
  # clone the repo and run the tests
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013-2019 Andrew Kane
1
+ Copyright (c) 2013-2020 Andrew Kane
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,16 +1,50 @@
1
1
  # Groupdate2
2
- groupdate2 is an enhanced version of the beautiful [Groupdate](https://github.com/ankane/groupdate) gem.
3
- It adds MS SQL Server (2016 and above) support by using `(AT TIME ZONE)`.
2
+ groupdate2 is an enhanced version of the beautiful [Groupdate](https://github.com/ankane/groupdate) gem, it adds more features to Groupdate
3
+ - SQL Server 2016+ support
4
+ - series_label support
4
5
 
5
- In JRuby, it relies on `activerecord-sqlserver-adapter` `jdbc-mode` branch, which is not ideal. The plan is using `activerecord-jdbcsqlserver-adapter` when JRuby 9.2+ is supported.
6
-
7
- # Version
8
- ## 4.1.x
9
- This version corresponds to groupdate 4.1.2 with SQL Server support.
10
-
11
- ## Installation
6
+ # Installation
12
7
  Add this line to your application’s Gemfile:
13
8
  ```ruby
14
9
  gem 'groupdate2'
15
10
  ```
16
11
 
12
+ # Features
13
+ ## SQL Server 2016+
14
+ MS SQL Server (2016 and above) support by using (AT TIME ZONE).
15
+
16
+ ## series_label, an option to include `group clause` in selected result
17
+ ### User case
18
+ If you use groupdate not with ActiveRecord::Calculations, but manually selecting the calculation like:
19
+ ```sql
20
+ sql = "COUNT(*) AS count, AVG(delivery.volume/truck.capacity) AS percentage, MIN(collected_at) AS collected_at"
21
+ ```
22
+ Delivery.select(sql).group_by_day(:collected_at).to_a
23
+
24
+ It works well except the series label is not included in the result. I have to use MIN(collected_at) to have it, then convert it to the correct label in ruby code. It works but is cumbersome.
25
+
26
+ With this new option `series_label: collected_at_date`, it indicates the group clause should be included in the result and can be accessed by the method collected_at_date.
27
+
28
+ ### Notes
29
+ - !!! This option does NOT work with ActiveRecord::Calculations.
30
+ - The series_label respects other Groupdate options like :locale, :dates and :format
31
+
32
+ [See more options](https://github.com/ankane/groupdate)
33
+
34
+ ### An example
35
+ ```ruby
36
+ users = User.select('COUNT(*) AS total, AVG(age) as average_age').group_by_month(:created_at, series_label: :created_at_month)
37
+
38
+ #The returned result would have attributes:
39
+ users.first.created_at_month
40
+ users.first.total
41
+ users.first.average_age
42
+ ```
43
+
44
+ # Upgrading
45
+ ## 5.0.x
46
+ - SQL server support
47
+ - series_label support
48
+
49
+ ## 4.1.x
50
+ This version corresponds to groupdate 4.1.2 with SQL Server support.
@@ -4,3 +4,4 @@ require "groupdate/relation"
4
4
 
5
5
  ActiveRecord::Base.extend(Groupdate::QueryMethods)
6
6
  ActiveRecord::Relation.include(Groupdate::Relation)
7
+ ActiveRecord::Relation.prepend(Groupdate::RelationRecords)
@@ -1,31 +1,25 @@
1
1
  module Enumerable
2
2
  Groupdate::PERIODS.each do |period|
3
- define_method :"group_by_#{period}" do |*args, &block|
3
+ define_method :"group_by_#{period}" do |*args, **options, &block|
4
4
  if block
5
- Groupdate::Magic::Enumerable.group_by(self, period, args[0] || {}, &block)
5
+ raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0)" if args.any?
6
+ Groupdate::Magic::Enumerable.group_by(self, period, options, &block)
6
7
  elsif respond_to?(:scoping)
7
- scoping { @klass.send(:"group_by_#{period}", *args, &block) }
8
+ scoping { @klass.group_by_period(period, *args, **options, &block) }
8
9
  else
9
10
  raise ArgumentError, "no block given"
10
11
  end
11
12
  end
12
13
  end
13
14
 
14
- def group_by_period(*args, &block)
15
+ def group_by_period(period, *args, **options, &block)
15
16
  if block || !respond_to?(:scoping)
16
- period = args[0]
17
- options = args[1] || {}
17
+ raise ArgumentError, "wrong number of arguments (given #{args.size + 1}, expected 1)" if args.any?
18
18
 
19
- options = options.dup
20
- # to_sym is unsafe on user input, so convert to strings
21
- permitted_periods = ((options.delete(:permit) || Groupdate::PERIODS).map(&:to_sym) & Groupdate::PERIODS).map(&:to_s)
22
- if permitted_periods.include?(period.to_s)
23
- send("group_by_#{period}", options, &block)
24
- else
25
- raise ArgumentError, "Unpermitted period"
26
- end
19
+ Groupdate::Magic.validate_period(period, options.delete(:permit))
20
+ send("group_by_#{period}", **options, &block)
27
21
  else
28
- scoping { @klass.send(:group_by_period, *args, &block) }
22
+ scoping { @klass.group_by_period(period, *args, **options, &block) }
29
23
  end
30
24
  end
31
25
  end
@@ -2,18 +2,42 @@ require "i18n"
2
2
 
3
3
  module Groupdate
4
4
  class Magic
5
+ DAYS = [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday]
6
+
5
7
  attr_accessor :period, :options, :group_index
6
8
 
7
9
  def initialize(period:, **options)
8
10
  @period = period
9
11
  @options = options
10
12
 
11
- unknown_keywords = options.keys - [:day_start, :time_zone, :dates, :series, :week_start, :format, :locale, :range, :reverse]
13
+ validate_keywords
14
+ validate_arguments
15
+ end
16
+
17
+ def validate_keywords
18
+ known_keywords = [:time_zone, :dates, :series, :format, :locale, :range, :reverse, :series_label]
19
+
20
+ if %i[week day_of_week].include?(period)
21
+ known_keywords << :week_start
22
+ end
23
+
24
+ if %i[day week month quarter year day_of_week hour_of_day day_of_month day_of_year month_of_year].include?(period)
25
+ known_keywords << :day_start
26
+ else
27
+ # prevent Groupdate.day_start from applying
28
+ @day_start = 0
29
+ end
30
+
31
+ unknown_keywords = options.keys - known_keywords
12
32
  raise ArgumentError, "unknown keywords: #{unknown_keywords.join(", ")}" if unknown_keywords.any?
33
+ end
13
34
 
14
- raise Groupdate::Error, "Unrecognized time zone" unless time_zone
15
- raise Groupdate::Error, "Unrecognized :week_start option" if period == :week && !week_start
16
- raise Groupdate::Error, "Cannot use endless range for :range option" if options[:range].is_a?(Range) && !options[:range].end
35
+ def validate_arguments
36
+ # TODO better messages
37
+ raise ArgumentError, "Unrecognized time zone" unless time_zone
38
+ raise ArgumentError, "Unrecognized :week_start option" unless week_start
39
+ raise ArgumentError, "Cannot use endless range for :range option" if options[:range].is_a?(Range) && !options[:range].end
40
+ raise ArgumentError, ":day_start must be between 0 and 24" if (day_start / 3600) < 0 || (day_start / 3600) >= 24
17
41
  end
18
42
 
19
43
  def time_zone
@@ -25,13 +49,20 @@ module Groupdate
25
49
  end
26
50
 
27
51
  def week_start
28
- @week_start ||= [:mon, :tue, :wed, :thu, :fri, :sat, :sun].index((options[:week_start] || options[:start] || Groupdate.week_start).to_sym)
52
+ @week_start ||= begin
53
+ v = (options[:week_start] || Groupdate.week_start).to_sym
54
+ DAYS.index(v) || [:mon, :tue, :wed, :thu, :fri, :sat, :sun].index(v)
55
+ end
29
56
  end
30
57
 
31
58
  def day_start
32
59
  @day_start ||= ((options[:day_start] || Groupdate.day_start).to_f * 3600).round
33
60
  end
34
61
 
62
+ def series_label
63
+ @series_label ||= (options[:series_label].present? ? options[:series_label] : nil)
64
+ end
65
+
35
66
  def series_builder
36
67
  @series_builder ||=
37
68
  SeriesBuilder.new(
@@ -47,6 +78,11 @@ module Groupdate
47
78
  series_builder.time_range
48
79
  end
49
80
 
81
+ def self.validate_period(period, permit)
82
+ permitted_periods = ((permit || Groupdate::PERIODS).map(&:to_sym) & Groupdate::PERIODS).map(&:to_s)
83
+ raise ArgumentError, "Unpermitted period" unless permitted_periods.include?(period.to_s)
84
+ end
85
+
50
86
  class Enumerable < Magic
51
87
  def group_by(enum, &_block)
52
88
  group = enum.group_by do |v|
@@ -85,10 +121,10 @@ module Groupdate
85
121
  def cast_method
86
122
  @cast_method ||= begin
87
123
  case period
124
+ when :minute_of_hour, :hour_of_day, :day_of_month, :day_of_year, :month_of_year
125
+ lambda { |k| k.to_i }
88
126
  when :day_of_week
89
127
  lambda { |k| (k.to_i - 1 - week_start) % 7 }
90
- when :hour_of_day, :day_of_month, :month_of_year, :minute_of_hour
91
- lambda { |k| k.to_i }
92
128
  else
93
129
  utc = ActiveSupport::TimeZone["UTC"]
94
130
  lambda { |k| (k.is_a?(String) || !k.respond_to?(:to_time) ? utc.parse(k.to_s) : k.to_time).in_time_zone(time_zone) }
@@ -130,6 +166,20 @@ module Groupdate
130
166
  end
131
167
  end
132
168
 
169
+ def perform_series_label(relation, result)
170
+ label = options[:series_label]
171
+ return result unless label.present?
172
+
173
+ result.map do |r|
174
+ r.send("#{label}=", cast_series_label(r.send(label)))
175
+ r
176
+ end
177
+ end
178
+
179
+ def cast_series_label(original_label)
180
+ series_builder.format_series_label(cast_method.call(original_label))
181
+ end
182
+
133
183
  def self.generate_relation(relation, field:, **options)
134
184
  magic = Groupdate::Magic::Relation.new(**options)
135
185
 
@@ -142,7 +192,8 @@ module Groupdate
142
192
  time_zone: magic.time_zone,
143
193
  time_range: magic.time_range,
144
194
  week_start: magic.week_start,
145
- day_start: magic.day_start
195
+ day_start: magic.day_start,
196
+ series_label: magic.series_label
146
197
  ).generate
147
198
 
148
199
  # add Groupdate info
@@ -159,6 +210,13 @@ module Groupdate
159
210
  end
160
211
  result
161
212
  end
213
+
214
+ def self.process_series_label(relation, result)
215
+ relation.groupdate_values.reverse.each do |gv|
216
+ result = gv.perform_series_label(relation, result)
217
+ end
218
+ result
219
+ end
162
220
  end
163
221
  end
164
222
  end