From c3fa5c3d25d9148d2806db577a2261032b341c34 Mon Sep 17 00:00:00 2001 From: kakipo Date: Sun, 3 Aug 2014 14:50:09 +0900 Subject: [PATCH] Allow symbol as values for `tokenize` of `LengthValidator` --- activemodel/CHANGELOG.md | 4 ++++ .../lib/active_model/validations/length.rb | 18 +++++++++++------- .../validations/length_validation_test.rb | 13 +++++++++++++ activemodel/test/models/topic.rb | 4 ++++ 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md index 8d22e3ac46..aa1f3debb5 100644 --- a/activemodel/CHANGELOG.md +++ b/activemodel/CHANGELOG.md @@ -1,3 +1,7 @@ +* Allow symbol as values for `tokenize` of `LengthValidator` + + *Kensuke Naito* + * Validate options passed to `ActiveModel::Validations.validate`. Preventing, in many cases, the simple mistake of using `validate` instead of `validates`. diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb index a96b30cadd..c63a9d74b3 100644 --- a/activemodel/lib/active_model/validations/length.rb +++ b/activemodel/lib/active_model/validations/length.rb @@ -38,7 +38,7 @@ module ActiveModel end def validate_each(record, attribute, value) - value = tokenize(value) + value = tokenize(record, value) value_length = value.respond_to?(:length) ? value.length : value.to_s.length errors_options = options.except(*RESERVED_OPTIONS) @@ -59,10 +59,14 @@ module ActiveModel end private - - def tokenize(value) - if options[:tokenizer] && value.kind_of?(String) - options[:tokenizer].call(value) + def tokenize(record, value) + tokenizer = options[:tokenizer] + if tokenizer && value.kind_of?(String) + if tokenizer.kind_of?(Proc) + tokenizer.call(value) + elsif record.respond_to?(tokenizer) + record.send(tokenizer, value) + end end || value end @@ -108,8 +112,8 @@ module ActiveModel # * :message - The error message to use for a :minimum, # :maximum, or :is violation. An alias of the appropriate # too_long/too_short/wrong_length message. - # * :tokenizer - Specifies how to split up the attribute string. - # (e.g. tokenizer: ->(str) { str.scan(/\w+/) } to count words + # * :tokenizer - Specifies a method, proc or string to how to split up the attribute string. + # (e.g. tokenizer: ->(str) { str.scan(/\w+/) } or tokenizer: :word_tokenizer to count words # as in above example). Defaults to ->(value) { value.split(//) } # which counts individual characters. # diff --git a/activemodel/test/cases/validations/length_validation_test.rb b/activemodel/test/cases/validations/length_validation_test.rb index 046ffcb16f..3fa9f9274c 100644 --- a/activemodel/test/cases/validations/length_validation_test.rb +++ b/activemodel/test/cases/validations/length_validation_test.rb @@ -331,6 +331,19 @@ class LengthValidationTest < ActiveModel::TestCase assert_equal ["Your essay must be at least 5 words."], t.errors[:content] end + + def test_validates_length_of_with_symbol + Topic.validates_length_of :content, minimum: 5, too_short: "Your essay must be at least %{count} words.", + tokenizer: :my_word_tokenizer + t = Topic.new(content: "this content should be long enough") + assert t.valid? + + t.content = "not long enough" + assert t.invalid? + assert t.errors[:content].any? + assert_equal ["Your essay must be at least 5 words."], t.errors[:content] + end + def test_validates_length_of_for_fixnum Topic.validates_length_of(:approved, is: 4) diff --git a/activemodel/test/models/topic.rb b/activemodel/test/models/topic.rb index 1411a093e9..fed50bc361 100644 --- a/activemodel/test/models/topic.rb +++ b/activemodel/test/models/topic.rb @@ -37,4 +37,8 @@ class Topic errors.add attr, "is missing" unless send(attr) end + def my_word_tokenizer(str) + str.scan(/\w+/) + end + end