mongoid-spec 4.0.1

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 (51) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +373 -0
  4. data/Rakefile +17 -0
  5. data/lib/matchers/accept_nested_attributes.rb +67 -0
  6. data/lib/matchers/allow_mass_assignment.rb +102 -0
  7. data/lib/matchers/associations.rb +330 -0
  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 +50 -0
  11. data/lib/matchers/have_field.rb +90 -0
  12. data/lib/matchers/have_index_for.rb +63 -0
  13. data/lib/matchers/have_timestamps.rb +61 -0
  14. data/lib/matchers/validations.rb +81 -0
  15. data/lib/matchers/validations/acceptance_of.rb +9 -0
  16. data/lib/matchers/validations/associated.rb +19 -0
  17. data/lib/matchers/validations/confirmation_of.rb +22 -0
  18. data/lib/matchers/validations/custom_validation_of.rb +47 -0
  19. data/lib/matchers/validations/exclusion_of.rb +49 -0
  20. data/lib/matchers/validations/format_of.rb +71 -0
  21. data/lib/matchers/validations/inclusion_of.rb +49 -0
  22. data/lib/matchers/validations/length_of.rb +147 -0
  23. data/lib/matchers/validations/numericality_of.rb +90 -0
  24. data/lib/matchers/validations/presence_of.rb +9 -0
  25. data/lib/matchers/validations/uniqueness_of.rb +82 -0
  26. data/lib/matchers/validations/with_message.rb +27 -0
  27. data/lib/mongoid-rspec.rb +1 -0
  28. data/lib/mongoid/rspec.rb +40 -0
  29. data/lib/mongoid/rspec/version.rb +5 -0
  30. data/spec/models/article.rb +29 -0
  31. data/spec/models/comment.rb +6 -0
  32. data/spec/models/log.rb +9 -0
  33. data/spec/models/movie_article.rb +8 -0
  34. data/spec/models/permalink.rb +5 -0
  35. data/spec/models/person.rb +10 -0
  36. data/spec/models/profile.rb +16 -0
  37. data/spec/models/record.rb +5 -0
  38. data/spec/models/site.rb +9 -0
  39. data/spec/models/user.rb +37 -0
  40. data/spec/spec_helper.rb +35 -0
  41. data/spec/unit/accept_nested_attributes_spec.rb +12 -0
  42. data/spec/unit/associations_spec.rb +42 -0
  43. data/spec/unit/be_dynamic_document_spec.rb +22 -0
  44. data/spec/unit/be_mongoid_document_spec.rb +25 -0
  45. data/spec/unit/be_stored_in.rb +54 -0
  46. data/spec/unit/document_spec.rb +16 -0
  47. data/spec/unit/have_index_for_spec.rb +46 -0
  48. data/spec/unit/have_timestamps_spec.rb +71 -0
  49. data/spec/unit/validations_spec.rb +54 -0
  50. data/spec/validators/ssn_validator.rb +16 -0
  51. metadata +171 -0
