calculate-all 0.3.0 → 0.4.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
  SHA256:
3
- metadata.gz: 0ccade1f3bc4b62c2842ef335e27d0c7a11a90fa18e90f3b1260bb572243a4b6
4
- data.tar.gz: 9d8dd96e03c6155f828d5fb586a652e9f0d94a5cbb21cfb41fc3c5022772501c
3
+ metadata.gz: d3928cde50fac04c8f8602ab9e83c685d190e8466febbb2c0314cf962182f99c
4
+ data.tar.gz: 83f9636ffda1f25e0faabc5454a4c731648371ba20572e5df53357f72e9b5708
5
5
  SHA512:
6
- metadata.gz: df3d5a6c997473a188f824f865951c05ed8d054758906c6647d4c868a55a37cb55cc0c5981b0bbe4106214d386a03c11fcb41211498c58b7f022a779b265d192
7
- data.tar.gz: ca3c3d75e899ff84f85886aeb649f86cc81f0612476297102036a7d991ed872819381c9505f7ed34b3713aed508960f71d8377ac986d92599f9bf585fe493d78
6
+ metadata.gz: 0a13ddf4a236a27bebbdf1bf7f316c1137eff5eb6cfb3a0589a0f91dfa0548b6167cc4441173ca0ce4b174a156092aa50185ec5f111e54f6c0e75ee3c738d430
7
+ data.tar.gz: 95f229487220d5bb23ef2a6a50b9aef7a782e25b627c3d87716690a0673fc6890e28c3ab023481f475837a0d595baac84078ef31841c0e3b135081c61eda47b9
@@ -8,6 +8,8 @@ jobs:
8
8
  include:
9
9
  - ruby: "3.4"
10
10
  gemfile: Gemfile
11
+ - ruby: "3.2"
12
+ gemfile: gemfiles/activerecord71.gemfile
11
13
  - ruby: "3.1"
12
14
  gemfile: gemfiles/activerecord70.gemfile
13
15
  - ruby: "3.0"
@@ -16,11 +18,9 @@ jobs:
16
18
  gemfile: gemfiles/activerecord60.gemfile
17
19
  - ruby: "2.6"
18
20
  gemfile: gemfiles/activerecord52.gemfile
19
- - ruby: "2.6"
20
- gemfile: gemfiles/activerecord51.gemfile
21
21
  - ruby: "2.6"
22
22
  gemfile: gemfiles/activerecord50.gemfile
23
- - ruby: "2.3"
23
+ - ruby: "2.6"
24
24
  gemfile: gemfiles/activerecord42.gemfile
25
25
  runs-on: ubuntu-latest
26
26
  env:
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 0.4.0
2
+
3
+ * Add #async_calculate_all for rails > 7.1
4
+ * Drop support for ruby < 2.6, groupdate < 4
5
+
6
+ ## 0.3.1
7
+
8
+ * Fix some arguments exceptions
9
+
1
10
  ## 0.3.0
2
11
 
3
12
  * Allow expression shortcuts as attribute values too for renaming
data/Gemfile CHANGED
@@ -11,6 +11,7 @@ gem "pg"
11
11
  gem "mysql2"
12
12
  gem "sqlite3", ">= 2.1"
13
13
 
14
+ gem "irb"
14
15
  gem "ostruct"
15
16
 
16
17
  gem "standardrb", require: false
data/README.md CHANGED
@@ -8,7 +8,10 @@ in a single request, with support for grouping.
8
8
 
9
9
  Should be useful for dashboards, timeseries stats, and charts.
10
10
 
11
- Currently tested with PostgreSQL, MySQL and SQLite3, ruby >= 2.3, rails >= 4, groupdate >= 4.
11
+ Currently tested with PostgreSQL, MySQL and SQLite3, ruby >= 2.6, rails >= 4, groupdate >= 4.
12
+
13
+ With rails >= 7.1, `#async_calculate_all` is also available, which does the same but in a separate
14
+ db connection and ruby thread, and returns `ActiveRecord::Promise`
12
15
 
13
16
  ## Usage
14
17
 
