calculate_in_group 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +43 -11
- data/lib/calculate_in_group/version.rb +1 -1
- data/lib/calculate_in_group.rb +21 -4
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a355bb8d2b965bf9e26129775305e20c02b7d8fed65331e63aa058c4ea4d26d7
|
4
|
+
data.tar.gz: 2179b1820693525eea6ce4ba9a28d25f0c2351133c0dccad55ac90a0130178d9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c4988070914bff246095f5fdce43e3fa77aa1f020760fd6857ce88750f2c675e301c61c4aa9ae9cb9d81a144f498fcf6a840284f62bf8bf45d2654710cf91f7
|
7
|
+
data.tar.gz: 302d7f0906ddee7951cd7a23cb2b4248de37bb3a6686e10b7d7f261b5615a39edd92747a7e4232c50709d22ed9fc4c5fbd8f73539ca01fa706b0cec065a36edf
|
data/README.md
CHANGED
@@ -1,11 +1,46 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# calculate_in_group
|
2
|
+
|
3
|
+
[![RailsJazz](https://github.com/igorkasyanchuk/rails_time_travel/blob/main/docs/my_other.svg?raw=true)](https://www.railsjazz.com)
|
4
|
+
[![https://www.patreon.com/igorkasyanchuk](https://github.com/igorkasyanchuk/rails_time_travel/blob/main/docs/patron.svg?raw=true)](https://www.patreon.com/igorkasyanchuk)
|
5
|
+
|
6
|
+
Group ActiveRecord models with ranges. No more need to SQL with complex statements. Make your life easier :)
|
7
|
+
|
8
|
+
Can solve problems like "I need to group users by age in different categories." or "I need to do some calculations for the reports/charts".
|
9
|
+
|
10
|
+
Easy to use, just add to Gemfile `gem "calculate_in_group"` and call `calculate_in_group` on your model.
|
3
11
|
|
4
12
|
## Usage
|
5
|
-
|
13
|
+
|
14
|
+
See below how to group your model by ranges or arrays and run aggregations for them in one SQL query.
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
# Grouping can be used with :count, :average, :sum, :maximum, :minimum.
|
18
|
+
|
19
|
+
# Group with Ranges
|
20
|
+
User.calculate_in_group(:count, :age, [...10, 10...50, 50..] # => {"...10"=>1, "10...50"=>3, "50.."=>3}
|
21
|
+
User.calculate_in_group(:count, :created_at, { "old" => 12.hours.ago..1.minutes.ago, "new" => Time.now..10.hours.from_now }) # => {"old" => 2, "new" => 1}
|
22
|
+
|
23
|
+
# Group with arrays or just values
|
24
|
+
User.calculate_in_group(:count, :role, "with_permissions" => ["admin", "moderator"], "no_permissions" => "user") # => {"with_permissions" => 3, "no_permissions" => 3}
|
25
|
+
|
26
|
+
# Other agg functions
|
27
|
+
User.calculate_in_group(:average, :age, "young" => 0..25, "old" => 60..100) # => {"young" => 11.0, "old" => 80.0}
|
28
|
+
User.calculate_in_group(:average, :age, "young" => 0..25, "old" => 60...100) # => {"young" => 11.0, "old" => 60.0}
|
29
|
+
User.calculate_in_group(:maximum, :age, "young" => 0..25, "old" => 60..100) # => {"young" => 20, "old" => 100}
|
30
|
+
User.calculate_in_group(:minimum, :age, "young" => 0..25, "old" => 60..100) # => {"young" => 3, "old" => 60}
|
31
|
+
User.calculate_in_group(:sum, :age, "young" => 0..25, "old" => 60..100) # => {"young" => 33, "old" => 160}
|
32
|
+
User.calculate_in_group(:sum, :age, {"young" => 0..25, "old" => 60..100}) # => {"young" => 33, "old" => 160}
|
33
|
+
|
34
|
+
# You can specify "other values" (with custom label) which are out of ranges
|
35
|
+
User.calculate_in_group(:count, :age, {"young" => 10, "average" => 25, "old" => 60}, {include_nil: "OTHER"}) # => {"young" => 1, "old" => 1, "OTHER" => 7}
|
36
|
+
|
37
|
+
# You can specify default value for keys which are missing in query
|
38
|
+
User.calculate_in_group(:count, :age, {"young" => 10, "average" => 25, "old" => 60}, { default_for_missing: 0 }) # => {"young" => 1, "old" => 1, "average" => 0}
|
39
|
+
|
40
|
+
# SEE MORE EXAMLES in test/calculate_in_group_test.rb
|
41
|
+
```
|
6
42
|
|
7
43
|
## Installation
|
8
|
-
Add this line to your application's Gemfile:
|
9
44
|
|
10
45
|
```ruby
|
11
46
|
gem "calculate_in_group"
|
@@ -16,19 +51,16 @@ And then execute:
|
|
16
51
|
$ bundle
|
17
52
|
```
|
18
53
|
|
19
|
-
Or install it yourself as:
|
20
|
-
```bash
|
21
|
-
$ gem install calculate_in_group
|
22
|
-
```
|
23
|
-
|
24
54
|
## Testing
|
25
55
|
|
26
|
-
ruby test/calculate_in_group_test.rb
|
56
|
+
`ruby test/calculate_in_group_test.rb`.
|
27
57
|
|
28
58
|
Not sure, why rake test doesn't works for me :)
|
29
59
|
|
30
60
|
## Contributing
|
31
|
-
|
61
|
+
|
62
|
+
You are welcome to contribute or share your ideas.
|
32
63
|
|
33
64
|
## License
|
65
|
+
|
34
66
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/lib/calculate_in_group.rb
CHANGED
@@ -3,10 +3,12 @@ require "calculate_in_group/railtie"
|
|
3
3
|
|
4
4
|
module CalculateInGroup
|
5
5
|
module QueryMethods
|
6
|
-
def calculate_in_group(operation_type, field, groups
|
6
|
+
def calculate_in_group(operation_type, field, groups, options = {})
|
7
7
|
# check if arguments are good
|
8
|
+
raise ArgumentError.new("Please specify options for groups. Check the documentation") if groups.empty?
|
8
9
|
raise ArgumentError.new("Operation #{operation_type} not supported. Try to use: :count, :average, :sum, :maximum, :minimum") if ![:count, :average, :sum, :maximum, :minimum].include?(operation_type.to_s.to_sym)
|
9
10
|
raise ArgumentError.new("Column #{field} not found in `#{table_name}`") if !column_names.include?(field.to_s)
|
11
|
+
raise ArgumentError.new("Groups `#{groups}` can be array or hash. Check the documentation") unless groups.is_a?(Array) || groups.is_a?(Hash)
|
10
12
|
|
11
13
|
# init variables
|
12
14
|
table = self.arel_table
|
@@ -14,14 +16,29 @@ module CalculateInGroup
|
|
14
16
|
operation_attribute = "__#{operation_type}_all"
|
15
17
|
query = Arel::Nodes::Case.new
|
16
18
|
conditions = []
|
19
|
+
groupings = groups.is_a?(Array) ? groups.inject({}) {|res, e| res[e.to_s] = e; res} : groups
|
17
20
|
|
18
21
|
# build conditions
|
19
|
-
|
22
|
+
groupings.each do |(k, v)|
|
20
23
|
c = case v
|
21
24
|
when Range
|
22
25
|
# range could be endless, so we need to compact and build correct SQL for "between"
|
23
26
|
a = table[field].gteq(v.begin) if v.begin
|
24
|
-
|
27
|
+
if v.end
|
28
|
+
b = if v.exclude_end? # e.g. 5...10 => [5,6,7,8,9]
|
29
|
+
# [3] pry(#<CalculateInGroupTest>)> a = 0...10
|
30
|
+
# a.to_a => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
31
|
+
# [4] pry(#<CalculateInGroupTest>)> a.exclude_end?
|
32
|
+
# => true
|
33
|
+
table[field].lt(v.end)
|
34
|
+
else
|
35
|
+
# [1] pry(#<CalculateInGroupTest>)> a = 0..10
|
36
|
+
# a.to_a => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
37
|
+
# [2] pry(#<CalculateInGroupTest>)> a.exclude_end?
|
38
|
+
# => false
|
39
|
+
table[field].lteq(v.end) # e.g. 5..10 => [5,6,7,8,9,10]
|
40
|
+
end
|
41
|
+
end
|
25
42
|
[a, b].compact.inject(&:and)
|
26
43
|
when Array
|
27
44
|
# SQL "IN"
|
@@ -60,7 +77,7 @@ module CalculateInGroup
|
|
60
77
|
|
61
78
|
# check if we need to build full hash with all grouped fields
|
62
79
|
if options.has_key?(:default_for_missing)
|
63
|
-
(
|
80
|
+
(groupings.keys.map(&:to_s) - result.keys).each do |k|
|
64
81
|
result[k] = options[:default_for_missing]
|
65
82
|
end
|
66
83
|
end
|