attestor 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 30d3fe02039b7df2f24461bd4eb4387deda2e93c
4
- data.tar.gz: f1ec4731ded236180c094171dff71d10fad448db
3
+ metadata.gz: e5d5fbaf26c97f5c5dd838289b64904714d118b7
4
+ data.tar.gz: 777f4abaa5ef17cfe19627cbb69b9e960394a4c7
5
5
  SHA512:
6
- metadata.gz: 595eaa79f730fdf98e8119e5d98c30f0711278ada88dde24b87dc3ea770a8206fef0d0a2c899714504299b530f4eeb93898f1666fb2d61601e01954a76ed6436
7
- data.tar.gz: 47078bc23acc2749312d84e9b343ae9236b300b099bafbb7a4554a5c4b34a7c0f7579716597ccdd736d7b3e05edc2956f7b8ad7d353cb226aafbe31e844933d2
6
+ metadata.gz: 91138866beb8ef0615181d4ab9c3a9cbad86f3bb2b238b648f155b0b7fea5d6a1b2b48185762e7b5289b66965faa5f7bc9c62c49e85db2705744ae28f4bdfd42
7
+ data.tar.gz: 3ff1370688ca7712e5e7fdfad088e8298f73e10a85f17e247923cec2e05ed36f0791604342c1a3ae77c481c9d702710a90861b5d80640b5464910280582072e4
data/README.md CHANGED
@@ -39,8 +39,6 @@ Approach
39
39
 
40
40
  Instead of collecting errors inside the object, the module's `validate` instance method just raises an exception (`Attestor::InvalidError`), that carries errors outside of the object. The object stays untouched (and can be made immutable).
41
41
 
42
- So to speak, validation just attests at the object and complains loudly when things goes wrong.
43
-
44
42
  Installation
45
43
  ------------
46
44
 
@@ -66,8 +64,6 @@ gem install attestor
66
64
  Basic Use
67
65
  ----------
68
66
 
69
- `Attestor::Validations` API consists of 1 class method `.validate` and 2 instance methods (`validate` and `invalid`).
70
-
71
67
  Declare validation in the same way as ActiveModel's `.validate` method does:
72
68
 
73
69
  ```ruby
@@ -95,11 +91,12 @@ end
95
91
 
96
92
  The `#invalid` method translates its argument in a current class scope and raises an exception.
97
93
 
98
- ```ruby
94
+ ```yaml
99
95
  # config/locales/en.yml
96
+ ---
100
97
  en:
101
98
  attestor:
102
- validations:
99
+ errors:
103
100
  transfer:
104
101
  inconsistent: "Credit differs from debet by %{fraud}"
105
102
  ```
@@ -120,7 +117,7 @@ rescue => error
120
117
  end
121
118
  ```
122
119
 
123
- Adding Contexts
120
+ Use of Contexts
124
121
  ---------------
125
122
 
126
123
  Sometimes you need to validate the object agaist the subset of validations, not all of them.
@@ -142,17 +139,17 @@ fraud_transfer.validate # => InvalidError
142
139
  fraud_transfer.validate :steal_of_money # => PASSES!
143
140
  ```
144
141
 
145
- Just as the `:except` option blacklists validations, the `:only` method whitelists them:
142
+ You can use the same validator several times with different contexts. Any validation will be made independently from the others:
146
143
 
147
144
  ```ruby
148
- class Transfer < Struct.new(:debet, :credit)
149
- include Attestor::Validations
150
-
145
+ class Transfer
146
+ # ...
151
147
  validate :consistent, only: :fair_trade
152
- end
148
+ validate :consistent, only: :legal
153
149
 
154
- fraud_transfer.validate # => PASSES
155
- fraud_transfer.validate :fair_trade # => InvalidError
150
+ # This is the same as:
151
+ # validate :consistent, only: [:fair_trade, :legal]
152
+ end
156
153
  ```
157
154
 
158
155
  Policy Objects
@@ -162,7 +159,7 @@ Extract a validator to the separate object (policy). Basically the policy includ
162
159
 
163
160
  ```ruby
164
161
  class ConsistencyPolicy < Struct.new(:debet, :credit)
165
- include Attestor::Policy
162
+ include Attestor::Policy # includes Attestor::Validator as well
166
163
 
167
164
  validate :consistent
168
165
 
@@ -179,33 +176,40 @@ This looks mainly the same as before. But the policy's debet and credit are numb
179
176
 
180
177
  This is the core part of the [Policy Object design pattern] - it isolates the rule from unsignificant details of the target.
181
178
 
