Class: Bacon::Specification

Inherits:
Object show all
Defined in:
lib/mac_bacon.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(context, description, block, before_filters, after_filters) ⇒ Specification

Returns a new instance of Specification.



135
136
137
138
139
140
141
142
143
144
# File 'lib/mac_bacon.rb', line 135

def initialize(context, description, block, before_filters, after_filters)
  @context, @description, @block = context, description, block
  @before_filters, @after_filters = before_filters.dup, after_filters.dup

  @postponed_blocks_count = 0
  @ran_spec_block = false
  @ran_after_filters = false
  @exception_occurred = false
  @error = ""
end

Instance Attribute Details

#descriptionObject (readonly)

Returns the value of attribute description.



133
134
135
# File 'lib/mac_bacon.rb', line 133

def description
  @description
end

Instance Method Details

#cancel_scheduled_requests!Object



268
269
270
271
# File 'lib/mac_bacon.rb', line 268

def cancel_scheduled_requests!
  NSObject.cancelPreviousPerformRequestsWithTarget(@context)
  NSObject.cancelPreviousPerformRequestsWithTarget(self)
end

#execute_blockObject



280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/mac_bacon.rb', line 280

def execute_block
  begin
    yield
  rescue Object => e
    @exception_occurred = true

    ErrorLog << "#{e.class}: #{e.message}\n"
    lines = $DEBUG ? e.backtrace : e.backtrace.find_all { |line| line !~ /bin\/macbacon|\/mac_bacon\.rb:\d+/ }
    lines.each_with_index { |line, i|
      ErrorLog << "\t#{line}#{i==0 ? ": #{@context.name} - #{@description}" : ""}\n"
    }
    ErrorLog << "\n"

    @error = if e.kind_of? Error
      Counter[e.count_as] += 1
      e.count_as.to_s.upcase
    else
      Counter[:errors] += 1
      "ERROR: #{e.class}"
    end
  end
end

#exit_specObject



273
274
275
276
277
278
# File 'lib/mac_bacon.rb', line 273

def exit_spec
  cancel_scheduled_requests!
  Counter[:depth] -= 1
  Bacon.handle_requirement_end(@error)
  @context.specification_did_finish(self)
end

#finish_specObject



259
260
261
262
263
264
265
266
# File 'lib/mac_bacon.rb', line 259

def finish_spec
  if !@exception_occurred && Counter[:requirements] == @number_of_requirements_before
    # the specification did not contain any requirements, so it flunked
    execute_block { raise Error.new(:missing, "empty specification: #{@context.name} #{@description}") }
  end
  run_after_filters
  exit_spec unless postponed?
end

#observeValueForKeyPath(key_path, ofObject: object, change: _, context: __) ⇒ Object



212
213
214
# File 'lib/mac_bacon.rb', line 212

def observeValueForKeyPath(key_path, ofObject:object, change:_, context:__)
  resume
end

#postpone_block(timeout = 1, &block) ⇒ Object



184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/mac_bacon.rb', line 184

def postpone_block(timeout = 1, &block)
  # If an exception occurred, we definitely don't need to schedule any more blocks
  unless @exception_occurred
    if @postponed_block
      raise "Only one indefinite `wait' block at the same time is allowed!"
    else
      @postponed_blocks_count += 1
      @postponed_block = block
      performSelector("postponed_block_timeout_exceeded", withObject:nil, afterDelay:timeout)
    end
  end
end

#postpone_block_until_change(object_to_observe, key_path, timeout = 1, &block) ⇒ Object



197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/mac_bacon.rb', line 197

def postpone_block_until_change(object_to_observe, key_path, timeout = 1, &block)
  # If an exception occurred, we definitely don't need to schedule any more blocks
  unless @exception_occurred
    if @postponed_block
      raise "Only one indefinite `wait' block at the same time is allowed!"
    else
      @postponed_blocks_count += 1
      @postponed_block = block
      @observed_object_and_key_path = [object_to_observe, key_path]
      object_to_observe.addObserver(self, forKeyPath:key_path, options:0, context:nil)
      performSelector("postponed_change_block_timeout_exceeded", withObject:nil, afterDelay:timeout)
    end
  end
