groupdate 2.0.3 → 2.0.4
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 +1 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +0 -9
- data/README.md +17 -1
- data/gemfiles/activerecord31.gemfile +6 -0
- data/groupdate.gemspec +1 -1
- data/lib/groupdate/scopes.rb +1 -1
- data/lib/groupdate/series.rb +51 -13
- data/lib/groupdate/version.rb +1 -1
- data/test/mysql_test.rb +0 -8
- data/test/postgresql_test.rb +0 -16
- data/test/test_helper.rb +74 -25
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 30c3377f6edb6fe33f5d0ac0aab691a6a7a6c198
|
4
|
+
data.tar.gz: d725a9a605553a390055a4bbf55a132233aade70
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d121c447a7c6c4c6b9be3e28942c640ea4f7bb5da420314eb863d7ff9a1a648c77129afe726ae3311a61cef6a32d386fbf76e97948adc0313f0a87935f4fe181
|
7
|
+
data.tar.gz: 13e3ba1f7530160f450cb48aa1e5e67f65af979602e2c3ff0d392cc436079db1c12cd457c122e30d63e1c5cac75001af3dafb4b3839520402d1b115015200613
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
@@ -2,12 +2,3 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in groupdate.gemspec
|
4
4
|
gemspec
|
5
|
-
|
6
|
-
# gem "activerecord", github: "rails/rails"
|
7
|
-
# gem "activerecord", "3.0.20"
|
8
|
-
# gem "mysql2", "< 0.3.0"
|
9
|
-
|
10
|
-
platform :jruby do
|
11
|
-
gem "activerecord-jdbcpostgresql-adapter", :github => "jruby/activerecord-jdbc-adapter"
|
12
|
-
gem "activerecord-jdbcmysql-adapter", :github => "jruby/activerecord-jdbc-adapter"
|
13
|
-
end
|
data/README.md
CHANGED
@@ -32,7 +32,7 @@ User.group_by_day(:created_at).count
|
|
32
32
|
# }
|
33
33
|
```
|
34
34
|
|
35
|
-
Results are returned in ascending order, so no need to sort.
|
35
|
+
Results are returned in ascending order by default, so no need to sort.
|
36
36
|
|
37
37
|
You can also group by:
|
38
38
|
|
@@ -48,6 +48,8 @@ and
|
|
48
48
|
- hour_of_day
|
49
49
|
- day_of_week (Sunday = 0, Monday = 1, etc)
|
50
50
|
|
51
|
+
Use it anywhere you can use `group`.
|
52
|
+
|
51
53
|
### Time Zones
|
52
54
|
|
53
55
|
The default time zone is `Time.zone`. Change this with:
|
@@ -105,6 +107,20 @@ To get a specific time range, use:
|
|
105
107
|
User.group_by_day(:created_at, range: 2.weeks.ago.midnight..Time.now).count
|
106
108
|
```
|
107
109
|
|
110
|
+
### Order
|
111
|
+
|
112
|
+
You can order in descending order with:
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
User.group_by_day(:created_at).reverse_order.count
|
116
|
+
```
|
117
|
+
|
118
|
+
or
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
User.group_by_day(:created_at).order("day desc").count
|
122
|
+
```
|
123
|
+
|
108
124
|
## Installation
|
109
125
|
|
110
126
|
Add this line to your application’s Gemfile:
|
data/groupdate.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["acekane1@gmail.com"]
|
11
11
|
spec.description = %q{The simplest way to group temporal data}
|
12
12
|
spec.summary = %q{The simplest way to group temporal data}
|
13
|
-
spec.homepage = ""
|
13
|
+
spec.homepage = "https://github.com/ankane/groupdate"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files`.split($/)
|
data/lib/groupdate/scopes.rb
CHANGED
@@ -76,7 +76,7 @@ module Groupdate
|
|
76
76
|
group = group(Groupdate::OrderHack.new(sanitize_sql_array(query), field, time_zone))
|
77
77
|
range = args[2] || options[:range] || true
|
78
78
|
unless options[:series] == false
|
79
|
-
Series.new(group, field, column, time_zone_object, range, week_start, day_start)
|
79
|
+
Series.new(group, field, column, time_zone_object, range, week_start, day_start, group.group_values.size - 1)
|
80
80
|
else
|
81
81
|
group
|
82
82
|
end
|
data/lib/groupdate/series.rb
CHANGED
@@ -1,23 +1,30 @@
|
|
1
1
|
module Groupdate
|
2
2
|
class Series
|
3
3
|
|
4
|
-
def initialize(relation, field, column, time_zone, time_range, week_start, day_start)
|
5
|
-
|
6
|
-
# doesn't matter whether we include the end of a ... range - it will be excluded later
|
7
|
-
@relation = relation.where("#{column} >= ? AND #{column} <= ?", time_range.first, time_range.last)
|
8
|
-
else
|
9
|
-
@relation = relation.where("#{column} IS NOT NULL")
|
10
|
-
end
|
4
|
+
def initialize(relation, field, column, time_zone, time_range, week_start, day_start, group_index)
|
5
|
+
@relation = relation
|
11
6
|
@field = field
|
7
|
+
@column = column
|
12
8
|
@time_zone = time_zone
|
13
9
|
@time_range = time_range
|
14
10
|
@week_start = week_start
|
15
11
|
@day_start = day_start
|
12
|
+
@group_index = group_index
|
16
13
|
end
|
17
14
|
|
18
15
|
def build_series(count)
|
19
16
|
utc = ActiveSupport::TimeZone["UTC"]
|
20
17
|
|
18
|
+
reverse = @reverse
|
19
|
+
order = @relation.order_values.first
|
20
|
+
if order.is_a?(String)
|
21
|
+
parts = order.split(" ")
|
22
|
+
reverse_order = (parts.size == 2 && parts[0] == @field && parts[1].to_s.downcase == "desc")
|
23
|
+
reverse = !reverse if reverse_order
|
24
|
+
end
|
25
|
+
|
26
|
+
multiple_groups = @relation.group_values.size > 1
|
27
|
+
|
21
28
|
cast_method =
|
22
29
|
case @field
|
23
30
|
when "day_of_week", "hour_of_day"
|
@@ -26,7 +33,7 @@ module Groupdate
|
|
26
33
|
lambda{|k| (k.is_a?(String) ? utc.parse(k) : k.to_time).in_time_zone(@time_zone) }
|
27
34
|
end
|
28
35
|
|
29
|
-
count = Hash[ count.map{|k, v| [cast_method.call(k), v] } ]
|
36
|
+
count = Hash[ count.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] } ]
|
30
37
|
|
31
38
|
series =
|
32
39
|
case @field
|
@@ -40,7 +47,12 @@ module Groupdate
|
|
40
47
|
@time_range
|
41
48
|
else
|
42
49
|
# use first and last values
|
43
|
-
sorted_keys =
|
50
|
+
sorted_keys =
|
51
|
+
if multiple_groups
|
52
|
+
count.keys.map{|k| k[@group_index] }.sort
|
53
|
+
else
|
54
|
+
count.keys.sort
|
55
|
+
end
|
44
56
|
sorted_keys.first..sorted_keys.last
|
45
57
|
end
|
46
58
|
|
@@ -76,12 +88,25 @@ module Groupdate
|
|
76
88
|
series << series.last + step
|
77
89
|
end
|
78
90
|
|
79
|
-
|
91
|
+
if multiple_groups
|
92
|
+
keys = count.keys.map{|k| k[0...@group_index] + k[(@group_index + 1)..-1] }.uniq
|
93
|
+
series = series.reverse if reverse
|
94
|
+
keys.flat_map do |k|
|
95
|
+
series.map{|s| k[0...@group_index] + [s] + k[@group_index..-1] }
|
96
|
+
end
|
97
|
+
else
|
98
|
+
series
|
99
|
+
end
|
80
100
|
else
|
81
101
|
[]
|
82
102
|
end
|
83
103
|
end
|
84
104
|
|
105
|
+
# reversed above if multiple groups
|
106
|
+
if !multiple_groups and reverse
|
107
|
+
series = series.to_a.reverse
|
108
|
+
end
|
109
|
+
|
85
110
|
Hash[series.map do |k|
|
86
111
|
[k, count[k] || 0]
|
87
112
|
end]
|
@@ -90,10 +115,23 @@ module Groupdate
|
|
90
115
|
def method_missing(method, *args, &block)
|
91
116
|
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation/calculations.rb
|
92
117
|
if ActiveRecord::Calculations.method_defined?(method)
|
93
|
-
|
118
|
+
relation =
|
119
|
+
if @time_range.is_a?(Range)
|
120
|
+
# doesn't matter whether we include the end of a ... range - it will be excluded later
|
121
|
+
@relation.where("#{@column} >= ? AND #{@column} <= ?", @time_range.first, @time_range.last)
|
122
|
+
else
|
123
|
+
@relation.where("#{@column} IS NOT NULL")
|
124
|
+
end
|
125
|
+
|
126
|
+
# undo reverse since we do not want this to appear in the query
|
127
|
+
if relation.reverse_order_value
|
128
|
+
@reverse = true
|
129
|
+
relation = relation.reverse_order
|
130
|
+
end
|
131
|
+
|
132
|
+
build_series(relation.send(method, *args, &block))
|
94
133
|
elsif @relation.respond_to?(method)
|
95
|
-
@relation
|
96
|
-
self
|
134
|
+
Groupdate::Series.new(@relation.send(method, *args, &block), @field, @column, @time_zone, @time_range, @week_start, @day_start, @group_index)
|
97
135
|
else
|
98
136
|
super
|
99
137
|
end
|
data/lib/groupdate/version.rb
CHANGED
data/test/mysql_test.rb
CHANGED
data/test/postgresql_test.rb
CHANGED
@@ -8,20 +8,4 @@ class TestPostgresql < Minitest::Unit::TestCase
|
|
8
8
|
User.establish_connection :adapter => "postgresql", :database => "groupdate_test"
|
9
9
|
end
|
10
10
|
|
11
|
-
def time_key(key)
|
12
|
-
if ActiveRecord::VERSION::MAJOR == 3
|
13
|
-
key.utc.strftime("%Y-%m-%d %H:%M:%S+00")
|
14
|
-
else
|
15
|
-
key
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def number_key(key)
|
20
|
-
if RUBY_PLATFORM != "java" and ActiveRecord::VERSION::MAJOR == 3
|
21
|
-
key.to_s
|
22
|
-
else
|
23
|
-
key
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
11
|
end
|
data/test/test_helper.rb
CHANGED
@@ -386,7 +386,7 @@ module TestGroupdate
|
|
386
386
|
7.times do |n|
|
387
387
|
expected[n] = n == 3 ? 1 : 0
|
388
388
|
end
|
389
|
-
assert_equal expected, User.group_by_day_of_week(:created_at, range: true).count
|
389
|
+
assert_equal expected, User.group_by_day_of_week(:created_at, range: true).count
|
390
390
|
end
|
391
391
|
|
392
392
|
def test_zeros_hour_of_day
|
@@ -395,13 +395,13 @@ module TestGroupdate
|
|
395
395
|
24.times do |n|
|
396
396
|
expected[n] = n == 20 ? 1 : 0
|
397
397
|
end
|
398
|
-
assert_equal expected, User.group_by_hour_of_day(:created_at, range: true).count
|
398
|
+
assert_equal expected, User.group_by_hour_of_day(:created_at, range: true).count
|
399
399
|
end
|
400
400
|
|
401
401
|
def test_zeros_excludes_end
|
402
402
|
create_user "2013-05-02 00:00:00 UTC"
|
403
403
|
expected = {
|
404
|
-
|
404
|
+
utc.parse("2013-05-01 00:00:00 UTC") => 0
|
405
405
|
}
|
406
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
|
407
407
|
end
|
@@ -409,7 +409,7 @@ module TestGroupdate
|
|
409
409
|
def test_zeros_previous_scope
|
410
410
|
create_user "2013-05-01 00:00:00 UTC"
|
411
411
|
expected = {
|
412
|
-
|
412
|
+
utc.parse("2013-05-01 00:00:00 UTC") => 0
|
413
413
|
}
|
414
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
|
415
415
|
end
|
@@ -417,7 +417,7 @@ module TestGroupdate
|
|
417
417
|
def test_zeros_datetime
|
418
418
|
create_user "2013-05-01 00:00:00 UTC"
|
419
419
|
expected = {
|
420
|
-
|
420
|
+
utc.parse("2013-05-01 00:00:00 UTC") => 1
|
421
421
|
}
|
422
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
|
423
423
|
end
|
@@ -432,9 +432,9 @@ module TestGroupdate
|
|
432
432
|
create_user "2013-05-01 00:00:00 UTC"
|
433
433
|
create_user "2013-05-03 00:00:00 UTC"
|
434
434
|
expected = {
|
435
|
-
|
436
|
-
|
437
|
-
|
435
|
+
utc.parse("2013-05-01 00:00:00 UTC") => 1,
|
436
|
+
utc.parse("2013-05-02 00:00:00 UTC") => 0,
|
437
|
+
utc.parse("2013-05-03 00:00:00 UTC") => 1
|
438
438
|
}
|
439
439
|
assert_equal expected, User.group_by_day(:created_at, range: true).count
|
440
440
|
end
|
@@ -453,16 +453,20 @@ module TestGroupdate
|
|
453
453
|
|
454
454
|
# misc
|
455
455
|
|
456
|
-
def
|
457
|
-
|
456
|
+
def test_order_hour_of_day
|
457
|
+
assert_equal 23, User.group_by_hour_of_day(:created_at).order("hour_of_day desc").count.keys.first
|
458
458
|
end
|
459
459
|
|
460
|
-
def
|
461
|
-
|
460
|
+
def test_order_hour_of_day_case
|
461
|
+
assert_equal 23, User.group_by_hour_of_day(:created_at).order("hour_of_day DESC").count.keys.first
|
462
462
|
end
|
463
463
|
|
464
|
-
def
|
465
|
-
|
464
|
+
def test_order_hour_of_day_reverse
|
465
|
+
assert_equal 23, User.group_by_hour_of_day(:created_at).reverse_order.count.keys.first
|
466
|
+
end
|
467
|
+
|
468
|
+
def test_order_hour_of_day_order_reverse
|
469
|
+
assert_equal 0, User.group_by_hour_of_day(:created_at).order("hour_of_day desc").reverse_order.count.keys.first
|
466
470
|
end
|
467
471
|
|
468
472
|
def test_table_name
|
@@ -487,6 +491,52 @@ module TestGroupdate
|
|
487
491
|
assert_equal expected, User.group_by_day(:created_at).where("created_at > ?", "2013-05-01 00:00:00 UTC").count
|
488
492
|
end
|
489
493
|
|
494
|
+
def test_group_before
|
495
|
+
create_user "2013-05-01 00:00:00 UTC", 1
|
496
|
+
create_user "2013-05-02 00:00:00 UTC", 2
|
497
|
+
create_user "2013-05-03 00:00:00 UTC", 2
|
498
|
+
expected = {
|
499
|
+
[1, utc.parse("2013-05-01 00:00:00 UTC")] => 1,
|
500
|
+
[1, utc.parse("2013-05-02 00:00:00 UTC")] => 0,
|
501
|
+
[1, utc.parse("2013-05-03 00:00:00 UTC")] => 0,
|
502
|
+
[2, utc.parse("2013-05-01 00:00:00 UTC")] => 0,
|
503
|
+
[2, utc.parse("2013-05-02 00:00:00 UTC")] => 1,
|
504
|
+
[2, utc.parse("2013-05-03 00:00:00 UTC")] => 1
|
505
|
+
}
|
506
|
+
assert_equal expected, User.group(:score).group_by_day(:created_at).order(:score).count
|
507
|
+
end
|
508
|
+
|
509
|
+
def test_group_after
|
510
|
+
create_user "2013-05-01 00:00:00 UTC", 1
|
511
|
+
create_user "2013-05-02 00:00:00 UTC", 2
|
512
|
+
create_user "2013-05-03 00:00:00 UTC", 2
|
513
|
+
expected = {
|
514
|
+
[utc.parse("2013-05-01 00:00:00 UTC"), 1] => 1,
|
515
|
+
[utc.parse("2013-05-02 00:00:00 UTC"), 1] => 0,
|
516
|
+
[utc.parse("2013-05-03 00:00:00 UTC"), 1] => 0,
|
517
|
+
[utc.parse("2013-05-01 00:00:00 UTC"), 2] => 0,
|
518
|
+
[utc.parse("2013-05-02 00:00:00 UTC"), 2] => 1,
|
519
|
+
[utc.parse("2013-05-03 00:00:00 UTC"), 2] => 1
|
520
|
+
}
|
521
|
+
assert_equal expected, User.group_by_day(:created_at).group(:score).order(:score).count
|
522
|
+
end
|
523
|
+
|
524
|
+
def test_groupdate_multiple
|
525
|
+
create_user "2013-05-01 00:00:00 UTC", 1
|
526
|
+
expected = {
|
527
|
+
[utc.parse("2013-05-01 00:00:00 UTC"), utc.parse("2013-01-01 00:00:00 UTC")] => 1
|
528
|
+
}
|
529
|
+
assert_equal expected, User.group_by_day(:created_at).group_by_year(:created_at).count
|
530
|
+
end
|
531
|
+
|
532
|
+
def test_not_modified
|
533
|
+
create_user "2013-05-01 00:00:00 UTC"
|
534
|
+
expected = {utc.parse("2013-05-01 00:00:00 UTC") => 1}
|
535
|
+
relation = User.group_by_day(:created_at)
|
536
|
+
relation.where("created_at > ?", "2013-05-01 00:00:00 UTC")
|
537
|
+
assert_equal expected, relation.count
|
538
|
+
end
|
539
|
+
|
490
540
|
def test_bad_method
|
491
541
|
assert_raises(NoMethodError) { User.group_by_day(:created_at).no_such_method }
|
492
542
|
end
|
@@ -502,31 +552,30 @@ module TestGroupdate
|
|
502
552
|
# helpers
|
503
553
|
|
504
554
|
def assert_result_time(method, expected, time_str, time_zone = false, options = {})
|
505
|
-
|
555
|
+
expected = {utc.parse(expected).in_time_zone(time_zone ? "Pacific Time (US & Canada)" : utc) => 1}
|
556
|
+
assert_equal expected, result(method, time_str, time_zone, options)
|
506
557
|
end
|
507
558
|
|
508
559
|
def assert_result(method, expected, time_str, time_zone = false, options = {})
|
560
|
+
assert_equal 1, result(method, time_str, time_zone, options)[expected]
|
561
|
+
end
|
562
|
+
|
563
|
+
def result(method, time_str, time_zone = false, options = {})
|
509
564
|
create_user time_str
|
510
|
-
|
511
|
-
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
|
512
|
-
assert_equal 1, User.send(:"group_by_#{method}", :created_at, options.merge(time_zone: time_zone ? "Pacific Time (US & Canada)" : nil)).count[expected]
|
565
|
+
User.send(:"group_by_#{method}", :created_at, options.merge(time_zone: time_zone ? "Pacific Time (US & Canada)" : nil)).count
|
513
566
|
end
|
514
567
|
|
515
568
|
def assert_zeros(method, created_at, keys, range_start, range_end, time_zone = nil, options = {})
|
516
569
|
create_user created_at
|
517
570
|
expected = {}
|
518
571
|
keys.each_with_index do |key, i|
|
519
|
-
expected[
|
572
|
+
expected[utc.parse(key).in_time_zone(time_zone ? "Pacific Time (US & Canada)" : utc)] = i == 1 ? 1 : 0
|
520
573
|
end
|
521
574
|
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
|
522
575
|
end
|
523
576
|
|
524
|
-
def
|
525
|
-
|
526
|
-
end
|
527
|
-
|
528
|
-
def create_user(created_at)
|
529
|
-
User.create! :name => "Andrew", :score => 1, :created_at => utc.parse(created_at)
|
577
|
+
def create_user(created_at, score = 1)
|
578
|
+
User.create! :name => "Andrew", :score => score, :created_at => utc.parse(created_at)
|
530
579
|
end
|
531
580
|
|
532
581
|
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: 2.0.
|
4
|
+
version: 2.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-03-
|
11
|
+
date: 2014-03-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -108,6 +108,7 @@ files:
|
|
108
108
|
- LICENSE.txt
|
109
109
|
- README.md
|
110
110
|
- Rakefile
|
111
|
+
- gemfiles/activerecord31.gemfile
|
111
112
|
- groupdate.gemspec
|
112
113
|
- lib/groupdate.rb
|
113
114
|
- lib/groupdate/order_hack.rb
|
@@ -117,7 +118,7 @@ files:
|
|
117
118
|
- test/mysql_test.rb
|
118
119
|
- test/postgresql_test.rb
|
119
120
|
- test/test_helper.rb
|
120
|
-
homepage:
|
121
|
+
homepage: https://github.com/ankane/groupdate
|
121
122
|
licenses:
|
122
123
|
- MIT
|
123
124
|
metadata: {}
|