assertion 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 +4 -4
- data/CHANGELOG.md +20 -0
- data/README.md +54 -43
- data/lib/assertion.rb +37 -6
- data/lib/assertion/attributes.rb +54 -0
- data/lib/assertion/base.rb +7 -65
- data/lib/assertion/guard.rb +99 -0
- data/lib/assertion/{exceptions/invalid_error.rb → invalid_error.rb} +5 -1
- data/lib/assertion/inversion.rb +1 -1
- data/lib/assertion/messages.rb +65 -0
- data/lib/assertion/state.rb +1 -1
- data/lib/assertion/transprocs/inflector.rb +2 -0
- data/lib/assertion/version.rb +1 -1
- data/spec/integration/assertion_spec.rb +3 -12
- data/spec/integration/guard_spec.rb +29 -0
- data/spec/{integration → shared}/en.yml +1 -1
- data/spec/shared/i18n.rb +21 -0
- data/spec/unit/assertion/attributes_spec.rb +97 -0
- data/spec/unit/assertion/base_spec.rb +6 -103
- data/spec/unit/assertion/exceptions/invalid_error_spec.rb +11 -10
- data/spec/unit/assertion/guard_spec.rb +82 -0
- data/spec/unit/assertion/messages_spec.rb +41 -0
- data/spec/unit/assertion_spec.rb +69 -12
- metadata +18 -15
- data/lib/assertion/exceptions/name_error.rb +0 -29
- data/lib/assertion/exceptions/not_implemented_error.rb +0 -29
- data/lib/assertion/transprocs/i18n.rb +0 -55
- data/spec/unit/assertion/exceptions/name_error_spec.rb +0 -26
- data/spec/unit/assertion/exceptions/not_implemented_error_spec.rb +0 -26
- data/spec/unit/assertion/transprocs/i18n/to_scope_spec.rb +0 -19
- data/spec/unit/assertion/transprocs/i18n/translate_spec.rb +0 -28
@@ -0,0 +1,82 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "ostruct"
|
4
|
+
|
5
|
+
describe Assertion::Guard do
|
6
|
+
|
7
|
+
before do
|
8
|
+
IsAdult = Assertion.about(:age) { age.to_i >= 18 }
|
9
|
+
AdultOnly = Class.new(described_class) { def state; IsAdult[object]; end }
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:valid) { OpenStruct.new(name: "Joe", age: 40) }
|
13
|
+
let(:invalid) { OpenStruct.new(name: "Ian", age: 10) }
|
14
|
+
let(:guard) { AdultOnly.new valid }
|
15
|
+
|
16
|
+
it "can declare attributes" do
|
17
|
+
expect(AdultOnly).to be_kind_of Assertion::Attributes
|
18
|
+
end
|
19
|
+
|
20
|
+
describe ".new" do
|
21
|
+
|
22
|
+
subject { guard }
|
23
|
+
|
24
|
+
it { is_expected.to be_frozen }
|
25
|
+
|
26
|
+
context "with an attriubute" do
|
27
|
+
|
28
|
+
before { allow(AdultOnly).to receive(:attributes) { [:foo] } }
|
29
|
+
|
30
|
+
it "adds the alias to the #object" do
|
31
|
+
expect(subject.foo).to eql subject.object
|
32
|
+
end
|
33
|
+
|
34
|
+
end # context
|
35
|
+
|
36
|
+
end # describe .new
|
37
|
+
|
38
|
+
describe ".[]" do
|
39
|
+
|
40
|
+
it "returns the result of the call" do
|
41
|
+
expect(AdultOnly[valid]).to eql valid
|
42
|
+
expect { AdultOnly[invalid] }.to raise_error Assertion::InvalidError
|
43
|
+
end
|
44
|
+
|
45
|
+
end # describe .[]
|
46
|
+
|
47
|
+
describe "#object" do
|
48
|
+
|
49
|
+
subject { guard.object }
|
50
|
+
|
51
|
+
it { is_expected.to eql valid }
|
52
|
+
|
53
|
+
end # describe #object
|
54
|
+
|
55
|
+
describe "#call" do
|
56
|
+
|
57
|
+
subject { guard.call }
|
58
|
+
|
59
|
+
context "when #state is valid" do
|
60
|
+
|
61
|
+
it { is_expected.to eql valid }
|
62
|
+
|
63
|
+
end # context
|
64
|
+
|
65
|
+
context "when #state is invalid" do
|
66
|
+
|
67
|
+
let(:guard) { AdultOnly.new invalid }
|
68
|
+
|
69
|
+
it "raises InvalidError" do
|
70
|
+
expect { subject }.to raise_error Assertion::InvalidError
|
71
|
+
end
|
72
|
+
|
73
|
+
end # context
|
74
|
+
|
75
|
+
end # describe #call
|
76
|
+
|
77
|
+
after do
|
78
|
+
Object.send :remove_const, :AdultOnly
|
79
|
+
Object.send :remove_const, :IsAdult
|
80
|
+
end
|
81
|
+
|
82
|
+
end # describe Assertion::Guard
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
describe Assertion::Messages, "#message" do
|
4
|
+
|
5
|
+
let(:test_klass) { Test = Class.new { include Assertion::Messages } }
|
6
|
+
let(:instance) { test_klass.new }
|
7
|
+
|
8
|
+
shared_examples "translating" do |state, as: nil|
|
9
|
+
|
10
|
+
subject(:message) { instance.message(state) }
|
11
|
+
|
12
|
+
it { is_expected.to eql "translation missing: en.assertion.test.#{as}" }
|
13
|
+
|
14
|
+
it "sends attributes to translation" do
|
15
|
+
expect(I18n).to receive(:translate) do |_, args|
|
16
|
+
expect(args.merge(instance.attributes)).to eql args
|
17
|
+
end
|
18
|
+
subject
|
19
|
+
end
|
20
|
+
|
21
|
+
end # shared examples
|
22
|
+
|
23
|
+
context "without attributes" do
|
24
|
+
|
25
|
+
it_behaves_like "translating", true, as: "right"
|
26
|
+
it_behaves_like "translating", false, as: "wrong"
|
27
|
+
|
28
|
+
end # context
|
29
|
+
|
30
|
+
context "with attributes" do
|
31
|
+
|
32
|
+
before { test_klass.send(:define_method, :attributes) { { foo: :FOO } } }
|
33
|
+
|
34
|
+
it_behaves_like "translating", true, as: "right"
|
35
|
+
it_behaves_like "translating", false, as: "wrong"
|
36
|
+
|
37
|
+
end # context
|
38
|
+
|
39
|
+
after { Object.send :remove_const, :Test }
|
40
|
+
|
41
|
+
end # describe Assertion::Messages
|
data/spec/unit/assertion_spec.rb
CHANGED
@@ -2,18 +2,18 @@
|
|
2
2
|
|
3
3
|
describe Assertion do
|
4
4
|
|
5
|
-
|
5
|
+
let(:john) { { age: 17, gender: :male } }
|
6
|
+
let(:jack) { { age: 18, gender: :male } }
|
6
7
|
|
7
|
-
|
8
|
-
let(:jack) { { age: 18, gender: :male } }
|
8
|
+
describe ".about" do
|
9
9
|
|
10
10
|
context "with attributes and a block" do
|
11
11
|
|
12
12
|
subject do
|
13
|
-
|
13
|
+
IsMan = Assertion.about(:age, :gender) { age >= 18 && gender == :male }
|
14
14
|
end
|
15
15
|
|
16
|
-
it "
|
16
|
+
it "builds the assertion class" do
|
17
17
|
expect(subject.superclass).to eql Assertion::Base
|
18
18
|
end
|
19
19
|
|
@@ -27,10 +27,10 @@ describe Assertion do
|
|
27
27
|
context "without attributes" do
|
28
28
|
|
29
29
|
subject do
|
30
|
-
|
30
|
+
IsMan = Assertion.about { true }
|
31
31
|
end
|
32
32
|
|
33
|
-
it "
|
33
|
+
it "builds the assertion class" do
|
34
34
|
expect(subject.superclass).to eql Assertion::Base
|
35
35
|
end
|
36
36
|
|
@@ -44,22 +44,79 @@ describe Assertion do
|
|
44
44
|
context "without a block" do
|
45
45
|
|
46
46
|
subject do
|
47
|
-
|
47
|
+
IsMan = Assertion.about(:age, :gender)
|
48
48
|
end
|
49
49
|
|
50
|
-
it "
|
50
|
+
it "builds the assertion class" do
|
51
51
|
expect(subject.superclass).to eql Assertion::Base
|
52
52
|
end
|
53
53
|
|
54
54
|
it "doesn't implement the #check method" do
|
55
|
-
expect { subject
|
56
|
-
.to raise_error Assertion::NotImplementedError
|
55
|
+
expect { subject.new(jack).check }.to raise_error NoMethodError
|
57
56
|
end
|
58
57
|
|
59
58
|
end # context
|
60
59
|
|
61
|
-
after { Object.send :remove_const, :
|
60
|
+
after { Object.send :remove_const, :IsMan }
|
62
61
|
|
63
62
|
end # describe .about
|
64
63
|
|
64
|
+
describe ".guards" do
|
65
|
+
|
66
|
+
before { IsAdult = Assertion.about(:age) { age.to_i >= 18 } }
|
67
|
+
|
68
|
+
context "with an attribute and a block" do
|
69
|
+
|
70
|
+
subject { Assertion.guards(:user) { IsAdult[user] } }
|
71
|
+
|
72
|
+
it "builds the guard class" do
|
73
|
+
expect(subject.superclass).to eql Assertion::Guard
|
74
|
+
end
|
75
|
+
|
76
|
+
it "defines an alias for the object" do
|
77
|
+
expect(subject.new(jack).user).to eql jack
|
78
|
+
end
|
79
|
+
|
80
|
+
it "implements the #state" do
|
81
|
+
expect { subject.new(jack).state }.not_to raise_error
|
82
|
+
end
|
83
|
+
|
84
|
+
end # context
|
85
|
+
|
86
|
+
context "without an attribute" do
|
87
|
+
|
88
|
+
subject { Assertion.guards { IsAdult[object] } }
|
89
|
+
|
90
|
+
it "builds the guard class" do
|
91
|
+
expect(subject.superclass).to eql Assertion::Guard
|
92
|
+
end
|
93
|
+
|
94
|
+
it "implements the #state of #object" do
|
95
|
+
expect { subject.new(jack).state }.not_to raise_error
|
96
|
+
end
|
97
|
+
|
98
|
+
end # context
|
99
|
+
|
100
|
+
context "without a block" do
|
101
|
+
|
102
|
+
subject { Assertion.guards(:user) }
|
103
|
+
|
104
|
+
it "builds the guard class" do
|
105
|
+
expect(subject.superclass).to eql Assertion::Guard
|
106
|
+
end
|
107
|
+
|
108
|
+
it "defines an alias for the object" do
|
109
|
+
expect(subject.new(jack).user).to eql jack
|
110
|
+
end
|
111
|
+
|
112
|
+
it "doesn't implement the #state" do
|
113
|
+
expect { subject.new(jack).state }.to raise_error NoMethodError
|
114
|
+
end
|
115
|
+
|
116
|
+
end # context
|
117
|
+
|
118
|
+
after { Object.send :remove_const, :IsAdult }
|
119
|
+
|
120
|
+
end # describe .guards
|
121
|
+
|
65
122
|
end # describe Assertion
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: assertion
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
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-06-
|
11
|
+
date: 2015-06-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: transproc
|
@@ -81,6 +81,7 @@ files:
|
|
81
81
|
- ".rubocop.yml"
|
82
82
|
- ".travis.yml"
|
83
83
|
- ".yardopts"
|
84
|
+
- CHANGELOG.md
|
84
85
|
- Gemfile
|
85
86
|
- Guardfile
|
86
87
|
- LICENSE
|
@@ -99,29 +100,30 @@ files:
|
|
99
100
|
- config/metrics/simplecov.yml
|
100
101
|
- config/metrics/yardstick.yml
|
101
102
|
- lib/assertion.rb
|
103
|
+
- lib/assertion/attributes.rb
|
102
104
|
- lib/assertion/base.rb
|
103
|
-
- lib/assertion/
|
104
|
-
- lib/assertion/
|
105
|
-
- lib/assertion/exceptions/not_implemented_error.rb
|
105
|
+
- lib/assertion/guard.rb
|
106
|
+
- lib/assertion/invalid_error.rb
|
106
107
|
- lib/assertion/inversion.rb
|
107
108
|
- lib/assertion/inverter.rb
|
109
|
+
- lib/assertion/messages.rb
|
108
110
|
- lib/assertion/state.rb
|
109
|
-
- lib/assertion/transprocs/i18n.rb
|
110
111
|
- lib/assertion/transprocs/inflector.rb
|
111
112
|
- lib/assertion/transprocs/list.rb
|
112
113
|
- lib/assertion/version.rb
|
113
114
|
- spec/integration/assertion_spec.rb
|
114
|
-
- spec/integration/
|
115
|
+
- spec/integration/guard_spec.rb
|
116
|
+
- spec/shared/en.yml
|
117
|
+
- spec/shared/i18n.rb
|
115
118
|
- spec/spec_helper.rb
|
119
|
+
- spec/unit/assertion/attributes_spec.rb
|
116
120
|
- spec/unit/assertion/base_spec.rb
|
117
121
|
- spec/unit/assertion/exceptions/invalid_error_spec.rb
|
118
|
-
- spec/unit/assertion/
|
119
|
-
- spec/unit/assertion/exceptions/not_implemented_error_spec.rb
|
122
|
+
- spec/unit/assertion/guard_spec.rb
|
120
123
|
- spec/unit/assertion/inversion_spec.rb
|
121
124
|
- spec/unit/assertion/inverter_spec.rb
|
125
|
+
- spec/unit/assertion/messages_spec.rb
|
122
126
|
- spec/unit/assertion/state_spec.rb
|
123
|
-
- spec/unit/assertion/transprocs/i18n/to_scope_spec.rb
|
124
|
-
- spec/unit/assertion/transprocs/i18n/translate_spec.rb
|
125
127
|
- spec/unit/assertion/transprocs/inflector/to_path_spec.rb
|
126
128
|
- spec/unit/assertion/transprocs/inflector/to_snake_path_spec.rb
|
127
129
|
- spec/unit/assertion/transprocs/inflector/to_snake_spec.rb
|
@@ -154,18 +156,19 @@ summary: PORO assertions and validation
|
|
154
156
|
test_files:
|
155
157
|
- spec/spec_helper.rb
|
156
158
|
- spec/integration/assertion_spec.rb
|
159
|
+
- spec/integration/guard_spec.rb
|
157
160
|
- spec/unit/assertion_spec.rb
|
158
161
|
- spec/unit/assertion/state_spec.rb
|
159
162
|
- spec/unit/assertion/transprocs/inflector/to_snake_path_spec.rb
|
160
163
|
- spec/unit/assertion/transprocs/inflector/to_snake_spec.rb
|
161
164
|
- spec/unit/assertion/transprocs/inflector/to_path_spec.rb
|
162
|
-
- spec/unit/assertion/transprocs/i18n/to_scope_spec.rb
|
163
|
-
- spec/unit/assertion/transprocs/i18n/translate_spec.rb
|
164
165
|
- spec/unit/assertion/transprocs/list/symbolize_spec.rb
|
165
166
|
- spec/unit/assertion/exceptions/invalid_error_spec.rb
|
166
|
-
- spec/unit/assertion/
|
167
|
-
- spec/unit/assertion/exceptions/name_error_spec.rb
|
167
|
+
- spec/unit/assertion/messages_spec.rb
|
168
168
|
- spec/unit/assertion/inverter_spec.rb
|
169
169
|
- spec/unit/assertion/inversion_spec.rb
|
170
|
+
- spec/unit/assertion/guard_spec.rb
|
171
|
+
- spec/unit/assertion/attributes_spec.rb
|
170
172
|
- spec/unit/assertion/base_spec.rb
|
173
|
+
- spec/shared/i18n.rb
|
171
174
|
has_rdoc:
|
@@ -1,29 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Assertion
|
4
|
-
|
5
|
-
# The exception to be raised when a Assertion attribute uses reserved name(s)
|
6
|
-
#
|
7
|
-
# @api public
|
8
|
-
#
|
9
|
-
class NameError < ::NameError
|
10
|
-
|
11
|
-
# @!scope class
|
12
|
-
# @!method new(*names)
|
13
|
-
# Creates an exception instance
|
14
|
-
#
|
15
|
-
# @param [Symbol, Array<Symbol>] names Wrong names of attribute(s)
|
16
|
-
#
|
17
|
-
# @return [Assertion::NameError]
|
18
|
-
#
|
19
|
-
# @api private
|
20
|
-
|
21
|
-
# @private
|
22
|
-
def initialize(*names)
|
23
|
-
super "Wrong name(s) for attribute(s): #{names.join(", ")}"
|
24
|
-
freeze
|
25
|
-
end
|
26
|
-
|
27
|
-
end # class NameError
|
28
|
-
|
29
|
-
end # module Assertion
|
@@ -1,29 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Assertion
|
4
|
-
|
5
|
-
# The exception to be raised when a assertion is applied to some data
|
6
|
-
# before its `check` method has been implemented
|
7
|
-
#
|
8
|
-
# @api public
|
9
|
-
#
|
10
|
-
class NotImplementedError < ::NotImplementedError
|
11
|
-
|
12
|
-
# @!scope class
|
13
|
-
# @!method new(klass, name)
|
14
|
-
# Creates an exception instance
|
15
|
-
#
|
16
|
-
# @param [Class] klass The class that should implement the instance method
|
17
|
-
# @param [Symbol] name The name of the method to be implemented
|
18
|
-
#
|
19
|
-
# @return [Assertion::NotImplementedError]
|
20
|
-
|
21
|
-
# @private
|
22
|
-
def initialize(klass, name)
|
23
|
-
super "#{klass.name}##{name} method not implemented"
|
24
|
-
freeze
|
25
|
-
end
|
26
|
-
|
27
|
-
end # class NotImplementedError
|
28
|
-
|
29
|
-
end # module Assertion
|
@@ -1,55 +0,0 @@
|
|
1
|
-
module Assertion
|
2
|
-
|
3
|
-
# The collection of pure functions for translating strings in the gem-specific
|
4
|
-
# scopes of `Assertion::Base` subclasses.
|
5
|
-
#
|
6
|
-
# @api private
|
7
|
-
#
|
8
|
-
module I18n
|
9
|
-
|
10
|
-
extend ::Transproc::Registry
|
11
|
-
|
12
|
-
uses :to_snake_path, from: Inflector, as: :snake
|
13
|
-
|
14
|
-
# Converts the name of the class to the corresponding gem-specific scope
|
15
|
-
#
|
16
|
-
# @example
|
17
|
-
# fn = I18n[:scope]
|
18
|
-
# fn["Foo::BarBaz"]
|
19
|
-
# # => [:assertion, :"foo/bar_baz"]
|
20
|
-
#
|
21
|
-
# @param [String] name The name of the class
|
22
|
-
#
|
23
|
-
# @return [Array<Symbol>] The `I18n`-compatible gem-specific scope
|
24
|
-
#
|
25
|
-
def scope(name)
|
26
|
-
[:assertion, snake(name).to_sym]
|
27
|
-
end
|
28
|
-
|
29
|
-
# Translates the key with hash of attributes in a given scope
|
30
|
-
#
|
31
|
-
# @example
|
32
|
-
# # config/locales/en.yml
|
33
|
-
# # ---
|
34
|
-
# # en:
|
35
|
-
# # assertion:
|
36
|
-
# # foo:
|
37
|
-
# # qux: "message %{bar}"
|
38
|
-
#
|
39
|
-
# fn = I18n[:translate, [:assertion, :foo], bar: :BAZ]
|
40
|
-
# fn[:qux]
|
41
|
-
# # => "message BAZ"
|
42
|
-
#
|
43
|
-
# @param [key] key The key to be translated
|
44
|
-
# @param [Array<Symbol>] scope The I18n scope for the translations
|
45
|
-
# @param [Hash] hash The hash of attributes for the translation
|
46
|
-
#
|
47
|
-
# @return [String] The translated string
|
48
|
-
#
|
49
|
-
def translate(key, scope, hash)
|
50
|
-
::I18n.t(key, hash.merge(scope: scope))
|
51
|
-
end
|
52
|
-
|
53
|
-
end # module I18n
|
54
|
-
|
55
|
-
end # module Assertion
|