where-or 0.1.0
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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +22 -0
- data/lib/where-or.rb +273 -0
- metadata +68 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 00f9b066d1de2204206334bd09620eb5d2f48153
|
4
|
+
data.tar.gz: e08eaff0f6245b9e14f46a17f09a215ac9d457ca
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f9855f971b1e406b405e556f06a0882eaf582ac344c010ab47c68999a99dec03401b9b44ea534650c7697433a16e7795dea76d4199de829a2c3fa886f3980380
|
7
|
+
data.tar.gz: 415af9190110cee119358e4b9d64ef9ce141ea0c01d3a49d2fd76d03f13245dff41ac5885370d6831d679dffe40c110b5666b2a83c8876dee8ad8624a7ac17b4
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Eric Guo
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Where Or
|
2
|
+
|
3
|
+
[Where or](https://github.com/rails/rails/pull/16052) function backport from Rails 5 for Rails 4.2
|
4
|
+
|
5
|
+
[](https://badge.fury.io/for/rb/where-or)
|
6
|
+
|
7
|
+
Installation:
|
8
|
+
|
9
|
+
``` ruby
|
10
|
+
gem 'where-or'
|
11
|
+
```
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
post = Post.where('id = 1').or(Post.where('id = 2'))
|
17
|
+
```
|
18
|
+
|
19
|
+
|
20
|
+
## Declare of original
|
21
|
+
|
22
|
+
Largely copy from [bf4 gist](https://gist.github.com/bf4/84cff9cc6ac8489d769e)
|
data/lib/where-or.rb
ADDED
@@ -0,0 +1,273 @@
|
|
1
|
+
abort "Congrats for being on Rails 5. Now please remove this patch" if Rails::VERSION::MAJOR > 4
|
2
|
+
# Tested on Rails Rails 4.2.3
|
3
|
+
warn "Patching ActiveRecord::Relation#or. This might blow up" if Rails.version < '4.2.3'
|
4
|
+
# https://github.com/rails/rails/commit/9e42cf019f2417473e7dcbfcb885709fa2709f89.patch
|
5
|
+
# CHANGELOG.md
|
6
|
+
# * Added the `#or` method on ActiveRecord::Relation, allowing use of the OR
|
7
|
+
# operator to combine WHERE or HAVING clauses.
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
#
|
11
|
+
# Post.where('id = 1').or(Post.where('id = 2'))
|
12
|
+
# # => SELECT * FROM posts WHERE (id = 1) OR (id = 2)
|
13
|
+
#
|
14
|
+
# *Sean Griffin*, *Matthew Draper*, *Gael Muller*, *Olivier El Mekki*
|
15
|
+
|
16
|
+
ActiveSupport.on_load(:active_record) do
|
17
|
+
|
18
|
+
module ActiveRecord::NullRelation
|
19
|
+
def or(other)
|
20
|
+
other.spawn
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module ActiveRecord::Querying
|
25
|
+
delegate :or, to: :all
|
26
|
+
end
|
27
|
+
|
28
|
+
module ActiveRecord::QueryMethods
|
29
|
+
|
30
|
+
CLAUSE_METHODS = [:where, :having]
|
31
|
+
|
32
|
+
CLAUSE_METHODS.each do |name|
|
33
|
+
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
34
|
+
def #{name}_clause # def where_clause
|
35
|
+
@values[:#{name}] || new_#{name}_clause # @values[:where] || new_where_clause
|
36
|
+
end # end
|
37
|
+
#
|
38
|
+
def #{name}_clause=(value) # def where_clause=(value)
|
39
|
+
raise ImmutableRelation if @loaded
|
40
|
+
check_cached_relation # assert_mutability!
|
41
|
+
@values[:#{name}] = value # @values[:where] = value
|
42
|
+
end # end
|
43
|
+
CODE
|
44
|
+
end
|
45
|
+
|
46
|
+
def where_values
|
47
|
+
where_clause.predicates
|
48
|
+
end
|
49
|
+
|
50
|
+
def where_values=(values)
|
51
|
+
self.where_clause = ActiveRecord::Relation::WhereClause.new(values || [], where_clause.binds)
|
52
|
+
end
|
53
|
+
|
54
|
+
def bind_values
|
55
|
+
where_clause.binds
|
56
|
+
end
|
57
|
+
|
58
|
+
def bind_values=(values)
|
59
|
+
self.where_clause = ActiveRecord::Relation::WhereClause.new(where_clause.predicates, values || [])
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns a new relation, which is the logical union of this relation and the one passed as an
|
63
|
+
# argument.
|
64
|
+
#
|
65
|
+
# The two relations must be structurally compatible: they must be scoping the same model, and
|
66
|
+
# they must differ only by +where+ (if no +group+ has been defined) or +having+ (if a +group+ is
|
67
|
+
# present). Neither relation may have a +limit+, +offset+, or +uniq+ set.
|
68
|
+
#
|
69
|
+
# Post.where("id = 1").or(Post.where("id = 2"))
|
70
|
+
# # SELECT `posts`.* FROM `posts` WHERE (('id = 1' OR 'id = 2'))
|
71
|
+
#
|
72
|
+
def or(other)
|
73
|
+
spawn.or!(other)
|
74
|
+
end
|
75
|
+
|
76
|
+
def or!(other) # :nodoc:
|
77
|
+
unless structurally_compatible_for_or?(other)
|
78
|
+
raise ArgumentError, 'Relation passed to #or must be structurally compatible'
|
79
|
+
end
|
80
|
+
|
81
|
+
self.where_clause = self.where_clause.or(other.where_clause)
|
82
|
+
self.having_clause = self.having_clause.or(other.having_clause)
|
83
|
+
|
84
|
+
self
|
85
|
+
end
|
86
|
+
|
87
|
+
private def structurally_compatible_for_or?(other) # :nodoc:
|
88
|
+
(ActiveRecord::Relation::SINGLE_VALUE_METHODS - [:from]).all? { |m| send("#{m}_value") == other.send("#{m}_value") } &&
|
89
|
+
(ActiveRecord::Relation::MULTI_VALUE_METHODS - [:extending, :where, :having, :bind]).all? { |m| send("#{m}_values") == other.send("#{m}_values") }
|
90
|
+
# https://github.com/rails/rails/commit/2c46d6db4feaf4284415f2fb6ceceb1bb535f278
|
91
|
+
# https://github.com/rails/rails/commit/39f2c3b3ea6fac371e79c284494e3d4cfdc1e929
|
92
|
+
# https://github.com/rails/rails/commit/bdc5141652770fd227455681cde1f9899f55b0b9
|
93
|
+
# (ActiveRecord::Relation::CLAUSE_METHODS - [:having, :where]).all? { |m| send("#{m}_clause") != other.send("#{m}_clause") }
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def new_where_clause
|
99
|
+
ActiveRecord::Relation::WhereClause.empty
|
100
|
+
end
|
101
|
+
alias new_having_clause new_where_clause
|
102
|
+
end
|
103
|
+
|
104
|
+
class ActiveRecord::Relation::WhereClause
|
105
|
+
# https://github.com/rails/rails/commit/d26dd00854c783bcb1249168bb3f4adf9f99be6c
|
106
|
+
attr_reader :binds, :predicates
|
107
|
+
|
108
|
+
delegate :any?, :empty?, to: :predicates
|
109
|
+
|
110
|
+
def initialize(predicates, binds)
|
111
|
+
@predicates = predicates
|
112
|
+
@binds = binds
|
113
|
+
end
|
114
|
+
|
115
|
+
def +(other)
|
116
|
+
ActiveRecord::Relation::WhereClause.new(
|
117
|
+
predicates + other.predicates,
|
118
|
+
binds + other.binds,
|
119
|
+
)
|
120
|
+
end
|
121
|
+
|
122
|
+
def merge(other)
|
123
|
+
ActiveRecord::Relation::WhereClause.new(
|
124
|
+
predicates_unreferenced_by(other) + other.predicates,
|
125
|
+
non_conflicting_binds(other) + other.binds,
|
126
|
+
)
|
127
|
+
end
|
128
|
+
|
129
|
+
def except(*columns)
|
130
|
+
ActiveRecord::Relation::WhereClause.new(
|
131
|
+
predicates_except(columns),
|
132
|
+
binds_except(columns),
|
133
|
+
)
|
134
|
+
end
|
135
|
+
|
136
|
+
def or(other)
|
137
|
+
if empty?
|
138
|
+
other
|
139
|
+
elsif other.empty?
|
140
|
+
self
|
141
|
+
else
|
142
|
+
ActiveRecord::Relation::WhereClause.new(
|
143
|
+
[ast.or(other.ast)],
|
144
|
+
binds + other.binds
|
145
|
+
)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def to_h(table_name = nil)
|
150
|
+
equalities = predicates.grep(Arel::Nodes::Equality)
|
151
|
+
if table_name
|
152
|
+
equalities = equalities.select do |node|
|
153
|
+
node.left.relation.name == table_name
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
binds = self.binds.map { |attr| [attr.name, attr.value] }.to_h
|
158
|
+
|
159
|
+
equalities.map { |node|
|
160
|
+
name = node.left.name
|
161
|
+
[name, binds.fetch(name.to_s) {
|
162
|
+
case node.right
|
163
|
+
when Array then node.right.map(&:val)
|
164
|
+
when Arel::Nodes::Casted, Arel::Nodes::Quoted
|
165
|
+
node.right.val
|
166
|
+
end
|
167
|
+
}]
|
168
|
+
}.to_h
|
169
|
+
end
|
170
|
+
|
171
|
+
def ast
|
172
|
+
Arel::Nodes::And.new(predicates_with_wrapped_sql_literals)
|
173
|
+
end
|
174
|
+
|
175
|
+
def ==(other)
|
176
|
+
other.is_a?(ActiveRecord::Relation::WhereClause) &&
|
177
|
+
predicates == other.predicates &&
|
178
|
+
binds == other.binds
|
179
|
+
end
|
180
|
+
|
181
|
+
def invert
|
182
|
+
ActiveRecord::Relation::WhereClause.new(inverted_predicates, binds)
|
183
|
+
end
|
184
|
+
|
185
|
+
def self.empty
|
186
|
+
new([], [])
|
187
|
+
end
|
188
|
+
|
189
|
+
protected
|
190
|
+
|
191
|
+
def referenced_columns
|
192
|
+
@referenced_columns ||= begin
|
193
|
+
equality_nodes = predicates.select { |n| equality_node?(n) }
|
194
|
+
Set.new(equality_nodes, &:left)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
private
|
199
|
+
|
200
|
+
def predicates_unreferenced_by(other)
|
201
|
+
predicates.reject do |n|
|
202
|
+
equality_node?(n) && other.referenced_columns.include?(n.left)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def equality_node?(node)
|
207
|
+
node.respond_to?(:operator) && node.operator == :==
|
208
|
+
end
|
209
|
+
|
210
|
+
def non_conflicting_binds(other)
|
211
|
+
conflicts = referenced_columns & other.referenced_columns
|
212
|
+
conflicts.map! { |node| node.name.to_s }
|
213
|
+
binds.reject { |attr| conflicts.include?(attr.name) }
|
214
|
+
end
|
215
|
+
|
216
|
+
def inverted_predicates
|
217
|
+
predicates.map { |node| invert_predicate(node) }
|
218
|
+
end
|
219
|
+
|
220
|
+
def invert_predicate(node)
|
221
|
+
case node
|
222
|
+
when NilClass
|
223
|
+
raise ArgumentError, 'Invalid argument for .where.not(), got nil.'
|
224
|
+
when Arel::Nodes::In
|
225
|
+
Arel::Nodes::NotIn.new(node.left, node.right)
|
226
|
+
when Arel::Nodes::Equality
|
227
|
+
Arel::Nodes::NotEqual.new(node.left, node.right)
|
228
|
+
when String
|
229
|
+
Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new(node))
|
230
|
+
else
|
231
|
+
Arel::Nodes::Not.new(node)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def predicates_except(columns)
|
236
|
+
predicates.reject do |node|
|
237
|
+
case node
|
238
|
+
when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThanOrEqual
|
239
|
+
subrelation = (node.left.kind_of?(Arel::Attributes::Attribute) ? node.left : node.right)
|
240
|
+
columns.include?(subrelation.name.to_s)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def binds_except(columns)
|
246
|
+
binds.reject do |attr|
|
247
|
+
columns.include?(attr.name)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def predicates_with_wrapped_sql_literals
|
252
|
+
non_empty_predicates.map do |node|
|
253
|
+
if Arel::Nodes::Equality === node
|
254
|
+
node
|
255
|
+
else
|
256
|
+
wrap_sql_literal(node)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def non_empty_predicates
|
262
|
+
predicates - ['']
|
263
|
+
end
|
264
|
+
|
265
|
+
def wrap_sql_literal(node)
|
266
|
+
if ::String === node
|
267
|
+
node = Arel.sql(node)
|
268
|
+
end
|
269
|
+
Arel::Nodes::Grouping.new(node)
|
270
|
+
end
|
271
|
+
|
272
|
+
end
|
273
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: where-or
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Eric Guo
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-09-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 4.2.3
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '5'
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 4.2.3
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '5'
|
33
|
+
description: Where or function backport from Rails 5 for Rails 4.2
|
34
|
+
email: eric.guo@sandisk.com
|
35
|
+
executables: []
|
36
|
+
extensions: []
|
37
|
+
extra_rdoc_files:
|
38
|
+
- LICENSE.txt
|
39
|
+
- README.md
|
40
|
+
files:
|
41
|
+
- LICENSE.txt
|
42
|
+
- README.md
|
43
|
+
- lib/where-or.rb
|
44
|
+
homepage: https://github.com/Eric-Guo/where-or
|
45
|
+
licenses:
|
46
|
+
- MIT
|
47
|
+
metadata: {}
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options: []
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
requirements: []
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 2.4.8
|
65
|
+
signing_key:
|
66
|
+
specification_version: 4
|
67
|
+
summary: Where or function backport from Rails 5 for Rails 4.2
|
68
|
+
test_files: []
|