groupdate 1.0.5 → 2.0.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/.gitignore +2 -0
- data/CHANGELOG.md +6 -0
- data/README.md +53 -147
- data/lib/groupdate.rb +2 -2
- data/lib/groupdate/scopes.rb +17 -13
- data/lib/groupdate/series.rb +36 -28
- data/lib/groupdate/version.rb +1 -1
- data/test/mysql_test.rb +1 -1
- data/test/postgresql_test.rb +1 -1
- data/test/test_helper.rb +134 -26
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 00f2a25fe5b1a02e4b8a6e2b483e407ed4d5d76e
|
4
|
+
data.tar.gz: 5f6c81ee95f601a8ad189d706c53b5e57cf6d483
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3f980694ad4a61ae5a3915720f8e90a4d01fd62e865aa5db751f06c3fb4bc87b3b91f0681cf0d49ba39287b08aeb1ba7bbfe78b86d1fe3d8554b3f4754e6113
|
7
|
+
data.tar.gz: 6700771e7de2ad8e0153e58efaa417fcbc73e5fd8f55beaa5af5314fc54d46425ea2d9ecacbed8a28a71f49931fa64f717f657b51da26e9203c2ed2b3f99fae9
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -4,10 +4,8 @@ The simplest way to group by:
|
|
4
4
|
|
5
5
|
- day
|
6
6
|
- week
|
7
|
-
- month
|
8
|
-
- day of the week
|
9
7
|
- hour of the day
|
10
|
-
- and more (complete list
|
8
|
+
- and more (complete list below)
|
11
9
|
|
12
10
|
:tada: Time zones supported!! **the best part**
|
13
11
|
|
@@ -21,7 +19,9 @@ Supports PostgreSQL and MySQL
|
|
21
19
|
|
22
20
|
:cupid: Goes hand in hand with [Chartkick](http://ankane.github.io/chartkick/)
|
23
21
|
|
24
|
-
##
|
22
|
+
## Get Started
|
23
|
+
|
24
|
+
Group by day
|
25
25
|
|
26
26
|
```ruby
|
27
27
|
User.group_by_day(:created_at).count
|
@@ -30,141 +30,76 @@ User.group_by_day(:created_at).count
|
|
30
30
|
# 2013-04-17 00:00:00 UTC => 100,
|
31
31
|
# 2013-04-18 00:00:00 UTC => 34
|
32
32
|
# }
|
33
|
-
|
34
|
-
Task.group_by_month(:updated_at).count
|
35
|
-
# {
|
36
|
-
# 2013-02-01 00:00:00 UTC => 84,
|
37
|
-
# 2013-03-01 00:00:00 UTC => 23,
|
38
|
-
# 2013-04-01 00:00:00 UTC => 44
|
39
|
-
# }
|
40
|
-
|
41
|
-
Goal.group_by_year(:accomplished_at).count
|
42
|
-
# {
|
43
|
-
# 2011-01-01 00:00:00 UTC => 7,
|
44
|
-
# 2012-01-01 00:00:00 UTC => 11,
|
45
|
-
# 2013-01-01 00:00:00 UTC => 3
|
46
|
-
# }
|
47
33
|
```
|
48
34
|
|
49
|
-
|
35
|
+
Results are returned in ascending order, so no need to sort.
|
50
36
|
|
51
|
-
|
52
|
-
User.group_by_week(:created_at, "Pacific Time (US & Canada)").count
|
53
|
-
# {
|
54
|
-
# 2013-03-03 08:00:00 UTC => 80,
|
55
|
-
# 2013-03-10 08:00:00 UTC => 70,
|
56
|
-
# 2013-03-17 07:00:00 UTC => 54
|
57
|
-
# }
|
37
|
+
You can also group by:
|
58
38
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
39
|
+
- second
|
40
|
+
- minute
|
41
|
+
- hour
|
42
|
+
- week
|
43
|
+
- month
|
44
|
+
- year
|
63
45
|
|
64
|
-
|
46
|
+
and
|
65
47
|
|
66
|
-
|
67
|
-
|
48
|
+
- hour_of_day
|
49
|
+
- day_of_week (Sunday = 0, Monday = 1, etc)
|
50
|
+
|
51
|
+
### Time Zones
|
68
52
|
|
69
|
-
|
70
|
-
User.group_by_week(:created_at, time_zone, :start => :sat)
|
53
|
+
The default time zone is `Time.zone`. Change this with:
|
71
54
|
|
72
|
-
|
73
|
-
Groupdate.
|
55
|
+
```ruby
|
56
|
+
Groupdate.time_zone = "Pacific Time (US & Canada)"
|
74
57
|
```
|
75
58
|
|
76
|
-
|
59
|
+
or
|
77
60
|
|
78
61
|
```ruby
|
79
|
-
|
80
|
-
User.group_by_day_of_week(:created_at).count
|
62
|
+
User.group_by_week(:created_at, time_zone: "Pacific Time (US & Canada)").count
|
81
63
|
# {
|
82
|
-
#
|
83
|
-
#
|
84
|
-
#
|
85
|
-
# 6 => 3 # Saturday
|
86
|
-
# }
|
87
|
-
|
88
|
-
# hour of the day
|
89
|
-
User.group_by_hour_of_day(:created_at, "Pacific Time (US & Canada)").count
|
90
|
-
# {
|
91
|
-
# 0 => 34,
|
92
|
-
# 1 => 61,
|
93
|
-
# ...
|
94
|
-
# 23 => 12
|
64
|
+
# 2013-03-03 08:00:00 UTC => 80,
|
65
|
+
# 2013-03-10 08:00:00 UTC => 70,
|
66
|
+
# 2013-03-17 07:00:00 UTC => 54
|
95
67
|
# }
|
96
68
|
```
|
97
69
|
|
98
|
-
|
99
|
-
|
100
|
-
```ruby
|
101
|
-
User.group_by_day(:created_at).order("day asc").count
|
102
|
-
|
103
|
-
User.group_by_week(:created_at).order("week desc").count
|
70
|
+
Time zone objects also work.
|
104
71
|
|
105
|
-
|
106
|
-
```
|
72
|
+
### Week Start
|
107
73
|
|
108
|
-
|
74
|
+
Weeks start on Sunday by default. Change this with:
|
109
75
|
|
110
76
|
```ruby
|
111
|
-
|
77
|
+
Groupdate.week_start = :mon # first three letters of day
|
112
78
|
```
|
113
79
|
|
114
|
-
|
80
|
+
or
|
115
81
|
|
116
82
|
```ruby
|
117
|
-
|
83
|
+
User.group_by_week(:created_at, week_start: :mon).count
|
118
84
|
```
|
119
85
|
|
120
|
-
###
|
86
|
+
### Day Start
|
121
87
|
|
122
|
-
You
|
88
|
+
You can change the hour days start with:
|
123
89
|
|
124
90
|
```ruby
|
125
|
-
|
126
|
-
# {
|
127
|
-
# 2013-05-02 00:00:00 UTC => 1,
|
128
|
-
# 2013-05-05 00:00:00 UTC => 1
|
129
|
-
# }
|
91
|
+
Groupdate.day_start = 2 # 2 am - 2 am
|
130
92
|
```
|
131
93
|
|
132
|
-
|
94
|
+
or
|
133
95
|
|
134
96
|
```ruby
|
135
|
-
|
136
|
-
time_range = 6.days.ago..Time.now
|
137
|
-
|
138
|
-
User.group_by_day(:created_at, Time.zone, time_range).count
|
139
|
-
# {
|
140
|
-
# 2013-05-01 00:00:00 UTC => 0,
|
141
|
-
# 2013-05-02 00:00:00 UTC => 1,
|
142
|
-
# 2013-05-03 00:00:00 UTC => 0,
|
143
|
-
# 2013-05-04 00:00:00 UTC => 0,
|
144
|
-
# 2013-05-05 00:00:00 UTC => 1,
|
145
|
-
# 2013-05-06 00:00:00 UTC => 0,
|
146
|
-
# 2013-05-07 00:00:00 UTC => 0
|
147
|
-
# }
|
148
|
-
|
149
|
-
User.group_by_day_of_week(:created_at, Time.zone, time_range).count
|
150
|
-
# {
|
151
|
-
# 0 => 0,
|
152
|
-
# 1 => 1,
|
153
|
-
# 2 => 0,
|
154
|
-
# 3 => 0,
|
155
|
-
# 4 => 1,
|
156
|
-
# 5 => 0,
|
157
|
-
# 6 => 0
|
158
|
-
# }
|
97
|
+
User.group_by_day(:created_at, day_start: 2).count
|
159
98
|
```
|
160
99
|
|
161
|
-
Results are returned in ascending order, so no need to sort.
|
162
|
-
|
163
|
-
Also, this form of the method returns a Groupdate::Series instead of an ActiveRecord::Relation. ActiveRecord::Relation method calls (like `where` and `joins`) should come before this.
|
164
|
-
|
165
100
|
## Installation
|
166
101
|
|
167
|
-
Add this line to your application
|
102
|
+
Add this line to your application’s Gemfile:
|
168
103
|
|
169
104
|
```ruby
|
170
105
|
gem 'groupdate'
|
@@ -190,60 +125,31 @@ gem "activerecord-jdbcpostgresql-adapter", :github => "jruby/activerecord-jdbc-a
|
|
190
125
|
gem "activerecord-jdbcmysql-adapter", :github => "jruby/activerecord-jdbc-adapter"
|
191
126
|
```
|
192
127
|
|
193
|
-
##
|
194
|
-
|
195
|
-
The default time zone is `Time.zone`. To change this, use:
|
196
|
-
|
197
|
-
```ruby
|
198
|
-
Groupdate.time_zone = "Pacific Time (US & Canada)"
|
199
|
-
```
|
200
|
-
|
201
|
-
## Complete list
|
202
|
-
|
203
|
-
group_by_?
|
204
|
-
|
205
|
-
- second
|
206
|
-
- minute
|
207
|
-
- hour
|
208
|
-
- day
|
209
|
-
- week
|
210
|
-
- month
|
211
|
-
- year
|
212
|
-
- hour_of_day
|
213
|
-
- day_of_week
|
214
|
-
|
215
|
-
## Note
|
128
|
+
## Upgrading to 2.0
|
216
129
|
|
217
|
-
|
218
|
-
[This is fixed on activerecord master](https://github.com/rails/rails/commit/2cc09441c2de57b024b11ba666ba1e72c2b20cfe)
|
130
|
+
Groupdate 2.0 brings a number a great improvements.
|
219
131
|
|
220
|
-
|
221
|
-
|
132
|
+
- the entire series is returned by default
|
133
|
+
- the `day_start` option
|
134
|
+
- an improved interface
|
222
135
|
|
223
|
-
|
224
|
-
# pg and activerecord master
|
225
|
-
{2013-04-22 00:00:00 UTC => 1} # Time object
|
136
|
+
However, there are a few things to be aware of when upgrading.
|
226
137
|
|
227
|
-
|
228
|
-
{"2013-04-22 00:00:00+00" => 1} # String
|
229
|
-
```
|
230
|
-
|
231
|
-
Another data type inconsistency
|
138
|
+
1. Groupdate methods must come after any `where`, `joins`, or `includes`.
|
232
139
|
|
233
|
-
|
234
|
-
User.group_by_day_of_week(:created_at).count
|
140
|
+
Throws error
|
235
141
|
|
236
|
-
|
237
|
-
|
142
|
+
```ruby
|
143
|
+
User.group_by_day(:created_at).where(company_id: 1).count
|
144
|
+
```
|
238
145
|
|
239
|
-
|
240
|
-
{"0" => 1, "4" => 1} # String
|
146
|
+
:moneybag:
|
241
147
|
|
242
|
-
|
243
|
-
|
244
|
-
```
|
148
|
+
```ruby
|
149
|
+
User.where(company_id: 1).group_by_day(:created_at).count
|
150
|
+
```
|
245
151
|
|
246
|
-
|
152
|
+
2. `Time` keys are now returned for every database adapter. Some older adapters previously returned `String` keys.
|
247
153
|
|
248
154
|
## History
|
249
155
|
|
data/lib/groupdate.rb
CHANGED
data/lib/groupdate/scopes.rb
CHANGED
@@ -11,7 +11,7 @@ module Groupdate
|
|
11
11
|
args = args.dup
|
12
12
|
options = args[-1].is_a?(Hash) ? args.pop : {}
|
13
13
|
column = connection.quote_table_name(args[0])
|
14
|
-
time_zone = args[1] || Groupdate.time_zone || Time.zone || "Etc/UTC"
|
14
|
+
time_zone = args[1] || options[:time_zone] || Groupdate.time_zone || Time.zone || "Etc/UTC"
|
15
15
|
if time_zone.is_a?(ActiveSupport::TimeZone) or time_zone = ActiveSupport::TimeZone[time_zone]
|
16
16
|
time_zone = time_zone.tzinfo.name
|
17
17
|
else
|
@@ -19,22 +19,25 @@ module Groupdate
|
|
19
19
|
end
|
20
20
|
|
21
21
|
# for week
|
22
|
-
week_start = [:mon, :tue, :wed, :thu, :fri, :sat, :sun].index((options[:start] || Groupdate.week_start).to_sym)
|
22
|
+
week_start = [:mon, :tue, :wed, :thu, :fri, :sat, :sun].index((options[:week_start] || options[:start] || Groupdate.week_start).to_sym)
|
23
23
|
if field == "week" and !week_start
|
24
24
|
raise "Unrecognized :start option"
|
25
25
|
end
|
26
26
|
|
27
|
+
# for day
|
28
|
+
day_start = (options[:day_start] || Groupdate.day_start).to_i
|
29
|
+
|
27
30
|
query =
|
28
31
|
case connection.adapter_name
|
29
32
|
when "MySQL", "Mysql2"
|
30
33
|
case field
|
31
34
|
when "day_of_week" # Sunday = 0, Monday = 1, etc
|
32
35
|
# use CONCAT for consistent return type (String)
|
33
|
-
["DAYOFWEEK(CONVERT_TZ(#{column}, '+00:00', ?)) - 1", time_zone]
|
36
|
+
["DAYOFWEEK(CONVERT_TZ(DATE_SUB(#{column}, INTERVAL #{day_start} HOUR), '+00:00', ?)) - 1", time_zone]
|
34
37
|
when "hour_of_day"
|
35
|
-
["EXTRACT(HOUR from CONVERT_TZ(#{column}, '+00:00', ?))", time_zone]
|
38
|
+
["(EXTRACT(HOUR from CONVERT_TZ(#{column}, '+00:00', ?)) + 24 - #{day_start}) % 24", time_zone]
|
36
39
|
when "week"
|
37
|
-
["CONVERT_TZ(DATE_FORMAT(CONVERT_TZ(DATE_SUB(#{column}, INTERVAL ((#{7 - week_start} + WEEKDAY(CONVERT_TZ(#{column}, '+00:00', ?))) % 7) DAY), '+00:00', ?), '%Y-%m-%d 00:00:00'), ?, '+00:00')", time_zone, time_zone, time_zone]
|
40
|
+
["CONVERT_TZ(DATE_FORMAT(CONVERT_TZ(DATE_SUB(#{column}, INTERVAL ((#{7 - week_start} + WEEKDAY(CONVERT_TZ(#{column}, '+00:00', ?) - INTERVAL #{day_start} HOUR)) % 7) DAY) - INTERVAL #{day_start} HOUR, '+00:00', ?), '%Y-%m-%d 00:00:00') + INTERVAL #{day_start} HOUR, ?, '+00:00')", time_zone, time_zone, time_zone]
|
38
41
|
else
|
39
42
|
format =
|
40
43
|
case field
|
@@ -50,28 +53,29 @@ module Groupdate
|
|
50
53
|
"%Y-%m-01 00:00:00"
|
51
54
|
else # year
|
52
55
|
"%Y-01-01 00:00:00"
|
53
|
-
|
56
|
+
end
|
54
57
|
|
55
|
-
["CONVERT_TZ(DATE_FORMAT(CONVERT_TZ(#{column}, '+00:00', ?), '#{format}'), ?, '+00:00')", time_zone, time_zone]
|
58
|
+
["DATE_ADD(CONVERT_TZ(DATE_FORMAT(CONVERT_TZ(DATE_SUB(#{column}, INTERVAL #{day_start} HOUR), '+00:00', ?), '#{format}'), ?, '+00:00'), INTERVAL #{day_start} HOUR)", time_zone, time_zone]
|
56
59
|
end
|
57
60
|
when "PostgreSQL", "PostGIS"
|
58
61
|
case field
|
59
62
|
when "day_of_week"
|
60
|
-
["EXTRACT(DOW from #{column}::timestamptz AT TIME ZONE ?)::integer", time_zone]
|
63
|
+
["EXTRACT(DOW from (#{column}::timestamptz AT TIME ZONE ? - INTERVAL '#{day_start} hour'))::integer", time_zone]
|
61
64
|
when "hour_of_day"
|
62
|
-
["EXTRACT(HOUR from #{column}::timestamptz AT TIME ZONE ?)::integer", time_zone]
|
65
|
+
["EXTRACT(HOUR from #{column}::timestamptz AT TIME ZONE ? - INTERVAL '#{day_start} hour')::integer", time_zone]
|
63
66
|
when "week" # start on Sunday, not PostgreSQL default Monday
|
64
|
-
["(DATE_TRUNC('#{field}', (#{column}::timestamptz - INTERVAL '#{week_start} day') AT TIME ZONE ?) + INTERVAL '#{week_start} day') AT TIME ZONE ?", time_zone, time_zone]
|
67
|
+
["(DATE_TRUNC('#{field}', (#{column}::timestamptz - INTERVAL '#{week_start} day' - INTERVAL '#{day_start}' hour) AT TIME ZONE ?) + INTERVAL '#{week_start} day' + INTERVAL '#{day_start}' hour) AT TIME ZONE ?", time_zone, time_zone]
|
65
68
|
else
|
66
|
-
["DATE_TRUNC('#{field}', #{column}::timestamptz AT TIME ZONE ?) AT TIME ZONE ?", time_zone, time_zone]
|
69
|
+
["(DATE_TRUNC('#{field}', (#{column}::timestamptz - INTERVAL '#{day_start} hour') AT TIME ZONE ?) + INTERVAL '#{day_start} hour') AT TIME ZONE ?", time_zone, time_zone]
|
67
70
|
end
|
68
71
|
else
|
69
72
|
raise "Connection adapter not supported: #{connection.adapter_name}"
|
70
73
|
end
|
71
74
|
|
72
75
|
group = group(Groupdate::OrderHack.new(sanitize_sql_array(query), field, time_zone))
|
73
|
-
|
74
|
-
|
76
|
+
range = args[2] || options[:range] || true
|
77
|
+
unless options[:series] == false
|
78
|
+
Series.new(group, field, column, time_zone, range, week_start, day_start)
|
75
79
|
else
|
76
80
|
group
|
77
81
|
end
|
data/lib/groupdate/series.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Groupdate
|
2
2
|
class Series
|
3
3
|
|
4
|
-
def initialize(relation, field, column, time_zone, time_range, week_start)
|
4
|
+
def initialize(relation, field, column, time_zone, time_range, week_start, day_start)
|
5
5
|
if time_range.is_a?(Range)
|
6
6
|
# doesn't matter whether we include the end of a ... range - it will be excluded later
|
7
7
|
@relation = relation.where("#{column} >= ? AND #{column} <= ?", time_range.first, time_range.last)
|
@@ -12,6 +12,7 @@ module Groupdate
|
|
12
12
|
@time_zone = time_zone
|
13
13
|
@time_range = time_range
|
14
14
|
@week_start = week_start
|
15
|
+
@day_start = day_start
|
15
16
|
end
|
16
17
|
|
17
18
|
def build_series(count)
|
@@ -43,37 +44,42 @@ module Groupdate
|
|
43
44
|
sorted_keys.first..sorted_keys.last
|
44
45
|
end
|
45
46
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
47
|
+
if time_range.first
|
48
|
+
# determine start time
|
49
|
+
time = time_range.first.to_time.in_time_zone(@time_zone) - @day_start.hours
|
50
|
+
starts_at =
|
51
|
+
case @field
|
52
|
+
when "second"
|
53
|
+
time.change(:usec => 0)
|
54
|
+
when "minute"
|
55
|
+
time.change(:sec => 0)
|
56
|
+
when "hour"
|
57
|
+
time.change(:min => 0)
|
58
|
+
when "day"
|
59
|
+
time.beginning_of_day
|
60
|
+
when "week"
|
61
|
+
# same logic as MySQL group
|
62
|
+
weekday = (time.wday - 1) % 7
|
63
|
+
(time - ((7 - @week_start + weekday) % 7).days).midnight
|
64
|
+
when "month"
|
65
|
+
time.beginning_of_month
|
66
|
+
else # year
|
67
|
+
time.beginning_of_year
|
68
|
+
end
|
67
69
|
|
68
|
-
|
70
|
+
starts_at += @day_start.hours
|
71
|
+
series = [starts_at]
|
69
72
|
|
70
|
-
|
73
|
+
step = 1.send(@field)
|
71
74
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
+
while time_range.cover?(series.last + step)
|
76
|
+
series << series.last + step
|
77
|
+
end
|
75
78
|
|
76
|
-
|
79
|
+
series.map{|s| s.to_time.utc }
|
80
|
+
else
|
81
|
+
[]
|
82
|
+
end
|
77
83
|
end
|
78
84
|
|
79
85
|
Hash[series.map do |k|
|
@@ -85,6 +91,8 @@ module Groupdate
|
|
85
91
|
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation/calculations.rb
|
86
92
|
if ActiveRecord::Calculations.method_defined?(method)
|
87
93
|
build_series(@relation.send(method, *args, &block))
|
94
|
+
elsif [:joins, :includes, :where].include?(method)
|
95
|
+
raise NoMethodError, "#{method} must come before the group_by_#{@field} method"
|
88
96
|
else
|
89
97
|
raise NoMethodError, "valid methods are: #{ActiveRecord::Calculations.instance_methods.join(", ")}"
|
90
98
|
end
|
data/lib/groupdate/version.rb
CHANGED
data/test/mysql_test.rb
CHANGED
data/test/postgresql_test.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -84,6 +84,24 @@ module TestGroupdate
|
|
84
84
|
assert_result_time :day, "2013-05-03 00:00:00 PDT", "2013-05-03 07:00:00", true
|
85
85
|
end
|
86
86
|
|
87
|
+
# day hour starts at 2 am
|
88
|
+
|
89
|
+
def test_test_day_end_of_day_day_start_2am
|
90
|
+
assert_result_time :day, "2013-05-03 02:00:00 UTC", "2013-05-04 01:59:59", false, :day_start => 2
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_test_day_start_of_day_day_start_2am
|
94
|
+
assert_result_time :day, "2013-05-03 02:00:00 UTC", "2013-05-03 02:00:00", false, :day_start => 2
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_test_day_end_of_day_with_time_zone_day_start_2am
|
98
|
+
assert_result_time :day, "2013-05-03 02:00:00 PDT", "2013-05-04 07:59:59", true, :day_start => 2
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_test_day_start_of_day_with_time_zone_day_start_2am
|
102
|
+
assert_result_time :day, "2013-05-03 02:00:00 PDT", "2013-05-03 09:00:00", true, :day_start => 2
|
103
|
+
end
|
104
|
+
|
87
105
|
# week
|
88
106
|
|
89
107
|
def test_week_end_of_week
|
@@ -105,37 +123,55 @@ module TestGroupdate
|
|
105
123
|
# week starting on monday
|
106
124
|
|
107
125
|
def test_week_end_of_week_mon
|
108
|
-
assert_result_time :week, "2013-03-18 00:00:00 UTC", "2013-03-24 23:59:59", false, :
|
126
|
+
assert_result_time :week, "2013-03-18 00:00:00 UTC", "2013-03-24 23:59:59", false, week_start: :mon
|
109
127
|
end
|
110
128
|
|
111
129
|
def test_week_start_of_week_mon
|
112
|
-
assert_result_time :week, "2013-03-25 00:00:00 UTC", "2013-03-25 00:00:00", false, :
|
130
|
+
assert_result_time :week, "2013-03-25 00:00:00 UTC", "2013-03-25 00:00:00", false, week_start: :mon
|
113
131
|
end
|
114
132
|
|
115
133
|
def test_week_end_of_week_with_time_zone_mon
|
116
|
-
assert_result_time :week, "2013-03-11 00:00:00 PDT", "2013-03-18 06:59:59", true, :
|
134
|
+
assert_result_time :week, "2013-03-11 00:00:00 PDT", "2013-03-18 06:59:59", true, week_start: :mon
|
117
135
|
end
|
118
136
|
|
119
137
|
def test_week_start_of_week_with_time_zone_mon
|
120
|
-
assert_result_time :week, "2013-03-18 00:00:00 PDT", "2013-03-18 07:00:00", true, :
|
138
|
+
assert_result_time :week, "2013-03-18 00:00:00 PDT", "2013-03-18 07:00:00", true, week_start: :mon
|
121
139
|
end
|
122
140
|
|
123
141
|
# week starting on saturday
|
124
142
|
|
125
143
|
def test_week_end_of_week_sat
|
126
|
-
assert_result_time :week, "2013-03-16 00:00:00 UTC", "2013-03-22 23:59:59", false, :
|
144
|
+
assert_result_time :week, "2013-03-16 00:00:00 UTC", "2013-03-22 23:59:59", false, week_start: :sat
|
127
145
|
end
|
128
146
|
|
129
147
|
def test_week_start_of_week_sat
|
130
|
-
assert_result_time :week, "2013-03-23 00:00:00 UTC", "2013-03-23 00:00:00", false, :
|
148
|
+
assert_result_time :week, "2013-03-23 00:00:00 UTC", "2013-03-23 00:00:00", false, week_start: :sat
|
131
149
|
end
|
132
150
|
|
133
151
|
def test_week_end_of_week_with_time_zone_sat
|
134
|
-
assert_result_time :week, "2013-03-09 00:00:00 PST", "2013-03-16 06:59:59", true, :
|
152
|
+
assert_result_time :week, "2013-03-09 00:00:00 PST", "2013-03-16 06:59:59", true, week_start: :sat
|
135
153
|
end
|
136
154
|
|
137
155
|
def test_week_start_of_week_with_time_zone_sat
|
138
|
-
assert_result_time :week, "2013-03-16 00:00:00 PDT", "2013-03-16 07:00:00", true, :
|
156
|
+
assert_result_time :week, "2013-03-16 00:00:00 PDT", "2013-03-16 07:00:00", true, week_start: :sat
|
157
|
+
end
|
158
|
+
|
159
|
+
# week starting at 2am
|
160
|
+
|
161
|
+
def test_week_end_of_week_day_start_2am
|
162
|
+
assert_result_time :week, "2013-03-17 02:00:00 UTC", "2013-03-24 01:59:59", false, :day_start => 2
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_week_start_of_week_day_start_2am
|
166
|
+
assert_result_time :week, "2013-03-17 02:00:00 UTC", "2013-03-17 02:00:00", false, :day_start => 2
|
167
|
+
end
|
168
|
+
|
169
|
+
def test_week_end_of_week_day_with_time_zone_start_2am
|
170
|
+
assert_result_time :week, "2013-03-17 02:00:00 PDT", "2013-03-24 08:59:59", true, :day_start => 2
|
171
|
+
end
|
172
|
+
|
173
|
+
def test_week_start_of_week_day_with_time_zone_start_2am
|
174
|
+
assert_result_time :week, "2013-03-17 02:00:00 PDT", "2013-03-17 09:00:00", true, :day_start => 2
|
139
175
|
end
|
140
176
|
|
141
177
|
# month
|
@@ -156,6 +192,24 @@ module TestGroupdate
|
|
156
192
|
assert_result_time :month, "2013-06-01 00:00:00 PDT", "2013-06-01 07:00:00", true
|
157
193
|
end
|
158
194
|
|
195
|
+
# month starts at 2am
|
196
|
+
|
197
|
+
def test_month_end_of_month_day_start_2am
|
198
|
+
assert_result_time :month, "2013-03-01 02:00:00 UTC", "2013-04-01 01:59:59", false, :day_start => 2
|
199
|
+
end
|
200
|
+
|
201
|
+
def test_month_start_of_month_day_start_2am
|
202
|
+
assert_result_time :month, "2013-03-01 02:00:00 UTC", "2013-03-01 02:00:00", false, :day_start => 2
|
203
|
+
end
|
204
|
+
|
205
|
+
def test_month_end_of_month_with_time_zone_day_start_2am
|
206
|
+
assert_result_time :month, "2013-03-01 02:00:00 PST", "2013-04-01 08:59:59", true, :day_start => 2
|
207
|
+
end
|
208
|
+
|
209
|
+
def test_month_start_of_month_with_time_zone_day_start_2am
|
210
|
+
assert_result_time :month, "2013-03-01 02:00:00 PST", "2013-03-01 10:00:00", true, :day_start => 2
|
211
|
+
end
|
212
|
+
|
159
213
|
# year
|
160
214
|
|
161
215
|
def test_year_end_of_year
|
@@ -174,6 +228,24 @@ module TestGroupdate
|
|
174
228
|
assert_result_time :year, "2014-01-01 00:00:00 PST", "2014-01-01 08:00:00", true
|
175
229
|
end
|
176
230
|
|
231
|
+
# year starts at 2am
|
232
|
+
|
233
|
+
def test_year_end_of_year_day_start_2am
|
234
|
+
assert_result_time :year, "2013-01-01 02:00:00 UTC", "2014-01-01 01:59:59", false, :day_start => 2
|
235
|
+
end
|
236
|
+
|
237
|
+
def test_year_start_of_year_day_start_2am
|
238
|
+
assert_result_time :year, "2013-01-01 02:00:00 UTC", "2013-01-01 02:00:00", false, :day_start => 2
|
239
|
+
end
|
240
|
+
|
241
|
+
def test_year_end_of_year_with_time_zone_day_start_2am
|
242
|
+
assert_result_time :year, "2013-01-01 02:00:00 PST", "2014-01-01 09:59:59", true, :day_start => 2
|
243
|
+
end
|
244
|
+
|
245
|
+
def test_year_start_of_year_with_time_zone_day_start_2am
|
246
|
+
assert_result_time :year, "2013-01-01 02:00:00 PST", "2013-01-01 10:00:00", true, :day_start => 2
|
247
|
+
end
|
248
|
+
|
177
249
|
# hour of day
|
178
250
|
|
179
251
|
def test_hour_of_day_end_of_hour
|
@@ -192,6 +264,24 @@ module TestGroupdate
|
|
192
264
|
assert_result :hour_of_day, 1, "2013-01-01 09:00:00", true
|
193
265
|
end
|
194
266
|
|
267
|
+
# hour of day starts at 2am
|
268
|
+
|
269
|
+
def test_hour_of_day_end_of_day_day_start_2am
|
270
|
+
assert_result :hour_of_day, 23, "2013-01-01 01:59:59", false, :day_start => 2
|
271
|
+
end
|
272
|
+
|
273
|
+
def test_hour_of_day_start_of_day_day_start_2am
|
274
|
+
assert_result :hour_of_day, 0, "2013-01-01 02:00:00", false, :day_start => 2
|
275
|
+
end
|
276
|
+
|
277
|
+
def test_hour_of_day_end_of_day_with_time_zone_day_start_2am
|
278
|
+
assert_result :hour_of_day, 23, "2013-01-01 09:59:59", true, :day_start => 2
|
279
|
+
end
|
280
|
+
|
281
|
+
def test_hour_of_day_start_of_day_with_time_zone_day_start_2am
|
282
|
+
assert_result :hour_of_day, 0, "2013-01-01 10:00:00", true, :day_start => 2
|
283
|
+
end
|
284
|
+
|
195
285
|
# day of week
|
196
286
|
|
197
287
|
def test_day_of_week_end_of_day
|
@@ -210,6 +300,24 @@ module TestGroupdate
|
|
210
300
|
assert_result :day_of_week, 3, "2013-01-02 08:00:00", true
|
211
301
|
end
|
212
302
|
|
303
|
+
# day of week starts at 2am
|
304
|
+
|
305
|
+
def test_day_of_week_end_of_day_day_start_2am
|
306
|
+
assert_result :day_of_week, 3, "2013-01-03 01:59:59", false, :day_start => 2
|
307
|
+
end
|
308
|
+
|
309
|
+
def test_day_of_week_start_of_day_day_start_2am
|
310
|
+
assert_result :day_of_week, 3, "2013-01-02 02:00:00", false, :day_start => 2
|
311
|
+
end
|
312
|
+
|
313
|
+
def test_day_of_week_end_of_day_with_time_zone_day_start_2am
|
314
|
+
assert_result :day_of_week, 3, "2013-01-03 09:59:59", true, :day_start => 2
|
315
|
+
end
|
316
|
+
|
317
|
+
def test_day_of_week_start_of_day_with_time_zone_day_start_2am
|
318
|
+
assert_result :day_of_week, 3, "2013-01-02 10:00:00", true, :day_start => 2
|
319
|
+
end
|
320
|
+
|
213
321
|
# zeros
|
214
322
|
|
215
323
|
def test_zeros_second
|
@@ -241,19 +349,19 @@ module TestGroupdate
|
|
241
349
|
end
|
242
350
|
|
243
351
|
def test_zeros_week_mon
|
244
|
-
assert_zeros :week, "2013-05-01 20:00:00 UTC", ["2013-04-22 00:00:00 UTC", "2013-04-29 00:00:00 UTC", "2013-05-06 00:00:00 UTC"], "2013-04-27 23:59:59 UTC", "2013-05-11 23:59:59 UTC", false, :
|
352
|
+
assert_zeros :week, "2013-05-01 20:00:00 UTC", ["2013-04-22 00:00:00 UTC", "2013-04-29 00:00:00 UTC", "2013-05-06 00:00:00 UTC"], "2013-04-27 23:59:59 UTC", "2013-05-11 23:59:59 UTC", false, week_start: :mon
|
245
353
|
end
|
246
354
|
|
247
355
|
def test_zeros_week_time_zone_mon
|
248
|
-
assert_zeros :week, "2013-05-01 20:00:00 PDT", ["2013-04-22 00:00:00 PDT", "2013-04-29 00:00:00 PDT", "2013-05-06 00:00:00 PDT"], "2013-04-27 23:59:59 PDT", "2013-05-11 23:59:59 PDT", true, :
|
356
|
+
assert_zeros :week, "2013-05-01 20:00:00 PDT", ["2013-04-22 00:00:00 PDT", "2013-04-29 00:00:00 PDT", "2013-05-06 00:00:00 PDT"], "2013-04-27 23:59:59 PDT", "2013-05-11 23:59:59 PDT", true, week_start: :mon
|
249
357
|
end
|
250
358
|
|
251
359
|
def test_zeros_week_sat
|
252
|
-
assert_zeros :week, "2013-05-01 20:00:00 UTC", ["2013-04-20 00:00:00 UTC", "2013-04-27 00:00:00 UTC", "2013-05-04 00:00:00 UTC"], "2013-04-26 23:59:59 UTC", "2013-05-10 23:59:59 UTC", false, :
|
360
|
+
assert_zeros :week, "2013-05-01 20:00:00 UTC", ["2013-04-20 00:00:00 UTC", "2013-04-27 00:00:00 UTC", "2013-05-04 00:00:00 UTC"], "2013-04-26 23:59:59 UTC", "2013-05-10 23:59:59 UTC", false, week_start: :sat
|
253
361
|
end
|
254
362
|
|
255
363
|
def test_zeros_week_time_zone_sat
|
256
|
-
assert_zeros :week, "2013-05-01 20:00:00 PDT", ["2013-04-20 00:00:00 PDT", "2013-04-27 00:00:00 PDT", "2013-05-04 00:00:00 PDT"], "2013-04-26 23:59:59 PDT", "2013-05-10 23:59:59 PDT", true, :
|
364
|
+
assert_zeros :week, "2013-05-01 20:00:00 PDT", ["2013-04-20 00:00:00 PDT", "2013-04-27 00:00:00 PDT", "2013-05-04 00:00:00 PDT"], "2013-04-26 23:59:59 PDT", "2013-05-10 23:59:59 PDT", true, week_start: :sat
|
257
365
|
end
|
258
366
|
|
259
367
|
def test_zeros_month
|
@@ -278,7 +386,7 @@ module TestGroupdate
|
|
278
386
|
7.times do |n|
|
279
387
|
expected[n] = n == 3 ? 1 : 0
|
280
388
|
end
|
281
|
-
assert_equal expected, User.group_by_day_of_week(:created_at,
|
389
|
+
assert_equal expected, User.group_by_day_of_week(:created_at, range: true).count(:created_at)
|
282
390
|
end
|
283
391
|
|
284
392
|
def test_zeros_hour_of_day
|
@@ -287,7 +395,7 @@ module TestGroupdate
|
|
287
395
|
24.times do |n|
|
288
396
|
expected[n] = n == 20 ? 1 : 0
|
289
397
|
end
|
290
|
-
assert_equal expected, User.group_by_hour_of_day(:created_at,
|
398
|
+
assert_equal expected, User.group_by_hour_of_day(:created_at, range: true).count(:created_at)
|
291
399
|
end
|
292
400
|
|
293
401
|
def test_zeros_excludes_end
|
@@ -295,7 +403,7 @@ module TestGroupdate
|
|
295
403
|
expected = {
|
296
404
|
Time.parse("2013-05-01 00:00:00 UTC") => 0
|
297
405
|
}
|
298
|
-
assert_equal expected, User.group_by_day(:created_at,
|
406
|
+
assert_equal expected, User.group_by_day(:created_at, range: Time.parse("2013-05-01 00:00:00 UTC")...Time.parse("2013-05-02 00:00:00 UTC")).count
|
299
407
|
end
|
300
408
|
|
301
409
|
def test_zeros_previous_scope
|
@@ -303,7 +411,7 @@ module TestGroupdate
|
|
303
411
|
expected = {
|
304
412
|
Time.parse("2013-05-01 00:00:00 UTC") => 0
|
305
413
|
}
|
306
|
-
assert_equal expected, User.where("id = 0").group_by_day(:created_at,
|
414
|
+
assert_equal expected, User.where("id = 0").group_by_day(:created_at, range: Time.parse("2013-05-01 00:00:00 UTC")..Time.parse("2013-05-01 23:59:59 UTC")).count
|
307
415
|
end
|
308
416
|
|
309
417
|
def test_zeros_datetime
|
@@ -311,13 +419,13 @@ module TestGroupdate
|
|
311
419
|
expected = {
|
312
420
|
Time.parse("2013-05-01 00:00:00 UTC") => 1
|
313
421
|
}
|
314
|
-
assert_equal expected, User.group_by_day(:created_at,
|
422
|
+
assert_equal expected, User.group_by_day(:created_at, range: DateTime.parse("2013-05-01 00:00:00 UTC")..DateTime.parse("2013-05-01 00:00:00 UTC")).count
|
315
423
|
end
|
316
424
|
|
317
425
|
def test_zeros_null_value
|
318
426
|
user = User.create!(name: "Andrew")
|
319
427
|
user.update_column :created_at, nil
|
320
|
-
assert_equal 0, User.group_by_hour_of_day(:created_at,
|
428
|
+
assert_equal 0, User.group_by_hour_of_day(:created_at, range: true).count[0]
|
321
429
|
end
|
322
430
|
|
323
431
|
def test_zeroes_range_true
|
@@ -328,7 +436,7 @@ module TestGroupdate
|
|
328
436
|
Time.parse("2013-05-02 00:00:00 UTC") => 0,
|
329
437
|
Time.parse("2013-05-03 00:00:00 UTC") => 1
|
330
438
|
}
|
331
|
-
assert_equal expected, User.group_by_day(:created_at,
|
439
|
+
assert_equal expected, User.group_by_day(:created_at, range: true).count
|
332
440
|
end
|
333
441
|
|
334
442
|
# week_start
|
@@ -340,21 +448,21 @@ module TestGroupdate
|
|
340
448
|
|
341
449
|
def test_week_start_and_start_option
|
342
450
|
Groupdate.week_start = :mon
|
343
|
-
assert_result_time :week, "2013-03-16 00:00:00 UTC", "2013-03-22 23:59:59", false, :
|
451
|
+
assert_result_time :week, "2013-03-16 00:00:00 UTC", "2013-03-22 23:59:59", false, week_start: :sat
|
344
452
|
end
|
345
453
|
|
346
454
|
# misc
|
347
455
|
|
348
456
|
def test_order_day
|
349
|
-
assert_empty User.group_by_day(:created_at).order("day desc").limit(20).count
|
457
|
+
assert_empty User.group_by_day(:created_at, series: false).order("day desc").limit(20).count
|
350
458
|
end
|
351
459
|
|
352
460
|
def test_order_week
|
353
|
-
assert_empty User.group_by_week(:created_at).order("week asc").count
|
461
|
+
assert_empty User.group_by_week(:created_at, series: false).order("week asc").count
|
354
462
|
end
|
355
463
|
|
356
464
|
def test_order_hour_of_day
|
357
|
-
assert_empty User.group_by_hour_of_day(:created_at).order("hour_of_day desc").count
|
465
|
+
assert_empty User.group_by_hour_of_day(:created_at, series: false).order("hour_of_day desc").count
|
358
466
|
end
|
359
467
|
|
360
468
|
def test_table_name
|
@@ -375,8 +483,8 @@ module TestGroupdate
|
|
375
483
|
def assert_result(method, expected, time_str, time_zone = false, options = {})
|
376
484
|
create_user time_str
|
377
485
|
expected = expected.is_a?(Time) ? time_key(expected) : number_key(expected)
|
378
|
-
assert_equal ordered_hash({expected => 1}), User.send(:"group_by_#{method}", :created_at, time_zone ? "Pacific Time (US & Canada)" : nil
|
379
|
-
assert_equal 1, User.send(:"group_by_#{method}", :created_at, time_zone ? "Pacific Time (US & Canada)" : nil
|
486
|
+
assert_equal ordered_hash({expected => 1}), User.send(:"group_by_#{method}", :created_at, options.merge(series: false, time_zone: time_zone ? "Pacific Time (US & Canada)" : nil)).order(method.to_s).count
|
487
|
+
assert_equal 1, User.send(:"group_by_#{method}", :created_at, options.merge(time_zone: time_zone ? "Pacific Time (US & Canada)" : nil)).count[expected]
|
380
488
|
end
|
381
489
|
|
382
490
|
def assert_zeros(method, created_at, keys, range_start, range_end, time_zone = nil, options = {})
|
@@ -385,7 +493,7 @@ module TestGroupdate
|
|
385
493
|
keys.each_with_index do |key, i|
|
386
494
|
expected[Time.parse(key)] = i == 1 ? 1 : 0
|
387
495
|
end
|
388
|
-
assert_equal expected, User.send(:"group_by_#{method}", :created_at, time_zone ? "Pacific Time (US & Canada)" : nil, Time.parse(range_start)..Time.parse(range_end)
|
496
|
+
assert_equal expected, User.send(:"group_by_#{method}", :created_at, options.merge(time_zone: time_zone ? "Pacific Time (US & Canada)" : nil, range: Time.parse(range_start)..Time.parse(range_end))).count
|
389
497
|
end
|
390
498
|
|
391
499
|
def ordered_hash(hash)
|