case_insensitive_arel 0.0.0 → 0.1.0

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/README.rdoc CHANGED
@@ -1,8 +1,86 @@
1
1
  = case_insensitive_arel
2
2
 
3
- Description goes here.
3
+ If you're using Oracle or another DBMS that uses case-sensitive collation sequences, and don't want to litter your
4
+ code with snippets of proprietary case-conversion SQL, this may be the solution you're looking for.
4
5
 
5
- == Contributing to case_insensitive_arel
6
+ == How It Works
7
+
8
+ This gem is pretty simple in its operation. Working at the Arel level, it takes +String+, +Arel_Attributes_Attribute+,
9
+ and +Arel_Attributes_String+ values, and executes a proc that is responsible for converting them into
10
+ a common format for comparison e.g. uppercase or lowercase. This proc can be overridden to do whatever you want. For
11
+ example, you might prefer to convert the values to lowercase, or use another conversion method. No problem! You can
12
+ also enable and disable the case-insensitive comparison behaviour by setting a flag, see below for details.
13
+
14
+ It's important to note that any custom SQL you create will _not_ be modified by this gem. Everything that is generated
15
+ by Arel however should be handled as you'd expect. If you find something that doesn't work the way you think it should,
16
+ please let me know.
17
+
18
+ == Examples
19
+
20
+ Here are some examples of what +case_insensitive_arel+ will do:
21
+
22
+ users.where(users[:name].eq('Steve')).to_sql
23
+
24
+ => SELECT FROM "USERS" WHERE UPPER("USERS"."NAME") = UPPER('Steve')
25
+
26
+ users.where(users[:name].matches('Steve%')).to_sql
27
+
28
+ => SELECT FROM "USERS" WHERE UPPER("USERS"."NAME") LIKE UPPER('Steve%')
29
+
30
+ users.where(users[:name].in(%w(Steve Barb))).to_sql
31
+
32
+ => SELECT FROM "USERS" WHERE UPPER("USERS"."NAME") IN (UPPER('Steve'), UPPER('Barb')')
33
+
34
+ users.group(users[:name]).to_sql
35
+
36
+ => SELECT FROM "USERS" GROUP BY UPPER("USERS"."NAME")
37
+
38
+ users.join(photos).on(users[:name].eq(photos[:name])).to_sql
39
+
40
+ => SELECT FROM "USERS" INNER JOIN "PHOTOS" ON UPPER("USERS"."NAME") = UPPER("PHOTOS"."USER_NAME")
41
+
42
+ users.project(users[:name]).where(users[:name].eq('Steve'))
43
+
44
+ => SELECT "USERS"."NAME" FROM "USERS" WHERE UPPER("USERS"."NAME") = UPPER('Steve')
45
+
46
+ == Customization
47
+
48
+ The following default settings can be overridden in your code as required:
49
+
50
+ Arel::CaseInsensitive.case_insensitive = false
51
+ Arel::CaseInsensitive.conversion_proc = Proc.new { |val| "UPPER(#{val})" }
52
+
53
+ These settings, which were used for the above examples, work for Oracle. You can customize the +convert_attribute+
54
+ setting to specify an alternative conversion function for your DBMS in your application. The +convert_string+ setting is
55
+ used to convert a string literal into a form that can be compared in a case-insensitive fasion to the result of what's
56
+ returned by +convert_attribute+ as the query is processed by the DBMS. Finally, the +case_insensitive+ setting can be
57
+ used to disable case-insensitive behaviour if need be.
58
+
59
+ As an example, you could add this code to your app if you wanted to use lowercase for conversions:
60
+
61
+ Arel::CaseInsensitive.conversion_proc = Proc.new { |val| "LOWER(#{val})" }
62
+
63
+ == Known Issues
64
+
65
+ === Performance/Query Optimization
66
+
67
+ Due to the wrapping of column names with function calls, certain DBMS' optimizers may ignore indexes on those columns
68
+ that might otherwise be used for a query. If supported by your DBMS, you may be able to create a special index on
69
+ these columns to help out wih your query. As an example, Oracle supports _function-based indexes_.
70
+
71
+ For more information, please refer to your DBMS's documentation.
72
+
73
+ === Single Connection Only
74
+
75
+ Because this implementation applies across all adapters, there may be issues with applications that use multiple
76
+ database adapters.
77
+
78
+ === Custom SQL
79
+
80
+ Rather than risk madness, I didn't even bother trying to make this work with custom SQL. Stick with Arel and there
81
+ won't be any issues.
82
+
83
+ == Contributing
6
84
 
7
85
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
8
86
  * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
data/Rakefile CHANGED
@@ -15,16 +15,14 @@ Jeweler::Tasks.new do |gem|
15
15
  gem.name = "case_insensitive_arel"
16
16
  gem.homepage = "http://github.com/slamotte/case_insensitive_arel"
17
17
  gem.license = "MIT"
18
- gem.summary = %Q{Allows Arel queries to be case-insensitive}
19
- gem.description = <<EOF
20
- If you're using Oracle or another DBMS that has case-insensitive collation sequences, this gem is for you.
21
- EOF
18
+ gem.summary = %Q{Forces Arel queries to be case-insensitive}
19
+ gem.description = %Q{If you're using Oracle or another DBMS that has case-insensitive collation sequences, and you don't want to litter your database access code with case conversions, this gem is for you.}
22
20
  gem.email = "steve@lexor.ca"
23
21
  gem.authors = ["Steve Lamotte"]
24
22
  # Include your dependencies below. Runtime dependencies are required when using your gem,
25
23
  # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
26
24
  gem.add_runtime_dependency 'arel', '>= 2.0.9'
27
- gem.add_runtime_dependency 'activesupport'
25
+ gem.add_runtime_dependency 'activesupport', '>= 2.0.0'
28
26
  end
29
27
  Jeweler::RubygemsDotOrgTasks.new
30
28
 
@@ -52,4 +50,5 @@ Rake::RDocTask.new do |rdoc|
52
50
  rdoc.title = "case_insensitive_arel #{version}"
53
51
  rdoc.rdoc_files.include('README*')
54
52
  rdoc.rdoc_files.include('lib/**/*.rb')
53
+ rdoc.options += %w(--line-numbers --inline-source --accessor cattr_accessor)
55
54
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.0
1
+ 0.1.0
@@ -5,13 +5,12 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{case_insensitive_arel}
8
- s.version = "0.0.0"
8
+ s.version = "0.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Steve Lamotte"]
12
- s.date = %q{2011-03-18}
13
- s.description = %q{If you're using Oracle or another DBMS that has case-insensitive collation sequences, this gem is for you.
14
- }
12
+ s.date = %q{2011-03-19}
13
+ s.description = %q{If you're using Oracle or another DBMS that has case-insensitive collation sequences, and you don't want to litter your database access code with case conversions, this gem is for you.}
15
14
  s.email = %q{steve@lexor.ca}