182
- From the other hand, the target needs to know nothing about how the policy works with data:
179
+ When you have a policy to follow, use the `follow_policy` method:
183
180
 
184
181
  ```ruby
185
182
  class Transfer < Struct.new(:debet, :credit)
186
183
  include Attestor::Validations
187
184
 
188
- validate :constistent
185
+ follow_policy :consistency, only: :fair_trade
189
186
 
190
187
  private
191
188
 
192
- def consistent
193
- policy = ConsistencyPolicy.new(debet.sum, credit.sum)
194
- invalid :inconsistent if policy.invalid?
189
+ def consistency # should respond to #valid? and #invalid?
190
+ ConsistencyPolicy.new(debet.sum, credit.sum)
195
191
  end
196
192
  end
197
193
  ```
198
194
 
199
- The "new" method `valid?` just returns true or false, trowing error messages out as unsignificant details.
195
+ In case the policy object is invalid, validation raises an exception.
200
196
 
201
- If you need messages from policy, you can use `validate` method and capture its exception. But should you?! Instead you'd better to provie the message, that makes sense in the Transfer context.
197
+ The name of the method (`consistency`) will be used for the error message:
202
198
 
203
- [Policy Object design pattern]: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
199
+ ```yaml
200
+ # config/locales/en.yml
201
+ ---
202
+ en:
203
+ attestor:
204
+ errors:
205
+ transfer:
206
+ consistency: "The transfer is inconsistent"
207
+ ```
204
208
 
205
209
  Complex Policies
206
210
  ----------------
207
211
 
208
- Now that we isolated policies, we can provide complex policies from simpler ones.
212
+ Now that policies are isolated from their targets, we can provide complex policies from simpler ones.
209
213
 
210
214
  Suppose we have two policy objects:
211
215
 
@@ -214,7 +218,7 @@ valid_policy.valid? # => true
214
218
  invalid_policy.valid? # => false
215
219
  ```
216
220
 
217
- Use `Policy` factory methods to provide compositions:
221
+ Use factory methods to provide compositions:
218
222
 
219
223
  ```ruby
220
224
  complex_policy = valid_policy.not
@@ -233,7 +237,7 @@ complex_policy = valid_policy.xor(valid_poicy, invalid_policy)
233
237
  complex_policy.validate # => passes
234
238
  ```
235
239
 
236
- The `or`, `and` and `xor` methods, called without argument(s), don't provide a policy object. They return lazy composer, expecting `#not` method.
240
+ The `or`, `and` and `xor` methods called without argument(s) don't provide a policy object. They return lazy composer, expecting `#not` method.
237
241
 
238
242
  ```ruby
239
243
  complex_policy = valid_policy.and.not(invalid_policy, invalid_policy)
@@ -245,13 +249,13 @@ If you prefer wrapping to chaining, use the `Policy` factory methods instead:
245
249
 
246
250
  ```ruby
247
251
  Policy.and(valid_policy, invalid_policy)
248
- # this is the same as: valid_policy.and invalid_policy
252
+ # this is the same as: valid_policy.and(invalid_policy)
249
253
 
250
254
  Policy.or(valid_policy, invalid_policy)
251
- # this is the same as: valid_policy.or invalid_policy
255
+ # this is the same as: valid_policy.or(invalid_policy)
252
256
 
253
257
  Policy.xor(valid_policy, invalid_policy)
254
- # this is the same as: valid_policy.xor invalid_policy
258
+ # this is the same as: valid_policy.xor(invalid_policy)
255
259
 
256
260
  Policy.not(valid_policy)
257
261
  # this is the same as: valid_policy.not
@@ -259,25 +263,6 @@ Policy.not(valid_policy)
259
263
 
260
264
  As before, you can use any number of policies (except for negation of a single policy) at any number of nesting.
261
265
 
262
- This can be used either in targets or in complex policies. In the later case do it like this:
263
-
264
- ```ruby
265
- class ComplexPolicy < Struct.new(:a, :b, :c)
266
- include Attestor::Policy
267
-
268
- validate :complex_rule
269
-
270
- private
271
-
272
- def complex_rule
273
- first_policy = FirstPolicy.new(a, b)
274
- second_policy = SecondPolicy.new(b, c)
275
-
276
- invalid :base unless first_policy.xor(second_policy).valid?
277
- end
278
- end
279
- ```
280
-
281
266
  Compatibility
282
267
  -------------
283
268
 
@@ -289,6 +274,9 @@ Tested under rubies compatible to rubies with API 2.0+:
289
274
 
290
275
  Uses [RSpec] 3.0+ for testing and [hexx-suit] for dev/test tools collection.
291
276
 
