Class: AE::Assertor

Inherits:
BasicObject
Includes:
Subjunctive
Defined in:
lib/ae/assertor.rb,
lib/ae/subjunctive.rb,
lib/ae/adapters/testunit.rb

Overview

:nodoc:

Constant Summary collapse

ZERO_COUNTS =

Initial settings of assertion counts.

{:total=>0,:pass=>0,:fail=>0}

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Subjunctive

#a, #be

Methods inherited from BasicObject

find_hidden_method, hide, reveal

Constructor Details

#initialize(delegate, opts = {}) ⇒ Assertor

New Assertor.



95
96
97
98
99
100
# File 'lib/ae/assertor.rb', line 95

def initialize(delegate, opts={}) #, backtrace)
  @delegate  = delegate
  @message   = opts[:message]
  @backtrace = opts[:backtrace] || caller #[1..-1]
  @negated   = !!opts[:negated]
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(sym, *a, &b) ⇒ Object (private)

Converts a missing method into an Assertion.

TODO: In future should probably be ‘@delegate.public_send(sym, *a, &b)`.



236
237
238
239
# File 'lib/ae/assertor.rb', line 236

def method_missing(sym, *a, &b)
  pass = @delegate.__send__(sym, *a, &b)
  __assert__(pass, @message || __msg__(sym, *a, &b))
end

Class Method Details

.assert(pass, message = nil, backtrace = nil) ⇒ Object

Basic assertion. This method by-passes all the Assertor fluent constructs and performs the underlying assertion procedure. It is used by Assertor as the end result of an assertion.



61
62
63
64
65
66
67
68
69
# File 'lib/ae/assertor.rb', line 61

def self.assert(pass, message=nil, backtrace=nil)
  increment_counts(pass)
  if !pass
    backtrace = backtrace || caller
    message   = message   || 'flunk'
    raise_assertion(message, backtrace)
  end
  return pass
end

.assertion_errorObject

Returns the Exception class to be raised when an assertion fails.



84
85
86
# File 'lib/ae/assertor.rb', line 84

def self.assertion_error
  ::Assertion
end

.countsObject

Returns Hash used to track assertion counts.



24
25
26
# File 'lib/ae/assertor.rb', line 24

def self.counts
  $assertion_counts
end

.increment_counts(pass) ⇒ Object

Increment assertion counts. If pass is true then :total and :pass are increased. If pass if false then :total and :fail are incremented.



48
49
50
51
52
53
54
55
56
# File 'lib/ae/assertor.rb', line 48

def self.increment_counts(pass)
  counts[:total] += 1
  if pass
    counts[:pass] += 1
  else
    counts[:fail] += 1
  end
  return counts
end

.raise_assertion(message, backtrace = nil) ⇒ Object

This method can be replaced to support alternate frameworks. The intent of the method is to raise the assertion failure class that the framework uses.



74
75
76
77
78
79
80
81
# File 'lib/ae/assertor.rb', line 74

def self.raise_assertion(message, backtrace=nil)
  backtrace = backtrace || caller

  error = assertion_error.new(message)
  error.set_backtrace(backtrace)
  error.set_assertion(true)
  fail error
end

.recount(reset = {}) ⇒ Object

Reset assertion counts.

reset - Hash which will be used to set counts manually (optional).

Returns the Hash of previous counts.



33
34
35
36
37
38
39
40
41
42
43
# File 'lib/ae/assertor.rb', line 33

def self.recount(reset={})
  old_counts = counts.dup
  if reset.empty?
    counts.replace(ZERO_COUNTS.dup)
  else
    reset.each do |type, value|
      counts[type.to_sym] = value
    end
  end
  return old_counts
end

Instance Method Details

#=~(match) ⇒ Object

Ruby seems to have a quark in it’s implementation whereby this must be defined explicitly, otherwise it somehow skips #method_missing.



208
209
210
# File 'lib/ae/assertor.rb', line 208

def =~(match)
  method_missing(:"=~", match)
end

#assert(*args, &block) ⇒ Object

Internal assert, provides all functionality associated with external #assert method. (See Assert#assert)

NOTE: I’m calling YAGNI on using extra arguments to pass to the block. The interface is much nicer if a macro is created to handle any neccessry arguments. Eg.

assert something(parameter)

instead of

assert something, parameter

Returns true or false based on assertions success.



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/ae/assertor.rb', line 127

def assert(*args, &block)
  return self if args.empty? && !block

  target = block || args.shift

  if ::Proc === target || target.respond_to?(:to_proc)
    block  = target.to_proc
    match  = args.shift
    result = block.arity > 0 ? block.call(@delegate) : block.call
    if match
      pass = (match == result)
      msg  = @message || "#{match.inspect} == #{result.inspect}"
    else
      pass = result
      msg  = @message || block.inspect  # "#{result.inspect}"
    end
  elsif target.respond_to?(:matches?)
    pass   = target.matches?(@delegate)
    msg    = @message || matcher_message(target) || target.inspect
  else
    pass   = target     # truthiness
    msg    = args.shift # optional mesage for TestUnit compatiability
  end

  __assert__(pass, msg)
end

#expect(*args, &block) ⇒ Object

Internal expect, provides all functionality associated with external #expect method. (See Expect#expect)

– TODO: Should we deprecate the receiver matches in favor of #expected ? In other words, should the || @delegate be dropped? ++



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/ae/assertor.rb', line 161

def expect(*args, &block)
  return self if args.empty? && !block  # same as #assert

  target = block || args.shift

  if ::Proc === target #|| target.respond_to?(:to_proc)
    #block = target.to_proc
    match = args.shift || @delegate
    if exception?(match)
      $DEBUG, debug = false, $DEBUG  # b/c it always spits-out a NameError
      begin
        block.arity > 0 ? block.call(@delegate) : block.call
        pass = false
        msg  = "#{match} not raised"
      rescue match => error
        pass = true
        msg  = "#{match} raised"
      rescue ::Exception => error
        pass = false
        msg  = "#{match} expected but #{error.class} was raised"
      ensure
        $DEBUG = debug
      end
    else
      result = block.arity > 0 ? block.call(@delegte) : block.call
      pass   = (match === result)
      msg    = @message || "#{match.inspect} === #{result.inspect}"
    end
  elsif target.respond_to?(:matches?)
    pass = target.matches?(@delegate)
    msg  = @message || matcher_message(target) || target.inspect
  else
    pass = (target === @delegate)
    msg  = @message || "#{target.inspect} === #{@delegate.inspect}"
  end

  __assert__(pass, msg)
end

#flunk(message = nil, backtrace = nil) ⇒ Object



201
202
203
# File 'lib/ae/assertor.rb', line 201

def flunk(message=nil, backtrace=nil)
  __assert__(false, message || @message)
end

#inspectObject



218
219
220
# File 'lib/ae/assertor.rb', line 218

def inspect
  @delegate.inspect
end

#not(msg = nil) ⇒ Object

Negate the meaning of the assertion.

– TODO: Should this return a new Assertor instead of in place negation? ++



107
108
109
110
111
# File 'lib/ae/assertor.rb', line 107

def not(msg=nil)
  @negated = !@negated
  @message = msg if msg
  self
end

#send(op, *a, &b) ⇒ Object



213
214
215
# File 'lib/ae/assertor.rb', line 213

def send(op, *a, &b)
  method_missing(op, *a, &b)
end