Skip to content

Commit

Permalink
Merge pull request #498 from leo-souza/master
Browse files Browse the repository at this point in the history
Use database's lower function for case-insensitive match
  • Loading branch information
seuros committed Mar 21, 2014
2 parents 68a17c8 + 501d274 commit 1db9880
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 12 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ As such, a _Feature_ would map to either major or minor. A _bug fix_ to a patch.
* [@rgould #417 Let '.count' work when tagged_with is accompanied by a group clause](https://github.com/mbleigh/acts-as-taggable-on/pull/417)
* [@developer88 #461 Move 'Distinct' out of select string and use .uniq instead](https://github.com/mbleigh/acts-as-taggable-on/pull/461)
* [@gerard-leijdekkers #473 Fixed down migration index name](https://github.com/mbleigh/acts-as-taggable-on/pull/473)
* [@leo-souza #492 Fix downcasing of unicode tag names](https://github.com/mbleigh/acts-as-taggable-on/pull/492)
* [@leo-souza #498 Use database's lower function for case-insensitive match](https://github.com/mbleigh/acts-as-taggable-on/pull/498)
* Misc
* [@billychan #463 Thread safe support](https://github.com/mbleigh/acts-as-taggable-on/pull/463)
* [@billychan #386 Add parse:true instructions to README](https://github.com/mbleigh/acts-as-taggable-on/pull/386)
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,8 @@ If you want to change the default delimiter (it defaults to ','). You can also p
ActsAsTaggableOn.delimiter = ','
```

*NOTE: SQLite by default can't upcase or downcase multibyte characters, resulting in unwanted behavior. Load the SQLite ICU extension for proper handle of such characters. [See docs](http://www.sqlite.org/src/artifact?ci=trunk&filename=ext/icu/README.txt)*

## Contributors

We have a long list of valued contributors. [Check them all](https://github.com/mbleigh/acts-as-taggable-on/contributors)
Expand Down
27 changes: 16 additions & 11 deletions lib/acts_as_taggable_on/tag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ def validates_name_uniqueness?

def self.named(name)
if ActsAsTaggableOn.strict_case_match
where(["name = #{binary}?", name])
where(["name = #{binary}?", as_8bit_ascii(name)])
else
where(["lower(name) = ?", name.downcase])
where(["LOWER(name) = LOWER(?)", as_8bit_ascii(unicode_downcase(name))])
end
end

Expand All @@ -38,8 +38,7 @@ def self.named_any(list)
where(clause)
else
clause = list.map { |tag|
lowercase_ascii_tag = as_8bit_ascii(tag, true)
sanitize_sql(["lower(name) = ?", lowercase_ascii_tag])
sanitize_sql(["LOWER(name) = LOWER(?)", as_8bit_ascii(unicode_downcase(tag))])
}.join(" OR ")
where(clause)
end
Expand Down Expand Up @@ -101,23 +100,29 @@ class << self

def comparable_name(str)
if ActsAsTaggableOn.strict_case_match
as_8bit_ascii(str)
str
else
as_8bit_ascii(str, true)
unicode_downcase(str.to_s)
end
end

def binary
using_mysql? ? "BINARY " : nil
end

def as_8bit_ascii(string, downcase=false)
string = string.to_s.dup.mb_chars
string.downcase! if downcase
def unicode_downcase(string)
if ActiveSupport::Multibyte::Unicode.respond_to?(:downcase)
ActiveSupport::Multibyte::Unicode.downcase(string)
else
ActiveSupport::Multibyte::Chars.new(string).downcase.to_s
end
end

def as_8bit_ascii(string)
if defined?(Encoding)
string.to_s.force_encoding('BINARY')
string.to_s.dup.force_encoding('BINARY')
else
string.to_s
string.to_s.mb_chars
end
end
end
Expand Down
16 changes: 16 additions & 0 deletions spec/acts_as_taggable_on/taggable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@
unless ActsAsTaggableOn::Tag.using_sqlite?
it "should not care about case for unicode names" do
ActsAsTaggableOn.strict_case_match = false

anya = TaggableModel.create(:name => "Anya", :tag_list => "ПРИВЕТ")
igor = TaggableModel.create(:name => "Igor", :tag_list => "привет")
katia = TaggableModel.create(:name => "Katia", :tag_list => "ПРИВЕТ")
Expand All @@ -255,6 +256,21 @@
end
end

it "should be able to create and find tags in languages without capitalization" do
ActsAsTaggableOn.strict_case_match = false
chihiro = TaggableModel.create(:name => "Chihiro", :tag_list => "日本の")
salim = TaggableModel.create(:name => "Salim", :tag_list => "עברית")
ieie = TaggableModel.create(:name => "Ieie", :tag_list => "中国的")
yasser = TaggableModel.create(:name => "Yasser", :tag_list => "العربية")
emo = TaggableModel.create(:name => "Emo", :tag_list => "✏")

TaggableModel.tagged_with("日本の").to_a.size.should == 1
TaggableModel.tagged_with("עברית").to_a.size.should == 1
TaggableModel.tagged_with("中国的").to_a.size.should == 1
TaggableModel.tagged_with("العربية").to_a.size.should == 1
TaggableModel.tagged_with("✏").to_a.size.should == 1
end

it "should be able to get tag counts on model as a whole" do
bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
Expand Down

0 comments on commit 1db9880

Please sign in to comment.