assertion 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|