ae 1.6.1 → 1.7.0

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