case_insensitive_arel 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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