phillumeny 0.1.0 → 0.2.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.
- checksums.yaml +5 -5
- data/lib/phillumeny/active_model.rb +203 -0
- data/lib/phillumeny/factory_bot.rb +1 -3
- data/lib/phillumeny/version.rb +1 -1
- data/lib/phillumeny.rb +1 -0
- metadata +18 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8403edeb95537840758d0411c34a4cb4eac959f3ae89498309a1c3faf62c7b59
|
4
|
+
data.tar.gz: cdddf19e194bdf21d13f8beaaf58f5e8185986cd443b5867381033c4c872edcb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 31e35ad66e92c1618c210c2a3a26a5d5a6f14b136c31179314e0b990d132c40163943ba0a1f213fc9e5989d9d5eabf8f6a7a8d8d3b8f7834a168d418d73f803e
|
7
|
+
data.tar.gz: 37a789b49915fb23ce58f7cf4ee1863226eada76cbef064b173440bac967e9cca55864519aef919345144dd5b78ae6296a93e27de0b860c65c281795e750a78c
|
@@ -0,0 +1,203 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phillumeny
|
4
|
+
|
5
|
+
# Matchers testing ActiveModel functionality
|
6
|
+
module ActiveModel
|
7
|
+
|
8
|
+
def validate_presence_of_any(*args)
|
9
|
+
ValidatePresenceOfAny.new(args)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Used for testing conditional validation of presence where you need one of the other
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
#
|
16
|
+
# class Webpage
|
17
|
+
#
|
18
|
+
# include ActiveModel::Validations
|
19
|
+
#
|
20
|
+
# attr_accessor :body
|
21
|
+
# attr_accessor :headline
|
22
|
+
# attr_accessor :title
|
23
|
+
#
|
24
|
+
# validates :body,
|
25
|
+
# presence: true
|
26
|
+
#
|
27
|
+
# validates :headline,
|
28
|
+
# presence: true,
|
29
|
+
# unless: -> { title.present? }
|
30
|
+
#
|
31
|
+
# validates :title,
|
32
|
+
# presence: true,
|
33
|
+
# inclusion: { allow_blank: true, in: ['this'] },
|
34
|
+
# unless: -> { headline.present? && headline != 'needs title' }
|
35
|
+
#
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# RSpec.describe Webpage, type: :model do
|
39
|
+
#
|
40
|
+
# it { should validate_presence_of_any(:headline, :title).valid_values(title: 'this') }
|
41
|
+
# it { should_not validate_presence_of_any(:headline, :title) }
|
42
|
+
# it { should_not validate_presence_of_any(:headline, :title).valid_value(:title, 'that') }
|
43
|
+
#
|
44
|
+
# end
|
45
|
+
class ValidatePresenceOfAny
|
46
|
+
|
47
|
+
attr_reader :attributes
|
48
|
+
attr_reader :subject
|
49
|
+
|
50
|
+
# Description used when matcher is used
|
51
|
+
#
|
52
|
+
# @api private
|
53
|
+
#
|
54
|
+
# @return [String]
|
55
|
+
def description
|
56
|
+
"validate the presence of any of these attributes: #{attributes.join(', ')}"
|
57
|
+
end
|
58
|
+
|
59
|
+
# Compiles the error message for display
|
60
|
+
#
|
61
|
+
# @api private
|
62
|
+
#
|
63
|
+
# @return [String]
|
64
|
+
def failure_message
|
65
|
+
messages = [subject.inspect]
|
66
|
+
attributes.each do |attribute|
|
67
|
+
messages << subject.errors.full_messages_for(attribute)
|
68
|
+
end
|
69
|
+
messages.compact.join("\n")
|
70
|
+
end
|
71
|
+
|
72
|
+
# Sets up arguments for the matcher
|
73
|
+
#
|
74
|
+
# @api private
|
75
|
+
#
|
76
|
+
# @return [void]
|
77
|
+
def initialize(args)
|
78
|
+
@attributes = args
|
79
|
+
end
|
80
|
+
|
81
|
+
# Runs the logic to determine if expectations are being met
|
82
|
+
#
|
83
|
+
# @api private
|
84
|
+
#
|
85
|
+
# @return [Boolean]
|
86
|
+
def matches?(subject)
|
87
|
+
@subject = subject
|
88
|
+
store_initial_values
|
89
|
+
invalid_when_none_present? && attributes.all? do |attribute|
|
90
|
+
clear_attributes_and_errors
|
91
|
+
initialize_value(attribute)
|
92
|
+
free_of_errors_on_attribute?(attribute) ? other_attributes_valid?(attribute) : false
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Explicitly set the value to test for an attribute if 'X' is not acceptable
|
97
|
+
#
|
98
|
+
# @api public
|
99
|
+
#
|
100
|
+
# @example
|
101
|
+
# it { should validate_presence_of_any(:headline, :title).valid_value(:title, 'A valid title') }
|
102
|
+
#
|
103
|
+
# @return [self]
|
104
|
+
def valid_value(attribute, value)
|
105
|
+
attribute_values[attribute] = value
|
106
|
+
self
|
107
|
+
end
|
108
|
+
|
109
|
+
# Explicitly set the value to test for an attribute if 'X' is not acceptable
|
110
|
+
#
|
111
|
+
# @api public
|
112
|
+
#
|
113
|
+
# @example
|
114
|
+
# it do
|
115
|
+
# should validate_presence_of_any(:headline, :title).valid_values(title: 'A valid title')
|
116
|
+
# end
|
117
|
+
#
|
118
|
+
# @return [self]
|
119
|
+
def valid_values(**values)
|
120
|
+
attribute_values.merge!(values)
|
121
|
+
self
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
# Storage for our initial and valid values for the attributes we are testing against
|
127
|
+
#
|
128
|
+
# @api private
|
129
|
+
#
|
130
|
+
# @return [Hash]
|
131
|
+
def attribute_values
|
132
|
+
@attribute_values ||= {}
|
133
|
+
end
|
134
|
+
|
135
|
+
# Clears all of the attributes that we are testing against
|
136
|
+
#
|
137
|
+
# @api private
|
138
|
+
#
|
139
|
+
# @return [void]
|
140
|
+
def clear_attributes_and_errors
|
141
|
+
attributes.each do |attribute|
|
142
|
+
subject.send("#{attribute}=", nil)
|
143
|
+
end
|
144
|
+
subject.errors.clear
|
145
|
+
end
|
146
|
+
|
147
|
+
# Checks that the current attribute we are checking against passes its other validations
|
148
|
+
#
|
149
|
+
# @api private
|
150
|
+
#
|
151
|
+
# @return [Boolean]
|
152
|
+
def free_of_errors_on_attribute?(attribute)
|
153
|
+
subject.class.validators_on(attribute).each do |validator|
|
154
|
+
# Can/do we use self[attribute] here or would that be better used in the
|
155
|
+
# conditional Proc on the validation itself?
|
156
|
+
validator.validate_each(subject, attribute, subject.send(attribute))
|
157
|
+
end
|
158
|
+
subject.errors[attribute].empty?
|
159
|
+
end
|
160
|
+
|
161
|
+
# Sets the value of the attribute that we currently testing against
|
162
|
+
#
|
163
|
+
# @api private
|
164
|
+
#
|
165
|
+
# @return [void]
|
166
|
+
def initialize_value(attribute)
|
167
|
+
subject.send("#{attribute}=", attribute_values[attribute] || 'X')
|
168
|
+
end
|
169
|
+
|
170
|
+
def invalid_when_none_present?
|
171
|
+
clear_attributes_and_errors
|
172
|
+
!subject.valid? && attributes.all? { |attribute| subject.errors[attribute].present? }
|
173
|
+
end
|
174
|
+
|
175
|
+
# Checks that the other attributes that we are checking against are valid as promised
|
176
|
+
#
|
177
|
+
# @api private
|
178
|
+
#
|
179
|
+
# @return [Boolean]
|
180
|
+
def other_attributes_valid?(exclude_attribute)
|
181
|
+
subject.valid?
|
182
|
+
(attributes - [exclude_attribute]).all? do |attribute|
|
183
|
+
subject.errors[attribute].empty?
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Store the initial values of our subject before we start messing around with it
|
188
|
+
#
|
189
|
+
# @api private
|
190
|
+
#
|
191
|
+
# @return [void]
|
192
|
+
def store_initial_values
|
193
|
+
attributes.each do |attribute|
|
194
|
+
next if attribute_values.key?(attribute)
|
195
|
+
attribute_values[attribute] = subject.send(attribute)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
@@ -19,9 +19,7 @@ module Phillumeny
|
|
19
19
|
class HaveAValidFactory
|
20
20
|
|
21
21
|
def description
|
22
|
-
|
23
|
-
msg << " with the trait '#{@trait}'" if @trait
|
24
|
-
msg
|
22
|
+
"have a valid factory '#{@name}'#{"with the trait '#{@trait}'" if @trait}"
|
25
23
|
end
|
26
24
|
|
27
25
|
def failure_message
|
data/lib/phillumeny/version.rb
CHANGED
data/lib/phillumeny.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: phillumeny
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Deering
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-02-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: rspec
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -32,6 +46,7 @@ extensions: []
|
|
32
46
|
extra_rdoc_files: []
|
33
47
|
files:
|
34
48
|
- lib/phillumeny.rb
|
49
|
+
- lib/phillumeny/active_model.rb
|
35
50
|
- lib/phillumeny/factory_bot.rb
|
36
51
|
- lib/phillumeny/version.rb
|
37
52
|
homepage: https://github.com/mdeering/phillumeny
|
@@ -53,7 +68,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
53
68
|
version: '0'
|
54
69
|
requirements: []
|
55
70
|
rubyforge_project:
|
56
|
-
rubygems_version: 2.
|
71
|
+
rubygems_version: 2.7.5
|
57
72
|
signing_key:
|
58
73
|
specification_version: 4
|
59
74
|
summary: Collection of RSpec matchers for verbose testers.
|