mongoid4-rspec 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) 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 +21 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE +20 -0
  9. data/README.md +207 -0
  10. data/Rakefile +17 -0
  11. data/lib/matchers/accept_nested_attributes.rb +67 -0
  12. data/lib/matchers/allow_mass_assignment.rb +101 -0
  13. data/lib/matchers/associations.rb +316 -0
  14. data/lib/matchers/collections.rb +9 -0
  15. data/lib/matchers/document.rb +163 -0
  16. data/lib/matchers/indexes.rb +84 -0
  17. data/lib/matchers/validations.rb +81 -0
  18. data/lib/matchers/validations/acceptance_of.rb +9 -0
  19. data/lib/matchers/validations/associated.rb +19 -0
  20. data/lib/matchers/validations/confirmation_of.rb +9 -0
  21. data/lib/matchers/validations/custom_validation_of.rb +47 -0
  22. data/lib/matchers/validations/exclusion_of.rb +49 -0
  23. data/lib/matchers/validations/format_of.rb +71 -0
  24. data/lib/matchers/validations/inclusion_of.rb +49 -0
  25. data/lib/matchers/validations/length_of.rb +147 -0
  26. data/lib/matchers/validations/numericality_of.rb +74 -0
  27. data/lib/matchers/validations/presence_of.rb +9 -0
  28. data/lib/matchers/validations/uniqueness_of.rb +82 -0
  29. data/lib/matchers/validations/with_message.rb +27 -0
  30. data/lib/mongoid-rspec.rb +33 -0
  31. data/lib/mongoid-rspec/version.rb +5 -0
  32. data/mongoid-rspec.gemspec +25 -0
  33. data/mongoid4-rspec.gemspec +25 -0
  34. data/spec/models/article.rb +29 -0
  35. data/spec/models/comment.rb +6 -0
  36. data/spec/models/log.rb +4 -0
  37. data/spec/models/movie_article.rb +8 -0
  38. data/spec/models/permalink.rb +5 -0
  39. data/spec/models/person.rb +10 -0
  40. data/spec/models/profile.rb +16 -0
  41. data/spec/models/record.rb +5 -0
  42. data/spec/models/site.rb +9 -0
  43. data/spec/models/user.rb +36 -0
  44. data/spec/spec_helper.rb +32 -0
  45. data/spec/unit/accept_nested_attributes_spec.rb +12 -0
  46. data/spec/unit/associations_spec.rb +42 -0
  47. data/spec/unit/collections_spec.rb +7 -0
  48. data/spec/unit/document_spec.rb +21 -0
  49. data/spec/unit/indexes_spec.rb +13 -0
  50. data/spec/unit/validations_spec.rb +52 -0
  51. data/spec/validators/ssn_validator.rb +16 -0
  52. metadata +137 -0
