active_median 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/LICENSE.txt +1 -1
- data/README.md +20 -15
- data/lib/active_median/model.rb +11 -4
- data/lib/active_median/mongoid.rb +3 -1
- data/lib/active_median/sqlite_handler.rb +1 -0
- data/lib/active_median/version.rb +1 -1
- data/lib/active_median.rb +6 -15
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5ba23ce76f2234aaba1595b26ce41faeba63c03807b8b101d7e0152d48df133
|
4
|
+
data.tar.gz: 15fb7d99f6991034b9f93bdfa6c7aeb681518cf45ab618bcd11e471ff163eb48
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4078461f5f215b8d5632f2c75caf7f57289af0b979e51fa2cf6f3c8a0dc83c7661bd6677efd5800e81337123681c111a8f908807d66203f5e0d713db9c5be3e
|
7
|
+
data.tar.gz: b96f21e8135e91a4d1e4ec78e128852b364319d7c56af0a15c77d810cf1276ec421ff37b91d1a2d5f66bd7683617d45e5483740f4f6f9c948354d9280d70ff62
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
## 0.4.0 (2023-05-25)
|
2
|
+
|
3
|
+
- Fixed error with Active Record 7.0.5
|
4
|
+
- Raise `ArgumentError` for invalid percentiles
|
5
|
+
- Dropped support for Ruby < 3 and Active Record < 6.1
|
6
|
+
|
7
|
+
## 0.3.3 (2021-08-17)
|
8
|
+
|
9
|
+
- Fixed null values for SQLite without an extension and MongoDB
|
10
|
+
|
1
11
|
## 0.3.2 (2021-08-16)
|
2
12
|
|
3
13
|
- Added support for SQLite without an extension
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -20,7 +20,7 @@ Supports:
|
|
20
20
|
Add this line to your application’s Gemfile:
|
21
21
|
|
22
22
|
```ruby
|
23
|
-
gem
|
23
|
+
gem "active_median"
|
24
24
|
```
|
25
25
|
|
26
26
|
For MySQL, also follow [these instructions](#additional-instructions).
|
@@ -105,18 +105,6 @@ db.load_extension("percentile.so") # or percentile.dylib
|
|
105
105
|
db.enable_load_extension(0)
|
106
106
|
```
|
107
107
|
|
108
|
-
## Upgrading
|
109
|
-
|
110
|
-
### 0.3.0
|
111
|
-
|
112
|
-
ActiveMedian 0.3.0 protects against unsafe input by default. For non-attribute arguments, use:
|
113
|
-
|
114
|
-
```ruby
|
115
|
-
Item.median(Arel.sql(known_safe_value))
|
116
|
-
```
|
117
|
-
|
118
|
-
Also, percentiles are now supported with SQLite. Use the [percentile extension](#sqlite) instead of `extension-functions`.
|
119
|
-
|
120
108
|
## Contributing
|
121
109
|
|
122
110
|
Everyone is encouraged to help improve this project. Here are a few ways you can help:
|
@@ -126,12 +114,29 @@ Everyone is encouraged to help improve this project. Here are a few ways you can
|
|
126
114
|
- Write, clarify, or fix documentation
|
127
115
|
- Suggest or add new features
|
128
116
|
|
129
|
-
To get started with development
|
117
|
+
To get started with development:
|
130
118
|
|
131
119
|
```sh
|
132
120
|
git clone https://github.com/ankane/active_median.git
|
133
121
|
cd active_median
|
134
|
-
createdb active_median_test
|
135
122
|
bundle install
|
123
|
+
|
124
|
+
# Postgres
|
125
|
+
createdb active_median_test
|
136
126
|
bundle exec rake test
|
127
|
+
|
128
|
+
# SQLite
|
129
|
+
ADAPTER=sqlite3 BUNDLE_GEMFILE=gemfiles/sqlite3.gemfile bundle exec rake test
|
130
|
+
|
131
|
+
# MariaDB and MySQL (for MySQL, install the extension first)
|
132
|
+
mysqladmin create active_median_test
|
133
|
+
ADAPTER=mysql2 BUNDLE_GEMFILE=gemfiles/mysql2.gemfile bundle exec rake test
|
134
|
+
|
135
|
+
# SQL Server
|
136
|
+
docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=YourStrong!Passw0rd' -p 1433:1433 -d mcr.microsoft.com/mssql/server:2022-latest
|
137
|
+
docker exec -it <container-id> /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P YourStrong\!Passw0rd -Q "CREATE DATABASE active_median_test"
|
138
|
+
ADAPTER=sqlserver BUNDLE_GEMFILE=gemfiles/sqlserver.gemfile bundle exec rake test
|
139
|
+
|
140
|
+
# MongoDB
|
141
|
+
BUNDLE_GEMFILE=gemfiles/mongoid7.gemfile bundle exec rake test
|
137
142
|
```
|
data/lib/active_median/model.rb
CHANGED
@@ -11,7 +11,8 @@ module ActiveMedian
|
|
11
11
|
private
|
12
12
|
|
13
13
|
def calculate_percentile(column, percentile, operation)
|
14
|
-
percentile = percentile
|
14
|
+
percentile = Float(percentile, exception: false)
|
15
|
+
raise ArgumentError, "invalid percentile" if percentile.nil?
|
15
16
|
raise ArgumentError, "percentile is not between 0 and 1" if percentile < 0 || percentile > 1
|
16
17
|
|
17
18
|
# basic version of Active Record disallow_raw_sql!
|
@@ -24,7 +25,13 @@ module ActiveMedian
|
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
27
|
-
column_alias =
|
28
|
+
column_alias =
|
29
|
+
if relation.respond_to?(:column_alias_for, true)
|
30
|
+
relation.send(:column_alias_for, "#{operation} #{column.to_s.downcase}")
|
31
|
+
else
|
32
|
+
# Active Record 7.0.5+
|
33
|
+
ActiveRecord::Calculations::ColumnAliasTracker.new(connection).alias_for("#{operation} #{column.to_s.downcase}")
|
34
|
+
end
|
28
35
|
# safety check
|
29
36
|
# could quote, but want to keep consistent with Active Record
|
30
37
|
raise "Bad column alias: #{column_alias}. Please report a bug." unless column_alias =~ /\A[a-z0-9_]+\z/
|
@@ -67,12 +74,12 @@ module ActiveMedian
|
|
67
74
|
relation.select(*group_values, "PERCENTILE_CONT(#{percentile}) WITHIN GROUP (ORDER BY #{column}) OVER (#{over}) AS #{column_alias}").unscope(:group)
|
68
75
|
when /sqlite/i
|
69
76
|
db = connection.raw_connection
|
70
|
-
unless db.instance_variable_get(:@
|
77
|
+
unless db.instance_variable_get(:@active_median)
|
71
78
|
if db.get_first_value("SELECT 1 FROM pragma_function_list WHERE name = 'percentile'").nil?
|
72
79
|
require "active_median/sqlite_handler"
|
73
80
|
db.create_aggregate_handler(ActiveMedian::SQLiteHandler)
|
74
81
|
end
|
75
|
-
db.instance_variable_set(:@
|
82
|
+
db.instance_variable_set(:@active_median, true)
|
76
83
|
end
|
77
84
|
|
78
85
|
relation.select(*group_values, "PERCENTILE(#{column}, #{percentile} * 100) AS #{column_alias}")
|
@@ -6,11 +6,13 @@ module ActiveMedian
|
|
6
6
|
|
7
7
|
# https://www.compose.com/articles/mongo-metrics-finding-a-happy-median/
|
8
8
|
def percentile(column, percentile)
|
9
|
-
percentile = percentile
|
9
|
+
percentile = Float(percentile, exception: false)
|
10
|
+
raise ArgumentError, "invalid percentile" if percentile.nil?
|
10
11
|
raise ArgumentError, "percentile is not between 0 and 1" if percentile < 0 || percentile > 1
|
11
12
|
|
12
13
|
relation =
|
13
14
|
all
|
15
|
+
.where(column => {"$ne" => nil})
|
14
16
|
.asc(column)
|
15
17
|
.group(_id: nil, values: {"$push" => "$#{column}"}, count: {"$sum" => 1})
|
16
18
|
.project(values: 1, count: {"$subtract" => ["$count", 1]})
|
@@ -18,6 +18,7 @@ module ActiveMedian
|
|
18
18
|
# 2. percentile same for all rows
|
19
19
|
# since input is already checked
|
20
20
|
def step(ctx, value, percentile)
|
21
|
+
return if value.nil?
|
21
22
|
raise ActiveRecord::StatementInvalid, "1st argument to percentile() is not numeric" unless value.is_a?(Numeric)
|
22
23
|
@percentile ||= percentile
|
23
24
|
@values << value
|
data/lib/active_median.rb
CHANGED
@@ -1,25 +1,16 @@
|
|
1
|
+
# dependencies
|
1
2
|
require "active_support"
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
module ActiveMedian
|
7
|
-
# TODO remove in 0.4.0
|
8
|
-
def self.drop_function
|
9
|
-
ActiveRecord::Base.connection.execute <<-SQL
|
10
|
-
DROP AGGREGATE IF EXISTS median(anyelement);
|
11
|
-
DROP FUNCTION IF EXISTS median(anyarray);
|
12
|
-
SQL
|
13
|
-
true
|
14
|
-
end
|
15
|
-
end
|
4
|
+
# modules
|
5
|
+
require_relative "active_median/enumerable"
|
6
|
+
require_relative "active_median/version"
|
16
7
|
|
17
8
|
ActiveSupport.on_load(:active_record) do
|
18
|
-
|
9
|
+
require_relative "active_median/model"
|
19
10
|
extend(ActiveMedian::Model)
|
20
11
|
end
|
21
12
|
|
22
13
|
ActiveSupport.on_load(:mongoid) do
|
23
|
-
|
14
|
+
require_relative "active_median/mongoid"
|
24
15
|
Mongoid::Document::ClassMethods.include(ActiveMedian::Mongoid)
|
25
16
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_median
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
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: 2023-05-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '6.1'
|
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: '6.1'
|
27
27
|
description:
|
28
28
|
email: andrew@ankane.org
|
29
29
|
executables: []
|
@@ -51,14 +51,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '3'
|
55
55
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
56
|
requirements:
|
57
57
|
- - ">="
|
58
58
|
- !ruby/object:Gem::Version
|
59
59
|
version: '0'
|
60
60
|
requirements: []
|
61
|
-
rubygems_version: 3.
|
61
|
+
rubygems_version: 3.4.10
|
62
62
|
signing_key:
|
63
63
|
specification_version: 4
|
64
64
|
summary: Median and percentile for Active Record, Mongoid, arrays, and hashes
|