assertion 0.0.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 +7 -0
- data/.coveralls.yml +2 -0
- data/.gitignore +9 -0
- data/.metrics +9 -0
- data/.rspec +2 -0
- data/.rubocop.yml +2 -0
- data/.travis.yml +19 -0
- data/.yardopts +3 -0
- data/Gemfile +9 -0
- data/Guardfile +18 -0
- data/LICENSE +21 -0
- data/README.md +222 -0
- data/Rakefile +29 -0
- data/assertion.gemspec +27 -0
- data/config/metrics/STYLEGUIDE +230 -0
- data/config/metrics/cane.yml +5 -0
- data/config/metrics/churn.yml +6 -0
- data/config/metrics/flay.yml +2 -0
- data/config/metrics/metric_fu.yml +15 -0
- data/config/metrics/reek.yml +1 -0
- data/config/metrics/roodi.yml +24 -0
- data/config/metrics/rubocop.yml +72 -0
- data/config/metrics/saikuro.yml +3 -0
- data/config/metrics/simplecov.yml +6 -0
- data/config/metrics/yardstick.yml +37 -0
- data/lib/assertion.rb +79 -0
- data/lib/assertion/base.rb +186 -0
- data/lib/assertion/exceptions/invalid_error.rb +36 -0
- data/lib/assertion/exceptions/name_error.rb +29 -0
- data/lib/assertion/exceptions/not_implemented_error.rb +29 -0
- data/lib/assertion/inversion.rb +64 -0
- data/lib/assertion/inverter.rb +62 -0
- data/lib/assertion/state.rb +79 -0
- data/lib/assertion/transprocs/i18n.rb +55 -0
- data/lib/assertion/transprocs/inflector.rb +39 -0
- data/lib/assertion/transprocs/list.rb +30 -0
- data/lib/assertion/version.rb +9 -0
- data/spec/integration/assertion_spec.rb +50 -0
- data/spec/integration/en.yml +10 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/unit/assertion/base_spec.rb +221 -0
- data/spec/unit/assertion/exceptions/invalid_error_spec.rb +40 -0
- data/spec/unit/assertion/exceptions/name_error_spec.rb +26 -0
- data/spec/unit/assertion/exceptions/not_implemented_error_spec.rb +26 -0
- data/spec/unit/assertion/inversion_spec.rb +89 -0
- data/spec/unit/assertion/inverter_spec.rb +80 -0
- data/spec/unit/assertion/state_spec.rb +224 -0
- data/spec/unit/assertion/transprocs/i18n/to_scope_spec.rb +19 -0
- data/spec/unit/assertion/transprocs/i18n/translate_spec.rb +28 -0
- data/spec/unit/assertion/transprocs/inflector/to_path_spec.rb +19 -0
- data/spec/unit/assertion/transprocs/inflector/to_snake_path_spec.rb +19 -0
- data/spec/unit/assertion/transprocs/inflector/to_snake_spec.rb +19 -0
- data/spec/unit/assertion/transprocs/list/symbolize_spec.rb +19 -0
- data/spec/unit/assertion_spec.rb +65 -0
- metadata +171 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
folders: # The list of folders to be used by any metric.
|
3
|
+
- lib
|
4
|
+
- app
|
5
|
+
metrics: # The list of allowed metrics. The other metrics are disabled.
|
6
|
+
- cane
|
7
|
+
- churn
|
8
|
+
- flay
|
9
|
+
- flog
|
10
|
+
- reek
|
11
|
+
- roodi
|
12
|
+
- saikuro
|
13
|
+
format: html
|
14
|
+
output: tmp/metric_fu
|
15
|
+
verbose: false
|
@@ -0,0 +1 @@
|
|
1
|
+
---
|
@@ -0,0 +1,24 @@
|
|
1
|
+
---
|
2
|
+
AssignmentInConditionalCheck:
|
3
|
+
CaseMissingElseCheck:
|
4
|
+
ClassLineCountCheck:
|
5
|
+
line_count: 300
|
6
|
+
ClassNameCheck:
|
7
|
+
pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/
|
8
|
+
ClassVariableCheck:
|
9
|
+
CyclomaticComplexityBlockCheck:
|
10
|
+
complexity: 4
|
11
|
+
CyclomaticComplexityMethodCheck:
|
12
|
+
complexity: 8
|
13
|
+
EmptyRescueBodyCheck:
|
14
|
+
ForLoopCheck:
|
15
|
+
MethodLineCountCheck:
|
16
|
+
line_count: 20
|
17
|
+
MethodNameCheck:
|
18
|
+
pattern: !ruby/regexp /^[\||\^|\&|\!]$|^[_a-z<>=\[|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/
|
19
|
+
ModuleLineCountCheck:
|
20
|
+
line_count: 300
|
21
|
+
ModuleNameCheck:
|
22
|
+
pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/
|
23
|
+
ParameterNumberCheck:
|
24
|
+
parameter_count: 5
|
@@ -0,0 +1,72 @@
|
|
1
|
+
---
|
2
|
+
# settings added by the 'hexx-suit' module
|
3
|
+
# output: "tmp/rubocop"
|
4
|
+
# format: "html"
|
5
|
+
|
6
|
+
AllCops:
|
7
|
+
Exclude:
|
8
|
+
- '**/db/schema.rb'
|
9
|
+
|
10
|
+
Lint/HandleExceptions:
|
11
|
+
Exclude:
|
12
|
+
- '**/*_spec.rb'
|
13
|
+
|
14
|
+
Lint/RescueException:
|
15
|
+
Exclude:
|
16
|
+
- '**/*_spec.rb'
|
17
|
+
|
18
|
+
Style/AccessorMethodName:
|
19
|
+
Exclude:
|
20
|
+
- '**/*_spec.rb'
|
21
|
+
|
22
|
+
Style/AsciiComments:
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
Style/ClassAndModuleChildren:
|
26
|
+
Enabled: false
|
27
|
+
|
28
|
+
Style/Documentation:
|
29
|
+
Enabled: false
|
30
|
+
|
31
|
+
Style/EmptyLinesAroundBlockBody:
|
32
|
+
Enabled: false
|
33
|
+
|
34
|
+
Style/EmptyLinesAroundClassBody:
|
35
|
+
Enabled: false
|
36
|
+
|
37
|
+
Style/EmptyLinesAroundMethodBody:
|
38
|
+
Enabled: false
|
39
|
+
|
40
|
+
Style/EmptyLinesAroundModuleBody:
|
41
|
+
Enabled: false
|
42
|
+
|
43
|
+
Style/EmptyLineBetweenDefs:
|
44
|
+
Enabled: false
|
45
|
+
|
46
|
+
Style/FileName:
|
47
|
+
Enabled: false
|
48
|
+
|
49
|
+
Style/RaiseArgs:
|
50
|
+
EnforcedStyle: compact
|
51
|
+
|
52
|
+
Style/SingleLineMethods:
|
53
|
+
Exclude:
|
54
|
+
- '**/*_spec.rb'
|
55
|
+
|
56
|
+
Style/SingleSpaceBeforeFirstArg:
|
57
|
+
Enabled: false
|
58
|
+
|
59
|
+
Style/SpecialGlobalVars:
|
60
|
+
Exclude:
|
61
|
+
- '**/Gemfile'
|
62
|
+
- '**/*.gemspec'
|
63
|
+
|
64
|
+
Style/StringLiterals:
|
65
|
+
EnforcedStyle: double_quotes
|
66
|
+
|
67
|
+
Style/StringLiteralsInInterpolation:
|
68
|
+
EnforcedStyle: double_quotes
|
69
|
+
|
70
|
+
Style/TrivialAccessors:
|
71
|
+
Exclude:
|
72
|
+
- '**/*_spec.rb'
|
@@ -0,0 +1,37 @@
|
|
1
|
+
---
|
2
|
+
# Settings added by the 'hexx-suit' gem
|
3
|
+
output: "tmp/yardstick/output.log"
|
4
|
+
path: "lib/**/*.rb"
|
5
|
+
rules:
|
6
|
+
ApiTag::Presence:
|
7
|
+
enabled: true
|
8
|
+
exclude: []
|
9
|
+
ApiTag::Inclusion:
|
10
|
+
enabled: true
|
11
|
+
exclude: []
|
12
|
+
ApiTag::ProtectedMethod:
|
13
|
+
enabled: true
|
14
|
+
exclude: []
|
15
|
+
ApiTag::PrivateMethod:
|
16
|
+
enabled: false
|
17
|
+
exclude: []
|
18
|
+
ExampleTag:
|
19
|
+
enabled: true
|
20
|
+
exclude: []
|
21
|
+
ReturnTag:
|
22
|
+
enabled: true
|
23
|
+
exclude: []
|
24
|
+
Summary::Presence:
|
25
|
+
enabled: true
|
26
|
+
exclude: []
|
27
|
+
Summary::Length:
|
28
|
+
enabled: true
|
29
|
+
exclude: []
|
30
|
+
Summary::Delimiter:
|
31
|
+
enabled: true
|
32
|
+
exclude: []
|
33
|
+
Summary::SingleLine:
|
34
|
+
enabled: true
|
35
|
+
exclude: []
|
36
|
+
threshold: 100
|
37
|
+
verbose: false
|
data/lib/assertion.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "transproc"
|
4
|
+
require "i18n"
|
5
|
+
|
6
|
+
require_relative "assertion/transprocs/inflector"
|
7
|
+
require_relative "assertion/transprocs/i18n"
|
8
|
+
require_relative "assertion/transprocs/list"
|
9
|
+
|
10
|
+
require_relative "assertion/exceptions/name_error"
|
11
|
+
require_relative "assertion/exceptions/not_implemented_error"
|
12
|
+
require_relative "assertion/exceptions/invalid_error"
|
13
|
+
|
14
|
+
require_relative "assertion/state"
|
15
|
+
require_relative "assertion/base"
|
16
|
+
require_relative "assertion/inversion"
|
17
|
+
require_relative "assertion/inverter"
|
18
|
+
|
19
|
+
# The module allows declaring assertions (assertions) about various objects,
|
20
|
+
# and apply (validate) them to concrete data.
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# # config/locales/en.yml
|
24
|
+
# # ---
|
25
|
+
# # en:
|
26
|
+
# # assertion:
|
27
|
+
# # adult:
|
28
|
+
# # right: "%{name} is an adult (age %{age})"
|
29
|
+
# # wrong: "%{name} is a child (age %{age})"
|
30
|
+
#
|
31
|
+
# Adult = Assertion.about :name, :age do
|
32
|
+
# age >= 18
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# joe = { name: 'Joe', age: 13 }
|
36
|
+
# Adult[joe].validate!
|
37
|
+
# # => #<Assertion::InvalidError @messages=["Joe is a child (age 13)"]>
|
38
|
+
#
|
39
|
+
# jane = { name: 'Jane', age: 22 }
|
40
|
+
# Adult.not[jane].validate!
|
41
|
+
# # => #<Assertion::InvalidError @messages=["Jane is an adult (age 22)"]
|
42
|
+
#
|
43
|
+
# @api public
|
44
|
+
#
|
45
|
+
module Assertion
|
46
|
+
|
47
|
+
# Builds the subclass of `Assertion::Base` with predefined `attributes`
|
48
|
+
# and implementation of the `#check` method.
|
49
|
+
#
|
50
|
+
# @example
|
51
|
+
# IsMan = Assertion.about :age, :gender do
|
52
|
+
# (age >= 18) && (gender == :male)
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# # This is the same as:
|
56
|
+
# class IsMan < Assertion::Base
|
57
|
+
# attribute :age, :gender
|
58
|
+
#
|
59
|
+
# def check
|
60
|
+
# (age >= 18) && (gender == :male)
|
61
|
+
# end
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# @param [Symbol, Array<Symbol>] attributes
|
65
|
+
# The list of attributes for the new assertion
|
66
|
+
# @param [Proc] block
|
67
|
+
# The content for the `check` method
|
68
|
+
#
|
69
|
+
# @return [Assertion::Base]
|
70
|
+
#
|
71
|
+
def self.about(*attributes, &block)
|
72
|
+
klass = Class.new(Base)
|
73
|
+
klass.public_send(:attribute, attributes)
|
74
|
+
klass.__send__(:define_method, :check, &block) if block_given?
|
75
|
+
|
76
|
+
klass
|
77
|
+
end
|
78
|
+
|
79
|
+
end # module Assertion
|
@@ -0,0 +1,186 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Assertion
|
4
|
+
|
5
|
+
# The base class for assertions about some attributes
|
6
|
+
#
|
7
|
+
# Every assertion should define a list of attributes to be checked and
|
8
|
+
# the [#check] method to apply the assertion to those attributes
|
9
|
+
#
|
10
|
+
# The assertion `call` method provides the object, describing the state
|
11
|
+
# of the assertion applied to its attributes. The provided state carries
|
12
|
+
# the result of the checkup and a corresponding <error> message.
|
13
|
+
# Later it can be composed with other states to provide complex validation.
|
14
|
+
#
|
15
|
+
# The class DSL also defines shortcuts:
|
16
|
+
#
|
17
|
+
# * [.[]] can be used to initialize the assertion for given attributes and
|
18
|
+
# then apply it immediately with creation of the corresponding state.
|
19
|
+
# * [.not] can be used to provide the assertion opposite to the initial one.
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# class Adult < Assertion::Base
|
23
|
+
# attribute :name, :age
|
24
|
+
#
|
25
|
+
# def check
|
26
|
+
# age >= 18
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# child = Adult.not
|
31
|
+
#
|
32
|
+
# jane = { name: "Jane", age: 12 }
|
33
|
+
# Adult[jane].valid? # => false
|
34
|
+
# child[jane].valid? # => true
|
35
|
+
#
|
36
|
+
class Base
|
37
|
+
|
38
|
+
# Class DSL
|
39
|
+
#
|
40
|
+
class << self
|
41
|
+
|
42
|
+
# List of attributes, defined for the class
|
43
|
+
#
|
44
|
+
# @return [Array<Symbol>]
|
45
|
+
#
|
46
|
+
def attributes
|
47
|
+
@attributes ||= []
|
48
|
+
end
|
49
|
+
|
50
|
+
# Adds a new attribute or a list of attributes to the class
|
51
|
+
#
|
52
|
+
# @param [Symbol, Array<Symbol>] names
|
53
|
+
#
|
54
|
+
# @return [undefined]
|
55
|
+
#
|
56
|
+
# @raise [Assertion::NameError]
|
57
|
+
# When the name is already used by instance attribute
|
58
|
+
#
|
59
|
+
def attribute(*names)
|
60
|
+
@attributes = List[:symbolize][attributes + names]
|
61
|
+
__check__
|
62
|
+
end
|
63
|
+
|
64
|
+
# Initializes a assertion with some attributes (data) and then calls it
|
65
|
+
#
|
66
|
+
# @param [Hash] hash
|
67
|
+
#
|
68
|
+
# @return [Assertion::State]
|
69
|
+
# The object that describes the state of the assertion
|
70
|
+
# applied to given attributes
|
71
|
+
#
|
72
|
+
def [](hash = {})
|
73
|
+
new(hash).call
|
74
|
+
end
|
75
|
+
|
76
|
+
# Initializes the intermediate inverter with `new` and `[]` methods
|
77
|
+
#
|
78
|
+
# The inverter can be used to initialize the assertion, that describes
|
79
|
+
# just the opposite statement to the current one
|
80
|
+
#
|
81
|
+
# @example
|
82
|
+
# Adult = Assertion.about :name, :age do
|
83
|
+
# age >= 18
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# joe = { name: 'Joe', age: 19 }
|
87
|
+
#
|
88
|
+
# Adult[joe].valid? # => true
|
89
|
+
# Adult.not[joe].valid? # => false
|
90
|
+
#
|
91
|
+
# @return [Assertion::Inverter]
|
92
|
+
#
|
93
|
+
def not
|
94
|
+
Inverter.new(self)
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
# Checks if all the attributes have valid names
|
100
|
+
def __check__
|
101
|
+
wrong = attributes & instance_methods
|
102
|
+
fail(NameError.new wrong) if wrong.any?
|
103
|
+
end
|
104
|
+
|
105
|
+
end # eigenclass
|
106
|
+
|
107
|
+
# @!attribute [r] attributes
|
108
|
+
#
|
109
|
+
# @example
|
110
|
+
# Adult = Assertion.about :name, :age do
|
111
|
+
# age >= 18
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
# adult = Adult[name: "Joe", age: 15, gender: :male]
|
115
|
+
# adult.attributes # => { name: "Joe", age: 15 }
|
116
|
+
#
|
117
|
+
# @return [Hash]
|
118
|
+
# The hash of the allowed attributes having been initialized
|
119
|
+
#
|
120
|
+
attr_reader :attributes
|
121
|
+
|
122
|
+
# @private
|
123
|
+
def initialize(args = {})
|
124
|
+
@attributes = {}
|
125
|
+
self.class.attributes.each { |name| __set_attribute__ name, args[name] }
|
126
|
+
freeze
|
127
|
+
end
|
128
|
+
|
129
|
+
# Returns the translated message about the current state of the assertion
|
130
|
+
# applied to its attributes
|
131
|
+
#
|
132
|
+
# @param [Symbol] state The state to be described
|
133
|
+
#
|
134
|
+
# @return [String] The message
|
135
|
+
#
|
136
|
+
def message(state = nil)
|
137
|
+
I18n[:translate, __scope__, attributes][state ? :right : :wrong]
|
138
|
+
end
|
139
|
+
|
140
|
+
# Checks whether the assertion is right for the current attributes
|
141
|
+
#
|
142
|
+
# @return [Boolean]
|
143
|
+
#
|
144
|
+
# @raise [Check::NotImplementedError]
|
145
|
+
# When the [#check] method hasn't been implemented
|
146
|
+
#
|
147
|
+
def check
|
148
|
+
fail NotImplementedError.new(self.class, :check)
|
149
|
+
end
|
150
|
+
|
151
|
+
# Calls the assertion checkup and returns the state of the assertion having
|
152
|
+
# been applied to the current attributes
|
153
|
+
#
|
154
|
+
# @return [Check::State]
|
155
|
+
# The state of the assertion being applied to its attributes
|
156
|
+
#
|
157
|
+
def call
|
158
|
+
state = check
|
159
|
+
State.new state, message
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
|
164
|
+
# The scope for translating messages using the `I18n` module
|
165
|
+
#
|
166
|
+
# @return [Array<Symbol>]
|
167
|
+
#
|
168
|
+
def __scope__
|
169
|
+
I18n[:scope][self.class.name]
|
170
|
+
end
|
171
|
+
|
172
|
+
# Defines the object attribute and assigns given value to it
|
173
|
+
#
|
174
|
+
# @param [Symbol] name The name of the attribute
|
175
|
+
# @param [Object] value The value of the attribute
|
176
|
+
#
|
177
|
+
# @return [undefined]
|
178
|
+
#
|
179
|
+
def __set_attribute__(name, value)
|
180
|
+
attributes[name] = value
|
181
|
+
singleton_class.__send__(:define_method, name) { value }
|
182
|
+
end
|
183
|
+
|
184
|
+
end # class Base
|
185
|
+
|
186
|
+
end # module Assertion
|