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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7589b66aa9d74af6afe94a766603a36feadf9b0f36d7be01212e97a4ae1de423
4
- data.tar.gz: de6fa949edc2220610f97ff42a03ce7f50cc1cf9e66979846c570c3a13e3e6b8
3
+ metadata.gz: d5ba23ce76f2234aaba1595b26ce41faeba63c03807b8b101d7e0152d48df133
4
+ data.tar.gz: 15fb7d99f6991034b9f93bdfa6c7aeb681518cf45ab618bcd11e471ff163eb48
5
5
  SHA512:
6
- metadata.gz: d2739f3b083658090a61b283d5d416f61e4b998576fe8a704cbcb22a7c2fb621f8b1c89c422bae8cf67cf3784624d5f056837980c2fa80737df67edbc337490c
7
- data.tar.gz: 9bac410d399bf81668e999d28da3f804ad4ab7e8891db726bbb07980a201902ca107d866b0f27866170df41c97c63d72cf4be4768666f67197f593d559da88b7
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
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013-2021 Andrew Kane
1
+ Copyright (c) 2013-2023 Andrew Kane
2
2
 
3
3
  MIT License
4
4
 
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 'active_median'
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 and testing:
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
  ```
@@ -11,7 +11,8 @@ module ActiveMedian
11
11
  private
12
12
 
13
13
  def calculate_percentile(column, percentile, operation)
14
- percentile = percentile.to_f
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 = relation.send(:column_alias_for, "#{operation} #{column.to_s.downcase}")
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(:@active_median_handler)
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(:@active_median_handler, true)
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.to_f
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
@@ -1,3 +1,3 @@
1
1
  module ActiveMedian
2
- VERSION = "0.3.2"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/active_median.rb CHANGED
@@ -1,25 +1,16 @@
1
+ # dependencies
1
2
  require "active_support"
2
3
 
3
- require "active_median/enumerable"
4
- require "active_median/version"
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
- require "active_median/model"
9
+ require_relative "active_median/model"
19
10
  extend(ActiveMedian::Model)
20
11
  end
21
12
 
22
13
  ActiveSupport.on_load(:mongoid) do
23
- require "active_median/mongoid"
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.3.2
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: 2021-08-17 00:00:00.000000000 Z
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: '5.2'
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: '5.2'
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: '2.6'
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.2.22
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