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.
data/.travis.yml CHANGED
@@ -1,15 +1,26 @@
1
- before_install: "gem install bundler -v=1.5.2"
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.9.2
4
- - 1.9.3
5
- - 2.0.0
6
- - 2.1.0
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/rails3_1.gemfile
9
- - gemfiles/rails3_2.gemfile
10
- - gemfiles/rails4_0.gemfile
15
+ - gemfiles/rails_4_0.gemfile
16
+ - gemfiles/rails_4_1.gemfile
17
+ - gemfiles/rails_4_2.gemfile
11
18
  matrix:
12
- exclude:
13
- - rvm: 1.9.2
14
- gemfile: gemfiles/rails4_0.gemfile
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 "rails3-1" do
3
- version = "3.1.12"
4
- gem 'activerecord', version
2
+ appraise "rails_4_0" do
3
+ version = "~> 4.0.12"
4
+ gem "activerecord", version
5
5
  end
6
6
 
7
- appraise "rails3-2" do
8
- version = "3.2.16"
9
- gem 'activerecord', version
7
+ appraise "rails_4_1" do
8
+ version = "~> 4.1.8"
9
+ gem "activerecord", version
10
10
  end
11
11
 
12
- appraise "rails4-0" do
13
- version = "4.0.2"
14
- gem 'activerecord', version
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
- ### Changelog
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
- - **0.3.0**
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
- - **0.2.0**
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
- - **0.1.0**
14
- - Initail Release
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
@@ -1,3 +1,3 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
3
  gemspec
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
- [![Build Status](https://travis-ci.org/PikachuEXE/where_lower.png?branch=master)](https://travis-ci.org/PikachuEXE/where_lower)
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
- Install
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
- Usage
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
- Contributors
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 "rake appraisal:install && rake appraisal spec"
14
+ sh "appraisal install && rake appraisal spec"
12
15
  end
13
16
  else
14
- task :default => :spec
17
+ task default: [:spec]
15
18
  end
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.0.12"
6
+
7
+ gemspec :path => "../"
@@ -2,6 +2,6 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "activerecord", "3.1.12"
5
+ gem "activerecord", "~> 4.1.8"
6
6
 
7
- gemspec :path=>"../"
7
+ gemspec :path => "../"
@@ -2,6 +2,6 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "activerecord", "3.2.16"
5
+ gem "activerecord", "~> 4.2.0"
6
6
 
7
- gemspec :path=>"../"
7
+ gemspec :path => "../"
@@ -2,6 +2,6 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "activerecord", "4.0.2"
5
+ gem "activerecord", "~> 5.0.0"
6
6
 
7
- gemspec :path=>"../"
7
+ gemspec :path => "../"
data/lib/where_lower.rb CHANGED
@@ -1,5 +1,5 @@
1
- require 'active_record'
2
- require 'where_lower/version'
3
- require 'where_lower/base'
4
- require 'where_lower/scope_spawner'
5
- require 'where_lower/active_record_extension'
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
- fields.is_a?(Hash) or raise AugumentError, 'fields is not a Hash'
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
@@ -1,22 +1,44 @@
1
+ require "where_lower/scope_spawner"
2
+
1
3
  module WhereLower
2
- SEPERATOR = '.'.freeze
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
- # @params [ActiveRecord::Relation, ActiveRecord::Base] scope
9
- # @params [Hash] fields
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
- # @params [ActiveRecord::Relation, ActiveRecord::Base] scope
17
- # @params [String, Symbol] column_or_table_name When used with nested hash, this is table name
18
- # @params [Object] value
19
- # @params [String, Symbol] prefix used when doing recursive call
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, @column_or_table_name, @value, @prefix = scope, column_or_table_name, value, prefix
32
+ @scope = scope
33
+ @column_or_table_name = column_or_table_name
34
+ @value = value
35
+ @prefix = prefix
11
36
  end
12
37
 
13
- def scope_arguments
14
- [scope, column_or_table_name, value, prefix]
15
- end
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
- case value
20
- when Hash
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
- # If prefix already exists, that means we are in association table already, which cannot accept another hash
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