groupdate2 4.1.5

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1a798d7e082e32adf30f0fd07348f8fec6369006
4
+ data.tar.gz: 6fc8861fc493d524bc29b8be51ef030887d38dd4
5
+ SHA512:
6
+ metadata.gz: 3c3459a9aa602d466dac261078dcd3dda23481e57b9cf1cf644699353f18e03af3924d0fda5f45887a6fad83d715ea7a0fc07f5772b30b0d4c34159fa0092bd3
7
+ data.tar.gz: fe47222def4bec06307c1930aff1c81b2bc6dd2819e9413d4df7e766345834d80271fa899e06a3f6655b53337e3864c68a32b2351f99432760f46731ec7b6c7d
@@ -0,0 +1,182 @@
1
+ ## 4.1.2
2
+
3
+ - Fixed error with empty data and `current: false`
4
+ - Fixed error in time zone check for Rails < 5.2
5
+ - Prevent infinite loop with endless ranges
6
+
7
+ ## 4.1.1
8
+
9
+ - Made column resolution consistent with `group`
10
+ - Added support for `alias_attribute`
11
+
12
+ ## 4.1.0
13
+
14
+ - Many performance improvements
15
+ - Added check for consistent time zone info
16
+ - Fixed error message for invalid queries with MySQL and SQLite
17
+ - Fixed issue with enumerable methods ignoring nils
18
+
19
+ ## 4.0.2
20
+
21
+ - Make `current` option work without `last`
22
+ - Fixed default value for `maximum`, `minimum`, and `average` (periods with no results now return `nil` instead of `0`, pass `default_value: 0` for previous behavior)
23
+
24
+ ## 4.0.1
25
+
26
+ - Fixed incorrect range with `last` option near time change
27
+
28
+ ## 4.0.0
29
+
30
+ - Custom calculation methods are supported by default - `groupdate_calculation_methods` is no longer needed
31
+
32
+ Breaking changes
33
+
34
+ - Dropped support for Rails < 4.2
35
+ - Invalid options now throw an `ArgumentError`
36
+ - `group_by` methods return an `ActiveRecord::Relation` instead of a `Groupdate::Series`
37
+ - `week_start` now affects `day_of_week`
38
+ - Removed support for `reverse_order` (was never supported in Rails 5)
39
+
40
+ ## 3.2.1
41
+
42
+ - Added `minute_of_hour`
43
+ - Added support for `unscoped`
44
+
45
+ ## 3.2.0
46
+
47
+ - Added limited support for SQLite
48
+
49
+ ## 3.1.1
50
+
51
+ - Fixed `current: false`
52
+ - Fixed `last` with `group_by_quarter`
53
+ - Raise `ArgumentError` when `last` option is not supported
54
+
55
+ ## 3.1.0
56
+
57
+ - Better support for date columns with `time_zone: false`
58
+ - Better date range handling for `range` option
59
+
60
+ ## 3.0.2
61
+
62
+ - Fixed `group_by_period` with associations
63
+ - Fixed `week_start` option for enumerables
64
+
65
+ ## 3.0.1
66
+
67
+ - Added support for Redshift
68
+ - Fix for infinite loop in certain cases for Rails 5
69
+
70
+ ## 3.0.0
71
+
72
+ Breaking changes
73
+
74
+ - `Date` objects are now returned for day, week, month, quarter, and year by default. Use `dates: false` for the previous behavior, or change this globally with `Groupdate.dates = false`.
75
+ - Array and hash methods no longer return the entire series by default. Use `series: true` for the previous behavior.
76
+ - The `series: false` option now returns the correct types and order, and plays nicely with other options.
77
+
78
+ ## 2.5.3
79
+
80
+ - All tests green with `mysql` gem
81
+ - Added support for decimal day start
82
+
83
+ ## 2.5.2
84
+
85
+ - Added `dates` option to return dates for day, week, month, quarter, and year
86
+
87
+ ## 2.5.1
88
+
89
+ - Added `group_by_quarter`
90
+ - Added `default_value` option
91
+ - Accept symbol for `format` option
92
+ - Raise `ArgumentError` if no field specified
93
+ - Added support for ActiveRecord 5 beta
94
+
95
+ ## 2.5.0
96
+
97
+ - Added `group_by_period` method
98
+ - Added `current` option
99
+ - Raise `ArgumentError` if no block given to enumerable
100
+
101
+ ## 2.4.0
102
+
103
+ - Added localization
104
+ - Added `carry_forward` option
105
+ - Added `series: false` option for arrays and hashes
106
+ - Fixed issue w/ Brasilia Summer Time
107
+ - Fixed issues w/ ActiveRecord 4.2
108
+
109
+ ## 2.3.0
110
+
111
+ - Raise error when ActiveRecord::Base.default_timezone is not `:utc`
112
+ - Added `day_of_month`
113
+ - Added `month_of_year`
114
+ - Do not quote column name
115
+
116
+ ## 2.2.1
117
+
118
+ - Fixed ActiveRecord 3 associations
119
+
120
+ ## 2.2.0
121
+
122
+ - Added support for arrays and hashes
123
+
124
+ ## 2.1.1
125
+
126
+ - Fixed format option with multiple groups
127
+ - Better error message if time zone support is missing for MySQL
128
+
129
+ ## 2.1.0
130
+
131
+ - Added last option
132
+ - Added format option
133
+
134
+ ## 2.0.4
135
+
136
+ - Added multiple groups
137
+ - Added order
138
+ - Subsequent methods no longer modify relation
139
+
140
+ ## 2.0.3
141
+
142
+ - Implemented respond_to?
143
+
144
+ ## 2.0.2
145
+
146
+ - where, joins, and includes no longer need to be before the group_by method
147
+
148
+ ## 2.0.1
149
+
150
+ - Use time zone instead of UTC for results
151
+
152
+ ## 2.0.0
153
+
154
+ - Returns entire series by default
155
+ - Added day_start option
156
+ - Better interface
157
+
158
+ ## 1.0.5
159
+
160
+ - Added global time_zone option
161
+
162
+ ## 1.0.4
163
+
164
+ - Added global week_start option
165
+ - Fixed bug with NULL values and series
166
+
167
+ ## 1.0.3
168
+
169
+ - Fixed deprecation warning when used with will_paginate
170
+ - Fixed bug with DateTime series
171
+
172
+ ## 1.0.2
173
+
174
+ - Added :start option for custom week start for group_by_week
175
+
176
+ ## 1.0.1
177
+
178
+ - Fixed series for Rails < 3.2 and MySQL
179
+
180
+ ## 1.0.0
181
+
182
+ - First major release
@@ -0,0 +1,75 @@
1
+ # Contributing
2
+
3
+ First, thanks for wanting to contribute. You’re awesome! :heart:
4
+
5
+ ## Help
6
+
7
+ We’re not able to provide support through GitHub Issues. If you’re looking for help with your code, try posting on [Stack Overflow](https://stackoverflow.com/).
8
+
9
+ All features should be documented. If you don’t see a feature in the docs, assume it doesn’t exist.
10
+
11
+ ## Bugs
12
+
13
+ Think you’ve discovered a bug?
14
+
15
+ 1. Search existing issues to see if it’s been reported.
16
+ 2. Try the `master` branch to make sure it hasn’t been fixed.
17
+
18
+ ```rb
19
+ gem "groupdate", github: "ankane/groupdate"
20
+ ```
21
+
22
+ If the above steps don’t help, create an issue. Include:
23
+
24
+ - Detailed steps to reproduce
25
+ - Complete backtraces for exceptions
26
+
27
+ ## New Features
28
+
29
+ If you’d like to discuss a new feature, create an issue and start the title with `[Idea]`.
30
+
31
+ ## Pull Requests
32
+
33
+ Fork the project and create a pull request. A few tips:
34
+
35
+ - Keep changes to a minimum. If you have multiple features or fixes, submit multiple pull requests.
36
+ - Follow the existing style. The code should read like it’s written by a single person.
37
+ - Add one or more tests if possible. Make sure existing tests pass with:
38
+
39
+ ```sh
40
+ bundle exec rake test
41
+ ```
42
+
43
+ Feel free to open an issue to get feedback on your idea before spending too much time on it.
44
+
45
+ ## Dev Setup
46
+
47
+ On Mac:
48
+
49
+ ```sh
50
+ # install and run PostgreSQL
51
+ brew install postgresql
52
+ brew services start postgresql
53
+
54
+ # install and run MySQL
55
+ brew install mysql
56
+ brew services start mysql
57
+
58
+ # create databases
59
+ createdb groupdate_test
60
+ mysql -u root -e "create database groupdate_test"
61
+ mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql
62
+
63
+ # clone the repo and run the tests
64
+ git clone https://github.com/ankane/groupdate.git
65
+ cd groupdate
66
+ bundle install
67
+ bundle exec rake test
68
+
69
+ # run a single test file
70
+ ruby test/postgresql_test.rb
71
+ ```
72
+
73
+ ---
74
+
75
+ This contributing guide is released under [CCO](https://creativecommons.org/publicdomain/zero/1.0/) (public domain). Use it for your own project without attribution.
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013-2019 Andrew Kane
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,16 @@
1
+ # Groupdate2
2
+ groupdate2 is an enhanced version of the beautiful [Groupdate](https://github.com/ankane/groupdate) gem.
3
+ It adds MS SQL Server (2016 and above) support by using `(AT TIME ZONE)`.
4
+
5
+ In JRuby, it relies on `activerecord-sqlserver-adapter` `jdbc-mode` branch, which is not ideal. The plan is using `activerecord-jdbcsqlserver-adapter` when JRuby 9.2+ is supported.
6
+
7
+ # Version
8
+ ## 4.1.x
9
+ This version corresponds to groupdate 4.1.2 with SQL Server support.
10
+
11
+ ## Installation
12
+ Add this line to your application’s Gemfile:
13
+ ```ruby
14
+ gem 'groupdate2'
15
+ ```
16
+
@@ -0,0 +1,6 @@
1
+ require "active_record"
2
+ require "groupdate/query_methods"
3
+ require "groupdate/relation"
4
+
5
+ ActiveRecord::Base.extend(Groupdate::QueryMethods)
6
+ ActiveRecord::Relation.include(Groupdate::Relation)
@@ -0,0 +1,31 @@
1
+ module Enumerable
2
+ Groupdate::PERIODS.each do |period|
3
+ define_method :"group_by_#{period}" do |*args, &block|
4
+ if block
5
+ Groupdate::Magic::Enumerable.group_by(self, period, args[0] || {}, &block)
6
+ elsif respond_to?(:scoping)
7
+ scoping { @klass.send(:"group_by_#{period}", *args, &block) }
8
+ else
9
+ raise ArgumentError, "no block given"
10
+ end
11
+ end
12
+ end
13
+
14
+ def group_by_period(*args, &block)
15
+ if block || !respond_to?(:scoping)
16
+ period = args[0]
17
+ options = args[1] || {}
18
+
19
+ options = options.dup
20
+ # to_sym is unsafe on user input, so convert to strings
21
+ permitted_periods = ((options.delete(:permit) || Groupdate::PERIODS).map(&:to_sym) & Groupdate::PERIODS).map(&:to_s)
22
+ if permitted_periods.include?(period.to_s)
23
+ send("group_by_#{period}", options, &block)
24
+ else
25
+ raise ArgumentError, "Unpermitted period"
26
+ end
27
+ else
28
+ scoping { @klass.send(:group_by_period, *args, &block) }
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,164 @@
1
+ require "i18n"
2
+
3
+ module Groupdate
4
+ class Magic
5
+ attr_accessor :period, :options, :group_index
6
+
7
+ def initialize(period:, **options)
8
+ @period = period
9
+ @options = options
10
+
11
+ unknown_keywords = options.keys - [:day_start, :time_zone, :dates, :series, :week_start, :format, :locale, :range, :reverse]
12
+ raise ArgumentError, "unknown keywords: #{unknown_keywords.join(", ")}" if unknown_keywords.any?
13
+
14
+ raise Groupdate::Error, "Unrecognized time zone" unless time_zone
15
+ raise Groupdate::Error, "Unrecognized :week_start option" if period == :week && !week_start
16
+ raise Groupdate::Error, "Cannot use endless range for :range option" if options[:range].is_a?(Range) && !options[:range].end
17
+ end
18
+
19
+ def time_zone
20
+ @time_zone ||= begin
21
+ time_zone = "Etc/UTC" if options[:time_zone] == false
22
+ time_zone ||= options[:time_zone] || Groupdate.time_zone || (Groupdate.time_zone == false && "Etc/UTC") || Time.zone || "Etc/UTC"
23
+ time_zone.is_a?(ActiveSupport::TimeZone) ? time_zone : ActiveSupport::TimeZone[time_zone]
24
+ end
25
+ end
26
+
27
+ def week_start
28
+ @week_start ||= [:mon, :tue, :wed, :thu, :fri, :sat, :sun].index((options[:week_start] || options[:start] || Groupdate.week_start).to_sym)
29
+ end
30
+
31
+ def day_start
32
+ @day_start ||= ((options[:day_start] || Groupdate.day_start).to_f * 3600).round
33
+ end
34
+
35
+ def series_builder
36
+ @series_builder ||=
37
+ SeriesBuilder.new(
38
+ **options,
39
+ period: period,
40
+ time_zone: time_zone,
41
+ day_start: day_start,
42
+ week_start: week_start
43
+ )
44
+ end
45
+
46
+ def time_range
47
+ series_builder.time_range
48
+ end
49
+
50
+ class Enumerable < Magic
51
+ def group_by(enum, &_block)
52
+ group = enum.group_by do |v|
53
+ v = yield(v)
54
+ raise ArgumentError, "Not a time" unless v.respond_to?(:to_time)
55
+ series_builder.round_time(v)
56
+ end
57
+ series_builder.generate(group, default_value: [], series_default: false)
58
+ end
59
+
60
+ def self.group_by(enum, period, options, &block)
61
+ Groupdate::Magic::Enumerable.new(period: period, **options).group_by(enum, &block)
62
+ end
63
+ end
64
+
65
+ class Relation < Magic
66
+ def initialize(**options)
67
+ super(**options.reject { |k, _| [:default_value, :carry_forward, :last, :current].include?(k) })
68
+ @options = options
69
+ end
70
+
71
+ def perform(relation, result, default_value:)
72
+ multiple_groups = relation.group_values.size > 1
73
+
74
+ check_nils(result, multiple_groups, relation)
75
+ result = cast_result(result, multiple_groups)
76
+
77
+ series_builder.generate(
78
+ result,
79
+ default_value: options.key?(:default_value) ? options[:default_value] : default_value,
80
+ multiple_groups: multiple_groups,
81
+ group_index: group_index
82
+ )
83
+ end
84
+
85
+ def cast_method
86
+ @cast_method ||= begin
87
+ case period
88
+ when :day_of_week
89
+ lambda { |k| (k.to_i - 1 - week_start) % 7 }
90
+ when :hour_of_day, :day_of_month, :month_of_year, :minute_of_hour
91
+ lambda { |k| k.to_i }
92
+ else
93
+ utc = ActiveSupport::TimeZone["UTC"]
94
+ lambda { |k| (k.is_a?(String) || !k.respond_to?(:to_time) ? utc.parse(k.to_s) : k.to_time).in_time_zone(time_zone) }
95
+ end
96
+ end
97
+ end
98
+
99
+ def cast_result(result, multiple_groups)
100
+ new_result = {}
101
+ result.each do |k, v|
102
+ if multiple_groups
103
+ k[group_index] = cast_method.call(k[group_index])
104
+ else
105
+ k = cast_method.call(k)
106
+ end
107
+ new_result[k] = v
108
+ end
109
+ new_result
110
+ end
111
+
112
+ def time_zone_support?(relation)
113
+ if relation.connection.adapter_name =~ /mysql/i
114
+ # need to call klass for Rails < 5.2
115
+ sql = relation.klass.send(:sanitize_sql_array, ["SELECT CONVERT_TZ(NOW(), '+00:00', ?)", time_zone.tzinfo.name])
116
+ !relation.connection.select_all(sql).first.values.first.nil?
117
+ else
118
+ true
119
+ end
120
+ end
121
+
122
+ def check_nils(result, multiple_groups, relation)
123
+ has_nils = multiple_groups ? (result.keys.first && result.keys.first[group_index].nil?) : result.key?(nil)
124
+ if has_nils
125
+ if time_zone_support?(relation)
126
+ raise Groupdate::Error, "Invalid query - be sure to use a date or time column"
127
+ else
128
+ raise Groupdate::Error, "Database missing time zone support for #{time_zone.tzinfo.name} - see https://github.com/ankane/groupdate#for-mysql"
129
+ end
130
+ end
131
+ end
132
+
133
+ def self.generate_relation(relation, field:, **options)
134
+ magic = Groupdate::Magic::Relation.new(**options)
135
+
136
+ # generate ActiveRecord relation
137
+ relation =
138
+ RelationBuilder.new(
139
+ relation,
140
+ column: field,
141
+ period: magic.period,
142
+ time_zone: magic.time_zone,
143
+ time_range: magic.time_range,
144
+ week_start: magic.week_start,
145
+ day_start: magic.day_start
146
+ ).generate
147
+
148
+ # add Groupdate info
149
+ magic.group_index = relation.group_values.size - 1
150
+ (relation.groupdate_values ||= []) << magic
151
+
152
+ relation
153
+ end
154
+
155
+ # allow any options to keep flexible for future
156
+ def self.process_result(relation, result, **options)
157
+ relation.groupdate_values.reverse.each do |gv|
158
+ result = gv.perform(relation, result, default_value: options[:default_value])
159
+ end
160
+ result
161
+ end
162
+ end
163
+ end
164
+ end