mongoid-rspec 2.1.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE +1 -1
  3. data/README.md +222 -102
  4. data/Rakefile +8 -5
  5. data/lib/matchers/accept_nested_attributes.rb +22 -23
  6. data/lib/matchers/allow_mass_assignment.rb +12 -12
  7. data/lib/matchers/associations.rb +85 -49
  8. data/lib/matchers/be_dynamic_document.rb +26 -0
  9. data/lib/matchers/be_mongoid_document.rb +26 -0
  10. data/lib/matchers/be_stored_in.rb +55 -0
  11. data/lib/matchers/have_field.rb +90 -0
  12. data/lib/matchers/have_timestamps.rb +61 -0
  13. data/lib/matchers/indexes/have_index_for.rb +16 -0
  14. data/lib/matchers/indexes/v3/have_index_for.rb +59 -0
  15. data/lib/matchers/indexes/v4/have_index_for.rb +54 -0
  16. data/lib/matchers/validations.rb +30 -11
  17. data/lib/matchers/validations/absence_of.rb +11 -0
  18. data/lib/matchers/validations/associated.rb +1 -1
  19. data/lib/matchers/validations/confirmation_of.rb +7 -1
  20. data/lib/matchers/validations/custom_validation_of.rb +1 -4
  21. data/lib/matchers/validations/exclusion_of.rb +5 -5
  22. data/lib/matchers/validations/format_of.rb +2 -2
  23. data/lib/matchers/validations/inclusion_of.rb +5 -5
  24. data/lib/matchers/validations/length_of.rb +13 -35
  25. data/lib/matchers/validations/numericality_of.rb +32 -16
  26. data/lib/matchers/validations/presence_of.rb +1 -1
  27. data/lib/matchers/validations/uniqueness_of.rb +7 -10
  28. data/lib/mongoid-rspec.rb +1 -33
  29. data/lib/mongoid/rspec.rb +46 -0
  30. data/lib/mongoid/rspec/version.rb +5 -0
  31. data/spec/models/article.rb +9 -6
  32. data/spec/models/comment.rb +1 -1
  33. data/spec/models/log.rb +3 -3
  34. data/spec/models/message.rb +17 -0
  35. data/spec/models/movie_article.rb +1 -2
  36. data/spec/models/person.rb +1 -1
  37. data/spec/models/profile.rb +2 -2
  38. data/spec/models/record.rb +1 -1
  39. data/spec/models/site.rb +5 -1
  40. data/spec/models/user.rb +12 -10
  41. data/spec/spec_helper.rb +12 -10
  42. data/spec/unit/accept_nested_attributes_spec.rb +1 -1
  43. data/spec/unit/associations_spec.rb +19 -7
  44. data/spec/unit/be_dynamic_document_spec.rb +21 -0
  45. data/spec/unit/be_mongoid_document_spec.rb +25 -0
  46. data/spec/unit/be_stored_in.rb +54 -0
  47. data/spec/unit/document_spec.rb +5 -14
  48. data/spec/unit/have_index_for_spec.rb +46 -0
  49. data/spec/unit/have_timestamps_spec.rb +71 -0
  50. data/spec/unit/validations_spec.rb +23 -14
  51. data/spec/validators/ssn_validator.rb +6 -6
  52. metadata +119 -43
  53. data/.document +0 -5
  54. data/.gitignore +0 -6
  55. data/.ruby-gemset +0 -1
  56. data/.ruby-version +0 -1
  57. data/.travis.yml +0 -10
  58. data/Gemfile +0 -4
  59. data/lib/matchers/collections.rb +0 -9
  60. data/lib/matchers/document.rb +0 -173
  61. data/lib/matchers/indexes.rb +0 -69
  62. data/lib/matchers/validations/with_message.rb +0 -27
  63. data/lib/mongoid-rspec/version.rb +0 -5
  64. data/mongoid-rspec.gemspec +0 -25
  65. data/spec/unit/collections_spec.rb +0 -7
  66. data/spec/unit/indexes_spec.rb +0 -17