end

#postponed?Boolean

Returns:

  • (Boolean)


146
147
148
# File 'lib/mac_bacon.rb', line 146

def postponed?
  @postponed_blocks_count != 0
end

#postponed_block_timeout_exceededObject



229
230
231
232
233
234
# File 'lib/mac_bacon.rb', line 229

def postponed_block_timeout_exceeded
  cancel_scheduled_requests!
  execute_block { raise Error.new(:failed, "timeout exceeded: #{@context.name} - #{@description}") }
  @postponed_blocks_count = 0
  finish_spec
end

#postponed_change_block_timeout_exceededObject



216
217
218
219
# File 'lib/mac_bacon.rb', line 216

def postponed_change_block_timeout_exceeded
  remove_observer!
  postponed_block_timeout_exceeded
end

#remove_observer!Object



221
222
223
224
225
226
227
# File 'lib/mac_bacon.rb', line 221

def remove_observer!
  if @observed_object_and_key_path
    object, key_path = @observed_object_and_key_path
    object.removeObserver(self, forKeyPath:key_path)
    @observed_object_and_key_path = nil
  end
end

#resumeObject



236
237
238
239
240
241
242
# File 'lib/mac_bacon.rb', line 236

def resume
  NSObject.cancelPreviousPerformRequestsWithTarget(self, selector:'postponed_block_timeout_exceeded', object:nil)
  NSObject.cancelPreviousPerformRequestsWithTarget(self, selector:'postponed_change_block_timeout_exceeded', object:nil)
  remove_observer!
  block, @postponed_block = @postponed_block, nil
  run_postponed_block(block)
end

#runObject



168
169
170
171
172
173
174
# File 'lib/mac_bacon.rb', line 168

def run
  Bacon.handle_requirement_begin(@description)
  Counter[:depth] += 1
  run_before_filters
  @number_of_requirements_before = Counter[:requirements]
  run_spec_block unless postponed?
end

#run_after_filtersObject



163
164
165
166
# File 'lib/mac_bacon.rb', line 163

def run_after_filters
  @ran_after_filters = true
  execute_block { @after_filters.each { |f| @context.instance_eval(&f) } }
end

#run_before_filtersObject



150
151
152
# File 'lib/mac_bacon.rb', line 150

def run_before_filters
  execute_block { @before_filters.each { |f| @context.instance_eval(&f) } }
end

#run_postponed_block(block) ⇒ Object



244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/mac_bacon.rb', line 244

def run_postponed_block(block)
  # If an exception occurred, we definitely don't need execute any more blocks
  execute_block(&block) unless @exception_occurred
  @postponed_blocks_count -= 1
  unless postponed?
    if @ran_after_filters
      exit_spec
    elsif @ran_spec_block
      finish_spec
    else
      run_spec_block
    end
  end
end

#run_spec_blockObject



154
155
156
157
158
159
160
161
# File 'lib/mac_bacon.rb', line 154

def run_spec_block
  @ran_spec_block = true
  # If an exception occurred, we definitely don't need to perform the actual spec anymore
  unless @exception_occurred
    execute_block { @context.instance_eval(&@block) }
  end
  finish_spec unless postponed?
end

#schedule_block(seconds, &block) ⇒ Object



176
177
178
179
180
181
182
# File 'lib/mac_bacon.rb', line 176

def schedule_block(seconds, &block)
  # If an exception occurred, we definitely don't need to schedule any more blocks
  unless @exception_occurred
    @postponed_blocks_count += 1
    performSelector("run_postponed_block:", withObject:block, afterDelay:seconds)
  end
end