@@ -48,6 +51,16 @@ stats = Order.group(:department_id).group(:payment_method).order(:payment_method
48
51
  # }
49
52
  ```
50
53
 
54
+ ```ruby
55
+ stats_by_department = Order.group(:department_id).async_calculate_all(:count, :price_sum)
56
+ # continue with some other expensive stuff
57
+
58
+ # later, in some view
59
+ stats_by_department.value.each do |department_id, stats|
60
+ # ...
61
+ end
62
+ ```
63
+
51
64
  ## Rationale
52
65
 
53
66
  Active Record makes it really easy to use most common database aggregate functions like COUNT(), MAX(), MIN(), AVG(), SUM().
@@ -78,7 +91,7 @@ or arbitrary symbols and keyword arguments with SQL snippets, aggregate function
78
91
  )
79
92
  ```
80
93
 
81
- For convenience, `calculate_all(:count, :avg_column)` is the same as `caculate(count: :count, avg_column: :avg_column)`
94
+ For convenience, `calculate_all(:count, :avg_column)` is the same as `calculate_all(count: :count, avg_column: :avg_column)`
82
95
 
83
96
  Here's a cheatsheet of recognized shortcuts:
84
97
 
@@ -139,7 +152,7 @@ with aggregate functions.
139
152
  ```ruby
140
153
  Order.group(:payment_method).calculate_all('CAST(SUM(price) AS decimal) / COUNT(DISTINCT user_id)')
141
154
  # => {
142
- # "card" => 0.524e3
155
+ # "card" => 0.524e3,
143
156
  # "cash" => 0.132e3
144
157
  # }
145
158
  ```
@@ -186,7 +199,7 @@ Order.group_by_year(:created_at).calculate_all(*Stats.members, &Stats.method(:ne
186
199
  # => {
187
200
  # Wed, 01 Jan 2014 => #<data Stats count=2, max_price=700>,
188
201
  # Thu, 01 Jan 2015 => #<data Stats count=0, max_price=nil>,
189
- # Fri, 01 Jan 2016 => #<data Stats count=3, max_price800>
202
+ # Fri, 01 Jan 2016 => #<data Stats count=3, max_price=800>
190
203
  # }
191
204
  ```
192
205
 
@@ -227,7 +240,7 @@ Or install it yourself as:
227
240
 
228
241
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests.
229
242
  Run `BUNDLE_GEMFILE=gemfiles/activerecord60.gemfile bundle` then `BUNDLE_GEMFILE=gemfiles/activerecord60.gemfile rake`
230
- to test agains specific active record version.
243
+ to test against specific active record version.
231
244
 
232
245
  To experiment you can load a test database and jump to IRB with
233
246
 
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.required_ruby_version = '>= 2.3.0'
21
+ spec.required_ruby_version = ">= 2.6.0"
22
22
 
23
23
  spec.add_dependency "activesupport", ">= 4.0.0"
24
24
  end
@@ -6,7 +6,7 @@ gem "rake"
6
6
  gem "minitest", "~> 5.0"
7
7
  gem "minitest-reporters"
8
8
  gem "activerecord", "~> 4.2.0"
9
- gem "groupdate", "~> 3.0.0"
9
+ gem "groupdate", "~> 4.0.0"
10
10
  gem "pg", "~> 0.15"
11
11
  gem "mysql2"
12
12
  gem "sqlite3", "~> 1.3.0"
@@ -5,8 +5,8 @@ gemspec path: ".."
5
5
  gem "rake"
6
6
  gem "minitest"
7
7
  gem "minitest-reporters"
8
- gem "activerecord", "~> 5.1.0"
9
- gem "groupdate", "~> 5.0.0"
8
+ gem "activerecord", "~> 7.1.0"
9
+ gem "groupdate", "~> 6.0.0"
10
10
  gem "pg"
11
11
  gem "mysql2"
12
- gem "sqlite3"
12
+ gem "sqlite3", "~> 1.4"
@@ -1,3 +1,3 @@
1
1
  module CalculateAll
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/calculate-all.rb CHANGED
@@ -11,7 +11,7 @@ module CalculateAll
11
11
  return_plain_values = true
12
12
  end
13
13
 
14
- named_expressions = expression_shortcuts.map { |name| [name, name] }.to_h.merge(named_expressions)
14
+ named_expressions = expression_shortcuts.index_by(&:itself).merge(named_expressions)
15
15
 
16
16
  named_expressions.transform_values! do |shortcut|
17
17
  Helpers.decode_expression_shortcut(shortcut, group_values)
@@ -25,48 +25,55 @@ module CalculateAll
25
25
  value_mapping = named_expressions.transform_values { |column| columns.index(column) }
26
26
  columns.map! { |column| column.is_a?(String) ? Arel.sql(column) : column }
27
27
 
28
- results = {}
29
- pluck(*columns).each do |row|
30
- # If pluck called with with a single argument
31
- # it will return an array of sclars instead of array of arrays
32
- row = [row] if columns.size == 1
33
-
34
- key = if group_values.size == 0
35
- :ALL
36
- elsif group_values.size == 1
37
- # If only one group is provided, the resulting key is just a scalar value
38
- row.first
39
- else
40
- # if multiple groups, the key will be an array.
41
- row.first(group_values.size)
42
- end
28
+ pluck(*columns).then do |rows|
29
+ results = rows.to_h do |row|
30
+ # If pluck called with a single argument
31
+ # it will return an array of scalars instead of array of arrays
32
+ row = [row] if columns.size == 1
43
33
 
44
- value = value_mapping.transform_values { |index| row[index] }
34
+ key = if group_values.size == 0
35
+ :ALL
36
+ elsif group_values.size == 1
37
+ # If only one group is provided, the resulting key is just a scalar value
38
+ row.first
39
+ else
40
+ # if multiple groups, the key will be an array.
41
+ row.first(group_values.size)
42
+ end
45
43
 
46
- value = value.values.last if return_plain_values
44
+ value = value_mapping.transform_values { |index| row[index] }
47
45
 
48
- results[key] = value
49
- end
46
+ value = value.values.last if return_plain_values
50
47
 
51
- # Additional groupdate magic of filling empty periods with defaults
52
- if defined?(Groupdate.process_result)
53
- # Since that hash is the same instance for every backfilled row, at least
54
- # freeze it to prevent surprize modifications across multiple rows in the calling code.
55
- default_value = return_plain_values ? nil : {}.freeze
56
- results = Groupdate.process_result(self, results, default_value: default_value)
57
- end
48
+ [key, value]
49
+ end
50
+
51
+ # Additional groupdate magic of filling empty periods with defaults
52
+ if defined?(Groupdate.process_result)
53
+ # Since that hash is the same instance for every backfilled row, at least
54
+ # freeze it to prevent surprise modifications across multiple rows in the calling code.
55
+ default_value = return_plain_values ? nil : {}.freeze
56
+ results = Groupdate.process_result(self, results, default_value: default_value)
57
+ end
58
58
 
59
- if block
60
- results.transform_values! do |value|
61
- return_plain_values ? block.call(value) : block.call(**value)
59
+ if block
60
+ results.transform_values! do |value|
61
+ return_plain_values ? block.call(value) : block.call(**value)
62
+ end
63
+ end
64
+
65
+ # Return unwrapped hash directly for scope without any .group()
66
+ if group_values.empty?
67
+ results[:ALL]
68
+ else
69
+ results
62
70
  end
63
71
  end
72
+ end
64
73
 
65
- # Return unwrapped hash directly for scope without any .group()
66
- if group_values.empty?
67
- results[:ALL]
68
- else
69
- results
74
+ if Gem::Version.new(ActiveRecord.version) >= Gem::Version.new('7.1.0')
75
+ def async_calculate_all(*expression_shortcuts, **named_expressions, &block)
76
+ async.calculate_all(*expression_shortcuts, **named_expressions, &block)
70
77
  end
71
78
  end
72
79
 
@@ -97,15 +104,22 @@ module CalculateAll
97
104
  when /^(\w+)_minimum$/, /^minimum_(\w+)$/
98
105
  "MIN(#{$1})"
99
106
  else
100
- raise ArgumentError, "Can't recognize expression shortcut #{key}"
107
+ raise ArgumentError, "Can't recognize expression shortcut #{shortcut}"
101
108
  end
102
109
  end
103
110
  end
104
111
 
105
112
  module Querying
106
- # @see CalculateAll#calculate_all
107
- def calculate_all(*args, **kwargs, &block)
108
- all.calculate_all(*args, **kwargs, &block)
113
+ # see CalculateAll#calculate_all
114
+ def calculate_all(*expression_shortcuts, **named_expressions, &block)
115
+ all.calculate_all(*expression_shortcuts, **named_expressions, &block)
116
+ end
117
+
118
+ if Gem::Version.new(ActiveRecord.version) >= Gem::Version.new('7.1.0')
119
+ # see CalculateAll#async_calculate_all
120
+ def async_calculate_all(*expression_shortcuts, **named_expressions, &block)
121
+ all.async_calculate_all(*expression_shortcuts, **named_expressions, &block)
122
+ end
109
123
  end
110
124
  end
111
125
  end
@@ -117,9 +131,4 @@ ActiveSupport.on_load(:active_record) do
117
131
  # Make the calculate_all method available for all ActiveRecord::Base classes
118
132
  # You can for example call Orders.calculate_all(:count, :sum_cents)
119
133
  ActiveRecord::Base.extend CalculateAll::Querying
120
-
121
- # A hack for groupdate 3.0 since it checks if the calculate_all method is defined
122
- # on the ActiveRecord::Calculations module. It is never called but it is just
123
- # needed for the check.
124
- ActiveRecord::Calculations.include CalculateAll::Querying
125
134
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: calculate-all
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexey Trofimenko
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-05-18 00:00:00.000000000 Z
10
+ date: 2025-05-19 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activesupport
@@ -42,11 +42,11 @@ files:
42
42
  - calculate-all.gemspec
43
43
  - gemfiles/activerecord42.gemfile
44
44
  - gemfiles/activerecord50.gemfile
45
- - gemfiles/activerecord51.gemfile
46
45
  - gemfiles/activerecord52.gemfile
47
46
  - gemfiles/activerecord60.gemfile
48
47
  - gemfiles/activerecord61.gemfile
49
48
  - gemfiles/activerecord70.gemfile
49
+ - gemfiles/activerecord71.gemfile
50
50
  - lib/calculate-all.rb
51
51
  - lib/calculate-all/version.rb
52
52
  homepage: http://github.com/codesnik/calculate-all
@@ -60,7 +60,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
60
60
  requirements:
61
61
  - - ">="
62
62
  - !ruby/object:Gem::Version
63
- version: 2.3.0
63
+ version: 2.6.0
64
64
  required_rubygems_version: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ">="