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 +80 -2
- data/Rakefile +4 -5
- data/VERSION +1 -1
- data/case_insensitive_arel.gemspec +7 -8
- data/lib/case_insensitive_arel.rb +30 -24
- data/test/test_comparisons.rb +11 -11
- metadata +10 -10
data/README.rdoc
CHANGED
@@ -1,8 +1,86 @@
|
|
1
1
|
= case_insensitive_arel
|
2
2
|
|
3
|
-
|
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
|
-
==
|
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{
|
19
|
-
gem.description =
|
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.
|
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.
|
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-
|
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{
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
30
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
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 *
|
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.
|
50
|
-
Arel::CaseInsensitive.convert_string = Proc.new{ |val| val.upcase }
|
51
|
-
|
57
|
+
Arel::CaseInsensitive.conversion_proc = Proc.new { |val| "UPPER(#{val})" }
|
data/test/test_comparisons.rb
CHANGED
@@ -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\") = '
|
7
|
-
should_be_like @users.where(@users[:name].not_eq('Steve')).to_sql, "SELECT FROM \"users\" WHERE UPPER(\"users\".\"name\") != '
|
8
|
-
should_be_like @users.where(@users[:name].matches('Ste%')).to_sql, "SELECT FROM \"users\" WHERE UPPER(\"users\".\"name\") LIKE '
|
9
|
-
should_be_like @users.where(@users[:name].does_not_match('Ste%')).to_sql, "SELECT FROM \"users\" WHERE UPPER(\"users\".\"name\") NOT LIKE '
|
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\") >= '
|
12
|
-
should_be_like @users.where(@users[:name].in(
|
13
|
-
should_be_like @users.where(@users[:name].not_in(
|
14
|
-
should_be_like @users.where(@users[:name].matches_any(
|
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(
|
26
|
-
should_be_like @users.where(@users[:name].not_in(
|
27
|
-
should_be_like @users.where(@users[:name].matches_any(
|
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:
|
4
|
+
hash: 27
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
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
|
+
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:
|
137
|
+
hash: 15
|
138
138
|
segments:
|
139
|
+
- 2
|
139
140
|
- 0
|
140
|
-
|
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:
|
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
|