277
+ [RSpec]: http://rspec.info
278
+ [hexx-suit]: https://github.com/nepalez/hexx-suit
279
+
292
280
  Contributing
293
281
  ------------
294
282
 
data/lib/attestor.rb CHANGED
@@ -7,8 +7,8 @@ require_relative "attestor/version"
7
7
  require_relative "attestor/invalid_error"
8
8
 
9
9
  require_relative "attestor/validations"
10
- require_relative "attestor/validations/item"
11
- require_relative "attestor/validations/collection"
10
+ require_relative "attestor/validations/validator"
11
+ require_relative "attestor/validations/validators"
12
12
  require_relative "attestor/validations/message"
13
13
 
14
14
  require_relative "attestor/policy/factory"
@@ -5,14 +5,14 @@ module Attestor
5
5
  # API for objects to be validated
6
6
  module Validations
7
7
 
8
- # Calls all validations used in the selected context
8
+ # Calls all validators for given context
9
9
  #
10
- # @raise [Attestor::Validations::InvalidError] if validations fail
11
- # @raise [NoMethodError] if some of validations are not implemented
10
+ # @raise [Attestor::Validations::InvalidError] if validators fail
11
+ # @raise [NoMethodError] if some of validators are not implemented
12
12
  #
13
13
  # @return [undefined]
14
14
  def validate(context = :all)
15
- self.class.validations.set(context).each(&method(:__send__))
15
+ self.class.validators.set(context).each { |item| item.validate(self) }
16
16
  end
17
17
 
18
18
  # Raises InvalidError with a corresponding message
@@ -42,18 +42,18 @@ module Attestor
42
42
  # @private
43
43
  module ClassMethods
44
44
 
45
- # Returns a collection of items describing applied validations
45
+ # Returns a collection of applied validators
46
46
  #
47
- # @return [Attestor::Collection]
47
+ # @return [Attestor::Validators]
48
48
  #
49
49
  # @api private
50
- def validations
51
- @validations ||= Collection.new
50
+ def validators
51
+ @validators ||= Validators.new
52
52
  end
53
53
 
54
- # Adds an item to {#validations}
54
+ # Registers a validator
55
55
  #
56
- # Mutates the class by changing its {#validations} attribute!
56
+ # Mutates the class by changing its {#validators} attribute!
57
57
  #
58
58
  # @param [#to_sym] name
59
59
  # @param [Hash] options
@@ -62,9 +62,25 @@ module Attestor
62
62
  # @option options [#to_sym, Array<#to_sym>] :only
63
63
  # the white list of contexts for validation
64
64
  #
65
- # @return [Attestor::Collection] the updated collection
65
+ # @return [Attestor::Validators] the updated collection
66
66
  def validate(name, options = {})
67
- @validations = validations.add(name, options)
67
+ @validators = validators.add(name, options)
68
+ end
69
+
70
+ # Registers a followed policy
71
+ #
72
+ # Mutates the class by changing its {#validators} attribute!
73
+ #
74
+ # @param [#to_sym] name
75
+ # @param [Hash] options
76
+ # @option options [#to_sym, Array<#to_sym>] :except
77
+ # the black list of contexts for validation
78
+ # @option options [#to_sym, Array<#to_sym>] :only
79
+ # the white list of contexts for validation
80
+ #
81
+ # @return [Attestor::Collection] the updated collection
82
+ def follow_policy(name, options = {})
83
+ @validators = validators.add(name, options.merge(policy: true))
68
84
  end
69
85
 
70
86
  end # module ClassMethods
@@ -4,10 +4,16 @@ module Attestor
4
4
 
5
5
  module Validations
6
6
 
7
- # Describes an item of validations' collection
7
+ # Describe a validator for class instances
8
+ #
9
+ # @example
10
+ # validator = Validator.new(:foo, policy: :bar, only: :baz)
11
+ #
12
+ # validator.used_in_context? :baz # => true
13
+ # validator.validate object
8
14
  #
9
15
  # @api private
10
- class Item
16
+ class Validator
11
17
 
12
18
  # @!scope class
13
19
  # @!method new(name, except: [], only: [])
@@ -17,11 +23,12 @@ module Attestor
17
23
  # @option [#to_sym, Array<#to_sym>] :except
18
24
  # @option [#to_sym, Array<#to_sym>] :only
19
25
  #
20
- # @return [Attestor::Collection::Item]
26
+ # @return [Attestor::Validations::Validator]
21
27
 
22
28
  # @private
23
- def initialize(name, except: nil, only: nil)
29
+ def initialize(name, except: nil, only: nil, policy: nil)
24
30
  @name = name.to_sym
