calculate_in_group 0.0.1 → 0.0.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 +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
|
+
[](https://www.railsjazz.com)
|
4
|
+
[](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
|