groupdate 3.1.1 → 3.2.0
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 +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +25 -7
- data/Rakefile +1 -0
- data/groupdate.gemspec +3 -1
- data/lib/groupdate.rb +2 -0
- data/lib/groupdate/magic.rb +49 -13
- data/lib/groupdate/version.rb +1 -1
- data/test/enumerable_test.rb +1 -1
- data/test/gemfiles/activerecord31.gemfile +1 -1
- data/test/gemfiles/activerecord32.gemfile +1 -1
- data/test/gemfiles/activerecord40.gemfile +1 -1
- data/test/gemfiles/activerecord41.gemfile +1 -1
- data/test/gemfiles/activerecord42.gemfile +1 -1
- data/test/gemfiles/redshift.gemfile +1 -1
- data/test/mysql_test.rb +1 -1
- data/test/postgresql_test.rb +1 -1
- data/test/redshift_test.rb +1 -1
- data/test/sqlite_test.rb +29 -0
- data/test/test_helper.rb +31 -10
- metadata +20 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df818b929dceeddfccab948ceb591ad26c7334c8
|
4
|
+
data.tar.gz: 370a704d7ff6a17fb82bb057d717141ed87bc69f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7a77d1af0c530bfab9552ddff8f7149575c9821bcff772d33454b50c0050561d861a7eb5f220c5aa19200f80194f07c6a550c412be2ac76eedd88dbddb143faf
|
7
|
+
data.tar.gz: 91d207a5493bcafe51e94a884809e37b8250e9c2e581bf26172a5d0810d32c64aa63f21aead7935d1096c4d27dcde4c8d49cc8de821cdae4c83634e6a168cf94
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -13,6 +13,8 @@ The simplest way to group by:
|
|
13
13
|
|
14
14
|
Supports PostgreSQL, MySQL, and Redshift, plus arrays and hashes
|
15
15
|
|
16
|
+
Limited support for [SQLite](#for-sqlite)
|
17
|
+
|
16
18
|
[](https://travis-ci.org/ankane/groupdate)
|
17
19
|
|
18
20
|
:cupid: Goes hand in hand with [Chartkick](http://ankane.github.io/chartkick/)
|
@@ -126,13 +128,7 @@ User.group_by_week(:created_at, last: 8, current: false).count
|
|
126
128
|
You can order in descending order with:
|
127
129
|
|
128
130
|
```ruby
|
129
|
-
User.group_by_day(:created_at).
|
130
|
-
```
|
131
|
-
|
132
|
-
or
|
133
|
-
|
134
|
-
```ruby
|
135
|
-
User.group_by_day(:created_at).order("day desc").count
|
131
|
+
User.group_by_day(:created_at, reverse: true).count
|
136
132
|
```
|
137
133
|
|
138
134
|
### Keys
|
@@ -234,6 +230,28 @@ mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql
|
|
234
230
|
|
235
231
|
or copy and paste [these statements](https://gist.githubusercontent.com/ankane/1d6b0022173186accbf0/raw/time_zone_support.sql) into a SQL console.
|
236
232
|
|
233
|
+
You can confirm it worked with:
|
234
|
+
|
235
|
+
```sql
|
236
|
+
SELECT CONVERT_TZ(NOW(), '+00:00', 'Etc/UTC');
|
237
|
+
```
|
238
|
+
|
239
|
+
It should return the time instead of `NULL`.
|
240
|
+
|
241
|
+
#### For SQLite
|
242
|
+
|
243
|
+
Groupdate has limited support for SQLite.
|
244
|
+
|
245
|
+
- No time zone support
|
246
|
+
- No `day_start` or `week_start` options
|
247
|
+
- No `group_by_quarter` method
|
248
|
+
|
249
|
+
If your application’s time zone is set to something other than `Etc/UTC` (the default), create an initializer with:
|
250
|
+
|
251
|
+
```ruby
|
252
|
+
Groupdate.time_zone = false
|
253
|
+
```
|
254
|
+
|
237
255
|
## Upgrading
|
238
256
|
|
239
257
|
### 3.0
|
data/Rakefile
CHANGED
data/groupdate.gemspec
CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |spec|
|
|
7
7
|
spec.name = "groupdate"
|
8
8
|
spec.version = Groupdate::VERSION
|
9
9
|
spec.authors = ["Andrew Kane"]
|
10
|
-
spec.email = ["
|
10
|
+
spec.email = ["andrew@chartkick.com"]
|
11
11
|
spec.description = "The simplest way to group temporal data"
|
12
12
|
spec.summary = "The simplest way to group temporal data"
|
13
13
|
spec.homepage = "https://github.com/ankane/groupdate"
|
@@ -28,8 +28,10 @@ Gem::Specification.new do |spec|
|
|
28
28
|
if RUBY_PLATFORM == "java"
|
29
29
|
spec.add_development_dependency "activerecord-jdbcpostgresql-adapter"
|
30
30
|
spec.add_development_dependency "activerecord-jdbcmysql-adapter"
|
31
|
+
spec.add_development_dependency "activerecord-jdbcsqlite3-adapter"
|
31
32
|
else
|
32
33
|
spec.add_development_dependency "pg"
|
33
34
|
spec.add_development_dependency "mysql2", "~> 0.3.20"
|
35
|
+
spec.add_development_dependency "sqlite3"
|
34
36
|
end
|
35
37
|
end
|
data/lib/groupdate.rb
CHANGED
@@ -4,6 +4,8 @@ require "groupdate/version"
|
|
4
4
|
require "groupdate/magic"
|
5
5
|
|
6
6
|
module Groupdate
|
7
|
+
class Error < RuntimeError; end
|
8
|
+
|
7
9
|
PERIODS = [:second, :minute, :hour, :day, :week, :month, :quarter, :year, :day_of_week, :hour_of_day, :day_of_month, :month_of_year]
|
8
10
|
# backwards compatibility for anyone who happened to use it
|
9
11
|
FIELDS = PERIODS
|
data/lib/groupdate/magic.rb
CHANGED
@@ -8,9 +8,9 @@ module Groupdate
|
|
8
8
|
@field = field
|
9
9
|
@options = options
|
10
10
|
|
11
|
-
raise "Unrecognized time zone" unless time_zone
|
11
|
+
raise Groupdate::Error, "Unrecognized time zone" unless time_zone
|
12
12
|
|
13
|
-
raise "Unrecognized :week_start option" if field == :week && !week_start
|
13
|
+
raise Groupdate::Error, "Unrecognized :week_start option" if field == :week && !week_start
|
14
14
|
end
|
15
15
|
|
16
16
|
def group_by(enum, &_block)
|
@@ -20,7 +20,7 @@ module Groupdate
|
|
20
20
|
|
21
21
|
def relation(column, relation)
|
22
22
|
if relation.default_timezone == :local
|
23
|
-
raise "ActiveRecord::Base.default_timezone must be :utc to use Groupdate"
|
23
|
+
raise Groupdate::Error, "ActiveRecord::Base.default_timezone must be :utc to use Groupdate"
|
24
24
|
end
|
25
25
|
|
26
26
|
time_zone = self.time_zone.tzinfo.name
|
@@ -77,6 +77,42 @@ module Groupdate
|
|
77
77
|
else
|
78
78
|
["(DATE_TRUNC('#{field}', (#{column}::timestamptz - INTERVAL '#{day_start} second') AT TIME ZONE ?) + INTERVAL '#{day_start} second') AT TIME ZONE ?", time_zone, time_zone]
|
79
79
|
end
|
80
|
+
when "SQLite"
|
81
|
+
raise Groupdate::Error, "Time zones not supported for SQLite" unless self.time_zone.utc_offset.zero?
|
82
|
+
raise Groupdate::Error, "day_start not supported for SQLite" unless day_start.zero?
|
83
|
+
raise Groupdate::Error, "week_start not supported for SQLite" unless week_start == 6
|
84
|
+
|
85
|
+
if field == :week
|
86
|
+
["strftime('%%Y-%%m-%%d 00:00:00 UTC', #{column}, '-6 days', 'weekday 0')"]
|
87
|
+
else
|
88
|
+
format =
|
89
|
+
case field
|
90
|
+
when :hour_of_day
|
91
|
+
"%H"
|
92
|
+
when :day_of_week
|
93
|
+
"%w"
|
94
|
+
when :day_of_month
|
95
|
+
"%d"
|
96
|
+
when :month_of_year
|
97
|
+
"%m"
|
98
|
+
when :second
|
99
|
+
"%Y-%m-%d %H:%M:%S UTC"
|
100
|
+
when :minute
|
101
|
+
"%Y-%m-%d %H:%M:00 UTC"
|
102
|
+
when :hour
|
103
|
+
"%Y-%m-%d %H:00:00 UTC"
|
104
|
+
when :day
|
105
|
+
"%Y-%m-%d 00:00:00 UTC"
|
106
|
+
when :month
|
107
|
+
"%Y-%m-01 00:00:00 UTC"
|
108
|
+
when :quarter
|
109
|
+
raise Groupdate::Error, "Quarter not supported for SQLite"
|
110
|
+
else # year
|
111
|
+
"%Y-01-01 00:00:00 UTC"
|
112
|
+
end
|
113
|
+
|
114
|
+
["strftime('#{format.gsub(/%/, '%%')}', #{column})"]
|
115
|
+
end
|
80
116
|
when "Redshift"
|
81
117
|
case field
|
82
118
|
when :day_of_week # Sunday = 0, Monday = 1, etc.
|
@@ -97,7 +133,7 @@ module Groupdate
|
|
97
133
|
["CONVERT_TIMEZONE(?, 'Etc/UTC', DATE_TRUNC(?, CONVERT_TIMEZONE(?, #{column}) - INTERVAL '#{day_start} second'))::timestamp + INTERVAL '#{day_start} second'", time_zone, field, time_zone]
|
98
134
|
end
|
99
135
|
else
|
100
|
-
raise "Connection adapter not supported: #{adapter_name}"
|
136
|
+
raise Groupdate::Error, "Connection adapter not supported: #{adapter_name}"
|
101
137
|
end
|
102
138
|
|
103
139
|
if adapter_name == "MySQL" && field == :week
|
@@ -144,14 +180,14 @@ module Groupdate
|
|
144
180
|
lambda { |k| (k.is_a?(String) || !k.respond_to?(:to_time) ? utc.parse(k.to_s) : k.to_time).in_time_zone(time_zone) }
|
145
181
|
end
|
146
182
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
183
|
+
result = relation.send(method, *args, &block)
|
184
|
+
missing_time_zone_support = multiple_groups ? (result.keys.first && result.keys.first[@group_index].nil?) : result.key?(nil)
|
185
|
+
if missing_time_zone_support
|
186
|
+
raise Groupdate::Error, "Be sure to install time zone support - https://github.com/ankane/groupdate#for-mysql"
|
187
|
+
end
|
188
|
+
result = Hash[result.map { |k, v| [multiple_groups ? k[0...@group_index] + [cast_method.call(k[@group_index])] + k[(@group_index + 1)..-1] : cast_method.call(k), v] }]
|
153
189
|
|
154
|
-
series(
|
190
|
+
series(result, (options.key?(:default_value) ? options[:default_value] : 0), multiple_groups, reverse)
|
155
191
|
end
|
156
192
|
|
157
193
|
protected
|
@@ -159,7 +195,7 @@ module Groupdate
|
|
159
195
|
def time_zone
|
160
196
|
@time_zone ||= begin
|
161
197
|
time_zone = "Etc/UTC" if options[:time_zone] == false
|
162
|
-
time_zone ||= options[:time_zone] || Groupdate.time_zone || Time.zone || "Etc/UTC"
|
198
|
+
time_zone ||= options[:time_zone] || Groupdate.time_zone || (Groupdate.time_zone == false && "Etc/UTC") || Time.zone || "Etc/UTC"
|
163
199
|
time_zone.is_a?(ActiveSupport::TimeZone) ? time_zone : ActiveSupport::TimeZone[time_zone]
|
164
200
|
end
|
165
201
|
end
|
@@ -349,7 +385,7 @@ module Groupdate
|
|
349
385
|
when :month_of_year
|
350
386
|
time.month
|
351
387
|
else
|
352
|
-
raise "Invalid field"
|
388
|
+
raise Groupdate::Error, "Invalid field"
|
353
389
|
end
|
354
390
|
|
355
391
|
time.is_a?(Time) ? time + day_start.seconds : time
|
data/lib/groupdate/version.rb
CHANGED
data/test/enumerable_test.rb
CHANGED
data/test/mysql_test.rb
CHANGED
data/test/postgresql_test.rb
CHANGED
data/test/redshift_test.rb
CHANGED
data/test/sqlite_test.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
|
3
|
+
class TestSqlite < Minitest::Test
|
4
|
+
include TestGroupdate
|
5
|
+
include TestDatabase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
super
|
9
|
+
@@setup ||= begin
|
10
|
+
ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
|
11
|
+
create_tables
|
12
|
+
true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_where_after
|
17
|
+
skip
|
18
|
+
end
|
19
|
+
|
20
|
+
def call_method(method, field, options)
|
21
|
+
if method == :quarter || options[:time_zone] || options[:day_start] || options[:week_start] || Groupdate.week_start != :sun || (Time.zone && options[:time_zone] != false)
|
22
|
+
error = assert_raises(Groupdate::Error) { super }
|
23
|
+
assert_includes error.message, "not supported for SQLite"
|
24
|
+
skip # after assertions
|
25
|
+
else
|
26
|
+
super
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -225,6 +225,21 @@ module TestDatabase
|
|
225
225
|
assert_equal expected, User.group_by_year(:created_at, last: 3).count
|
226
226
|
end
|
227
227
|
|
228
|
+
def test_last_date
|
229
|
+
Time.zone = pt
|
230
|
+
today = Date.today
|
231
|
+
create_user today.to_s
|
232
|
+
this_month = pt.parse(today.to_s).beginning_of_month
|
233
|
+
last_month = this_month - 1.month
|
234
|
+
expected = {
|
235
|
+
last_month.to_date => 0,
|
236
|
+
this_month.to_date => 1
|
237
|
+
}
|
238
|
+
assert_equal expected, call_method(:month, :created_on, last: 2)
|
239
|
+
ensure
|
240
|
+
Time.zone = nil
|
241
|
+
end
|
242
|
+
|
228
243
|
def test_last_hour_of_day
|
229
244
|
error = assert_raises(ArgumentError) { User.group_by_hour_of_day(:created_at, last: 3).count }
|
230
245
|
assert_equal "Cannot use last option with hour_of_day", error.message
|
@@ -241,13 +256,15 @@ module TestDatabase
|
|
241
256
|
end
|
242
257
|
|
243
258
|
def test_quarter_and_last
|
244
|
-
|
245
|
-
create_user
|
259
|
+
today = Date.today
|
260
|
+
create_user today.to_s
|
261
|
+
this_quarter = today.to_time.beginning_of_quarter
|
262
|
+
last_quarter = this_quarter - 3.months
|
246
263
|
expected = {
|
247
|
-
|
248
|
-
|
264
|
+
last_quarter.to_date => 0,
|
265
|
+
this_quarter.to_date => 1
|
249
266
|
}
|
250
|
-
assert_equal expected,
|
267
|
+
assert_equal expected, call_method(:quarter, :created_at, last: 2)
|
251
268
|
end
|
252
269
|
|
253
270
|
def test_format_locale
|
@@ -337,7 +354,7 @@ module TestDatabase
|
|
337
354
|
|
338
355
|
def test_default_timezone_local
|
339
356
|
User.default_timezone = :local
|
340
|
-
assert_raises(
|
357
|
+
assert_raises(Groupdate::Error) { User.group_by_day(:created_at).count }
|
341
358
|
ensure
|
342
359
|
User.default_timezone = :utc
|
343
360
|
end
|
@@ -352,7 +369,7 @@ module TestDatabase
|
|
352
369
|
Date.parse("2014-10-19") => 1,
|
353
370
|
Date.parse("2014-10-20") => 1
|
354
371
|
}
|
355
|
-
assert_equal expected,
|
372
|
+
assert_equal expected, call_method(:day, :created_at, time_zone: "Brasilia")
|
356
373
|
end
|
357
374
|
|
358
375
|
# carry_forward option
|
@@ -391,7 +408,7 @@ module TestDatabase
|
|
391
408
|
end
|
392
409
|
|
393
410
|
def test_using_listed_but_undefined_custom_calculation_method_raises_error
|
394
|
-
assert_raises(
|
411
|
+
assert_raises(NoMethodError) do
|
395
412
|
User.group_by_day(:created_at).undefined_calculation
|
396
413
|
end
|
397
414
|
end
|
@@ -1165,11 +1182,15 @@ module TestGroupdate
|
|
1165
1182
|
end
|
1166
1183
|
|
1167
1184
|
def this_quarters_month
|
1168
|
-
Time.now.
|
1185
|
+
Time.now.beginning_of_quarter.month
|
1169
1186
|
end
|
1170
1187
|
|
1171
1188
|
def this_year
|
1172
|
-
Time.now.
|
1189
|
+
Time.now.year
|
1190
|
+
end
|
1191
|
+
|
1192
|
+
def this_month
|
1193
|
+
Time.now.month
|
1173
1194
|
end
|
1174
1195
|
|
1175
1196
|
def utc
|
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: 3.
|
4
|
+
version: 3.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -108,9 +108,23 @@ dependencies:
|
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: 0.3.20
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: sqlite3
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
111
125
|
description: The simplest way to group temporal data
|
112
126
|
email:
|
113
|
-
-
|
127
|
+
- andrew@chartkick.com
|
114
128
|
executables: []
|
115
129
|
extensions: []
|
116
130
|
extra_rdoc_files: []
|
@@ -142,6 +156,7 @@ files:
|
|
142
156
|
- test/mysql_test.rb
|
143
157
|
- test/postgresql_test.rb
|
144
158
|
- test/redshift_test.rb
|
159
|
+
- test/sqlite_test.rb
|
145
160
|
- test/test_helper.rb
|
146
161
|
homepage: https://github.com/ankane/groupdate
|
147
162
|
licenses:
|
@@ -163,7 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
163
178
|
version: '0'
|
164
179
|
requirements: []
|
165
180
|
rubyforge_project:
|
166
|
-
rubygems_version: 2.
|
181
|
+
rubygems_version: 2.6.8
|
167
182
|
signing_key:
|
168
183
|
specification_version: 4
|
169
184
|
summary: The simplest way to group temporal data
|
@@ -178,4 +193,5 @@ test_files:
|
|
178
193
|
- test/mysql_test.rb
|
179
194
|
- test/postgresql_test.rb
|
180
195
|
- test/redshift_test.rb
|
196
|
+
- test/sqlite_test.rb
|
181
197
|
- test/test_helper.rb
|