31
+ @policy = policy
25
32
  @whitelist = normalize(only)
26
33
  @blacklist = normalize(except)
27
34
  generate_id
@@ -30,9 +37,18 @@ module Attestor
30
37
 
31
38
  # @!attribute [r] name
32
39
  # The name of the item
40
+ #
33
41
  # @return [Symbol]
34
42
  attr_reader :name
35
43
 
44
+ # @!method policy?
45
+ # Whether the validator uses a policy
46
+ #
47
+ # @return [Boolean]
48
+ def policy?
49
+ @policy ? true : false
50
+ end
51
+
36
52
  # Compares an item to another one
37
53
  #
38
54
  # @param [Object] other
@@ -52,6 +68,19 @@ module Attestor
52
68
  whitelisted?(symbol) && !blacklisted?(symbol)
53
69
  end
54
70
 
71
+ # Validates given object
72
+ #
73
+ # @param [Object] object
74
+ #
75
+ # @raise [Attestor::InvalidError]
76
+ # if object doesn't match validation rule
77
+ #
78
+ # @return [undefined]
79
+ def validate(object)
80
+ result = object.__send__(name)
81
+ object.__send__(:invalid, name) if policy? && result.invalid?
82
+ end
83
+
55
84
  protected
56
85
 
57
86
  # @!attribute [r] id
@@ -80,7 +109,7 @@ module Attestor
80
109
  Array(list).map(&:to_sym).uniq
81
110
  end
82
111
 
83
- end # class Item
112
+ end # class Validator
84
113
 
85
114
  end # module Validations
86
115
 
@@ -7,16 +7,16 @@ module Attestor
7
7
  # The collection of validations used by class instances
8
8
  #
9
9
  # @api private
10
- class Collection
10
+ class Validators
11
11
  include Enumerable
12
12
 
13
13
  # @!scope class
14
14
  # @!method new(items = [])
15
15
  # Creates an immutable collection with optional list of items
16
16
  #
17
- # @param [Array<Attestor::Collection::Item>] items
17
+ # @param [Array<Attestor::Validators::Validator>] items
18
18
  #
19
- # @return [Attestor::Collection]
19
+ # @return [Attestor::Validators]
20
20
 
21
21
  # @private
22
22
  def initialize(items = [])
@@ -27,46 +27,46 @@ module Attestor
27
27
  # Iterates through the collection
28
28
  #
29
29
  # @yield the block
30
- # @yieldparam [Attestor::Collection::Item] item
30
+ # @yieldparam [Attestor::Validators::Validator] item
31
31
  # items from the collection
32
32
  #
33
33
  # @return [Enumerator]
34
34
  def each
35
- return to_enum unless block_given?
36
- items.map(&:name).uniq.each { |item| yield(item) }
35
+ block_given? ? @items.each { |item| yield(item) } : to_enum
37
36
  end
38
37
 
39
- # Returns the collection, updated with new item
38
+ # Returns validators updated by new item
40
39
  #
41
40
  # @param [#to_sym] name
42
41
  # @param [Hash] options
43
42
  # @option options [Array<#to_sym>] :except
44
43
  # @option options [Array<#to_sym>] :only
44
+ # @option options [Symbol, nil] :policy
45
45
  #
46
- # @return [Attestor::Collection]
46
+ # @return [Attestor::Validators]
47
47
  def add(name, options = {})
48
- item = Item.new(name, options)
49
- return self if items.include? item
48
+ item = Validator.new(name, options)
49
+ return self if include? item
50
50
 
51
- self.class.new(items + [item])
51
+ self.class.new(@items + [item])
52
52
  end
53
53
 
54
- # Returns the collection of items used in given context
54
+ # Returns validators used in given context
55
55
  #
56
56
  # @param [#to_sym] context
57
57
  #
58
- # @return [Attestor::Collection]
58
+ # @return [Attestor::Validators]
59
59
  def set(context)
60
- collection = items.select { |item| item.used_in_context? context }
60
+ validators = select { |item| item.used_in_context? context }
61
61
 
62
- self.class.new(collection)
62
+ self.class.new(validators)
63
63
  end
64
64
 
65
65
  private
66
66
 
67
67
  attr_reader :items
68
68
 
69
- end # class Collection
69
+ end # class Validators
70
70
 
71
71
  end # module Validations
72
72
 
@@ -4,6 +4,6 @@ module Attestor
4
4
 
5
5
  # The semantic version of the module.
6
6
  # @see http://semver.org/ Semantic versioning 2.0
7
- VERSION = "0.0.1".freeze
7
+ VERSION = "0.1.0".freeze
8
8
 
