glebtv-mongoid-rspec 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gitignore +6 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +14 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE +20 -0
  9. data/README.md +203 -0
  10. data/Rakefile +18 -0
  11. data/gemfiles/mongoid-3.1.gemfile +5 -0
  12. data/gemfiles/mongoid-4.0.gemfile +5 -0
  13. data/glebtv-mongoid-rspec.gemspec +25 -0
  14. data/lib/glebtv-mongoid-rspec.rb +1 -0
  15. data/lib/matchers/accept_nested_attributes.rb +65 -0
  16. data/lib/matchers/allow_mass_assignment.rb +101 -0
  17. data/lib/matchers/associations.rb +313 -0
  18. data/lib/matchers/collections.rb +9 -0
  19. data/lib/matchers/document.rb +160 -0
  20. data/lib/matchers/indexes.rb +81 -0
  21. data/lib/matchers/validations.rb +78 -0
  22. data/lib/matchers/validations/acceptance_of.rb +9 -0
  23. data/lib/matchers/validations/associated.rb +19 -0
  24. data/lib/matchers/validations/confirmation_of.rb +9 -0
  25. data/lib/matchers/validations/custom_validation_of.rb +47 -0
  26. data/lib/matchers/validations/exclusion_of.rb +49 -0
  27. data/lib/matchers/validations/format_of.rb +71 -0
  28. data/lib/matchers/validations/inclusion_of.rb +49 -0
  29. data/lib/matchers/validations/length_of.rb +147 -0
  30. data/lib/matchers/validations/numericality_of.rb +74 -0
  31. data/lib/matchers/validations/presence_of.rb +9 -0
  32. data/lib/matchers/validations/uniqueness_of.rb +82 -0
  33. data/lib/matchers/validations/with_message.rb +27 -0
  34. data/lib/mongoid-rspec.rb +33 -0
  35. data/lib/mongoid-rspec/version.rb +5 -0
  36. data/spec/models/article.rb +29 -0
  37. data/spec/models/comment.rb +6 -0
  38. data/spec/models/log.rb +4 -0
  39. data/spec/models/movie_article.rb +8 -0
  40. data/spec/models/permalink.rb +5 -0
  41. data/spec/models/person.rb +10 -0
  42. data/spec/models/profile.rb +16 -0
  43. data/spec/models/record.rb +5 -0
  44. data/spec/models/site.rb +9 -0
  45. data/spec/models/user.rb +36 -0
  46. data/spec/spec_helper.rb +34 -0
  47. data/spec/unit/accept_nested_attributes_spec.rb +12 -0
  48. data/spec/unit/associations_spec.rb +42 -0
  49. data/spec/unit/collections_spec.rb +7 -0
  50. data/spec/unit/document_spec.rb +27 -0
  51. data/spec/unit/indexes_spec.rb +13 -0
  52. data/spec/unit/validations_spec.rb +52 -0
  53. data/spec/validators/ssn_validator.rb +16 -0
  54. metadata +163 -0