16
15
  s.extra_rdoc_files = [
17
16
  "LICENSE.txt",
@@ -38,7 +37,7 @@ Gem::Specification.new do |s|
38
37
  s.licenses = ["MIT"]
39
38
  s.require_paths = ["lib"]
40
39
  s.rubygems_version = %q{1.3.7}
41
- s.summary = %q{Allows Arel queries to be case-insensitive}
40
+ s.summary = %q{Forces Arel queries to be case-insensitive}
42
41
  s.test_files = [
43
42
  "test/helper.rb",
44
43
  "test/support/fake_record.rb",
@@ -60,7 +59,7 @@ Gem::Specification.new do |s|
60
59
  s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
61
60
  s.add_development_dependency(%q<rcov>, [">= 0"])
62
61
  s.add_runtime_dependency(%q<arel>, [">= 2.0.9"])
63
- s.add_runtime_dependency(%q<activesupport>, [">= 0"])
62
+ s.add_runtime_dependency(%q<activesupport>, [">= 2.0.0"])
64
63
  else
65
64
  s.add_dependency(%q<arel>, [">= 2.0.9"])
66
65
  s.add_dependency(%q<activesupport>, [">= 3.0.0"])
@@ -69,7 +68,7 @@ Gem::Specification.new do |s|
69
68
  s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
70
69
  s.add_dependency(%q<rcov>, [">= 0"])
71
70
  s.add_dependency(%q<arel>, [">= 2.0.9"])
72
- s.add_dependency(%q<activesupport>, [">= 0"])
71
+ s.add_dependency(%q<activesupport>, [">= 2.0.0"])
73
72
  end
74
73
  else
75
74
  s.add_dependency(%q<arel>, [">= 2.0.9"])
@@ -79,7 +78,7 @@ Gem::Specification.new do |s|
79
78
  s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
80
79
  s.add_dependency(%q<rcov>, [">= 0"])
81
80
  s.add_dependency(%q<arel>, [">= 2.0.9"])
82
- s.add_dependency(%q<activesupport>, [">= 0"])
81
+ s.add_dependency(%q<activesupport>, [">= 2.0.0"])
83
82
  end
84
83
  end
85
84
 
@@ -1,51 +1,57 @@
1
1
  require 'active_support/core_ext/class/attribute_accessors'
2
2
  require 'active_support/core_ext/module/aliasing'
3
3
 
4
- module Arel
5
- module Visitors
4
+ module Arel #:nodoc:
5
+ module Visitors #:nodoc: all
6
6
  class ToSql
7
- # List of Arel types that should be converted to upper or lower case
7
+ # List of Arel types that should be converted to make them comparable in a case-insensitive fashion
8
8
  %w(Arel_Attributes_Attribute Arel_Attributes_String String).each do |arel_type_name|
9
- convert_method = case arel_type_name
10
- when /^Arel_Attributes_.+/
11
- :convert_attribute
12
- when 'String'
13
- :convert_string
14
- else
15
- raise "Unexpected Arel type name: #{arel_type_name}"
16
- end
17
9
  define_method "visit_#{arel_type_name}_with_case_insensitive" do |o|
18
10
  value = send("visit_#{arel_type_name}_without_case_insensitive", o)
19
- o.respond_to?(:do_not_make_case_insensitive?) ? value : Arel::CaseInsensitive.convert_value(value, convert_method)
11
+
12
+ # If the object has been tagged with
13
+ o.respond_to?(:do_not_make_case_insensitive?) ? value : Arel::CaseInsensitive.convert_value(value)
20
14
  end
21
15
  alias_method_chain "visit_#{arel_type_name}", :case_insensitive
22
16
  end
23
17
  end
24
18
  end
25
19
 
20
+ # Controls case-insensitive comparisons. The following attributes can be set to customize behaviour.
26
21
  class CaseInsensitive
27
- cattr_accessor :case_insensitive, :convert_attribute, :convert_string
22
+ # Boolean that determines whether case-insensitive processing is enabled or not. Defaults to:
23
+ # Arel::CaseInsensitive.case_insensitive = true
24
+ cattr_accessor :case_insensitive
25
+
26
+ # Proc that accepts an Arel object and converts it into something that can be compared in a case-insensitive manner. Defaults to:
27
+ # Arel::CaseInsensitive.conversion_proc = Proc.new { |val| "UPPER(#{val})" }
28
+ cattr_accessor :conversion_proc
28
29
 
29
- def self.convert_value(val, method)
30
- case_insensitive ? send(method).call(val) : val
30
+ private
31
+
32
+ # Return the converted value or the value itself depending on the current state of +case_insensitive+
33
+ def self.convert_value(val)
34
+ case_insensitive ? conversion_proc.call(val) : val
31
35
  end
32
36
  end
33
37
 
34
- class Table
38
+ class Table # :nodoc:
39
+ # We don't want an attribute in the SELECT to be processed by the conversion proc. As such, tag +project+'s' parameters with a special
40
+ # singleton method to prevent them from being converted.
35
41
  def project_with_case_insensitive(*things)
36
- things = things.map do |thing|
37
- new_thing = thing.clone # Need to clone these because we might want to use an attribute in the SELECT and WHERE clauses, but the former shouldn't be altered
38
- def new_thing.do_not_make_case_insensitive?; true; end
42
+ # If a +thing+ is used elsewhere (e.g. in the WHERE clause), tagging it will cause the WHERE clause to be affected as well. So create a new
43
+ # list of things and tag them instead.
44
+ new_things = things.map do |thing|
45
+ new_thing = thing.clone
46
+ def new_thing.do_not_make_case_insensitive?; end
39
47
  new_thing
40
48
  end
41
- project_without_case_insensitive *things
49
+ project_without_case_insensitive *new_things
42
50
  end
43
51
  alias_method_chain :project, :case_insensitive
44
52
  end
45
53
  end
46
54
 
47
- # Set the default values (for Oracle)
55
+ # Set the default values (these work for Oracle)
48
56
  Arel::CaseInsensitive.case_insensitive = true
49
- Arel::CaseInsensitive.convert_attribute = Proc.new { |val| "UPPER(#{val})" }
50
- Arel::CaseInsensitive.convert_string = Proc.new{ |val| val.upcase }
51
-
57
+ Arel::CaseInsensitive.conversion_proc = Proc.new { |val| "UPPER(#{val})" }
@@ -3,15 +3,15 @@ require 'helper'
3
3
  class TestComparisons < Test::Unit::TestCase
4
4
  should "work correctly in case-insensitive mode" do
5
5
  Arel::CaseInsensitive.case_insensitive = true
6
- should_be_like @users.where(@users[:name].eq('Steve')).to_sql, "SELECT FROM \"users\" WHERE UPPER(\"users\".\"name\") = 'STEVE'"
7
- should_be_like @users.where(@users[:name].not_eq('Steve')).to_sql, "SELECT FROM \"users\" WHERE UPPER(\"users\".\"name\") != 'STEVE'"
8
- should_be_like @users.where(@users[:name].matches('Ste%')).to_sql, "SELECT FROM \"users\" WHERE UPPER(\"users\".\"name\") LIKE 'STE%'"
9
- should_be_like @users.where(@users[:name].does_not_match('Ste%')).to_sql, "SELECT FROM \"users\" WHERE UPPER(\"users\".\"name\") NOT LIKE 'STE%'"
6
+ should_be_like @users.where(@users[:name].eq('Steve')).to_sql, "SELECT FROM \"users\" WHERE UPPER(\"users\".\"name\") = UPPER('Steve')"
7
+ should_be_like @users.where(@users[:name].not_eq('Steve')).to_sql, "SELECT FROM \"users\" WHERE UPPER(\"users\".\"name\") != UPPER('Steve')"
8
+ should_be_like @users.where(@users[:name].matches('Ste%')).to_sql, "SELECT FROM \"users\" WHERE UPPER(\"users\".\"name\") LIKE UPPER('Ste%')"
9
+ should_be_like @users.where(@users[:name].does_not_match('Ste%')).to_sql, "SELECT FROM \"users\" WHERE UPPER(\"users\".\"name\") NOT LIKE UPPER('Ste%')"
10
10
  should_be_like @users.where(@users[:name].eq(@users[:name])).to_sql, "SELECT FROM \"users\" WHERE UPPER(\"users\".\"name\") = UPPER(\"users\".\"name\")"
11
- should_be_like @users.where(@users[:name].gteq('Steve')).to_sql, "SELECT FROM \"users\" WHERE UPPER(\"users\".\"name\") >= 'STEVE'"
12
- should_be_like @users.where(@users[:name].in(['Steve', 'Barb'])).to_sql, "SELECT FROM \"users\" WHERE UPPER(\"users\".\"name\") IN ('STEVE', 'BARB')"
13
- should_be_like @users.where(@users[:name].not_in(['Steve', 'Barb'])).to_sql, "SELECT FROM \"users\" WHERE UPPER(\"users\".\"name\") NOT IN ('STEVE', 'BARB')"
14
- should_be_like @users.where(@users[:name].matches_any(['Steve', 'Barb'])).to_sql, "SELECT FROM \"users\" WHERE (UPPER(\"users\".\"name\") LIKE 'STEVE' OR UPPER(\"users\".\"name\") LIKE 'BARB')"
11
+ should_be_like @users.where(@users[:name].gteq('Steve')).to_sql, "SELECT FROM \"users\" WHERE UPPER(\"users\".\"name\") >= UPPER('Steve')"
12
+ should_be_like @users.where(@users[:name].in(%w(Steve Barb))).to_sql, "SELECT FROM \"users\" WHERE UPPER(\"users\".\"name\") IN (UPPER('Steve'), UPPER('Barb'))"
13
+ should_be_like @users.where(@users[:name].not_in(%w(Steve Barb))).to_sql, "SELECT FROM \"users\" WHERE UPPER(\"users\".\"name\") NOT IN (UPPER('Steve'), UPPER('Barb'))"
14
+ should_be_like @users.where(@users[:name].matches_any(%w(Steve Barb))).to_sql, "SELECT FROM \"users\" WHERE (UPPER(\"users\".\"name\") LIKE UPPER('Steve') OR UPPER(\"users\".\"name\") LIKE UPPER('Barb'))"
15
15
  end
16
16
 
17
17
  should "work correctly in case-sensitive mode" do
@@ -22,8 +22,8 @@ class TestComparisons < Test::Unit::TestCase
22
22
  should_be_like @users.where(@users[:name].does_not_match('Ste%')).to_sql, "SELECT FROM \"users\" WHERE \"users\".\"name\" NOT LIKE 'Ste%'"
23
23
  should_be_like @users.where(@users[:name].eq(@users[:name])).to_sql, "SELECT FROM \"users\" WHERE \"users\".\"name\" = \"users\".\"name\""
24
24
  should_be_like @users.where(@users[:name].gteq('Steve')).to_sql, "SELECT FROM \"users\" WHERE \"users\".\"name\" >= 'Steve'"
25
- should_be_like @users.where(@users[:name].in(['Steve', 'Barb'])).to_sql, "SELECT FROM \"users\" WHERE \"users\".\"name\" IN ('Steve', 'Barb')"
26
- should_be_like @users.where(@users[:name].not_in(['Steve', 'Barb'])).to_sql, "SELECT FROM \"users\" WHERE \"users\".\"name\" NOT IN ('Steve', 'Barb')"
27
- should_be_like @users.where(@users[:name].matches_any(['Steve', 'Barb'])).to_sql, "SELECT FROM \"users\" WHERE (\"users\".\"name\" LIKE 'Steve' OR \"users\".\"name\" LIKE 'Barb')"
25
+ should_be_like @users.where(@users[:name].in(%w(Steve Barb))).to_sql, "SELECT FROM \"users\" WHERE \"users\".\"name\" IN ('Steve', 'Barb')"
26
+ should_be_like @users.where(@users[:name].not_in(%w(Steve Barb))).to_sql, "SELECT FROM \"users\" WHERE \"users\".\"name\" NOT IN ('Steve', 'Barb')"
27
+ should_be_like @users.where(@users[:name].matches_any(%w(Steve Barb))).to_sql, "SELECT FROM \"users\" WHERE (\"users\".\"name\" LIKE 'Steve' OR \"users\".\"name\" LIKE 'Barb')"
28
28
  end
29
29
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: case_insensitive_arel
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
+ - 1
8
9
  - 0
9
- - 0
10
- version: 0.0.0
10
+ version: 0.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Steve Lamotte
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-03-18 00:00:00 -05:00
18
+ date: 2011-03-19 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -134,15 +134,15 @@ dependencies:
134
134
  requirements:
135
135
  - - ">="
136
136
  - !ruby/object:Gem::Version
137
- hash: 3
137
+ hash: 15
138
138
  segments:
139
+ - 2
139
140
  - 0
140
- version: "0"
141
+ - 0
142
+ version: 2.0.0
141
143
  requirement: *id008
142
144
  type: :runtime
143
- description: |
144
- If you're using Oracle or another DBMS that has case-insensitive collation sequences, this gem is for you.
145
-
145
+ description: If you're using Oracle or another DBMS that has case-insensitive collation sequences, and you don't want to litter your database access code with case conversions, this gem is for you.
146
146
  email: steve@lexor.ca
147
147
  executables: []
148
148
 
@@ -200,7 +200,7 @@ rubyforge_project:
200
200
  rubygems_version: 1.3.7
201
201
  signing_key:
202
202
  specification_version: 3
203
- summary: Allows Arel queries to be case-insensitive
203
+ summary: Forces Arel queries to be case-insensitive
204
204
  test_files:
205
205
  - test/helper.rb
206
206
  - test/support/fake_record.rb