assertion 0.0.1 → 0.1.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 +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
|