nobrainer-rspec 1.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.
@@ -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