groupdate 0.0.3 → 0.1.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/README.md +34 -11
- data/groupdate.gemspec +2 -0
- data/lib/groupdate.rb +49 -4
- data/lib/groupdate/version.rb +1 -1
- data/test/groupdate_test.rb +108 -34
- metadata +30 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 51407cdc14a5d08e4a7f50111c5d5bd91f7d2a8a
|
4
|
+
data.tar.gz: 00650eedbd754abced4b15701c631eee2baa9e61
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9528a76cc3e2a523216c18b0692c5edb4b6739c34e720e2ae35797305266125bc059b60dbe5cca9ea36e981a85b538600f9aa1ebb8613c87538066453b0c0175
|
7
|
+
data.tar.gz: 2be5da38025c0865eaaf8e8086dd146378172187972ec80efa72171331134088388d96dd2296b1567e10ab0f2ff74bd2e3b63168129abf2ddf552986d4910e3b
|
data/README.md
CHANGED
@@ -5,12 +5,13 @@ The simplest way to group by:
|
|
5
5
|
- day
|
6
6
|
- week
|
7
7
|
- month
|
8
|
-
-
|
8
|
+
- day of the week
|
9
|
+
- hour of the day
|
9
10
|
- *and more* (complete list at bottom)
|
10
11
|
|
11
12
|
:tada: Time zones supported!!
|
12
13
|
|
13
|
-
PostgreSQL only at the moment - support for other datastores coming soon
|
14
|
+
PostgreSQL and MySQL only at the moment - support for other datastores coming soon
|
14
15
|
|
15
16
|
## Usage
|
16
17
|
|
@@ -42,9 +43,9 @@ The default time zone is `Time.zone`. Pass a time zone as the second argument.
|
|
42
43
|
```ruby
|
43
44
|
User.group_by_week(:created_at, "Pacific Time (US & Canada)").count
|
44
45
|
# {
|
45
|
-
# "2013-
|
46
|
-
# "2013-03-
|
47
|
-
# "2013-03-
|
46
|
+
# "2013-03-03 08:00:00+00" => 80,
|
47
|
+
# "2013-03-10 08:00:00+00" => 70,
|
48
|
+
# "2013-03-17 07:00:00+00" => 54
|
48
49
|
# }
|
49
50
|
|
50
51
|
# equivalently
|
@@ -52,6 +53,30 @@ time_zone = ActiveSupport::TimeZone["Pacific Time (US & Canada)"]
|
|
52
53
|
User.group_by_week(:created_at, time_zone).count
|
53
54
|
```
|
54
55
|
|
56
|
+
**Note:** Weeks start on Sunday.
|
57
|
+
|
58
|
+
You can also group by the day of the week or hour of the day.
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
# day of the week
|
62
|
+
User.group_by_day_of_week(:created_at).count
|
63
|
+
# {
|
64
|
+
# "0" => 54, # Sunday
|
65
|
+
# "1" => 2, # Monday
|
66
|
+
# ...
|
67
|
+
# "6" => 3 # Saturday
|
68
|
+
# }
|
69
|
+
|
70
|
+
# hour of the day
|
71
|
+
User.group_by_hour_of_day(:created_at, "Pacific Time (US & Canada)").count
|
72
|
+
# {
|
73
|
+
# "0" => 34,
|
74
|
+
# "1" => 61,
|
75
|
+
# ...
|
76
|
+
# "23" => 12
|
77
|
+
# }
|
78
|
+
```
|
79
|
+
|
55
80
|
Use it with anything you can use `group` with:
|
56
81
|
|
57
82
|
```ruby
|
@@ -76,19 +101,17 @@ gem 'groupdate'
|
|
76
101
|
|
77
102
|
## Complete list
|
78
103
|
|
79
|
-
|
80
|
-
|
104
|
+
group_by_?
|
105
|
+
|
81
106
|
- second
|
82
107
|
- minute
|
83
108
|
- hour
|
84
109
|
- day
|
85
110
|
- week
|
86
111
|
- month
|
87
|
-
- quarter
|
88
112
|
- year
|
89
|
-
-
|
90
|
-
-
|
91
|
-
- millennium
|
113
|
+
- hour_of_day
|
114
|
+
- day_of_week
|
92
115
|
|
93
116
|
## Contributing
|
94
117
|
|
data/groupdate.gemspec
CHANGED
data/lib/groupdate.rb
CHANGED
@@ -27,16 +27,61 @@ module Groupdate
|
|
27
27
|
included do
|
28
28
|
# Field list from
|
29
29
|
# http://www.postgresql.org/docs/9.1/static/functions-datetime.html
|
30
|
-
%w(
|
31
|
-
|
30
|
+
fields = %w(second minute hour day week month year day_of_week hour_of_day)
|
31
|
+
fields.each do |field|
|
32
|
+
self.scope :"group_by_#{field}", lambda {|unsafe_column, time_zone = Time.zone|
|
33
|
+
column = connection.quote_column_name(unsafe_column)
|
32
34
|
time_zone ||= "Etc/UTC"
|
33
35
|
if time_zone.is_a?(ActiveSupport::TimeZone) or time_zone = ActiveSupport::TimeZone[time_zone]
|
34
36
|
time_zone = time_zone.tzinfo.name
|
35
37
|
else
|
36
38
|
raise "Unrecognized time zone"
|
37
39
|
end
|
38
|
-
|
39
|
-
|
40
|
+
query =
|
41
|
+
case connection.adapter_name
|
42
|
+
when "MySQL", "Mysql2"
|
43
|
+
case field
|
44
|
+
when "day_of_week" # Sunday = 0, Monday = 1, etc
|
45
|
+
# use CONCAT for consistent return type (String)
|
46
|
+
["CONCAT('', DAYOFWEEK(CONVERT_TZ(#{column}, '+00:00', ?)) - 1)", time_zone]
|
47
|
+
when "hour_of_day"
|
48
|
+
["CONCAT('', EXTRACT(HOUR from CONVERT_TZ(#{column}, '+00:00', ?)))", time_zone]
|
49
|
+
when "week"
|
50
|
+
["CONCAT(CONVERT_TZ(DATE_FORMAT(CONVERT_TZ(DATE_SUB(#{column}, INTERVAL (DAYOFWEEK(CONVERT_TZ(#{column}, '+00:00', ?)) - 1) DAY), '+00:00', ?), '%Y-%m-%d 00:00:00'), ?, '+00:00'), '+00')", time_zone, time_zone, time_zone]
|
51
|
+
else
|
52
|
+
format =
|
53
|
+
case field
|
54
|
+
when "second"
|
55
|
+
"%Y-%m-%d %H:%i:%S"
|
56
|
+
when "minute"
|
57
|
+
"%Y-%m-%d %H:%i:00"
|
58
|
+
when "hour"
|
59
|
+
"%Y-%m-%d %H:00:00"
|
60
|
+
when "day"
|
61
|
+
"%Y-%m-%d 00:00:00"
|
62
|
+
when "month"
|
63
|
+
"%Y-%m-01 00:00:00"
|
64
|
+
else # year
|
65
|
+
"%Y-01-01 00:00:00"
|
66
|
+
end
|
67
|
+
|
68
|
+
["CONCAT(CONVERT_TZ(DATE_FORMAT(CONVERT_TZ(#{column}, '+00:00', ?), '#{format}'), ?, '+00:00'), '+00')", time_zone, time_zone]
|
69
|
+
end
|
70
|
+
when "PostgreSQL"
|
71
|
+
case field
|
72
|
+
when "day_of_week"
|
73
|
+
["EXTRACT(DOW from #{column}::timestamptz AT TIME ZONE ?)", time_zone]
|
74
|
+
when "hour_of_day"
|
75
|
+
["EXTRACT(HOUR from #{column}::timestamptz AT TIME ZONE ?)", time_zone]
|
76
|
+
when "week" # start on Sunday, not PostgreSQL default Monday
|
77
|
+
["(DATE_TRUNC('#{field}', (#{column}::timestamptz + INTERVAL '1 day') AT TIME ZONE ?) - INTERVAL '1 day') AT TIME ZONE ?", time_zone, time_zone]
|
78
|
+
else
|
79
|
+
["DATE_TRUNC('#{field}', #{column}::timestamptz AT TIME ZONE ?) AT TIME ZONE ?", time_zone, time_zone]
|
80
|
+
end
|
81
|
+
else
|
82
|
+
raise "Connection adapter not supported"
|
83
|
+
end
|
84
|
+
group(sanitize_sql_array(query))
|
40
85
|
}
|
41
86
|
end
|
42
87
|
end
|
data/lib/groupdate/version.rb
CHANGED
data/test/groupdate_test.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "minitest/spec"
|
1
2
|
require "minitest/autorun"
|
2
3
|
require "active_record"
|
3
4
|
require "groupdate"
|
@@ -10,45 +11,118 @@ require "logger"
|
|
10
11
|
ActiveRecord::Base.default_timezone = :utc
|
11
12
|
ActiveRecord::Base.time_zone_aware_attributes = true
|
12
13
|
|
13
|
-
# start connection
|
14
|
-
ActiveRecord::Base.establish_connection adapter: "postgresql", database: "groupdate"
|
15
|
-
|
16
|
-
# ActiveRecord::Migration.create_table :users do |t|
|
17
|
-
# t.string :name
|
18
|
-
# t.integer :score
|
19
|
-
# t.timestamps
|
20
|
-
# end
|
21
|
-
|
22
14
|
class User < ActiveRecord::Base
|
23
15
|
end
|
24
16
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
{name: "Jordan", score: 2, created_at: Time.parse("2013-04-01 00:00:00 UTC")},
|
31
|
-
{name: "Nick", score: 3, created_at: Time.parse("2013-04-02 00:00:00 UTC")}
|
32
|
-
].each{|u| User.create!(u) }
|
33
|
-
end
|
17
|
+
describe Groupdate do
|
18
|
+
%w(postgresql mysql mysql2).each do |adapter|
|
19
|
+
describe adapter do
|
20
|
+
before do
|
21
|
+
ActiveRecord::Base.establish_connection adapter: adapter, database: "groupdate"
|
34
22
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
assert_equal expected, User.group_by_day(:created_at).count
|
41
|
-
end
|
23
|
+
# ActiveRecord::Migration.create_table :users do |t|
|
24
|
+
# t.string :name
|
25
|
+
# t.integer :score
|
26
|
+
# t.timestamps
|
27
|
+
# end
|
42
28
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
29
|
+
User.delete_all
|
30
|
+
end
|
31
|
+
|
32
|
+
it "works!" do
|
33
|
+
[
|
34
|
+
{name: "Andrew", score: 1, created_at: Time.parse("2013-04-01 00:00:10.200 UTC")},
|
35
|
+
{name: "Jordan", score: 2, created_at: Time.parse("2013-04-01 00:00:10.200 UTC")},
|
36
|
+
{name: "Nick", score: 3, created_at: Time.parse("2013-04-02 00:00:20.800 UTC")}
|
37
|
+
].each{|u| User.create!(u) }
|
38
|
+
|
39
|
+
assert_equal(
|
40
|
+
{"2013-04-01 00:00:00+00" => 1, "2013-04-02 00:00:00+00" => 1},
|
41
|
+
User.where("score > 1").group_by_day(:created_at).count
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "group_by_second" do
|
46
|
+
create_user "2013-04-01 00:00:01 UTC"
|
47
|
+
assert_equal({"2013-04-01 00:00:01+00" => 1}, User.group_by_second(:created_at).count)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "group_by_minute" do
|
51
|
+
create_user "2013-04-01 00:01:01 UTC"
|
52
|
+
assert_equal({"2013-04-01 00:01:00+00" => 1}, User.group_by_minute(:created_at).count)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "group_by_hour" do
|
56
|
+
create_user "2013-04-01 01:01:01 UTC"
|
57
|
+
assert_equal({"2013-04-01 01:00:00+00" => 1}, User.group_by_hour(:created_at).count)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "group_by_day" do
|
61
|
+
create_user "2013-04-01 01:01:01 UTC"
|
62
|
+
assert_equal({"2013-04-01 00:00:00+00" => 1}, User.group_by_day(:created_at).count)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "group_by_day with time zone" do
|
66
|
+
create_user "2013-04-01 01:01:01 UTC"
|
67
|
+
assert_equal({"2013-03-31 07:00:00+00" => 1}, User.group_by_day(:created_at, "Pacific Time (US & Canada)").count)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "group_by_week" do
|
71
|
+
create_user "2013-03-17 01:01:01 UTC"
|
72
|
+
assert_equal({"2013-03-17 00:00:00+00" => 1}, User.group_by_week(:created_at).count)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "group_by_week with time zone" do # day of DST
|
76
|
+
create_user "2013-03-17 01:01:01 UTC"
|
77
|
+
assert_equal({"2013-03-10 08:00:00+00" => 1}, User.group_by_week(:created_at, "Pacific Time (US & Canada)").count)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "group_by_month" do
|
81
|
+
create_user "2013-04-01 01:01:01 UTC"
|
82
|
+
assert_equal({"2013-04-01 00:00:00+00" => 1}, User.group_by_month(:created_at).count)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "group_by_month with time zone" do
|
86
|
+
create_user "2013-04-01 01:01:01 UTC"
|
87
|
+
assert_equal({"2013-03-01 08:00:00+00" => 1}, User.group_by_month(:created_at, "Pacific Time (US & Canada)").count)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "group_by_year" do
|
91
|
+
create_user "2013-01-01 01:01:01 UTC"
|
92
|
+
assert_equal({"2013-01-01 00:00:00+00" => 1}, User.group_by_year(:created_at).count)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "group_by_year with time zone" do
|
96
|
+
create_user "2013-01-01 01:01:01 UTC"
|
97
|
+
assert_equal({"2012-01-01 08:00:00+00" => 1}, User.group_by_year(:created_at, "Pacific Time (US & Canada)").count)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "group_by_hour_of_day" do
|
101
|
+
create_user "2013-01-01 11:00:00 UTC"
|
102
|
+
assert_equal({"11" => 1}, User.group_by_hour_of_day(:created_at).count)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "group_by_hour_of_day with time zone" do
|
106
|
+
create_user "2013-01-01 11:00:00 UTC"
|
107
|
+
assert_equal({"3" => 1}, User.group_by_hour_of_day(:created_at, "Pacific Time (US & Canada)").count)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "group_by_day_of_week" do
|
111
|
+
create_user "2013-03-03 00:00:00 UTC"
|
112
|
+
assert_equal({"0" => 1}, User.group_by_day_of_week(:created_at).count)
|
113
|
+
end
|
114
|
+
|
115
|
+
it "group_by_day_of_week with time zone" do
|
116
|
+
create_user "2013-03-03 00:00:00 UTC"
|
117
|
+
assert_equal({"6" => 1}, User.group_by_day_of_week(:created_at, "Pacific Time (US & Canada)").count)
|
118
|
+
end
|
119
|
+
|
120
|
+
# helper methods
|
121
|
+
|
122
|
+
def create_user(created_at)
|
123
|
+
User.create!(name: "Andrew", score: 1, created_at: Time.parse(created_at))
|
124
|
+
end
|
50
125
|
|
51
|
-
|
52
|
-
assert_equal({"2013-04-02 00:00:00+00" => 1}, User.where("score > 2").group_by_day(:created_at).count)
|
126
|
+
end
|
53
127
|
end
|
54
128
|
end
|
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: 0.0
|
4
|
+
version: 0.1.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: 2013-04-
|
11
|
+
date: 2013-04-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -80,6 +80,34 @@ dependencies:
|
|
80
80
|
- - '>='
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: mysql
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: mysql2
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
83
111
|
description: The simplest way to group temporal data
|
84
112
|
email:
|
85
113
|
- acekane1@gmail.com
|