Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed validates_length_of to work on UTF-8 strings by using character…

…s instead of bytes (closes #3699) [Masao Mutoh]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@3654 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
commit 272729e0a339d80bc422fcbdfc86dd60e3e4c030 1 parent ad9f678
@dhh dhh authored
View
2  activerecord/CHANGELOG
@@ -1,5 +1,7 @@
*SVN*
+* Fixed validates_length_of to work on UTF-8 strings by using characters instead of bytes #3699 [Masao Mutoh]
+
* Fixed that reflections would bleed across class boundaries in single-table inheritance setups #3796 [lars@pind.com]
* Added calculations: Base.count, Base.average, Base.sum, Base.minimum, Base.maxmium, and the generic Base.calculate. All can be used with :group and :having. Calculations and statitics need no longer require custom SQL. #3958 [Rick Olson]. Examples:
View
46 activerecord/lib/active_record/validations.rb
@@ -439,31 +439,35 @@ def validates_length_of(*attrs)
option_value = options[range_options.first]
case option
- when :within, :in
- raise ArgumentError, ":#{option} must be a Range" unless option_value.is_a?(Range)
-
- too_short = options[:too_short] % option_value.begin
- too_long = options[:too_long] % option_value.end
-
- validates_each(attrs, options) do |record, attr, value|
- if value.nil? or value.size < option_value.begin
- record.errors.add(attr, too_short)
- elsif value.size > option_value.end
- record.errors.add(attr, too_long)
+ when :within, :in
+ raise ArgumentError, ":#{option} must be a Range" unless option_value.is_a?(Range)
+
+ too_short = options[:too_short] % option_value.begin
+ too_long = options[:too_long] % option_value.end
+
+ validates_each(attrs, options) do |record, attr, value|
+ if value.nil? or value.split(//).size < option_value.begin
+ record.errors.add(attr, too_short)
+ elsif value.split(//).size > option_value.end
+ record.errors.add(attr, too_long)
+ end
end
- end
- when :is, :minimum, :maximum
- raise ArgumentError, ":#{option} must be a nonnegative Integer" unless option_value.is_a?(Integer) and option_value >= 0
+ when :is, :minimum, :maximum
+ raise ArgumentError, ":#{option} must be a nonnegative Integer" unless option_value.is_a?(Integer) and option_value >= 0
- # Declare different validations per option.
- validity_checks = { :is => "==", :minimum => ">=", :maximum => "<=" }
- message_options = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }
+ # Declare different validations per option.
+ validity_checks = { :is => "==", :minimum => ">=", :maximum => "<=" }
+ message_options = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }
- message = (options[:message] || options[message_options[option]]) % option_value
+ message = (options[:message] || options[message_options[option]]) % option_value
- validates_each(attrs, options) do |record, attr, value|
- record.errors.add(attr, message) unless !value.nil? and value.size.method(validity_checks[option])[option_value]
- end
+ validates_each(attrs, options) do |record, attr, value|
+ if value.kind_of?(String)
+ record.errors.add(attr, message) unless !value.nil? and value.split(//).size.method(validity_checks[option])[option_value]
+ else
+ record.errors.add(attr, message) unless !value.nil? and value.size.method(validity_checks[option])[option_value]
+ end
+ end
end
end
View
1  activerecord/test/connections/native_mysql/connection.rb
@@ -10,6 +10,7 @@
ActiveRecord::Base.establish_connection(
:adapter => "mysql",
:username => "rails",
+ :encoding => "utf8",
:database => db1
)
View
135 activerecord/test/validations_test.rb
@@ -614,6 +614,141 @@ def test_validates_length_of_custom_errors_for_is_with_wrong_length
assert_equal "hoo 5", t.errors["title"]
end
+ def kcode_scope(kcode)
+ orig_kcode = $KCODE
+ $KCODE = kcode
+ begin
+ yield
+ ensure
+ $KCODE = orig_kcode
+ end
+ end
+
+ def test_validates_length_of_using_minimum_utf8
+ kcode_scope('UTF8') do
+ Topic.validates_length_of :title, :minimum => 5
+
+ t = Topic.create("title" => "一二三四五", "content" => "whatever")
+ assert t.valid?
+
+ t.title = "一二三四"
+ assert !t.valid?
+ assert t.errors.on(:title)
+ assert_equal "is too short (min is 5 characters)", t.errors["title"]
+ end
+ end
+
+ def test_validates_length_of_using_maximum_utf8
+ kcode_scope('UTF8') do
+ Topic.validates_length_of :title, :maximum => 5
+
+ t = Topic.create("title" => "一二三四五", "content" => "whatever")
+ assert t.valid?
+
+ t.title = "一二34五六"
+ assert !t.valid?
+ assert t.errors.on(:title)
+ assert_equal "is too long (max is 5 characters)", t.errors["title"]
+ end
+ end
+
+ def test_validates_length_of_using_within_utf8
+ kcode_scope('UTF8') do
+ Topic.validates_length_of(:title, :content, :within => 3..5)
+
+ t = Topic.new("title" => "一二", "content" => "12三四五六七")
+ assert !t.valid?
+ assert_equal "is too short (min is 3 characters)", t.errors.on(:title)
+ assert_equal "is too long (max is 5 characters)", t.errors.on(:content)
+ t.title = "一二三"
+ t.content = "12三"
+ assert t.valid?
+ end
+ end
+
+ def test_optionally_validates_length_of_using_within_utf8
+ kcode_scope('UTF8') do
+ Topic.validates_length_of :title, :content, :within => 3..5, :allow_nil => true
+
+ t = Topic.create('title' => '一二三', 'content' => '一二三四五')
+ assert t.valid?
+
+ t.title = nil
+ assert t.valid?
+ end
+ end
+
+ def test_optionally_validates_length_of_using_within_on_create_utf8
+ kcode_scope('UTF8') do
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :create, :too_long => "長すぎます: %d"
+
+ t = Topic.create("title" => "一二三四五六七八九十A", "content" => "whatever")
+ assert !t.save
+ assert t.errors.on(:title)
+ assert_equal "長すぎます: 10", t.errors[:title]
+
+ t.title = "一二三四五六七八九"
+ assert t.save
+
+ t.title = "一二3"
+ assert t.save
+
+ t.content = "一二三四五六七八九十"
+ assert t.save
+
+ t.content = t.title = "一二三四五六"
+ assert t.save
+ end
+ end
+
+ def test_optionally_validates_length_of_using_within_on_update_utf8
+ kcode_scope('UTF8') do
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :update, :too_short => "短すぎます: %d"
+
+ t = Topic.create("title" => "一二三4", "content" => "whatever")
+ assert !t.save
+ assert t.errors.on(:title)
+
+ t.title = "1二三4"
+ assert !t.save
+ assert t.errors.on(:title)
+ assert_equal "短すぎます: 5", t.errors[:title]
+
+ t.title = "valid"
+ t.content = "一二三四五六七八九十A"
+ assert !t.save
+ assert t.errors.on(:content)
+
+ t.content = "一二345"
+ assert t.save
+ end
+ end
+
+ def test_validates_length_of_using_is_utf8
+ kcode_scope('UTF8') do
+ Topic.validates_length_of :title, :is => 5
+
+ t = Topic.create("title" => "一二345", "content" => "whatever")
+ assert t.valid?
+
+ t.title = "一二345六"
+ assert !t.valid?
+ assert t.errors.on(:title)
+ assert_equal "is the wrong length (should be 5 characters)", t.errors["title"]
+ end
+ end
+
+ def test_validates_size_of_association_utf8
+ kcode_scope('UTF8') do
+ assert_nothing_raised { Topic.validates_size_of :replies, :minimum => 1 }
+ t = Topic.new('title' => 'あいうえお', 'content' => 'かきくけこ')
+ assert !t.save
+ assert t.errors.on(:replies)
+ t.replies.create('title' => 'あいうえお', 'content' => 'かきくけこ')
+ assert t.valid?
+ end
+ end
+
def test_validates_associated_many
Topic.validates_associated( :replies )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
Please sign in to comment.
Something went wrong with that request. Please try again.