mongoid-rspec 1.8.0 → 1.9.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.
@@ -1,71 +1,71 @@
1
1
  module Mongoid
2
2
  module Matchers
3
- module Validations
3
+ module Validations
4
4
  class ValidateFormatOfMatcher < HaveValidationMatcher
5
5
  def initialize(field)
6
6
  super(field, :format)
7
7
  end
8
-
8
+
9
9
  def with_format(format)
10
10
  @format = format
11
11
  self
12
12
  end
13
-
13
+
14
14
  def to_allow(valid_value)
15
15
  @valid_value = valid_value
16
- self
16
+ self
17
17
  end
18
-
18
+
19
19
  def not_to_allow(invalid_value)
20
20
  @invalid_value = invalid_value
21
- self
21
+ self
22
22
  end
23
-
23
+
24
24
  def matches?(actual)
25
25
  return false unless result = super(actual)
26
-
26
+
27
27
  if @format
28
28
  if @validator.options[:with] == @format
29
29
  @positive_result_message = @positive_result_message << " with format #{@validator.options[:format].inspect}"
30
30
  else
31
31
  @negative_result_message = @negative_result_message << " with format #{@validator.options[:format].inspect}"
32
32
  result = false
33
- end
33
+ end
34
34
  end
35
-
35
+
36
36
  if @valid_value
37
37
  if @validator.options[:with] =~ @valid_value
38
38
  @positive_result_message = @positive_result_message << " with #{@valid_value.inspect} as a valid value"
39
39
  else
40
40
  @negative_result_message = @negative_result_message << " with #{@valid_value.inspect} as an invalid value"
41
41
  result = false
42
- end
42
+ end
43
43
  end
44
-
45
- if @invalid_value
44
+
45
+ if @invalid_value
46
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"
50
50
  result = false
51
- end
51
+ end
52
52
  end
53
-
54
- result
53
+
54
+ result
55
55
  end
56
-
56
+
57
57
  def description
58
58
  options_desc = []
59
59
  options_desc << " with format #{@format.inspect}" if @format
60
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
61
+ options_desc << " not allowing the value #{@invalid_value.inspect}" if @invalid_value
62
62
  super << options_desc.to_sentence
63
- end
63
+ end
64
64
  end
65
65
 
66
66
  def validate_format_of(field)
67
67
  ValidateFormatOfMatcher.new(field)
68
- end
68
+ end
69
69
  end
70
70
  end
71
- end
71
+ end
@@ -2,6 +2,8 @@ module Mongoid
2
2
  module Matchers
3
3
  module Validations
4
4
  class ValidateLengthOfMatcher < HaveValidationMatcher
5
+ include WithMessage
6
+
5
7
  def initialize(name)
6
8
  super(name, :length)
7
9
  end
@@ -30,6 +32,11 @@ module Mongoid
30
32
  end
31
33
  alias :is :as_exactly
32
34
 
35
+ def with_message(message)
36
+ @expected_message = message
37
+ self
38
+ end
39
+
33
40
  def matches?(actual)
34
41
  return false unless @result = super(actual)
35
42
 
@@ -37,6 +44,7 @@ module Mongoid
37
44
  check_minimum if @minimum
38
45
  check_range if @within
39
46
  check_exact if @is
47
+ check_expected_message if @expected_message
40
48
 
41
49
  @result
42
50
  end
@@ -47,6 +55,7 @@ module Mongoid
47
55
  options_desc << "with maximum of #{@maximum}" if @maximum
48
56
  options_desc << "within the range of #{@within}" if @within
49
57
  options_desc << "as exactly #{@is}" if @is
58
+ options_desc << "with message '#{@expected_message}'" if @expected_message
50
59
  super << " #{options_desc.to_sentence}"
51
60
  end
52
61
 
@@ -104,6 +113,19 @@ module Mongoid
104
113
  end
105
114
  end
106
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
+
107
129
  def actual_is
108
130
  actual_is = @validator.options[:is]