9
9
  end # module Attestor
@@ -0,0 +1,109 @@
1
+ # encoding: utf-8
2
+
3
+ describe "Base example" do
4
+
5
+ before do
6
+ Account = Struct.new(:client, :limit)
7
+ Transaction = Struct.new(:account, :sum)
8
+ Transfer = Struct.new(:debet, :credit)
9
+
10
+ ConsistencyPolicy = Struct.new(:debet, :credit)
11
+ LimitPolicy = Struct.new(:transaction)
12
+ InternalTransfer = Struct.new(:debet, :credit)
13
+
14
+ class ConsistencyPolicy
15
+ include Attestor::Policy
16
+
17
+ validate :consistent
18
+
19
+ private
20
+
21
+ def consistent
22
+ return if debet.sum + credit.sum == 0
23
+ invalid :inconsistent
24
+ end
25
+ end
26
+
27
+ class LimitPolicy
28
+ include Attestor::Policy
29
+
30
+ validate :limited
31
+
32
+ private
33
+
34
+ def limited
35
+ return unless (transaction.account.limit + transaction.sum) < 0
36
+ invalid :over_the_limit
37
+ end
38
+ end
39
+
40
+ class InternalTransfer
41
+ include Attestor::Policy
42
+
43
+ validate :internal
44
+
45
+ private
46
+
47
+ def internal
48
+ return if debet.account.client == credit.account.client
49
+ invalid :external
50
+ end
51
+ end
52
+
53
+ class Transfer
54
+ include Attestor::Validations
55
+
56
+ follow_policy :consistent
57
+ follow_policy :limited, except: :blocked
58
+ follow_policy :internal, only: :blocked
59
+
60
+ private
61
+
62
+ def consistent
63
+ ConsistencyPolicy.new(debet, credit)
64
+ end
65
+
66
+ def limited
67
+ LimitPolicy.new(debet).or internal
68
+ end
69
+
70
+ def internal
71
+ InternalTransfer.new(debet, credit)
72
+ end
73
+ end
74
+ end
75
+
76
+ let(:alice) { Account.new("Alice", 100) }
77
+ let(:bob) { Account.new("Bob", 100) }
78
+
79
+ let(:a_to_a) do
80
+ Transfer.new Transaction.new(alice, -150), Transaction.new(alice, 150)
81
+ end
82
+
83
+ let(:a_to_b) do
84
+ Transfer.new Transaction.new(alice, -150), Transaction.new(bob, 150)
85
+ end
86
+
87
+ let(:b_to_a) do
88
+ Transfer.new Transaction.new(bob, -50), Transaction.new(alice, 50)
89
+ end
90
+
91
+ it "works fine" do
92
+ expect { a_to_a.validate }.not_to raise_error
93
+ expect { b_to_a.validate }.not_to raise_error
94
+
95
+ expect { a_to_b.validate }.to raise_error Attestor::InvalidError
96
+ expect { b_to_a.validate :blocked }.to raise_error Attestor::InvalidError
97
+ end
98
+
99
+ after do
100
+ %w(
101
+ Transfer
102
+ InternalTransfer
103
+ LimitPolicy
104
+ ConsistencyPolicy
105
+ Transaction
106
+ Account
107
+ ).each { |klass| Object.send :remove_const, klass }
108
+ end
109
+ end
@@ -1,6 +1,8 @@
1
1
  # encoding: utf-8
2
2
 
3
- describe Attestor::Validations::Item do
3
+ require "support/policies"
4
+
5
+ describe Attestor::Validations::Validator do
4
6
 
5
7
  subject { described_class.new "foo" }
6
8
 
@@ -36,9 +38,13 @@ describe Attestor::Validations::Item do
36
38
  .to eq(described_class.new "foo", only: %i(bar))
37
39
  end
38
40
 
41
+ it "accepts array option :policy" do
42
+ expect { described_class.new "foo", policy: :bar }.not_to raise_error
43
+ end
44
+
39
45
  end # describe .new
40
46
 
41
- describe ".name" do
47
+ describe "#name" do
42
48
 
43
49
  it "is initialized as a symbol" do
44
50
  expect(subject.name).to eq :foo
@@ -46,11 +52,24 @@ describe Attestor::Validations::Item do
46
52
 
47
53
  end # describe .name
48
54
 
55
+ describe "#policy?" do
56
+
57
+ it "is set to false by default" do
58
+ expect(subject.policy?).to eq false
59
+ end
60
+
61
+ it "can be initialized" do
62
+ subject = described_class.new "foo", policy: 1
63
+ expect(subject.policy?).to eq true
64
+ end
65
+
66
+ end # describe #policy?
67
+
49
68
  describe "#==" do
