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.
@@ -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.