@@ -0,0 +1,61 @@
1
+ module Mongoid
2
+ module Matchers
3
+ def have_timestamps
4
+ HaveTimestamps.new
5
+ end
6
+
7
+ class HaveTimestamps
8
+ def initialize
9
+ @root_module = 'Mongoid::Timestamps'
10
+ end
11
+
12
+ def matches?(actual)
13
+ @model = actual.is_a?(Class) ? actual : actual.class
14
+ @model.included_modules.include?(expected_module)
15
+ end
16
+
17
+ def for(phase)
18
+ raise('You\'ve already declared timetamp\'s sub-module via "for" clause') if @submodule
19
+
20
+ case @phase = phase.to_sym
21
+ when :creating then @submodule = 'Created'
22
+ when :updating then @submodule = 'Updated'
23
+ else
24
+ raise('Timestamps can be declared only for creating or updating')
25
+ end
26
+
27
+ self
28
+ end
29
+
30
+ def shortened
31
+ @shortened = true
32
+ self
33
+ end
34
+
35
+ def description
36
+ desc = 'be a Mongoid document with'
37
+ desc << ' shorted' if @shortened
38
+ desc << " #{@phase}" if @phase
39
+ desc << ' timestamps'
40
+ desc
41
+ end
42
+
43
+ def failure_message
44
+ "Expected #{@model.inspect} class to #{description}"
45
+ end
46
+
47
+ def failure_message_when_negated
48
+ "Expected #{@model.inspect} class to not #{description}"
49
+ end
50
+
51
+ private
52
+
53
+ def expected_module
54
+ expected_module = @root_module
55
+ expected_module << "::#{@submodule}" if @submodule
56
+ expected_module << '::Short' if @shortened
57
+ expected_module.constantize
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,16 @@
1
+ module Mongoid
2
+ module Matchers
3
+ class HaveIndexForBase
4
+ attr_reader :index_key, :index_options, :model
5
+
6
+ def initialize(index_key)
7
+ @index_key = index_key.symbolize_keys
8
+ end
9
+
10
+ def with_options(index_options = {})
11
+ @index_options = index_options
12
+ self
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,59 @@
1
+ module Mongoid
2
+ module Matchers
3
+ def have_index_for(index_key)
4
+ HaveIndexFor.new(index_key)
5
+ end
6
+
7
+ class HaveIndexFor < Mongoid::Matchers::HaveIndexForBase
8
+ def matches?(klass)
9
+ @model = klass.is_a?(Class) ? klass : klass.class
10
+ @errors = []
11
+
12
+ if model.index_options[index_key]
13
+ if !index_options.nil? && !index_options.empty?
14
+ index_options.each do |option, option_value|
15
+ if denormalising_options(model.index_options[index_key])[option] != option_value
16
+ @errors.push "index for #{index_key.inspect} with options of #{model.index_options[index_key].inspect}"
17
+ end
18
+ end
19
+ end
20
+ else
21
+ @errors.push "no index for #{index_key}"
22
+ end
23
+
24
+ @errors.empty?
25
+ end
26
+
27
+ def failure_message_for_should
28
+ "Expected #{model.inspect} to #{description}, got #{@errors.to_sentence}"
29
+ end
30
+
31
+ def failure_message_for_should_not
32
+ "Expected #{model.inspect} to not #{description}, got #{model.inspect} to #{description}"
33
+ end
34
+
35
+ alias failure_message failure_message_for_should
36
+ alias failure_message_when_negated failure_message_for_should_not
37
+
38
+ def description
39
+ desc = "have an index for #{index_key.inspect}"
40
+ desc << " with options of #{index_options.inspect}" if index_options
41
+ desc
42
+ end
43
+
44
+ private
45
+
46
+ MAPPINGS = {
47
+ dropDups: :drop_dups
48
+ }.freeze
49
+
50
+ def denormalising_options(opts)
51
+ options = {}
52
+ opts.each_pair do |option, value|
53
+ options[MAPPINGS[option] || option] = value
54
+ end
55
+ options
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,54 @@
1
+ module Mongoid
2
+ module Matchers
3
+ def have_index_for(index_key)
4
+ HaveIndexFor.new(index_key)
5
+ end
6
+
7
+ class HaveIndexFor < Mongoid::Matchers::HaveIndexForBase
8
+ def matches?(actual)
9
+ @model = actual.is_a?(Class) ? actual : actual.class
10
+
11
+ actual_index &&
12
+ expected_index.key == actual_index.key &&
13
+ expected_index.fields == actual_index.fields &&
14
+ (expected_index.options.to_a - actual_index.options.to_a).empty?
15
+ end
16
+
17
+ def failure_message
18
+ message = "Expected #{model.inspect} to #{description},"
19
+ message << if actual_index.nil?
20
+ ' found no index'
21
+ else
22
+ " got #{index_description(actual_index)}"
23
+ end
24
+ message
25
+ end
26
+
27
+ def failure_message_when_negated
28
+ "Expected #{model.inspect} to not #{description}, got #{index_description(actual_index)}"
29
+ end
30
+
31
+ def description
32
+ "have an index #{index_description(expected_index)}"
33
+ end
34
+
35
+ private
36
+
37
+ def index_description(index)
38
+ desc = index.key.inspect.to_s
39
+ desc << " for fields #{index.fields.inspect}" if index.fields.present?
40
+ desc << " including options #{index.options.inspect}" if index.options.present?
41
+ desc
42
+ end
43
+
44
+ def expected_index
45
+ @expected_index ||=
46
+ Mongoid::Indexable::Specification.new(model, index_key, index_options)
47
+ end
48
+
49
+ def actual_index
50
+ @actual_index ||= model.index_specification(expected_index.key)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,9 +1,7 @@
1
1
  module Mongoid
