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.
@@ -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
- activerecord-where-any-of
2
- =========================
1
+ # ActiveRecord where_any_of
3
2
 
4
- Description:
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.0.5'
3
+ s.version = '1.1.3'
4
4
  s.license = 'MIT'
5
- s.date = '2014-07-24'
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.where_values.reduce(:and)
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
- require 'active_record/where-any-of-mixin'
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
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format progress
@@ -0,0 +1,3 @@
1
+ require 'rspec'
2
+
3
+ Dir[File.expand_path('../support/**/*.rb', __FILE__)].each { |f| require f }
@@ -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.0.5
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: 2014-07-24 00:00:00.000000000 Z
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
- description: |-
56
- Provides a mechanism for OR'ing together one or more AREL relations. Normally when
57
- one performs `SomeModel.where(condition_1).where(condition_2)`, the result is to AND
58
- those conditions together. Unfortunately, there is no good way of OR'ing them without
59
- converting the conditions into manual SQL. This mixin addresses that issue.
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.2.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.