calculate-all 0.2.1 → 0.2.2

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
- SHA1:
3
- metadata.gz: 35426c107c947f1f28da7166c703bade37858910
4
- data.tar.gz: 810c8b1b9020a9ec4aef2886b6a6fc523f44340e
2
+ SHA256:
3
+ metadata.gz: 3cd324cd911d0d4998540ce59b30addac7a8e9842676f1adbf75b5bf6adee1c0
4
+ data.tar.gz: 26ff146cce10d207334d6232e72dae5d8d558246fd47f58da7571a6e84bbba7a
5
5
  SHA512:
6
- metadata.gz: d90936efd4b9a78893e534b347c92934becc305281a7e2b2e9582644eaf3e063da8311c30a8da2b48ca0fa893024a6800e008af964d21a7f3eb64b4c2789a4d9
7
- data.tar.gz: ac2dcf69499e33939e28f5b9bc7cd9582e1796b628910edf1ac20173ce6c373f903d8b958eb94f4840927acfd2ead5b22d3b15be4dbec13f17d31a0debe604a4
6
+ metadata.gz: 1570c270af925b14130207b5905235d02f19960fc83f2ae189c67cd482a06538b341a031279daea5c8dc01933eb5303c0cc5db30a7fb706a4ff111c79bac4ec4
7
+ data.tar.gz: 38f29b21183c14901b61790e5e4a69ce70918e2c99bac4ba4a714a5ee8a1be4466bfcc6ab6cfd93e43a098041d8cbedfa4206c2adc1d6ce766111cdbd0db26f1
@@ -0,0 +1,39 @@
1
+ name: build
2
+ on: [push, pull_request]
3
+ jobs:
4
+ build:
5
+ strategy:
6
+ fail-fast: false
7
+ matrix:
8
+ include:
9
+ - ruby: "3.1"
10
+ gemfile: Gemfile
11
+ - ruby: "3.0"
12
+ gemfile: gemfiles/activerecord61.gemfile
13
+ - ruby: "2.7"
14
+ gemfile: gemfiles/activerecord60.gemfile
15
+ - ruby: "2.6"
16
+ gemfile: gemfiles/activerecord52.gemfile
17
+ - ruby: "2.6"
18
+ gemfile: gemfiles/activerecord51.gemfile
19
+ - ruby: "2.6"
20
+ gemfile: gemfiles/activerecord50.gemfile
21
+ - ruby: "2.5"
22
+ gemfile: gemfiles/activerecord42.gemfile
23
+ runs-on: ubuntu-latest
24
+ env:
25
+ BUNDLE_GEMFILE: ${{ matrix.gemfile }}
26
+ steps:
27
+ - uses: actions/checkout@v2
28
+ - uses: ruby/setup-ruby@v1
29
+ with:
30
+ ruby-version: ${{ matrix.ruby }}
31
+ bundler-cache: true
32
+ - uses: ankane/setup-postgres@v1
33
+ with:
34
+ database: calculate_all_test
35
+ - uses: ankane/setup-mysql@v1
36
+ with:
37
+ database: calculate_all_test
38
+ - run: mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql
39
+ - run: bundle exec rake test
data/.gitignore CHANGED
@@ -7,3 +7,5 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+
11
+ *.lock
data/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ ## 0.2.2
2
+
3
+ * Added support for Groupdate 4+ (Andrew <acekane1@gmail.com>)
4
+ * Tested with sqlite3, ruby 3.1, Rails 7
5
+
6
+ ## 0.2.1
7
+
8
+ * Silence deprecation warnings (Forrest Ye <fye@mutan.io>)
9
+
10
+ ## 0.2.0
11
+
12
+ * Rails 5 compatibility (Stef Schenkelaars <stef.schenkelaars@gmail.com>)
13
+
1
14
  ## 0.1.1
2
15
 
3
16
  * groupdate compatibility
data/Gemfile CHANGED
@@ -1,7 +1,13 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
- # Specify your gem's dependencies in calculate-all.gemspec
4
3
  gemspec
5
4
 
