glebtv-mongoid-rspec 1.12.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 (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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b2fe5714fda2ed60f7ddfcc3e3435baca3651311
4
+ data.tar.gz: 7cc78332ecf79fdf911dda3b1d08a690c0235c2b
5
+ SHA512:
6
+ metadata.gz: 585662e9ca279626b7b93ff82aaf614b954d5a4513f13bb1738276fa6a7c56866d2498252d96e0e6f7f94f760dd1be0b64a5eab763fe891b9563526929b3d9fa
7
+ data.tar.gz: ff4bccaf1fd97808af64b87622d2f400059d381063c98940f5ba5292ad51c850e7050806a87685e0d23d4929d53794ed750a0e5e2b4c2e7901d7c8c54d51a7fe
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .rvmrc
3
+ .bundle
4
+ Gemfile.lock
5
+ pkg/*
6
+
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ mongoid-rspec
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.1
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ services: mongodb
2
+ language: ruby
3
+ rvm:
4
+ - 2.0.0
5
+ - 2.1.1
6
+ - jruby-20mode
7
+ - rbx-2.1.1
8
+ - rbx-19mode
9
+
10
+ gemfile:
11
+ - Gemfile
12
+ - gemfiles/mongoid-3.1.gemfile
13
+ - gemfiles/mongoid-4.0.gemfile
14
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in mongoid-rspec.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Evan Sagge
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,203 @@
1
+ mongoid-rspec
2
+ =
3
+
4
+ [![Build Status](https://secure.travis-ci.org/glebtv/mongoid-rspec.png?branch=master)](https://travis-ci.org/glebtv/mongoid-rspec)
5
+
6
+ http://rubygems.org/gems/glebtv-mongoid-rspec
7
+
8
+ RSpec matchers for Mongoid 3.x / 4.x
9
+
10
+ Installation
11
+ -
12
+ Add to your Gemfile
13
+
14
+ gem 'glebtv-mongoid-rspec'
15
+
16
+ Drop in existing or dedicated support file in spec/support (spec/support/mongoid.rb)
17
+
18
+ ```ruby
19
+ RSpec.configure do |config|
20
+ config.include Mongoid::Matchers, type: :model
21
+ end
22
+ ```
23
+
24
+ Association Matchers
25
+ -
26
+
27
+ ```ruby
28
+ describe User do
29
+ it { should have_many(:articles).with_foreign_key(:author_id).ordered_by(:title) }
30
+
31
+ it { should have_one(:record) }
32
+ #can verify autobuild is set to true
33
+ it { should have_one(:record).with_autobuild }
34
+
35
+ it { should have_many :comments }
36
+
37
+ #can also specify with_dependent to test if :dependent => :destroy/:destroy_all/:delete is set
38
+ it { should have_many(:comments).with_dependent(:destroy) }
39
+ #can verify autosave is set to true
40
+ it { should have_many(:comments).with_autosave }
41
+
42
+ it { should embed_one :profile }
43
+
44
+ it { should have_and_belong_to_many(:children) }
45
+ it { should have_and_belong_to_many(:children).of_type(User) }
46
+ end
47
+
48
+ describe Profile do
49
+ it { should be_embedded_in(:user).as_inverse_of(:profile) }
50
+ end
51
+
52
+ describe Article do
53
+ it { should belong_to(:author).of_type(User).as_inverse_of(:articles) }
54
+ it { should belong_to(:author).of_type(User).as_inverse_of(:articles).with_index }
55
+ it { should embed_many(:comments) }
56
+ end
57
+
58
+ describe Comment do
59
+ it { should be_embedded_in(:article).as_inverse_of(:comments) }
60
+ it { should belong_to(:user).as_inverse_of(:comments) }
61
+ end
62
+
63
+ describe Record do
64
+ it { should belong_to(:user).as_inverse_of(:record) }
65
+ end
66
+
67
+ describe Site do
68
+ it { should have_many(:users).as_inverse_of(:site).ordered_by(:email.asc) }
69
+ end
70
+ ```
71
+
72
+ Mass Assignment Matcher
73
+ -
74
+
75
+ ```ruby
76
+ describe User do
77
+ it { should allow_mass_assignment_of(:login) }
78
+ it { should allow_mass_assignment_of(:email) }
79
+ it { should allow_mass_assignment_of(:age) }
80
+ it { should allow_mass_assignment_of(:password) }
81
+ it { should allow_mass_assignment_of(:password) }
82
+ it { should allow_mass_assignment_of(:role).as(:admin) }
83
+
84
+ it { should_not allow_mass_assignment_of(:role) }
85
+ end
86
+ ```
87
+
88
+ Validation Matchers
89
+ -
90
+
91
+ ```ruby
92
+ describe Site do
93
+ it { should validate_presence_of(:name) }
94
+ it { should validate_uniqueness_of(:name) }
95
+ end
96
+
97
+ describe User do
98
+ it { should validate_presence_of(:login) }
99
+ it { should validate_uniqueness_of(:login).scoped_to(:site) }
100
+ it { should validate_uniqueness_of(:email).case_insensitive.with_message("is already taken") }
101
+ it { should validate_format_of(:login).to_allow("valid_login").not_to_allow("invalid login") }
102
+ it { should validate_associated(:profile) }
103
+ it { should validate_exclusion_of(:login).to_not_allow("super", "index", "edit") }
104
+ it { should validate_inclusion_of(:role).to_allow("admin", "member") }
105
+ it { should validate_confirmation_of(:email) }
106
+ it { should validate_presence_of(:age).on(:create, :update) }
107
+ it { should validate_numericality_of(:age).on(:create, :update) }
108
+ it { should validate_inclusion_of(:age).to_allow(23..42).on([:create, :update]) }
109
+ it { should validate_presence_of(:password).on(:create) }
110
+ it { should validate_presence_of(:provider_uid).on(:create) }
111
+ it { should validate_inclusion_of(:locale).to_allow([:en, :ru]) }
112
+ end
113
+
114
+ describe Article do
115
+ it { should validate_length_of(:title).within(8..16) }
116
+ end
117
+
118
+ describe Profile do
119
+ it { should validate_numericality_of(:age).greater_than(0) }
120
+ end
121
+
122
+ describe MovieArticle do
123
+ it { should validate_numericality_of(:rating).to_allow(:greater_than => 0).less_than_or_equal_to(5) }
124
+ it { should validate_numericality_of(:classification).to_allow(:even => true, :only_integer => true, :nil => false) }
125
+ end
126
+
127
+ describe Person do
128
+ # in order to be able to use the custom_validate matcher, the custom validator class (in this case SsnValidator)
129
+ # should redefine the kind method to return :custom, i.e. "def self.kind() :custom end"
130
+ it { should custom_validate(:ssn).with_validator(SsnValidator) }
131
+ end
132
+ ```
133
+
134
+ Accepts Nested Attributes Matcher
135
+ -
136
+
137
+ ```ruby
138
+ describe User do
139
+ it { should accept_nested_attributes_for(:articles) }
140
+ it { should accept_nested_attributes_for(:comments) }
141
+ end
142
+
143
+ describe Article do
144
+ it { should accept_nested_attributes_for(:permalink) }
145
+ end
146
+ ```
147
+
148
+ Index Matcher
149
+ -
150
+
151
+ ```ruby
152
+ describe Article do
153
+ it { should have_index_for(published: 1) }
154
+ it { should have_index_for(title: 1).with_options(unique: true, background: true) }
155
+ end
156
+
157
+ describe Profile do
158
+ it { should have_index_for(first_name: 1, last_name: 1) }
159
+ end
160
+ ```
161
+
162
+ Others
163
+ -
164
+
165
+ ```ruby
166
+ describe User do
167
+ it { should have_fields(:email, :login) }
168
+ it { should have_field(:s).with_alias(:status) }
169
+ it { should have_fields(:birthdate, :registered_at).of_type(DateTime) }
170
+
171
+ # if you're declaring 'include Mongoid::Timestamps'
172
+ # or any of 'include Mongoid::Timestamps::Created' and 'Mongoid::Timestamps::Updated'
173
+ it { should be_timestamped_document }
174
+ it { should be_timestamped_document.with(:created) }
175
+ it { should_not be_timestamped_document.with(:updated) }
176
+
177
+ it { should be_versioned_document } # if you're declaring `include Mongoid::Versioning`
178
+ it { should be_paranoid_document } # if you're declaring `include Mongoid::Paranoia`
179
+ it { should be_multiparameted_document } # if you're declaring `include Mongoid::MultiParameterAttributes`
180
+ end
181
+
182
+ describe Log do
183
+ it { should be_stored_in :logs }
184
+ end
185
+
186
+ describe Article do
187
+ it { should have_field(:published).of_type(Boolean).with_default_value_of(false) }
188
+ it { should have_field(:allow_comments).of_type(Boolean).with_default_value_of(true) }
189
+ it { should_not have_field(:allow_comments).of_type(Boolean).with_default_value_of(false) }
190
+ it { should_not have_field(:number_of_comments).of_type(Integer).with_default_value_of(1) }
191
+ end
192
+ ```
193
+
194
+ Known issues
195
+ -
196
+
197
+ accept_nested_attributes_for matcher must test options [issue 91](https://github.com/evansagge/mongoid-rspec/issues/91).
198
+
199
+ Acknowledgement
200
+ -
201
+ Thanks to [Durran Jordan](https://github.com/durran) for providing the changes necessary to make
202
+ this compatible with mongoid 2.0.0.rc, and for other [contributors](https://github.com/evansagge/mongoid-rspec/contributors)
203
+ to this project.
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+
3
+ require 'bundler'
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ require 'rspec/core/rake_task'
7
+
8
+ task :default => :spec
9
+
10
+ RSpec::Core::RakeTask.new(:spec) do |spec|
11
+ spec.pattern = "./spec/**/*_spec.rb"
12
+ end
13
+
14
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
15
+ spec.pattern = "./spec/**/*_spec.rb"
16
+ spec.rcov = true
17
+ end
18
+
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "mongoid", github: "mongoid/mongoid", branch: "3.1.0-stable"
4
+
5
+ gemspec path: "../"
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "mongoid", "~> 4.0.0.beta1"
4
+
5
+ gemspec path: "../"
@@ -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 = "glebtv-mongoid-rspec"
7
+ s.version = Mongoid::Rspec::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Evan Sagge", "GlebTv"]
10
+ s.email = %q{evansagge@gmail.com}
11
+ s.homepage = %q{http://github.com/glebtv/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', [">= 3.0", "< 4.1"]
24
+ s.add_dependency 'rspec', '>= 2.14'
25
+ end
@@ -0,0 +1 @@
1
+ require 'mongoid-rspec'
@@ -0,0 +1,65 @@
1
+ module Mongoid
2
+ module Matchers # :nodoc:
3
+
4
+ # Ensures that the model can accept nested attributes for the specified
5
+ # association.
6
+ #
7
+ # Example:
8
+ # it { should accept_nested_attributes_for(:articles) }
9
+ #
10
+ def accept_nested_attributes_for(attribute)
11
+ AcceptNestedAttributesForMatcher.new(attribute)
12
+ end
13
+
14
+ class AcceptNestedAttributesForMatcher
15
+
16
+ def initialize(attribute)
17
+ @attribute = attribute.to_s
18
+ @options = {}
19
+ end
20
+
21
+ def matches?(subject)
22
+ @subject = subject
23
+ match?
24
+ end
25
+
26
+ def failure_message
27
+ "Expected #{expectation} (#{@problem})"
28
+ end
29
+
30
+ def negative_failure_message
31
+ "Did not expect #{expectation}"
32
+ end
33
+
34
+ def description
35
+ description = "accepts_nested_attributes_for :#{@attribute}"
36
+ end
37
+
38
+ protected
39
+ def match?
40
+ exists?
41
+ end
42
+
43
+ def exists?
44
+ if config
45
+ true
46
+ else
47
+ @problem = 'is not declared'
48
+ false
49
+ end
50
+ end
51
+
52
+ def config
53
+ model_class.nested_attributes["#{@attribute}_attributes"]
54
+ end
55
+
56
+ def model_class
57
+ @subject.class
58
+ end
59
+
60
+ def expectation
61
+ "#{model_class.name} to accept nested attributes for #{@attribute}"
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,101 @@
1
+ # this code is totally extracted from shoulda-matchers gem.
2
+ module Mongoid
3
+ module Matchers
4
+ class AllowMassAssignmentOfMatcher # :nodoc:
5
+ attr_reader :failure_message, :negative_failure_message
6
+
7
+ def initialize(attribute)
8
+ @attribute = attribute.to_s
9
+ @options = {}
10
+ end
11
+
12
+ def as(role)
13
+ if active_model_less_than_3_1?
14
+ raise "You can specify role only in Rails 3.1 or greater"
15
+ end
16
+ @options[:role] = role
17
+ self
18
+ end
19
+
20
+ def matches?(klass)
21
+ @klass = klass
22
+ if attr_mass_assignable?
23
+ if whitelisting?
24
+ @negative_failure_message = "#{@attribute} was made accessible"
25
+ else
26
+ if protected_attributes.empty?
27
+ @negative_failure_message = "no attributes were protected"
28
+ else
29
+ @negative_failure_message = "#{class_name} is protecting " <<
30
+ "#{protected_attributes.to_a.to_sentence}, " <<
31
+ "but not #{@attribute}."
32
+ end
33
+ end
34
+ true
35
+ else
36
+ if whitelisting?
37
+ @failure_message = "Expected #{@attribute} to be accessible"
38
+ else
39
+ @failure_message = "Did not expect #{@attribute} to be protected"
40
+ end
41
+ false
42
+ end
43
+ end
44
+
45
+ def description
46
+ "allow mass assignment of #{@attribute}"
47
+ end
48
+
49
+ private
50
+
51
+ def role
52
+ @options[:role] || :default
53
+ end
54
+
55
+ def protected_attributes
56
+ @protected_attributes ||= (@klass.class.protected_attributes || [])
57
+ end
58
+
59
+ def accessible_attributes
60
+ @accessible_attributes ||= (@klass.class.accessible_attributes || [])
61
+ end
62
+
63
+ def whitelisting?
64
+ authorizer.kind_of?(::ActiveModel::MassAssignmentSecurity::WhiteList)
65
+ end
66
+
67
+ def attr_mass_assignable?
68
+ !authorizer.deny?(@attribute)
69
+ end
70
+
71
+ def authorizer
72
+ if active_model_less_than_3_1?
73
+ @klass.class.active_authorizer
74
+ else
75
+ @klass.class.active_authorizer[role]
76
+ end
77
+ end
78
+
79
+ def class_name
80
+ @klass.class.name
81
+ end
82
+
83
+ def active_model_less_than_3_1?
84
+ ::ActiveModel::VERSION::STRING.to_f < 3.1
85
+ end
86
+ end
87
+
88
+ # Ensures that the attribute can be set on mass update.
89
+ #
90
+ # it { should_not allow_mass_assignment_of(:password) }
91
+ # it { should allow_mass_assignment_of(:first_name) }
92
+ #
93
+ # In Rails 3.1 you can check role as well:
94
+ #
95
+ # it { should allow_mass_assignment_of(:first_name).as(:admin) }
96
+ #
97
+ def allow_mass_assignment_of(value)
98
+ AllowMassAssignmentOfMatcher.new(value)
99
+ end
100
+ end
101
+ end