shoulda-matchers 3.0.1 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +3 -3
- data/CONTRIBUTING.md +60 -28
- data/Gemfile +1 -0
- data/Gemfile.lock +15 -12
- data/NEWS.md +111 -0
- data/README.md +94 -6
- data/Rakefile +10 -8
- data/custom_plan.rb +88 -0
- data/gemfiles/4.0.0.gemfile +1 -0
- data/gemfiles/4.0.0.gemfile.lock +21 -18
- data/gemfiles/4.0.1.gemfile +1 -0
- data/gemfiles/4.0.1.gemfile.lock +21 -18
- data/gemfiles/4.1.gemfile +1 -0
- data/gemfiles/4.1.gemfile.lock +21 -18
- data/gemfiles/4.2.gemfile +1 -0
- data/gemfiles/4.2.gemfile.lock +24 -21
- data/lib/shoulda/matchers/action_controller/permit_matcher.rb +6 -11
- data/lib/shoulda/matchers/active_model.rb +10 -1
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +258 -180
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_changed_value_error.rb +45 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_does_not_exist_error.rb +23 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter.rb +236 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator.rb +62 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters.rb +40 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters_and_validators.rb +48 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/successful_check.rb +14 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/successful_setting.rb +14 -0
- data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +34 -14
- data/lib/shoulda/matchers/active_model/helpers.rb +9 -17
- data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +13 -6
- data/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb +13 -2
- data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +19 -35
- data/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb +13 -2
- data/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +12 -2
- data/lib/shoulda/matchers/active_model/qualifiers.rb +12 -0
- data/lib/shoulda/matchers/active_model/qualifiers/ignore_interference_by_writer.rb +101 -0
- data/lib/shoulda/matchers/active_model/qualifiers/ignoring_interference_by_writer.rb +21 -0
- data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +30 -32
- data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +5 -8
- data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +22 -22
- data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +27 -16
- data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +58 -15
- data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +22 -12
- data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +165 -87
- data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +7 -9
- data/lib/shoulda/matchers/active_model/validation_matcher.rb +111 -49
- data/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +60 -0
- data/lib/shoulda/matchers/active_model/validator.rb +71 -52
- data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +19 -5
- data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +450 -124
- data/lib/shoulda/matchers/util.rb +43 -0
- data/lib/shoulda/matchers/util/word_wrap.rb +59 -31
- data/lib/shoulda/matchers/version.rb +1 -1
- data/script/update_gem_in_all_appraisals +1 -1
- data/script/update_gems_in_all_appraisals +1 -1
- data/spec/acceptance/multiple_libraries_integration_spec.rb +5 -2
- data/spec/acceptance/rails_integration_spec.rb +6 -2
- data/spec/spec_helper.rb +1 -3
- data/spec/support/acceptance/helpers/step_helpers.rb +4 -1
- data/spec/support/tests/current_bundle.rb +21 -7
- data/spec/support/unit/active_record/create_table.rb +54 -0
- data/spec/support/unit/attribute.rb +47 -0
- data/spec/support/unit/capture.rb +6 -0
- data/spec/support/unit/change_value.rb +111 -0
- data/spec/support/unit/create_model_arguments/basic.rb +135 -0
- data/spec/support/unit/create_model_arguments/has_many.rb +15 -0
- data/spec/support/unit/create_model_arguments/uniqueness_matcher.rb +74 -0
- data/spec/support/unit/helpers/active_record_versions.rb +1 -1
- data/spec/support/unit/helpers/class_builder.rb +61 -47
- data/spec/support/unit/helpers/database_helpers.rb +5 -3
- data/spec/support/unit/helpers/model_builder.rb +77 -97
- data/spec/support/unit/helpers/validation_matcher_scenario_helpers.rb +44 -0
- data/spec/support/unit/load_environment.rb +12 -0
- data/spec/support/unit/matchers/fail_with_message_including_matcher.rb +2 -2
- data/spec/support/unit/matchers/fail_with_message_matcher.rb +12 -1
- data/spec/support/unit/model_creation_strategies/active_model.rb +111 -0
- data/spec/support/unit/model_creation_strategies/active_record.rb +77 -0
- data/spec/support/unit/model_creators.rb +19 -0
- data/spec/support/unit/model_creators/active_model.rb +39 -0
- data/spec/support/unit/model_creators/active_record.rb +43 -0
- data/spec/support/unit/model_creators/active_record/has_and_belongs_to_many.rb +95 -0
- data/spec/support/unit/model_creators/active_record/has_many.rb +67 -0
- data/spec/support/unit/model_creators/active_record/uniqueness_matcher.rb +42 -0
- data/spec/support/unit/model_creators/basic.rb +97 -0
- data/spec/support/unit/rails_application.rb +1 -1
- data/spec/support/unit/record_validating_confirmation_builder.rb +3 -7
- data/spec/support/unit/shared_examples/ignoring_interference_by_writer.rb +79 -0
- data/spec/support/unit/validation_matcher_scenario.rb +62 -0
- data/spec/unit/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +4 -0
- data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +575 -140
- data/spec/unit/shoulda/matchers/active_model/validate_absence_of_matcher_spec.rb +115 -15
- data/spec/unit/shoulda/matchers/active_model/validate_acceptance_of_matcher_spec.rb +42 -4
- data/spec/unit/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb +92 -6
- data/spec/unit/shoulda/matchers/active_model/validate_exclusion_of_matcher_spec.rb +122 -10
- data/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb +306 -58
- data/spec/unit/shoulda/matchers/active_model/validate_length_of_matcher_spec.rb +122 -3
- data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +805 -131
- data/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +196 -29
- data/spec/unit/shoulda/matchers/active_record/define_enum_for_matcher_spec.rb +82 -40
- data/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb +600 -101
- data/spec/unit/shoulda/matchers/util/word_wrap_spec.rb +88 -33
- data/spec/unit_spec_helper.rb +10 -22
- data/zeus.json +11 -0
- metadata +64 -23
- data/lib/shoulda/matchers/active_model/strict_validator.rb +0 -51
- data/spec/support/unit/shared_examples/numerical_type_submatcher.rb +0 -15
- data/spec/unit/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +0 -288
- data/spec/unit/shoulda/matchers/active_model/numericality_matchers/even_number_matcher_spec.rb +0 -100
- data/spec/unit/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher_spec.rb +0 -100
- data/spec/unit/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher_spec.rb +0 -100
@@ -0,0 +1,47 @@
|
|
1
|
+
module UnitTests
|
2
|
+
class Attribute
|
3
|
+
attr_reader :name, :column_type, :column_options
|
4
|
+
|
5
|
+
DEFAULT_COLUMN_TYPE = :string
|
6
|
+
DEFAULT_COLUMN_OPTIONS = {
|
7
|
+
null: false,
|
8
|
+
array: false
|
9
|
+
}
|
10
|
+
|
11
|
+
def initialize(args)
|
12
|
+
@args = args
|
13
|
+
end
|
14
|
+
|
15
|
+
def name
|
16
|
+
args.fetch(:name)
|
17
|
+
end
|
18
|
+
|
19
|
+
def column_type
|
20
|
+
args.fetch(:column_type, DEFAULT_COLUMN_TYPE)
|
21
|
+
end
|
22
|
+
|
23
|
+
def column_options
|
24
|
+
DEFAULT_COLUMN_OPTIONS.
|
25
|
+
merge(args.fetch(:column_options, {})).
|
26
|
+
merge(type: column_type)
|
27
|
+
end
|
28
|
+
|
29
|
+
def array?
|
30
|
+
column_options[:array]
|
31
|
+
end
|
32
|
+
|
33
|
+
def default_value
|
34
|
+
args.fetch(:default_value) do
|
35
|
+
if column_options[:null]
|
36
|
+
nil
|
37
|
+
else
|
38
|
+
Shoulda::Matchers::Util.dummy_value_for(value_type, array: array?)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
|
45
|
+
attr_reader :args
|
46
|
+
end
|
47
|
+
end
|
@@ -2,6 +2,8 @@ module Kernel
|
|
2
2
|
# #capture, #silence_stream, and #silence_stderr are deprecated after Rails
|
3
3
|
# 4.2 and will be removed in 5.0, so just override them completely here
|
4
4
|
|
5
|
+
undef_method :capture
|
6
|
+
|
5
7
|
def capture(stream)
|
6
8
|
stream = stream.to_s
|
7
9
|
captured_stream = Tempfile.new(stream)
|
@@ -18,6 +20,8 @@ module Kernel
|
|
18
20
|
stream_io.reopen(origin_stream)
|
19
21
|
end
|
20
22
|
|
23
|
+
undef_method :silence_stream
|
24
|
+
|
21
25
|
def silence_stream(stream)
|
22
26
|
old_stream = stream.dup
|
23
27
|
stream.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
|
@@ -28,6 +32,8 @@ module Kernel
|
|
28
32
|
old_stream.close
|
29
33
|
end
|
30
34
|
|
35
|
+
undef_method :silence_stderr
|
36
|
+
|
31
37
|
def silence_stderr
|
32
38
|
silence_stream(STDERR) { yield }
|
33
39
|
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module UnitTests
|
2
|
+
class ChangeValue
|
3
|
+
def self.call(column_type, value, value_changer)
|
4
|
+
new(column_type, value, value_changer).call
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize(column_type, value, value_changer)
|
8
|
+
@column_type = column_type
|
9
|
+
@value = value
|
10
|
+
@value_changer = value_changer
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
if value_changer.is_a?(Proc)
|
15
|
+
value_changer.call(value)
|
16
|
+
elsif respond_to?(value_changer, true)
|
17
|
+
send(value_changer)
|
18
|
+
else
|
19
|
+
value.public_send(value_changer)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
attr_reader :column_type, :value, :value_changer
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def previous_value
|
30
|
+
if value.is_a?(String)
|
31
|
+
value[0..-2] + (value[-1].ord - 1).chr
|
32
|
+
else
|
33
|
+
value - 1
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def next_value
|
38
|
+
if value.is_a?(Array)
|
39
|
+
value + [value.first.class.new]
|
40
|
+
elsif value.respond_to?(:next)
|
41
|
+
value.next
|
42
|
+
else
|
43
|
+
value + 1
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def next_next_value
|
48
|
+
change_value(change_value(value, :next_value), :next_value)
|
49
|
+
end
|
50
|
+
|
51
|
+
def next_value_or_numeric_value
|
52
|
+
if value
|
53
|
+
change_value(value, :next_value)
|
54
|
+
else
|
55
|
+
change_value(value, :numeric_value)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def next_value_or_non_numeric_value
|
60
|
+
if value
|
61
|
+
change_value(value, :next_value)
|
62
|
+
else
|
63
|
+
change_value(value, :non_numeric_value)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def never_falsy
|
68
|
+
value || dummy_value_for_column
|
69
|
+
end
|
70
|
+
|
71
|
+
def truthy_or_numeric
|
72
|
+
value || 1
|
73
|
+
end
|
74
|
+
|
75
|
+
def never_blank
|
76
|
+
value.presence || dummy_value_for_column
|
77
|
+
end
|
78
|
+
|
79
|
+
def nil_to_blank
|
80
|
+
value || ''
|
81
|
+
end
|
82
|
+
|
83
|
+
def always_nil
|
84
|
+
nil
|
85
|
+
end
|
86
|
+
|
87
|
+
def add_character
|
88
|
+
value + 'a'
|
89
|
+
end
|
90
|
+
|
91
|
+
def remove_character
|
92
|
+
value.chop
|
93
|
+
end
|
94
|
+
|
95
|
+
def numeric_value
|
96
|
+
'1'
|
97
|
+
end
|
98
|
+
|
99
|
+
def non_numeric_value
|
100
|
+
'a'
|
101
|
+
end
|
102
|
+
|
103
|
+
def change_value(value, value_changer)
|
104
|
+
self.class.call(column_type, value, value_changer)
|
105
|
+
end
|
106
|
+
|
107
|
+
def dummy_value_for_column
|
108
|
+
Shoulda::Matchers::Util.dummy_value_for(column_type)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module UnitTests
|
4
|
+
module CreateModelArguments
|
5
|
+
class Basic
|
6
|
+
DEFAULT_MODEL_NAME = 'Example'
|
7
|
+
DEFAULT_ATTRIBUTE_NAME = :attr
|
8
|
+
DEFAULT_COLUMN_TYPE = :string
|
9
|
+
|
10
|
+
def self.wrap(args)
|
11
|
+
if args.is_a?(self)
|
12
|
+
args
|
13
|
+
else
|
14
|
+
new(args)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
extend Forwardable
|
19
|
+
|
20
|
+
def_delegators(
|
21
|
+
:attribute,
|
22
|
+
:column_type,
|
23
|
+
:column_options,
|
24
|
+
:default_value,
|
25
|
+
:value_type
|
26
|
+
)
|
27
|
+
|
28
|
+
def initialize(args)
|
29
|
+
@args = args
|
30
|
+
end
|
31
|
+
|
32
|
+
def fetch(*args, &block)
|
33
|
+
self.args.fetch(*args, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def merge(given_args)
|
37
|
+
self.class.new(args.deep_merge(given_args))
|
38
|
+
end
|
39
|
+
|
40
|
+
def model_name
|
41
|
+
args.fetch(:model_name, DEFAULT_MODEL_NAME)
|
42
|
+
end
|
43
|
+
|
44
|
+
def attribute_name
|
45
|
+
args.fetch(:attribute_name, default_attribute_name)
|
46
|
+
end
|
47
|
+
|
48
|
+
def model_creation_strategy
|
49
|
+
args.fetch(:model_creation_strategy)
|
50
|
+
end
|
51
|
+
|
52
|
+
def columns
|
53
|
+
{ attribute_name => column_options }
|
54
|
+
end
|
55
|
+
|
56
|
+
def attribute
|
57
|
+
@_attribute ||= attribute_class.new(attribute_args)
|
58
|
+
end
|
59
|
+
|
60
|
+
def all_attribute_overrides
|
61
|
+
@_all_attribute_overrides ||= begin
|
62
|
+
attribute_overrides = args.slice(
|
63
|
+
:changing_values_with,
|
64
|
+
:default_value
|
65
|
+
)
|
66
|
+
|
67
|
+
overrides =
|
68
|
+
if attribute_overrides.empty?
|
69
|
+
{}
|
70
|
+
else
|
71
|
+
{ attribute_name => attribute_overrides }
|
72
|
+
end
|
73
|
+
|
74
|
+
overrides.deep_merge(args.fetch(:attribute_overrides, {}))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def attribute_overrides
|
79
|
+
all_attribute_overrides.fetch(attribute_name, {})
|
80
|
+
end
|
81
|
+
|
82
|
+
def validation_name
|
83
|
+
args.fetch(:validation_name) { map_matcher_name_to_validation_name }
|
84
|
+
end
|
85
|
+
|
86
|
+
def validation_options
|
87
|
+
args.fetch(:validation_options, {})
|
88
|
+
end
|
89
|
+
|
90
|
+
def custom_validation?
|
91
|
+
args.fetch(:custom_validation, false)
|
92
|
+
end
|
93
|
+
|
94
|
+
def matcher_name
|
95
|
+
args.fetch(:matcher_name)
|
96
|
+
end
|
97
|
+
|
98
|
+
def attribute_default_values_by_name
|
99
|
+
if attribute_overrides.key?(:default_value)
|
100
|
+
{ attribute_name => attribute_overrides[:default_value] }
|
101
|
+
else
|
102
|
+
{}
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def to_hash
|
107
|
+
args.deep_dup
|
108
|
+
end
|
109
|
+
|
110
|
+
protected
|
111
|
+
|
112
|
+
attr_reader :args
|
113
|
+
|
114
|
+
def attribute_class
|
115
|
+
UnitTests::Attribute
|
116
|
+
end
|
117
|
+
|
118
|
+
def default_attribute_name
|
119
|
+
DEFAULT_ATTRIBUTE_NAME
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def map_matcher_name_to_validation_name
|
125
|
+
matcher_name.to_s.sub('validate', 'validates')
|
126
|
+
end
|
127
|
+
|
128
|
+
def attribute_args
|
129
|
+
args.slice(:column_type).deep_merge(
|
130
|
+
attribute_overrides.deep_merge(name: attribute_name)
|
131
|
+
)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module UnitTests
|
2
|
+
module CreateModelArguments
|
3
|
+
class UniquenessMatcher < Basic
|
4
|
+
def self.normalize_attribute(attribute)
|
5
|
+
if attribute.is_a?(Hash)
|
6
|
+
Attribute.new(attribute)
|
7
|
+
else
|
8
|
+
Attribute.new(name: attribute)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.normalize_attributes(attributes)
|
13
|
+
attributes.map do |attribute|
|
14
|
+
normalize_attribute(attribute)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def columns
|
19
|
+
attributes.reduce({}) do |options, attribute|
|
20
|
+
options.merge(
|
21
|
+
attribute.name => {
|
22
|
+
type: attribute.column_type,
|
23
|
+
options: attribute.column_options
|
24
|
+
}
|
25
|
+
)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def validation_options
|
30
|
+
super.merge(scope: scope_attribute_names)
|
31
|
+
end
|
32
|
+
|
33
|
+
def attribute_default_values_by_name
|
34
|
+
attributes.reduce({}) do |values, attribute|
|
35
|
+
values.merge(attribute.name => attribute.default_value)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
|
41
|
+
def attribute_class
|
42
|
+
Attribute
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def attributes
|
48
|
+
[attribute] + scope_attributes + additional_attributes
|
49
|
+
end
|
50
|
+
|
51
|
+
def scope_attribute_names
|
52
|
+
scope_attributes.map(&:name)
|
53
|
+
end
|
54
|
+
|
55
|
+
def scope_attributes
|
56
|
+
@_scope_attributes ||= self.class.normalize_attributes(
|
57
|
+
args.fetch(:scopes, [])
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
def additional_attributes
|
62
|
+
@_additional_attributes ||= self.class.normalize_attributes(
|
63
|
+
args.fetch(:additional_attributes, [])
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
class Attribute < UnitTests::Attribute
|
68
|
+
def value_type
|
69
|
+
args.fetch(:value_type) { column_type }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -1,76 +1,90 @@
|
|
1
1
|
module UnitTests
|
2
2
|
module ClassBuilder
|
3
|
-
def
|
4
|
-
|
5
|
-
qualified_namespace = (namespace.presence || 'Object').constantize
|
6
|
-
name_without_namespace = name.to_s.demodulize
|
7
|
-
[qualified_namespace, name_without_namespace]
|
3
|
+
def define_module(*args, &block)
|
4
|
+
ClassBuilder.define_module(*args, &block)
|
8
5
|
end
|
9
6
|
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
example_group.after do
|
14
|
-
teardown_defined_constants
|
15
|
-
end
|
7
|
+
def define_class(*args, &block)
|
8
|
+
ClassBuilder.define_class(*args, &block)
|
16
9
|
end
|
17
10
|
|
18
|
-
|
19
|
-
|
11
|
+
class << self
|
12
|
+
def configure_example_group(example_group)
|
13
|
+
example_group.include(self)
|
20
14
|
|
21
|
-
|
22
|
-
|
15
|
+
example_group.after do
|
16
|
+
ClassBuilder.reset
|
17
|
+
end
|
18
|
+
end
|
23
19
|
|
24
|
-
|
25
|
-
|
20
|
+
def reset
|
21
|
+
remove_defined_classes
|
26
22
|
end
|
27
23
|
|
28
|
-
|
29
|
-
|
30
|
-
end
|
31
|
-
RUBY
|
24
|
+
def define_module(module_name, &block)
|
25
|
+
module_name = module_name.to_s.camelize
|
32
26
|
|
33
|
-
|
34
|
-
|
27
|
+
namespace, name_without_namespace =
|
28
|
+
ClassBuilder.parse_constant_name(module_name)
|
35
29
|
|
36
|
-
if
|
37
|
-
|
30
|
+
if namespace.const_defined?(name_without_namespace, false)
|
31
|
+
namespace.__send__(:remove_const, name_without_namespace)
|
38
32
|
end
|
39
|
-
end
|
40
|
-
end
|
41
33
|
|
42
|
-
|
43
|
-
|
34
|
+
eval <<-RUBY
|
35
|
+
module #{namespace}::#{name_without_namespace}
|
36
|
+
end
|
37
|
+
RUBY
|
44
38
|
|
45
|
-
|
46
|
-
|
39
|
+
namespace.const_get(name_without_namespace).tap do |constant|
|
40
|
+
constant.unloadable
|
47
41
|
|
48
|
-
|
49
|
-
|
42
|
+
if block
|
43
|
+
constant.module_eval(&block)
|
44
|
+
end
|
45
|
+
end
|
50
46
|
end
|
51
47
|
|
52
|
-
|
53
|
-
|
48
|
+
def define_class(class_name, parent_class = Object, &block)
|
49
|
+
class_name = class_name.to_s.camelize
|
50
|
+
|
51
|
+
namespace, name_without_namespace =
|
52
|
+
ClassBuilder.parse_constant_name(class_name)
|
53
|
+
|
54
|
+
if namespace.const_defined?(name_without_namespace, false)
|
55
|
+
namespace.__send__(:remove_const, name_without_namespace)
|
54
56
|
end
|
55
|
-
RUBY
|
56
57
|
|
57
|
-
|
58
|
-
|
58
|
+
eval <<-RUBY
|
59
|
+
class #{namespace}::#{name_without_namespace} < ::#{parent_class}
|
60
|
+
end
|
61
|
+
RUBY
|
62
|
+
|
63
|
+
namespace.const_get(name_without_namespace).tap do |constant|
|
64
|
+
constant.unloadable
|
59
65
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
66
|
+
if block
|
67
|
+
if block.arity == 0
|
68
|
+
constant.class_eval(&block)
|
69
|
+
else
|
70
|
+
block.call(constant)
|
71
|
+
end
|
65
72
|
end
|
66
73
|
end
|
67
74
|
end
|
68
|
-
end
|
69
75
|
|
70
|
-
|
76
|
+
def parse_constant_name(name)
|
77
|
+
namespace = Shoulda::Matchers::Util.deconstantize(name)
|
78
|
+
qualified_namespace = (namespace.presence || 'Object').constantize
|
79
|
+
name_without_namespace = name.to_s.demodulize
|
80
|
+
[qualified_namespace, name_without_namespace]
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
71
84
|
|
72
|
-
|
73
|
-
|
85
|
+
def remove_defined_classes
|
86
|
+
::ActiveSupport::Dependencies.clear
|
87
|
+
end
|
74
88
|
end
|
75
89
|
end
|
76
90
|
end
|