@@ -0,0 +1,81 @@
1
+ module Mongoid
2
+ module Matchers
3
+ class HaveIndexForMatcher # :nodoc:
4
+ def initialize(index_fields)
5
+ @index_fields = index_fields.symbolize_keys!
6
+ end
7
+
8
+ def with_options(options = { })
9
+ @options = options
10
+ self
11
+ end
12
+
13
+ def matches?(klass)
14
+ @klass = klass.is_a?(Class) ? klass : klass.class
15
+ @errors = []
16
+
17
+ if @klass.respond_to?(:index_options)
18
+ # Mongoid 3
19
+ unless @klass.index_options[@index_fields]
20
+ @errors.push "no index for #{@index_fields}"
21
+ else
22
+ if !@options.nil? && !@options.empty?
23
+ @options.each do |option, option_value|
24
+ if denormalising_options(@klass.index_options[@index_fields])[option] != option_value
25
+ @errors.push "index for #{@index_fields.inspect} with options of #{@klass.index_options[@index_fields].inspect}"
26
+ end
27
+ end
28
+ end
29
+ end
30
+ else
31
+ # Mongoid 4
32
+ unless @klass.index_specifications.map(&:key).include?(@index_fields)
33
+ @errors.push "no index for #{@index_fields}"
34
+ else
35
+ if !@options.nil? && !@options.empty?
36
+ index_options = @klass.index_specifications.select { |is| is.key == @index_fields }.first.options
37
+ @options.each do |option, option_value|
38
+ if denormalising_options(index_options)[option] != option_value
39
+ @errors.push "index for #{@index_fields.inspect} with options of #{@klass.index_options[@index_fields].inspect}"
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ @errors.empty?
47
+ end
48
+
49
+ def failure_message_for_should
50
+ "Expected #{@klass.inspect} to #{description}, got #{@errors.to_sentence}"
51
+ end
52
+
53
+ def failure_message_for_should_not
54
+ "Expected #{@klass.inspect} to not #{description}, got #{@klass.inspect} to #{description}"
55
+ end
56
+
57
+ def description
58
+ desc = "have an index for #{@index_fields.inspect}"
59
+ desc << " with options of #{@options.inspect}" if @options
60
+ desc
61
+ end
62
+
63
+ private
64
+ MAPPINGS = {
65
+ dropDups: :drop_dups
66
+ }
67
+
68
+ def denormalising_options(opts)
69
+ options = {}
70
+ opts.each_pair do |option, value|
71
+ options[MAPPINGS[option] || option] = value
72
+ end
73
+ options
74
+ end
75
+ end
76
+
77
+ def have_index_for(index_fields)
78
+ HaveIndexForMatcher.new(index_fields)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,78 @@
1
+ module Mongoid
2
+ module Matchers
3
+ module Validations
4
+
5
+ class HaveValidationMatcher
6
+
7
+ def initialize(field, validation_type)
8
+ @field = field.to_s
9
+ @type = validation_type.to_s
10
+ @options = {}
11
+ end
12
+
13
+ def matches?(actual)
14
+ @klass = actual.is_a?(Class) ? actual : actual.class
15
+
16
+ @validator = @klass.validators_on(@field).detect{ |v|
17
+ v.kind.to_s == @type and (!v.options[:on] or on_options_matches?(v))
18
+ }
19
+
20
+ if @validator
21
+ @negative_result_message = "#{@type.inspect} validator on #{@field.inspect}"
22
+ @positive_result_message = "#{@type.inspect} validator on #{@field.inspect}"
23
+ else
24
+ @negative_result_message = "no #{@type.inspect} validator on #{@field.inspect}"
25
+ return false
26
+ end
27
+ @result = true
28
+ check_on if @options[:on]
29
+ @result
30
+ end
31
+
32
+ def failure_message_for_should
33
+ "Expected #{@klass.inspect} to #{description}; instead got #{@negative_result_message}"
34
+ end
35
+
36
+ def failure_message_for_should_not
37
+ "Expected #{@klass.inspect} to not #{description}; instead got #{@positive_result_message}"
38
+ end
39
+
40
+ def description
41
+ desc = "have #{@type.inspect} validator on #{@field.inspect}"
42
+ desc << " on #{@options[:on]}" if @options[:on]
43
+ desc
44
+ end
45
+
46
+ def on(*on_method)
47
+ @options[:on] = on_method.flatten
48
+ self
49
+ end
50
+
51
+ def check_on
52
+ validator_on_methods = [@validator.options[:on]].flatten
53
+
54
+ if validator_on_methods.any?
55
+ message = " on methods: #{validator_on_methods}"
56
+
57
+ if on_options_covered_by?( @validator )
58
+ @positive_result_message << message
59
+ else
60
+ @negative_result_message << message
61
+ @result = false
62
+ end
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def on_options_matches?(validator)
69
+ @options[:on] and validator.options[:on] and on_options_covered_by?(validator)
70
+ end
71
+
72
+ def on_options_covered_by?(validator)
73
+ ([@options[:on]].flatten - [validator.options[:on]].flatten).empty?
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,9 @@
1
+ module Mongoid
2
+ module Matchers
3
+ module Validations
4
+ def validate_acceptance_of(field)
5
+ HaveValidationMatcher.new(field, :acceptance)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ module Mongoid
2
+ module Matchers
3
+ module Validations
4
+ class ValidateAssociatedMatcher < HaveValidationMatcher
5
+ def initialize(name)
6
+ super(name, :associated)
7
+ end
8
+
9
+ def description
10
+ "validate associated #{@field.inspect}"
11
+ end
12
+ end
13
+
14
+ def validate_associated(association_name)
15
+ ValidateAssociatedMatcher.new(association_name)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ module Mongoid
2
+ module Matchers
3
+ module Validations
4
+ def validate_confirmation_of(field)
5
+ HaveValidationMatcher.new(field, :confirmation)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,47 @@
1
+ module Mongoid
2
+ module Matchers
3
+ module Validations
4
+ class ValidateWithCustomValidatorMatcher < HaveValidationMatcher
5
+ include WithMessage
6
+ def initialize(field)
7
+ super(field, :custom)
8
+ end
9
+
10
+ def with_validator(custom_validator)
11
+ @custom_validator = custom_validator
12
+ self
13
+ end
14
+
15
+ def matches?(actual)
16
+ return false unless (@result = super(actual))
17
+ check_custom_validator if @custom_validator
18
+ check_expected_message if @expected_message
19
+
20
+ @result
21
+ end
22
+
23
+ def description
24
+ options_desc = []
25
+ options_desc << " with custom validator #{@custom_validator.name}" if @validator
26
+ options_desc << " with message '#{@expected_message}'" if @expected_message
27
+ "validate field #{@field.inspect}" << options_desc.to_sentence
28
+ end
29
+
30
+ private
31
+
32
+ def check_custom_validator
33
+ if @validator.kind_of? @custom_validator
34
+ @positive_result_message << " with custom validator of type #{@custom_validator.name}"
35
+ else
36
+ @negative_result_message << " with custom validator not of type #{@custom_validator.name}"
37
+ @result = false
38
+ end
39
+ end
40
+ end
41
+
42
+ def custom_validate(field)
43
+ ValidateWithCustomValidatorMatcher.new(field)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,49 @@
1
+ module Mongoid
2
+ module Matchers
3
+ module Validations
4
+ class ValidateExclusionOfMatcher < HaveValidationMatcher
5
+ def initialize(name)
6
+ super(name, :exclusion)
7
+ end
8
+
9
+ def to_not_allow(*values)
10
+ @not_allowed_values = [values].flatten
11
+ self
12
+ end
13
+
14
+ def matches?(actual)
15
+ return false unless result = super(actual)
16
+
17
+ if @not_allowed_values
18
+ raw_validator_not_allowed_values = @validator.options[:in]
19
+
20
+ validator_not_allowed_values = case raw_validator_not_allowed_values
21
+ when Range then raw_validator_not_allowed_values.to_a
22
+ when Proc then raw_validator_not_allowed_values.call(actual)
23
+ else raw_validator_not_allowed_values end
24
+
25
+ allowed_values = @not_allowed_values - validator_not_allowed_values
26
+ if allowed_values.empty?
27
+ @positive_result_message = @positive_result_message << " not allowing all values mentioned"
28
+ else
29
+ @negative_result_message = @negative_result_message << " allowing the following the ff. values: #{allowed_values.inspect}"
30
+ result = false
31
+ end
32
+ end
33
+
34
+ result
35
+ end
36
+
37
+ def description
38
+ options_desc = []
39
+ options_desc << " not allowing the ff. values: #{@not_allowed_values}" if @not_allowed_values
40
+ super << options_desc.to_sentence
41
+ end
42
+ end
43
+
44
+ def validate_exclusion_of(field)
45
+ ValidateExclusionOfMatcher.new(field)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,71 @@
1
+ module Mongoid
2
+ module Matchers
3
+ module Validations
4
+ class ValidateFormatOfMatcher < HaveValidationMatcher
5
+ def initialize(field)
6
+ super(field, :format)
7
+ end
8
+
9
+ def with_format(format)
10
+ @format = format
11
+ self
12
+ end
13
+
14
+ def to_allow(valid_value)
15
+ @valid_value = valid_value
16
+ self
17
+ end
18
+
19
+ def not_to_allow(invalid_value)
20
+ @invalid_value = invalid_value
21
+ self
22
+ end
23
+
24
+ def matches?(actual)
25
+ return false unless result = super(actual)
26
+
27
+ if @format
28
+ if @validator.options[:with] == @format
29
+ @positive_result_message = @positive_result_message << " with format #{@validator.options[:format].inspect}"
30
+ else
31
+ @negative_result_message = @negative_result_message << " with format #{@validator.options[:format].inspect}"
32
+ result = false
33
+ end
34
+ end
35
+
36
+ if @valid_value
37
+ if @validator.options[:with] =~ @valid_value
38
+ @positive_result_message = @positive_result_message << " with #{@valid_value.inspect} as a valid value"
39
+ else
40
+ @negative_result_message = @negative_result_message << " with #{@valid_value.inspect} as an invalid value"
41
+ result = false
42
+ end
43
+ end
44
+
45
+ if @invalid_value
46
+ if !(@invalid_value =~ @validator.options[:with])
47
+ @positive_result_message = @positive_result_message << " with #{@invalid_value.inspect} as an invalid value"
48
+ else
49
+ @negative_result_message = @negative_result_message << " with #{@invalid_value.inspect} as a valid value"
50
+ result = false
51
+ end
52
+ end
53
+
54
+ result
55
+ end
56
+
57
+ def description
58
+ options_desc = []
59
+ options_desc << " with format #{@format.inspect}" if @format
60
+ options_desc << " allowing the value #{@valid_value.inspect}" if @valid_value
61
+ options_desc << " not allowing the value #{@invalid_value.inspect}" if @invalid_value
62
+ super << options_desc.to_sentence
63
+ end
64
+ end
65
+
66
+ def validate_format_of(field)
67
+ ValidateFormatOfMatcher.new(field)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,49 @@
1
+ module Mongoid
2
+ module Matchers
3
+ module Validations
4
+ class ValidateInclusionOfMatcher < HaveValidationMatcher
5
+ def initialize(name)
6
+ super(name, :inclusion)
7
+ end
8
+
9
+ def to_allow(*values)
10
+ @allowed_values = values.map { |v| v.respond_to?(:to_a) ? v.to_a : v }.flatten
11
+ self
12
+ end
13
+
14
+ def matches?(actual)
15
+ return false unless result = super(actual)
16
+
17
+ if @allowed_values
18
+ raw_validator_allowed_values = @validator.options[:in]
19
+
20
+ validator_allowed_values = case raw_validator_allowed_values
21
+ when Range then raw_validator_allowed_values.to_a
22
+ when Proc then raw_validator_allowed_values.call(actual)
23
+ else raw_validator_allowed_values end
24
+
25
+ not_allowed_values = @allowed_values - validator_allowed_values
26
+ if not_allowed_values.empty?
27
+ @positive_result_message = @positive_result_message << " allowing all values mentioned"
28
+ else
29
+ @negative_result_message = @negative_result_message << " not allowing these values: #{not_allowed_values.inspect}"
30
+ result = false
31
+ end
32
+ end
33
+
34
+ result
35
+ end
36
+
37
+ def description
38
+ options_desc = []
39
+ options_desc << " allowing these values: #{@allowed_values}" if @allowed_values
40
+ super << options_desc.to_sentence
41
+ end
42
+ end
43
+
44
+ def validate_inclusion_of(field)
45
+ ValidateInclusionOfMatcher.new(field)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,147 @@
1
+ module Mongoid
2
+ module Matchers
3
+ module Validations
4
+ class ValidateLengthOfMatcher < HaveValidationMatcher
5
+ include WithMessage
6
+
7
+ def initialize(name)
8
+ super(name, :length)
9
+ end
10
+
11
+ def with_maximum(value)
12
+ @maximum = value
13
+ self
14
+ end
15
+ alias :less_than :with_maximum
16
+
17
+ def with_minimum(value)
18
+ @minimum = value
19
+ self
20
+ end
21
+ alias :greater_than :with_minimum
22
+
23
+ def within(value)
24
+ @within = value
25
+ self
26
+ end
27
+ alias :in :within
28
+
29
+ def as_exactly(value)
30
+ @is = value
31
+ self
32
+ end
33
+ alias :is :as_exactly
34
+
35
+ def with_message(message)
36
+ @expected_message = message
37
+ self
38
+ end
39
+
40
+ def matches?(actual)
41
+ return false unless @result = super(actual)
42
+
43
+ check_maximum if @maximum
44
+ check_minimum if @minimum
45
+ check_range if @within
46
+ check_exact if @is
47
+ check_expected_message if @expected_message
48
+
49
+ @result
50
+ end
51
+
52
+ def description
53
+ options_desc = []
54
+ options_desc << "with minimum of #{@minimum}" if @minimum
55
+ options_desc << "with maximum of #{@maximum}" if @maximum
56
+ options_desc << "within the range of #{@within}" if @within
57
+ options_desc << "as exactly #{@is}" if @is
58
+ options_desc << "with message '#{@expected_message}'" if @expected_message
59
+ super << " #{options_desc.to_sentence}"
60
+ end
61
+
62
+ private
63
+
64
+ def check_maximum
65
+ if actual_max.nil?
66
+ @negative_result_message << " with no maximum"
67
+ @result = false
68
+ elsif actual_max == @maximum
69
+ @positive_result_message << " with maximum of #{@maximum}"
70
+ else
71
+ @negative_result_message << " with maximum of #{actual_max}"
72
+ @result = false
73
+ end
74
+ end
75
+
76
+ def check_minimum
77
+ if actual_min.nil?
78
+ @negative_result_message << " with no minimum"
79
+ @result = false
80
+ elsif actual_min == @minimum
81
+ @positive_result_message << " with minimum of #{@minimum}"
82
+ else
83
+ @negative_result_message << " with minimum of #{actual_min}"
84
+ @result = false
85
+ end
86
+ end
87
+
88
+ def check_range
89
+ min, max = [@within.min, @within.max]
90
+ if !actual_min.nil? and actual_max.nil?
91
+ @negative_result_message << " with no minimum but with maximum of #{actual_max}"
92
+ @result = false
93
+ elsif actual_min.nil? and !actual_max.nil?
94
+ @negative_result_message << " with minimum_of #{actual_min} but no maximum"
95
+ @result = false
96
+ elsif actual_min.nil? and actual_max.nil?
97
+ @negative_result_message << " with no minimum and maximum"
98
+ @result = false
99
+ elsif actual_min == min && actual_max == max
100
+ @positive_result_message << " within the range of #{@within.inspect}"
101
+ else
102
+ @negative_result_message << " within the range of #{(actual_min..actual_max).inspect}"
103
+ @result = false
104
+ end
105
+ end
106
+
107
+ def check_exact
108
+ if actual_is == @is
109
+ @positive_result_message << " as exactly #{@is}"
110
+ else
111
+ @negative_result_message << " as exactly #{actual_is}"
112
+ @result = false
113
+ end
114
+ end
115
+
116
+ def check_expected_message
117
+ actual_message = @validator.options[:message]
118
+ if actual_message.nil?
119
+ @negative_result_message << " with no custom message"
120
+ @result = false
121
+ elsif actual_message == @expected_message
122
+ @positive_result_message << " with custom message '#{@expected_message}'"
123
+ else
124
+ @negative_result_message << " got message '#{actual_message}'"
125
+ @result = false
126
+ end
127
+ end
128
+
129
+ def actual_is
130
+ actual_is = @validator.options[:is]
131
+ end
132
+
133
+ def actual_min
134
+ @validator.options[:minimum] || ((@validator.options[:in] || @validator.options[:within]).try(&:min))
135
+ end
136
+
137
+ def actual_max
138
+ @validator.options[:maximum] || ((@validator.options[:in] || @validator.options[:within]).try(&:max))
139
+ end
140
+ end
141
+
142
+ def validate_length_of(field)
143
+ ValidateLengthOfMatcher.new(field)
144
+ end
145
+ end
146
+ end
147
+ end