2
2
  module Matchers
3
3
  module Validations
4
-
5
4
  class HaveValidationMatcher
6
-
7
5
  def initialize(field, validation_type)
8
6
  @field = field.to_s
9
7
  @type = validation_type.to_s
@@ -13,9 +11,9 @@ module Mongoid
13
11
  def matches?(actual)
14
12
  @klass = actual.is_a?(Class) ? actual : actual.class
15
13
 
16
- @validator = @klass.validators_on(@field).detect{ |v|
17
- v.kind.to_s == @type and (!v.options[:on] or on_options_matches?(v))
18
- }
14
+ @validator = @klass.validators_on(@field).detect do |v|
15
+ (v.kind.to_s == @type) && (!v.options[:on] || on_options_matches?(v))
16
+ end
19
17
 
20
18
  if @validator
21
19
  @negative_result_message = "#{@type.inspect} validator on #{@field.inspect}"
@@ -26,6 +24,7 @@ module Mongoid
26
24
  end
27
25
  @result = true
28
26
  check_on if @options[:on]
27
+ check_expected_message if @expected_message
29
28
  @result
30
29
  end
31
30
 
@@ -37,12 +36,14 @@ module Mongoid
37
36
  "Expected #{@klass.inspect} to not #{description}; instead got #{@positive_result_message}"
38
37
  end
39
38
 
40
- alias :failure_message :failure_message_for_should
41
- alias :failure_message_when_negated :failure_message_for_should_not
39
+ alias failure_message failure_message_for_should
40
+ alias failure_message_when_negated failure_message_for_should_not
42
41
 
43
42
  def description
44
43
  desc = "have #{@type.inspect} validator on #{@field.inspect}"
45
44
  desc << " on #{@options[:on]}" if @options[:on]
45
+ desc << " with message #{@expected_message.inspect}" if @expected_message
46
+
46
47
  desc
47
48
  end
48
49
 
@@ -51,13 +52,20 @@ module Mongoid
51
52
  self
52
53
  end
53
54
 
55
+ def with_message(message)
56
+ @expected_message = message
57
+ self
58
+ end
59
+
60
+ private
61
+
54
62
  def check_on
55
63
  validator_on_methods = [@validator.options[:on]].flatten
56
64
 
57
65
  if validator_on_methods.any?
58
66
  message = " on methods: #{validator_on_methods}"
59
67
 
60
- if on_options_covered_by?( @validator )
68
+ if on_options_covered_by?(@validator)
61
69
  @positive_result_message << message
62
70
  else
63
71
  @negative_result_message << message
@@ -66,15 +74,26 @@ module Mongoid
66
74
  end
67
75
  end
68
76
 
69
- private
70
-
71
77
  def on_options_matches?(validator)
72
- @options[:on] and validator.options[:on] and on_options_covered_by?(validator)
78
+ @options[:on] && validator.options[:on] && on_options_covered_by?(validator)
73
79
  end
74
80
 
75
81
  def on_options_covered_by?(validator)
76
82
  ([@options[:on]].flatten - [validator.options[:on]].flatten).empty?
77
83
  end
84
+
85
+ def check_expected_message
86
+ actual_message = @validator.options[:message]
87
+ if actual_message.nil?
88
+ @negative_result_message << ' with no custom message'
89
+ @result = false
90
+ elsif actual_message == @expected_message
91
+ @positive_result_message << " with custom message '#{@expected_message}'"
92
+ else
93
+ @negative_result_message << " got message '#{actual_message}'"
94
+ @result = false
95
+ end
96
+ end
78
97
  end
79
98
  end
80
99
  end