109
131
  end
@@ -2,14 +2,14 @@ module Mongoid
2
2
  module Matchers
3
3
  module Validations
4
4
  class ValidateNumericalityOfMatcher < HaveValidationMatcher
5
- @@allowed_options = [:equal_to, :greater_than, :greater_than_or_equal_to, :less_than, :less_than_or_equal_to,
5
+ @@allowed_options = [:equal_to, :greater_than, :greater_than_or_equal_to, :less_than, :less_than_or_equal_to,
6
6
  :even, :odd, :only_integer, :allow_nil, :nil]
7
-
7
+
8
8
  def initialize(field)
9
9
  super(field, :numericality)
10
10
  @options = {}
11
11
  end
12
-
12
+
13
13
  def to_allow(options)
14
14
  options[:equal_to] = options if options.is_a?(Numeric)
15
15
  options[:allow_nil] = options.delete(:nil) if options.has_key?(:nil)
@@ -21,7 +21,7 @@ module Mongoid
21
21
 
22
22
  def matches?(actual)
23
23
  return false unless result = super(actual)
24
-
24
+
25
25
  @@allowed_options.each do |comparator|
26
26
  if @options.has_key?(comparator) and !([:even, :odd, :only_integer].include?(comparator) and !@validator.options.include?(comparator))
27
27
  result &= (@validator.options[comparator] == @options[comparator])
@@ -35,12 +35,12 @@ module Mongoid
35
35
  def description
36
36
  super << options_message(@options)
37
37
  end
38
-
38
+
39
39
  protected
40
-
40
+
41
41
  def options_message(options)
42
42
  type_msg = []
43
- comp_msg = []
43
+ comp_msg = []
44
44
  options.each_pair do |key, value|
45
45
  case key
46
46
  when :allow_nil
@@ -52,10 +52,10 @@ module Mongoid
52
52
  comp_msg << "#{key.to_s.gsub("_", " ")} #{value.inspect}"
53
53
  end
54
54
  end
55
- allow_nil = (options[:allow_nil] ? "nil" : "non-nil") if options.has_key?(:allow_nil)
55
+ allow_nil = (options[:allow_nil] ? "nil" : "non-nil") if options.has_key?(:allow_nil)
56
56
  ["", "allowing", allow_nil, type_msg.any? ? type_msg.to_sentence : nil, "values", comp_msg.any? ? comp_msg.to_sentence : nil].compact.join(" ")
57
- end
58
-
57
+ end
58
+
59
59
  def method_missing(m, *args, &block)
60
60
  if @@allowed_options.include?(m.to_sym)
61
61
  raise ArgumentError, "wrong number of arguments (#{args.length} for 1)" if args.length > 1
@@ -1,9 +1,9 @@
1
1
  module Mongoid
2
2
  module Matchers
3
- module Validations
3
+ module Validations
4
4
  def validate_presence_of(field)
5
5
  HaveValidationMatcher.new(field, :presence)
6
6
  end
7
7
  end
8
8
  end
9
- end
9
+ end
@@ -1,40 +1,36 @@
1
1
  module Mongoid
2
2
  module Matchers
3
- module Validations
3
+ module Validations
4
4
  class ValidateUniquenessOfMatcher < HaveValidationMatcher
5
+ include WithMessage
5
6
  def initialize(field)
6
7
  super(field, :uniqueness)
7
8
  end
8
-
9
+
9
10
  def scoped_to(*scope)
10
11
  @scope = [scope].flatten.map(&:to_sym)
11
12
  self
12
- end
13
+ end
13
14
  alias_method :scoped_on, :scoped_to
14
-
15
+
15
16
  def case_insensitive
16
17
  @case_insensitive = true
17
18
  self
18
19
  end
19
-
20
+
20
21
  def allow_blank?(allow_blank)
21
22
  @allow_blank = allow_blank
22
23
  self
23
24
  end
24
25
 
25
- def with_message(message)
26
- @expected_message = message
27
- self
28
- end
29
-
30
26
  def matches?(actual)
31
27
  return false unless @result = super(actual)
32
-
28
+
33
29
  check_scope if @scope
34
30
  check_allow_blank if @allow_blank
35
31
  check_case_sensitivity if @case_insensitive
36
32
  check_expected_message if @expected_message
37
-
33
+
38
34
  @result
39
35
  end
40
36
 
@@ -46,7 +42,7 @@ module Mongoid
46
42
  options_desc << " with message '#{@expected_message}'" if @expected_message
47
43
  super << options_desc.to_sentence
48
44
  end
49
-
45
+
50
46
  private
51
47
 
52
48
  def check_allow_blank
@@ -67,7 +63,7 @@ module Mongoid
67
63
  @result = false
68
64
  end
69
65
  end
70
-
66
+
71
67
  def check_case_sensitivity
72
68
  if @validator.options[:case_sensitive] == false
73
69
  @positive_result_message << " with case insensitive values"
@@ -76,24 +72,11 @@ module Mongoid
76
72
  @result = false
77
73
  end
78
74
  end
79
-
80
- def check_expected_message
81
- actual_message = @validator.options[:message]
82
- if actual_message.nil?
83
- @negative_result_message << " with no custom message"
84
- @result = false
85
- elsif actual_message == @expected_message
86
- @positive_result_message << " with custom message '#{@expected_message}'"
87
- else
88
- @negative_result_message << " got message '#{actual_message}'"
89
- @result = false
90
- end
91
- end
92
75
  end
93
-
76
+
94
77
  def validate_uniqueness_of(field)
95
78
  ValidateUniquenessOfMatcher.new(field)
96
- end
79
+ end
97
80
  end
98
81
  end
99
- end
82
+ end
@@ -0,0 +1,27 @@
1
+ module Mongoid
2
+ module Matchers
3
+ module Validations
4
+ module WithMessage
5
+ def with_message(message)
6
+ @expected_message = message
7
+ self
8
+ end
9
+
10
+ private
11
+
12
+ def check_expected_message
13
+ actual_message = @validator.options[:message]
14
+ if actual_message.nil?
15
+ @negative_result_message << " with no custom message"
16
+ @result = false
17
+ elsif actual_message == @expected_message
18
+ @positive_result_message << " with custom message '#{@expected_message}'"
19
+ else
20
+ @negative_result_message << " got message '#{actual_message}'"
21
+ @result = false
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -13,7 +13,9 @@ module Mongoid
13
13
  def matches?(actual)
14
14
  @klass = actual.is_a?(Class) ? actual : actual.class
15
15
 
16
- @validator = @klass.validators_on(@field).detect{|v| v.kind.to_s == @type}
16
+ @validator = @klass.validators_on(@field).detect{ |v|
17
+ v.kind.to_s == @type and (!v.options[:on] or on_options_matches?(v))
18
+ }
17
19
 
18
20
  if @validator
19
21
  @negative_result_message = "#{@type.inspect} validator on #{@field.inspect}"
@@ -52,7 +54,7 @@ module Mongoid
52
54
  if validator_on_methods.any?
53
55
  message = " on methods: #{validator_on_methods}"
54
56
 
55
- if (@options[:on] - validator_on_methods).empty?
57
+ if on_options_covered_by?( @validator )
56
58
  @positive_result_message << message
57
59
  else
58
60
  @negative_result_message << message
@@ -60,6 +62,16 @@ module Mongoid
60
62
  end
61
63
  end
62
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
63
75
  end
64
76
  end
65
77
  end
@@ -1,5 +1,5 @@
1
1
  module Mongoid
2
2
  module Rspec
3
- VERSION = "1.8.0"
3
+ VERSION = "1.9.0"
4
4
  end
5
5
  end
data/lib/mongoid-rspec.rb CHANGED
@@ -10,6 +10,7 @@ require 'matchers/indexes'
10
10
  require 'matchers/allow_mass_assignment'
11
11
  require 'matchers/accept_nested_attributes'
12
12
  require 'matchers/validations'
13
+ require 'matchers/validations/with_message'
13
14
  require 'matchers/validations/associated'
14
15
  require 'matchers/validations/confirmation_of'
15
16
  require 'matchers/validations/exclusion_of'
@@ -21,5 +21,5 @@ Gem::Specification.new do |s|
21
21
 
22
22
  s.add_dependency 'rake'
23
23
  s.add_dependency 'mongoid', '>= 3.0.1'
24
- s.add_dependency 'rspec', '>= 2.9'
24
+ s.add_dependency 'rspec', '>= 2.14'
25
25
  end
@@ -5,20 +5,24 @@ class Article
5
5
  include Mongoid::Versioning
6
6
  include Mongoid::MultiParameterAttributes
7
7
 
8
- field :title, :localize => true
8
+ field :title, localize: true
9
9
  field :content
10
- field :published, :type => Boolean, :default => false
11
- field :allow_comments, :type => Boolean, :default => true
12
- field :number_of_comments, :type => Integer
10
+ field :published, type: Boolean, default: false
11
+ field :allow_comments, type: Boolean, default: true
12
+ field :number_of_comments, type: Integer
13
+ field :status, type: Symbol
13
14
 
14
- embeds_many :comments
15
+ embeds_many :comments, cascade_callbacks: true
15
16
  embeds_one :permalink
16
- belongs_to :author, :class_name => 'User', :inverse_of => :articles, :index => true
17
+ belongs_to :author, class_name: 'User', inverse_of: :articles, index: true
17
18
 
18
- validates :title, :presence => true
19
+ validates :title, presence: true
19
20
 
20
- validates_length_of :title, :within => 8..16
21
- validates_length_of :content, :minimum => 200
21
+ validates_inclusion_of :status, in: [:pending], on: :create
22
+ validates_inclusion_of :status, in: [:approved, :rejected ], on: :update
23
+
24
+ validates_length_of :title, within: 8..16
25
+ validates_length_of :content, minimum: 200
22
26
 
23
27
  index({ title: 1 }, { unique: true, background: true })
24
28
  index({ published: 1 })
@@ -1,6 +1,6 @@
1
1
  class Comment
2
2
  include Mongoid::Document
3
3
 
4
- embedded_in :article, :inverse_of => :comments
5
- belongs_to :user, :inverse_of => :comments
4
+ embedded_in :article, inverse_of: :comments, polymorphic: true
5
+ belongs_to :user, inverse_of: :comments
6
6
  end
@@ -1,7 +1,8 @@
1
1
  class MovieArticle < Article
2
- field :rating, :type => Float
3
- field :classification, :type => Integer
4
-
5
- validates_numericality_of :rating, :greater_than => 0, :less_than_or_equal_to => 5
6
- validates_numericality_of :classification, :even => true, :only_integer => true, :allow_nil => false
2
+
3
+ field :rating, type: Float
4
+ field :classification, type: Integer
5
+
6
+ validates :rating, numericality: { greater_than: 0, less_than_or_equal_to: 5 }
7
+ validates :classification, numericality: { even: true, only_integer: true, allow_nil: false }
7
8
  end
@@ -1,5 +1,5 @@
1
1
  class Permalink
2
2
  include Mongoid::Document
3
3
 
4
- embedded_in :linkable, :inverse_of => :link
4
+ embedded_in :linkable, inverse_of: :link
5
5
  end
@@ -1,13 +1,10 @@
1
1
  require 'ssn_validator.rb'
2
2
 
3
3
  class Person
4
-
5
4
  include Mongoid::Document
6
5
 
7
6
  field :name
8
7
  field :ssn
9
8
 
10
9
  validates :ssn, ssn: true
11
-
12
-
13
10
  end
@@ -1,14 +1,16 @@
1
1
  class Profile
2
2
  include Mongoid::Document
3
-
3
+
4
4
  field :first_name
5
5
  field :last_name
6
6
  field :age
7
-
8
- embedded_in :user, :inverse_of => :profile
9
-
10
- validates_numericality_of :age, :greater_than => 0
11
- validates_acceptance_of :terms_of_service
7
+ field :hobbies, type: Array, default: []
8
+
9
+ embedded_in :user, inverse_of: :profile
10
+
11
+ validates :age, numericality: { greater_than: 0 }
12
+ validates :terms_of_service, acceptance: true
13
+ validates :hobbies, length: { minimum: 1, message: "requires at least one hobby" }
12
14
 
13
- index({first_name: 1, last_name: 1 })
15
+ index({ first_name: 1, last_name: 1 })
14
16
  end
@@ -1,5 +1,5 @@
1
1
  class Record
2
2
  include Mongoid::Document
3
3
 
4
- belongs_to :user, :inverse_of => :record
4
+ belongs_to :user, inverse_of: :record
5
5
  end
data/spec/models/site.rb CHANGED
@@ -3,8 +3,7 @@ class Site
3
3
 
4
4
  field :name
5
5
 
6
- has_many :users, :inverse_of => :site
7
-
8
- validates :name, :presence => true, :uniqueness => true
6
+ has_many :users, inverse_of: :site, order: :email.desc
9
7
 
8
+ validates :name, presence: true, uniqueness: true
10
9
  end
data/spec/models/user.rb CHANGED
@@ -10,25 +10,26 @@ class User
10
10
  field :provider_uid
11
11
  field :locale
12
12
 
13
- belongs_to :site, :inverse_of => :users
14
- has_many :articles, :foreign_key => :author_id, :order => :title
15
- has_many :comments, :dependent => :destroy, :autosave => true
16
- has_and_belongs_to_many :children, :class_name => "User"
17
- has_one :record, :autobuild => true
13
+ belongs_to :site, inverse_of: :users
14
+ has_many :articles, foreign_key: :author_id, order: :title
15
+ has_many :comments, dependent: :destroy, autosave: true
16
+ has_and_belongs_to_many :children, class_name: "User"
17
+ has_one :record, autobuild: true
18
18
 
19
19
  embeds_one :profile
20
20
 
21
- validates :login, :presence => true, :uniqueness => { :scope => :site }, :format => { :with => /^[\w\-]+$/ }, :exclusion => { :in => ["super", "index", "edit"]}
22
- validates :email, :uniqueness => { :case_sensitive => false, :scope => :site, :message => "is already taken" }, :confirmation => true
23
- validates :role, :presence => true, :inclusion => { :in => ["admin", "moderator", "member"]}
24
- validates :profile, :presence => true, :associated => true
25
- validates :age, :presence => true, :numericality => true, :inclusion => { :in => 23..42 }, :on => [:create, :update]
26
- validates :password, :presence => true, :on => [:create, :update]
21
+ validates :login, presence: true, uniqueness: { scope: :site }, format: { with: /^[\w\-]+$/ }, exclusion: { in: ["super", "index", "edit"] }
22
+ validates :email, uniqueness: { case_sensitive: false, scope: :site, message: "is already taken" }, confirmation: true
23
+ validates :role, presence: true, inclusion: { in: ["admin", "moderator", "member"] }
24
+ validates :profile, presence: true, associated: true
25
+ validates :age, presence: true, numericality: true, inclusion: { in: 23..42 }, on: [:create, :update]
26
+ validates :password, presence: true, on: [:create, :update]
27
+ validates :password, exclusion: { in: ->(user) { ['password'] } }
27
28
  validates :provider_uid, presence: true
28
- validates :locale, :inclusion => {:in => lambda { |user| [:en, :ru] } }
29
+ validates :locale, inclusion: { in: ->(user) { [:en, :ru] } }
29
30
 
30
31
  attr_accessible :login, :email, :age, :password
31
- attr_accessible :role, :as => :admin
32
+ attr_accessible :role, as: :admin
32
33
 
33
34
  accepts_nested_attributes_for :articles, :comments
34
35
 
@@ -8,7 +8,7 @@ describe "AllowMassAssignment" do
8
8
  it { should allow_mass_assignment_of(:password) }
9
9
  it { should allow_mass_assignment_of(:password) }
10
10
  it { should allow_mass_assignment_of(:role).as(:admin) }
11
-
11
+
12
12
  it { should_not allow_mass_assignment_of(:role) }
13
13
  end
14
14
  end
@@ -19,12 +19,12 @@ describe "Associations" do
19
19
 
20
20
  describe Article do
21
21
  it { should belong_to(:author).of_type(User).as_inverse_of(:articles).with_index }
22
- it { should embed_many(:comments) }
22
+ it { should embed_many(:comments).with_cascading_callbacks }
23
23
  it { should embed_one(:permalink) }
24
24
  end
25
25
 
26
26
  describe Comment do
27
- it { should be_embedded_in(:article).as_inverse_of(:comments) }
27
+ it { should be_embedded_in(:article).as_inverse_of(:comments).with_polymorphism }
28
28
  it { should belong_to(:user).as_inverse_of(:comments) }
29
29
  end
30
30
 
@@ -37,6 +37,6 @@ describe "Associations" do
37
37
  end
38
38
 
39
39
  describe Site do
40
- it { should have_many(:users).as_inverse_of(:site) }
40
+ it { should have_many(:users).as_inverse_of(:site).ordered_by(:email.desc) }
41
41
  end
42
42
  end
@@ -6,7 +6,7 @@ describe "Indexes" do
6
6
  it { should have_index_for(title: 1).with_options(unique: true, background: true) }
7
7
  it { should have_index_for('permalink._id' => 1) }
8
8
  end
9
-
9
+
10
10
  describe Profile do
11
11
  it { should have_index_for(first_name: 1, last_name: 1) }
12
12
  end
@@ -13,6 +13,7 @@ describe "Validations" do
13
13
  it { should validate_format_of(:login).to_allow("valid_login").not_to_allow("invalid login") }
14
14
  it { should validate_associated(:profile) }
15
15
  it { should validate_exclusion_of(:login).to_not_allow("super", "index", "edit") }
16
+ it { should validate_exclusion_of(:password).to_not_allow("password") }
16
17
  it { should validate_inclusion_of(:role).to_allow("admin", "member") }
17
18
  it { should validate_confirmation_of(:email) }
18
19
  it { should validate_presence_of(:age).on(:create, :update) }
@@ -26,12 +27,15 @@ describe "Validations" do
26
27
  describe Profile do
27
28
  it { should validate_numericality_of(:age).greater_than(0) }
28
29
  it { should validate_acceptance_of(:terms_of_service) }
30
+ it { should validate_length_of(:hobbies).with_minimum(1).with_message("requires at least one hobby") }
29
31
  end
30
32
 
31
33
  describe Article do
32
34
  it { should validate_length_of(:title).within(8..16) }
33
35
  it { should_not validate_length_of(:content).greater_than(200).less_than(16) }
34
36
  it { should validate_length_of(:content).greater_than(200) }
37
+ it { should validate_inclusion_of(:status).to_allow([:pending]).on( :create ) }
38
+ it { should validate_inclusion_of(:status).to_allow([:approved, :rejected]).on( :update ) }
35
39
  end
36
40
 
37
41
  describe MovieArticle do
@@ -1,7 +1,9 @@
1
1
  class SsnValidator < ActiveModel::EachValidator
2
2
 
3
3
  def validate_each(record, attribute, value)
4
- record.errors[attribute] << "#{value} is not a valid Social Security Number" unless valid_ssn?(record, attribute, value)
4
+ unless valid_ssn?(record, attribute, value)
5
+ record.errors[attribute] << "#{value} is not a valid Social Security Number"
6
+ end
5
7
  end
6
8
 
7
9
  def self.kind() :custom end