From 938243feb9ea177b84276821818c9eed66064340 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 19 Nov 2010 16:26:09 -0800 Subject: do not require ruby-debug automatically. please require it if you have declared it as a dependency --- activemodel/test/cases/helper.rb | 6 ------ 1 file changed, 6 deletions(-) (limited to 'activemodel') diff --git a/activemodel/test/cases/helper.rb b/activemodel/test/cases/helper.rb index a81584bbad..01f0158678 100644 --- a/activemodel/test/cases/helper.rb +++ b/activemodel/test/cases/helper.rb @@ -12,9 +12,3 @@ ActiveSupport::Deprecation.debug = true require 'rubygems' require 'test/unit' - -begin - require 'ruby-debug' - Debugger.start -rescue LoadError -end -- cgit v1.2.3 From cb1570936d70d624f72229c8a19ffbe3a10cc5ca Mon Sep 17 00:00:00 2001 From: raggi Date: Thu, 25 Nov 2010 04:03:04 +0800 Subject: Rakefiles are executables, and rake loads rake, not rakefile code --- activemodel/Rakefile | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 activemodel/Rakefile (limited to 'activemodel') diff --git a/activemodel/Rakefile b/activemodel/Rakefile old mode 100644 new mode 100755 -- cgit v1.2.3 From 04922bafda9456ca9b6b8c59f231c402cb7952b7 Mon Sep 17 00:00:00 2001 From: Aditya Sanghi Date: Sat, 27 Nov 2010 19:56:08 +0530 Subject: grammar fix --- activemodel/lib/active_model/attribute_methods.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb index c1c5640616..fc5f5c4c66 100644 --- a/activemodel/lib/active_model/attribute_methods.rb +++ b/activemodel/lib/active_model/attribute_methods.rb @@ -46,8 +46,8 @@ module ActiveModel # end # end # - # Notice that whenever you include ActiveModel::AttributeMethods in your class, - # it requires you to implement a attributes methods which returns a hash + # Note that whenever you include ActiveModel::AttributeMethods in your class, + # it requires you to implement an attributes method which returns a hash # with each attribute name in your model as hash key and the attribute value as # hash value. # -- cgit v1.2.3 From a9b666b51d28b2e74da630c31327dee7cbe96d37 Mon Sep 17 00:00:00 2001 From: Krekoten' Marjan Date: Tue, 30 Nov 2010 22:48:19 +0800 Subject: Fix generation of wrong json string when field has multiple errors --- activemodel/lib/active_model/errors.rb | 10 ++++++++++ activemodel/test/cases/errors_test.rb | 10 ++++++++++ 2 files changed, 20 insertions(+) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index 99f47f2cbe..01ca540d28 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -167,6 +167,16 @@ module ActiveModel def as_json(options=nil) self end + + def encode_json(encoder) + errors = [] + each_pair do |key, value| + value = value.first if value.size == 1 + errors << "#{encoder.encode(key.to_s)}:#{encoder.encode(value, false)}" + end + + "{#{errors * ','}}" + end # Adds +message+ to the error messages on +attribute+, which will be returned on a call to # on(attribute) for the same attribute. More than one error can be added to the same diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb index 79b45bb298..1958ed112c 100644 --- a/activemodel/test/cases/errors_test.rb +++ b/activemodel/test/cases/errors_test.rb @@ -61,5 +61,15 @@ class ErrorsTest < ActiveModel::TestCase assert_equal ["name can not be blank", "name can not be nil"], person.errors.to_a end + + test 'to_json should return valid json string' do + person = Person.new + person.errors.add(:name, "can not be blank") + person.errors.add(:name, "can not be nil") + + hash = ActiveSupport::OrderedHash[:name, ["can not be blank", "can not be nil"]] + + assert_equal person.errors.to_json, hash.to_json + end end -- cgit v1.2.3 From 1ec126dd37b52ecf7c0c24a842fc87836d8f2e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 1 Dec 2010 11:42:30 +0100 Subject: Revert "Fix generation of wrong json string when field has multiple errors" This reverts commit a9b666b51d28b2e74da630c31327dee7cbe96d37. --- activemodel/lib/active_model/errors.rb | 10 ---------- activemodel/test/cases/errors_test.rb | 10 ---------- 2 files changed, 20 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index 01ca540d28..99f47f2cbe 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -167,16 +167,6 @@ module ActiveModel def as_json(options=nil) self end - - def encode_json(encoder) - errors = [] - each_pair do |key, value| - value = value.first if value.size == 1 - errors << "#{encoder.encode(key.to_s)}:#{encoder.encode(value, false)}" - end - - "{#{errors * ','}}" - end # Adds +message+ to the error messages on +attribute+, which will be returned on a call to # on(attribute) for the same attribute. More than one error can be added to the same diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb index 1958ed112c..79b45bb298 100644 --- a/activemodel/test/cases/errors_test.rb +++ b/activemodel/test/cases/errors_test.rb @@ -61,15 +61,5 @@ class ErrorsTest < ActiveModel::TestCase assert_equal ["name can not be blank", "name can not be nil"], person.errors.to_a end - - test 'to_json should return valid json string' do - person = Person.new - person.errors.add(:name, "can not be blank") - person.errors.add(:name, "can not be nil") - - hash = ActiveSupport::OrderedHash[:name, ["can not be blank", "can not be nil"]] - - assert_equal person.errors.to_json, hash.to_json - end end -- cgit v1.2.3 From 7148b933c4865a5140187d7ed792fd6df9b860a4 Mon Sep 17 00:00:00 2001 From: Thilo Utke Date: Sun, 28 Nov 2010 14:36:40 +0100 Subject: ActiveModel::Errors.to_hash returns plain OrderedHash and used in to_json serialization to properly handle multiple errors per attribute [#5615 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activemodel/lib/active_model/errors.rb | 8 +++++++- activemodel/test/cases/errors_test.rb | 5 +++++ .../test/cases/serializeration/json_serialization_test.rb | 15 ++++++++------- activemodel/test/cases/validations_test.rb | 4 ++-- 4 files changed, 22 insertions(+), 10 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index 99f47f2cbe..fdca852c7a 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -165,7 +165,13 @@ module ActiveModel # Returns an ActiveSupport::OrderedHash that can be used as the JSON representation for this object. def as_json(options=nil) - self + to_hash + end + + def to_hash + hash = ActiveSupport::OrderedHash.new + each { |k, v| (hash[k] ||= []) << v } + hash end # Adds +message+ to the error messages on +attribute+, which will be returned on a call to diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb index 79b45bb298..27821c333b 100644 --- a/activemodel/test/cases/errors_test.rb +++ b/activemodel/test/cases/errors_test.rb @@ -62,4 +62,9 @@ class ErrorsTest < ActiveModel::TestCase end + test 'to_hash should return an ordered hash' do + person = Person.new + person.errors.add(:name, "can not be blank") + assert_instance_of ActiveSupport::OrderedHash, person.errors.to_hash + end end diff --git a/activemodel/test/cases/serializeration/json_serialization_test.rb b/activemodel/test/cases/serializeration/json_serialization_test.rb index 20d123ef0b..500a5c575f 100644 --- a/activemodel/test/cases/serializeration/json_serialization_test.rb +++ b/activemodel/test/cases/serializeration/json_serialization_test.rb @@ -6,6 +6,7 @@ require 'active_support/core_ext/object/instance_variables' class Contact extend ActiveModel::Naming include ActiveModel::Serializers::JSON + include ActiveModel::Validations def attributes instance_values @@ -105,15 +106,15 @@ class JsonSerializationTest < ActiveModel::TestCase end test "should return OrderedHash for errors" do - car = Automobile.new - - # run the validation - car.valid? + contact = Contact.new + contact.errors.add :name, "can't be blank" + contact.errors.add :name, "is too short (minimum is 2 characters)" + contact.errors.add :age, "must be 16 or over" hash = ActiveSupport::OrderedHash.new - hash[:make] = "can't be blank" - hash[:model] = "is too short (minimum is 2 characters)" - assert_equal hash.to_json, car.errors.to_json + hash[:name] = ["can't be blank", "is too short (minimum is 2 characters)"] + hash[:age] = ["must be 16 or over"] + assert_equal hash.to_json, contact.errors.to_json end test "serializable_hash should not modify options passed in argument" do diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb index 4024002aaa..55b477dd10 100644 --- a/activemodel/test/cases/validations_test.rb +++ b/activemodel/test/cases/validations_test.rb @@ -174,8 +174,8 @@ class ValidationsTest < ActiveModel::TestCase assert_match %r{Content can't be blank}, xml hash = ActiveSupport::OrderedHash.new - hash[:title] = "can't be blank" - hash[:content] = "can't be blank" + hash[:title] = ["can't be blank"] + hash[:content] = ["can't be blank"] assert_equal t.errors.to_json, hash.to_json end -- cgit v1.2.3 From acbabd96268237826e1ab094273a1d7d53b4e579 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 2 Dec 2010 15:43:12 -0200 Subject: Don't compute this string again --- activemodel/lib/active_model/validations/numericality.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb index b6aff7aa6b..95fe20de75 100644 --- a/activemodel/lib/active_model/validations/numericality.rb +++ b/activemodel/lib/active_model/validations/numericality.rb @@ -24,7 +24,7 @@ module ActiveModel def validate_each(record, attr_name, value) before_type_cast = "#{attr_name}_before_type_cast" - raw_value = record.send("#{attr_name}_before_type_cast") if record.respond_to?(before_type_cast.to_sym) + raw_value = record.send(before_type_cast) if record.respond_to?(before_type_cast.to_sym) raw_value ||= value return if options[:allow_nil] && raw_value.nil? -- cgit v1.2.3 From 7ffd5daa7fe1fd805fd071d40dfad1773299c7d1 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sun, 14 Nov 2010 18:30:09 +0100 Subject: bump i18n gem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activemodel/activemodel.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activemodel') diff --git a/activemodel/activemodel.gemspec b/activemodel/activemodel.gemspec index 318d71a610..1f38e70c36 100644 --- a/activemodel/activemodel.gemspec +++ b/activemodel/activemodel.gemspec @@ -21,5 +21,5 @@ Gem::Specification.new do |s| s.add_dependency('activesupport', version) s.add_dependency('builder', '~> 3.0.0') - s.add_dependency('i18n', '~> 0.4.2') + s.add_dependency('i18n', '~> 0.5.0') end -- cgit v1.2.3 From b870d6729090865f0a21c2ea6b9f1fffdd1c4d7e Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 5 Dec 2010 12:31:06 -0200 Subject: Test using default option added for human_attribute_name --- activemodel/test/cases/translation_test.rb | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'activemodel') diff --git a/activemodel/test/cases/translation_test.rb b/activemodel/test/cases/translation_test.rb index ac2e56321e..f0cb02eb5c 100644 --- a/activemodel/test/cases/translation_test.rb +++ b/activemodel/test/cases/translation_test.rb @@ -17,6 +17,10 @@ class ActiveModelI18nTests < ActiveModel::TestCase assert_equal 'name default attribute', Person.human_attribute_name('name') end + def test_translated_model_attributes_using_default_option + assert_equal 'name default attribute', Person.human_attribute_name('name', :default => "name default attribute") + end + def test_translated_model_attributes_with_symbols I18n.backend.store_translations 'en', :activemodel => {:attributes => {:person => {:name => 'person name attribute'} } } assert_equal 'person name attribute', Person.human_attribute_name(:name) -- cgit v1.2.3 From 0dbf4ac709c0c31d4d6664bcb53b861c6d6b0157 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 5 Dec 2010 12:32:29 -0200 Subject: Test falling back to default added for human_attribute_name --- activemodel/test/cases/translation_test.rb | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'activemodel') diff --git a/activemodel/test/cases/translation_test.rb b/activemodel/test/cases/translation_test.rb index f0cb02eb5c..da6cb98bc9 100644 --- a/activemodel/test/cases/translation_test.rb +++ b/activemodel/test/cases/translation_test.rb @@ -21,6 +21,10 @@ class ActiveModelI18nTests < ActiveModel::TestCase assert_equal 'name default attribute', Person.human_attribute_name('name', :default => "name default attribute") end + def test_translated_model_attributes_falling_back_to_default + assert_equal 'Name', Person.human_attribute_name('name') + end + def test_translated_model_attributes_with_symbols I18n.backend.store_translations 'en', :activemodel => {:attributes => {:person => {:name => 'person name attribute'} } } assert_equal 'person name attribute', Person.human_attribute_name(:name) -- cgit v1.2.3 From 3ab98cf6e0d399042424d44a788b8bc679b8dd1c Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 5 Dec 2010 12:34:58 -0200 Subject: If default is provided don't add attribute.to_s.humanize to the options --- activemodel/lib/active_model/translation.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/translation.rb b/activemodel/lib/active_model/translation.rb index 920a133159..6959f99b5e 100644 --- a/activemodel/lib/active_model/translation.rb +++ b/activemodel/lib/active_model/translation.rb @@ -48,8 +48,7 @@ module ActiveModel end defaults << :"attributes.#{attribute}" - defaults << options.delete(:default) if options[:default] - defaults << attribute.to_s.humanize + defaults << (options[:default] ? options.delete(:default) : attribute.to_s.humanize) options.reverse_merge! :count => 1, :default => defaults I18n.translate(defaults.shift, options) -- cgit v1.2.3 From 33b0a30fcc5694d26034bc74ed61e34edecbfbc4 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 5 Dec 2010 12:57:45 -0200 Subject: default could be a symbol here so attribute.to_s.humanize should be the final option --- activemodel/lib/active_model/translation.rb | 3 ++- activemodel/test/cases/translation_test.rb | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/translation.rb b/activemodel/lib/active_model/translation.rb index 6959f99b5e..920a133159 100644 --- a/activemodel/lib/active_model/translation.rb +++ b/activemodel/lib/active_model/translation.rb @@ -48,7 +48,8 @@ module ActiveModel end defaults << :"attributes.#{attribute}" - defaults << (options[:default] ? options.delete(:default) : attribute.to_s.humanize) + defaults << options.delete(:default) if options[:default] + defaults << attribute.to_s.humanize options.reverse_merge! :count => 1, :default => defaults I18n.translate(defaults.shift, options) diff --git a/activemodel/test/cases/translation_test.rb b/activemodel/test/cases/translation_test.rb index da6cb98bc9..17c1083cf5 100644 --- a/activemodel/test/cases/translation_test.rb +++ b/activemodel/test/cases/translation_test.rb @@ -25,6 +25,10 @@ class ActiveModelI18nTests < ActiveModel::TestCase assert_equal 'Name', Person.human_attribute_name('name') end + def test_translated_model_attributes_using_default_option_as_symbol_and_falling_back_to_default + assert_equal 'Name', Person.human_attribute_name('name', :default => :default_name) + end + def test_translated_model_attributes_with_symbols I18n.backend.store_translations 'en', :activemodel => {:attributes => {:person => {:name => 'person name attribute'} } } assert_equal 'person name attribute', Person.human_attribute_name(:name) -- cgit v1.2.3 From 7c920631ec3b314cfaa3a60d265de40cba3e8135 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 5 Dec 2010 13:26:14 -0200 Subject: Test using default option as symbol added for human_attribute_name --- activemodel/test/cases/translation_test.rb | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'activemodel') diff --git a/activemodel/test/cases/translation_test.rb b/activemodel/test/cases/translation_test.rb index 17c1083cf5..c299d6eb5e 100644 --- a/activemodel/test/cases/translation_test.rb +++ b/activemodel/test/cases/translation_test.rb @@ -21,6 +21,11 @@ class ActiveModelI18nTests < ActiveModel::TestCase assert_equal 'name default attribute', Person.human_attribute_name('name', :default => "name default attribute") end + def test_translated_model_attributes_using_default_option_as_symbol + I18n.backend.store_translations 'en', :default_name => 'name default attribute' + assert_equal 'name default attribute', Person.human_attribute_name('name', :default => :default_name) + end + def test_translated_model_attributes_falling_back_to_default assert_equal 'Name', Person.human_attribute_name('name') end -- cgit v1.2.3 From f572a02b94ce85bb8db64e838aa159cf3ef4b1fa Mon Sep 17 00:00:00 2001 From: Mike Dvorkin Date: Wed, 8 Dec 2010 20:39:46 -0800 Subject: Take into account current time zone when serializing datetime values [#6096 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activemodel/lib/active_model/serializers/xml.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb index 26a134568c..b897baa614 100644 --- a/activemodel/lib/active_model/serializers/xml.rb +++ b/activemodel/lib/active_model/serializers/xml.rb @@ -17,6 +17,7 @@ module ActiveModel def initialize(name, serializable, raw_value=nil) @name, @serializable = name, serializable + raw_value = raw_value.in_time_zone if raw_value.respond_to?(:in_time_zone) @value = raw_value || @serializable.send(name) @type = compute_type end -- cgit v1.2.3 From b4ec6f0d3964fb550dbd42a9cd23925cff5f552e Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Thu, 9 Dec 2010 18:16:18 +1000 Subject: Move ActiveModel::Lint::Tests documentation to be above module declaration so it appears in the API docs for this module. --- activemodel/lib/active_model/lint.rb | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/lint.rb b/activemodel/lib/active_model/lint.rb index d7a6da48ca..957d1b9d70 100644 --- a/activemodel/lib/active_model/lint.rb +++ b/activemodel/lib/active_model/lint.rb @@ -1,19 +1,19 @@ -# == Active Model Lint Tests -# -# You can test whether an object is compliant with the Active Model API by -# including ActiveModel::Lint::Tests in your TestCase. It will include -# tests that tell you whether your object is fully compliant, or if not, -# which aspects of the API are not implemented. -# -# These tests do not attempt to determine the semantic correctness of the -# returned values. For instance, you could implement valid? to always -# return true, and the tests would pass. It is up to you to ensure that -# the values are semantically meaningful. -# -# Objects you pass in are expected to return a compliant object from a -# call to to_model. It is perfectly fine for to_model to return self. module ActiveModel module Lint + # == Active Model Lint Tests + # + # You can test whether an object is compliant with the Active Model API by + # including ActiveModel::Lint::Tests in your TestCase. It will include + # tests that tell you whether your object is fully compliant, or if not, + # which aspects of the API are not implemented. + # + # These tests do not attempt to determine the semantic correctness of the + # returned values. For instance, you could implement valid? to always + # return true, and the tests would pass. It is up to you to ensure that + # the values are semantically meaningful. + # + # Objects you pass in are expected to return a compliant object from a + # call to to_model. It is perfectly fine for to_model to return self. module Tests # == Responds to to_key -- cgit v1.2.3 From 972011a2e58be5caf84bf06dd954ba095c823984 Mon Sep 17 00:00:00 2001 From: Samuel Kadolph Date: Thu, 9 Dec 2010 13:30:02 -0500 Subject: Add support for namespaced validators Includes test and documentation for new feature Signed-off-by: Santiago Pastorino --- activemodel/lib/active_model/validations/validates.rb | 8 +++++++- activemodel/test/cases/validations/validates_test.rb | 8 ++++++++ activemodel/test/validators/namespace/email_validator.rb | 6 ++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100755 activemodel/test/validators/namespace/email_validator.rb (limited to 'activemodel') diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb index 77c5073c6e..0132f68282 100644 --- a/activemodel/lib/active_model/validations/validates.rb +++ b/activemodel/lib/active_model/validations/validates.rb @@ -55,6 +55,10 @@ module ActiveModel # validates :name, :title => true # end # + # Additionally validator classes may be in another namespace and still used within any class. + # + # validates :name, :'file/title' => true + # # The validators hash can also handle regular expressions, ranges, # arrays and strings in shortcut form, e.g. # @@ -86,8 +90,10 @@ module ActiveModel defaults.merge!(:attributes => attributes) validations.each do |key, options| + key = "#{key.to_s.camelize}Validator" + begin - validator = const_get("#{key.to_s.camelize}Validator") + validator = key.include?('::') ? key.constantize : const_get(key) rescue NameError raise ArgumentError, "Unknown validator: '#{key}'" end diff --git a/activemodel/test/cases/validations/validates_test.rb b/activemodel/test/cases/validations/validates_test.rb index 666c48c8a0..3a9900939e 100644 --- a/activemodel/test/cases/validations/validates_test.rb +++ b/activemodel/test/cases/validations/validates_test.rb @@ -3,6 +3,7 @@ require 'cases/helper' require 'models/person' require 'models/person_with_validator' require 'validators/email_validator' +require 'validators/namespace/email_validator' class ValidatesTest < ActiveModel::TestCase setup :reset_callbacks @@ -34,6 +35,13 @@ class ValidatesTest < ActiveModel::TestCase assert_equal ['is not an email'], person.errors[:karma] end + def test_validates_with_namespaced_validator_class + Person.validates :karma, :'namespace/email' => true + person = Person.new + person.valid? + assert_equal ['is not an email'], person.errors[:karma] + end + def test_validates_with_if_as_local_conditions Person.validates :karma, :presence => true, :email => { :unless => :condition_is_true } person = Person.new diff --git a/activemodel/test/validators/namespace/email_validator.rb b/activemodel/test/validators/namespace/email_validator.rb new file mode 100755 index 0000000000..57e2793ce2 --- /dev/null +++ b/activemodel/test/validators/namespace/email_validator.rb @@ -0,0 +1,6 @@ +require 'validators/email_validator' + +module Namespace + class EmailValidator < ::EmailValidator + end +end -- cgit v1.2.3 From 9cfbada93a1e838f6d07e46ea5ab2c3da6e08e78 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 11 Dec 2010 16:40:05 -0200 Subject: This is not an executable file --- activemodel/test/validators/namespace/email_validator.rb | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 activemodel/test/validators/namespace/email_validator.rb (limited to 'activemodel') diff --git a/activemodel/test/validators/namespace/email_validator.rb b/activemodel/test/validators/namespace/email_validator.rb old mode 100755 new mode 100644 -- cgit v1.2.3 From bcf4e4f2b02157cecc1f1727a95cdf5bfa471771 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sat, 18 Dec 2010 13:38:05 -0800 Subject: Added ActiveRecord::Base#has_secure_password (via ActiveModel::SecurePassword) to encapsulate dead-simple password usage with SHA2 encryption and salting --- activemodel/CHANGELOG | 5 +- activemodel/lib/active_model.rb | 1 + activemodel/lib/active_model/secure_password.rb | 73 +++++++++++++++++++++++++ activemodel/test/cases/secure_password_test.rb | 42 ++++++++++++++ activemodel/test/models/user.rb | 8 +++ 5 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 activemodel/lib/active_model/secure_password.rb create mode 100644 activemodel/test/cases/secure_password_test.rb create mode 100644 activemodel/test/models/user.rb (limited to 'activemodel') diff --git a/activemodel/CHANGELOG b/activemodel/CHANGELOG index 4e963c77b0..a19d029217 100644 --- a/activemodel/CHANGELOG +++ b/activemodel/CHANGELOG @@ -1,15 +1,18 @@ *Rails 3.1.0 (unreleased)* -* No changes +* Added ActiveModel::SecurePassword to encapsulate dead-simple password usage with SHA2 encryption and salting [DHH] + *Rails 3.0.2 (unreleased)* * No changes + *Rails 3.0.1 (October 15, 2010)* * No Changes, just a version bump. + *Rails 3.0.0 (August 29, 2010)* * Added ActiveModel::MassAssignmentSecurity [Eric Chapweske, Josh Kalderimis] diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index be0f24ff92..dd6ee058cc 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -42,6 +42,7 @@ module ActiveModel autoload :Naming autoload :Observer, 'active_model/observing' autoload :Observing + autoload :SecurePassword autoload :Serialization autoload :TestCase autoload :Translation diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb new file mode 100644 index 0000000000..0599ce6865 --- /dev/null +++ b/activemodel/lib/active_model/secure_password.rb @@ -0,0 +1,73 @@ +require 'digest/sha2' + +module ActiveModel + module SecurePassword + extend ActiveSupport::Concern + + module ClassMethods + # Adds methods to set and authenticate against a SHA2-encrypted and salted password. + # This mechanism requires you to have password_digest and password_salt attributes. + # + # Validations for presence of password, confirmation of password (using a "password_confirmation" attribute), + # and strength of password (at least 6 chars, not "password") are automatically added. + # You can add more validations by hand if need be. + # + # Example using Active Record (which automatically includes ActiveModel::SecurePassword): + # + # # Schema: User(name:string, password_digest:string, password_salt:string) + # class User < ActiveRecord::Base + # has_secure_password + # end + # + # user = User.new(:name => "david", :password => "secret", :password_confirmation => "nomatch") + # user.save # => false, password not long enough + # user.password = "mUc3m00RsqyRe" + # user.save # => false, confirmation doesn't match + # user.password_confirmation = "mUc3m00RsqyRe" + # user.save # => true + # user.authenticate("notright") # => false + # user.authenticate("mUc3m00RsqyRe") # => user + # User.find_by_name("david").try(:authenticate, "notright") # => nil + # User.find_by_name("david").try(:authenticate, "mUc3m00RsqyRe") # => user + def has_secure_password + attr_reader :password + attr_accessor :password_confirmation + + attr_protected(:password_digest, :password_salt) if respond_to?(:attr_protected) + + validates_confirmation_of :password + validates_presence_of :password_digest + validate :password_must_be_strong + end + end + + module InstanceMethods + # Returns self if the password is correct, otherwise false. + def authenticate(unencrypted_password) + password_digest == encrypt_password(unencrypted_password) ? self : false + end + + # Encrypts the password into the password_digest attribute. + def password=(unencrypted_password) + @password = unencrypted_password + self.password_digest = encrypt_password(unencrypted_password) + end + + private + def salt_for_password + self.password_salt ||= self.object_id.to_s + rand.to_s + end + + def encrypt_password(unencrypted_password) + Digest::SHA2.hexdigest(unencrypted_password + salt_for_password) + end + + def password_must_be_strong + if @password.present? + errors.add(:password, "must be longer than 6 characters") unless @password.size > 6 + errors.add(:password, "can't be 'password'") if @password == "password" + end + end + end + end +end \ No newline at end of file diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb new file mode 100644 index 0000000000..7d7c51e6cb --- /dev/null +++ b/activemodel/test/cases/secure_password_test.rb @@ -0,0 +1,42 @@ +require 'cases/helper' +require 'models/user' + +class SecurePasswordTest < ActiveModel::TestCase + setup do + @user = User.new + end + + test "password must be present" do + assert !@user.valid? + assert_equal 1, @user.errors.size + end + + test "password must match confirmation" do + @user.password = "thiswillberight" + @user.password_confirmation = "wrong" + + assert !@user.valid? + + @user.password_confirmation = "thiswillberight" + + assert @user.valid? + end + + test "password must pass validation rules" do + @user.password = "password" + assert !@user.valid? + + @user.password = "short" + assert !@user.valid? + + @user.password = "plentylongenough" + assert @user.valid? + end + + test "authenticate" do + @user.password = "secret" + + assert !@user.authenticate("wrong") + assert @user.authenticate("secret") + end +end \ No newline at end of file diff --git a/activemodel/test/models/user.rb b/activemodel/test/models/user.rb new file mode 100644 index 0000000000..e221bb8091 --- /dev/null +++ b/activemodel/test/models/user.rb @@ -0,0 +1,8 @@ +class User + include ActiveModel::Validations + include ActiveModel::SecurePassword + + has_secure_password + + attr_accessor :password_digest, :password_salt +end -- cgit v1.2.3 From 39b5ea6e01f6fc652cc63ab4e7e701cfaa9f9405 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sat, 18 Dec 2010 15:39:32 -0800 Subject: Switch from SHA2 to BCrypt (easy Windows compatibility is coming shortly with new compiled gem) --- activemodel/CHANGELOG | 2 +- activemodel/activemodel.gemspec | 2 ++ activemodel/lib/active_model/secure_password.rb | 14 +++++++------- 3 files changed, 10 insertions(+), 8 deletions(-) (limited to 'activemodel') diff --git a/activemodel/CHANGELOG b/activemodel/CHANGELOG index a19d029217..9dd5e03685 100644 --- a/activemodel/CHANGELOG +++ b/activemodel/CHANGELOG @@ -1,6 +1,6 @@ *Rails 3.1.0 (unreleased)* -* Added ActiveModel::SecurePassword to encapsulate dead-simple password usage with SHA2 encryption and salting [DHH] +* Added ActiveModel::SecurePassword to encapsulate dead-simple password usage with BCrypt encryption and salting [DHH] *Rails 3.0.2 (unreleased)* diff --git a/activemodel/activemodel.gemspec b/activemodel/activemodel.gemspec index 1f38e70c36..64aa7ad922 100644 --- a/activemodel/activemodel.gemspec +++ b/activemodel/activemodel.gemspec @@ -22,4 +22,6 @@ Gem::Specification.new do |s| s.add_dependency('activesupport', version) s.add_dependency('builder', '~> 3.0.0') s.add_dependency('i18n', '~> 0.5.0') + s.add_dependency('bcrypt-ruby', '~> 2.1.2') + end diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 0599ce6865..900205cf3f 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -1,4 +1,4 @@ -require 'digest/sha2' +require 'bcrypt' module ActiveModel module SecurePassword @@ -44,13 +44,17 @@ module ActiveModel module InstanceMethods # Returns self if the password is correct, otherwise false. def authenticate(unencrypted_password) - password_digest == encrypt_password(unencrypted_password) ? self : false + if BCrypt::Password.new(password_digest) == (unencrypted_password + salt_for_password) + self + else + false + end end # Encrypts the password into the password_digest attribute. def password=(unencrypted_password) @password = unencrypted_password - self.password_digest = encrypt_password(unencrypted_password) + self.password_digest = BCrypt::Password.create(unencrypted_password + salt_for_password) end private @@ -58,10 +62,6 @@ module ActiveModel self.password_salt ||= self.object_id.to_s + rand.to_s end - def encrypt_password(unencrypted_password) - Digest::SHA2.hexdigest(unencrypted_password + salt_for_password) - end - def password_must_be_strong if @password.present? errors.add(:password, "must be longer than 6 characters") unless @password.size > 6 -- cgit v1.2.3 From bd9dc4ff23ab1e185df6ccf35d6058c0a3d234ce Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sat, 18 Dec 2010 19:09:07 -0800 Subject: BCrypt does its own salting, lovely! --- activemodel/lib/active_model/secure_password.rb | 23 +++++++++++------------ activemodel/test/cases/secure_password_test.rb | 11 +++++++++++ 2 files changed, 22 insertions(+), 12 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 900205cf3f..54191f41df 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -4,17 +4,19 @@ module ActiveModel module SecurePassword extend ActiveSupport::Concern + WEAK_PASSWORDS = %w( password qwerty 123456 ) + module ClassMethods - # Adds methods to set and authenticate against a SHA2-encrypted and salted password. - # This mechanism requires you to have password_digest and password_salt attributes. + # Adds methods to set and authenticate against a BCrypt password. + # This mechanism requires you to have a password_digest attribute. # # Validations for presence of password, confirmation of password (using a "password_confirmation" attribute), - # and strength of password (at least 6 chars, not "password") are automatically added. + # and strength of password (at least 6 chars, not "password", etc) are automatically added. # You can add more validations by hand if need be. # # Example using Active Record (which automatically includes ActiveModel::SecurePassword): # - # # Schema: User(name:string, password_digest:string, password_salt:string) + # # Schema: User(name:string, password_digest:string) # class User < ActiveRecord::Base # has_secure_password # end @@ -33,7 +35,7 @@ module ActiveModel attr_reader :password attr_accessor :password_confirmation - attr_protected(:password_digest, :password_salt) if respond_to?(:attr_protected) + attr_protected(:password_digest) if respond_to?(:attr_protected) validates_confirmation_of :password validates_presence_of :password_digest @@ -44,7 +46,7 @@ module ActiveModel module InstanceMethods # Returns self if the password is correct, otherwise false. def authenticate(unencrypted_password) - if BCrypt::Password.new(password_digest) == (unencrypted_password + salt_for_password) + if BCrypt::Password.new(password_digest) == unencrypted_password self else false @@ -54,18 +56,15 @@ module ActiveModel # Encrypts the password into the password_digest attribute. def password=(unencrypted_password) @password = unencrypted_password - self.password_digest = BCrypt::Password.create(unencrypted_password + salt_for_password) + self.password_digest = BCrypt::Password.create(unencrypted_password) end - private - def salt_for_password - self.password_salt ||= self.object_id.to_s + rand.to_s - end + private def password_must_be_strong if @password.present? errors.add(:password, "must be longer than 6 characters") unless @password.size > 6 - errors.add(:password, "can't be 'password'") if @password == "password" + errors.add(:password, "is a too weak and common") if WEAK_PASSWORDS.include?(@password) end end end diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb index 7d7c51e6cb..62fffce233 100644 --- a/activemodel/test/cases/secure_password_test.rb +++ b/activemodel/test/cases/secure_password_test.rb @@ -33,6 +33,17 @@ class SecurePasswordTest < ActiveModel::TestCase assert @user.valid? end + test "too weak passwords" do + @user.password = "123456" + assert !@user.valid? + + @user.password = "password" + assert !@user.valid? + + @user.password = "d9034rfjlakj34RR$!!" + assert @user.valid? + end + test "authenticate" do @user.password = "secret" -- cgit v1.2.3 From d592fa946d43fdadf23f872a5c3334fb4f108f80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 19 Dec 2010 09:28:15 +0100 Subject: Avoid warnings and fix small typo on SecurePassword. --- activemodel/lib/active_model/secure_password.rb | 6 +++--- activemodel/test/cases/secure_password_test.rb | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 54191f41df..3e7d3174ac 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -62,9 +62,9 @@ module ActiveModel private def password_must_be_strong - if @password.present? - errors.add(:password, "must be longer than 6 characters") unless @password.size > 6 - errors.add(:password, "is a too weak and common") if WEAK_PASSWORDS.include?(@password) + if password.present? + errors.add(:password, "must be longer than 6 characters") unless password.size > 6 + errors.add(:password, "is too weak and common") if WEAK_PASSWORDS.include?(password) end end end diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb index 62fffce233..c46a092d2d 100644 --- a/activemodel/test/cases/secure_password_test.rb +++ b/activemodel/test/cases/secure_password_test.rb @@ -34,11 +34,13 @@ class SecurePasswordTest < ActiveModel::TestCase end test "too weak passwords" do - @user.password = "123456" + @user.password = "012345" assert !@user.valid? + assert_equal ["must be longer than 6 characters"], @user.errors[:password] @user.password = "password" assert !@user.valid? + assert_equal ["is too weak and common"], @user.errors[:password] @user.password = "d9034rfjlakj34RR$!!" assert @user.valid? -- cgit v1.2.3 From b8f6dd8cbb2de870a4805800fd89148a417bc612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 19 Dec 2010 09:30:46 +0100 Subject: Add missing require and remove extra module. --- activemodel/lib/active_model/secure_password.rb | 39 ++++++++++++------------- 1 file changed, 19 insertions(+), 20 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 3e7d3174ac..cd6256e3d6 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -1,3 +1,4 @@ +require 'active_support/core_ext/object/blank' require 'bcrypt' module ActiveModel @@ -43,30 +44,28 @@ module ActiveModel end end - module InstanceMethods - # Returns self if the password is correct, otherwise false. - def authenticate(unencrypted_password) - if BCrypt::Password.new(password_digest) == unencrypted_password - self - else - false - end + # Returns self if the password is correct, otherwise false. + def authenticate(unencrypted_password) + if BCrypt::Password.new(password_digest) == unencrypted_password + self + else + false end + end - # Encrypts the password into the password_digest attribute. - def password=(unencrypted_password) - @password = unencrypted_password - self.password_digest = BCrypt::Password.create(unencrypted_password) - end + # Encrypts the password into the password_digest attribute. + def password=(unencrypted_password) + @password = unencrypted_password + self.password_digest = BCrypt::Password.create(unencrypted_password) + end + private - private - def password_must_be_strong - if password.present? - errors.add(:password, "must be longer than 6 characters") unless password.size > 6 - errors.add(:password, "is too weak and common") if WEAK_PASSWORDS.include?(password) - end - end + def password_must_be_strong + if password.present? + errors.add(:password, "must be longer than 6 characters") unless password.size > 6 + errors.add(:password, "is too weak and common") if WEAK_PASSWORDS.include?(password) + end end end end \ No newline at end of file -- cgit v1.2.3 From 432556b9238b182dbd380a8f9936e3ca5dc6fa57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 19 Dec 2010 09:34:31 +0100 Subject: Make password messages translatable. --- activemodel/lib/active_model/locale/en.yml | 4 ++++ activemodel/lib/active_model/secure_password.rb | 4 ++-- activemodel/test/cases/secure_password_test.rb | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/locale/en.yml b/activemodel/lib/active_model/locale/en.yml index 44425b4a28..c370a1f33e 100644 --- a/activemodel/lib/active_model/locale/en.yml +++ b/activemodel/lib/active_model/locale/en.yml @@ -25,3 +25,7 @@ en: less_than_or_equal_to: "must be less than or equal to %{count}" odd: "must be odd" even: "must be even" + + attributes: + password: + unsecure: "is too weak and common" diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index cd6256e3d6..cd997a61db 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -63,8 +63,8 @@ module ActiveModel def password_must_be_strong if password.present? - errors.add(:password, "must be longer than 6 characters") unless password.size > 6 - errors.add(:password, "is too weak and common") if WEAK_PASSWORDS.include?(password) + errors.add(:password, :too_short, :count => 7) unless password.size > 6 + errors.add(:password, :unsecure) if WEAK_PASSWORDS.include?(password) end end end diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb index c46a092d2d..e75bfca02a 100644 --- a/activemodel/test/cases/secure_password_test.rb +++ b/activemodel/test/cases/secure_password_test.rb @@ -36,7 +36,7 @@ class SecurePasswordTest < ActiveModel::TestCase test "too weak passwords" do @user.password = "012345" assert !@user.valid? - assert_equal ["must be longer than 6 characters"], @user.errors[:password] + assert_equal ["is too short (minimum is 7 characters)"], @user.errors[:password] @user.password = "password" assert !@user.valid? -- cgit v1.2.3 From 863de37b05900f037132656812b7ef550d096ae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 19 Dec 2010 09:37:08 +0100 Subject: 'unsecure' => 'insecure' --- activemodel/lib/active_model/locale/en.yml | 2 +- activemodel/lib/active_model/secure_password.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/locale/en.yml b/activemodel/lib/active_model/locale/en.yml index c370a1f33e..4a27355c6c 100644 --- a/activemodel/lib/active_model/locale/en.yml +++ b/activemodel/lib/active_model/locale/en.yml @@ -28,4 +28,4 @@ en: attributes: password: - unsecure: "is too weak and common" + insecure: "is too weak and common" diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index cd997a61db..1dcd389f8a 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -64,7 +64,7 @@ module ActiveModel def password_must_be_strong if password.present? errors.add(:password, :too_short, :count => 7) unless password.size > 6 - errors.add(:password, :unsecure) if WEAK_PASSWORDS.include?(password) + errors.add(:password, :insecure) if WEAK_PASSWORDS.include?(password) end end end -- cgit v1.2.3 From a39a3337698ca42ab158dc3b4b08ea75039b8a89 Mon Sep 17 00:00:00 2001 From: Mikel Lindsaar Date: Sun, 19 Dec 2010 20:39:54 +1100 Subject: Added ability to specify which passwords you want as weak passwords --- activemodel/lib/active_model/secure_password.rb | 31 ++++++++++++++---- activemodel/test/cases/secure_password_test.rb | 42 ++++++++++++++++++------- 2 files changed, 56 insertions(+), 17 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 1dcd389f8a..06af18dfd1 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -5,12 +5,10 @@ module ActiveModel module SecurePassword extend ActiveSupport::Concern - WEAK_PASSWORDS = %w( password qwerty 123456 ) - module ClassMethods # Adds methods to set and authenticate against a BCrypt password. # This mechanism requires you to have a password_digest attribute. - # + # # Validations for presence of password, confirmation of password (using a "password_confirmation" attribute), # and strength of password (at least 6 chars, not "password", etc) are automatically added. # You can add more validations by hand if need be. @@ -24,9 +22,9 @@ module ActiveModel # # user = User.new(:name => "david", :password => "secret", :password_confirmation => "nomatch") # user.save # => false, password not long enough - # user.password = "mUc3m00RsqyRe" + # user.password = "mUc3m00RsqyRe" # user.save # => false, confirmation doesn't match - # user.password_confirmation = "mUc3m00RsqyRe" + # user.password_confirmation = "mUc3m00RsqyRe" # user.save # => true # user.authenticate("notright") # => false # user.authenticate("mUc3m00RsqyRe") # => user @@ -42,6 +40,27 @@ module ActiveModel validates_presence_of :password_digest validate :password_must_be_strong end + + # Allows you to specify the set of weak passwords that will be validated against + # if you specify has_secure_password in your model. + # + # The default set of weak passwords are: + # + # class User < ActiveRecord::Base + # weak_passwords = %w( password qwerty 123456 mypass ) + # end + def weak_passwords=(*values) + @weak_passwords = values.flatten + end + + # Returns the list of current weak passwords defined. Defaults to the standard + # list of 'password', 'qwerty' and '123456' + # + # User.weak_passwords #=> ['password', 'qwerty', '123456'] + def weak_passwords + @weak_passwords ||= %w( password qwerty 123456 ) + end + end # Returns self if the password is correct, otherwise false. @@ -64,7 +83,7 @@ module ActiveModel def password_must_be_strong if password.present? errors.add(:password, :too_short, :count => 7) unless password.size > 6 - errors.add(:password, :insecure) if WEAK_PASSWORDS.include?(password) + errors.add(:password, :insecure) if self.class.weak_passwords.include?(password) end end end diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb index e75bfca02a..5d788d7a1e 100644 --- a/activemodel/test/cases/secure_password_test.rb +++ b/activemodel/test/cases/secure_password_test.rb @@ -2,37 +2,57 @@ require 'cases/helper' require 'models/user' class SecurePasswordTest < ActiveModel::TestCase + setup do + User.weak_passwords = %w( password qwerty 123456 ) @user = User.new end + test "there should be a list of default weak passwords" do + assert_equal %w( password qwerty 123456 ), User.weak_passwords + end + + test "specifying the list of passwords" do + User.weak_passwords = %w( pass ) + assert_equal %w( pass ), User.weak_passwords + end + + test "adding to the list of passwords" do + User.weak_passwords << 'pass' + @user.password = "password" + assert !@user.valid? + + @user.password = "pass" + assert !@user.valid? + end + test "password must be present" do assert !@user.valid? assert_equal 1, @user.errors.size end - + test "password must match confirmation" do @user.password = "thiswillberight" @user.password_confirmation = "wrong" - + assert !@user.valid? - + @user.password_confirmation = "thiswillberight" - + assert @user.valid? end - + test "password must pass validation rules" do @user.password = "password" assert !@user.valid? - + @user.password = "short" assert !@user.valid? - + @user.password = "plentylongenough" assert @user.valid? end - + test "too weak passwords" do @user.password = "012345" assert !@user.valid? @@ -41,14 +61,14 @@ class SecurePasswordTest < ActiveModel::TestCase @user.password = "password" assert !@user.valid? assert_equal ["is too weak and common"], @user.errors[:password] - + @user.password = "d9034rfjlakj34RR$!!" assert @user.valid? end - + test "authenticate" do @user.password = "secret" - + assert !@user.authenticate("wrong") assert @user.authenticate("secret") end -- cgit v1.2.3 From fa14df08a845abc61a6ed5bed1742ba59a4c2b8d Mon Sep 17 00:00:00 2001 From: Mikel Lindsaar Date: Sun, 19 Dec 2010 20:54:15 +1100 Subject: Fix incorrect docs --- activemodel/lib/active_model/secure_password.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 06af18dfd1..c5f32fa3fa 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -42,9 +42,7 @@ module ActiveModel end # Allows you to specify the set of weak passwords that will be validated against - # if you specify has_secure_password in your model. - # - # The default set of weak passwords are: + # if you specify has_secure_password in your model: # # class User < ActiveRecord::Base # weak_passwords = %w( password qwerty 123456 mypass ) -- cgit v1.2.3 From 6c217f98db6984c8d000103ac3cf66970eeaeb3f Mon Sep 17 00:00:00 2001 From: Mikel Lindsaar Date: Sun, 19 Dec 2010 21:28:37 +1100 Subject: Add set_weak_passwords call in alignment with set_table_name. --- activemodel/lib/active_model/secure_password.rb | 16 +++++++++++----- activemodel/test/cases/secure_password_test.rb | 5 +++++ 2 files changed, 16 insertions(+), 5 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index c5f32fa3fa..6703d5daac 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -41,12 +41,18 @@ module ActiveModel validate :password_must_be_strong end - # Allows you to specify the set of weak passwords that will be validated against - # if you specify has_secure_password in your model: + # Specify the weak passwords to be used in the model: # - # class User < ActiveRecord::Base - # weak_passwords = %w( password qwerty 123456 mypass ) + # class User + # weak_passwords %w( password qwerty 123456 mypass ) # end + def set_weak_passwords(values) + @weak_passwords = values + end + + # Change the list of weak passwords that will be validated against: + # + # User.weak_passwords = %w( password qwerty 123456 mypass ) def weak_passwords=(*values) @weak_passwords = values.flatten end @@ -55,7 +61,7 @@ module ActiveModel # list of 'password', 'qwerty' and '123456' # # User.weak_passwords #=> ['password', 'qwerty', '123456'] - def weak_passwords + def weak_passwords(values = nil) @weak_passwords ||= %w( password qwerty 123456 ) end diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb index 5d788d7a1e..2c3da2c93e 100644 --- a/activemodel/test/cases/secure_password_test.rb +++ b/activemodel/test/cases/secure_password_test.rb @@ -17,6 +17,11 @@ class SecurePasswordTest < ActiveModel::TestCase assert_equal %w( pass ), User.weak_passwords end + test "specifying the list of passwords in the class" do + User.send(:set_weak_passwords, ['pass']) + assert_equal %w( pass ), User.weak_passwords + end + test "adding to the list of passwords" do User.weak_passwords << 'pass' @user.password = "password" -- cgit v1.2.3 From 6d80f3a1ba52248cb8837af5ad40a23dc4fea213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 19 Dec 2010 13:30:19 +0100 Subject: Use class_attribute to ensure weak_passwords are inheritable. --- activemodel/lib/active_model/secure_password.rb | 26 ++++++++----------------- 1 file changed, 8 insertions(+), 18 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 6703d5daac..8da08f34ec 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -1,10 +1,16 @@ require 'active_support/core_ext/object/blank' +require 'active_support/core_ext/class/attribute' require 'bcrypt' module ActiveModel module SecurePassword extend ActiveSupport::Concern + included do + class_attribute :weak_passwords + self.weak_passwords = %w( password qwerty 123456 ) + end + module ClassMethods # Adds methods to set and authenticate against a BCrypt password. # This mechanism requires you to have a password_digest attribute. @@ -44,27 +50,11 @@ module ActiveModel # Specify the weak passwords to be used in the model: # # class User - # weak_passwords %w( password qwerty 123456 mypass ) + # set_weak_passwords %w( password qwerty 123456 mypass ) # end def set_weak_passwords(values) - @weak_passwords = values - end - - # Change the list of weak passwords that will be validated against: - # - # User.weak_passwords = %w( password qwerty 123456 mypass ) - def weak_passwords=(*values) - @weak_passwords = values.flatten + self.weak_passwords = values end - - # Returns the list of current weak passwords defined. Defaults to the standard - # list of 'password', 'qwerty' and '123456' - # - # User.weak_passwords #=> ['password', 'qwerty', '123456'] - def weak_passwords(values = nil) - @weak_passwords ||= %w( password qwerty 123456 ) - end - end # Returns self if the password is correct, otherwise false. -- cgit v1.2.3 From 08ccd29b5b1e3badc2176a8036fea138b774c38f Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 19 Dec 2010 14:58:14 -0200 Subject: Remove weak_passwords list and the length/strong password validator, leave that up to the programmer --- activemodel/lib/active_model/locale/en.yml | 4 -- activemodel/lib/active_model/secure_password.rb | 37 +++--------------- activemodel/test/cases/secure_password_test.rb | 50 +------------------------ 3 files changed, 7 insertions(+), 84 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/locale/en.yml b/activemodel/lib/active_model/locale/en.yml index 4a27355c6c..44425b4a28 100644 --- a/activemodel/lib/active_model/locale/en.yml +++ b/activemodel/lib/active_model/locale/en.yml @@ -25,7 +25,3 @@ en: less_than_or_equal_to: "must be less than or equal to %{count}" odd: "must be odd" even: "must be even" - - attributes: - password: - insecure: "is too weak and common" diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 8da08f34ec..f4411cde80 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -1,22 +1,16 @@ -require 'active_support/core_ext/object/blank' -require 'active_support/core_ext/class/attribute' +require 'active_support/concern' require 'bcrypt' module ActiveModel module SecurePassword extend ActiveSupport::Concern - included do - class_attribute :weak_passwords - self.weak_passwords = %w( password qwerty 123456 ) - end - module ClassMethods # Adds methods to set and authenticate against a BCrypt password. # This mechanism requires you to have a password_digest attribute. # - # Validations for presence of password, confirmation of password (using a "password_confirmation" attribute), - # and strength of password (at least 6 chars, not "password", etc) are automatically added. + # Validations for presence of password, confirmation of password (using + # a "password_confirmation" attribute) are automatically added. # You can add more validations by hand if need be. # # Example using Active Record (which automatically includes ActiveModel::SecurePassword): @@ -26,8 +20,8 @@ module ActiveModel # has_secure_password # end # - # user = User.new(:name => "david", :password => "secret", :password_confirmation => "nomatch") - # user.save # => false, password not long enough + # user = User.new(:name => "david", :password => "", :password_confirmation => "nomatch") + # user.save # => false, password required # user.password = "mUc3m00RsqyRe" # user.save # => false, confirmation doesn't match # user.password_confirmation = "mUc3m00RsqyRe" @@ -44,16 +38,6 @@ module ActiveModel validates_confirmation_of :password validates_presence_of :password_digest - validate :password_must_be_strong - end - - # Specify the weak passwords to be used in the model: - # - # class User - # set_weak_passwords %w( password qwerty 123456 mypass ) - # end - def set_weak_passwords(values) - self.weak_passwords = values end end @@ -71,14 +55,5 @@ module ActiveModel @password = unencrypted_password self.password_digest = BCrypt::Password.create(unencrypted_password) end - - private - - def password_must_be_strong - if password.present? - errors.add(:password, :too_short, :count => 7) unless password.size > 6 - errors.add(:password, :insecure) if self.class.weak_passwords.include?(password) - end - end end -end \ No newline at end of file +end diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb index 2c3da2c93e..79be715730 100644 --- a/activemodel/test/cases/secure_password_test.rb +++ b/activemodel/test/cases/secure_password_test.rb @@ -4,33 +4,9 @@ require 'models/user' class SecurePasswordTest < ActiveModel::TestCase setup do - User.weak_passwords = %w( password qwerty 123456 ) @user = User.new end - test "there should be a list of default weak passwords" do - assert_equal %w( password qwerty 123456 ), User.weak_passwords - end - - test "specifying the list of passwords" do - User.weak_passwords = %w( pass ) - assert_equal %w( pass ), User.weak_passwords - end - - test "specifying the list of passwords in the class" do - User.send(:set_weak_passwords, ['pass']) - assert_equal %w( pass ), User.weak_passwords - end - - test "adding to the list of passwords" do - User.weak_passwords << 'pass' - @user.password = "password" - assert !@user.valid? - - @user.password = "pass" - assert !@user.valid? - end - test "password must be present" do assert !@user.valid? assert_equal 1, @user.errors.size @@ -47,34 +23,10 @@ class SecurePasswordTest < ActiveModel::TestCase assert @user.valid? end - test "password must pass validation rules" do - @user.password = "password" - assert !@user.valid? - - @user.password = "short" - assert !@user.valid? - - @user.password = "plentylongenough" - assert @user.valid? - end - - test "too weak passwords" do - @user.password = "012345" - assert !@user.valid? - assert_equal ["is too short (minimum is 7 characters)"], @user.errors[:password] - - @user.password = "password" - assert !@user.valid? - assert_equal ["is too weak and common"], @user.errors[:password] - - @user.password = "d9034rfjlakj34RR$!!" - assert @user.valid? - end - test "authenticate" do @user.password = "secret" assert !@user.authenticate("wrong") assert @user.authenticate("secret") end -end \ No newline at end of file +end -- cgit v1.2.3 From 5fb42ac478923c78ebc3e457acea4dc6726e3796 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 19 Dec 2010 18:50:18 -0200 Subject: Tests and docs which explain the use of validate with a block and without arguments --- activemodel/lib/active_model/validations.rb | 12 +++++++++++- activemodel/test/cases/validations_test.rb | 10 +++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb index b044caa8d3..6cb015a144 100644 --- a/activemodel/lib/active_model/validations.rb +++ b/activemodel/lib/active_model/validations.rb @@ -104,7 +104,7 @@ module ActiveModel # end # end # - # Or with a block which is passed with the current record to be validated: + # With a block which is passed with the current record to be validated: # # class Comment # include ActiveModel::Validations @@ -118,6 +118,16 @@ module ActiveModel # end # end # + # Or with a block where self points to the current record to be validated: + # + # class Comment + # include ActiveModel::Validations + # + # validate do + # errors.add(:base, "Must be friends to leave a comment") unless commenter.friend_of?(commentee) + # end + # end + # def validate(*args, &block) options = args.extract_options! if options.key?(:on) diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb index 55b477dd10..e90dc7d4e3 100644 --- a/activemodel/test/cases/validations_test.rb +++ b/activemodel/test/cases/validations_test.rb @@ -148,6 +148,14 @@ class ValidationsTest < ActiveModel::TestCase end def test_validate_block + Topic.validate { errors.add("title", "will never be valid") } + t = Topic.new("title" => "Title", "content" => "whatever") + assert t.invalid? + assert t.errors[:title].any? + assert_equal ["will never be valid"], t.errors["title"] + end + + def test_validate_block_with_params Topic.validate { |topic| topic.errors.add("title", "will never be valid") } t = Topic.new("title" => "Title", "content" => "whatever") assert t.invalid? @@ -187,7 +195,7 @@ class ValidationsTest < ActiveModel::TestCase assert t.invalid? assert_equal "can't be blank", t.errors["title"].first Topic.validates_presence_of :title, :author_name - Topic.validate {|topic| topic.errors.add('author_email_address', 'will never be valid')} + Topic.validate {errors.add('author_email_address', 'will never be valid')} Topic.validates_length_of :title, :content, :minimum => 2 t = Topic.new :title => '' -- cgit v1.2.3 From ab2bde45f8140a3ebf8b478e688ef612fc4181fc Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 19 Dec 2010 19:15:26 -0200 Subject: Remove require AS::Concern from places where is already in --- activemodel/lib/active_model/dirty.rb | 1 - activemodel/lib/active_model/secure_password.rb | 1 - 2 files changed, 2 deletions(-) (limited to 'activemodel') diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb index 1dfd0b6132..a479795d51 100644 --- a/activemodel/lib/active_model/dirty.rb +++ b/activemodel/lib/active_model/dirty.rb @@ -1,5 +1,4 @@ require 'active_model/attribute_methods' -require 'active_support/concern' require 'active_support/hash_with_indifferent_access' require 'active_support/core_ext/object/duplicable' diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index f4411cde80..52941942b8 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -1,4 +1,3 @@ -require 'active_support/concern' require 'bcrypt' module ActiveModel -- cgit v1.2.3