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