shoulda-matchers 4.2.0 → 4.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/{MIT-LICENSE → LICENSE} +1 -1
- data/README.md +167 -85
- data/lib/shoulda/matchers/action_controller/callback_matcher.rb +4 -2
- data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +3 -2
- data/lib/shoulda/matchers/action_controller/permit_matcher.rb +26 -21
- data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +6 -8
- data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +6 -8
- data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +16 -13
- data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +2 -1
- data/lib/shoulda/matchers/action_controller/route_matcher.rb +5 -6
- data/lib/shoulda/matchers/action_controller/route_params.rb +1 -1
- data/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +19 -13
- data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +18 -16
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +29 -27
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_changed_value_error.rb +1 -1
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter.rb +5 -5
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator.rb +2 -2
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters.rb +1 -1
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters_and_validators.rb +1 -1
- data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +51 -25
- data/lib/shoulda/matchers/active_model/helpers.rb +1 -1
- data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +32 -30
- data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_model/qualifiers/ignoring_interference_by_writer.rb +1 -1
- data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +9 -1
- data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +2 -2
- data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +9 -8
- data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +28 -46
- data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +33 -9
- data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +71 -26
- data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +2 -2
- data/lib/shoulda/matchers/active_model/validation_matcher.rb +31 -6
- data/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +2 -4
- data/lib/shoulda/matchers/active_model/validation_message_finder.rb +2 -4
- data/lib/shoulda/matchers/active_model/validator.rb +3 -3
- data/lib/shoulda/matchers/active_record.rb +26 -23
- data/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +6 -3
- data/lib/shoulda/matchers/active_record/association_matcher.rb +118 -47
- data/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +5 -2
- data/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +4 -4
- data/lib/shoulda/matchers/active_record/association_matchers/inverse_of_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +11 -6
- data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +14 -15
- data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +30 -8
- data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +23 -5
- data/lib/shoulda/matchers/active_record/association_matchers/optional_matcher.rb +3 -3
- data/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +3 -3
- data/lib/shoulda/matchers/active_record/association_matchers/source_matcher.rb +3 -2
- data/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb +7 -5
- data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +8 -8
- data/lib/shoulda/matchers/active_record/have_attached_matcher.rb +185 -0
- data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +39 -17
- data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_record/have_implicit_order_column.rb +106 -0
- data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +12 -10
- data/lib/shoulda/matchers/active_record/have_rich_text_matcher.rb +83 -0
- data/lib/shoulda/matchers/active_record/have_secure_token_matcher.rb +30 -9
- data/lib/shoulda/matchers/active_record/serialize_matcher.rb +13 -9
- data/lib/shoulda/matchers/active_record/uniqueness.rb +1 -1
- data/lib/shoulda/matchers/active_record/uniqueness/test_model_creator.rb +1 -3
- data/lib/shoulda/matchers/active_record/uniqueness/test_models.rb +0 -2
- data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +80 -73
- data/lib/shoulda/matchers/doublespeak.rb +2 -1
- data/lib/shoulda/matchers/doublespeak/double.rb +1 -1
- data/lib/shoulda/matchers/doublespeak/double_collection.rb +3 -3
- data/lib/shoulda/matchers/doublespeak/double_implementation_registry.rb +8 -5
- data/lib/shoulda/matchers/doublespeak/object_double.rb +1 -1
- data/lib/shoulda/matchers/doublespeak/stub_implementation.rb +1 -5
- data/lib/shoulda/matchers/doublespeak/world.rb +2 -2
- data/lib/shoulda/matchers/error.rb +1 -1
- data/lib/shoulda/matchers/independent.rb +0 -1
- data/lib/shoulda/matchers/independent/delegate_method_matcher.rb +14 -13
- data/lib/shoulda/matchers/integrations/configuration.rb +1 -1
- data/lib/shoulda/matchers/integrations/libraries/action_controller.rb +1 -1
- data/lib/shoulda/matchers/integrations/libraries/rails.rb +2 -2
- data/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb +1 -1
- data/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb +1 -1
- data/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb +1 -1
- data/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb +1 -1
- data/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb +1 -1
- data/lib/shoulda/matchers/rails_shim.rb +9 -3
- data/lib/shoulda/matchers/util.rb +16 -4
- data/lib/shoulda/matchers/util/word_wrap.rb +8 -8
- data/lib/shoulda/matchers/version.rb +1 -1
- data/lib/shoulda/matchers/warn.rb +3 -3
- data/shoulda-matchers.gemspec +10 -7
- metadata +11 -9
- data/lib/shoulda/matchers/independent/delegate_method_matcher/stubbed_target.rb +0 -37
@@ -80,7 +80,7 @@ module Shoulda
|
|
80
80
|
#
|
81
81
|
# RSpec.describe Post, type: :model do
|
82
82
|
# describe "validations" do
|
83
|
-
# subject { Post.
|
83
|
+
# subject { Post.new(content: "Here is the content") }
|
84
84
|
# it { should validate_uniqueness_of(:title) }
|
85
85
|
# end
|
86
86
|
# end
|
@@ -92,7 +92,7 @@ module Shoulda
|
|
92
92
|
#
|
93
93
|
# RSpec.describe Post, type: :model do
|
94
94
|
# describe "validations" do
|
95
|
-
# subject { FactoryBot.
|
95
|
+
# subject { FactoryBot.build(:post) }
|
96
96
|
# it { should validate_uniqueness_of(:title) }
|
97
97
|
# end
|
98
98
|
# end
|
@@ -157,6 +157,12 @@ module Shoulda
|
|
157
157
|
# should validate_uniqueness_of(:slug).scoped_to(:journal_id)
|
158
158
|
# end
|
159
159
|
#
|
160
|
+
# NOTE: Support for testing uniqueness validation scoped to an array of
|
161
|
+
# associations is not available.
|
162
|
+
#
|
163
|
+
# For more information, please refer to
|
164
|
+
# https://github.com/thoughtbot/shoulda-matchers/issues/814
|
165
|
+
#
|
160
166
|
# ##### case_insensitive
|
161
167
|
#
|
162
168
|
# Use `case_insensitive` to test usage of the `:case_sensitive` option
|
@@ -264,14 +270,14 @@ module Shoulda
|
|
264
270
|
super(attribute)
|
265
271
|
@expected_message = :taken
|
266
272
|
@options = {
|
267
|
-
case_sensitivity_strategy: :sensitive
|
273
|
+
case_sensitivity_strategy: :sensitive,
|
268
274
|
}
|
269
275
|
@existing_record_created = false
|
270
276
|
@failure_reason = nil
|
271
277
|
@failure_reason_when_negated = nil
|
272
278
|
@attribute_setters = {
|
273
279
|
existing_record: AttributeSetters.new,
|
274
|
-
new_record: AttributeSetters.new
|
280
|
+
new_record: AttributeSetters.new,
|
275
281
|
}
|
276
282
|
end
|
277
283
|
|
@@ -396,7 +402,7 @@ module Shoulda
|
|
396
402
|
end
|
397
403
|
|
398
404
|
def validations
|
399
|
-
model.
|
405
|
+
model.validators_on(@attribute).select do |validator|
|
400
406
|
validator.is_a?(::ActiveRecord::Validations::UniquenessValidator)
|
401
407
|
end
|
402
408
|
end
|
@@ -407,11 +413,12 @@ module Shoulda
|
|
407
413
|
else
|
408
414
|
@failure_reason = 'Expected the validation '
|
409
415
|
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
416
|
+
@failure_reason <<
|
417
|
+
if expected_scopes.empty?
|
418
|
+
'not to be scoped to anything, '
|
419
|
+
else
|
420
|
+
"to be scoped to #{inspected_expected_scopes}, "
|
421
|
+
end
|
415
422
|
|
416
423
|
if actual_sets_of_scopes.any?
|
417
424
|
@failure_reason << 'but it was scoped to '
|
@@ -455,7 +462,7 @@ module Shoulda
|
|
455
462
|
def inspected_actual_scopes
|
456
463
|
inspected_actual_sets_of_scopes.to_sentence(
|
457
464
|
words_connector: ' and ',
|
458
|
-
last_word_connector: ', and'
|
465
|
+
last_word_connector: ', and',
|
459
466
|
)
|
460
467
|
end
|
461
468
|
|
@@ -491,7 +498,9 @@ module Shoulda
|
|
491
498
|
def does_not_match_allow_nil?
|
492
499
|
expects_to_allow_nil? && (
|
493
500
|
update_existing_record!(nil) &&
|
494
|
-
(@failure_reason = nil ||
|
501
|
+
(@failure_reason = nil ||
|
502
|
+
disallows_value_of(nil, @expected_message)
|
503
|
+
)
|
495
504
|
)
|
496
505
|
end
|
497
506
|
|
@@ -527,21 +536,15 @@ module Shoulda
|
|
527
536
|
end
|
528
537
|
|
529
538
|
def find_existing_record
|
530
|
-
|
531
|
-
|
532
|
-
if record.present?
|
533
|
-
record
|
534
|
-
else
|
535
|
-
nil
|
536
|
-
end
|
539
|
+
model.first.presence
|
537
540
|
end
|
538
541
|
|
539
542
|
def create_existing_record
|
540
543
|
@given_record.tap do |existing_record|
|
541
544
|
existing_record.save(validate: false)
|
542
545
|
end
|
543
|
-
rescue ::ActiveRecord::StatementInvalid =>
|
544
|
-
raise ExistingRecordInvalid.create(underlying_exception:
|
546
|
+
rescue ::ActiveRecord::StatementInvalid => e
|
547
|
+
raise ExistingRecordInvalid.create(underlying_exception: e)
|
545
548
|
end
|
546
549
|
|
547
550
|
def update_existing_record!(value)
|
@@ -577,7 +580,7 @@ module Shoulda
|
|
577
580
|
attribute_names_under_test.each do |attribute_name|
|
578
581
|
set_attribute_on_new_record!(
|
579
582
|
attribute_name,
|
580
|
-
existing_record.public_send(attribute_name)
|
583
|
+
existing_record.public_send(attribute_name),
|
581
584
|
)
|
582
585
|
end
|
583
586
|
|
@@ -619,11 +622,12 @@ module Shoulda
|
|
619
622
|
|
620
623
|
reason << inspected_scopes.to_sentence
|
621
624
|
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
625
|
+
reason <<
|
626
|
+
if inspected_scopes.many?
|
627
|
+
' do not seem to be attributes'
|
628
|
+
else
|
629
|
+
' does not seem to be an attribute'
|
630
|
+
end
|
627
631
|
|
628
632
|
reason << " on #{model.name}."
|
629
633
|
|
@@ -643,11 +647,12 @@ module Shoulda
|
|
643
647
|
|
644
648
|
reason << inspected_scopes.to_sentence
|
645
649
|
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
650
|
+
reason <<
|
651
|
+
if inspected_scopes.many?
|
652
|
+
' seem to be attributes'
|
653
|
+
else
|
654
|
+
' seems to be an attribute'
|
655
|
+
end
|
651
656
|
|
652
657
|
reason << " on #{model.name}."
|
653
658
|
|
@@ -658,14 +663,14 @@ module Shoulda
|
|
658
663
|
end
|
659
664
|
|
660
665
|
def scopes_present_on_model
|
661
|
-
@
|
666
|
+
@_scopes_present_on_model ||= expected_scopes.select do |scope|
|
662
667
|
model.method_defined?("#{scope}=")
|
663
668
|
end
|
664
669
|
end
|
665
670
|
|
666
671
|
def scopes_missing_on_model
|
667
|
-
@
|
668
|
-
|
672
|
+
@_scopes_missing_on_model ||= expected_scopes.reject do |scope|
|
673
|
+
model.method_defined?("#{scope}=")
|
669
674
|
end
|
670
675
|
end
|
671
676
|
|
@@ -697,7 +702,7 @@ module Shoulda
|
|
697
702
|
raise NonCaseSwappableValueError.create(
|
698
703
|
model: model,
|
699
704
|
attribute: @attribute,
|
700
|
-
value: value
|
705
|
+
value: value,
|
701
706
|
)
|
702
707
|
end
|
703
708
|
|
@@ -724,7 +729,7 @@ module Shoulda
|
|
724
729
|
raise NonCaseSwappableValueError.create(
|
725
730
|
model: model,
|
726
731
|
attribute: @attribute,
|
727
|
-
value: value
|
732
|
+
value: value,
|
728
733
|
)
|
729
734
|
end
|
730
735
|
|
@@ -789,7 +794,7 @@ module Shoulda
|
|
789
794
|
column = column_for(scope)
|
790
795
|
|
791
796
|
if column.respond_to?(:array) && column.array
|
792
|
-
[
|
797
|
+
[dummy_scalar_value_for(column)]
|
793
798
|
else
|
794
799
|
dummy_scalar_value_for(column)
|
795
800
|
end
|
@@ -801,7 +806,7 @@ module Shoulda
|
|
801
806
|
|
802
807
|
def next_value_for(scope, previous_value)
|
803
808
|
if previous_value.is_a?(Array)
|
804
|
-
[
|
809
|
+
[next_scalar_value_for(scope, previous_value[0])]
|
805
810
|
else
|
806
811
|
next_scalar_value_for(scope, previous_value)
|
807
812
|
end
|
@@ -820,7 +825,7 @@ module Shoulda
|
|
820
825
|
elsif previous_value.respond_to?(:next)
|
821
826
|
previous_value.next
|
822
827
|
elsif previous_value.respond_to?(:to_datetime)
|
823
|
-
previous_value.to_datetime.next
|
828
|
+
previous_value.to_datetime.in(60).next
|
824
829
|
elsif boolean_value?(previous_value)
|
825
830
|
!previous_value
|
826
831
|
else
|
@@ -857,7 +862,7 @@ module Shoulda
|
|
857
862
|
attribute_setter = build_attribute_setter(
|
858
863
|
record,
|
859
864
|
attribute_name,
|
860
|
-
value
|
865
|
+
value,
|
861
866
|
)
|
862
867
|
attribute_setter.set!
|
863
868
|
|
@@ -869,7 +874,7 @@ module Shoulda
|
|
869
874
|
:existing_record,
|
870
875
|
existing_record,
|
871
876
|
attribute_name,
|
872
|
-
value
|
877
|
+
value,
|
873
878
|
)
|
874
879
|
end
|
875
880
|
|
@@ -878,7 +883,7 @@ module Shoulda
|
|
878
883
|
:new_record,
|
879
884
|
new_record,
|
880
885
|
attribute_name,
|
881
|
-
value
|
886
|
+
value,
|
882
887
|
)
|
883
888
|
end
|
884
889
|
|
@@ -896,13 +901,14 @@ module Shoulda
|
|
896
901
|
end
|
897
902
|
|
898
903
|
def build_attribute_setter(record, attribute_name, value)
|
899
|
-
Shoulda::Matchers::ActiveModel::AllowValueMatcher::AttributeSetter.
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
904
|
+
Shoulda::Matchers::ActiveModel::AllowValueMatcher::AttributeSetter.
|
905
|
+
new(
|
906
|
+
matcher_name: :validate_uniqueness_of,
|
907
|
+
object: record,
|
908
|
+
attribute_name: attribute_name,
|
909
|
+
value: value,
|
910
|
+
ignore_interference_by_writer: ignore_interference_by_writer,
|
911
|
+
)
|
906
912
|
end
|
907
913
|
|
908
914
|
def existing_value_read
|
@@ -929,7 +935,7 @@ module Shoulda
|
|
929
935
|
@given_record.class
|
930
936
|
end
|
931
937
|
|
932
|
-
def failure_message_preface
|
938
|
+
def failure_message_preface # rubocop:disable Metrics/MethodLength
|
933
939
|
prefix = ''
|
934
940
|
|
935
941
|
if @existing_record_created
|
@@ -938,30 +944,28 @@ module Shoulda
|
|
938
944
|
if attribute_setter_for_existing_record
|
939
945
|
prefix << ', setting '
|
940
946
|
prefix << description_for_attribute_setter(
|
941
|
-
attribute_setter_for_existing_record
|
947
|
+
attribute_setter_for_existing_record,
|
942
948
|
)
|
943
949
|
else
|
944
950
|
prefix << ", whose :#{attribute} is "
|
945
951
|
prefix << "‹#{existing_value_read.inspect}›"
|
946
952
|
end
|
947
953
|
|
948
|
-
prefix <<
|
954
|
+
prefix << ', and saving it as the existing record, then'
|
955
|
+
elsif attribute_setter_for_existing_record
|
956
|
+
prefix << "Given an existing #{model.name},"
|
957
|
+
prefix << ' after setting '
|
958
|
+
prefix << description_for_attribute_setter(
|
959
|
+
attribute_setter_for_existing_record,
|
960
|
+
)
|
961
|
+
prefix << ', then'
|
949
962
|
else
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
prefix << ', then'
|
957
|
-
else
|
958
|
-
prefix << "Given an existing #{model.name} whose :#{attribute}"
|
959
|
-
prefix << ' is '
|
960
|
-
prefix << Shoulda::Matchers::Util.inspect_value(
|
961
|
-
existing_value_read
|
962
|
-
)
|
963
|
-
prefix << ', after'
|
964
|
-
end
|
963
|
+
prefix << "Given an existing #{model.name} whose :#{attribute}"
|
964
|
+
prefix << ' is '
|
965
|
+
prefix << Shoulda::Matchers::Util.inspect_value(
|
966
|
+
existing_value_read,
|
967
|
+
)
|
968
|
+
prefix << ', after'
|
965
969
|
end
|
966
970
|
|
967
971
|
prefix << " making a new #{model.name} and setting "
|
@@ -988,7 +992,10 @@ different altogether.
|
|
988
992
|
MESSAGE
|
989
993
|
end
|
990
994
|
|
991
|
-
def description_for_attribute_setter(
|
995
|
+
def description_for_attribute_setter(
|
996
|
+
attribute_setter,
|
997
|
+
same_as_existing: nil
|
998
|
+
)
|
992
999
|
description = "its :#{attribute_setter.attribute_name} to "
|
993
1000
|
|
994
1001
|
if same_as_existing == false
|
@@ -996,13 +1003,13 @@ different altogether.
|
|
996
1003
|
end
|
997
1004
|
|
998
1005
|
description << Shoulda::Matchers::Util.inspect_value(
|
999
|
-
attribute_setter.value_written
|
1006
|
+
attribute_setter.value_written,
|
1000
1007
|
)
|
1001
1008
|
|
1002
1009
|
if attribute_setter.attribute_changed_value?
|
1003
1010
|
description << ' (read back as '
|
1004
1011
|
description << Shoulda::Matchers::Util.inspect_value(
|
1005
|
-
attribute_setter.value_read
|
1012
|
+
attribute_setter.value_read,
|
1006
1013
|
)
|
1007
1014
|
description << ')'
|
1008
1015
|
end
|
@@ -1026,7 +1033,7 @@ different altogether.
|
|
1026
1033
|
)
|
1027
1034
|
description_for_attribute_setter(
|
1028
1035
|
attribute_setter,
|
1029
|
-
same_as_existing: same_as_existing
|
1036
|
+
same_as_existing: same_as_existing,
|
1030
1037
|
)
|
1031
1038
|
end
|
1032
1039
|
end
|
@@ -1109,7 +1116,7 @@ b) If you meant for the validation to be case-insensitive, then you need to
|
|
1109
1116
|
|
1110
1117
|
For more information, please see:
|
1111
1118
|
|
1112
|
-
|
1119
|
+
https://matchers.shoulda.io/docs/v#{Shoulda::Matchers::VERSION}/file.NonCaseSwappableValueError.html
|
1113
1120
|
MESSAGE
|
1114
1121
|
end
|
1115
1122
|
end
|
@@ -18,19 +18,19 @@ module Shoulda
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def activate
|
21
|
-
doubles_by_method_name.each do |
|
21
|
+
doubles_by_method_name.each do |_method_name, double|
|
22
22
|
double.activate
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
26
|
def deactivate
|
27
|
-
doubles_by_method_name.each do |
|
27
|
+
doubles_by_method_name.each do |_method_name, double|
|
28
28
|
double.deactivate
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
32
|
def calls_by_method_name
|
33
|
-
doubles_by_method_name.
|
33
|
+
doubles_by_method_name.inject({}) do |hash, (method_name, double)|
|
34
34
|
hash.merge method_name => double.calls.map(&:args)
|
35
35
|
end
|
36
36
|
end
|
@@ -4,23 +4,26 @@ module Shoulda
|
|
4
4
|
# @private
|
5
5
|
module DoubleImplementationRegistry
|
6
6
|
class << self
|
7
|
-
REGISTRY = {}
|
8
|
-
|
9
7
|
def find(type)
|
10
8
|
find_class!(type).create
|
11
9
|
end
|
12
10
|
|
13
11
|
def register(klass, type)
|
14
|
-
|
12
|
+
registry[type] = klass
|
15
13
|
end
|
16
14
|
|
17
15
|
private
|
18
16
|
|
19
17
|
def find_class!(type)
|
20
|
-
|
21
|
-
raise ArgumentError,
|
18
|
+
registry.fetch(type) do
|
19
|
+
raise ArgumentError, 'No double implementation class found for'\
|
20
|
+
" '#{type}'"
|
22
21
|
end
|
23
22
|
end
|
23
|
+
|
24
|
+
def registry
|
25
|
+
@_registry ||= {}
|
26
|
+
end
|
24
27
|
end
|
25
28
|
end
|
26
29
|
end
|