groupdate2 4.1.5

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