50
69
 
51
70
  subject { described_class.new :foo, except: [:foo], only: %i(bar) }
52
71
 
53
- context "item with the same name and options" do
72
+ context "item with the same arguments" do
54
73
 
55
74
  let(:other) { described_class.new :foo, except: %i(foo), only: %i(bar) }
56
75
 
@@ -150,4 +169,51 @@ describe Attestor::Validations::Item do
150
169
 
151
170
  end # describe #name
152
171
 
172
+ describe "#validate" do
173
+
174
+ after { subject.validate object }
175
+
176
+ context "when a #policy isn't set" do
177
+
178
+ let(:object) { double foo: nil }
179
+ subject { described_class.new :foo }
180
+
181
+ it "calls validation method" do
182
+ expect(object).to receive :foo
183
+ end
184
+
185
+ end # context
186
+
187
+ context "when a #policy is set to valid policy" do
188
+
189
+ let(:object) { double foo: valid_policy }
190
+ subject { described_class.new :foo, policy: true }
191
+
192
+ it "calls policy method" do
193
+ expect(object).to receive(:foo)
194
+ end
195
+
196
+ it "doesn't call #invalid" do
197
+ expect(object).not_to receive(:invalid)
198
+ end
199
+
200
+ end # context
201
+
202
+ context "when a #policy is set to valid policy" do
203
+
204
+ let(:object) { double foo: invalid_policy, invalid: nil }
205
+ subject { described_class.new :foo, policy: true }
206
+
207
+ it "calls policy method" do
208
+ expect(object).to receive(:foo)
209
+ end
210
+
211
+ it "calls #invalid with name" do
212
+ expect(object).to receive(:invalid).with(:foo)
213
+ end
214
+
215
+ end # context
216
+
217
+ end # describe #validate
218
+
153
219
  end # describe Attestor::Validation
@@ -1,8 +1,8 @@
1
1
  # encoding: utf-8
2
2
 
3
- describe Attestor::Validations::Collection do
3
+ describe Attestor::Validations::Validators do
4
4
 
5
- let(:item_class) { Attestor::Validations::Item }
5
+ let(:validator_class) { Attestor::Validations::Validator }
6
6
 
7
7
  describe ".new" do
8
8
 
@@ -22,11 +22,11 @@ describe Attestor::Validations::Collection do
22
22
  expect(subject.each).to be_kind_of Enumerator
23
23
  end
24
24
 
25
- it "iterates trough the unique item names" do
26
- items = %w(foo bar foo).map(&item_class.method(:new))
27
- subject = described_class.new items
25
+ it "iterates trough validators" do
26
+ validators = %w(foo bar foo).map(&validator_class.method(:new))
27
+ subject = described_class.new validators
28
28
 
29
- expect(subject.to_a).to match_array %i(foo bar)
29
+ expect(subject.to_a).to match_array validators
30
30
  end
31
31
 
32
32
  end # describe #each
@@ -37,17 +37,17 @@ describe Attestor::Validations::Collection do
37
37
 
38
38
  let(:result) { subject.add("foo") }
39
39
 
40
- it "returns the collection" do
40
+ it "returns validators" do
41
41
  expect(result).to be_kind_of described_class
42
42
  end
43
43
 
44
- it "adds item to the collection" do
44
+ it "adds item to validators" do
45
45
  item = result.first
46
- expect(item).to eq :foo
46
+ expect(item.name).to eq :foo
47
47
  end
48
48
 
49
49
  it "preserves existing items" do
50
- expect(result.add(:bar).to_a).to contain_exactly :foo, :bar
50
+ expect(result.add(:bar).map(&:name)).to contain_exactly :foo, :bar
51
51
  end
52
52
 
53
53
  end # context
@@ -56,15 +56,15 @@ describe Attestor::Validations::Collection do
56
56
 
57
57
  let(:result) { subject.add "foo", only: [:foo] }
58
58
 
59
- it "adds item to the collection" do
60
- expect(result.to_a).to eq [:foo]
61
- expect(result.set(:foo).to_a).to eq [:foo]
62
- expect(result.set(:all).to_a).to eq []
59
+ it "adds item to validators" do
60
+ expect(result.map(&:name)).to eq [:foo]
61
+ expect(result.set(:foo).map(&:name)).to eq [:foo]
62
+ expect(result.set(:all).map(&:name)).to eq []
63
63
  end
64
64
 
65
65
  end # context
66
66
 
67
- context "existing item" do
67
+ context "existing validator" do
68
68
 