@@ -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 @@
1
+ require 'mongoid/rspec'
@@ -0,0 +1,40 @@
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_support/core_ext/hash/keys'
8
+ require 'active_support/core_ext/hash/transform_values'
9
+ require 'active_support/core_ext/object/blank'
10
+ require 'active_support/core_ext/string/inflections'
11
+
12
+ require 'matchers/associations'
13
+ require 'matchers/allow_mass_assignment'
14
+ require 'matchers/accept_nested_attributes'
15
+ require 'matchers/validations'
16
+ require 'matchers/validations/with_message'
17
+ require 'matchers/validations/associated'
18
+ require 'matchers/validations/confirmation_of'
19
+ require 'matchers/validations/exclusion_of'
20
+ require 'matchers/validations/format_of'
21
+ require 'matchers/validations/inclusion_of'
22
+ require 'matchers/validations/length_of'
23
+ require 'matchers/validations/numericality_of'
24
+ require 'matchers/validations/presence_of'
25
+ require 'matchers/validations/uniqueness_of'
26
+ require 'matchers/validations/acceptance_of'
27
+ require 'matchers/validations/custom_validation_of'
28
+ require 'matchers/be_mongoid_document'
29
+ require 'matchers/be_dynamic_document'
30
+ require 'matchers/be_stored_in'
31
+ require 'matchers/have_field'
32
+ require 'matchers/have_index_for'
33
+ require 'matchers/have_timestamps'
34
+
35
+ module Mongoid
36
+ module Matchers
37
+ include Mongoid::Matchers::Associations
38
+ include Mongoid::Matchers::Validations
39
+ end
40
+ end
@@ -0,0 +1,5 @@
1
+ module Mongoid
2
+ module RSpec
3
+ VERSION = '4.0.1'
4
+ end
5
+ 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, inverse_of: :article
13
+ embeds_one :permalink, inverse_of: :linkable
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,9 @@
1
+ class Log
2
+ include Mongoid::Document
3
+ include Mongoid::Timestamps
4
+ include Mongoid::Attributes::Dynamic
5
+
6
+ store_in collection: "logs"
7
+
8
+ index({ created_at: 1 }, { bucket_size: 100, expire_after_seconds: 3600 } )
9
+ 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, counter_cache: true
7
+
8
+ validates :name, presence: true, uniqueness: true
9
+ end
@@ -0,0 +1,37 @@
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, inverse_of: :user
18
+
19
+ embeds_one :profile, inverse_of: :user
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 :password, confirmation: { message: "Password confirmation must match given password" }
29
+ validates :provider_uid, presence: true
30
+ validates :locale, inclusion: { in: ->(user) { [:en, :ru] } }
31
+
32
+ accepts_nested_attributes_for :articles, :comments
33
+
34
+ def admin?
35
+ false
36
+ end
37
+ end
@@ -0,0 +1,35 @@
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::Config.connect_to('mongoid-rspec-test')
18
+ Mongo::Logger.logger.level = ::Logger::INFO
19
+
20
+ Dir[ File.join(MODELS, "*.rb") ].sort.each { |file| require File.basename(file) }
21
+
22
+ require 'mongoid-rspec'
23
+
24
+ RSpec.configure do |config|
25
+ config.include RSpec::Matchers
26
+ config.include Mongoid::Matchers
27
+ config.mock_with :rspec
28
+ config.after :all do
29
+ Mongoid::Config.purge!
30
+ end
31
+ config.after :suite do
32
+ print "\n# Mongoid v#{Mongoid::VERSION}"
33
+ end
34
+ config.disable_monkey_patching!
35
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe "AcceptsNestedAttributes" do
4
+ describe User do
5
+ it { is_expected.to accept_nested_attributes_for(:articles) }
6
+ it { is_expected.to accept_nested_attributes_for(:comments) }
7
+ end
8
+
9
+ describe Article do
10
+ it { is_expected.to accept_nested_attributes_for(:permalink) }
11
+ end
12
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe "Associations" do
4
+ describe User do
5
+ it { is_expected.to have_many(:articles).with_foreign_key(:author_id).ordered_by(:title) }
6
+
7
+ it { is_expected.to have_one(:record).as_inverse_of(:user).with_autobuild }
8
+
9
+ it { is_expected.to have_many(:comments).with_dependent(:destroy).with_autosave }
10
+
11
+ it { is_expected.to embed_one(:profile).as_inverse_of(:user) }
12
+
13
+ it { is_expected.to have_and_belong_to_many(:children).of_type(User) }
14
+ end
15
+
16
+ describe Profile do
17
+ it { is_expected.to be_embedded_in(:user).as_inverse_of(:profile) }
18
+ end
19
+
20
+ describe Article do
21
+ it { is_expected.to belong_to(:author).of_type(User).as_inverse_of(:articles).with_index }
22
+ it { is_expected.to embed_many(:comments).as_inverse_of(:article).with_cascading_callbacks }
23
+ it { is_expected.to embed_one(:permalink).as_inverse_of(:linkable) }
24
+ end
25
+
26
+ describe Comment do
27
+ it { is_expected.to be_embedded_in(:article).as_inverse_of(:comments).with_polymorphism }
28
+ it { is_expected.to belong_to(:user).as_inverse_of(:comments) }
29
+ end
30
+
31
+ describe Record do
32
+ it { is_expected.to belong_to(:user).as_inverse_of(:record) }
33
+ end
34
+
35
+ describe Permalink do
36
+ it { is_expected.to be_embedded_in(:linkable).as_inverse_of(:link) }
37
+ end
38
+
39
+ describe Site do
40
+ it { is_expected.to have_many(:users).as_inverse_of(:site).ordered_by(:email.desc).with_counter_cache }
41
+ end
42
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Mongoid::Matchers::BeDynamicDocument do
4
+ context 'when model does\'t include Mongoid::Document' do
5
+ subject do
6
+ Class.new
7
+ end
8
+
9
+ it { is_expected.not_to be_mongoid_document }
10
+ end
11
+
12
+
13
+ context 'when model doesn\'t include Mongoid::Document' do
14
+ subject do
15
+ Class.new do
16
+ include Mongoid::Document
17
+ end
18
+ end
19
+
20
+ it { is_expected.to be_mongoid_document }
21
+ end
22
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Mongoid::Matchers::BeMongoidDocument do
4
+ context 'when model does\'t include Mongoid::Attributes::Dynamic' do
5
+ subject do
6
+ Class.new do
7
+ include Mongoid::Document
8
+ end
9
+ end
10
+
11
+ it { is_expected.not_to be_dynamic_document }
12
+ end
13
+
14
+
15
+ context 'when model doesn\'t include Mongoid::Attributes::Dynamic' do
16
+ subject do
17
+ Class.new do
18
+ include Mongoid::Document
19
+ include Mongoid::Attributes::Dynamic
20
+ end
21
+ end
22
+
23
+ it { is_expected.to be_dynamic_document }
24
+ end
25
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Mongoid::Matchers::BeStoredIn do
4
+ subject do
5
+ Class.new do
6
+ include Mongoid::Document
7
+ store_in collection: 'citizens', database: 'other', client: 'secondary'
8
+ end
9
+ end
10
+
11
+ it 'detects storage options' do
12
+ is_expected.to be_stored_in(collection: 'citizens', database: 'other', client: 'secondary')
13
+ end
14
+
15
+ it 'detects even part of storage options' do
16
+ is_expected.to be_stored_in(database: 'other')
17
+ is_expected.to be_stored_in(client: 'secondary')
18
+ is_expected.to be_stored_in(collection: 'citizens')
19
+ is_expected.to be_stored_in(collection: 'citizens', database: 'other')
20
+ is_expected.to be_stored_in(database: 'other', client: 'secondary')
21
+ is_expected.to be_stored_in(collection: 'citizens', client: 'secondary')
22
+ end
23
+
24
+ it 'detects differences' do
25
+ is_expected.not_to be_stored_in(collection: 'aliens')
26
+ end
27
+
28
+ context 'when models has storage options defined via blocks, procs or lambdas' do
29
+ subject do
30
+ Class.new do
31
+ include Mongoid::Document
32
+ store_in database: ->{ Thread.current[:database] }
33
+ end
34
+ end
35
+
36
+ before do
37
+ Thread.current[:database] = 'db1981'
38
+ end
39
+
40
+ it 'detects storage options' do
41
+ is_expected.to be_stored_in(database: 'db1981')
42
+ end
43
+
44
+ it 'reflects changes in storage options' do
45
+ is_expected.to be_stored_in(database: 'db1981')
46
+ Thread.current[:database] = 'db2200'
47
+ is_expected.to be_stored_in(database: 'db2200')
48
+ end
49
+
50
+ it 'detects differences' do
51
+ is_expected.not_to be_stored_in(database: 'other')
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe "Document" do
4
+ describe User do
5
+ it { is_expected.to have_fields(:email, :login) }
6
+ end
7
+
8
+ describe Article do
9
+ it { is_expected.to have_field(:published).of_type(Mongoid::Boolean).with_default_value_of(false) }
10
+ it { is_expected.to have_field(:allow_comments).of_type(Mongoid::Boolean).with_default_value_of(true) }
11
+ it { is_expected.to belong_to(:author) }
12
+ it { is_expected.to have_field(:title).localized }
13
+ it { is_expected.not_to have_field(:allow_comments).of_type(Mongoid::Boolean).with_default_value_of(false) }
14
+ it { is_expected.not_to have_field(:number_of_comments).of_type(Integer).with_default_value_of(1) }
15
+ end
16
+ end