6
- gem 'activerecord'
7
- gem 'pry'
5
+ gem "rake"
6
+ gem "minitest"
7
+ gem "activerecord", "~> 7.0.0"
8
+ gem "groupdate", "~> 5.2.0"
9
+ gem "pg"
10
+ gem "mysql2"
11
+ gem "sqlite3", "~> 1.4"
12
+
13
+ gem "standardrb", require: false
data/README.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # CalculateAll
2
2
 
3
- Provides `#calculate_all` method on your Active Record models, scopes and relations.
4
- It's a little addition to Active Record's `#count`, `#maximum`, `#minimum`, `#average` and `#sum`.
5
- It allows to fetch all of the above and any other aggregate functions results in one request, with respect to grouping.
3
+ Provides the `#calculate_all` method for your Active Record models, scopes and relations.
4
+ It's a small addition to Active Record's `#count`, `#maximum`, `#minimum`, `#average` and `#sum`.
5
+ It allows you to fetch all of the above, as well as other aggregate function results,
6
+ in a single request, with support for grouping.
6
7
 
7
- Tested only with Postgres and MySQL only right now. It relies on automatic values type-casting of underlying driver.
8
+ Currently tested with Postgres, MySQL and sqlite3, ruby >= 2.3, rails >= 4, groupdate >= 4.
8
9
 
9
10
  ## Usage
10
11
 
