activerecord-where-any-of 1.0.5 → 1.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Travis Build Status](https://travis-ci.org/david-mccullars/activerecord-where-any-of.svg?branch=master)](https://travis-ci.org/david-mccullars/activerecord-where-any-of)
|
11
|
+
[![Code Climate](https://codeclimate.com/github/david-mccullars/activerecord-where-any-of/badges/gpa.svg)](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.
|