activerecord-where-any-of 1.0.5 → 1.1.3
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 +5 -5
- data/.codeclimate.yml +29 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +1157 -0
- data/.travis.yml +13 -0
- data/README.md +15 -8
- data/Rakefile +29 -0
- data/activerecord-where-any-of.gemspec +5 -3
- data/lib/active_record/where-any-of-mixin-rails-5.rb +62 -0
- data/lib/active_record/where-any-of-mixin.rb +22 -2
- data/lib/activerecord-where-any-of.rb +5 -1
- data/spec/database.sqlite3 +0 -0
- data/spec/lib/activerecord_where_any_of_spec.rb +93 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/active_record.rb +10 -0
- data/spec/support/example_model.rb +23 -0
- data/spec/support/match_ignoring_whitespace.rb +18 -0
- metadata +43 -8
data/.travis.yml
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.3.1
|
4
|
+
before_install:
|
5
|
+
- gem install bundler -v 1.12.5
|
6
|
+
- gem install activerecord -v '~> 3'
|
7
|
+
- gem install activerecord -v '~> 4'
|
8
|
+
- gem install activerecord -v '~> 5'
|
9
|
+
script:
|
10
|
+
- rake spec:all
|
11
|
+
addons:
|
12
|
+
code_climate:
|
13
|
+
repo_token: a778d31468c1d1c1376890f708b04c93c7cc0af521a15e91c9ba00e08cfeeaf0
|
data/README.md
CHANGED
@@ -1,16 +1,24 @@
|
|
1
|
-
|
2
|
-
=========================
|
1
|
+
# ActiveRecord where_any_of
|
3
2
|
|
4
|
-
|
5
|
-
|
3
|
+
* README: https://github.com/david-mccullars/activerecord-where-any-of
|
4
|
+
* Documentation: http://www.rubydoc.info/github/david-mccullars/activerecord-where-any-of
|
5
|
+
* Bug Reports: https://github.com/david-mccullars/activerecord-where-any-of/issues
|
6
|
+
|
7
|
+
|
8
|
+
## Status
|
9
|
+
|
10
|
+
[](https://travis-ci.org/david-mccullars/activerecord-where-any-of)
|
11
|
+
[](https://codeclimate.com/github/david-mccullars/activerecord-where-any-of)
|
12
|
+
|
13
|
+
|
14
|
+
## Description
|
6
15
|
|
7
16
|
Provides a mechanism for OR'ing together one or more AREL relations. Normally when
|
8
17
|
one performs `SomeModel.where(condition_1).where(condition_2)`, the result is to AND
|
9
18
|
those conditions together. Unfortunately, there is no good way of OR'ing them without
|
10
19
|
converting the conditions into manual SQL. This mixin addresses that issue.
|
11
20
|
|
12
|
-
Usage
|
13
|
-
------
|
21
|
+
## Usage
|
14
22
|
|
15
23
|
`where_any_of` accepts any number of relations, symbols, or strings. For symbols and strings,
|
16
24
|
the corresponding method is called (unscoped) which is expected to return a relation.
|
@@ -60,8 +68,7 @@ puts SomeRecord.where_any_of(
|
|
60
68
|
> SELECT "some_records".* FROM "some_records"
|
61
69
|
> WHERE (extra is not null) AND ((("some_records"."extra" = 'A' OR "some_records"."owner" = 'Bill') OR "some_records"."active" = 't'))
|
62
70
|
|
63
|
-
Install
|
64
|
-
--------
|
71
|
+
## Install
|
65
72
|
|
66
73
|
In your app, add this line to your `Gemfile`:
|
67
74
|
|
data/Rakefile
CHANGED
@@ -1 +1,30 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
|
4
|
+
module WithoutBundler
|
5
|
+
def run_task(*args)
|
6
|
+
Bundler.with_clean_env do
|
7
|
+
ENV['ACTIVE_RECORD_VERSION'] = $1 if name.to_s =~ /active_record_(\d+)/
|
8
|
+
super
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
RSpec::Core::RakeTask.prepend(WithoutBundler)
|
14
|
+
|
15
|
+
namespace :spec do
|
16
|
+
tasks = [3, 4, 5].map do |version|
|
17
|
+
name = "active_record_#{version}"
|
18
|
+
RSpec::Core::RakeTask.new(name).rspec_opts = '-O spec/spec.opts'
|
19
|
+
name
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "Run specs against all three versions of ActiveRecord"
|
23
|
+
task all: tasks
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'rdoc/task'
|
27
|
+
RDoc::Task.new do |rdoc|
|
28
|
+
rdoc.main = "README.md"
|
29
|
+
rdoc.rdoc_files.include("README.md", "lib/**/*.rb")
|
30
|
+
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'activerecord-where-any-of'
|
3
|
-
s.version = '1.
|
3
|
+
s.version = '1.1.3'
|
4
4
|
s.license = 'MIT'
|
5
|
-
s.date = '
|
5
|
+
s.date = '2020-06-25'
|
6
6
|
s.summary = 'A simple mixin to ActiveRecord::Base that allows use of OR arel relations.'
|
7
|
-
s.description = '
|
7
|
+
s.description = 'Should work in Rails 3, 4, & 5. However, in Rails 5 use of #or is prefered.'
|
8
8
|
s.description = File.read(File.expand_path('../README.md', __FILE__))[/Description:\n-+\s*(.*?)\n\n/m, 1]
|
9
9
|
s.authors = ['David McCullars']
|
10
10
|
s.email = ['david.mccullars@gmail.com']
|
@@ -15,4 +15,6 @@ Gem::Specification.new do |s|
|
|
15
15
|
s.add_runtime_dependency 'activerecord', '>= 3.0'
|
16
16
|
s.add_development_dependency 'bundler', '>= 1.3'
|
17
17
|
s.add_development_dependency 'rake'
|
18
|
+
s.add_development_dependency 'rspec'
|
19
|
+
s.add_development_dependency 'sqlite3'
|
18
20
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Rails 5+ mixin
|
2
|
+
module ActiveRecord
|
3
|
+
module WhereAnyOfMixin
|
4
|
+
|
5
|
+
INVALID_RELATION_VALUES = %i[
|
6
|
+
eager_load
|
7
|
+
group
|
8
|
+
union_ordering
|
9
|
+
union
|
10
|
+
unscope
|
11
|
+
window
|
12
|
+
with
|
13
|
+
]
|
14
|
+
|
15
|
+
RELATION_VALUES_TO_COPY = %i[
|
16
|
+
includes
|
17
|
+
joined_includes
|
18
|
+
joins
|
19
|
+
left_outer_joins
|
20
|
+
preload
|
21
|
+
references
|
22
|
+
]
|
23
|
+
|
24
|
+
def where_any_of(*conditions)
|
25
|
+
# Rails 5 comes with an #or method for relations, so we can just use that
|
26
|
+
# It is advised that once on Rails 5, this gem should be removed.
|
27
|
+
|
28
|
+
# Do our best to preserve joins from the conditions into the outer relation
|
29
|
+
values_to_copy = {}
|
30
|
+
|
31
|
+
conditions = conditions.map do |c|
|
32
|
+
c = self.unscoped.send(c) if c.is_a? Symbol or c.is_a? String
|
33
|
+
raise ArgumentError, "Unsupported argument type: #{c.class.name}" unless c.is_a?(ActiveRecord::Relation)
|
34
|
+
|
35
|
+
INVALID_RELATION_VALUES.each do |m|
|
36
|
+
raise ArgumentError, "Unsupported condition contains #{m} values" if c.send("#{m}_values").present?
|
37
|
+
end
|
38
|
+
|
39
|
+
RELATION_VALUES_TO_COPY.each do |m|
|
40
|
+
values = c.send("#{m}_values").presence or next
|
41
|
+
values_to_copy[m] ||= []
|
42
|
+
values_to_copy[m].concat(values)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Only preserve the where clause of the condition
|
46
|
+
c2 = ActiveRecord::Relation.new(c.klass)
|
47
|
+
c2.where_clause = c.where_clause
|
48
|
+
c2
|
49
|
+
end
|
50
|
+
|
51
|
+
if conditions.empty?
|
52
|
+
all.none
|
53
|
+
else
|
54
|
+
all_with_copied_values = values_to_copy.reduce(all) do |s, (key, values)|
|
55
|
+
s.send(key, values)
|
56
|
+
end
|
57
|
+
all_with_copied_values.merge conditions.reduce(:or)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -1,16 +1,36 @@
|
|
1
|
+
# Rails 4+ mixin
|
1
2
|
module ActiveRecord
|
2
3
|
module WhereAnyOfMixin
|
3
4
|
|
4
5
|
def where_any_of(*conditions)
|
5
6
|
# Takes any number of scopes and OR them together
|
6
7
|
# into a new scope which can be combined with other scopes
|
8
|
+
bind_values = []
|
7
9
|
conditions = conditions.map do |c|
|
8
10
|
if c.is_a? Symbol or c.is_a? String
|
9
11
|
c = self.unscoped.send(c)
|
10
12
|
end
|
11
|
-
c.
|
13
|
+
bind_values.concat(c.bind_values)
|
14
|
+
__convert_string_wheres(c.where_values).reduce(:and)
|
15
|
+
end
|
16
|
+
if conditions.empty?
|
17
|
+
respond_to?(:none) ? none : where('1=0')
|
18
|
+
else
|
19
|
+
s = where(conditions.reduce(:or))
|
20
|
+
s.bind_values += bind_values if bind_values.present?
|
21
|
+
s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def __convert_string_wheres(wheres)
|
26
|
+
wheres.map do |w|
|
27
|
+
if w.is_a?(Arel::Nodes::Equality)
|
28
|
+
w
|
29
|
+
else
|
30
|
+
w = Arel.sql(w) if String === w
|
31
|
+
Arel::Nodes::Grouping.new(w)
|
32
|
+
end
|
12
33
|
end
|
13
|
-
where(conditions.reduce(:or))
|
14
34
|
end
|
15
35
|
|
16
36
|
end
|
@@ -1,3 +1,7 @@
|
|
1
1
|
require 'active_record'
|
2
|
-
|
2
|
+
if ActiveRecord::VERSION::MAJOR >= 5
|
3
|
+
require 'active_record/where-any-of-mixin-rails-5'
|
4
|
+
else
|
5
|
+
require 'active_record/where-any-of-mixin'
|
6
|
+
end
|
3
7
|
ActiveRecord::Base.send(:extend, ActiveRecord::WhereAnyOfMixin)
|
Binary file
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'activerecord-where-any-of'
|
3
|
+
|
4
|
+
describe 'where_any_of' do
|
5
|
+
|
6
|
+
subject { ExampleModel }
|
7
|
+
|
8
|
+
let(:lparen) do
|
9
|
+
'(' if ActiveRecord::VERSION::MAJOR <= 3
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:rparen) do
|
13
|
+
')' if ActiveRecord::VERSION::MAJOR <= 3
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '(*relations)' do
|
17
|
+
|
18
|
+
let(:relation) do
|
19
|
+
subject.where_any_of(
|
20
|
+
subject.active.on_shelf('h'),
|
21
|
+
subject.inactive.recent,
|
22
|
+
subject.with_name('apple'),
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
specify :count do
|
27
|
+
expect(relation.count).to eq(6)
|
28
|
+
end
|
29
|
+
|
30
|
+
specify :first do
|
31
|
+
m = relation.first
|
32
|
+
expect(m.id).to eq(1)
|
33
|
+
expect(m.name).to eq('apple')
|
34
|
+
end
|
35
|
+
|
36
|
+
specify :to_sql do
|
37
|
+
expect(relation.to_sql).to match_ignoring_whitespace(<<-END)
|
38
|
+
SELECT "example_models".*
|
39
|
+
FROM "example_models"
|
40
|
+
WHERE #{lparen}((
|
41
|
+
"example_models"."active" = 't' AND (name LIKE 'h%')
|
42
|
+
OR "example_models"."active" = 'f' AND (created_at > '2016-11-01'))
|
43
|
+
OR "example_models"."name" = 'apple'
|
44
|
+
)#{rparen}
|
45
|
+
END
|
46
|
+
end
|
47
|
+
|
48
|
+
specify :group_count do
|
49
|
+
expect(relation.group(:active).count).to match(true => 2, false => 4)
|
50
|
+
end
|
51
|
+
|
52
|
+
specify :order_limit do
|
53
|
+
expect(relation.order('updated_at DESC').limit(2).pluck(:name)).to eq [
|
54
|
+
"historiette boxthorn quinaldine",
|
55
|
+
"slipcase diorthotic holocentrid plessimetry prestudiously",
|
56
|
+
]
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "(:symbol, 'string', relation)" do
|
62
|
+
|
63
|
+
let(:relation) do
|
64
|
+
subject.where('name <> ?', 'bad').where_any_of(
|
65
|
+
:recent,
|
66
|
+
'active',
|
67
|
+
subject.on_shelf('z'),
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
specify :count do
|
72
|
+
expect(relation.count).to eq(38)
|
73
|
+
end
|
74
|
+
|
75
|
+
specify :merge_and_count do
|
76
|
+
expect(relation.merge(subject.inactive).count).to eq(4)
|
77
|
+
end
|
78
|
+
|
79
|
+
specify :to_sql do
|
80
|
+
expect(relation.to_sql).to match_ignoring_whitespace(<<-END)
|
81
|
+
SELECT "example_models".*
|
82
|
+
FROM "example_models"
|
83
|
+
WHERE (name <> 'bad') AND #{lparen}((
|
84
|
+
(created_at > '2016-11-01')
|
85
|
+
OR "example_models"."active" = 't')
|
86
|
+
OR (name LIKE 'z%')
|
87
|
+
)#{rparen}
|
88
|
+
END
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
version = ENV.fetch('ACTIVE_RECORD_VERSION', '3.0').to_f
|
2
|
+
raise "Unsupported ActiveRecord version #{version.inspect}" unless version >= 3.0 && version < 6.0
|
3
|
+
gem 'activerecord', "~> #{version}"
|
4
|
+
|
5
|
+
require 'active_record'
|
6
|
+
|
7
|
+
ActiveRecord::Base.establish_connection(
|
8
|
+
adapter: 'sqlite3',
|
9
|
+
database: File.expand_path('../../database.sqlite3', __FILE__),
|
10
|
+
)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class ExampleModel < ActiveRecord::Base
|
2
|
+
|
3
|
+
def self.with_name(name)
|
4
|
+
where(name: name)
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.on_shelf(letter)
|
8
|
+
letter =~ /\A[a-z]\z/i ? where("name LIKE '#{letter}%'") : where('1=0')
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.active
|
12
|
+
where(active: true)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.inactive
|
16
|
+
where(active: false)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.recent
|
20
|
+
where('created_at > ?', Date.parse('2016-11-01'))
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
RSpec::Matchers.define :match_ignoring_whitespace do |expected|
|
2
|
+
match do |actual|
|
3
|
+
expected.to_s.gsub(/\s/, '') == actual.to_s.gsub(/\s/, '')
|
4
|
+
end
|
5
|
+
|
6
|
+
failure_message do |actual|
|
7
|
+
sep = '=' * 80
|
8
|
+
<<-END
|
9
|
+
#{sep}
|
10
|
+
Expected:
|
11
|
+
#{expected.to_s.strip}
|
12
|
+
#{sep}
|
13
|
+
Actual:
|
14
|
+
#{actual.to_s.strip}
|
15
|
+
#{sep}
|
16
|
+
END
|
17
|
+
end
|
18
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-where-any-of
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David McCullars
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-06-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -52,25 +52,60 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sqlite3
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: ''
|
60
84
|
email:
|
61
85
|
- david.mccullars@gmail.com
|
62
86
|
executables: []
|
63
87
|
extensions: []
|
64
88
|
extra_rdoc_files: []
|
65
89
|
files:
|
90
|
+
- ".codeclimate.yml"
|
66
91
|
- ".gitignore"
|
92
|
+
- ".rubocop.yml"
|
93
|
+
- ".travis.yml"
|
67
94
|
- Gemfile
|
68
95
|
- LICENSE
|
69
96
|
- README.md
|
70
97
|
- Rakefile
|
71
98
|
- activerecord-where-any-of.gemspec
|
99
|
+
- lib/active_record/where-any-of-mixin-rails-5.rb
|
72
100
|
- lib/active_record/where-any-of-mixin.rb
|
73
101
|
- lib/activerecord-where-any-of.rb
|
102
|
+
- spec/database.sqlite3
|
103
|
+
- spec/lib/activerecord_where_any_of_spec.rb
|
104
|
+
- spec/spec.opts
|
105
|
+
- spec/spec_helper.rb
|
106
|
+
- spec/support/active_record.rb
|
107
|
+
- spec/support/example_model.rb
|
108
|
+
- spec/support/match_ignoring_whitespace.rb
|
74
109
|
homepage:
|
75
110
|
licenses:
|
76
111
|
- MIT
|
@@ -91,7 +126,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
126
|
version: '0'
|
92
127
|
requirements: []
|
93
128
|
rubyforge_project:
|
94
|
-
rubygems_version: 2.
|
129
|
+
rubygems_version: 2.7.6
|
95
130
|
signing_key:
|
96
131
|
specification_version: 4
|
97
132
|
summary: A simple mixin to ActiveRecord::Base that allows use of OR arel relations.
|