ae 1.6.1 → 1.7.0

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.
data/lib/ae/assert.rb CHANGED
@@ -2,8 +2,10 @@ require 'ae/assertor'
2
2
 
3
3
  module AE
4
4
 
5
- # = Assert
5
+ # The Assert module is simple a conatiner module for the core
6
+ # extension methods: #assert, #expect, etc.
6
7
  #
8
+ # This module is included directory into the Object class.
7
9
  module Assert
8
10
 
9
11
  # Assert a operational relationship.
@@ -31,30 +33,35 @@ module AE
31
33
  Assertor.new(self, :backtrace=>caller).assert == cmp
32
34
  end
33
35
 
34
- # Assert not an operational relationship.
35
- # Read it as "assert not".
36
+ # Opposite of assert.
36
37
  #
37
38
  # 4.refute == 4 #=> Assertion Error
38
39
  #
39
- # See #assert.
40
40
  def refute(*args, &block)
41
41
  Assertor.new(self, :backtrace=>caller).not.assert(*args, &block)
42
42
  end
43
43
 
44
- # Same as 'object.assert == other'.
44
+ # Same as 'object.refute == other'.
45
45
  def refute=(cmp)
46
46
  Assertor.new(self, :backtrace=>caller).not.assert == cmp
47
47
  end
48
48
 
49
- # Alias for #assert!.
49
+ # Alias for #refute. Read it as "assert not".
50
50
  #
51
51
  # 4.assert! == 4
52
52
  #
53
53
  # NOTE: This method would not be necessary if Ruby would allow
54
54
  # +!=+ to be define as a method, or at least +!+ as a unary method.
55
- # This may be possible in Ruby 1.9.
55
+ # Looks like this is possible in Ruby 1.9, but we will wait until
56
+ # Ruby 1.9 is the norm.
56
57
  alias_method :assert!, :refute
57
-
58
+
59
+ # Directly raise an Assertion failure.
60
+ def flunk(message=nil, backtrace=nil)
61
+ #Assertor.new(self, :backtrace=>caller).assert(false, message)
62
+ Assertor.assert(false, message, caller)
63
+ end
64
+
58
65
  end
59
66
 
60
67
  end
data/lib/ae/assertion.rb CHANGED
@@ -1,77 +1,53 @@
1
- require 'ae/core_ext'
1
+ # Copyright (c) 2008,2010 Thomas Sawyer
2
2
 
3
- # = Assertion
4
- #
5
- # "The reserve of modern assertions is sometimes pushed to extremes,
6
- # in which the fear of being contradicted leads the writer to strip
7
- # himself of almost all sense and meaning."
8
- # -- Sir Winston Churchill (1874 - 1965)
9
- #
10
- # This is the underlying Exception class of the whole system.
11
- #
12
- class Assertion < Exception
3
+ require 'ae/core_ext'
13
4
 
14
- # TODO: This doesn't seem to cut it anymore!
15
- @count = 0
16
- @fails = 0
5
+ module AE
17
6
 
18
- class << self
19
- attr_accessor :count
20
- attr_accessor :fails
7
+ # The Assertion class is simply a subclass of Exception that is used
8
+ # by AE as the default error raised when an assertion fails.
9
+ #
10
+ # "The reserve of modern assertions is sometimes pushed to extremes,
11
+ # in which the fear of being contradicted leads the writer to strip
12
+ # himself of almost all sense and meaning."
13
+ # -- Sir Winston Churchill (1874 - 1965)
14
+ #
15
+ #
16
+ class Assertion < Exception
21
17
 
22
- #
23
- def test(test, options={})
24
- if test
25
- increment(true)
26
- else
27
- framework_flunk(options)
28
- end
29
- test
18
+ # DEPRECATE: This will be removed in favor of Assertor#counts.
19
+ def self.counts
20
+ AE::Assertor.counts
30
21
  end
31
22
 
23
+ # New assertion (failure).
32
24
  #
