aspect 0.0.1 → 0.0.2
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/Gemfile +23 -8
- data/README.md +41 -0
- data/Rakefile +53 -7
- data/VERSION +1 -1
- data/aspect.gemspec +12 -15
- data/lib/aspect/has_attributes.rb +31 -22
- data/lib/aspect/has_registry.rb +28 -0
- data/lib/aspect/message_transform.rb +97 -0
- data/lib/aspect/validator.rb +112 -0
- data/lib/aspect/verifier/check.rb +125 -0
- data/lib/aspect/verifier.rb +212 -0
- data/lib/aspect/version.rb +3 -0
- data/lib/aspect.rb +1 -2
- data/spec/coverage/assets/0.10.0/application.css +799 -0
- data/spec/coverage/assets/0.10.0/application.js +1707 -0
- data/spec/coverage/assets/0.10.0/colorbox/border.png +0 -0
- data/spec/coverage/assets/0.10.0/colorbox/controls.png +0 -0
- data/spec/coverage/assets/0.10.0/colorbox/loading.gif +0 -0
- data/spec/coverage/assets/0.10.0/colorbox/loading_background.png +0 -0
- data/spec/coverage/assets/0.10.0/favicon_green.png +0 -0
- data/spec/coverage/assets/0.10.0/favicon_red.png +0 -0
- data/spec/coverage/assets/0.10.0/favicon_yellow.png +0 -0
- data/spec/coverage/assets/0.10.0/loading.gif +0 -0
- data/spec/coverage/assets/0.10.0/magnify.png +0 -0
- data/spec/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/spec/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/spec/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/spec/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/spec/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/spec/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/spec/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/spec/coverage/assets/0.10.0/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/spec/coverage/assets/0.10.0/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/spec/coverage/assets/0.10.0/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/spec/coverage/assets/0.10.0/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/spec/coverage/assets/0.10.0/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/spec/coverage/assets/0.10.0/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/spec/coverage/index.html +72 -0
- data/spec/lib/aspect/has_attributes_spec.rb +247 -67
- data/spec/mutants.txt +12676 -0
- data/spec/spec_helper.rb +1 -0
- metadata +63 -19
@@ -0,0 +1,125 @@
|
|
1
|
+
require "aspect/has_registry" if Kernel.respond_to?(:require)
|
2
|
+
|
3
|
+
module Aspect
|
4
|
+
class Verifier
|
5
|
+
# A single verification check.
|
6
|
+
#
|
7
|
+
# To define a new check, you must first register it, then define an attribute for it on the {Aspect::Verifier} class.
|
8
|
+
#
|
9
|
+
# If the check's block returns a falsey value or raises an error, the check is considered failed.
|
10
|
+
# When a truthy value is return, the check has passed.
|
11
|
+
#
|
12
|
+
# @example Basic usage.
|
13
|
+
# require "aspect/verifier/check"
|
14
|
+
#
|
15
|
+
# check = Aspect::Verifier::Check[:greater_than_or_equal_to]
|
16
|
+
#
|
17
|
+
# loop do
|
18
|
+
# age = gets
|
19
|
+
#
|
20
|
+
# if check.call(age, 18)
|
21
|
+
# puts "Welcome!"
|
22
|
+
#
|
23
|
+
# break
|
24
|
+
# else
|
25
|
+
# puts "Must be 18 or older"
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
# @example Defining a new {Verifier} check.
|
29
|
+
# require "aspect/verifier"
|
30
|
+
#
|
31
|
+
# Aspect::Verifier::Check.register(:length_of_at_least) { |value, expectation| value.length >= expectation }
|
32
|
+
# Aspect::Verifier.attribute(:length_of_at_least) { |value| value.to_i }
|
33
|
+
#
|
34
|
+
# verifier = Aspect::Verifier.new(length_of_at_least: 3)
|
35
|
+
#
|
36
|
+
# verifier.verify("12") # => { length_of_at_least: 3 } # Invalid
|
37
|
+
# verifier.verify("123") # => nil # Valid
|
38
|
+
class Check
|
39
|
+
extend HasRegistry
|
40
|
+
|
41
|
+
class << self
|
42
|
+
# Get a registered `Check` instance by it's name.
|
43
|
+
#
|
44
|
+
# @param [#to_sym] name
|
45
|
+
# @return [Check]
|
46
|
+
def [](name)
|
47
|
+
registry[name.to_sym]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize(&block)
|
52
|
+
self.block = block
|
53
|
+
end
|
54
|
+
|
55
|
+
# Get or set the block for the check.
|
56
|
+
#
|
57
|
+
# @return [Proc]
|
58
|
+
def block(&block)
|
59
|
+
self.block = block if block_given?
|
60
|
+
|
61
|
+
@block
|
62
|
+
end
|
63
|
+
|
64
|
+
# Set the block for the check.
|
65
|
+
#
|
66
|
+
# @param [Proc] value
|
67
|
+
# @return [Proc]
|
68
|
+
def block=(value)
|
69
|
+
if block_given?
|
70
|
+
raise ArgumentError, "block must have 1 block argument" unless value.arity == 1
|
71
|
+
|
72
|
+
@block = value
|
73
|
+
end
|
74
|
+
|
75
|
+
@block
|
76
|
+
end
|
77
|
+
|
78
|
+
# Call the block.
|
79
|
+
#
|
80
|
+
# @param [Object] value
|
81
|
+
# @return [Boolean]
|
82
|
+
def call(value, expectation)
|
83
|
+
!!@block.call(value, expectation) rescue false
|
84
|
+
end
|
85
|
+
|
86
|
+
register(:equal_to) do |value, expectation|
|
87
|
+
value == expectation
|
88
|
+
end
|
89
|
+
|
90
|
+
register(:presence) do |value|
|
91
|
+
value = value.is_a?(String) ? value.gsub(/\s+/, "") : value
|
92
|
+
|
93
|
+
value.nil? || value.respond_to?(:empty?) && value.empty?
|
94
|
+
end
|
95
|
+
|
96
|
+
register(:integer) { |value| Kernel.Integer(value.to_s) }
|
97
|
+
|
98
|
+
register(:float) { |value| Kernel.Float(value.to_s) }
|
99
|
+
|
100
|
+
register(:greater_than) do |value, expectation|
|
101
|
+
value = Kernel.Float(value.to_s)
|
102
|
+
|
103
|
+
value > expectation
|
104
|
+
end
|
105
|
+
|
106
|
+
register(:greater_than_or_equal_to) do |value, expectation|
|
107
|
+
value = Kernel.Float(value.to_s)
|
108
|
+
|
109
|
+
value >= expectation
|
110
|
+
end
|
111
|
+
|
112
|
+
register(:less_than) do |value, expectation|
|
113
|
+
value = Kernel.Float(value.to_s)
|
114
|
+
|
115
|
+
value < expectation
|
116
|
+
end
|
117
|
+
|
118
|
+
register(:less_than_or_equal_to) do |value, expectation|
|
119
|
+
value = Kernel.Float(value.to_s)
|
120
|
+
|
121
|
+
value <= expectation
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,212 @@
|
|
1
|
+
if Kernel.respond_to?(:require)
|
2
|
+
require "aspect/has_attributes"
|
3
|
+
require "aspect/verifier/check"
|
4
|
+
end
|
5
|
+
|
6
|
+
module Aspect
|
7
|
+
# Verify a set of checks on an object.
|
8
|
+
#
|
9
|
+
# The result is a `Hash` where the keys are the check name and the value is the expectation or `nil` when
|
10
|
+
# the object is verified to be valid.
|
11
|
+
#
|
12
|
+
# This allows for it to easily be transformed into messages using {Aspect::MessageTransform}, if needed.
|
13
|
+
#
|
14
|
+
# @example Simple boolean checks.
|
15
|
+
# require "aspect/verifier"
|
16
|
+
#
|
17
|
+
# verifier = Aspect::Verifier.new(:presence, :integer)
|
18
|
+
#
|
19
|
+
# verifier.verify(nil) # => { presence: true, integer: true } # Invalid
|
20
|
+
# verifier.verify("") # => { presence: true, integer: true } # Invalid
|
21
|
+
# verifier.verify("hello") # => { integer: true } # Invalid
|
22
|
+
# verifier.verify("24") # => nil # Valid
|
23
|
+
# @example Simple checks with arguments.
|
24
|
+
# require "aspect/verifier"
|
25
|
+
#
|
26
|
+
# verifier = Aspect::Verifier.new(integer: true, greater_than: 10)
|
27
|
+
#
|
28
|
+
# verifier.verify(10) # => { greater_than: 10 } # Invalid
|
29
|
+
# verifier.verify("hello") # => { integer: true, greater_than: 10 } # Invalid
|
30
|
+
# verifier.verify("24") # => nil # Valid
|
31
|
+
# @example Break on fail.
|
32
|
+
# require "aspect/verifier"
|
33
|
+
#
|
34
|
+
# verifier = Aspect::Verifier.new(integer: true, greater_than: 10)
|
35
|
+
#
|
36
|
+
# verifier.verify("hello") # => { integer: true, greater_than: 10 }
|
37
|
+
# verifier.verify("hello", break_on_fail: true) # => { integer: true }
|
38
|
+
# @example Fake application usage example
|
39
|
+
# require "aspect/verifier"
|
40
|
+
#
|
41
|
+
# age_verifier = Aspect::Verifier.new(:integer, greater_than_or_equal_to: 18)
|
42
|
+
#
|
43
|
+
# loop do
|
44
|
+
# print "age? "
|
45
|
+
#
|
46
|
+
# input = gets
|
47
|
+
# error = age_verifier.verify(input, break_on_fail: true)
|
48
|
+
#
|
49
|
+
# if error
|
50
|
+
# puts " Must give a number." if error[:integer]
|
51
|
+
# puts " Must be #{error[:greater_than_or_equal_to]} or older." if error[:greater_than_or_equal_to]
|
52
|
+
# else
|
53
|
+
# puts " Welcome, appropriately-aged user!"
|
54
|
+
#
|
55
|
+
# break
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# # Command line:
|
60
|
+
# # age? foobar
|
61
|
+
# # Must give a number.
|
62
|
+
# # age? 17
|
63
|
+
# # Must be 18 or older.
|
64
|
+
# # age? 25
|
65
|
+
# # Welcome, appropriately-aged user!
|
66
|
+
# @example Fake application usage example with {Aspect::MessageTransform}
|
67
|
+
# require "aspect/verifier"
|
68
|
+
# require "aspect/message_transform"
|
69
|
+
#
|
70
|
+
# age_verifier = Aspect::Verifier.new(:integer, greater_than_or_equal_to: 18)
|
71
|
+
# message = Aspect::MessageTransform.new(
|
72
|
+
# welcome: "Welcome, appropriately-aged user!",
|
73
|
+
# error: {
|
74
|
+
# integer: "Must give a number.",
|
75
|
+
# greater_than_or_equal_to: "Must be %{greater_than_or_equal_to} or older."
|
76
|
+
# }
|
77
|
+
# )
|
78
|
+
#
|
79
|
+
# loop do
|
80
|
+
# print "age? "
|
81
|
+
#
|
82
|
+
# input = gets
|
83
|
+
# error = age_verifier.verify(input, break_on_fail: true)
|
84
|
+
#
|
85
|
+
# print " "
|
86
|
+
# if error
|
87
|
+
# puts message.to_s(error: error)
|
88
|
+
# else
|
89
|
+
# puts message.to_s(:welcome)
|
90
|
+
#
|
91
|
+
# break
|
92
|
+
# end
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# # Command line:
|
96
|
+
# # age? foobar
|
97
|
+
# # Must give a number.
|
98
|
+
# # age? 17
|
99
|
+
# # Must be 18 or older.
|
100
|
+
# # age? 25
|
101
|
+
# # Welcome, appropriately-aged user!
|
102
|
+
class Verifier
|
103
|
+
include HasAttributes
|
104
|
+
|
105
|
+
# @example Multiple flags, for boolean checks.
|
106
|
+
# Aspect::Verifier.new(:presence, :integer)
|
107
|
+
# @example `Array` of flags, for boolean checks.
|
108
|
+
# Aspect::Verifier.new([:presence, :integer])
|
109
|
+
# @example Passing a `Hash`, for non-boolean checks.
|
110
|
+
# Aspect::Verifier.new(presence: true, integer: true, greater_than: 10)
|
111
|
+
# @example Mixing both boolean and non-boolean checks.
|
112
|
+
# Aspect::Verifier.new(:presence, :integer, greater_than: 10)
|
113
|
+
def initialize(*arguments)
|
114
|
+
attributes = {}
|
115
|
+
|
116
|
+
if arguments.length == 1 && arguments[0].respond_to?(:to_h)
|
117
|
+
attributes = arguments[0]
|
118
|
+
else
|
119
|
+
arguments = arguments[0] if arguments.length == 1 && arguments[0].is_a?(Array)
|
120
|
+
|
121
|
+
arguments.collect(&:to_sym).each { |check| attributes[check] = true }
|
122
|
+
end
|
123
|
+
|
124
|
+
update_attributes(attributes)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Validate a value.
|
128
|
+
#
|
129
|
+
# @param value
|
130
|
+
# @param [#to_h] options
|
131
|
+
# @option options [Boolean] :break_on_fail Set to `true` to stop running checks as soon as one fails.
|
132
|
+
# @return [<Symbol, Object>] The result is a `Hash` where the keys are the check name and the value is the expectation or `nil` when the object is verified to be valid.
|
133
|
+
def validate(value, options={})
|
134
|
+
options = { break_on_fail: false }.merge(options.to_h)
|
135
|
+
errors = {}
|
136
|
+
|
137
|
+
Check.registry.each do |check_name, check|
|
138
|
+
check_iv = instance_variable_get("@#{check_name}")
|
139
|
+
|
140
|
+
errors[check_name] = check_iv if check_iv && !check.call(value, check_iv)
|
141
|
+
|
142
|
+
break if options[:break_on_fail]
|
143
|
+
end
|
144
|
+
|
145
|
+
errors
|
146
|
+
end
|
147
|
+
|
148
|
+
# @method presence=
|
149
|
+
# Set whether to check for presence.
|
150
|
+
#
|
151
|
+
# Objects that are `nil`, or respond to `empty?` and return `true`, are considered blank.
|
152
|
+
#
|
153
|
+
# @param [Boolean] value
|
154
|
+
# @return [Boolean]
|
155
|
+
|
156
|
+
# @method presence?
|
157
|
+
# Get whether to check for presence.
|
158
|
+
#
|
159
|
+
# @return [Boolean]
|
160
|
+
attribute(:presence, query: true)
|
161
|
+
|
162
|
+
# @method integer=
|
163
|
+
# Set whether to check for `Integer`.
|
164
|
+
#
|
165
|
+
# The values `123` and `"123"` will both pass, but `123.4` and `"123.4"` will both fail.
|
166
|
+
#
|
167
|
+
# @param [Boolean] value
|
168
|
+
# @return [Boolean]
|
169
|
+
|
170
|
+
# @method integer?
|
171
|
+
# Get whether to check for `Integer`.
|
172
|
+
#
|
173
|
+
# @return [Boolean]
|
174
|
+
attribute(:integer, query: true)
|
175
|
+
|
176
|
+
# @method float=
|
177
|
+
# Set whether to check for `Float`.
|
178
|
+
#
|
179
|
+
# The values `123.4` and `"123.4"` will both pass.
|
180
|
+
#
|
181
|
+
# @param [Boolean] value
|
182
|
+
# @return [Boolean]
|
183
|
+
|
184
|
+
# @method float?
|
185
|
+
# Get whether to check for `Float`.
|
186
|
+
#
|
187
|
+
# @return [Boolean]
|
188
|
+
attribute(:float, query: true)
|
189
|
+
|
190
|
+
# @method equal_to=
|
191
|
+
# Set the object to check equality against.
|
192
|
+
#
|
193
|
+
# @param [nil, Object] value
|
194
|
+
# @return [nil, Object]
|
195
|
+
|
196
|
+
# @method equal_to
|
197
|
+
# Get whether to for equality against a given object.
|
198
|
+
#
|
199
|
+
# @return [Boolean]
|
200
|
+
attribute(:equal_to)
|
201
|
+
|
202
|
+
attribute(:greater_than) { |value| value.to_f }
|
203
|
+
|
204
|
+
attribute(:greater_than_or_equal_to) { |value| value.to_f }
|
205
|
+
|
206
|
+
attribute(:less_than) { |value| value.to_f }
|
207
|
+
|
208
|
+
attribute(:less_than_or_equal_to) { |value| value.to_f }
|
209
|
+
|
210
|
+
# TODO: documentation for equal_to, greater_than, greater_than_or_equal_to, less_than, less_than_or_equal_to
|
211
|
+
end
|
212
|
+
end
|