69
69
  subject { described_class.new.add "foo" }
70
70
 
@@ -90,11 +90,11 @@ describe Attestor::Validations::Collection do
90
90
  end
91
91
 
92
92
  it "returns a set of items used in given context" do
93
- expect(subject.set("cad").to_a).to contain_exactly :foo, :baz
94
- expect(subject.set("cam").to_a).to contain_exactly :foo, :bar
95
- expect(subject.set("all").to_a).to contain_exactly :bar, :baz
93
+ expect(subject.set("cad").map(&:name)).to contain_exactly :foo, :baz
94
+ expect(subject.set("cam").map(&:name)).to contain_exactly :foo, :bar
95
+ expect(subject.set("all").map(&:name)).to contain_exactly :bar, :baz
96
96
  end
97
97
 
98
98
  end # describe #context
99
99
 
100
- end # describe Attestor::Collection
100
+ end # describe Attestor::Validators
@@ -1,8 +1,12 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require "support/policies" # for #valid_policy and #invalid_policy definitions
4
+
3
5
  describe Attestor::Validations do
4
6
 
5
- let(:collection_class) { Attestor::Validations::Collection }
7
+ let(:validators_class) { Attestor::Validations::Validators }
8
+ let(:validator_class) { Attestor::Validations::Validator }
9
+ let(:collection_class) { Attestor::Validations::Validators }
6
10
  let(:item_class) { Attestor::Validations::Item }
7
11
  let(:message_class) { Attestor::Validations::Message }
8
12
  let(:invalid_error) { Attestor::InvalidError }
@@ -13,17 +17,17 @@ describe Attestor::Validations do
13
17
 
14
18
  subject { test_class.new }
15
19
 
16
- describe ".validations" do
20
+ describe ".validators" do
17
21
 
18
- it "returns a Collection" do
19
- expect(test_class.validations).to be_kind_of collection_class
22
+ it "returns Validators" do
23
+ expect(test_class.validators).to be_kind_of validators_class
20
24
  end
21
25
 
22
26
  it "is empty by default" do
23
- expect(test_class.validations.to_a).to be_empty
27
+ expect(test_class.validators.to_a).to be_empty
24
28
  end
25
29
 
26
- end # describe .validations
30
+ end # describe .validators
27
31
 
28
32
  describe ".validate" do
29
33
 
@@ -31,58 +35,59 @@ describe Attestor::Validations do
31
35
 
32
36
  before { test_class.validate :foo }
33
37
 
34
- it "adds the item to the collection" do
35
- expect(test_class.validations.to_a).to eq [:foo]
38
+ it "registers a validator" do
39
+ expect(test_class.validators.map(&:name)).to eq [:foo]
40
+ end
41
+
42
+ it "sets a validator's policy to false" do
43
+ expect(test_class.validators.first).not_to be_policy
36
44
  end
37
45
 
38
46
  end # context
39
47
 
40
- context "with options" do
48
+ context "with restrictions" do
41
49
 
42
50
  before { test_class.validate :foo, only: %w(bar baz), except: "bar" }
43
51
 
44
52
  it "uses options" do
45
- expect(test_class.validations.to_a).to eq [:foo]
46
- expect(test_class.validations.set(:baz).to_a).to eq [:foo]
47
- expect(test_class.validations.set(:bar).to_a).to eq []
53
+ expect(test_class.validators.map(&:name)).to eq [:foo]
54
+ expect(test_class.validators.set(:baz).map(&:name)).to eq [:foo]
55
+ expect(test_class.validators.set(:bar).map(&:name)).to eq []
48
56
  end
49
57
 
50
58
  end # context
51
59
 
52
60
  end # describe .validate
53
61
 
54
- describe "#validate" do
62
+ describe ".follow_policy" do
55
63
 
56
- before do
57
- test_class.validate :foo
58
- test_class.validate :bar, only: :all
59
- test_class.validate :baz, only: :foo
60
- %i(foo bar baz).each { |method| allow(subject).to receive(method) }
61
- end
64
+ context "without options" do
62
65
 
63
- context "without an argument" do
66
+ before { test_class.follow_policy :foo }
64
67
 
65
- it "calls validations for :all context" do
66
- expect(subject).to receive(:foo)
67
- expect(subject).to receive(:bar)
68
- expect(subject).not_to receive(:baz)
69
- subject.validate
68
+ it "registers a validator" do
69
+ expect(test_class.validators.map(&:name)).to eq [:foo]
70
+ end
71
+
72
+ it "sets a validator's policy to true" do
73
+ expect(test_class.validators.first).to be_policy
70
74
  end
