nobrainer-rspec 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4e0c49605afc9e72f119e3d5ecb2809a556defc5ab84d52574eef569d8c08697
4
+ data.tar.gz: 6a71e9bcb29d5e29c8cc4071fbab8ece22bb3ecbc1d814619c442782032d0924
5
+ SHA512:
6
+ metadata.gz: 916dfe718138432512a974b258d5d6a9d86a46c184225889e41ebbcd2f13ec8989619f243b8a0ea05791fbcd87e88ad0ab328f4fda83090061275e0951f06fc2
7
+ data.tar.gz: 7416170408b071fbc3eb6c448e4d604ae771dec9a67b7b37202405d5b36521e2b3c860a29f2b6c77c368af48d2cb235da4175d7bced7572c74730e6419bfd53b
@@ -0,0 +1,37 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [1.0.1] - 2020-11-27
10
+ ### Fixed
11
+ - Fixes gem build which was creating an empty gem
12
+
13
+ ## [1.0.0] - 2020-11-26
14
+ ### Added
15
+ - be_nobrainer_document
16
+ - have_field
17
+ - have_index_for
18
+ - have_timestamps
19
+ - belong_to
20
+ - have_one
21
+ - have_one(...).through(...)
22
+ - have_many
23
+ - have_many(...).through(...)
24
+ - validate_presence_of
25
+ - validate_uniqueness_of
26
+ - validate_length_of
27
+ - validate_inclusion_of
28
+ - validate_acceptance_of
29
+ - validate_confirmation_of
30
+ - validate_exclusion_of
31
+ - validate_format_of
32
+ - validate_numericality_of
33
+
34
+ [Unreleased]: https://gitlab.com/zedtux/nobrainer-rspec/-/compare/v1.0.1...master
35
+ [1.0.1]: https://gitlab.com/zedtux/nobrainer-rspec/-/compare/v1.0.0...v1.1.0
36
+ [1.0.0]: https://gitlab.com/zedtux/nobrainer-rspec/-/tags/v1.0.0
37
+
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'byebug'
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2020 Guillaume Hain and Contributors
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.
@@ -0,0 +1,61 @@
1
+ # nobrainer-rspec
2
+
3
+ The nobrainer-rspec library provides a collection of RSpec-compatible matchers that help to test NoBrainer documents.
4
+
5
+ This gem is heavily inspiring from the [mongoid-rspec](https://github.com/mongoid/mongoid-rspec) gem, keeping same API so that migrating from one to the other is easy.
6
+
7
+ ## Installation
8
+
9
+ Drop this line into your Gemfile:
10
+
11
+ ```ruby
12
+ group :test do
13
+ gem 'nobrainer-rspec'
14
+ end
15
+
16
+ ```
17
+
18
+ ## Configuration
19
+
20
+ Add to your `rails_helper.rb` or `spec_helper.rb` file.
21
+
22
+ ```ruby
23
+ require 'nobrainer-rspec'
24
+
25
+ RSpec.configure do |config|
26
+ config.include NoBrainer::Matchers
27
+ end
28
+ ```
29
+
30
+ ## Matchers
31
+
32
+ * [be_nobrainer_document](https://gitlab.com/zedtux/nobrainer-rspec/-/blob/master/lib/matchers/be_nobrainer_document.rb)
33
+ * [have_field](https://gitlab.com/zedtux/nobrainer-rspec/-/blob/master/lib/matchers/have_field.rb)
34
+ * [have_index_for](https://gitlab.com/zedtux/nobrainer-rspec/-/blob/master/lib/matchers/have_index_for.rb)
35
+ * [have_timestamps](https://gitlab.com/zedtux/nobrainer-rspec/-/blob/master/lib/matchers/have_timestamps.rb)
36
+
37
+ ## Associations
38
+
39
+ * [belong_to, have_one, have_many](https://gitlab.com/zedtux/nobrainer-rspec/-/blob/master/lib/matchers/associations.rb)
40
+
41
+ ## Validations
42
+
43
+ * [validate_acceptance_of](https://gitlab.com/zedtux/nobrainer-rspec/-/blob/master/lib/matchers/validations/acceptance_of.rb)
44
+ * [validate_confirmation_of](https://gitlab.com/zedtux/nobrainer-rspec/-/blob/master/lib/matchers/validations/confirmation_of.rb)
45
+ * [validate_exclusion_of](https://gitlab.com/zedtux/nobrainer-rspec/-/blob/master/lib/matchers/validations/exclusion_of.rb)
46
+ * [validate_format_of](https://gitlab.com/zedtux/nobrainer-rspec/-/blob/master/lib/matchers/validations/format_of.rb)
47
+ * [validate_inclusion_of](https://gitlab.com/zedtux/nobrainer-rspec/-/blob/master/lib/matchers/validations/inclusion_of.rb)
48
+ * [validate_length_of](https://gitlab.com/zedtux/nobrainer-rspec/-/blob/master/lib/matchers/validations/length_of.rb)
49
+ * [validate_numericality_of](https://gitlab.com/zedtux/nobrainer-rspec/-/blob/master/lib/matchers/validations/numericality_of.rb)
50
+ * [validate_presence_of](https://gitlab.com/zedtux/nobrainer-rspec/-/blob/master/lib/matchers/validations/presence_of.rb)
51
+ * [validate_uniqueness_of](https://gitlab.com/zedtux/nobrainer-rspec/-/blob/master/lib/matchers/validations/uniqueness_of.rb)
52
+
53
+ ## Contributing
54
+
55
+ You're encouraged to contribute to this library.
56
+
57
+ ## Copyright and License
58
+
59
+ Copyright (c) 2020 Guillaume Hain and Contributors.
60
+
61
+ MIT License. See [LICENSE](LICENSE) for details.
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'nobrainer'
5
+ require 'nobrainer/rspec'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ NoBrainer.configure do |config|
15
+ config.app_name = 'nobrainer-rspec'
16
+ end
17
+
18
+ require 'irb'
19
+ IRB.start(__FILE__)
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle check || bundle install
7
+
@@ -0,0 +1,287 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NoBrainer
4
+ module Matchers
5
+ module Associations
6
+ HAS_MANY = NoBrainer::Document::Association::HasMany
7
+ HAS_MANY_THROUGH = NoBrainer::Document::Association::HasManyThrough
8
+ HAS_ONE = NoBrainer::Document::Association::HasOne
9
+ HAS_ONE_THROUGH = NoBrainer::Document::Association::HasOneThrough
10
+ BELONGS_TO = NoBrainer::Document::Association::BelongsTo
11
+
12
+ # The `belong_to` matcher is used to ensure that a `belongs_to`
13
+ # association exists on your model.
14
+ #
15
+ # class Person
16
+ # include NoBrainer::Document
17
+ #
18
+ # belongs_to :organization
19
+ # end
20
+ #
21
+ # # RSpec
22
+ # RSpec.describe Person, type: :model do
23
+ # it { is_expected.to belong_to(:organization) }
24
+ # end
25
+ #
26
+ # #### Qualifiers
27
+ #
28
+ # ##### class_name
29
+ #
30
+ # Use `class_name` to test usage of the `:class_name` option. This
31
+ # asserts that the model you're referring to actually exists.
32
+ #
33
+ # class Person
34
+ # include NoBrainer::Document
35
+ #
36
+ # belongs_to :ancient_city, class_name: 'City'
37
+ # end
38
+ #
39
+ # # RSpec
40
+ # RSpec.describe Person, type: :model do
41
+ # it { is_expected.to belong_to(:ancient_city).class_name('City') }
42
+ # end
43
+ #
44
+ # ##### with_primary_key
45
+ #
46
+ # Use `with_primary_key` to test usage of the `:primary_key` option.
47
+ #
48
+ # class Person
49
+ # include NoBrainer::Document
50
+ #
51
+ # belongs_to :great_country, primary_key: 'country_id'
52
+ # end
53
+ #
54
+ # # RSpec
55
+ # RSpec.describe Person, type: :model do
56
+ # it do
57
+ # is_expected.to belong_to(:great_country)
58
+ # .with_primary_key('country_id')
59
+ # end
60
+ # end
61
+ #
62
+ # ##### with_foreign_key
63
+ #
64
+ # Use `with_foreign_key` to test usage of the `:foreign_key` option.
65
+ #
66
+ # class Person
67
+ # include NoBrainer::Document
68
+ #
69
+ # belongs_to :great_country, foreign_key: 'country_id'
70
+ # end
71
+ #
72
+ # # RSpec
73
+ # RSpec.describe Person, type: :model do
74
+ # it do
75
+ # is_expected.to belong_to(:great_country)
76
+ # .with_foreign_key('country_id')
77
+ # end
78
+ # end
79
+ #
80
+ # ##### with_dependent
81
+ #
82
+ # Use `with_dependent` to assert that the `:dependent` option was
83
+ # specified.
84
+ #
85
+ # class Person
86
+ # include NoBrainer::Document
87
+ #
88
+ # belongs_to :world, dependent: :destroy
89
+ # end
90
+ #
91
+ # # RSpec
92
+ # RSpec.describe Person, type: :model do
93
+ # it { is_expected.to belong_to(:world).with_dependent(:destroy) }
94
+ # end
95
+ #
96
+ # ##### required
97
+ #
98
+ # Use `required` to assert that the association is not allowed to be nil.
99
+ #
100
+ # class Person
101
+ # include NoBrainer::Document
102
+ #
103
+ # belongs_to :organization, required: true
104
+ # end
105
+ #
106
+ # # RSpec
107
+ # describe Person
108
+ # it { is_expected.to belong_to(:organization).with_required(true) }
109
+ # end
110
+ #
111
+ class HaveAssociationMatcher
112
+ def initialize(name, association_type)
113
+ @association = {}
114
+ @association[:name] = name.to_sym
115
+ @association[:type] = association_type
116
+ @expectation_message = "#{type_description} #{@association[:name].inspect}"
117
+ @expectation_message += " of type #{@association[:class].inspect}" unless @association[:class].nil?
118
+ end
119
+
120
+ def class_name(klass)
121
+ @association[:class] = klass
122
+ @expectation_message += " with class_name #{@association[:class].inspect}"
123
+ self
124
+ end
125
+
126
+ def through(association_key)
127
+ @association[:through] = association_key
128
+ @association[:type] = @association[:type] == HAS_ONE ? HAS_ONE_THROUGH : HAS_MANY_THROUGH
129
+ @expectation_message += " through #{association_key.inspect}"
130
+ self
131
+ end
132
+
133
+ def with_dependent(method_name)
134
+ @association[:dependent] = method_name
135
+ @expectation_message += " which specifies dependent as #{@association[:dependent]}"
136
+ self
137
+ end
138
+
139
+ def with_primary_key(primary_key)
140
+ @association[:primary_key] = primary_key.to_sym
141
+ @expectation_message += " using primary key #{@association[:primary_key].inspect}"
142
+ self
143
+ end
144
+
145
+ def with_foreign_key(foreign_key)
146
+ @association[:foreign_key] = foreign_key.to_sym
147
+ @expectation_message += " using foreign key #{@association[:foreign_key].inspect}"
148
+ self
149
+ end
150
+
151
+ def with_required(required)
152
+ @association[:required] = required
153
+ @expectation_message += " with required #{@association[:required].inspect}"
154
+ self
155
+ end
156
+
157
+ def matches?(actual)
158
+ @actual = actual.is_a?(Class) ? actual : actual.class
159
+ metadata = @actual.association_metadata[@association[:name]]
160
+
161
+ if metadata.nil?
162
+ @negative_result_message = "no association named #{@association[:name]}"
163
+ return false
164
+ else
165
+ @positive_result_message = "association named #{@association[:name]}"
166
+ end
167
+
168
+ relation = metadata.class.name.gsub(/::Metadata$/, '').constantize
169
+ if relation != @association[:type]
170
+ @negative_result_message = "#{@actual.inspect} #{type_description(relation, false)} #{@association[:name].inspect}"
171
+ return false
172
+ else
173
+ @positive_result_message = "#{@actual.inspect} #{type_description(relation, false)} #{@association[:name].inspect}"
174
+ end
175
+
176
+ if !@association[:class].nil? && (@association[:class] != metadata.options[:class_name])
177
+ @negative_result_message = "#{@positive_result_message} with class_name #{metadata.options[:class_name].inspect}"
178
+ return false
179
+ else
180
+ @positive_result_message = "#{@positive_result_message}#{" with class_name #{metadata.options[:class_name].inspect}" if @association[:class]}"
181
+ end
182
+
183
+ if @association[:dependent]
184
+ if @association[:dependent].to_sym != (metadata.options[:dependent] && metadata.options[:dependent].to_sym)
185
+ @negative_result_message = "#{@positive_result_message} which specified dependent as #{metadata.options[:dependent]}"
186
+ return false
187
+ else
188
+ @positive_result_message = "#{@positive_result_message} which specified dependent as #{metadata.options[:dependent]}"
189
+ end
190
+ end
191
+
192
+ if @association[:primary_key]
193
+ if metadata.options[:primary_key] != @association[:primary_key]
194
+ @negative_result_message = "#{@positive_result_message} with primary key #{metadata.options[:primary_key].inspect}"
195
+ return false
196
+ else
197
+ @positive_result_message = "#{@positive_result_message} with primary key #{metadata.options[:primary_key].inspect}"
198
+ end
199
+ end
200
+
201
+ if @association[:foreign_key]
202
+ if metadata.foreign_key != @association[:foreign_key]
203
+ @negative_result_message = "#{@positive_result_message} with foreign key #{metadata.foreign_key.inspect}"
204
+ return false
205
+ else
206
+ @positive_result_message = "#{@positive_result_message} with foreign key #{metadata.foreign_key.inspect}"
207
+ end
208
+ end
209
+
210
+ if @association[:through]
211
+ if metadata.options[:through] != @association[:through]
212
+ @negative_result_message = "#{@positive_result_message} through #{metadata.options[:through].inspect}"
213
+ return false
214
+ else
215
+ @positive_result_message = "#{@positive_result_message} through #{metadata.options[:through].inspect}"
216
+ end
217
+ end
218
+
219
+ if @association[:required]
220
+ if metadata.options[:required] != @association[:required]
221
+ @negative_result_message = "#{@positive_result_message} with required #{metadata.options[:required].inspect}"
222
+ return false
223
+ else
224
+ @positive_result_message = "#{@positive_result_message} with required #{metadata.options[:required].inspect}"
225
+ end
226
+ end
227
+
228
+ true
229
+ end
230
+
231
+ def failure_message_for_should
232
+ "Expected #{@actual.inspect} to #{@expectation_message}, got #{@negative_result_message}"
233
+ end
234
+
235
+ def failure_message_for_should_not
236
+ "Expected #{@actual.inspect} to not #{@expectation_message}, got #{@positive_result_message}"
237
+ end
238
+
239
+ alias failure_message failure_message_for_should
240
+ alias failure_message_when_negated failure_message_for_should_not
241
+
242
+ def description
243
+ @expectation_message
244
+ end
245
+
246
+ def type_description(type = nil, passive = true)
247
+ type ||= @association[:type]
248
+ case type.name
249
+ when HAS_ONE.name
250
+ (passive ? 'have one' : 'having one')
251
+ when HAS_ONE_THROUGH.name
252
+ (passive ? 'have one through' : 'having one')
253
+ when HAS_MANY.name
254
+ (passive ? 'have many' : 'having many')
255
+ when HAS_MANY_THROUGH.name
256
+ (passive ? 'have many through' : 'having many')
257
+ when BELONGS_TO.name
258
+ (passive ? 'belong to' : 'belonging to')
259
+ else
260
+ raise format("Unknown association type #{type}")
261
+ end
262
+ end
263
+
264
+ private
265
+
266
+ def association_kind_of
267
+ Mongoid::Compatibility::Version.mongoid5_or_older? ? Origin::Key : Mongoid::Criteria::Queryable::Key
268
+ end
269
+ end
270
+
271
+ def have_one_related(association_name)
272
+ HaveAssociationMatcher.new(association_name, HAS_ONE)
273
+ end
274
+ alias have_one have_one_related
275
+
276
+ def have_many_related(association_name)
277
+ HaveAssociationMatcher.new(association_name, HAS_MANY)
278
+ end
279
+ alias have_many have_many_related
280
+
281
+ def belong_to_related(association_name)
282
+ HaveAssociationMatcher.new(association_name, BELONGS_TO)
283
+ end
284
+ alias belong_to belong_to_related
285
+ end
286
+ end
287
+ end