@@ -0,0 +1,11 @@
1
+ if Mongoid::Compatibility::Version.mongoid4_or_newer?
2
+ module Mongoid
3
+ module Matchers
4
+ module Validations
5
+ def validate_absence_of(field)
6
+ HaveValidationMatcher.new(field, :absence)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -16,4 +16,4 @@ module Mongoid
16
16
  end
17
17
  end
18
18
  end
19
- end
19
+ end
@@ -1,8 +1,14 @@
1
1
  module Mongoid
2
2
  module Matchers
3
3
  module Validations
4
+ class ValidateConfirmationOfMatcher < HaveValidationMatcher
5
+ def initialize(name)
6
+ super(name, :confirmation)
7
+ end
8
+ end
9
+
4
10
  def validate_confirmation_of(field)
5
- HaveValidationMatcher.new(field, :confirmation)
11
+ ValidateConfirmationOfMatcher.new(field)
6
12
  end
7
13
  end
8
14
  end
@@ -2,7 +2,6 @@ module Mongoid
2
2
  module Matchers
3
3
  module Validations
4
4
  class ValidateWithCustomValidatorMatcher < HaveValidationMatcher
5
- include WithMessage
6
5
  def initialize(field)
7
6
  super(field, :custom)
8
7
  end
@@ -15,7 +14,6 @@ module Mongoid
15
14
  def matches?(actual)
16
15
  return false unless (@result = super(actual))
17
16
  check_custom_validator if @custom_validator
18
- check_expected_message if @expected_message
19
17
 
20
18
  @result
21
19
  end
@@ -23,14 +21,13 @@ module Mongoid
23
21
  def description
24
22
  options_desc = []
25
23
  options_desc << " with custom validator #{@custom_validator.name}" if @validator
26
- options_desc << " with message '#{@expected_message}'" if @expected_message
27
24
  "validate field #{@field.inspect}" << options_desc.to_sentence
28
25
  end
29
26
 
30
27
  private
31
28
 
32
29
  def check_custom_validator
33
- if @validator.kind_of? @custom_validator
30
+ if @validator.is_a? @custom_validator
34
31
  @positive_result_message << " with custom validator of type #{@custom_validator.name}"
35
32
  else
36
33
  @negative_result_message << " with custom validator not of type #{@custom_validator.name}"
@@ -18,13 +18,13 @@ module Mongoid
18
18
  raw_validator_not_allowed_values = @validator.options[:in]
19
19
 
20
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
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
24
 
25
25
  allowed_values = @not_allowed_values - validator_not_allowed_values
26
26
  if allowed_values.empty?
27
- @positive_result_message = @positive_result_message << " not allowing all values mentioned"
27
+ @positive_result_message = @positive_result_message << ' not allowing all values mentioned'
28
28
  else
29
29
  @negative_result_message = @negative_result_message << " allowing the following the ff. values: #{allowed_values.inspect}"
30
30
  result = false
@@ -46,4 +46,4 @@ module Mongoid
46
46
  end
47
47
  end
48
48
  end
49
- end
49
+ end
@@ -43,7 +43,7 @@ module Mongoid
43
43
  end
44
44
 
45
45
  if @invalid_value
46
- if !(@invalid_value =~ @validator.options[:with])
46
+ if @invalid_value !~ @validator.options[:with]
47
47
  @positive_result_message = @positive_result_message << " with #{@invalid_value.inspect} as an invalid value"
48
48
  else
49
49
  @negative_result_message = @negative_result_message << " with #{@invalid_value.inspect} as a valid value"
@@ -68,4 +68,4 @@ module Mongoid
68
68
  end
69
69
  end
70
70
  end
71
- end
71
+ end
@@ -18,13 +18,13 @@ module Mongoid
18
18
  raw_validator_allowed_values = @validator.options[:in]
19
19
 
20
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
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
24
 
25
25
  not_allowed_values = @allowed_values - validator_allowed_values
26
26
  if not_allowed_values.empty?
27
- @positive_result_message = @positive_result_message << " allowing all values mentioned"
27
+ @positive_result_message = @positive_result_message << ' allowing all values mentioned'
28
28
  else
29
29
  @negative_result_message = @negative_result_message << " not allowing these values: #{not_allowed_values.inspect}"
30
30
  result = false
@@ -46,4 +46,4 @@ module Mongoid
46
46
  end
47
47
  end
48
48
  end
49
- end
49
+ end
@@ -2,8 +2,6 @@ module Mongoid
2
2
  module Matchers
