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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +23 -8
  3. data/README.md +41 -0
  4. data/Rakefile +53 -7
  5. data/VERSION +1 -1
  6. data/aspect.gemspec +12 -15
  7. data/lib/aspect/has_attributes.rb +31 -22
  8. data/lib/aspect/has_registry.rb +28 -0
  9. data/lib/aspect/message_transform.rb +97 -0
  10. data/lib/aspect/validator.rb +112 -0
  11. data/lib/aspect/verifier/check.rb +125 -0
  12. data/lib/aspect/verifier.rb +212 -0
  13. data/lib/aspect/version.rb +3 -0
  14. data/lib/aspect.rb +1 -2
  15. data/spec/coverage/assets/0.10.0/application.css +799 -0
  16. data/spec/coverage/assets/0.10.0/application.js +1707 -0
  17. data/spec/coverage/assets/0.10.0/colorbox/border.png +0 -0
  18. data/spec/coverage/assets/0.10.0/colorbox/controls.png +0 -0
  19. data/spec/coverage/assets/0.10.0/colorbox/loading.gif +0 -0
  20. data/spec/coverage/assets/0.10.0/colorbox/loading_background.png +0 -0
  21. data/spec/coverage/assets/0.10.0/favicon_green.png +0 -0
  22. data/spec/coverage/assets/0.10.0/favicon_red.png +0 -0
  23. data/spec/coverage/assets/0.10.0/favicon_yellow.png +0 -0
  24. data/spec/coverage/assets/0.10.0/loading.gif +0 -0
  25. data/spec/coverage/assets/0.10.0/magnify.png +0 -0
  26. data/spec/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  27. data/spec/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  28. data/spec/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  29. data/spec/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  30. data/spec/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  31. data/spec/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  32. data/spec/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  33. data/spec/coverage/assets/0.10.0/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  34. data/spec/coverage/assets/0.10.0/smoothness/images/ui-icons_222222_256x240.png +0 -0
  35. data/spec/coverage/assets/0.10.0/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  36. data/spec/coverage/assets/0.10.0/smoothness/images/ui-icons_454545_256x240.png +0 -0
  37. data/spec/coverage/assets/0.10.0/smoothness/images/ui-icons_888888_256x240.png +0 -0
  38. data/spec/coverage/assets/0.10.0/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  39. data/spec/coverage/index.html +72 -0
  40. data/spec/lib/aspect/has_attributes_spec.rb +247 -67
  41. data/spec/mutants.txt +12676 -0
  42. data/spec/spec_helper.rb +1 -0
  43. 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
@@ -0,0 +1,3 @@
1
+ module Aspect
2
+ VERSION = "0.0.1".freeze
3
+ end
data/lib/aspect.rb CHANGED
@@ -1,8 +1,7 @@
1
- require "version"
1
+ require "aspect/version"
2
2
 
3
3
  # A small collection of useful mixins for plain old Ruby objects.
4
4
  module Aspect
5
- is_versioned
6
5
  end
7
6
 
8
7
  require "aspect/has_attributes"