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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 389dc65b32212f41266af6d39d4f422c5ed00143
4
- data.tar.gz: b0345ab008fc00b1ae2ac0dffecb1b6677e29daf
3
+ metadata.gz: 51407cdc14a5d08e4a7f50111c5d5bd91f7d2a8a
4
+ data.tar.gz: 00650eedbd754abced4b15701c631eee2baa9e61
5
5
  SHA512:
6
- metadata.gz: fe786c3908de9a1288f147b4bddac9f62d4e7f67c33734da2b94a98d1b2601ae8c35731a7cacdc0d439ba4a68d0a00dffe9554cee67ed95c907e111c44262e41
7
- data.tar.gz: 6534a271d8e8a7730b58cdd0aa73f818a92194b3ee58d2259097634544b2667ef10be5ac75cd5368841e65e7601597f44227bbec4f9414c0e1264a70c1218c0a
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
- - hour
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-02-25 08:00:00+00" => 80,
46
- # "2013-03-04 08:00:00+00" => 70,
47
- # "2013-03-11 07:00:00+00" => 54
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
- - microseconds
80
- - milliseconds
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
- - decade
90
- - century
91
- - millennium
113
+ - hour_of_day
114
+ - day_of_week
92
115
 
93
116
  ## Contributing
94
117
 
data/groupdate.gemspec CHANGED
@@ -24,4 +24,6 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency "rake"
25
25
  spec.add_development_dependency "minitest"
26
26
  spec.add_development_dependency "pg"
27
+ spec.add_development_dependency "mysql"
28
+ spec.add_development_dependency "mysql2"
27
29
  end
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(microseconds milliseconds second minute hour day week month quarter year decade century millennium).each do |field|
31
- self.scope :"group_by_#{field}", lambda {|column, time_zone = Time.zone|
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
- sql = "DATE_TRUNC('#{field}', #{column}::timestamptz AT TIME ZONE ?) AT TIME ZONE ?"
39
- group(sanitize_sql_array([sql, time_zone, time_zone]))
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
@@ -1,3 +1,3 @@
1
1
  module Groupdate
2
- VERSION = "0.0.3"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -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
- class TestGroupdate < MiniTest::Unit::TestCase
26
- def setup
27
- User.delete_all
28
- [
29
- {name: "Andrew", score: 1, created_at: Time.parse("2013-04-01 00:00:00 UTC")},
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
- def test_count
36
- expected = {
37
- "2013-04-01 00:00:00+00" => 2,
38
- "2013-04-02 00:00:00+00" => 1
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
- def test_time_zone
44
- expected = {
45
- "2013-03-31 07:00:00+00" => 2,
46
- "2013-04-01 07:00:00+00" => 1
47
- }
48
- assert_equal expected, User.group_by_day(:created_at, "Pacific Time (US & Canada)").count
49
- end
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
- def test_where
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.3
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-18 00:00:00.000000000 Z
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