@@ -27,8 +28,8 @@ stats = Order.group(:department_id).group(:payment_method).calculate_all(
27
28
  # count_distinct_user_id: 5,
28
29
  # price_max: 500,
29
30
  # price_min: 100,
30
- # price_avg: #<BigDecimal:7ff5932ff3d8,'0.3E3',9(27)>,
31
- # price_median: #<BigDecimal:7ff5932ff3c2,'0.4E3',9(27)>
31
+ # price_avg: 0.3e3,
32
+ # price_median: 0.4e3
32
33
  # },
33
34
  # [1, "card"] => {
34
35
  # ...
@@ -36,23 +37,24 @@ stats = Order.group(:department_id).group(:payment_method).calculate_all(
36
37
  # }
37
38
  ```
38
39
 
40
+ (median example works in Postgres only, but check out very cool https://github.com/ankane/active_median)
41
+
39
42
  ## Rationale
40
43
 
41
- Active Record allows to use most common DB aggregate functions, COUNT(), MAX(), MIN(), AVG(), SUM() really easy.
42
- But there's a whole world of wonderful other functions in
43
- [Postgres](http://www.postgresql.org/docs/9.5/static/functions-aggregate.html) which I can't recommend enough
44
- if you going to have any work with statistics and BI on your data, though MySQL has something
45
- [too](http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html).
44
+ Active Record makes it really easy to use most common database aggregate functions like COUNT(), MAX(), MIN(), AVG(), SUM().
45
+ But there's a whole world of other [powerful functions](http://www.postgresql.org/docs/current/functions-aggregate.html) in
46
+ Postgres, which I cant recommend enough, especially if you’re working with statistics or business intelligence.
47
+ MySQL has some useful ones [as well](https://dev.mysql.com/doc/refman/9.3/en/aggregate-functions.html).
46
48
 
47
- Also, in many cases you'll need several metrics at once, and database often has to perform a full scan on
48
- the table for each metric, but it as well can calculate them all in one scan and one request.
49
+ Also, in many cases, youll need multiple metrics at once. Typically, the database performs a full scan of the table for each metric.
50
+ However, it can calculate all of them in a single scan and a single request.
49
51
 
50
52
  `#calculate_all` to the rescue!
51
53
 
52
54
  ## Arguments
53
55
 
54
- `#calculate_all` accepts a list of expression aliases and/or expression mapping.
55
- It could be either one string of SQL,
56
+ `#calculate_all` accepts a list of expression aliases and/or expression mappings.
57
+ It can be a single string of SQL,
56
58
 
57
59
  ```ruby
58
60
  Model.calculate_all('SUM(price) / COUNT(DISTINCT user_id)')
@@ -63,7 +65,7 @@ a hash of expressions with arbitrary symbol keys
63
65
  ```ruby
64
66
  Model.calculate_all(total: 'COUNT(*)', average_spendings: 'SUM(price) / COUNT(DISTINCT user_id)')
65
67
  ```
66
- or a list of one or more symbols without expressions, in which case `#calculate_all` tries to guess
68
+ and/or a list of one or more symbols without expressions, in which case `#calculate_all` tries to guess
67
69
  what you wanted from it.
68
70
 
69
71
  ```ruby
@@ -82,6 +84,9 @@ It's not so smart right now, but here's a cheatsheet:
82
84
  | `:avg_column1`, `:column1_avg`, `:average_column1`, `:column1_average` | `AVG(column1)`
83
85
  | `:sum_column1`, `:column1_sum` | `SUM(column1)`
84
86
 
87
+ Please don't put values from unverified sources (like HTML form or javascript call) into expression list,
88
+ it could result in malicious SQL injection.
89
+
85
90
  ## Result
86
91
 
87
92
  `#calculate_all` tries to mimic magic of Active Record's `#group`, `#count` and `#pluck`
@@ -90,16 +95,16 @@ so result type depends on arguments and on groupings.
90
95
  If you have no `group()` on underlying scope, `#calculate_all` will return just one result.
91
96
 
92
97
  ```ruby
93
- # same as Order.distinct.count(:user_id), so, probably useless example
94
- # but you can have any expression with aggregate functions there.
98
+ # Same as Order.distinct.count(:user_id), so probably a useless example.
99
+ # But you can use any expression with aggregate functions there.
95
100
  Order.calculate_all('COUNT(DISTINCT user_id)')
96
101
  # => 50
97
102
  ```
98
103
 
99
- If you have one group, it will return hash of results, with simple keys.
104
+ If you have a single `group()`, it will return a hash of results with simple keys.
100
105
 
101
106
  ```ruby
102
- # again, Order.group(:department_id).distinct.count(:user_id) would do the same
107
+ # Again, Order.group(:department_id).distinct.count(:user_id) would do the same.
103
108
  Order.group(:department_id).calculate_all(:count_distinct_user_id)
104
109
  # => {
105
110
  # 1 => 20,
@@ -120,8 +125,8 @@ Order.group(:department_id).group(:department_method).calculate_all(:count_disti
120
125
  # }
121
126
  ```
122
127
 
123
- If you provide just one argument to `#calculate_all`, its calculated value will be returned as is.
124
- Otherwise results would be returned as hash(es) with symbol keys.
128
+ If you provide only one argument to `#calculate_all`, its calculated value will be returned as-is.
129
+ Otherwise, the results will be returned as hash(es) with symbol keys.
125
130
 
126
131
  so, `Order.calculate_all(:count)` will return just a single integer, but
127
132
 
@@ -134,8 +139,8 @@ Order.group(:department_id).group(:payment_method).calculate_all(:min_price, exp
134
139
  # }
135
140
  ```
136
141
 
137
- You can pass block to calculate_all. Rows will be passed to it and returned value will be used instead of
138
- row in result hash (or returned as is if there's no grouping)
142
+ You can pass a block to `calculate_all`. Rows will be passed to it, and returned value will be used instead of
143
+ the row in the result hash (or returned as-is if there's no grouping).
139
144
 
140
145
  ```ruby
141
146
  Order.group(:country_id).calculate_all(:count, :avg_price) { |count:, avg_price:|
@@ -161,16 +166,18 @@ Order.calculate_all(:count, :max_price, &OpenStruct.method(:new))
161
166
  calculate-all should work with [groupdate](https://github.com/ankane/groupdate) too:
162
167
 
163
168
  ```ruby
164
- Order.group_by_year(:created_at, last: 5, default_value: {}).calculate_all(:price_min, :price_max)
165
- => {
166
- Sun, 01 Jan 2012 => {},
167
- Tue, 01 Jan 2013 => {},
168
- Wed, 01 Jan 2014 => {},
169
- Thu, 01 Jan 2015 => {},
170
- Fri, 01 Jan 2016 => {:price_min=>100, :price_max=>500}
171
- }
169
+ Order.group_by_year(:created_at, last: 5).calculate_all(:price_min, :price_max)
170
+ # => {
171
+ # Sun, 01 Jan 2012 => {},
172
+ # Tue, 01 Jan 2013 => {},
173
+ # Wed, 01 Jan 2014 => {},
174
+ # Thu, 01 Jan 2015 => {},
175
+ # Fri, 01 Jan 2016 => {:price_min=>100, :price_max=>500}
176
+ # }
172
177
  ```
173
178
 
179
+ It works even with groupdate < 4, though you'd have to explicitly provide `default_value: {}` for blank periods.
180
+
174
181
  ## Installation
175
182
 
176
183
  Add this line to your application's Gemfile:
@@ -189,7 +196,9 @@ Or install it yourself as:
189
196
 
190
197
  ## Development
191
198
 
192
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
199
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests.
200
+ Run `BUNDLE_GEMFILE=gemfiles/activerecord60.gemfile bundle` then `BUNDLE_GEMFILE=gemfiles/activerecord60.gemfile rake`
201
+ to test agains specific active record version.
193
202
 
194
203
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
195
204
 
data/Rakefile CHANGED
@@ -1,10 +1,10 @@
1
- require 'bundler/gem_tasks'
2
- require 'rake/testtask'
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
3
 
4
4
  Rake::TestTask.new(:test) do |t|
5
- t.libs << 'test'
6
- t.libs << 'lib'
7
- t.test_files = FileList['test/**/*_test.rb']
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
8
  end
9
9
 
10
- task :default => :test
10
+ task default: :test
data/bin/setup CHANGED
@@ -2,6 +2,9 @@
2
2
  set -euo pipefail
3
3
  IFS=$'\n\t'
4
4
 
5
+ echo "Installing gems..."
5
6
  bundle install
6
-
7
- # Do any other automated setup that you need to do here
7
+ echo "Creating postgres database..."
8
+ createdb calculate_all_test || echo "...failed"
9
+ echo "Creating mysql database..."
10
+ mysqladmin -u root create calculate_all_test || echo "...failed"
@@ -1,30 +1,22 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path("../lib", __FILE__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'calculate-all/version'
3
+ require "calculate-all/version"
5
4
 
6
5
  Gem::Specification.new do |spec|
7
- spec.name = "calculate-all"
8
- spec.version = CalculateAll::VERSION
9
- spec.authors = ["codesnik"]
10
- spec.email = ["aronaxis@gmail.com"]
6
+ spec.name = "calculate-all"
7
+ spec.version = CalculateAll::VERSION
8
+ spec.authors = ["Alexey Trofimenko"]
9
+ spec.email = ["aronaxis@gmail.com"]
11
10
 
12
- spec.summary = %q{Fetch from database results of several aggregate functions at once}
13
- spec.description = %q{Extends Active Record with #calculate_all method}
14
- spec.homepage = "http://github.com/codesnik/calculate-all"
15
- spec.license = "MIT"
11
+ spec.summary = "Fetch from database results of several aggregate functions at once"
12
+ spec.description = "Extends Active Record with #calculate_all method"
13
+ spec.homepage = "http://github.com/codesnik/calculate-all"
14
+ spec.license = "MIT"
16
15
 
17
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
- spec.bindir = "exe"
19
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
19
  spec.require_paths = ["lib"]
21
20
 
22
- spec.add_dependency "activerecord"
23
-
24
- spec.add_development_dependency "bundler", "~> 1.10"
25
- spec.add_development_dependency "rake", "~> 10.0"
26
- spec.add_development_dependency "minitest"
27
- spec.add_development_dependency "pg"
28
- spec.add_development_dependency "mysql2"
29
- spec.add_development_dependency "groupdate"
21
+ spec.add_dependency "activesupport"
30
22
  end
@@ -0,0 +1,11 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: ".."
4
+
5
+ gem "rake"
6
+ gem "minitest", "~> 5.0"
7
+ gem "activerecord", "~> 4.2.0"
8
+ gem "groupdate", "~> 3.0.0"
9
+ gem "pg", "~> 0.15"
10
+ gem "mysql2"
11
+ gem "sqlite3", "~> 1.3.0"
@@ -0,0 +1,11 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: ".."
4
+
5
+ gem "rake"
6
+ gem "minitest"
7
+ gem "activerecord", "~> 5.0.0"
8
+ gem "groupdate", "~> 4.0.0"
9
+ gem "pg"
10
+ gem "mysql2"
11
+ gem "sqlite3", "~> 1.3.0"
@@ -0,0 +1,11 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: ".."
4
+
5
+ gem "rake"
6
+ gem "minitest"
7
+ gem "activerecord", "~> 5.1.0"
8
+ gem "groupdate", "~> 5.0.0"
9
+ gem "pg"
10
+ gem "mysql2"
11
+ gem "sqlite3"
@@ -0,0 +1,11 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: ".."
4
+
5
+ gem "rake"
6
+ gem "minitest"
7
+ gem "activerecord", "~> 5.2.0"
8
+ gem "groupdate", "~> 5.0.0"
9
+ gem "pg"
10
+ gem "mysql2"
11
+ gem "sqlite3"
@@ -0,0 +1,11 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: ".."
4
+
5
+ gem "rake"
6
+ gem "minitest"
7
+ gem "activerecord", "~> 6.0.0"
8
+ gem "groupdate", "~> 5.0.0"
9
+ gem "pg"
10
+ gem "mysql2"
11
+ gem "sqlite3"
@@ -0,0 +1,11 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: ".."
4
+
5
+ gem "rake"
6
+ gem "minitest"
7
+ gem "activerecord", "~> 6.1.0"
8
+ gem "groupdate", "~> 5.0.0"
9
+ gem "pg"
10
+ gem "mysql2"
11
+ gem "sqlite3", "~> 1.4"
@@ -1,7 +1,8 @@
1
1
  module CalculateAll
2
2
  module Helpers
3
3
  module_function
4
- # Method to convert function aliases like :count to SQL commands like 'COUNT(*)'
4
+
5
+ # Convert aliases like :count to SQL aggregate functions like 'COUNT(*)'
5
6
  def decode_function_aliases(aliases)
6
7
  aliases.map do |key|
7
8
  function =
@@ -9,7 +10,7 @@ module CalculateAll
9
10
  when String
10
11
  key
11
12
  when :count
12
- 'COUNT(*)'
13
+ "COUNT(*)"
13
14
  when /^(.*)_distinct_count$/, /^count_distinct_(.*)$/
14
15
  "COUNT(DISTINCT #{$1})"
15
16
  when /^(.*)_(count|sum|max|min|avg)$/
@@ -1,5 +1,8 @@
1
1
  module CalculateAll
2
2
  module Querying
3
- delegate :calculate_all, to: :all
3
+ # @see CalculateAll#calculate_all
4
+ def calculate_all(*args, **kwargs, &block)
5
+ all.calculate_all(*args, **kwargs, &block)
6
+ end
4
7
  end
5
8
  end
@@ -1,3 +1,3 @@
1
1
  module CalculateAll
2
- VERSION = '0.2.1'
2
+ VERSION = "0.2.2"
3
3
  end
data/lib/calculate-all.rb CHANGED
@@ -1,89 +1,88 @@
1
- require 'calculate-all/version'
2
- require 'calculate-all/helpers'
3
- require 'calculate-all/querying'
1
+ require "active_support"
2
+ require "calculate-all/version"
4
3
 
5
4
  module CalculateAll
6
- # Method to aggregate function results in one request
5
+ # Calculates multiple aggregate values on a scope in one request, similarly to #calculate
7
6
  def calculate_all(*function_aliases, **functions, &block)
8
-
9
- # If only one function_alias is given, the result can be just a single value
10
- # So return [{ cash: 3 }] instead of [{ cash: { count: 3 }}]
11
- if function_aliases.size == 1 && functions == {}
7
+ # If only one aggregate is given without explicit naming,
8
+ # return row(s) directly without wrapping in Hash
9
+ if function_aliases.size == 1 && functions.size == 0
12
10
  return_plain_values = true
13
11
  end
14
12
 
15
13
  # Convert the function_aliases to actual SQL
16
- functions.merge!(
17
- CalculateAll::Helpers.decode_function_aliases(function_aliases)
18
- )
14
+ functions.merge!(CalculateAll::Helpers.decode_function_aliases(function_aliases))
19
15
 
20
16
  # Check if any functions are given
21
17
  if functions == {}
22
- raise ArgumentError, 'provide at least one function to calculate'
23
- end
24
-
25
- # If function is called without a group, the pluck method will still return
26
- # an array but it is an array with the final results instead of each group
27
- # The plain_rows boolean states how the results should be used
28
- if functions.size == 1 && group_values.size == 0
29
- plain_rows = true
18
+ raise ArgumentError, "provide at least one function to calculate"
30
19
  end
31
20
 
32
- # Final output hash
21
+ columns = (group_values.map(&:to_s) + functions.values).map { |sql| Arel.sql(sql) }
33
22
  results = {}
23
+ pluck(*columns).each do |row|
24
+ # If pluck called without any groups and with a single argument,
25
+ # it will return an array of simple results instead of array of arrays
26
+ if functions.size == 1 && group_values.size == 0
27
+ row = [row]
28
+ end
34
29
 
35
- # Fetch all the requested calculations from the database
36
- # Note the map(&:to_s). It is required since groupdate returns a
37
- # Groupdate::OrderHack instead of a string for the group_values which is not
38
- # accepted by ActiveRecord's pluck method.
39
- sql_snippets = group_values.map(&:to_s) + functions.values
40
- # Fix DEPRECATION WARNING:
41
- # Dangerous query method, will be disallowed in Rails 6.0
42
- # using Arel.sql() to silence the warning
43
- # https://github.com/rails/rails/commit/310c3a8f2d043f3d00d3f703052a1e160430a2c2
44
- pluck(*sql_snippets.map { |sql| Arel.sql(sql) }).each do |row|
45
-
46
- # If no grouping, make sure it is still a results array
47
- row = [row] if plain_rows
48
-
49
- # If only one value, return a single value, else return a hash
50
- if return_plain_values
51
- value = row.last
30
+ key = if group_values.size == 0
31
+ :ALL
32
+ elsif group_values.size == 1
33
+ # If only one group is provided, the resulting key is just a scalar value
34
+ row.shift
52
35
  else
53
- value = functions.keys.zip(row.last(functions.size)).to_h
36
+ # if multiple groups, the key will be an array.
37
+ row.shift(group_values.size)
54
38
  end
55
39
 
56
- # Call the block for each group
57
- value = block.call(value) if block
58
-
59
- # Return unwrapped hash directly for scope without any .group()
60
- return value if group_values.empty?
61
-
62
- # If only one group is provided, the resulting key is just the group name
63
- # if multiple group methods are provided, the key will be an array.
64
- if group_values.size == 1
65
- key = row.first
40
+ value = if return_plain_values
41
+ row.last
66
42
  else
67
- key = row.first(group_values.size)
43
+ # it is possible to have more actual group values returned than group_values.size
44
+ functions.keys.zip(row.last(functions.size)).to_h
68
45
  end
69
46
 
70
- # Set the value in the output array
71
47
  results[key] = value
72
48
  end
73
49
 
74
- # Return the output array
75
- results
50
+ # Additional groupdate magic of filling empty periods with defaults
51
+ if defined?(Groupdate.process_result)
52
+ # Since that hash is the same instance for every backfilled raw, at least
53
+ # freeze it to prevent surprize modifications in calling code.
54
+ default_value = return_plain_values ? nil : {}.freeze
55
+ results = Groupdate.process_result(self, results, default_value: default_value)
56
+ end
57
+
58
+ if block
59
+ results.transform_values! do |value|
60
+ return_plain_values ? block.call(value) : block.call(**value)
61
+ end
62
+ end
63
+
64
+ # Return unwrapped hash directly for scope without any .group()
65
+ if group_values.empty?
66
+ results[:ALL]
67
+ else
68
+ results
69
+ end
76
70
  end
77
71
  end
78
72
 
79
- # Make the calculate_all method available for all ActiveRecord::Relations instances
80
- ActiveRecord::Relation.include CalculateAll
73
+ ActiveSupport.on_load(:active_record) do
74
+ require "calculate-all/helpers"
75
+ require "calculate-all/querying"
81
76
 
82
- # Make the calculate_all method available for all ActiveRecord::Base classes
83
- # You can for example call Orders.calculate_all(:count, :sum_cents)
84
- ActiveRecord::Base.extend CalculateAll::Querying
77
+ # Make the calculate_all method available for all ActiveRecord::Relations instances
78
+ ActiveRecord::Relation.include CalculateAll
85
79
 
86
- # A hack for groupdate since it checks if the calculate_all method is defined
87
- # on the ActiveRecord::Calculations module. It is never called but it is just
88
- # needed for the check.
89
- ActiveRecord::Calculations.include CalculateAll::Querying
80
+ # Make the calculate_all method available for all ActiveRecord::Base classes
81
+ # You can for example call Orders.calculate_all(:count, :sum_cents)
82
+ ActiveRecord::Base.extend CalculateAll::Querying
83
+
84
+ # A hack for groupdate 3.0 since it checks if the calculate_all method is defined
85
+ # on the ActiveRecord::Calculations module. It is never called but it is just
86
+ # needed for the check.
87
+ ActiveRecord::Calculations.include CalculateAll::Querying
88
+ end
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: calculate-all
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
- - codesnik
8
- autorequire:
7
+ - Alexey Trofimenko
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-08-13 00:00:00.000000000 Z
11
+ date: 2025-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: activerecord
14
+ name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
@@ -24,90 +24,6 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: bundler
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '1.10'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '1.10'
41
- - !ruby/object:Gem::Dependency
42
- name: rake
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '10.0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '10.0'
55
- - !ruby/object:Gem::Dependency
56
- name: minitest
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: pg
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: mysql2
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: groupdate
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'
111
27
  description: 'Extends Active Record with #calculate_all method'
112
28
  email:
113
29
  - aronaxis@gmail.com
@@ -115,17 +31,22 @@ executables: []
115
31
  extensions: []
116
32
  extra_rdoc_files: []
117
33
  files:
34
+ - ".github/workflows/build.yml"
118
35
  - ".gitignore"
119
- - ".travis.yml"
120
36
  - CHANGELOG.md
121
37
  - Gemfile
122
38
  - LICENSE.txt
123
39
  - README.md
124
40
  - Rakefile
125
41
  - _config.yml
126
- - bin/console
127
42
  - bin/setup
128
43
  - calculate-all.gemspec
44
+ - gemfiles/activerecord42.gemfile
45
+ - gemfiles/activerecord50.gemfile
46
+ - gemfiles/activerecord51.gemfile
47
+ - gemfiles/activerecord52.gemfile
48
+ - gemfiles/activerecord60.gemfile
49
+ - gemfiles/activerecord61.gemfile
129
50
  - lib/calculate-all.rb
130
51
  - lib/calculate-all/helpers.rb
131
52
  - lib/calculate-all/querying.rb
@@ -134,7 +55,7 @@ homepage: http://github.com/codesnik/calculate-all
134
55
  licenses:
135
56
  - MIT
136
57
  metadata: {}
137
- post_install_message:
58
+ post_install_message:
138
59
  rdoc_options: []
139
60
  require_paths:
140
61
  - lib
@@ -149,9 +70,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
149
70
  - !ruby/object:Gem::Version
150
71
  version: '0'
151
72
  requirements: []
152
- rubyforge_project:
153
- rubygems_version: 2.4.5.1
154
- signing_key:
73
+ rubygems_version: 3.4.19
74
+ signing_key:
155
75
  specification_version: 4
156
76
  summary: Fetch from database results of several aggregate functions at once
157
77
  test_files: []
data/.travis.yml DELETED
@@ -1,4 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.2.0
4
- before_install: gem install bundler -v 1.10.6
data/bin/console DELETED
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'bundler/setup'
4
- require 'calculate-all'
5
-
6
- require './test/test_helper'
7
- require 'logger'
8
- ActiveRecord::Base.logger = Logger.new(STDERR)
9
- require 'pry'
10
- Pry.start