33
- #def self.framework_assert(options={})
34
- #end
35
-
36
- # This method can be replaced to support alternate frameworks.
37
- # The intent of the methods is to raise the assertion failure
38
- # class used.
39
- def framework_flunk(options={})
40
- message = options.delete(:message)
41
- fail ::Assertion.new(message, options)
25
+ # message - the failure message
26
+ # options - such as :backtrace
27
+ #
28
+ def initialize(message=nil, options={})
29
+ super(message)
30
+ backtrace = options[:backtrace]
31
+ set_backtrace(backtrace) if backtrace
32
+ set_assertion(true)
42
33
  end
43
34
 
44
- # Increment assertion counts. If +pass+ is true then only +@count+
45
- # is increased. If +pass+ if false then both +@count+ and +@fails+
46
- # are incremented.
47
- def increment(pass)
48
- recount unless instance_variable_defined?('@count') # TODO: Come on, there has to be a better way!
49
- @count += 1
50
- @fails += 1 unless pass
35
+ # Technically any object that affirmatively responds to #assertion?
36
+ # can be taken to be an Assertion. This makes it easier for various
37
+ # libraries to work together without having to depend upon a common
38
+ # Assertion base class.
39
+ def assertion?
40
+ true
51
41
  end
52
42
 
53
- # Reset counts.
54
- def recount
55
- f, c = @fails, @count
56
- @count = 0
57
- @fails = 0
58
- return f, c
43
+ #
44
+ def to_s
45
+ '(assertion) ' + super
59
46
  end
60
- end
61
47
 
62
- #
63
- def initialize(message=nil, options={})
64
- super(message)
65
- backtrace = options[:backtrace]
66
- set_backtrace(backtrace) if backtrace
67
- self.class.increment(false)
68
- end
69
-
70
- #
71
- def to_s
72
- 'fail ' + super
73
48
  end
74
49
 
75
50
  end
76
51
 
77
- # Copyright (c) 2008, 2010 Thomas Sawyer
52
+ # Set top-level Assertion to AE::Assertion
53
+ Assertion = AE::Assertion
data/lib/ae/assertor.rb CHANGED
@@ -1,215 +1,287 @@
1
1
  require 'ae/assertion'
2
2
  require 'ae/basic_object'
3
3
 
4
- # = Assertor (Assertion Functor)
5
- #
6
- # == What is a Functor?
7
- #
8
- # A Functor is a succinct name for what is also know as a
9
- # Higher Order Function. In other words, it is a function
10
- # that acts on a function. It is very similiar to a delegator
11
- # in most respects, but is conditioned on the operation applied,
12
- # rather then simply passing-off to an alternate reciever.
13
- #
14
- class Assertor < AE::BasicObject
4
+ module AE
15
5
 
6
+ # Assertor is the underlying class of the whole system. It implements
7
+ # the flutent assertion notation.
16
8
  #
17
- #instance_methods.each{ |m| protected m unless /^(__|object_id$)/ =~ m.to_s }
9
+ # An Assertor is an Assertion Functor. A Functor is a succinct name for what
10
+ # is also known as Higher Order Function. In other words, it is a function
11
+ # that acts on a function. It is very similiar to a delegator in most
12
+ # respects, but is conditioned on the operation applied, rather then simply
13
+ # passing-off to an alternate reciever.
14
+ #
15
+ class Assertor < AE::BasicObject
18
16
 
19
- if ::RUBY_VERSION >= '1.9'
20
- eval "private :==, :!, :!=" # using eval here b/c it's a syntax error in 1.8-
21
- end
17
+ # Initial settings of assertion counts.
18
+ ZERO_COUNTS = {:total=>0,:pass=>0,:fail=>0}
22
19
 
23
- # New Assertor.
24
- #
25
- def initialize(delegate, opts={}) #, backtrace)
26
- @delegate = delegate
27
- @message = opts[:message]
28
- @backtrace = opts[:backtrace] || caller #[1..-1]
29
- @negated = !!opts[:negated]
30
- end
20
+ # Initialize assertion counts global variable.
21
+ $assertion_counts = ZERO_COUNTS.dup
31
22
 
32
- # Negate the meaning of the assertion.
33
- #
34
- # TODO: Should this return a new Assertor instead of inplace negation?
35
- def not(msg=nil)
36
- @negated = !@negated
37
- @message = msg if msg
38
- self
39
- end
23
+ # Returns Hash used to track assertion counts.
24
+ def self.counts
25
+ $assertion_counts
26
+ end
40
27
 
41
- # Internal assert, provides all functionality associated
42
- # with external #assert method. (See Assert#assert)
43
- #
44
- # NOTE: I'm calling YAGNI on using extra arguments to pass
45
- # to the block. The interface is much nicer if a macro is
46
- # created to handle any neccessry arguments. Eg.
47
- #
48
- # assert something(parameter)
49
- #
50
- # instead of
51
- #
52
- # assert something, parameter
53
- #
54
- def assert(*args, &block)
55
- return self if args.empty? && !block
56
-
57
- target = block || args.shift
58
-
59
- if ::Proc === target || target.respond_to?(:to_proc)
60
- block = target.to_proc
61
- match = args.shift
62
- result = block.arity > 0 ? block.call(@delegate) : block.call
63
- if match
64
- pass = (match == result)
65
- msg = @message || "#{match.inspect} == #{result.inspect}"
28
+ # Reset assertion counts.
29
+ #
30
+ # reset - Hash which will be used to set counts manually (optional).
31
+ #
32
+ # Returns the Hash of previous counts.
33
+ def self.recount(reset={})
34
+ old_counts = counts.dup
35
+ if reset.empty?
36
+ counts.replace(ZERO_COUNTS.dup)
66
37
  else
67
- pass = result
68
- msg = @message || block.inspect # "#{result.inspect}"
38
+ reset.each do |type, value|
39
+ counts[type.to_sym] = value
40
+ end
69
41
  end
70
- elsif target.respond_to?(:matches?)
71
- pass = target.matches?(@delegate)
72
- msg = @message || matcher_message(target) || target.inspect
73
- else
74
- pass = target # truthiness
75
- msg = args.shift # optional mesage for TestUnit compatiability
42
+ return old_counts
76
43
  end
77
44
 
78
- __assert__(pass, msg)
79
- end
45
+ # Increment assertion counts. If +pass+ is +true+ then +:total+
46
+ # and +:pass+ are increased. If +pass+ if +false+ then +:total+
47
+ # and +:fail+ are incremented.
48
+ def self.increment_counts(pass)
49
+ counts[:total] += 1
50
+ if pass
51
+ counts[:pass] += 1
52
+ else
53
+ counts[:fail] += 1
54
+ end
55
+ return counts
56
+ end
80
57
 
81
- # Internal expect, provides all functionality associated
82
- # with external #expect method. (See Expect#expect)
83
- #
84
- #--
85
- # TODO: Should we deprecate the receiver matches in favor of #expected ?
86
- # In other words, should the <code>|| @delegate</code> be dropped?
87
- #++
88
- def expect(*args, &block)
89
- return self if args.empty? && !block # same as #assert
90
-
91
- target = block || args.shift
92
-
93
- if ::Proc === target #|| target.respond_to?(:to_proc)
94
- #block = target.to_proc
95
- match = args.shift || @delegate
96
- if exception?(match)
97
- $DEBUG, debug = false, $DEBUG # b/c it always spits-out a NameError
98
- begin
99
- block.arity > 0 ? block.call(@delegate) : block.call
100
- pass = false
101
- msg = "#{match} not raised"
102
- rescue match => error
103
- pass = true
104
- msg = "#{match} raised"
105
- rescue ::Exception => error
106
- pass = false
107
- msg = "#{match} expected but #{error.class} was raised"
108
- ensure
109
- $DEBUG = debug
58
+ # Basic assertion. This method by-passes all the Assertor fluent
59
+ # constructs and performs the underlying assertion procedure. It
60
+ # is used by Assertor as the end result of an assertion.
61
+ def self.assert(pass, message=nil, backtrace=nil)
62
+ increment_counts(pass)
63
+ if !pass
64
+ backtrace = backtrace || caller
65
+ message = message || 'flunk'
66
+ raise_assertion(message, backtrace)
67
+ end
68
+ return pass
69
+ end
70
+
71
+ # This method can be replaced to support alternate frameworks.
72
+ # The intent of the method is to raise the assertion failure
73
+ # class that the framework uses.
74
+ def self.raise_assertion(message, backtrace=nil)
75
+ backtrace = backtrace || caller
76
+
77
+ error = assertion_error.new(message)
78
+ error.set_backtrace(backtrace)
79
+ error.set_assertion(true)
80
+ fail error
81
+ end
82
+
83
+ # Returns the Exception class to be raised when an assertion fails.
84
+ def self.assertion_error
85
+ ::Assertion
86
+ end
87
+
88
+ #
89
+ if ::RUBY_VERSION >= '1.9'
90
+ eval "private :==, :!, :!=" # using eval here b/c it's a syntax error in 1.8-
91
+ end
92
+
93
+ # New Assertor.
94
+ #
95
+ def initialize(delegate, opts={}) #, backtrace)
96
+ @delegate = delegate
97
+ @message = opts[:message]
98
+ @backtrace = opts[:backtrace] || caller #[1..-1]
99
+ @negated = !!opts[:negated]
100
+ end
101
+
102
+ # Negate the meaning of the assertion.
103
+ #
104
+ #--
105
+ # TODO: Should this return a new Assertor instead of in place negation?
106
+ #++
107
+ def not(msg=nil)
108
+ @negated = !@negated
109
+ @message = msg if msg
110
+ self
111
+ end
112
+
113
+ # Internal assert, provides all functionality associated
114
+ # with external #assert method. (See Assert#assert)
115
+ #
116
+ # NOTE: I'm calling YAGNI on using extra arguments to pass
117
+ # to the block. The interface is much nicer if a macro is
118
+ # created to handle any neccessry arguments. Eg.
119
+ #
120
+ # assert something(parameter)
121
+ #
122
+ # instead of
123
+ #
124
+ # assert something, parameter
125
+ #
126
+ # Returns +true+ or +false+ based on assertions success.
127
+ def assert(*args, &block)
128
+ return self if args.empty? && !block
129
+
130
+ target = block || args.shift
131
+
132
+ if ::Proc === target || target.respond_to?(:to_proc)
133
+ block = target.to_proc
134
+ match = args.shift
135
+ result = block.arity > 0 ? block.call(@delegate) : block.call
136
+ if match
137
+ pass = (match == result)
138
+ msg = @message || "#{match.inspect} == #{result.inspect}"
139
+ else
140
+ pass = result
141
+ msg = @message || block.inspect # "#{result.inspect}"
110
142
  end
143
+ elsif target.respond_to?(:matches?)
144
+ pass = target.matches?(@delegate)
145
+ msg = @message || matcher_message(target) || target.inspect
111
146
  else
112
- result = block.arity > 0 ? block.call(@delegte) : block.call
113
- pass = (match === result)
114
- msg = @message || "#{match.inspect} === #{result.inspect}"
147
+ pass = target # truthiness
148
+ msg = args.shift # optional mesage for TestUnit compatiability
115
149
  end
116
- elsif target.respond_to?(:matches?)
117
- pass = target.matches?(@delegate)
118
- msg = @message || matcher_message(target) || target.inspect
119
- else
120
- pass = (target === @delegate)
121
- msg = @message || "#{target.inspect} === #{@delegate.inspect}"
150
+
151
+ __assert__(pass, msg)
122
152
  end