@@ -0,0 +1,74 @@
1
+ module Mongoid
2
+ module Matchers
3
+ module Validations
4
+ class ValidateNumericalityOfMatcher < HaveValidationMatcher
5
+ @@allowed_options = [:equal_to, :greater_than, :greater_than_or_equal_to, :less_than, :less_than_or_equal_to,
6
+ :even, :odd, :only_integer, :allow_nil, :nil]
7
+
8
+ def initialize(field)
9
+ super(field, :numericality)
10
+ @options = {}
11
+ end
12
+
13
+ def to_allow(options)
14
+ options[:equal_to] = options if options.is_a?(Numeric)
15
+ options[:allow_nil] = options.delete(:nil) if options.has_key?(:nil)
16
+ raise ArgumentError, "validate_numericality_of#to_allow requires a Hash parameter containing any of the following keys: " <<
17
+ @@allowed_options.map(&:inspect).join(", ") if !options.is_a?(Hash) or options.empty? or (options.keys - @@allowed_options).any?
18
+ @options.merge!(options)
19
+ self
20
+ end
21
+
22
+ def matches?(actual)
23
+ return false unless result = super(actual)
24
+
25
+ @@allowed_options.each do |comparator|
26
+ if @options.has_key?(comparator) and !([:even, :odd, :only_integer].include?(comparator) and !@validator.options.include?(comparator))
27
+ result &= (@validator.options[comparator] == @options[comparator])
28
+ end
29
+ end
30
+ @positive_result_message <<= options_message(@validator.options)
31
+ @negative_result_message <<= options_message(@validator.options)
32
+ result
33
+ end
34
+
35
+ def description
36
+ super << options_message(@options)
37
+ end
38
+
39
+ protected
40
+
41
+ def options_message(options)
42
+ type_msg = []
43
+ comp_msg = []
44
+ options.each_pair do |key, value|
45
+ case key
46
+ when :allow_nil
47
+ when :only_integer
48
+ type_msg << "integer" if value
49
+ when :odd, :even
50
+ type_msg << "#{key.to_s}-numbered" if value
51
+ else
52
+ comp_msg << "#{key.to_s.gsub("_", " ")} #{value.inspect}"
53
+ end
54
+ end
55
+ allow_nil = (options[:allow_nil] ? "nil" : "non-nil") if options.has_key?(:allow_nil)
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
+
59
+ def method_missing(m, *args, &block)
60
+ if @@allowed_options.include?(m.to_sym)
61
+ raise ArgumentError, "wrong number of arguments (#{args.length} for 1)" if args.length > 1
62
+ send :to_allow, m.to_sym => args.first
63
+ else
64
+ super
65
+ end
66
+ end
67
+ end
68
+
69
+ def validate_numericality_of(field)
70
+ ValidateNumericalityOfMatcher.new(field)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,9 @@
1
+ module Mongoid
2
+ module Matchers
3
+ module Validations
4
+ def validate_presence_of(field)
5
+ HaveValidationMatcher.new(field, :presence)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,82 @@
1
+ module Mongoid
2
+ module Matchers
3
+ module Validations
4
+ class ValidateUniquenessOfMatcher < HaveValidationMatcher
5
+ include WithMessage
6
+ def initialize(field)
7
+ super(field, :uniqueness)
8
+ end
9
+
10
+ def scoped_to(*scope)
11
+ @scope = [scope].flatten.map(&:to_sym)
12
+ self
13
+ end
14
+ alias_method :scoped_on, :scoped_to
15
+
16
+ def case_insensitive
17
+ @case_insensitive = true
18
+ self
19
+ end
20
+
21
+ def allow_blank?(allow_blank)
22
+ @allow_blank = allow_blank
23
+ self
24
+ end
25
+
26
+ def matches?(actual)
27
+ return false unless @result = super(actual)
28
+
29
+ check_scope if @scope
30
+ check_allow_blank if @allow_blank
31
+ check_case_sensitivity if @case_insensitive
32
+ check_expected_message if @expected_message
33
+
34
+ @result
35
+ end
36
+
37
+ def description
38
+ options_desc = []
39
+ options_desc << " scoped to #{@scope.inspect}" if @scope
40
+ options_desc << " allowing blank values" if @allow_blank
41
+ options_desc << " allowing case insensitive values" if @case_insensitive
42
+ options_desc << " with message '#{@expected_message}'" if @expected_message
43
+ super << options_desc.to_sentence
44
+ end
45
+
46
+ private
47
+
48
+ def check_allow_blank
49
+ if @validator.options[:allow_blank] == @allow_blank
50
+ @positive_result_message << " with blank values allowed"
51
+ else
52
+ @negative_result_message << " with no blank values allowed"
53
+ @result = false
54
+ end
55
+ end
56
+
57
+ def check_scope
58
+ message = " scope to #{@validator.options[:scope]}"
59
+ if [@validator.options[:scope]].flatten.map(&:to_sym) == @scope
60
+ @positive_result_message << message
61
+ else
62
+ @negative_result_message << message
63
+ @result = false
64
+ end
65
+ end
66
+
67
+ def check_case_sensitivity
68
+ if @validator.options[:case_sensitive] == false
69
+ @positive_result_message << " with case insensitive values"
70
+ else
71
+ @negative_result_message << " without case insensitive values"
72
+ @result = false
73
+ end
74
+ end
75
+ end
76
+
77
+ def validate_uniqueness_of(field)
78
+ ValidateUniquenessOfMatcher.new(field)
79
+ end
80
+ end
81
+ 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
@@ -0,0 +1,33 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+
3
+ require 'mongoid'
4
+ require 'rspec/core'
5
+ require 'rspec/expectations'
6
+ require 'rspec/mocks'
7
+ require "active_model"
8
+ require 'matchers/document'
9
+ require 'matchers/associations'
10
+ require 'matchers/collections'
11
+ require 'matchers/indexes'
12
+ require 'matchers/allow_mass_assignment'
13
+ require 'matchers/accept_nested_attributes'
14
+ require 'matchers/validations'
15
+ require 'matchers/validations/with_message'
16
+ require 'matchers/validations/associated'
17
+ require 'matchers/validations/confirmation_of'
18
+ require 'matchers/validations/exclusion_of'
19
+ require 'matchers/validations/format_of'
20
+ require 'matchers/validations/inclusion_of'
21
+ require 'matchers/validations/length_of'
22
+ require 'matchers/validations/numericality_of'
23
+ require 'matchers/validations/presence_of'
24
+ require 'matchers/validations/uniqueness_of'
25
+ require 'matchers/validations/acceptance_of'
26
+ require 'matchers/validations/custom_validation_of'
27
+
28
+ module Mongoid
29
+ module Matchers
30
+ include Mongoid::Matchers::Associations
31
+ include Mongoid::Matchers::Validations
32
+ end
33
+ end
@@ -0,0 +1,5 @@
1
+ module Mongoid
2
+ module Rspec
3
+ VERSION = "1.11.0"
4
+ end
5
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "mongoid-rspec/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "mongoid-rspec"
7
+ s.version = Mongoid::Rspec::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Evan Sagge"]
10
+ s.email = %q{evansagge@gmail.com}
11
+ s.homepage = %q{http://github.com/evansagge/mongoid-rspec}
12
+ s.summary = %q{RSpec matchers for Mongoid}
13
+ s.description = %q{RSpec matches for Mongoid models, including association and validation matchers}
14
+
15
+ s.rubyforge_project = "mongoid-rspec"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency 'rake'
23
+ s.add_dependency 'mongoid', '~> 4.0.0.beta2'
24
+ s.add_dependency 'rspec', '>= 2.14'
25
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "mongoid-rspec/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "mongoid4-rspec"
7
+ s.version = Mongoid::Rspec::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Francesco Boffa", "Evan Sagge"]
10
+ s.email = %q{fra.boffa@gmail.com evansagge@gmail.com}
11
+ s.homepage = %q{http://github.com/aomega08/mongoid4-rspec}
12
+ s.summary = %q{RSpec matchers for Mongoid 4}
13
+ s.description = %q{RSpec matches for Mongoid 4 models, including association and validation matchers}
14
+
15
+ s.rubyforge_project = "mongoid-rspec"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency 'rake'
23
+ s.add_dependency 'mongoid', '~> 4.0.0.beta2'
24
+ s.add_dependency 'rspec', '>= 2.14'
25
+ end
@@ -0,0 +1,29 @@
1
+ class Article
2
+ include Mongoid::Document
3
+ include Mongoid::Timestamps
4
+
5
+ field :title, localize: true
6
+ field :content
7
+ field :published, type: Boolean, default: false
8
+ field :allow_comments, type: Boolean, default: true
9
+ field :number_of_comments, type: Integer
10
+ field :status, type: Symbol
11
+
12
+ embeds_many :comments, cascade_callbacks: true
13
+ embeds_one :permalink
14
+ belongs_to :author, class_name: 'User', inverse_of: :articles, index: true
15
+
16
+ validates :title, presence: true
17
+
18
+ validates_inclusion_of :status, in: [:pending], on: :create
19
+ validates_inclusion_of :status, in: [:approved, :rejected ], on: :update
20
+
21
+ validates_length_of :title, within: 8..16
22
+ validates_length_of :content, minimum: 200
23
+
24
+ index({ title: 1 }, { unique: true, background: true, drop_dups: true })
25
+ index({ published: 1 })
26
+ index({ 'permalink._id' => 1 })
27
+
28
+ accepts_nested_attributes_for :permalink
29
+ end
@@ -0,0 +1,6 @@
1
+ class Comment
2
+ include Mongoid::Document
3
+
4
+ embedded_in :article, inverse_of: :comments, polymorphic: true
5
+ belongs_to :user, inverse_of: :comments
6
+ end
@@ -0,0 +1,4 @@
1
+ class Log
2
+ include Mongoid::Document
3
+ store_in collection: "logs"
4
+ end
@@ -0,0 +1,8 @@
1
+ class MovieArticle < Article
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 }
8
+ end
@@ -0,0 +1,5 @@
1
+ class Permalink
2
+ include Mongoid::Document
3
+
4
+ embedded_in :linkable, inverse_of: :link
5
+ end
@@ -0,0 +1,10 @@
1
+ require 'ssn_validator.rb'
2
+
3
+ class Person
4
+ include Mongoid::Document
5
+
6
+ field :name
7
+ field :ssn
8
+
9
+ validates :ssn, ssn: true
10
+ end
@@ -0,0 +1,16 @@
1
+ class Profile
2
+ include Mongoid::Document
3
+
4
+ field :first_name
5
+ field :last_name
6
+ field :age
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" }
14
+
15
+ index({ first_name: 1, last_name: 1 })
16
+ end
@@ -0,0 +1,5 @@
1
+ class Record
2
+ include Mongoid::Document
3
+
4
+ belongs_to :user, inverse_of: :record
5
+ end
@@ -0,0 +1,9 @@
1
+ class Site
2
+ include Mongoid::Document
3
+
4
+ field :name
5
+
6
+ has_many :users, inverse_of: :site, order: :email.desc
7
+
8
+ validates :name, presence: true, uniqueness: true
9
+ end
@@ -0,0 +1,36 @@
1
+ class User
2
+ include Mongoid::Document
3
+ include Mongoid::Timestamps::Created
4
+
5
+ field :login
6
+ field :email
7
+ field :role
8
+ field :age, type: Integer
9
+ field :password, type: String
10
+ field :provider_uid
11
+ field :locale
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
18
+
19
+ embeds_one :profile
20
+
21
+ validates :login, presence: true, uniqueness: { scope: :site }, format: { with: /\A[\w\-]+\z/ }, 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'] } }
28
+ validates :provider_uid, presence: true
29
+ validates :locale, inclusion: { in: ->(user) { [:en, :ru] } }
30
+
31
+ accepts_nested_attributes_for :articles, :comments
32
+
33
+ def admin?
34
+ false
35
+ end
36
+ end
@@ -0,0 +1,32 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "lib"))
3
+ MODELS = File.join(File.dirname(__FILE__), "models")
4
+ $LOAD_PATH.unshift(MODELS)
5
+ VALIDATORS = File.join(File.dirname(__FILE__), "validators")
6
+ $LOAD_PATH.unshift(VALIDATORS)
7
+
8
+ require "rubygems"
9
+ require "bundler"
10
+ Bundler.setup
11
+
12
+ require 'mongoid'
13
+ require 'rspec'
14
+ require 'rspec/core'
15
+ require 'rspec/expectations'
16
+
17
+ Mongoid.configure do |config|
18
+ config.connect_to("mongoid-rspec-test")
19
+ end
20
+
21
+ Dir[ File.join(MODELS, "*.rb") ].sort.each { |file| require File.basename(file) }
22
+
23
+ require 'mongoid-rspec'
24
+
25
+ RSpec.configure do |config|
26
+ config.include RSpec::Matchers
27
+ config.include Mongoid::Matchers
28
+ config.mock_with :rspec
29
+ config.after :all do
30
+ Mongoid::Config.purge!
31
+ end
32
+ end