activerecord_any_of 1.3 → 2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 3086cee8dc2889c8e3f8874ef32631af08c750c2
4
- data.tar.gz: 5e01aa2499c952e4fc215c24890b215d1749f104
2
+ SHA256:
3
+ metadata.gz: 3f426526955604bce6800c7d7b84913cd198ad0a0b821be1e5fa7dc5b0bb215b
4
+ data.tar.gz: '08597e05b96433c2bd29cd95750beba918810fe50c320733bb864e93b273eeb1'
5
5
  SHA512:
6
- metadata.gz: 2288d0dec57e66b4b9b454f6082eca83125d30912044de57aedfc828f8aa20b98137b0d71c7de5b53a81f94a9c2f3ec6b2b6373b7afa1f10be58e1c127013832
7
- data.tar.gz: 59c3984d1052c8cff9b04cf5bd8bd07b19fef3e3082d7379f56383a6531bf63bf94b002c810c24aa56d899948b14f923313f06a2c41fd78697dbd11632a36c8b
6
+ metadata.gz: a4451ade0e580f14d30f943989c3263e1ee322bfb6d9a32483c29407c1f9b3e96e4781b7ec9a6adda3ae02675c711f0d4e6009e8c88d378fc1786ad02e33660b
7
+ data.tar.gz: e64bb560165f871e14687b27bab765d30e5a575bce057b1387a58115ec40cb8b7b15707228675f0ade9847bfeb94f7326239c97d38fd13cc1bc78d24b03bb428
data/README.md CHANGED
@@ -1,52 +1,41 @@
1
1
  # ActiverecordAnyOf
2
2
 
3
- ## A note for < 1.2 users
3
+ ## Introduction
4
4
 
5
- There was a lot of confusion about explit/implicit hash parameter notation,
6
- with people expecting this to generate an OR query :
5
+ This gem provides `#any_of` and `#none_of` on ActiveRecord.
7
6
 
