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 +4 -4
- data/CHANGELOG.md +58 -34
- data/CONTRIBUTING.md +1 -1
- data/LICENSE.txt +1 -1
- data/README.md +43 -9
- data/lib/groupdate/active_record.rb +1 -0
- data/lib/groupdate/enumerable.rb +9 -15
- data/lib/groupdate/magic.rb +66 -8
- data/lib/groupdate/query_methods.rb +3 -10
- data/lib/groupdate/relation.rb +7 -0
- data/lib/groupdate/relation_builder.rb +64 -48
- data/lib/groupdate/series_builder.rb +107 -62
- data/lib/groupdate/sql_server_group_clause.rb +3 -3
- data/lib/groupdate/version.rb +1 -1
- data/lib/groupdate2.rb +14 -4
- metadata +45 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7dfbcb0e43b31bb8e60084798e7b36b30fe7fb90
|
4
|
+
data.tar.gz: 0ac168fda29a6df9f1e28272ef80ee11c0029459
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b272dfa52234edaad18d44b78b129106b53a44ed79e577f593684428ba7b80972d78a48d99715ba52e0ecafab1e716b8cd318fd657293066ee91ad5f26f5c1cd
|
7
|
+
data.tar.gz: 53d31a3bd69f26d2cdb32617e8741c25a66991b29d1bca1544ec494991007b0ea5ed6b54040d0df49a32372958c647dd81975f54591c4da5856ee49bc7dd8456
|
data/CHANGELOG.md
CHANGED
@@ -1,31 +1,55 @@
|
|
1
|
-
##
|
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
|
data/CONTRIBUTING.md
CHANGED
@@ -57,7 +57,7 @@ brew services start mysql
|
|
57
57
|
|
58
58
|
# create databases
|
59
59
|
createdb groupdate_test
|
60
|
-
|
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
|
data/LICENSE.txt
CHANGED
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
|
-
|
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
|
-
|
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.
|
data/lib/groupdate/enumerable.rb
CHANGED
@@ -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
|
-
|
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.
|
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
|
-
|
17
|
-
options = args[1] || {}
|
17
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size + 1}, expected 1)" if args.any?
|
18
18
|
|
19
|
-
|
20
|
-
#
|
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.
|
22
|
+
scoping { @klass.group_by_period(period, *args, **options, &block) }
|
29
23
|
end
|
30
24
|
end
|
31
25
|
end
|
data/lib/groupdate/magic.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
15
|
-
|
16
|
-
raise
|
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 ||=
|
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
|