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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 53f0a80e90261ef7bd81c063c905b8cd84e95cdb
4
- data.tar.gz: bb55c8e0eb57052af2bf0dec1cc6c6666c98fcf0
2
+ SHA256:
3
+ metadata.gz: 8403edeb95537840758d0411c34a4cb4eac959f3ae89498309a1c3faf62c7b59
4
+ data.tar.gz: cdddf19e194bdf21d13f8beaaf58f5e8185986cd443b5867381033c4c872edcb
5
5
  SHA512:
6
- metadata.gz: 8abac74f92990787081850397babe4fecfeeaaf1302186ce0ee64f1c006aca43dcf4e2ddb9f81c1e3504a4f541188becb47e724718139f6524bb5d2514d40f59
7
- data.tar.gz: cf740b8212962b00295acb35ae679b2a0a9654b5be2e5064c89021e5d256b064d4eb981502c32f60c990095853f543db425f5cef8dc235a9e6c7922aa7a395ee
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
- msg = "have a valid factory '#{@name}'"
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
@@ -7,7 +7,7 @@ module Phillumeny
7
7
  module Version
8
8
 
9
9
  MAJOR = 0
10
- MINOR = 1
10
+ MINOR = 2
11
11
  PATCH = 0
12
12
  PRERELEASE = nil
13
13
  # PRERELEASE = 'alpha'.freeze
data/lib/phillumeny.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'phillumeny/version'
4
4
 
5
+ require 'phillumeny/active_model'
5
6
  require 'phillumeny/factory_bot'
6
7
 
7
8
  # Phillumeny is a collection of RSpec matchers for verbose testers that I use to fills some of the gaps
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.1.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: 2017-10-30 00:00:00.000000000 Z
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.6.14
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.