256 lines
9.8 KiB
Diff
256 lines
9.8 KiB
Diff
diff -Nur a/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb b/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb
|
|
--- a/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb 2022-03-02 17:38:38.274324841 +0800
|
|
+++ b/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb 2022-03-02 17:48:15.220014811 +0800
|
|
@@ -562,7 +562,8 @@
|
|
|
|
def has_been_qualified?
|
|
@submatchers.any? do |submatcher|
|
|
- submatcher.class.parent == NumericalityMatchers
|
|
+ Shoulda::Matchers::RailsShim.parent_of(submatcher.class) ==
|
|
+ NumericalityMatchers
|
|
end
|
|
end
|
|
|
|
diff -Nur a/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb b/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb
|
|
--- a/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb 2022-03-02 17:38:38.274324841 +0800
|
|
+++ b/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb 2022-03-02 17:48:15.220014811 +0800
|
|
@@ -134,9 +134,8 @@
|
|
private
|
|
|
|
def secure_password_being_validated?
|
|
- defined?(::ActiveModel::SecurePassword) &&
|
|
- @subject.class.ancestors.include?(::ActiveModel::SecurePassword::InstanceMethodsOnActivation) &&
|
|
- @attribute == :password
|
|
+ Shoulda::Matchers::RailsShim.digestible_attributes_in(@subject).
|
|
+ include?(@attribute)
|
|
end
|
|
|
|
def disallows_and_double_checks_value_of!(value, message)
|
|
diff -Nur a/lib/shoulda/matchers/active_record/association_matcher.rb b/lib/shoulda/matchers/active_record/association_matcher.rb
|
|
--- a/lib/shoulda/matchers/active_record/association_matcher.rb 2022-03-02 17:38:38.278324908 +0800
|
|
+++ b/lib/shoulda/matchers/active_record/association_matcher.rb 2022-03-02 17:57:16.205124877 +0800
|
|
@@ -1182,13 +1182,11 @@
|
|
def class_has_foreign_key?(klass)
|
|
if options.key?(:foreign_key)
|
|
option_verifier.correct_for_string?(:foreign_key, options[:foreign_key])
|
|
+ elsif column_names_for(klass).include?(foreign_key)
|
|
+ true
|
|
else
|
|
- if klass.column_names.include?(foreign_key)
|
|
- true
|
|
- else
|
|
- @missing = "#{klass} does not have a #{foreign_key} foreign key."
|
|
- false
|
|
- end
|
|
+ @missing = "#{klass} does not have a #{foreign_key} foreign key."
|
|
+ false
|
|
end
|
|
end
|
|
|
|
@@ -1226,6 +1224,11 @@
|
|
def submatchers_match?
|
|
failing_submatchers.empty?
|
|
end
|
|
+ def column_names_for(klass)
|
|
+ klass.column_names
|
|
+ rescue ::ActiveRecord::StatementInvalid
|
|
+ []
|
|
+ end
|
|
end
|
|
end
|
|
end
|
|
diff -Nur a/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb b/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb
|
|
--- a/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb 2022-03-02 17:38:38.278324908 +0800
|
|
+++ b/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb 2022-03-02 17:48:15.224014878 +0800
|
|
@@ -502,10 +502,11 @@
|
|
end
|
|
|
|
def ensure_secure_password_set(instance)
|
|
- if has_secure_password?
|
|
- instance.password = "password"
|
|
- instance.password_confirmation = "password"
|
|
- end
|
|
+ Shoulda::Matchers::RailsShim.digestible_attributes_in(instance).
|
|
+ each do |attribute|
|
|
+ instance.send("#{attribute}=", 'password')
|
|
+ instance.send("#{attribute}_confirmation=", 'password')
|
|
+ end
|
|
end
|
|
|
|
def update_existing_record!(value)
|
|
@@ -529,9 +530,7 @@
|
|
end
|
|
|
|
def has_secure_password?
|
|
- model.ancestors.map(&:to_s).include?(
|
|
- 'ActiveModel::SecurePassword::InstanceMethodsOnActivation'
|
|
- )
|
|
+ Shoulda::Matchers::RailsShim.has_secure_password?(subject, @attribute)
|
|
end
|
|
|
|
def build_new_record
|
|
diff -Nur a/lib/shoulda/matchers/rails_shim.rb b/lib/shoulda/matchers/rails_shim.rb
|
|
--- a/lib/shoulda/matchers/rails_shim.rb 2022-03-02 17:38:38.274324841 +0800
|
|
+++ b/lib/shoulda/matchers/rails_shim.rb 2022-03-02 17:48:15.224014878 +0800
|
|
@@ -107,6 +107,43 @@
|
|
end
|
|
end
|
|
|
|
+ def parent_of(mod)
|
|
+ if mod.respond_to?(:module_parent)
|
|
+ mod.module_parent
|
|
+ else
|
|
+ mod.parent
|
|
+ end
|
|
+ end
|
|
+
|
|
+ def has_secure_password?(record, attribute_name)
|
|
+ if secure_password_module
|
|
+ attribute_name == :password &&
|
|
+ record.class.ancestors.include?(secure_password_module)
|
|
+ else
|
|
+ record.respond_to?("authenticate_#{attribute_name}")
|
|
+ end
|
|
+ end
|
|
+
|
|
+ def digestible_attributes_in(record)
|
|
+ record.methods.inject([]) do |array, method_name|
|
|
+ match = method_name.to_s.match(
|
|
+ /\A(\w+)_(?:confirmation|digest)=\Z/,
|
|
+ )
|
|
+
|
|
+ if match
|
|
+ array.concat([match[1].to_sym])
|
|
+ else
|
|
+ array
|
|
+ end
|
|
+ end
|
|
+ end
|
|
+
|
|
+ def secure_password_module
|
|
+ ::ActiveModel::SecurePassword::InstanceMethodsOnActivation
|
|
+ rescue NameError
|
|
+ nil
|
|
+ end
|
|
+
|
|
private
|
|
|
|
def simply_generate_validation_message(
|
|
diff -Nur a/spec/support/unit/helpers/class_builder.rb b/spec/support/unit/helpers/class_builder.rb
|
|
--- a/spec/support/unit/helpers/class_builder.rb 2022-03-02 17:38:38.270324774 +0800
|
|
+++ b/spec/support/unit/helpers/class_builder.rb 2022-03-02 17:48:15.224014878 +0800
|
|
@@ -18,18 +18,15 @@
|
|
end
|
|
|
|
def reset
|
|
- remove_defined_classes
|
|
+ remove_defined_modules
|
|
+ defined_modules.clear
|
|
end
|
|
|
|
def define_module(module_name, &block)
|
|
module_name = module_name.to_s.camelize
|
|
+ namespace, name_without_namespace = parse_constant_name(module_name)
|
|
|
|
- namespace, name_without_namespace =
|
|
- ClassBuilder.parse_constant_name(module_name)
|
|
-
|
|
- if namespace.const_defined?(name_without_namespace, false)
|
|
- namespace.__send__(:remove_const, name_without_namespace)
|
|
- end
|
|
+ remove_defined_module(module_name)
|
|
|
|
eval <<-RUBY
|
|
module #{namespace}::#{name_without_namespace}
|
|
@@ -38,6 +35,7 @@
|
|
|
|
namespace.const_get(name_without_namespace).tap do |constant|
|
|
constant.unloadable
|
|
+ @_defined_modules = defined_modules | [constant]
|
|
|
|
if block
|
|
constant.module_eval(&block)
|
|
@@ -47,13 +45,9 @@
|
|
|
|
def define_class(class_name, parent_class = Object, &block)
|
|
class_name = class_name.to_s.camelize
|
|
+ namespace, name_without_namespace = parse_constant_name(class_name)
|
|
|
|
- namespace, name_without_namespace =
|
|
- ClassBuilder.parse_constant_name(class_name)
|
|
-
|
|
- if namespace.const_defined?(name_without_namespace, false)
|
|
- namespace.__send__(:remove_const, name_without_namespace)
|
|
- end
|
|
+ remove_defined_module(class_name)
|
|
|
|
eval <<-RUBY
|
|
class #{namespace}::#{name_without_namespace} < ::#{parent_class}
|
|
@@ -62,6 +56,7 @@
|
|
|
|
namespace.const_get(name_without_namespace).tap do |constant|
|
|
constant.unloadable
|
|
+ @_defined_modules = defined_modules | [constant]
|
|
|
|
if block
|
|
if block.arity == 0
|
|
@@ -82,8 +77,21 @@
|
|
|
|
private
|
|
|
|
- def remove_defined_classes
|
|
- ::ActiveSupport::Dependencies.clear
|
|
+ def remove_defined_modules
|
|
+ defined_modules.reverse_each { |mod| remove_defined_module(mod.name) }
|
|
+ ActiveSupport::Dependencies.clear
|
|
+ end
|
|
+
|
|
+ def remove_defined_module(module_name)
|
|
+ namespace, name_without_namespace = parse_constant_name(module_name)
|
|
+
|
|
+ if namespace.const_defined?(name_without_namespace, false)
|
|
+ namespace.__send__(:remove_const, name_without_namespace)
|
|
+ end
|
|
+ end
|
|
+
|
|
+ def defined_modules
|
|
+ @_defined_modules ||= []
|
|
end
|
|
end
|
|
end
|
|
diff -Nur a/spec/unit/shoulda/matchers/active_model/have_secure_password_matcher_spec.rb b/spec/unit/shoulda/matchers/active_model/have_secure_password_matcher_spec.rb
|
|
--- a/spec/unit/shoulda/matchers/active_model/have_secure_password_matcher_spec.rb 2022-03-02 17:38:38.262324640 +0800
|
|
+++ b/spec/unit/shoulda/matchers/active_model/have_secure_password_matcher_spec.rb 2022-03-02 18:04:41.336620792 +0800
|
|
@@ -1,20 +1,18 @@
|
|
require 'unit_spec_helper'
|
|
|
|
describe Shoulda::Matchers::ActiveModel::HaveSecurePasswordMatcher, type: :model do
|
|
- if active_model_3_1?
|
|
- it 'matches when the subject configures has_secure_password with default options' do
|
|
- working_model = define_model(:example, password_digest: :string) { has_secure_password }
|
|
- expect(working_model.new).to have_secure_password
|
|
- end
|
|
+ it 'matches when the subject configures has_secure_password with default options' do
|
|
+ working_model = define_model(:example, password_digest: :string) { has_secure_password }
|
|
+ expect(working_model.new).to have_secure_password
|
|
+ end
|
|
|
|
- it 'does not match when the subject does not authenticate a password' do
|
|
- no_secure_password = define_model(:example)
|
|
- expect(no_secure_password.new).not_to have_secure_password
|
|
- end
|
|
+ it 'does not match when the subject does not authenticate a password' do
|
|
+ no_secure_password = define_model(:example)
|
|
+ expect(no_secure_password.new).not_to have_secure_password
|
|
+ end
|
|
|
|
- it 'does not match when the subject is missing the password_digest attribute' do
|
|
- no_digest_column = define_model(:example) { has_secure_password }
|
|
- expect(no_digest_column.new).not_to have_secure_password
|
|
- end
|
|
+ it 'does not match when the subject is missing the password_digest attribute' do
|
|
+ no_digest_column = define_model(:example) { has_secure_password }
|
|
+ expect(no_digest_column.new).not_to have_secure_password
|
|
end
|
|
end
|