71
75
 
72
76
  end # context
73
77
 
74
- context ":foo" do
78
+ context "with restrictions" do
75
79
 
76
- it "calls validations for :foo context" do
77
- expect(subject).to receive(:foo)
78
- expect(subject).to receive(:baz)
79
- expect(subject).not_to receive(:bar)
80
- subject.validate :foo
80
+ before { test_class.follow_policy :foo, only: %w(bar baz), except: "bar" }
81
+
82
+ it "uses options" do
83
+ expect(test_class.validators.map(&:name)).to eq [:foo]
84
+ expect(test_class.validators.set(:baz).map(&:name)).to eq [:foo]
85
+ expect(test_class.validators.set(:bar).map(&:name)).to eq []
81
86
  end
82
87
 
83
88
  end # context
84
89
 
85
- end # describe #validate
90
+ end # describe .follow_policy
86
91
 
87
92
  describe "#invalid" do
88
93
 
@@ -123,4 +128,40 @@ describe Attestor::Validations do
123
128
 
124
129
  end # invalid
125
130
 
131
+ describe "#validate" do
132
+
133
+ before do
134
+ test_class.validate :foo
135
+ test_class.validate :bar, only: :all
136
+ test_class.follow_policy :baz, only: :foo
137
+
138
+ allow(subject).to receive(:foo)
139
+ allow(subject).to receive(:bar)
140
+ allow(subject).to receive(:baz) { valid_policy }
141
+ end
142
+
143
+ context "without an argument" do
144
+
145
+ it "calls validators for :all context" do
146
+ expect(subject).to receive(:foo)
147
+ expect(subject).to receive(:bar)
148
+ expect(subject).not_to receive(:baz)
149
+ subject.validate
150
+ end
151
+
152
+ end # context
153
+
154
+ context ":foo" do
155
+
156
+ it "calls validators for :foo context" do
157
+ expect(subject).to receive(:foo)
158
+ expect(subject).to receive(:baz)
159
+ expect(subject).not_to receive(:bar)
160
+ subject.validate :foo
161
+ end
162
+
163
+ end # context
164
+
165
+ end # describe #validate
166
+
126
167
  end # describe Attestor::Validations
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attestor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kozin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-25 00:00:00.000000000 Z
11
+ date: 2015-03-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: extlib
@@ -81,10 +81,11 @@ files:
81
81
  - lib/attestor/policy/or.rb
82
82
  - lib/attestor/policy/xor.rb
83
83
  - lib/attestor/validations.rb
84
- - lib/attestor/validations/collection.rb
85
- - lib/attestor/validations/item.rb
86
84
  - lib/attestor/validations/message.rb
85
+ - lib/attestor/validations/validator.rb
86
+ - lib/attestor/validations/validators.rb
87
87
  - lib/attestor/version.rb
88
+ - spec/features/example_spec.rb
88
89
  - spec/spec_helper.rb
89
90
  - spec/support/policies.rb
90
91
  - spec/tests/invalid_error_spec.rb
@@ -96,9 +97,9 @@ files:
96
97
  - spec/tests/policy/or_spec.rb
97
98
  - spec/tests/policy/xor_spec.rb
98
99
  - spec/tests/policy_spec.rb
99
- - spec/tests/validations/collection_spec.rb
100
- - spec/tests/validations/item_spec.rb
101
100
  - spec/tests/validations/message_spec.rb
101
+ - spec/tests/validations/validator_spec.rb
102
+ - spec/tests/validations/validators_spec.rb
102
103
  - spec/tests/validations_spec.rb
103
104
  homepage: https://github.com/nepalez/attestor
104
105
  licenses:
@@ -130,8 +131,8 @@ test_files:
130
131
  - spec/tests/invalid_error_spec.rb
131
132
  - spec/tests/validations_spec.rb
132
133
  - spec/tests/validations/message_spec.rb
133
- - spec/tests/validations/collection_spec.rb
134
- - spec/tests/validations/item_spec.rb
134
+ - spec/tests/validations/validator_spec.rb
135
+ - spec/tests/validations/validators_spec.rb
135
136
  - spec/tests/policy/or_spec.rb
136
137
  - spec/tests/policy/factory_spec.rb
137
138
  - spec/tests/policy/and_spec.rb
@@ -139,5 +140,6 @@ test_files:
139
140
  - spec/tests/policy/negator_spec.rb
140
141
  - spec/tests/policy/not_spec.rb
141
142
  - spec/tests/policy/node_spec.rb
143
+ - spec/features/example_spec.rb
142
144
  - spec/support/policies.rb
143
145
  has_rdoc: