aspect 0.0.1 → 0.0.2

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