assertion 0.2.0 → 0.2.1

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
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