groupdate 3.1.1 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
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