123
153
 
124
- __assert__(pass, msg)
125
- end
154
+ # Internal expect, provides all functionality associated
155
+ # with external #expect method. (See Expect#expect)
156
+ #
157
+ #--
158
+ # TODO: Should we deprecate the receiver matches in favor of #expected ?
159
+ # In other words, should the <code>|| @delegate</code> be dropped?
160
+ #++
161
+ def expect(*args, &block)
162
+ return self if args.empty? && !block # same as #assert
126
163
 
127
- # Is the +object+ and Exception or an instance of one.
128
- #--
129
- # TODO: Should we use a more libreral determination of exception.
130
- # e.g. <code>respond_to?(:exception)</code>.
131
- #++
132
- def exception?(object)
133
- ::Exception === object or ::Class === object and object.ancestors.include?(::Exception)
134
- end
164
+ target = block || args.shift
135
165
 
136
- #
137
- def flunk(message=nil)
138
- __assert__(false, message || @message)
139
- end
166
+ if ::Proc === target #|| target.respond_to?(:to_proc)
167
+ #block = target.to_proc
168
+ match = args.shift || @delegate
169
+ if exception?(match)
170
+ $DEBUG, debug = false, $DEBUG # b/c it always spits-out a NameError
171
+ begin
172
+ block.arity > 0 ? block.call(@delegate) : block.call
173
+ pass = false
174
+ msg = "#{match} not raised"
175
+ rescue match => error
176
+ pass = true
177
+ msg = "#{match} raised"
178
+ rescue ::Exception => error
179
+ pass = false
180
+ msg = "#{match} expected but #{error.class} was raised"
181
+ ensure
182
+ $DEBUG = debug
183
+ end
184
+ else
185
+ result = block.arity > 0 ? block.call(@delegte) : block.call
186
+ pass = (match === result)
187
+ msg = @message || "#{match.inspect} === #{result.inspect}"
188
+ end
189
+ elsif target.respond_to?(:matches?)
190
+ pass = target.matches?(@delegate)
191
+ msg = @message || matcher_message(target) || target.inspect
192
+ else
193
+ pass = (target === @delegate)
194
+ msg = @message || "#{target.inspect} === #{@delegate.inspect}"
195
+ end
140
196
 
141
- # Ruby seems to have a quark in it's implementation whereby
142
- # this must be defined explicitly, otherwise it somehow
143
- # skips #method_missing.
144
- def =~(match)
145
- method_missing(:"=~", match)
146
- end
197
+ __assert__(pass, msg)
198
+ end
147
199
 
148
- #
149
- def send(op, *a, &b)
150
- method_missing(op, *a, &b)
151
- end
200
+ #
201
+ def flunk(message=nil, backtrace=nil)
202
+ __assert__(false, message || @message)
203
+ end
152
204
 
153
- private
205
+ # Ruby seems to have a quark in it's implementation whereby
206
+ # this must be defined explicitly, otherwise it somehow
207
+ # skips #method_missing.
208
+ def =~(match)
209
+ method_missing(:"=~", match)
210
+ end
154
211
 
155
- # Converts a missing methods into an Assertion.
156
- #
157
- def method_missing(sym, *a, &b)
158
- pass = @delegate.__send__(sym, *a, &b)
159
- #pass = @delegate.public_send(sym, *a, &b)
160
- __assert__(pass, @message || __msg__(sym, *a, &b))
161
- end
212
+ #
213
+ def send(op, *a, &b)
214
+ method_missing(op, *a, &b)
215
+ end
162
216
 
163
- # Puts together a suitable error message.
164
- #
165
- def __msg__(m, *a, &b)
166
- inspection = @delegate.send(:inspect)
167
- if @negated
168
- "! #{inspection} #{m} #{a.collect{|x| x.inspect}.join(',')}"
169
- else
170
- "#{inspection} #{m} #{a.collect{|x| x.inspect}.join(',')}"
171
- end
172
- #self.class.message(m)[@delegate, *a] )
173
- end
217
+ #
218
+ def inspect
219
+ @delegate.inspect
220
+ end
174
221
 
