active_median 0.2.0 → 0.2.5
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/CHANGELOG.md +29 -6
- data/LICENSE.txt +1 -1
- data/README.md +83 -11
- data/lib/active_median.rb +7 -1
- data/lib/active_median/enumerable.rb +41 -0
- data/lib/active_median/model.rb +63 -10
- data/lib/active_median/mongoid.rb +30 -0
- data/lib/active_median/version.rb +1 -1
- metadata +42 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cbc48a266db4698647e93b35bdad3e2ed1fc131ec48a2a00e1ac33583f693c4d
|
|
4
|
+
data.tar.gz: 9e49da11f496e63c1fca879b7f6b509cbc80b6551f22743f9f586dfdf6f3d2f3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7c37697facc95126f6b11bd29831f0aa685b781242a729c86e938e9f4be0a0b7fbfadbdf8ae246f987f452bb7977de21f2f879860f945f1a7bc9db005f85dc49
|
|
7
|
+
data.tar.gz: d37c90a02b845b8a2cd8e41ed5109da758ca51c9921675f7f63e30e0921e430eed193d6ae02676e3aa08c5eaf97207d29fd8f50540e1be060924022aa818dc03
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,27 @@
|
|
|
1
|
-
## 0.2.
|
|
1
|
+
## 0.2.5 (2020-09-07)
|
|
2
|
+
|
|
3
|
+
- Added warning for non-attribute argument
|
|
4
|
+
|
|
5
|
+
## 0.2.4 (2020-03-12)
|
|
6
|
+
|
|
7
|
+
- Added `percentile` method
|
|
8
|
+
|
|
9
|
+
## 0.2.3 (2019-09-03)
|
|
10
|
+
|
|
11
|
+
- Added support for Mongoid
|
|
12
|
+
- Dropped support for Rails 4.2
|
|
13
|
+
|
|
14
|
+
## 0.2.2 (2018-10-29)
|
|
15
|
+
|
|
16
|
+
- Added support for MySQL with udf_infusion
|
|
17
|
+
- Added support for SQL Server and Redshift
|
|
18
|
+
|
|
19
|
+
## 0.2.1 (2018-10-15)
|
|
20
|
+
|
|
21
|
+
- Added support for arrays and hashes
|
|
22
|
+
- Added compatibility with Groupdate 4
|
|
23
|
+
|
|
24
|
+
## 0.2.0 (2018-10-15)
|
|
2
25
|
|
|
3
26
|
- Added support for MariaDB 10.3.3+ and SQLite
|
|
4
27
|
- Use `PERCENTILE_CONT` for 4x performance increase
|
|
@@ -7,23 +30,23 @@ Breaking
|
|
|
7
30
|
|
|
8
31
|
- Dropped support for Postgres < 9.4
|
|
9
32
|
|
|
10
|
-
## 0.1.4
|
|
33
|
+
## 0.1.4 (2016-12-03)
|
|
11
34
|
|
|
12
35
|
- Added `drop_function` method
|
|
13
36
|
|
|
14
|
-
## 0.1.3
|
|
37
|
+
## 0.1.3 (2016-03-22)
|
|
15
38
|
|
|
16
39
|
- Added support for ActiveRecord 5.0
|
|
17
40
|
|
|
18
|
-
## 0.1.2
|
|
41
|
+
## 0.1.2 (2014-12-27)
|
|
19
42
|
|
|
20
43
|
- Added support for ActiveRecord 4.2
|
|
21
44
|
|
|
22
|
-
## 0.1.1
|
|
45
|
+
## 0.1.1 (2014-08-12)
|
|
23
46
|
|
|
24
47
|
- 10x faster median
|
|
25
48
|
- Added tests
|
|
26
49
|
|
|
27
|
-
## 0.1.0
|
|
50
|
+
## 0.1.0 (2014-03-13)
|
|
28
51
|
|
|
29
52
|
- First release
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
|
@@ -1,34 +1,96 @@
|
|
|
1
1
|
# ActiveMedian
|
|
2
2
|
|
|
3
|
-
Median for
|
|
3
|
+
Median and percentile for Active Record, Mongoid, arrays, and hashes
|
|
4
4
|
|
|
5
|
-
Supports
|
|
5
|
+
Supports:
|
|
6
|
+
|
|
7
|
+
- PostgreSQL 9.4+
|
|
8
|
+
- MariaDB 10.3.3+
|
|
9
|
+
- MySQL and SQL (with extensions)
|
|
10
|
+
- SQL Server 2012+
|
|
11
|
+
- MongoDB 2.1+
|
|
6
12
|
|
|
7
13
|
:fire: Uses native functions for blazing performance
|
|
8
14
|
|
|
9
|
-
[](https://travis-ci.org/ankane/active_median)
|
|
15
|
+
[](https://travis-ci.org/ankane/active_median)
|
|
16
|
+
|
|
17
|
+
## Getting Started
|
|
18
|
+
|
|
19
|
+
Add this line to your application’s Gemfile:
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
gem 'active_median'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
For MySQL and SQLite, also follow [these instructions](#additional-instructions).
|
|
26
|
+
|
|
27
|
+
## Models
|
|
10
28
|
|
|
11
|
-
|
|
29
|
+
Median
|
|
12
30
|
|
|
13
31
|
```ruby
|
|
14
32
|
Item.median(:price)
|
|
15
33
|
```
|
|
16
34
|
|
|
17
|
-
|
|
35
|
+
Percentile
|
|
18
36
|
|
|
19
37
|
```ruby
|
|
20
|
-
|
|
38
|
+
Request.percentile(:response_time, 0.95)
|
|
21
39
|
```
|
|
22
40
|
|
|
23
|
-
|
|
41
|
+
Works with grouping, too
|
|
24
42
|
|
|
25
|
-
|
|
43
|
+
```ruby
|
|
44
|
+
Order.group(:store_id).median(:total)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## User Input
|
|
48
|
+
|
|
49
|
+
If passing user input as the column, be sure to sanitize it first [like you must](https://rails-sqli.org/) with other aggregate methods like `sum`.
|
|
26
50
|
|
|
27
51
|
```ruby
|
|
28
|
-
|
|
52
|
+
column = params[:column]
|
|
53
|
+
|
|
54
|
+
# check against permitted columns
|
|
55
|
+
raise "Unpermitted column" unless ["column_a", "column_b"].include?(column)
|
|
56
|
+
|
|
57
|
+
User.median(column)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Arrays and Hashes
|
|
61
|
+
|
|
62
|
+
Median
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
[1, 2, 3].median
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Percentile
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
[1, 2, 3].percentile(0.95)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
You can also pass a block
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
{a: 1, b: 2, c: 3}.median { |k, v| v }
|
|
29
78
|
```
|
|
30
79
|
|
|
31
|
-
|
|
80
|
+
## Additional Instructions
|
|
81
|
+
|
|
82
|
+
### MySQL
|
|
83
|
+
|
|
84
|
+
MySQL requires the `PERCENTILE_CONT` function from [udf_infusion](https://github.com/infusion/udf_infusion). To install it, do:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
git clone https://github.com/infusion/udf_infusion.git
|
|
88
|
+
cd udf_infusion
|
|
89
|
+
./configure --enable-functions="percentile_cont"
|
|
90
|
+
make
|
|
91
|
+
sudo make install
|
|
92
|
+
mysql <options> < load.sql
|
|
93
|
+
```
|
|
32
94
|
|
|
33
95
|
### SQLite
|
|
34
96
|
|
|
@@ -51,7 +113,7 @@ db.enable_load_extension(0)
|
|
|
51
113
|
|
|
52
114
|
### 0.2.0
|
|
53
115
|
|
|
54
|
-
A user-defined function is no longer needed. Create a migration with `ActiveMedian.drop_function` to remove it.
|
|
116
|
+
A user-defined function is no longer needed for Postgres. Create a migration with `ActiveMedian.drop_function` to remove it.
|
|
55
117
|
|
|
56
118
|
## Contributing
|
|
57
119
|
|
|
@@ -61,3 +123,13 @@ Everyone is encouraged to help improve this project. Here are a few ways you can
|
|
|
61
123
|
- Fix bugs and [submit pull requests](https://github.com/ankane/active_median/pulls)
|
|
62
124
|
- Write, clarify, or fix documentation
|
|
63
125
|
- Suggest or add new features
|
|
126
|
+
|
|
127
|
+
To get started with development and testing:
|
|
128
|
+
|
|
129
|
+
```sh
|
|
130
|
+
git clone https://github.com/ankane/active_median.git
|
|
131
|
+
cd active_median
|
|
132
|
+
createdb active_median_test
|
|
133
|
+
bundle install
|
|
134
|
+
bundle exec rake test
|
|
135
|
+
```
|
data/lib/active_median.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
require "active_support"
|
|
2
2
|
|
|
3
|
-
require "active_median/
|
|
3
|
+
require "active_median/enumerable"
|
|
4
4
|
require "active_median/version"
|
|
5
5
|
|
|
6
6
|
module ActiveMedian
|
|
@@ -14,5 +14,11 @@ module ActiveMedian
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
ActiveSupport.on_load(:active_record) do
|
|
17
|
+
require "active_median/model"
|
|
17
18
|
extend(ActiveMedian::Model)
|
|
18
19
|
end
|
|
20
|
+
|
|
21
|
+
ActiveSupport.on_load(:mongoid) do
|
|
22
|
+
require "active_median/mongoid"
|
|
23
|
+
Mongoid::Document::ClassMethods.include(ActiveMedian::Mongoid)
|
|
24
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Enumerable
|
|
2
|
+
unless method_defined?(:median)
|
|
3
|
+
def median(*args, &block)
|
|
4
|
+
if !block && respond_to?(:scoping)
|
|
5
|
+
scoping { @klass.median(*args) }
|
|
6
|
+
elsif !block && respond_to?(:with_scope)
|
|
7
|
+
with_scope(self) { klass.median(*args) }
|
|
8
|
+
else
|
|
9
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0)" if args.any?
|
|
10
|
+
percentile(0.5, &block)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
unless method_defined?(:percentile)
|
|
16
|
+
def percentile(*args, &block)
|
|
17
|
+
if !block && respond_to?(:scoping)
|
|
18
|
+
scoping { @klass.percentile(*args) }
|
|
19
|
+
elsif !block && respond_to?(:with_scope)
|
|
20
|
+
with_scope(self) { klass.percentile(*args) }
|
|
21
|
+
else
|
|
22
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 1)" if args.size != 1
|
|
23
|
+
|
|
24
|
+
percentile = args[0].to_f
|
|
25
|
+
raise ArgumentError, "percentile is not between 0 and 1" if percentile < 0 || percentile > 1
|
|
26
|
+
|
|
27
|
+
# uses C=1 variant, like percentile_cont
|
|
28
|
+
# https://en.wikipedia.org/wiki/Percentile#The_linear_interpolation_between_closest_ranks_method
|
|
29
|
+
sorted = map(&block).sort
|
|
30
|
+
x = percentile * (sorted.size - 1)
|
|
31
|
+
r = x % 1
|
|
32
|
+
i = x.floor
|
|
33
|
+
if i == sorted.size - 1
|
|
34
|
+
sorted[-1]
|
|
35
|
+
else
|
|
36
|
+
sorted[i] + r * (sorted[i + 1] - sorted[i])
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
data/lib/active_median/model.rb
CHANGED
|
@@ -1,20 +1,69 @@
|
|
|
1
1
|
module ActiveMedian
|
|
2
2
|
module Model
|
|
3
3
|
def median(column)
|
|
4
|
+
percentile(column, 0.5)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def percentile(column, percentile)
|
|
8
|
+
percentile = percentile.to_f
|
|
9
|
+
raise ArgumentError, "percentile is not between 0 and 1" if percentile < 0 || percentile > 1
|
|
10
|
+
|
|
11
|
+
# basic version of Active Record disallow_raw_sql!
|
|
12
|
+
# symbol = column (safe), Arel node = SQL (safe), other = untrusted
|
|
13
|
+
# matches table.column and column
|
|
14
|
+
unless column.is_a?(Symbol) || column.is_a?(Arel::Nodes::SqlLiteral) || /\A\w+(\.\w+)?\z/i.match(column.to_s)
|
|
15
|
+
warn "[active_median] Non-attribute argument: #{column}. Use Arel.sql() for known-safe values. This will raise an error in ActiveMedian 0.3.0"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# column resolution
|
|
19
|
+
node = relation.send(:arel_columns, [column]).first
|
|
20
|
+
node = Arel::Nodes::SqlLiteral.new(node) if node.is_a?(String)
|
|
21
|
+
column = relation.connection.visitor.accept(node, Arel::Collectors::SQLString.new).value
|
|
22
|
+
|
|
23
|
+
# prevent SQL injection
|
|
24
|
+
percentile = connection.quote(percentile)
|
|
25
|
+
|
|
4
26
|
group_values = all.group_values
|
|
5
27
|
|
|
6
28
|
relation =
|
|
7
29
|
case connection.adapter_name
|
|
8
30
|
when /mysql/i
|
|
31
|
+
# assume mariadb by default
|
|
32
|
+
# use send as this method is private in Rails 4.2
|
|
33
|
+
mariadb = connection.send(:mariadb?) rescue true
|
|
34
|
+
|
|
35
|
+
if mariadb
|
|
36
|
+
if group_values.any?
|
|
37
|
+
over = "PARTITION BY #{group_values.join(", ")}"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
select(*group_values, "PERCENTILE_CONT(#{percentile}) WITHIN GROUP (ORDER BY #{column}) OVER (#{over})").unscope(:group)
|
|
41
|
+
else
|
|
42
|
+
# if mysql gets native function, check (and memoize) version first
|
|
43
|
+
select(*group_values, "PERCENTILE_CONT(#{column}, #{percentile})")
|
|
44
|
+
end
|
|
45
|
+
when /sqlserver/i
|
|
9
46
|
if group_values.any?
|
|
10
47
|
over = "PARTITION BY #{group_values.join(", ")}"
|
|
11
48
|
end
|
|
12
49
|
|
|
13
|
-
select(*group_values, "PERCENTILE_CONT(
|
|
50
|
+
select(*group_values, "PERCENTILE_CONT(#{percentile}) WITHIN GROUP (ORDER BY #{column}) OVER (#{over})").unscope(:group)
|
|
14
51
|
when /sqlite/i
|
|
15
|
-
|
|
52
|
+
case percentile.to_f
|
|
53
|
+
when 0
|
|
54
|
+
select(*group_values, "MIN(#{column})")
|
|
55
|
+
when 0.5
|
|
56
|
+
select(*group_values, "MEDIAN(#{column})")
|
|
57
|
+
when 1
|
|
58
|
+
select(*group_values, "MAX(#{column})")
|
|
59
|
+
else
|
|
60
|
+
# LOWER_QUARTILE and UPPER_QUARTILE use different calculation than 0.25 and 0.75
|
|
61
|
+
raise "SQLite only supports 0, 0.5, and 1 percentiles"
|
|
62
|
+
end
|
|
63
|
+
when /postg/i, /redshift/i # postgis too
|
|
64
|
+
select(*group_values, "PERCENTILE_CONT(#{percentile}) WITHIN GROUP (ORDER BY #{column})")
|
|
16
65
|
else
|
|
17
|
-
|
|
66
|
+
raise "Connection adapter not supported: #{connection.adapter_name}"
|
|
18
67
|
end
|
|
19
68
|
|
|
20
69
|
result = connection.select_all(relation.to_sql)
|
|
@@ -22,16 +71,20 @@ module ActiveMedian
|
|
|
22
71
|
# typecast
|
|
23
72
|
rows = []
|
|
24
73
|
columns = result.columns
|
|
25
|
-
cast_method = ActiveRecord::VERSION::MAJOR < 5 ? :type_cast : :cast_value
|
|
26
74
|
result.rows.each do |untyped_row|
|
|
27
|
-
rows << (result.column_types.empty? ? untyped_row : columns.each_with_index.map { |c, i| untyped_row[i] ? result.column_types[c].send(
|
|
75
|
+
rows << (result.column_types.empty? ? untyped_row : columns.each_with_index.map { |c, i| untyped_row[i] ? result.column_types[c].send(:cast_value, untyped_row[i]) : untyped_row[i] })
|
|
28
76
|
end
|
|
29
77
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
78
|
+
result =
|
|
79
|
+
if group_values.any?
|
|
80
|
+
Hash[rows.map { |r| [r.size == 2 ? r[0] : r[0..-2], r[-1]] }]
|
|
81
|
+
else
|
|
82
|
+
rows[0] && rows[0][0]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
result = Groupdate.process_result(relation, result) if defined?(Groupdate.process_result)
|
|
86
|
+
|
|
87
|
+
result
|
|
35
88
|
end
|
|
36
89
|
end
|
|
37
90
|
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module ActiveMedian
|
|
2
|
+
module Mongoid
|
|
3
|
+
def median(column)
|
|
4
|
+
percentile(column, 0.5)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
# https://www.compose.com/articles/mongo-metrics-finding-a-happy-median/
|
|
8
|
+
def percentile(column, percentile)
|
|
9
|
+
percentile = percentile.to_f
|
|
10
|
+
raise ArgumentError, "percentile is not between 0 and 1" if percentile < 0 || percentile > 1
|
|
11
|
+
|
|
12
|
+
relation =
|
|
13
|
+
all
|
|
14
|
+
.asc(column)
|
|
15
|
+
.group(_id: nil, values: {"$push" => "$#{column}"}, count: {"$sum" => 1})
|
|
16
|
+
.project(values: 1, count: {"$subtract" => ["$count", 1]})
|
|
17
|
+
.project(values: 1, count: 1, x: {"$multiply" => ["$count", percentile]})
|
|
18
|
+
.project(values: 1, count: 1, r: {"$mod" => ["$x", 1]}, i: {"$floor" => "$x"})
|
|
19
|
+
.project(values: 1, count: 1, r: 1, i: 1, i2: {"$add" => ["$i", 1]})
|
|
20
|
+
.project(values: 1, count: 1, r: 1, i: 1, i2: {"$min" => ["$i2", "$count"]})
|
|
21
|
+
.project(r: 1, beginValue: {"$arrayElemAt" => ["$values", "$i"]}, endValue: {"$arrayElemAt" => ["$values", "$i2"]})
|
|
22
|
+
.project(r: 1, beginValue: 1, result: {"$subtract" => ["$endValue", "$beginValue"]})
|
|
23
|
+
.project(beginValue: 1, result: {"$multiply" => ["$result", "$r"]})
|
|
24
|
+
.project(result: {"$add" => ["$beginValue", "$result"]})
|
|
25
|
+
|
|
26
|
+
res = collection.aggregate(relation.pipeline).first
|
|
27
|
+
res ? res["result"] : nil
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
metadata
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: active_median
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Kane
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-09-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
14
|
+
name: activesupport
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
19
|
+
version: '5'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
26
|
+
version: '5'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: bundler
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -56,16 +56,16 @@ dependencies:
|
|
|
56
56
|
name: pg
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
|
-
- - "
|
|
59
|
+
- - ">="
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
61
|
+
version: '0'
|
|
62
62
|
type: :development
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
|
-
- - "
|
|
66
|
+
- - ">="
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
68
|
+
version: '0'
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
70
|
name: rake
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -80,6 +80,34 @@ dependencies:
|
|
|
80
80
|
- - ">="
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: '0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: activerecord
|
|
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'
|
|
83
111
|
description:
|
|
84
112
|
email: andrew@chartkick.com
|
|
85
113
|
executables: []
|
|
@@ -90,7 +118,9 @@ files:
|
|
|
90
118
|
- LICENSE.txt
|
|
91
119
|
- README.md
|
|
92
120
|
- lib/active_median.rb
|
|
121
|
+
- lib/active_median/enumerable.rb
|
|
93
122
|
- lib/active_median/model.rb
|
|
123
|
+
- lib/active_median/mongoid.rb
|
|
94
124
|
- lib/active_median/version.rb
|
|
95
125
|
homepage: https://github.com/ankane/active_median
|
|
96
126
|
licenses:
|
|
@@ -104,16 +134,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
104
134
|
requirements:
|
|
105
135
|
- - ">="
|
|
106
136
|
- !ruby/object:Gem::Version
|
|
107
|
-
version: '2.
|
|
137
|
+
version: '2.4'
|
|
108
138
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
139
|
requirements:
|
|
110
140
|
- - ">="
|
|
111
141
|
- !ruby/object:Gem::Version
|
|
112
142
|
version: '0'
|
|
113
143
|
requirements: []
|
|
114
|
-
|
|
115
|
-
rubygems_version: 2.7.7
|
|
144
|
+
rubygems_version: 3.1.2
|
|
116
145
|
signing_key:
|
|
117
146
|
specification_version: 4
|
|
118
|
-
summary: Median for
|
|
147
|
+
summary: Median and percentile for Active Record, Mongoid, arrays, and hashes
|
|
119
148
|
test_files: []
|