8
- ```ruby
9
- User.where.any_of(name: 'Doe', active: true)
10
- ```
7
+ `#any_of` is inspired by [any_of from mongoid](http://two.mongoid.org/docs/querying/criteria.html#any_of).
11
8
 
12
- This wouldn't work, since there is only one parameter, here : `{name: 'Doe', active: true}`,
13
- so there's a single group of condition that is joined as a AND. To achieve
14
- expected result, this should have been used :
9
+ It was released before `#or` was implemented in ActiveRecord. Its main purpose was to both :
15
10
 
16
- ```ruby
17
- User.where.any_of({name: 'Doe'}, {active: true})
18
- ```
11
+ * remove the need to write a sql string when we want an `OR`
12
+ * allows to write dynamic `OR` queries, which would be a pain with a string
19
13
 
14
+ It can still be useful today given the various ways you can call it. While
15
+ ActiveRecord's `#or` only accepts relations, you can pass to `#any_of` and
16
+ `#none_of` the same kind of conditions you would pass to `#where`:
20
17
 
21
- To be true to principle of least surprise, we now automatically expand
22
- parameters consisting of a single Hash as a hash for each key, so first
23
- query will indeed generate :
24
18
 
25
19
  ```ruby
26
- User.where.any_of(name: 'Doe', active: true)
27
- # => SELECT * FROM users WHERE name = 'Doe' OR active = '1'
20
+ User.where.any_of({ active: true }, ['offline = ?', required_status], 'posts_count > 0')
28
21
  ```
29
22
 
30
-
31
- Grouping conditions can still be achieved using explicit curly brackets :
23
+ And you can still use relations, like AR's `#or`:
32
24
 
33
25
  ```ruby
34
- User.where.any_of({first_name: 'John', last_name: 'Doe'}, active: true)
35
- # => SELECT * FROM users WHERE (first_name = 'John' AND last_name = 'Doe') OR active = '1'
36
- ```
37
-
38
-
39
- ## Introduction
40
-
41
- This gem provides `#any_of` and `#none_of` on ActiveRecord.
26
+ inactive_users = User.not_activated
27
+ offline_users = User.offline
42
28
 
43
- `#any_of` is inspired by [any_of from mongoid](http://two.mongoid.org/docs/querying/criteria.html#any_of).
29
+ User.where.any_of(inactive_users, offline)
30
+ ```
44
31
 
45
- Its main purpose is to both :
32
+ ## Installation
46
33
 
47
- * remove the need to write a sql string when we want an `OR`
48
- * allows to write dynamic `OR` queries, which would be a pain with a string
34
+ In your Gemfile :
49
35
 
36
+ ```
37
+ gem 'activerecord_any_of'
38
+ ```
50
39
 
51
40
  ## Usage
52
41
 
@@ -54,7 +43,6 @@ Its main purpose is to both :
54
43
 
55
44
  It allows to compute an `OR` like query that leverages AR's `#where` syntax:
56
45
 
57
-
58
46
  #### basics
59
47
 
60
48
  ```ruby
@@ -62,26 +50,23 @@ User.where.any_of(first_name: 'Joe', last_name: 'Joe')
62
50
  # => SELECT * FROM users WHERE first_name = 'Joe' OR last_name = 'Joe'
63
51
  ```
64
52
 
65
-
66
53
  #### grouped conditions
67
54
 
68
55
  You can separate sets of hash condition by explicitly group them as hashes :
69
56
 
70
57
  ```ruby
71
- User.where.any_of({first_name: 'John', last_name: 'Joe'}, {first_name: 'Simon', last_name: 'Joe'})
58
+ User.where.any_of({ first_name: 'John', last_name: 'Joe' }, { first_name: 'Simon', last_name: 'Joe' })
72
59
  # => SELECT * FROM users WHERE ( first_name = 'John' AND last_name = 'Joe' ) OR ( first_name = 'Simon' AND last_name = 'Joe' )
73
60
  ```
74
61
 
75
-
76
62
  #### it's plain #where syntax
77
63
 
78
64
  Each `#any_of` set is the same kind you would have passed to #where :
79
65
 
80
66
  ```ruby
81
- Client.where.any_of("orders_count = '2'", ["name = ?", 'Joe'], {email: 'joe@example.com'})
67
+ Client.where.any_of("orders_count = '2'", ["name = ?", 'Joe'], { email: 'joe@example.com' })
82
68
  ```
83
69
 
84
-
85
70
  #### with relations
86
71
 
87
72
  You can as well pass `#any_of` to other relations :
@@ -90,16 +75,14 @@ You can as well pass `#any_of` to other relations :
90
75
  Client.where("orders_count = '2'").where.any_of({ email: 'joe@example.com' }, { email: 'john@example.com' })
91
76
  ```
92
77
 
93
-
94
78
  #### with associations
95
79
 
96
80
  And with associations :
97
81
 
98
82
  ```ruby
99
- User.find(1).posts.where.any_of({published: false}, "user_id IS NULL")
83
+ User.find(1).posts.where.any_of({ published: false }, 'user_id IS NULL')
100
84
  ```
101
85
 
102
-
103
86
  #### dynamic OR queries
104
87
 
105
88
  The best part is that `#any_of` accepts other relations as parameter, to help compute
@@ -117,67 +100,6 @@ inactive_users = User.where.any_of(banned_users, unconfirmed_users)
117
100
 
118
101
  ```ruby
119
102
  banned_users = User.where(banned: true)
120
- unconfirmed_users = User.where("confirmed_at IS NULL")
103
+ unconfirmed_users = User.where('confirmed_at IS NULL')
121
104
  active_users = User.where.none_of(banned_users, unconfirmed_users)
122
105
  ```
123
-
124
- ## Rails-3
125
-
126
- `activerecord_any_of` uses WhereChain, which has been introduced in rails-4. In
127
- rails-3, simply call `#any_of` and `#none_of` directly, without using `#where` :
128
-
129
- ```ruby
130
- manual_removal = User.where(id: params[:users][:destroy_ids])
131
- User.any_of(manual_removal, "email like '%@example.com'", {banned: true})
132
- @company.users.any_of(manual_removal, "email like '%@example.com'", {banned: true})
133
- User.where(offline: false).any_of( manual_removal, "email like '%@example.com'", {banned: true})
134
- ```
135
-
136
- ## Installation
137
-
138
- In your Gemfile :
139
-
140
- ```
141
- gem 'activerecord_any_of'
142
- ```
143
-
144
- Activerecord_any_of supports rails >= 3.2.13 and rails-4 (let me know if tests
145
- pass for rails < 3.2.13, I may edit gem dependencies).
146
-
147
-
148
- ## Why not an `#or` method instead ?
149
-
150
- ```ruby
151
- User.where( "email LIKE '%@example.com" ).where( active: true ).or( offline: true )
152
- ```
153
-
154
- What does this query do ? `where (email LIKE '%@example.com' AND active = '1' )
155
- OR offline = '1'` ? Or `where email LIKE '%@example.com' AND ( active = '1' OR
156
- offline = '1' )` ? This can quickly get messy and counter intuitive.
157
-
158
- The MongoId solution is quite elegant. Using `#any_of`, it is made clear which
159
- conditions are grouped through `OR` and which are grouped through `AND` :
160
-
161
- * `User.where( "email LIKE '%@example.com" ).where.any_of({ active: true }, { offline: true })`
162
- * `fakes = User.where( "email LIKE '%@example.com'" ).where( active: true ); User.where.any_of( fakes, { offline: true })`
163
-
164
- ## I want this in active_record
165
-
166
- You can [say it there](https://github.com/rails/rails/pull/10891).
167
-
168
- ## Running test
169
-
170
- Testing is done using TravisCI. You can use the wonderful [wwtd gem](https://github.com/grosser/wwtd) to run all tests locally. By default, the task to run is `bundle exec rake spec`, and will run against `sqlite3` in memory. You can change the database like so: `DB=postgresql bundle exec rake spec`. Please note that you may need to change the credentials for your database in the `database.yml` file. *Do not commit those changes.*
171
-
172
- ## Pull requests
173
-
174
- This gem is extracted from a pull request made to activerecord core, and
175
- still hope to be merged. So, any pull request here should respects usual
176
- [Rails contributing rules](http://guides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-code)
177
- when it makes sense (especially : coding conventions) to make integration
178
- in source pull request easy.
179
-
180
-
181
- ## Licence
182
-
183
- MIT-LICENSE.
data/Rakefile CHANGED
@@ -1,29 +1,4 @@
1
- #!/usr/bin/env rake
2
- $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
1
+ # frozen_string_literal: true
3
2
 
4
- require 'rubygems'
5
- require 'bundler/setup'
6
- require 'activerecord_any_of/version'
7
-
8
- task :default => :spec
9
-
10
- task :spec do
11
- puts "\n" + (cmd = "bundle exec rspec spec")
12
- system cmd
13
- end
14
-
15
- begin
16
- require 'rdoc/task'
17
- rescue LoadError
18
- require 'rdoc/rdoc'
19
- require 'rake/rdoctask'
20
- RDoc::Task = Rake::RDocTask
21
- end
22
-
23
- RDoc::Task.new(:rdoc) do |rdoc|
24
- rdoc.rdoc_dir = 'rdoc'
25
- rdoc.title = 'ActiverecordAnyOf'
26
- rdoc.options << '--line-numbers'
27
- rdoc.rdoc_files.include('README.rdoc')
28
- rdoc.rdoc_files.include('lib/**/*.rb')
29
- end
3
+ require 'bundler/gem_tasks'
4
+ task default: :spec
@@ -1,136 +1,141 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiverecordAnyOf
4
+ # Main class allowing to build alternative conditions for the query.
2
5
  class AlternativeBuilder
3
6
  def initialize(match_type, context, *queries)
4
- if Hash === queries.first and queries.count == 1
5
- queries = queries.first.each_pair.map { |attr, predicate| Hash[attr, predicate] }
7
+ if queries.first.is_a?(Hash) && (queries.count == 1)
8
+ queries = queries.first.each_pair.map { |attr, predicate| { attr => predicate } }
6
9
  end
7
10
 
8
- @builder = match_type == :negative ? NegativeBuilder.new(context, *queries) : PositiveBuilder.new(context, *queries)
11
+ @builder = if match_type == :negative
12
+ NegativeBuilder.new(context,
13
+ *queries)
14
+ else
15
+ PositiveBuilder.new(context, *queries)
16
+ end
9
17
  end
10
18
 
11
19
  def build
12
20
  @builder.build
13
21
  end
14
22
 
23
+ # Common methods for both the positive builder and the negative one.
15
24
  class Builder
16
- attr_accessor :queries_bind_values, :queries_joins_values
25
+ attr_accessor :queries_joins_values
17
26
 
18
27
  def initialize(context, *source_queries)
19
- @context, @source_queries = context, source_queries
20
- @queries_bind_values, @queries_joins_values = [], { includes: [], joins: [], references: [] }
21
- end
22
-
23
- def build
24
- ActiveRecord::Base.connection.supports_statement_cache? ? with_statement_cache : without_statement_cache
28
+ @context = context
29
+ @source_queries = source_queries
30
+ @queries_joins_values = { includes: [], joins: [], references: [] }
25
31
  end
26
32
 
27
33
  private
28
34
 
29
- def queries
30
- @queries ||= @source_queries.map do |query|
31
- if String === query || Hash === query
32
- query = where(query)
33
- elsif Array === query
34
- query = where(*query)
35
- end
36
-
37
- self.queries_bind_values += query.bind_values if query.bind_values.any?
38
- queries_joins_values[:includes].concat(query.includes_values) if query.includes_values.any?
39
- queries_joins_values[:joins].concat(query.joins_values) if query.joins_values.any?
40
- queries_joins_values[:references].concat(query.references_values) if ActiveRecord::VERSION::MAJOR >= 4 && query.references_values.any?
41
- query.arel.constraints.reduce(:and)
42
- end
35
+ def query_to_relation(query)
36
+ if query.is_a?(String) || query.is_a?(Hash)
37
+ query = where(query)
38
+ elsif query.is_a?(Array)
39
+ query = where(*query)
43
40
  end
44
41
 
45
- def uniq_queries_joins_values
46
- @uniq_queries_joins_values ||= begin
47
- { includes: [], joins: [], references: [] }.tap do |values|
48
- queries_joins_values.each do |join_type, statements|
49
- if Symbol === statements.first or String === statements.first
50
- values[ join_type ] = statements.uniq
51
- else
52
- values[ join_type ] = statements.uniq( &:to_sql )
53
- end
54
- end
55
- end
56
- end
42
+ query
43
+ end
44
+
45
+ def append_join_values(query)
46
+ { includes_values: :includes, joins_values: :joins, references_values: :references }.each do |q, joins|
47
+ values = query.send(q)
48
+ queries_joins_values[joins].concat(values) if values.any?
57
49
  end
50
+ end
58
51
 
59
- def method_missing(method_name, *args, &block)
60
- @context.send(method_name, *args, &block)
52
+ def queries
53
+ @queries ||= @source_queries.map do |query|
54
+ query = query_to_relation(query)
55
+ append_join_values(query)
56
+ query.arel.constraints.reduce(:and)
61
57
  end
58
+ end
62
59
 
63
- def add_joins_to(relation)
64
- relation = relation.references(uniq_queries_joins_values[:references]) if ActiveRecord::VERSION::MAJOR >= 4
65
- relation = relation.includes(uniq_queries_joins_values[:includes])
66
- relation.joins(uniq_queries_joins_values[:joins])
60
+ def uniq_queries_joins_values
61
+ @uniq_queries_joins_values ||= { includes: [], joins: [], references: [] }.tap do |values|
62
+ queries_joins_values.each do |join_type, statements|
63
+ values[join_type] = if statements.first.respond_to?(:to_sql)
64
+ statements.uniq(&:to_sql)
65
+ else
66
+ statements.uniq
67
+ end
68
+ end
67
69
  end
70
+ end
68
71
 
69
- def add_related_values_to(relation)
70
- relation.bind_values += queries_bind_values
71
- relation.includes_values += uniq_queries_joins_values[:includes]
72
- relation.joins_values += uniq_queries_joins_values[:joins]
73
- relation.references_values += uniq_queries_joins_values[:references] if ActiveRecord::VERSION::MAJOR >= 4
72
+ def map_multiple_bind_values(query)
73
+ query.children.map do |child|
74
+ next unless child.respond_to?(:right)
75
+ next unless child.right.respond_to?(:value)
74
76
 
75
- relation
77
+ child.right.value
76
78
  end
79
+ end
80
+
81
+ def queries_bind_values
82
+ queries.map do |query|
83
+ if query.respond_to?(:children)
84
+ map_multiple_bind_values(query)
85
+ else
86
+ next unless query.respond_to?(:right)
87
+ next unless query.right.respond_to?(:value)
77
88
 
78
- def unprepare_query(query)
79
- query.gsub(/((?<!\\)'.*?(?<!\\)'|(?<!\\)".*?(?<!\\)")|(\=\ \$\d)/) do |match|
80
- $2 and $2.gsub(/\=\ \$\d/, "= ?") or match
89
+ query.right.value
81
90
  end
82
- end
83
- end
91
+ end.flatten.compact
92
+ end
84
93
 
85
- class PositiveBuilder < Builder
86
- private
94
+ def method_missing(method_name, *args, &block)
95
+ @context.send(method_name, *args, &block)
96
+ end
87
97
 
88
- def with_statement_cache
89
- if queries && queries_bind_values.any?
90
- relation = where([unprepare_query(queries.reduce(:or).to_sql), *queries_bind_values.map { |v| v[1] }])
91
- else
92
- relation = where(queries.reduce(:or).to_sql)
93
- end
98
+ def respond_to_missing?(method, *)
99
+ @context.respond_to? method
100
+ end
94
101
 
95
- add_joins_to relation
96
- end
102
+ def add_joins_to(relation)
103
+ relation = relation.references(uniq_queries_joins_values[:references])
104
+ relation = relation.includes(uniq_queries_joins_values[:includes])
105
+ relation.joins(uniq_queries_joins_values[:joins])
106
+ end
97
107
 
98
- def without_statement_cache
99
- relation = where(queries.reduce(:or))
100
- add_related_values_to relation
108
+ def unprepare_query(query)
109
+ query.gsub(/((?<!\\)'.*?(?<!\\)'|(?<!\\)".*?(?<!\\)")|(=\ \$\d+)/) do |match|
110
+ ::Regexp.last_match(2)&.gsub(/=\ \$\d+/, '= ?') or match
101
111
  end
112
+ end
102
113
  end
103
114
 
104
- class NegativeBuilder < Builder
105
- private
106
-
107
- def with_statement_cache
108
- if ActiveRecord::VERSION::MAJOR >= 4
109
- if queries && queries_bind_values.any?
110
- relation = where.not([unprepare_query(queries.reduce(:or).to_sql), *queries_bind_values.map { |v| v[1] }])
111
- else
112
- relation = where.not(queries.reduce(:or).to_sql)
113
- end
114
- else
115
- if queries && queries_bind_values.any?
116
- relation = where([unprepare_query(Arel::Nodes::Not.new(queries.reduce(:or)).to_sql), *queries_bind_values.map { |v| v[1] }])
117
- else
118
- relation = where(Arel::Nodes::Not.new(queries.reduce(:or)).to_sql)
119
- end
120
- end
115
+ # Returns records that match any of the conditions, ie `#any_of`.
116
+ class PositiveBuilder < Builder
117
+ def build
118
+ relation = if queries && queries_bind_values.any?
119
+ where([unprepare_query(queries.reduce(:or).to_sql), *queries_bind_values])
120
+ else
121
+ where(queries.reduce(:or).to_sql)
122
+ end
121
123
 
122
- add_joins_to relation
123
- end
124
+ add_joins_to relation
125
+ end
126
+ end
124
127
 
125
- def without_statement_cache
126
- if ActiveRecord::VERSION::MAJOR >= 4
127
- relation = where.not(queries.reduce(:or))
128
- else
129
- relation = where(Arel::Nodes::Not.new(queries.reduce(:or)))
130
- end
128
+ # Returns records that match none of the conditions, ie `#none_of`.
129
+ class NegativeBuilder < Builder
130
+ def build
131
+ relation = if queries && queries_bind_values.any?
132
+ where.not([unprepare_query(queries.reduce(:or).to_sql), *queries_bind_values])
133
+ else
134
+ where.not(queries.reduce(:or).to_sql)
135
+ end
131
136
 
132
- add_related_values_to relation
133
- end
137
+ add_joins_to relation
138
+ end
134
139
  end
135
140
  end
136
141
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiverecordAnyOf
2
- VERSION = "1.3"
4
+ VERSION = '2.0'
3
5
  end
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'activerecord_any_of/alternative_builder'
2
4
 
3
5
  module ActiverecordAnyOf
6
+ # Injected into WhereChain.
4
7
  module Chained
5
8
  # Returns a new relation, which includes results matching any of the conditions
6
9
  # passed as parameters. You can think of it as a sql <tt>OR</tt> implementation :
@@ -12,7 +15,8 @@ module ActiverecordAnyOf
12
15
  # You can separate sets of hash condition by explicitly group them as hashes :
13
16
  #
14
17
  # User.where.any_of({first_name: 'John', last_name: 'Joe'}, {first_name: 'Simon', last_name: 'Joe'})
15
- # # => SELECT * FROM users WHERE ( first_name = 'John' AND last_name = 'Joe' ) OR ( first_name = 'Simon' AND last_name = 'Joe' )
18
+ # # => SELECT * FROM users WHERE ( first_name = 'John' AND last_name = 'Joe' ) OR
19
+ # ( first_name = 'Simon' AND last_name = 'Joe' )
16
20
  #
17
21
  #
18
22
  # Each #any_of set is the same kind you would have passed to #where :
@@ -38,6 +42,7 @@ module ActiverecordAnyOf
38
42
  # inactive_users = User.where.any_of(banned_users, unconfirmed_users)
39
43
  def any_of(*queries)
40
44
  raise ArgumentError, 'Called any_of() with no arguments.' if queries.none?
45
+
41
46
  AlternativeBuilder.new(:positive, @scope, *queries).build
42
47
  end
43
48
 
@@ -51,43 +56,10 @@ module ActiverecordAnyOf
51
56
  # active_users = User.where.none_of(banned_users, unconfirmed_users)
52
57
  def none_of(*queries)
53
58
  raise ArgumentError, 'Called none_of() with no arguments.' if queries.none?
54
- AlternativeBuilder.new(:negative, @scope, *queries).build
55
- end
56
- end
57
-
58
- module Deprecated
59
- def any_of(*queries)
60
- if ActiveRecord::VERSION::MAJOR >= 4
61
- ActiveSupport::Deprecation.warn( "Calling #any_of directly is deprecated and will be removed in activerecord_any_of-1.2.\nPlease call it with #where : User.where.any_of(cond1, cond2)." )
62
- end
63
-
64
- raise ArgumentError, 'Called any_of() with no arguments.' if queries.none?
65
- AlternativeBuilder.new(:positive, self, *queries).build
66
- end
67
-
68
- def none_of(*queries)
69
- if ActiveRecord::VERSION::MAJOR >= 4
70
- ActiveSupport::Deprecation.warn( "Calling #none_of directly is deprecated and will be removed in activerecord_any_of-1.2.\nPlease call it with #where : User.where.none_of(cond1, cond2)." )
71
- end
72
59
 
73
- raise ArgumentError, 'Called none_of() with no arguments.' if queries.none?
74
- AlternativeBuilder.new(:negative, self, *queries).build
60
+ AlternativeBuilder.new(:negative, @scope, *queries).build
75
61
  end
76
62
  end
77
63
  end
78
64
 
79
- if ActiveRecord::VERSION::MAJOR >= 4
80
- module ActiverecordAnyOfDelegation
81
- delegate :any_of, to: :all
82
- delegate :none_of, to: :all
83
- end
84
- else
85
- module ActiverecordAnyOfDelegation
86
- delegate :any_of, to: :scoped
87
- delegate :none_of, to: :scoped
88
- end
89
- end
90
-
91
- ActiveRecord::Relation.send(:include, ActiverecordAnyOf::Deprecated)
92
- ActiveRecord::Relation::WhereChain.send(:include, ActiverecordAnyOf::Chained) if ActiveRecord::VERSION::MAJOR >= 4
93
- ActiveRecord::Base.send(:extend, ActiverecordAnyOfDelegation)
65
+ ActiveRecord::Relation::WhereChain.include ActiverecordAnyOf::Chained
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord_any_of
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.3'
4
+ version: '2.0'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Olivier El Mekki
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-04 00:00:00.000000000 Z
11
+ date: 2023-06-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,76 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 3.2.13
19
+ version: '7'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '5'
22
+ version: '8'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: 3.2.13
29
+ version: '7'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '5'
33
- - !ruby/object:Gem::Dependency
34
- name: rspec-rails
35
- requirement: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '2.12'
40
- type: :development
41
- prerelease: false
42
- version_requirements: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - "~>"
45
- - !ruby/object:Gem::Version
46
- version: '2.12'
47
- - !ruby/object:Gem::Dependency
48
- name: rake
49
- requirement: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - "~>"
52
- - !ruby/object:Gem::Version
53
- version: '10'
54
- type: :development
55
- prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
57
- requirements:
58
- - - "~>"
59
- - !ruby/object:Gem::Version
60
- version: '10'
61
- - !ruby/object:Gem::Dependency
62
- name: combustion
63
- requirement: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - ">="
66
- - !ruby/object:Gem::Version
67
- version: 0.5.1
68
- type: :development
69
- prerelease: false
70
- version_requirements: !ruby/object:Gem::Requirement
71
- requirements:
72
- - - ">="
73
- - !ruby/object:Gem::Version
74
- version: 0.5.1
75
- - !ruby/object:Gem::Dependency
76
- name: database_cleaner
77
- requirement: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - ">="
80
- - !ruby/object:Gem::Version
81
- version: '0'
82
- type: :development
83
- prerelease: false
84
- version_requirements: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - ">="
87
- - !ruby/object:Gem::Version
88
- version: '0'
32
+ version: '8'
89
33
  description: Any_of allows to compute dynamic OR queries.
90
34
  email:
91
35
  - olivier@el-mekki.com
@@ -99,19 +43,12 @@ files:
99
43
  - lib/activerecord_any_of.rb
100
44
  - lib/activerecord_any_of/alternative_builder.rb
101
45
  - lib/activerecord_any_of/version.rb
102
- - spec/activerecord_any_of_spec.rb
103
- - spec/db/database.yml
104
- - spec/db/schema.rb
105
- - spec/fixtures/authors.yml
106
- - spec/fixtures/posts.yml
107
- - spec/fixtures/users.yml
108
- - spec/spec_helper.rb
109
- - spec/support/models.rb
110
46
  homepage: https://github.com/oelmekki/activerecord_any_of
111
47
  licenses:
112
48
  - MIT
113
- metadata: {}
114
- post_install_message:
49
+ metadata:
50
+ rubygems_mfa_required: 'true'
51
+ post_install_message:
115
52
  rdoc_options: []
116
53
  require_paths:
117
54
  - lib
@@ -119,25 +56,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
119
56
  requirements:
120
57
  - - ">="
121
58
  - !ruby/object:Gem::Version
122
- version: '0'
59
+ version: 2.7.0
123
60
  required_rubygems_version: !ruby/object:Gem::Requirement
124
61
  requirements:
125
62
  - - ">="
126
63
  - !ruby/object:Gem::Version
127
64
  version: '0'
128
65
  requirements: []
129
- rubyforge_project:
130
- rubygems_version: 2.2.2
131
- signing_key:
66
+ rubygems_version: 3.3.26
67
+ signing_key:
132
68
  specification_version: 4
133
69
  summary: Mongoid's any_of like implementation for activerecord
134
- test_files:
135
- - spec/db/schema.rb
136
- - spec/db/database.yml
137
- - spec/support/models.rb
138
- - spec/spec_helper.rb
139
- - spec/fixtures/users.yml
140
- - spec/fixtures/posts.yml
141
- - spec/fixtures/authors.yml
142
- - spec/activerecord_any_of_spec.rb
143
- has_rdoc:
70
+ test_files: []
@@ -1,196 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ActiverecordAnyOf do
4
- fixtures :authors, :posts, :users
5
-
6
- describe 'finding with alternate conditions' do
7
- let(:davids) { Author.where(name: "David") }
8
-
9
- it "matches hash combinations" do
10
- if ActiveRecord::VERSION::MAJOR >= 4
11
- expect(Author.where.any_of({name: 'David'}, {name: 'Mary'})).to match_array(authors(:david, :mary))
12
- else
13
- expect(Author.any_of({name: 'David'}, {name: 'Mary'})).to match_array(authors(:david, :mary))
14
- end
15
- end
16
-
17
- it "matches combination of hash and array" do
18
- if ActiveRecord::VERSION::MAJOR >= 4
19
- expect(Author.where.any_of({name: 'David'}, ['name = ?', 'Mary'])).to match_array(authors(:david, :mary))
20
- else
21
- expect(Author.any_of({name: 'David'}, ['name = ?', 'Mary'])).to match_array(authors(:david, :mary))
22
- end
23
- end
24
-
25
- it "matches a combination of hashes, arrays, and AR relations" do
26
- if ActiveRecord::VERSION::MAJOR >= 4
27
- expect(Author.where.any_of(davids, ['name = ?', 'Mary'], {name: 'Bob'})).to match_array(authors(:david, :mary, :bob))
28
- else
29
- expect(Author.any_of(davids, ['name = ?', 'Mary'], {name: 'Bob'})).to match_array(authors(:david, :mary, :bob))
30
- end
31
- end
32
-
33
- it "matches a combination of strings, hashes, and AR relations" do
34
- if ActiveRecord::VERSION::MAJOR >= 4
35
- expect(Author.where.any_of(davids, "name = 'Mary'", {name: 'Bob', id: 3})).to match_array(authors(:david, :mary, :bob))
36
- else
37
- expect(Author.any_of(davids, "name = 'Mary'", {name: 'Bob', id: 3})).to match_array(authors(:david, :mary, :bob))
38
- end
39
- end
40
-
41
- it "doesn't find combinations previously filtered out" do
42
- if ActiveRecord::VERSION::MAJOR >= 4
43
- expect(Author.where.not(name: 'Mary').where.any_of(davids, ['name = ?', 'Mary'])).to match_array([authors(:david)])
44
- else
45
- expect(Author.where("name != 'Mary'").any_of(davids, ['name = ?', 'Mary'])).to match_array([authors(:david)])
46
- end
47
- end
48
- end
49
-
50
- it 'finding with alternate conditions on has_many association' do
51
- david = authors(:david)
52
- welcome = david.posts.where(body: 'Such a lovely day')
53
- expected = ['Welcome to the weblog', 'So I was thinking']
54
-
55
- if ActiveRecord::VERSION::MAJOR >= 4
56
- expect(david.posts.where.any_of(welcome, {type: 'SpecialPost'}).map(&:title)).to match_array(expected)
57
- else
58
- expect(david.posts.any_of(welcome, {type: 'SpecialPost'}).map(&:title)).to match_array(expected)
59
- end
60
- end
61
-
62
- it 'finds with combined polymorphic associations' do
63
- company = Company.create!
64
- university = University.create!
65
-
66
- company.users << users(:ezra)
67
- university.users << users(:aria)
68
-
69
- if ActiveRecord::VERSION::MAJOR >= 4
70
- expect(User.where.any_of(company.users, university.users)).to match_array(users(:ezra, :aria))
71
- else
72
- expect(User.any_of(company.users, university.users)).to match_array(users(:ezra, :aria))
73
- end
74
- end
75
-
76
- it 'finds with more than 2 combined polymorphic associations' do
77
- company = Company.create!
78
- university = University.create!
79
- company2 = Company.create!
80
-
81
- company.users << users(:ezra)
82
- university.users << users(:aria)
83
- company2.users << users(:james)
84
-
85
- if ActiveRecord::VERSION::MAJOR >= 4
86
- expect(User.where.any_of(company.users, university.users, company2.users)).to match_array(users(:ezra, :aria, :james))
87
- else
88
- expect(User.any_of(company.users, university.users, company2.users)).to match_array(users(:ezra, :aria, :james))
89
- end
90
- end
91
-
92
- it 'finds alternatives with combined has_many associations' do
93
- david, mary = authors(:david, :mary)
94
-
95
- if ActiveRecord::VERSION::MAJOR >= 4
96
- expect(Post.where.any_of(david.posts, mary.posts)).to match_array(david.posts + mary.posts)
97
- else
98
- expect(Post.any_of(david.posts, mary.posts)).to match_array(david.posts + mary.posts)
99
- end
100
- end
101
-
102
- it 'finds alternatives with more than 2 combined has_many associations' do
103
- david, mary, bob = authors(:david, :mary, :bob)
104
-
105
- if ActiveRecord::VERSION::MAJOR >= 4
106
- expect(Post.where.any_of(david.posts, mary.posts, bob.posts)).to match_array(david.posts + mary.posts + bob.posts)
107
- else
108
- expect(Post.any_of(david.posts, mary.posts, bob.posts)).to match_array(david.posts + mary.posts + bob.posts)
109
- end
110
- end
111
-
112
- describe 'finding alternate dynamically with joined queries' do
113
- it "matches combined AR relations with joins" do
114
- david = Author.where(posts: { title: 'Welcome to the weblog' }).joins(:posts)
115
- mary = Author.where(posts: { title: "eager loading with OR'd conditions" }).joins(:posts)
116
-
117
- if ActiveRecord::VERSION::MAJOR >= 4
118
- expect(Author.where.any_of(david, mary)).to match_array(authors(:david, :mary))
119
- else
120
- expect(Author.any_of(david, mary)).to match_array(authors(:david, :mary))
121
- end
122
- end
123
-
124
- it "matches combined AR relations with joins and includes" do
125
- if ActiveRecord::VERSION::MAJOR >= 4
126
- david = Author.where(posts: { title: 'Welcome to the weblog' }).includes(:posts).references(:posts)
127
- mary = Author.where(posts: { title: "eager loading with OR'd conditions" }).includes(:posts).references(:posts)
128
- expect(Author.where.any_of(david, mary)).to match_array(authors(:david, :mary))
129
- else
130
- david = Author.where(posts: { title: 'Welcome to the weblog' }).includes(:posts)
131
- mary = Author.where(posts: { title: "eager loading with OR'd conditions" }).includes(:posts)
132
- expect(Author.any_of(david, mary)).to match_array(authors(:david, :mary))
133
- end
134
- end
135
- end
136
-
137
- it 'finding with alternate negative conditions' do
138
- if ActiveRecord::VERSION::MAJOR >= 4
139
- expect(Author.where.none_of({name: 'David'}, {name: 'Mary'})).to match_array([authors(:bob)])
140
- else
141
- expect(Author.none_of({name: 'David'}, {name: 'Mary'})).to match_array([authors(:bob)])
142
- end
143
- end
144
-
145
- it 'finding with alternate negative conditions on association' do
146
- david = Author.where(name: 'David').first
147
- welcome = david.posts.where(body: 'Such a lovely day')
148
- expected = ['sti comments', 'sti me', 'habtm sti test']
149
-
150
- if ActiveRecord::VERSION::MAJOR >= 4
151
- expect(david.posts.where.none_of(welcome, {type: 'SpecialPost'}).map(&:title)).to match_array(expected)
152
- else
153
- expect(david.posts.none_of(welcome, {type: 'SpecialPost'}).map(&:title)).to match_array(expected)
154
- end
155
- end
156
-
157
- it 'calling #any_of with no argument raise exception' do
158
- if ActiveRecord::VERSION::MAJOR >= 4
159
- expect { Author.where.any_of }.to raise_exception(ArgumentError)
160
- else
161
- expect { Author.any_of }.to raise_exception(ArgumentError)
162
- end
163
- end
164
-
165
- it 'calling #none_of with no argument raise exception' do
166
- if ActiveRecord::VERSION::MAJOR >= 4
167
- expect { Author.where.none_of }.to raise_exception(ArgumentError)
168
- else
169
- expect { Author.none_of }.to raise_exception(ArgumentError)
170
- end
171
- end
172
-
173
- it 'calling #any_of after a wildcard query works' do
174
- if ActiveRecord::VERSION::MAJOR >= 4
175
- expect(Author.where("name like '%av%'").where.any_of({name: 'David'}, {name: 'Mary'})).to match_array([authors(:david)])
176
- else
177
- expect(Author.where("name like '%av%'").any_of({name: 'David'}, {name: 'Mary'})).to match_array([authors(:david)])
178
- end
179
- end
180
-
181
- it 'calling #any_of with a single Hash as parameter expands it' do
182
- if ActiveRecord::VERSION::MAJOR >= 4
183
- expect(Author.where.any_of(name: 'David', id: 2)).to match_array(authors(:david, :mary))
184
- else
185
- expect(Author.any_of(name: 'David', id: 2)).to match_array(authors(:david, :mary))
186
- end
187
- end
188
-
189
- if ActiveRecord::VERSION::MAJOR >= 4
190
- it 'calling directly #any_of is deprecated in rails-4' do
191
- allow(ActiveSupport::Deprecation).to receive(:warn)
192
- Author.any_of({name: 'David'}, {name: 'Mary'})
193
- expect(ActiveSupport::Deprecation).to have_received(:warn)
194
- end
195
- end
196
- end
data/spec/db/database.yml DELETED
@@ -1,28 +0,0 @@
1
- sqlite3:
2
- adapter: <%= "jdbc" if defined? JRUBY_VERSION %>sqlite3
3
- database: activerecord_any_of.sqlite3.db
4
- sqlite3mem:
5
- adapter: <%= "jdbc" if defined? JRUBY_VERSION %>sqlite3
6
- database: ":memory:"
7
- postgresql:
8
- adapter: postgresql
9
- encoding: unicode
10
- database: activerecord_any_of_test
11
- pool: 5
12
- username: postgres
13
- password:
14
- min_messages: warning
15
- mysql:
16
- adapter: <%= defined?(JRUBY_VERSION) ? "jdbcmysql" : "mysql2" %>
17
- host: localhost
18
- username: root
19
- password:
20
- database: activerecord_any_of_test
21
- encoding: utf8
22
- ## Add DB Configuration to run Oracle tests
23
- oracle:
24
- adapter: oracle_enhanced
25
- host: localhost
26
- username: activerecord_any_of_dev
27
- password:
28
- database: xe
data/spec/db/schema.rb DELETED
@@ -1,33 +0,0 @@
1
- ActiveRecord::Schema.define do
2
- create_table :authors do |t|
3
- t.string :name
4
- t.datetime :created_at
5
- t.datetime :updated_at
6
- end
7
-
8
- create_table :posts do |t|
9
- t.string :title
10
- t.text :body
11
- t.integer :author_id
12
- t.string :type
13
- t.datetime :created_at
14
- t.datetime :updated_at
15
- end
16
-
17
- create_table :companies do |t|
18
- t.string :name
19
- end
20
-
21
- create_table :universities do |t|
22
- t.string :name
23
- end
24
-
25
- create_table :memberships do |t|
26
- t.references :organization, polymorphic: true
27
- t.references :user
28
- end
29
-
30
- create_table :users do |t|
31
- t.string :name
32
- end
33
- end
@@ -1,11 +0,0 @@
1
- david:
2
- id: 1
3
- name: David
4
-
5
- mary:
6
- id: 2
7
- name: Mary
8
-
9
- bob:
10
- id: 3
11
- name: Bob
@@ -1,76 +0,0 @@
1
- welcome:
2
- id: 1
3
- author_id: 1
4
- title: Welcome to the weblog
5
- body: Such a lovely day
6
- type: Post
7
-
8
- thinking:
9
- id: 2
10
- author_id: 1
11
- title: So I was thinking
12
- body: Like I hopefully always am
13
- type: SpecialPost
14
-
15
- authorless:
16
- id: 3
17
- author_id: 0
18
- title: I don't have any comments
19
- body: I just don't want to
20
- type: Post
21
-
22
- sti_comments:
23
- id: 4
24
- author_id: 1
25
- title: sti comments
26
- body: hello
27
- type: Post
28
-
29
- sti_post_and_comments:
30
- id: 5
31
- author_id: 1
32
- title: sti me
33
- body: hello
34
- type: StiPost
35
-
36
- sti_habtm:
37
- id: 6
38
- author_id: 1
39
- title: habtm sti test
40
- body: hello
41
- type: Post
42
-
43
- eager_other:
44
- id: 7
45
- author_id: 2
46
- title: eager loading with OR'd conditions
47
- body: hello
48
- type: Post
49
-
50
- misc_by_bob:
51
- id: 8
52
- author_id: 3
53
- title: misc post by bob
54
- body: hello
55
- type: Post
56
-
57
- misc_by_mary:
58
- id: 9
59
- author_id: 2
60
- title: misc post by mary
61
- body: hello
62
- type: Post
63
-
64
- other_by_bob:
65
- id: 10
66
- author_id: 3
67
- title: other post by bob
68
- body: hello
69
- type: Post
70
-
71
- other_by_mary:
72
- id: 11
73
- author_id: 2
74
- title: other post by mary
75
- body: hello
76
- type: Post
@@ -1,9 +0,0 @@
1
- ezra:
2
- id: 1
3
- name: "Ezra"
4
- aria:
5
- id: 2
6
- name: "Aria"
7
- james:
8
- id: 3
9
- name: "James"
data/spec/spec_helper.rb DELETED
@@ -1,36 +0,0 @@
1
- plugin_test_dir = File.dirname(__FILE__)
2
-
3
- require 'rubygems'
4
- require 'bundler/setup'
5
- require 'pry'
6
-
7
- require 'logger'
8
- require 'rails/all'
9
- require 'active_record'
10
- ActiveRecord::Base.logger = Logger.new(plugin_test_dir + "/debug.log")
11
-
12
- require 'yaml'
13
- require 'erb'
14
- ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read(plugin_test_dir + "/db/database.yml")).result)
15
- ActiveRecord::Base.establish_connection((ENV["DB"] ||= 'sqlite3mem').to_sym)
16
- ActiveRecord::Migration.verbose = false
17
-
18
- require 'combustion/database'
19
- Combustion::Database.create_database(ActiveRecord::Base.configurations[ENV["DB"]])
20
- load(File.join(plugin_test_dir, "db", "schema.rb"))
21
-
22
- require 'activerecord_any_of'
23
- require 'support/models'
24
-
25
- require 'action_controller'
26
- require 'rspec/rails'
27
- require 'database_cleaner'
28
- RSpec.configure do |config|
29
- config.fixture_path = "#{plugin_test_dir}/fixtures"
30
- config.use_transactional_fixtures = true
31
- config.after(:suite) do
32
- unless /sqlite/ === ENV['DB']
33
- Combustion::Database.drop_database(ActiveRecord::Base.configurations[ENV['DB']])
34
- end
35
- end
36
- end
@@ -1,34 +0,0 @@
1
- class Author < ActiveRecord::Base
2
- has_many :posts
3
- end
4
-
5
- class Post < ActiveRecord::Base
6
- belongs_to :author
7
- end
8
-
9
- class SpecialPost < Post
10
- end
11
-
12
- class StiPost < Post
13
- end
14
-
15
- class User < ActiveRecord::Base
16
- has_many :memberships
17
- has_many :companies, through: :memberships, source: :organization, source_type: "Company"
18
- has_many :universities, through: :memberships, source: :organization, source_type: "University"
19
- end
20
-
21
- class Membership < ActiveRecord::Base
22
- belongs_to :user
23
- belongs_to :organization, polymorphic: true
24
- end
25
-
26
- class Company < ActiveRecord::Base
27
- has_many :memberships, as: :organization
28
- has_many :users, through: :memberships
29
- end
30
-
31
- class University < ActiveRecord::Base
32
- has_many :memberships, as: :organization
33
- has_many :users, through: :memberships
34
- end