175
- # Pure old simple assert.
176
- #--
177
- # TODO: Can the handling of the message be simplified/improved?
178
- #++
179
- def __assert__(pass, message=nil)
180
- pass = @negated ^ pass
181
- # msg = message || @message
182
- ::Assertion.test(pass, :message=>message, :backtrace=>@backtrace)
183
- return pass
184
- end
222
+ private
185
223
 
186
- # This method can be replaced to support alternate frameworks.
187
- # The idea is to use to record that an assertion took place.
188
- # def framework_assert(pass, message)
189
- # # by default nothing needed
190
- # end
224
+ # Is the +object+ an Exception or an instance of one?
225
+ #--
226
+ # TODO: Should we use a more libreral determination of exception.
227
+ # e.g. <code>respond_to?(:exception)</code>.
228
+ #++
229
+ def exception?(object)
230
+ ::Exception === object or ::Class === object and object.ancestors.include?(::Exception)
231
+ end
191
232
 
192
- #
193
- def matcher_message(matcher)
194
- if @negated
195
- if matcher.respond_to?(:negative_failure_message)
196
- return matcher.failure_message
233
+ # Converts a missing method into an Assertion.
234
+ #
235
+ # TODO: In future should probably be `@delegate.public_send(sym, *a, &b)`.
236
+ def method_missing(sym, *a, &b)
237
+ pass = @delegate.__send__(sym, *a, &b)
238
+ __assert__(pass, @message || __msg__(sym, *a, &b))
239
+ end
240
+
241
+ # Puts together a suitable error message.
242
+ #
243
+ def __msg__(m, *a, &b)
244
+ inspection = @delegate.send(:inspect)
245
+ if @negated
246
+ "! #{inspection} #{m} #{a.collect{|x| x.inspect}.join(',')}"
247
+ else
248
+ "#{inspection} #{m} #{a.collect{|x| x.inspect}.join(',')}"
197
249
  end
250
+ #self.class.message(m)[@delegate, *a] )
198
251
  end
199
- if matcher.respond_to?(:failure_message)
200
- return matcher.failure_message
252
+
253
+ # Simple assert.
254
+ #--
255
+ # TODO: Can the handling of the message be simplified/improved?
256
+ #++
257
+ def __assert__(pass, message=nil)
258
+ pass = @negated ^ pass
259
+ Assertor.assert(pass, message, @backtrace)
201
260
  end
202
- false
261
+
262
+ #
263
+ def matcher_message(matcher)
264
+ if @negated
265
+ if matcher.respond_to?(:negative_failure_message)
266
+ return matcher.failure_message
267
+ end
268
+ end
269
+ if matcher.respond_to?(:failure_message)
270
+ return matcher.failure_message
271
+ end
272
+ false
273
+ end
274
+
275
+ # TODO: Ultimately better messages might be nice.
276
+ #
277
+ #def self.message(op,&block)
278
+ # @message ||= {}
279
+ # block ? @message[op.to_sym] = block : @message[op.to_sym]
280
+ #end
281
+ #
282
+ #message(:==){ |*a| "Expected #{a[0].inspect} to be equal to #{a[1].inspect}" }
203
283
  end
204
284
 
205
- # TODO: Ultimately better messages might be nice.
206
- #
207
- #def self.message(op,&block)
208
- # @message ||= {}
209
- # block ? @message[op.to_sym] = block : @message[op.to_sym]
210
- #end
211
- #
212
- #message(:==){ |*a| "Expected #{a[0].inspect} to be equal to #{a[1].inspect}" }
213
285
  end
214
286
 
215
287
  # DO WE MAKE THESE EXCEPTIONS?