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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a75601315268495e473958ab19c6a39197efb3b9
4
- data.tar.gz: 1b0e1d8b246e49e5e4d3abb76c40bc617f97e7bf
3
+ metadata.gz: df818b929dceeddfccab948ceb591ad26c7334c8
4
+ data.tar.gz: 370a704d7ff6a17fb82bb057d717141ed87bc69f
5
5
  SHA512:
6
- metadata.gz: 4c0edf976534abc2ccb263b39e48b3664991122ed50b541d4535f139243e5d657a2a525054a2906a1d037a4a65f7dbe1ea9d5f932d70f1d1fd507433cb00d499
7
- data.tar.gz: 073ec218da11f747a93af186e5c82ed7e4035f0904816013b34f89130608befe984af491acbc3fcfd8da55ffd6d92d6545eeffb2a3ed15f946e821f3eb45dcf3
6
+ metadata.gz: 7a77d1af0c530bfab9552ddff8f7149575c9821bcff772d33454b50c0050561d861a7eb5f220c5aa19200f80194f07c6a550c412be2ac76eedd88dbddb143faf
7
+ data.tar.gz: 91d207a5493bcafe51e94a884809e37b8250e9c2e581bf26172a5d0810d32c64aa63f21aead7935d1096c4d27dcde4c8d49cc8de821cdae4c83634e6a168cf94
@@ -1,3 +1,7 @@
1
+ ## 3.2.0
2
+
3
+ - Added limited support for SQLite
4
+
1
5
  ## 3.1.1
2
6
 
3
7
  - Fixed `current: false`
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
  [![Build Status](https://travis-ci.org/ankane/groupdate.svg?branch=master)](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).reverse_order.count
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
@@ -5,6 +5,7 @@ task default: :test
5
5
  Rake::TestTask.new do |t|
6
6
  t.libs << "test"
7
7
  t.test_files = FileList["test/**/*_test.rb"].exclude(/redshift/)
8
+ t.warning = false
8
9
  end
9
10
 
10
11
  namespace :test do
@@ -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 = ["acekane1@gmail.com"]
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
@@ -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
@@ -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
- count =
148
- begin
149
- Hash[relation.send(method, *args, &block).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] }]
150
- rescue NoMethodError
151
- raise "Be sure to install time zone support - https://github.com/ankane/groupdate#for-mysql"
152
- end
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(count, (options.key?(:default_value) ? options[:default_value] : 0), multiple_groups, reverse)
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
@@ -1,3 +1,3 @@
1
1
  module Groupdate
2
- VERSION = "3.1.1"
2
+ VERSION = "3.2.0"
3
3
  end
@@ -1,7 +1,7 @@
1
1
  require_relative "test_helper"
2
2
  require "ostruct"
3
3
 
4
- class TestEnumerable < Minitest::Test
4
+ class EnumerableTest < Minitest::Test
5
5
  include TestGroupdate
6
6
 
7
7
  def test_enumerable
@@ -1,6 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in searchkick.gemspec
3
+ # Specify your gem's dependencies in groupdate.gemspec
4
4
  gemspec path: "../../"
5
5
 
6
6
  gem "activerecord", "~> 3.1.0"
@@ -1,6 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in searchkick.gemspec
3
+ # Specify your gem's dependencies in groupdate.gemspec
4
4
  gemspec path: "../../"
5
5
 
6
6
  gem "activerecord", "~> 3.2.0"
@@ -1,6 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in searchkick.gemspec
3
+ # Specify your gem's dependencies in groupdate.gemspec
4
4
  gemspec path: "../../"
5
5
 
6
6
  gem "activerecord", "~> 4.0.0"
@@ -1,6 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in searchkick.gemspec
3
+ # Specify your gem's dependencies in groupdate.gemspec
4
4
  gemspec path: "../../"
5
5
 
6
6
  gem "activerecord", "~> 4.1.0"
@@ -1,6 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in searchkick.gemspec
3
+ # Specify your gem's dependencies in groupdate.gemspec
4
4
  gemspec path: "../../"
5
5
 
6
6
  gem "activerecord", "~> 4.2.0"
@@ -1,6 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in searchkick.gemspec
3
+ # Specify your gem's dependencies in groupdate.gemspec
4
4
  gemspec path: "../../"
5
5
 
6
6
  gem "activerecord", "~> 4.2.0"
@@ -1,6 +1,6 @@
1
1
  require_relative "test_helper"
2
2
 
3
- class TestMysql < Minitest::Test
3
+ class MysqlTest < Minitest::Test
4
4
  include TestGroupdate
5
5
  include TestDatabase
6
6
 
@@ -1,6 +1,6 @@
1
1
  require_relative "test_helper"
2
2
 
3
- class TestPostgresql < Minitest::Test
3
+ class PostgresqlTest < Minitest::Test
4
4
  include TestGroupdate
5
5
  include TestDatabase
6
6
 
@@ -1,6 +1,6 @@
1
1
  require_relative "test_helper"
2
2
 
3
- class TestRedshift < Minitest::Test
3
+ class RedshiftTest < Minitest::Test
4
4
  include TestGroupdate
5
5
  include TestDatabase
6
6
 
@@ -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
@@ -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
- create_user "#{this_year}-#{this_quarters_month}-01"
245
- create_user "#{this_year}-#{this_quarters_month - 6}-01"
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
- Date.parse("#{this_year}-#{this_quarters_month - 3}-01") => 0,
248
- Date.parse("#{this_year}-#{this_quarters_month}-01") => 1
264
+ last_quarter.to_date => 0,
265
+ this_quarter.to_date => 1
249
266
  }
250
- assert_equal expected, User.group_by_quarter(:created_at, last: 2).count
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(RuntimeError) { User.group_by_day(:created_at).count }
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, User.group_by_day(:created_at, time_zone: "Brasilia").count
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(RuntimeError) do
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.utc.beginning_of_quarter.month
1185
+ Time.now.beginning_of_quarter.month
1169
1186
  end
1170
1187
 
1171
1188
  def this_year
1172
- Time.now.utc.year
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.1.1
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: 2016-10-26 00:00:00.000000000 Z
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
- - acekane1@gmail.com
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.5.1
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