where_lower 0.3.0 → 0.3.1
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 +4 -4
- data/.gitignore +11 -1
- data/.rubocop.yml +856 -0
- data/.travis.yml +23 -12
- data/Appraisals +14 -9
- data/CHANGELOG.md +59 -10
- data/Gemfile +1 -1
- data/README.md +17 -20
- data/Rakefile +5 -2
- data/gemfiles/rails_4_0.gemfile +7 -0
- data/gemfiles/{rails3_1.gemfile → rails_4_1.gemfile} +2 -2
- data/gemfiles/{rails3_2.gemfile → rails_4_2.gemfile} +2 -2
- data/gemfiles/{rails4_0.gemfile → rails_5_0.gemfile} +2 -2
- data/lib/where_lower.rb +5 -5
- data/lib/where_lower/active_record_extension.rb +22 -1
- data/lib/where_lower/base.rb +29 -7
- data/lib/where_lower/scope_spawner.rb +69 -33
- data/lib/where_lower/version.rb +9 -1
- data/spec/spec_helper.rb +21 -16
- data/spec/where_lower_spec.rb +549 -300
- data/where_lower.gemspec +21 -8
- metadata +95 -42
data/.travis.yml
CHANGED
@@ -1,15 +1,26 @@
|
|
1
|
-
|
1
|
+
# Send builds to container-based infrastructure
|
2
|
+
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
|
3
|
+
sudo: false
|
4
|
+
language: ruby
|
5
|
+
cache:
|
6
|
+
- bundler
|
2
7
|
rvm:
|
3
|
-
- 1.
|
4
|
-
-
|
5
|
-
|
6
|
-
|
8
|
+
- 2.1.10
|
9
|
+
- 2.2.6
|
10
|
+
# Since the Travis Build Env does not recognize `2.3` alias yet
|
11
|
+
# We need to specify the version precisely
|
12
|
+
- 2.3.3
|
13
|
+
- ruby-head
|
7
14
|
gemfile:
|
8
|
-
- gemfiles/
|
9
|
-
- gemfiles/
|
10
|
-
- gemfiles/
|
15
|
+
- gemfiles/rails_4_0.gemfile
|
16
|
+
- gemfiles/rails_4_1.gemfile
|
17
|
+
- gemfiles/rails_4_2.gemfile
|
11
18
|
matrix:
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
19
|
+
fast_finish: true
|
20
|
+
allow_failures:
|
21
|
+
- rvm: ruby-head
|
22
|
+
include:
|
23
|
+
- rvm: 2.3.3
|
24
|
+
gemfile: gemfiles/rails_5_0.gemfile
|
25
|
+
- rvm: ruby-head
|
26
|
+
gemfile: gemfiles/rails_5_0.gemfile
|
data/Appraisals
CHANGED
@@ -1,15 +1,20 @@
|
|
1
1
|
|
2
|
-
appraise "
|
3
|
-
version = "
|
4
|
-
gem
|
2
|
+
appraise "rails_4_0" do
|
3
|
+
version = "~> 4.0.12"
|
4
|
+
gem "activerecord", version
|
5
5
|
end
|
6
6
|
|
7
|
-
appraise "
|
8
|
-
version = "
|
9
|
-
gem
|
7
|
+
appraise "rails_4_1" do
|
8
|
+
version = "~> 4.1.8"
|
9
|
+
gem "activerecord", version
|
10
10
|
end
|
11
11
|
|
12
|
-
appraise "
|
13
|
-
version = "4.0
|
14
|
-
gem
|
12
|
+
appraise "rails_4_2" do
|
13
|
+
version = "~> 4.2.0"
|
14
|
+
gem "activerecord", version
|
15
|
+
end
|
16
|
+
|
17
|
+
appraise "rails_5_0" do
|
18
|
+
version = "~> 5.0.0"
|
19
|
+
gem "activerecord", version
|
15
20
|
end
|
data/CHANGELOG.md
CHANGED
@@ -1,14 +1,63 @@
|
|
1
|
-
|
1
|
+
# Change Log
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
This project adheres to [Semantic Versioning](http://semver.org/).
|
2
4
|
|
3
5
|
|
4
|
-
|
5
|
-
- Add: You can now pass a nested hash for associaiton conditions (see README)
|
6
|
-
- Change: Now it requires at least ruby 1.9.2
|
6
|
+
## [Unreleased]
|
7
7
|
|
8
|
-
|
9
|
-
- Fix: You can now use this with named scopes
|
10
|
-
- Change: Use dynamic query string instead of Arel Table to generate relation (which cause the bug)
|
11
|
-
- Dev: Add a few test for compatibility with gem `squeel`
|
8
|
+
### Added
|
12
9
|
|
13
|
-
-
|
14
|
-
|
10
|
+
- Nothing
|
11
|
+
|
12
|
+
### Changed
|
13
|
+
|
14
|
+
- Nothing
|
15
|
+
|
16
|
+
### Fixed
|
17
|
+
|
18
|
+
- Nothing
|
19
|
+
|
20
|
+
|
21
|
+
## [0.3.1]
|
22
|
+
|
23
|
+
### Changed
|
24
|
+
|
25
|
+
- Add support for AR 5.0.x
|
26
|
+
- Drop support for AR 3.x
|
27
|
+
- Drop support for Ruby < 2.1
|
28
|
+
|
29
|
+
|
30
|
+
## [0.3.0]
|
31
|
+
|
32
|
+
### Added
|
33
|
+
|
34
|
+
- You can now pass a nested hash for associaiton conditions (see README)
|
35
|
+
|
36
|
+
### Changed
|
37
|
+
|
38
|
+
- Now it requires at least ruby 1.9.2
|
39
|
+
|
40
|
+
|
41
|
+
## [0.2.0]
|
42
|
+
|
43
|
+
### Changed
|
44
|
+
|
45
|
+
- Use dynamic query string instead of Arel Table to generate relation (which cause the bug)
|
46
|
+
- Add a few test for compatibility with gem `squeel`
|
47
|
+
|
48
|
+
### Fixed
|
49
|
+
|
50
|
+
- You can now use this with named scopes
|
51
|
+
|
52
|
+
|
53
|
+
## 0.1.0
|
54
|
+
|
55
|
+
### Added
|
56
|
+
|
57
|
+
- Initial Release
|
58
|
+
|
59
|
+
|
60
|
+
[Unreleased]: https://github.com/AssetSync/asset_sync/compare/v0.3.1...HEAD
|
61
|
+
[0.3.1]: https://github.com/AssetSync/asset_sync/compare/v0.3.0...v0.3.1
|
62
|
+
[0.3.0]: https://github.com/AssetSync/asset_sync/compare/v0.2.0...v0.3.0
|
63
|
+
[0.2.0]: https://github.com/AssetSync/asset_sync/compare/v0.1.0...v0.2.0
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,29 +1,26 @@
|
|
1
|
-
where_lower
|
2
|
-
===========
|
1
|
+
# where_lower
|
3
2
|
|
4
3
|
Provide an easy way to use case insensitive `where` in ActiveRecord.
|
5
4
|
|
6
|
-
### Support
|
7
|
-
===========
|
8
|
-
Tested against:
|
9
|
-
- Active Record of version `3.1`, `3.2` and `4.0`
|
10
|
-
- Ruby `1.9.2`, `1.9.3`, `2.0.0` (except Rails 4 with `1.9.2`)
|
11
5
|
|
12
|
-
|
13
|
-
[](http://badge.fury.io/rb/where_lower)
|
14
|
-
[](https://gemnasium.com/PikachuEXE/where_lower)
|
15
|
-
[](https://coveralls.io/r/PikachuEXE/where_lower)
|
16
|
-
[](https://codeclimate.com/github/PikachuEXE/where_lower)
|
6
|
+
## Status
|
17
7
|
|
18
|
-
|
19
|
-
|
8
|
+
[](https://travis-ci.org/PikachuEXE/where_lower)
|
9
|
+
[](http://badge.fury.io/rb/where_lower)
|
10
|
+
[](https://gemnasium.com/PikachuEXE/where_lower)
|
11
|
+
[](https://coveralls.io/r/PikachuEXE/where_lower)
|
12
|
+
[](https://codeclimate.com/github/PikachuEXE/where_lower)
|
13
|
+
[](https://inch-ci.org/github/PikachuEXE/where_lower)
|
14
|
+
|
15
|
+
|
16
|
+
## Installation
|
20
17
|
|
21
18
|
```ruby
|
22
19
|
gem 'where_lower'
|
23
20
|
```
|
24
21
|
|
25
|
-
|
26
|
-
|
22
|
+
|
23
|
+
## Usage
|
27
24
|
Supports `String`, `Array`, `Range`
|
28
25
|
Values in `Array` and `Range` will be converted to `String` and then `downcase`
|
29
26
|
Other types will not be touched
|
@@ -32,7 +29,7 @@ Other types will not be touched
|
|
32
29
|
SomeActiveRecordClass.where_lower(attribute1: 'AbC', attribute2: ['stRing', 123, :symBol], attribute3: ('AA'..'AZ'))
|
33
30
|
```
|
34
31
|
|
35
|
-
Since `0.3.0`
|
32
|
+
### Since `0.3.0`
|
36
33
|
You can pass a nested hash (1 level deep only) for association condition
|
37
34
|
```ruby
|
38
35
|
record.association_records.where_lower(association_table: {association_column: value})
|
@@ -44,6 +41,6 @@ I don't plan to support any "smart" table guessing though
|
|
44
41
|
record.association_records.where_lower('association_table.association_column' => value)
|
45
42
|
```
|
46
43
|
|
47
|
-
|
48
|
-
|
49
|
-
[Matthew Rudy Jacobs](https://github.com/matthewrudy) (Who wrote the first version of `where_lower` method)
|
44
|
+
|
45
|
+
## Contributors
|
46
|
+
- [Matthew Rudy Jacobs](https://github.com/matthewrudy) (Who wrote the first version of `where_lower` method)
|
data/Rakefile
CHANGED
@@ -1,15 +1,18 @@
|
|
1
1
|
require "appraisal"
|
2
2
|
require "bundler"
|
3
3
|
require "rspec/core/rake_task"
|
4
|
+
require "rubocop/rake_task"
|
4
5
|
|
5
6
|
Bundler::GemHelper.install_tasks
|
6
7
|
|
7
8
|
RSpec::Core::RakeTask.new(:spec)
|
8
9
|
|
10
|
+
RuboCop::RakeTask.new(:rubocop)
|
11
|
+
|
9
12
|
if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"]
|
10
13
|
task :default do
|
11
|
-
sh "
|
14
|
+
sh "appraisal install && rake appraisal spec"
|
12
15
|
end
|
13
16
|
else
|
14
|
-
task :
|
17
|
+
task default: [:spec]
|
15
18
|
end
|
data/lib/where_lower.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
1
|
+
require "where_lower/version"
|
2
|
+
|
3
|
+
require "where_lower/base"
|
4
|
+
require "where_lower/scope_spawner"
|
5
|
+
require "where_lower/active_record_extension"
|
@@ -1,12 +1,32 @@
|
|
1
|
+
require "active_record"
|
2
|
+
require "where_lower/base"
|
3
|
+
|
4
|
+
# :nodoc:
|
1
5
|
module WhereLower
|
6
|
+
# :nodoc:
|
2
7
|
module ActiveRecordExtension
|
3
8
|
def self.included(base)
|
4
9
|
base.extend(ClassMethods)
|
5
10
|
end
|
6
11
|
|
12
|
+
# :nodoc:
|
7
13
|
module ClassMethods
|
14
|
+
# A bit like `where`, but only accept hash, with value of `String`, `Array`, `Range`
|
15
|
+
#
|
16
|
+
# @api
|
17
|
+
#
|
18
|
+
# @param fields [Hash]
|
19
|
+
# the conditions in hash, values will be downcase
|
20
|
+
# It could also be a 1 level deep hash so that it can be used in join
|
21
|
+
#
|
22
|
+
# @example Find user by username case insensitively
|
23
|
+
# User.where_lower(username: param[:name])
|
24
|
+
# @example Find user by alias case insensitively
|
25
|
+
# User.join(:aliases).where_lower(aliases:{name: param[:name]})
|
26
|
+
#
|
27
|
+
# @raise [ArgumentError] when fields is not a Hash
|
8
28
|
def where_lower(fields)
|
9
|
-
|
29
|
+
fail AugumentError, "fields is not a Hash" unless fields.is_a?(Hash)
|
10
30
|
|
11
31
|
WhereLower::Base.spawn_lower_scope(self, fields)
|
12
32
|
end
|
@@ -14,6 +34,7 @@ module WhereLower
|
|
14
34
|
end
|
15
35
|
end
|
16
36
|
|
37
|
+
# Automagically include the module on require
|
17
38
|
ActiveRecord::Base.class_eval do
|
18
39
|
include ::WhereLower::ActiveRecordExtension
|
19
40
|
end
|
data/lib/where_lower/base.rb
CHANGED
@@ -1,22 +1,44 @@
|
|
1
|
+
require "where_lower/scope_spawner"
|
2
|
+
|
1
3
|
module WhereLower
|
2
|
-
|
4
|
+
# Table name and column name seperator
|
5
|
+
# This should never change
|
6
|
+
SEPERATOR = ".".freeze
|
3
7
|
|
8
|
+
# Raised when some deeply nested hash passed in which cannot be handled by this gem
|
4
9
|
class TooDeepNestedConditions < ArgumentError; end
|
5
10
|
|
11
|
+
# Internal API to be called from extension methods
|
6
12
|
module Base
|
7
13
|
class << self
|
8
|
-
#
|
9
|
-
#
|
14
|
+
# Spawn a new scope based on existing scope and hash
|
15
|
+
# This is method is for internal use
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
#
|
19
|
+
# @params scope [ActiveRecord::Relation, ActiveRecord::Base]
|
20
|
+
# the existing scope, ActiveRecord::Base also works since `where` will be delegated
|
21
|
+
# @params fields [Hash]
|
22
|
+
# @see `.where_lower`
|
23
|
+
#
|
24
|
+
# @see `.spawn_lower_scope_by_type`
|
25
|
+
#
|
26
|
+
# @return [ActiveRecord::Relation]
|
10
27
|
def spawn_lower_scope(scope, fields)
|
11
28
|
fields.inject(scope) do |new_scope, (name, value)|
|
12
29
|
spawn_lower_scope_by_type(new_scope, name, value)
|
13
30
|
end
|
14
31
|
end
|
15
32
|
|
16
|
-
#
|
17
|
-
#
|
18
|
-
# @
|
19
|
-
#
|
33
|
+
# This is method is for internal use
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
#
|
37
|
+
# @params scope [ActiveRecord::Relation, ActiveRecord::Base]
|
38
|
+
# @see spawn_lower_scope
|
39
|
+
# @params column_or_table_name [String, Symbol]
|
40
|
+
# When used with nested hash, this is table name
|
41
|
+
# @params value [Object]
|
20
42
|
#
|
21
43
|
# @raise [TooDeepNestedConditions] if the conditions hash has is too deep nested
|
22
44
|
#
|
@@ -1,107 +1,143 @@
|
|
1
1
|
module WhereLower
|
2
|
+
# :nodoc:
|
2
3
|
class ScopeSpawner
|
4
|
+
# :nodoc:
|
3
5
|
attr_reader :scope, :column_or_table_name, :value, :prefix
|
4
6
|
|
7
|
+
# Spawn different scopes based on value
|
8
|
+
# Just delegates to new though
|
9
|
+
#
|
10
|
+
# @param args [Array] arguments that are passed to #initialize
|
11
|
+
#
|
12
|
+
# @see #initialize
|
5
13
|
def self.spawn(*args)
|
6
14
|
new(*args).spawn
|
7
15
|
end
|
8
16
|
|
17
|
+
# Assign ivar only
|
18
|
+
# Actual operation is in #spawn
|
19
|
+
#
|
20
|
+
# @param scope [ActiveRecord::Relation]
|
21
|
+
# Relation or collection proxy or some AR classes
|
22
|
+
# @param column_or_table_name [Symbol, String]
|
23
|
+
# when column name, the actual column name (not attribute name)
|
24
|
+
# when table name, the actual table name (not class name converted to underscore)
|
25
|
+
# @param value [Object]
|
26
|
+
# value to compare to, also determine what scope spawner to be used
|
27
|
+
# @param prefix [Symbol, String]
|
28
|
+
# used internally for specifying a scope with prefix (table name)
|
29
|
+
#
|
30
|
+
# @see #spawn
|
9
31
|
def initialize(scope, column_or_table_name, value, prefix = nil)
|
10
|
-
@scope
|
32
|
+
@scope = scope
|
33
|
+
@column_or_table_name = column_or_table_name
|
34
|
+
@value = value
|
35
|
+
@prefix = prefix
|
11
36
|
end
|
12
37
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
# acts as factory
|
38
|
+
# Spawn different scopes based on value
|
39
|
+
# Data conversion and query string generation are handled by different spanwer classes
|
40
|
+
#
|
41
|
+
# @return [ActiveRecord::Relation] Relation or collection proxy or some AR classs
|
18
42
|
def spawn
|
19
|
-
|
20
|
-
|
21
|
-
HashScopeSpawner.spawn(*scope_arguments)
|
22
|
-
when String
|
23
|
-
StringScopeSpawner.spawn(*scope_arguments)
|
24
|
-
when Range
|
25
|
-
RangeScopeSpawner.spawn(*scope_arguments)
|
26
|
-
when Array
|
27
|
-
ArrayScopeSpawner.spawn(*scope_arguments)
|
28
|
-
else
|
29
|
-
BasicScopeSpawner.spawn(*scope_arguments)
|
30
|
-
end
|
43
|
+
CLASS_TO_SPAWNER_CLASS_MAPPINGS[value.class].
|
44
|
+
spawn(*scope_arguments)
|
31
45
|
end
|
32
46
|
|
33
|
-
|
34
47
|
private
|
35
48
|
|
49
|
+
# :nodoc:
|
50
|
+
def scope_arguments
|
51
|
+
[scope, column_or_table_name, value, prefix]
|
52
|
+
end
|
53
|
+
|
54
|
+
# :nodoc:
|
36
55
|
class BasicScopeSpawner < ScopeSpawner
|
56
|
+
# :nodoc:
|
37
57
|
def spawn
|
38
58
|
scope.where(column_name => value)
|
39
59
|
end
|
40
60
|
|
41
61
|
private
|
42
62
|
|
63
|
+
# :nodoc:
|
43
64
|
def column_name
|
44
65
|
[prefix, column_or_table_name].compact.join(SEPERATOR)
|
45
66
|
end
|
46
67
|
end
|
47
68
|
|
69
|
+
# This class is only for inheritance
|
70
|
+
#
|
48
71
|
# @abstract
|
72
|
+
# Subclass has to implement #query_string & #processed_value
|
49
73
|
class EqualScopeSpawner < BasicScopeSpawner
|
74
|
+
# We generate query_string and pass values to it
|
75
|
+
# To avoid some scope chaining problems
|
50
76
|
def spawn
|
51
77
|
scope.where(query_string, processed_value)
|
52
78
|
end
|
53
|
-
|
54
|
-
private
|
55
|
-
|
56
|
-
def query_string
|
57
|
-
"#{column_name} = ?"
|
58
|
-
end
|
59
|
-
|
60
|
-
def processed_value
|
61
|
-
value
|
62
|
-
end
|
63
79
|
end
|
64
80
|
|
81
|
+
# :nodoc:
|
65
82
|
class StringScopeSpawner < EqualScopeSpawner
|
83
|
+
# :nodoc:
|
66
84
|
def query_string
|
67
85
|
"lower(#{column_name}) = ?"
|
68
86
|
end
|
69
87
|
|
88
|
+
# :nodoc:
|
70
89
|
def processed_value
|
71
90
|
value.downcase
|
72
91
|
end
|
73
92
|
end
|
74
93
|
|
94
|
+
# :nodoc:
|
75
95
|
class RangeScopeSpawner < EqualScopeSpawner
|
96
|
+
# :nodoc:
|
76
97
|
def query_string
|
77
98
|
"lower(#{column_name}) IN (?)"
|
78
99
|
end
|
79
100
|
|
101
|
+
# :nodoc:
|
80
102
|
def processed_value
|
81
103
|
Range.new(value.begin.to_s.downcase, value.end.to_s.downcase, value.exclude_end?)
|
82
104
|
end
|
83
105
|
end
|
84
106
|
|
107
|
+
# :nodoc:
|
85
108
|
class ArrayScopeSpawner < EqualScopeSpawner
|
109
|
+
# :nodoc:
|
86
110
|
def query_string
|
87
111
|
"lower(#{column_name}) IN (?)"
|
88
112
|
end
|
89
113
|
|
114
|
+
# :nodoc:
|
90
115
|
def processed_value
|
91
|
-
value.to_a.map {|x| x.to_s.downcase}
|
116
|
+
value.to_a.map { |x| x.to_s.downcase }
|
92
117
|
end
|
93
118
|
end
|
94
119
|
|
120
|
+
# :nodoc:
|
95
121
|
class HashScopeSpawner < BasicScopeSpawner
|
122
|
+
# If prefix already exists,
|
123
|
+
# that means we are in association table already,
|
124
|
+
# which cannot accept another hash
|
125
|
+
# This gem has no ability to handle deep nested associaiton reflection yet
|
96
126
|
def spawn
|
97
|
-
|
98
|
-
# This gem has no ability to handle deep nested associaiton reflection yet
|
99
|
-
raise TooDeepNestedConditions unless prefix.nil?
|
127
|
+
fail TooDeepNestedConditions unless prefix.nil?
|
100
128
|
|
101
129
|
value.inject(scope) do |new_scope, (column_name, column_value)|
|
102
130
|
ScopeSpawner.spawn(new_scope, column_name, column_value, column_or_table_name)
|
103
131
|
end
|
104
132
|
end
|
105
133
|
end
|
134
|
+
|
135
|
+
# This was extracted from `case..when`
|
136
|
+
CLASS_TO_SPAWNER_CLASS_MAPPINGS = {
|
137
|
+
Hash => HashScopeSpawner,
|
138
|
+
String => StringScopeSpawner,
|
139
|
+
Range => RangeScopeSpawner,
|
140
|
+
Array => ArrayScopeSpawner,
|
141
|
+
}.tap { |h| h.default = BasicScopeSpawner }.freeze
|
106
142
|
end
|
107
143
|
end
|