where_lower 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Gem Version](https://badge.fury.io/rb/where_lower.png)](http://badge.fury.io/rb/where_lower)
|
14
|
-
[![Dependency Status](https://gemnasium.com/PikachuEXE/where_lower.png)](https://gemnasium.com/PikachuEXE/where_lower)
|
15
|
-
[![Coverage Status](https://coveralls.io/repos/PikachuEXE/where_lower/badge.png)](https://coveralls.io/r/PikachuEXE/where_lower)
|
16
|
-
[![Code Climate](https://codeclimate.com/github/PikachuEXE/where_lower.png)](https://codeclimate.com/github/PikachuEXE/where_lower)
|
6
|
+
## Status
|
17
7
|
|
18
|
-
|
19
|
-
|
8
|
+
[![Build Status](http://img.shields.io/travis/PikachuEXE/where_lower.svg?style=flat-square)](https://travis-ci.org/PikachuEXE/where_lower)
|
9
|
+
[![Gem Version](http://img.shields.io/gem/v/where_lower.svg?style=flat-square)](http://badge.fury.io/rb/where_lower)
|
10
|
+
[![Dependency Status](http://img.shields.io/gemnasium/PikachuEXE/where_lower.svg?style=flat-square)](https://gemnasium.com/PikachuEXE/where_lower)
|
11
|
+
[![Coverage Status](http://img.shields.io/coveralls/PikachuEXE/where_lower.svg?style=flat-square)](https://coveralls.io/r/PikachuEXE/where_lower)
|
12
|
+
[![Code Climate](http://img.shields.io/codeclimate/github/PikachuEXE/where_lower.svg?style=flat-square)](https://codeclimate.com/github/PikachuEXE/where_lower)
|
13
|
+
[![Inch CI](https://inch-ci.org/github/PikachuEXE/where_lower.svg?branch=master)](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
|