3
3
  module Validations
4
4
  class ValidateLengthOfMatcher < HaveValidationMatcher
5
- include WithMessage
6
-
7
5
  def initialize(name)
8
6
  super(name, :length)
9
7
  end
@@ -12,30 +10,25 @@ module Mongoid
12
10
  @maximum = value
13
11
  self
14
12
  end
15
- alias :less_than :with_maximum
13
+ alias less_than with_maximum
16
14
 
17
15
  def with_minimum(value)
18
16
  @minimum = value
19
17
  self
20
18
  end
21
- alias :greater_than :with_minimum
19
+ alias greater_than with_minimum
22
20
 
23
21
  def within(value)
24
22
  @within = value
25
23
  self
26
24
  end
27
- alias :in :within
25
+ alias in within
28
26
 
29
27
  def as_exactly(value)
30
28
  @is = value
31
29
  self
32
30
  end
33
- alias :is :as_exactly
34
-
35
- def with_message(message)
36
- @expected_message = message
37
- self
38
- end
31
+ alias is as_exactly
39
32
 
40
33
  def matches?(actual)
41
34
  return false unless @result = super(actual)
@@ -44,7 +37,6 @@ module Mongoid
44
37
  check_minimum if @minimum
45
38
  check_range if @within
46
39
  check_exact if @is
47
- check_expected_message if @expected_message
48
40
 
49
41
  @result
50
42
  end
@@ -55,7 +47,6 @@ module Mongoid
55
47
  options_desc << "with maximum of #{@maximum}" if @maximum
56
48
  options_desc << "within the range of #{@within}" if @within
57
49
  options_desc << "as exactly #{@is}" if @is
58
- options_desc << "with message '#{@expected_message}'" if @expected_message
59
50
  super << " #{options_desc.to_sentence}"
60
51
  end
61
52
 
@@ -63,7 +54,7 @@ module Mongoid
63
54
 
64
55
  def check_maximum
65
56
  if actual_max.nil?
66
- @negative_result_message << " with no maximum"
57
+ @negative_result_message << ' with no maximum'
67
58
  @result = false
68
59
  elsif actual_max == @maximum
69
60
  @positive_result_message << " with maximum of #{@maximum}"
@@ -75,7 +66,7 @@ module Mongoid
75
66
 
76
67
  def check_minimum
77
68
  if actual_min.nil?
78
- @negative_result_message << " with no minimum"
69
+ @negative_result_message << ' with no minimum'
79
70
  @result = false
80
71
  elsif actual_min == @minimum
81
72
  @positive_result_message << " with minimum of #{@minimum}"
@@ -86,15 +77,15 @@ module Mongoid
86
77
  end
87
78
 
88
79
  def check_range
89
- min, max = [@within.min, @within.max]
90
- if !actual_min.nil? and actual_max.nil?
80
+ min, max = @within.minmax
81
+ if !actual_min.nil? && actual_max.nil?
91
82
  @negative_result_message << " with no minimum but with maximum of #{actual_max}"
92
83
  @result = false
93
- elsif actual_min.nil? and !actual_max.nil?
84
+ elsif actual_min.nil? && !actual_max.nil?
94
85
  @negative_result_message << " with minimum_of #{actual_min} but no maximum"
95
86
  @result = false
96
- elsif actual_min.nil? and actual_max.nil?
97
- @negative_result_message << " with no minimum and maximum"
87
+ elsif actual_min.nil? && actual_max.nil?
88
+ @negative_result_message << ' with no minimum and maximum'
98
89
  @result = false
99
90
  elsif actual_min == min && actual_max == max
100
91
  @positive_result_message << " within the range of #{@within.inspect}"
@@ -113,29 +104,16 @@ module Mongoid
113
104
  end
114
105
  end
115
106
 
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
107
  def actual_is
130
108
  actual_is = @validator.options[:is]
131
109
  end
132
110
 
133
111
  def actual_min
134
- @validator.options[:minimum] || ((@validator.options[:in] || @validator.options[:within]).try(&:min))
112
+ @validator.options[:minimum] || (@validator.options[:in] || @validator.options[:within]).try(&:min)
135
113
  end
136
114
 
137
115
  def actual_max
138
- @validator.options[:maximum] || ((@validator.options[:in] || @validator.options[:within]).try(&:max))
116
+ @validator.options[:maximum] || (@validator.options[:in] || @validator.options[:within]).try(&:max)
139
117
  end
140
118
  end
141
119