assertion 0.2.0 → 0.2.1

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: 5cd46bf51eb8357641668f5f3762bf0fb5a0dbd7
4
- data.tar.gz: 048a9c8f663aed972a4ee8e0ded514a7310a6742
3
+ metadata.gz: e02fce1546e9b7523fbff83894476957ca0b335d
4
+ data.tar.gz: 49d6ede0ddd7a992b8bd688cc55ef06b0b7a66bd
5
5
  SHA512:
6
- metadata.gz: ec1de6dade9383cd776f497fe0bfbaf452a1a60ae3ed5b341fe8741fbf891a4f43bf3ac08cbfa7fa1541aae1ce71937ba350cbd3932ae0f1975a01f7ac859e79
7
- data.tar.gz: 7fe6666427458a4291988444e4a333219297cde70cd1aa592d2d3624625d8bc0c39bb647eb25bc1c49c8a1963e609f927443ef6ba51d4cd35ee45a0e0c4d5a81
6
+ metadata.gz: d4db34e23434e46569f8fa530d41bec8a0d8b920e7d13344ed6a0a864a0c416398f4835faa1e1e77b35b8d63025416b1fc32b2e9f17ceb3c85b42678fda9e75e
7
+ data.tar.gz: d9d16dd22f86a0da3e37b6e23f68b0c049bc6832dfec864c94fbfe41f6dd6e2e71b8f0f677f35757fddd012b69116853a59bb1db2d3960db9b73d08c3f059a87
@@ -1,3 +1,19 @@
1
+ ## v0.2.1 2015-06-23
2
+
3
+ ### Bugs Fixed
4
+
5
+ * Support any values returned by `Base#check` method, not only boolean ones (nepalez)
6
+
7
+ ### Internal
8
+
9
+ * `[]` caller method is extracted from `BaseDSL`, `Inverter`, and `GuardDSL` to `DSL::Caller` (nepalez)
10
+ * `GuardDSL` is splitted to `DSL::Caller` and `DSL::Attribute` (nepalez)
11
+ * `.attribute`, `.attributes` and `#attributes` extracted from `BaseDSL` to `DSL::Attributes` (nepalez)
12
+ * `BaseDSL` is splitted to `DSL::Attributes`, `DSL::Caller` and `DSL::Inversion` (nepalez)
13
+ * `DSL` is renamed to `DSL::Builder` to follow the convention. `DSL` is reserved for collection of DSLs (nepalez)
14
+
15
+ [Compare v0.2.0...v0.2.1](https://github.com/nepalez/assertion/compare/v0.2.0...v0.2.1)
16
+
1
17
  ## v0.2.0 2015-06-22
2
18
 
3
19
  ### Changed (backward-incompatible!)
data/README.md CHANGED
@@ -29,7 +29,7 @@ No `ActiveSupport`, no mutation of any instances.
29
29
  ### Basic Usage
30
30
 
31
31
  Define an assertion by inheriting it from the `Assertion::Base` class with attributes to which it should be applied.
32
- Then implement the method `check` that should return a boolean value.
32
+ Then implement the method `check` to describe if the assertion is truthy or falsey.
33
33
 
34
34
  You can do it either in the classic style:
35
35
 
@@ -4,15 +4,19 @@ require "transproc"
4
4
 
5
5
  require_relative "assertion/inflector"
6
6
  require_relative "assertion/invalid_error"
7
+
8
+ require_relative "assertion/dsl/caller"
9
+ require_relative "assertion/dsl/attribute"
10
+ require_relative "assertion/dsl/attributes"
11
+ require_relative "assertion/dsl/inversion"
12
+ require_relative "assertion/dsl/builder"
13
+
7
14
  require_relative "assertion/translator"
8
15
  require_relative "assertion/state"
9
- require_relative "assertion/base_dsl"
10
16
  require_relative "assertion/base"
11
17
  require_relative "assertion/inversion"
12
18
  require_relative "assertion/inverter"
13
- require_relative "assertion/guard_dsl"
14
19
  require_relative "assertion/guard"
15
- require_relative "assertion/dsl"
16
20
 
17
21
  # The module declares:
18
22
  #
@@ -54,6 +58,6 @@ require_relative "assertion/dsl"
54
58
  #
55
59
  module Assertion
56
60
 
57
- extend DSL
61
+ extend DSL::Builder
58
62
 
59
63
  end # module Assertion
@@ -35,7 +35,9 @@ module Assertion
35
35
  #
36
36
  class Base
37
37
 
38
- extend BaseDSL
38
+ extend DSL::Attributes
39
+ extend DSL::Inversion
40
+ extend DSL::Caller
39
41
 
40
42
  # The translator of states for the current class
41
43
  #
@@ -81,7 +83,7 @@ module Assertion
81
83
  #
82
84
  # @return [String]
83
85
  #
84
- def message(state)
86
+ def message(state = nil)
85
87
  self.class.translator.call(state, attributes)
86
88
  end
87
89
 
@@ -94,7 +96,7 @@ module Assertion
94
96
  # The state of the assertion being applied to its attributes
95
97
  #
96
98
  def call
97
- State.new check, message(false)
99
+ State.new check, message
98
100
  end
99
101
 
100
102
  end # class Base
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+
3
+ module Assertion
4
+
5
+ # Collection of DSLs for various modules and classes
6
+ #
7
+ module DSL
8
+
9
+ # Allows adding aliases to `#object` method.
10
+ #
11
+ module Attribute
12
+
13
+ # Adds alias to the [#object] method
14
+ #
15
+ # @param [#to_sym] name
16
+ #
17
+ # @return [undefined]
18
+ #
19
+ # @raise [NameError]
20
+ # When a given name is either used by instance methods,
21
+ # or reserved by the `#state` method to be implemented later.
22
+ #
23
+ def attribute(name)
24
+ __check_attribute__(name)
25
+ alias_method name, :object
26
+ end
27
+
28
+ # @private
29
+ def self.extended(klass)
30
+ klass.__send__ :attr_reader, :object
31
+ end
32
+
33
+ private
34
+
35
+ def __check_attribute__(key)
36
+ return unless (instance_methods << :state).include? key.to_sym
37
+ fail NameError.new "#{self}##{key} is already defined"
38
+ end
39
+
40
+ end # module Attribute
41
+
42
+ end # module DSL
43
+
44
+ end # module Assertion
@@ -0,0 +1,54 @@
1
+ # encoding: utf-8
2
+
3
+ module Assertion
4
+
5
+ module DSL
6
+
7
+ # Allows adding aliases to values of the `#attributes` hash.
8
+ #
9
+ module Attributes
10
+
11
+ # List of declared attributes
12
+ #
13
+ # @return [Array<Symbol>]
14
+ #
15
+ def attributes
16
+ @attributes ||= []
17
+ end
18
+
19
+ # Declares new attribute(s) by name(s)
20
+ #
21
+ # @param [#to_sym, Array<#to_sym>] names
22
+ #
23
+ # @return [undefined]
24
+ #
25
+ # @raise [NameError]
26
+ # When a given name is either used by instance methods,
27
+ # or reserved by the `#check` method to be implemented later.
28
+ #
29
+ def attribute(*names)
30
+ names.flatten.map(&:to_sym).each(&method(:__add_attribute__))
31
+ end
32
+
33
+ # @private
34
+ def self.extended(klass)
35
+ klass.__send__(:define_method, :attributes) { @attributes ||= {} }
36
+ end
37
+
38
+ private
39
+
40
+ def __add_attribute__(name)
41
+ __check_attribute__(name)
42
+ attributes << define_method(name) { attributes[name] }
43
+ end
44
+
45
+ def __check_attribute__(name)
46
+ return unless (instance_methods << :check).include? name
47
+ fail NameError.new "#{self}##{name} is already defined"
48
+ end
49
+
50
+ end # module Attributes
51
+
52
+ end # module DSL
53
+
54
+ end # module Assertion
@@ -0,0 +1,81 @@
1
+ # encoding: utf-8
2
+
3
+ module Assertion
4
+
5
+ module DSL
6
+
7
+ # Provides methods to build assertions and guards
8
+ #
9
+ module Builder
10
+
11
+ # Builds the subclass of `Assertion::Base` with predefined `attributes`
12
+ # and implementation of the `#check` method.
13
+ #
14
+ # @example
15
+ # IsMan = Assertion.about :age, :gender do
16
+ # (age >= 18) && (gender == :male)
17
+ # end
18
+ #
19
+ # # This is the same as:
20
+ # class IsMan < Assertion::Base
21
+ # attribute :age, :gender
22
+ #
23
+ # def check
24
+ # (age >= 18) && (gender == :male)
25
+ # end
26
+ # end
27
+ #
28
+ # @param [Symbol, Array<Symbol>] attributes
29
+ # The list of attributes for the new assertion
30
+ # @param [Proc] block
31
+ # The content for the `check` method
32
+ #
33
+ # @return [Class] The specific assertion class
34
+ #
35
+ def about(*attributes, &block)
36
+ __build__(Base, attributes, :check, &block)
37
+ end
38
+
39
+ # Builds the subclass of `Assertion::Guard` with given attribute
40
+ # (alias for the `object`) and implementation of the `#state` method.
41
+ #
42
+ # @example
43
+ # VoterOnly = Assertion.guards :user do
44
+ # IsAdult[user.attributes] & IsCitizen[user.attributes]
45
+ # end
46
+ #
47
+ # # This is the same as:
48
+ # class VoterOnly < Assertion::Guard
49
+ # alias_method :user, :object
50
+ #
51
+ # def state
52
+ # IsAdult[user.attributes] & IsCitizen[user.attributes]
53
+ # end
54
+ # end
55
+ #
56
+ # @param [Symbol] attribute
57
+ # The alias for the `object` attribute
58
+ # @param [Proc] block
59
+ # The content for the `state` method
60
+ #
61
+ # @return [Class] The specific guard class
62
+ #
63
+ def guards(attribute = nil, &block)
64
+ __build__(Guard, attribute, :state, &block)
65
+ end
66
+
67
+ private
68
+
69
+ def __build__(type, attributes, name, &block)
70
+ klass = Class.new(type)
71
+ klass.public_send(:attribute, attributes) if attributes
72
+ klass.__send__(:define_method, name, &block) if block_given?
73
+
74
+ klass
75
+ end
76
+
77
+ end # module Builder
78
+
79
+ end # module DSL
80
+
81
+ end # module Assertion
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ module Assertion
4
+
5
+ module DSL
6
+
7
+ # Allows to initialize and call objects at once
8
+ #
9
+ module Caller
10
+
11
+ # Initializes and immediately calls the instance of the class
12
+ #
13
+ # @param [Object, Array<Object>] args
14
+ # The <list of> arguments to initialize the instance with
15
+ #
16
+ # @return [Object] the result of #call method
17
+ #
18
+ def [](*args)
19
+ new(*args).call
20
+ end
21
+
22
+ end # module Caller
23
+
24
+ end # module DSL
25
+
26
+ end # module Assertion
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+
3
+ module Assertion
4
+
5
+ module DSL
6
+
7
+ # Allows to provide inverter for objects of the current class
8
+ #
9
+ module Inversion
10
+
11
+ # Initializes the intermediate inverter with `new` and `[]` methods
12
+ #
13
+ # The inverter can be used to initialize the assertion, that describes
14
+ # just the opposite statement to the current one
15
+ #
16
+ # @example
17
+ # IsAdult = Assertion.about :name, :age do
18
+ # age >= 18
19
+ # end
20
+ #
21
+ # joe = { name: 'Joe', age: 19 }
22
+ #
23
+ # IsAdult[joe].valid? # => true
24
+ # IsAdult.not[joe].valid? # => false
25
+ #
26
+ # @return [Assertion::Inverter]
27
+ #
28
+ def not
29
+ Inverter.new(self)
30
+ end
31
+
32
+ end # module Inversion
33
+
34
+ end # module DSL
35
+
36
+ end # module Assertion
@@ -32,13 +32,8 @@ module Assertion
32
32
  #
33
33
  class Guard
34
34
 
35
- extend GuardDSL
36
-
37
- # @!attribute [r] object
38
- #
39
- # @return [Object] The object whose state should be tested
40
- #
41
- attr_reader :object
35
+ extend DSL::Attribute
36
+ extend DSL::Caller
42
37
 
43
38
  # @!scope class
44
39
  # @!method new(object)
@@ -17,6 +17,8 @@ module Assertion
17
17
  #
18
18
  class Inverter
19
19
 
20
+ include DSL::Caller
21
+
20
22
  # @!attribute [r] source
21
23
  #
22
24
  # @return [Class] The `Assertion::Base` sublcass to build negators for
@@ -47,16 +49,6 @@ module Assertion
47
49
  Inversion.new source.new(hash)
48
50
  end
49
51
 
50
- # Initializes an assertion, builds its inversion, and applies it to the data
51
- #
52
- # @param (see #new)
53
- #
54
- # @return (see Assertion::Base#call)
55
- #
56
- def [](hash = {})
57
- new(hash).call
58
- end
59
-
60
52
  end # class Inverter
61
53
 
62
54
  end # module Assertion
@@ -87,7 +87,7 @@ module Assertion
87
87
  # @return [String] The translation
88
88
  #
89
89
  def call(state, args = {})
90
- I18n.translate DICTIONARY[state], args.merge(scope: scope)
90
+ I18n.translate DICTIONARY[state ? true : false], args.merge(scope: scope)
91
91
  end
92
92
 
93
93
  end # class Translator
@@ -4,6 +4,6 @@ module Assertion
4
4
 
5
5
  # The semantic version of the module.
6
6
  # @see http://semver.org/ Semantic versioning 2.0
7
- VERSION = "0.2.0".freeze
7
+ VERSION = "0.2.1".freeze
8
8
 
9
9
  end # module Assertion
@@ -5,6 +5,18 @@ describe Assertion::Base do
5
5
  let(:klass) { Class.new(described_class) }
6
6
  before { allow(klass).to receive(:name) { "Test" } }
7
7
 
8
+ it "implements DSL::Caller" do
9
+ expect(klass).to be_kind_of Assertion::DSL::Caller
10
+ end
11
+
12
+ it "implements DSL::Attributes" do
13
+ expect(klass).to be_kind_of Assertion::DSL::Attributes
14
+ end
15
+
16
+ it "implements DSL::Inversion" do
17
+ expect(klass).to be_kind_of Assertion::DSL::Inversion
18
+ end
19
+
8
20
  describe ".new" do
9
21
 
10
22
  let(:klass) { Class.new(described_class) { attribute :foo, :bar } }
@@ -37,158 +49,39 @@ describe Assertion::Base do
37
49
 
38
50
  end # describe .new
39
51
 
40
- describe ".attributes" do
41
-
42
- subject { klass.attributes }
43
- it { is_expected.to eql [] }
44
-
45
- end # describe .attributes
52
+ describe "#message" do
46
53
 
47
- describe ".attribute" do
54
+ let(:klass) { Class.new(described_class) { attribute :foo } }
55
+ let(:assertion) { klass.new(foo: :FOO) }
48
56
 
49
- shared_examples "defining attributes" do
57
+ shared_examples "translating" do |as: nil|
50
58
 
51
- it "registers attributes" do
52
- expect { subject }.to change { klass.attributes }.to [:foo, :bar]
53
- end
59
+ let(:translator) { Assertion::Translator.new(klass) }
60
+ let(:attributes) { assertion.attributes }
54
61
 
55
- it "declares attributes" do
62
+ it "uses attributes in a translation" do
63
+ expect(I18n).to receive :translate do |_, args|
64
+ expect(args.merge(attributes)).to eql args
65
+ end
56
66
  subject
57
- assertion = klass.new(foo: :FOO, bar: :BAR, baz: :BAZ)
58
- expect(assertion.attributes).to eql(foo: :FOO, bar: :BAR)
59
- expect(assertion.foo).to eql :FOO
60
- expect(assertion.bar).to eql :BAR
61
67
  end
62
68
 
63
- end # shared examples
64
-
65
- shared_examples "raising NameError" do |with: nil|
66
-
67
- it "fails" do
68
- expect { subject }.to raise_error do |exception|
69
- expect(exception).to be_kind_of NameError
70
- expect(exception.message).to eql "#{klass}##{with} is already defined"
71
- end
69
+ it "returns a translation" do
70
+ expect(subject).to eql translator.call(as, attributes)
72
71
  end
73
72
 
74
73
  end # shared examples
75
74
 
76
- context "with a single name" do
77
-
78
- subject do
79
- klass.attribute :foo
80
- klass.attribute "bar"
81
- end
82
- it_behaves_like "defining attributes"
83
-
84
- end # context
85
-
86
- context "with a list of names" do
87
-
88
- subject { klass.attribute :foo, :bar }
89
- it_behaves_like "defining attributes"
90
-
91
- end # context
92
-
93
- context "with an array of names" do
94
-
95
- subject { klass.attribute %w(foo bar) }
96
- it_behaves_like "defining attributes"
97
-
98
- end # context
99
-
100
- context ":check" do
101
-
102
- subject { klass.attribute :check }
103
- it_behaves_like "raising NameError", with: :check
104
-
105
- end # context
106
-
107
- context ":call" do
108
-
109
- subject { klass.attribute :call }
110
- it_behaves_like "raising NameError", with: :call
111
-
112
- end # context
113
-
114
- end # describe .attribute
115
-
116
- describe ".not" do
117
-
118
- subject { klass.not }
119
-
120
- it "creates the iverter for the current class" do
121
- expect(subject).to be_kind_of Assertion::Inverter
122
- expect(subject.source).to eql klass
75
+ it_behaves_like "translating", as: true do
76
+ subject { assertion.message(true) }
123
77
  end
124
78
 
125
- end # describe .not
126
-
127
- describe ".[]" do
128
-
129
- let(:params) { { foo: :FOO } }
130
- let(:state) { double }
131
- let(:assertion) { double call: state }
132
-
133
- context "with params" do
134
-
135
- subject { klass[params] }
136
-
137
- it "checks the assertion for given attributes" do
138
- allow(klass).to receive(:new).with(params) { assertion }
139
- expect(subject).to eql state
140
- end
141
-
142
- end # context
143
-
144
- context "without params" do
145
-
146
- subject { klass[] }
147
-
148
- it "checks the assertion" do
149
- allow(klass).to receive(:new) { assertion }
150
- expect(subject).to eql state
151
- end
152
-
153
- end # context
154
-
155
- end # describe .[]
156
-
157
- describe ".translator" do
158
-
159
- subject { klass.translator }
160
-
161
- it { is_expected.to be_kind_of Assertion::Translator }
162
-
163
- it "refers to the current class" do
164
- expect(subject.assertion).to eql klass
79
+ it_behaves_like "translating", as: false do
80
+ subject { assertion.message(false) }
165
81
  end
166
82
 
167
- end # describe .translator
168
-
169
- describe "#attributes" do
170
-
171
- let(:attrs) { { foo: :FOO, bar: :BAR } }
172
- let(:klass) { Class.new(described_class) { attribute :foo, :bar } }
173
- let(:assertion) { klass.new attrs }
174
-
175
- subject { assertion.attributes }
176
- it { is_expected.to eql attrs }
177
-
178
- end # describe #attributes
179
-
180
- describe "#message" do
181
-
182
- let(:state) { double }
183
- let(:attrs) { { foo: :FOO, bar: :BAR } }
184
- let(:klass) { Class.new(described_class) { attribute :foo, :bar } }
185
- let(:assertion) { klass.new attrs }
186
- let(:translator) { double call: nil }
187
-
188
- it "calls a translator with state and attributes" do
189
- allow(klass).to receive(:translator) { translator }
190
- expect(translator).to receive(:call).with(state, attrs)
191
- assertion.message(state)
83
+ it_behaves_like "translating", as: false do
84
+ subject { assertion.message }
192
85
  end
193
86
 
194
87
  end # describe #message