Class: QED::Scope

Inherits:
Module show all
Defined in:
lib/qed/scope.rb

Overview

Scope is the context in which QED documents are run.

Constant Summary collapse

DIRECTORY =

Location of `qed/scope.rb`.

File.dirname(__FILE__)

Instance Method Summary collapse

Constructor Details

#initialize(demo, options = {}) ⇒ Scope

Setup new Scope instance.


13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/qed/scope.rb', line 13

def initialize(demo, options={})
  super()

  @_applique = demo.applique_prime

  @_file     = demo.file
  @_root     = options[:root] || $ROOT  # FIXME

  @_features = []

  include *demo.applique

  # TODO: custom extends?

  __create_clean_binding_method__
end

Instance Method Details

#__create_clean_binding_method__Object

This turns out to be the key to proper scoping.


31
32
33
34
35
36
37
# File 'lib/qed/scope.rb', line 31

def __create_clean_binding_method__
  module_eval %{
    def __binding__
      @__binding__ ||= binding
    end
  }
end

#__DIR__(file = nil) ⇒ Object

Directory of current document.


103
104
105
106
107
108
109
# File 'lib/qed/scope.rb', line 103

def __DIR__(file=nil)
  if file
    Dir.glob(File.join(File.dirname(@_file), file)).first || file
  else
    File.dirname(@_file)
  end
end

#After(type = :each, &procedure) ⇒ Object

Define “after” advice. Default type is :each, which evaluates just after example code is run.


96
97
98
99
100
# File 'lib/qed/scope.rb', line 96

def After(type=:each, &procedure)
  type = :step if type == :each
  type = :demo if type == :all
  @_applique.After(type, &procedure)
end

#Before(type = :each, &procedure) ⇒ Object

Define “before” advice. Default type is :each, which evaluates just before example code is run.


88
89
90
91
92
# File 'lib/qed/scope.rb', line 88

def Before(type=:each, &procedure)
  type = :step if type == :each
  type = :demo if type == :all
  @_applique.Before(type, &procedure)
end

#clear_working_directory!Object

Helper method to clear temporary work directory.


169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/qed/scope.rb', line 169

def clear_working_directory!
  dir = @_root
  dir = File.expand_path(dir)

  if dir == '/' or dir == File.expand_path('~')
    abort "DANGER! Trying to use home or root as a temporary directory!"
  end

  entries = Dir.glob(File.join(dir, '**/*'))

  dirs, files = entries.partition{ |f| File.directory?(f) }

  files.each { |file| FileUtils.rm(file)   }
  dirs.each  { |dir|  FileUtils.rmdir(dir) }
end

#const_missing(const) ⇒ Object

Redirect constant missing to toplevel (i.e. Object). This is to allow the evaluation scope to emulate the toplevel.


192
193
194
# File 'lib/qed/scope.rb', line 192

def const_missing(const)
  Object.const_get(const)
end

#Data(file) ⇒ Object

Read a static data file and yield contents to block if given.

This method no longer automatically uses YAML.load.


152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/qed/scope.rb', line 152

def Data(file) #:yield:
  file = Dir.glob(File.join(File.dirname(@_file), file)).first || file  #case File.extname(file)
  #when '.yml', '.yaml'
  #  data = YAML.load(File.new(file))
  #else

    data = File.read(file)  #end

  if block_given?
    yield(data)
  else
    data
  end
end

#demo_directoryObject

Expanded dirname of file.


46
47
48
# File 'lib/qed/scope.rb', line 46

def demo_directory
  @_demo_directory ||= File.expand_path(File.dirname(@_file))
end

#evaluate(code, file = nil, line = nil) ⇒ Object

Evaluate code in the context of the scope's special binding. The return value of the evaluation is stored in `@_`.


53
54
55
56
57
58
59
# File 'lib/qed/scope.rb', line 53

def evaluate(code, file=nil, line=nil)
  if file
    @_ = eval(code, __binding__, file.to_s, line.to_i)
  else
    @_ = eval(code, __binding__)
  end
end

#include(*modules) ⇒ Object


40
41
42
43
# File 'lib/qed/scope.rb', line 40

def include(*modules)
  super(*modules)
  extend self  # overcome dynamic inclusion problem
end

#Table(file = nil, options = {}) ⇒ Object

Use sample table to run steps. The table file is located relative to the demo, failing that it will be looked for relative to the working directory.


119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/qed/scope.rb', line 119

def Table(file=nil, options={}) #:yield:
  if file
    file = Dir.glob(File.join(File.dirname(@_file), file)).first || file
  else
    file = @_last_table
  end
  @_last_table = file

  file_handle = File.new(file)

  if options[:stream]
    if block_given?
      YAML.load_documents(file_handle) do |data|
        yield data
      end
    else
      YAML.load_stream(file_handle)
    end
  else
    if block_given?
      tbl = YAML.load(file_handle)
      tbl.each do |data|
        yield(*data)
      end
    else
      YAML.load(file_handle)
    end
  end
end

#utilize(file) ⇒ Object

Utilize is like #require, but will evaluate the script in the context of the current scope.


66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/qed/scope.rb', line 66

def utilize(file)
  file = Dir[DIRECTORY + "/helpers/#{file}"].first
  if !file
    require 'plugin'
    file = Plugin.find("#{file}{,.rb}", :directory=>nil)
  end
  if file && !@_features.include?(file)
    code = File.read(file)
    evaluate(code, nil, file)
  else
    raise LoadError, "no such file -- #{file}"
  end
end

#When(*patterns, &procedure) ⇒ Object

Define “when” advice.


81
82
83
84
# File 'lib/qed/scope.rb', line 81

def When(*patterns, &procedure)
  #patterns = patterns.map{ |pat| pat == :text ? :desc : pat }
  @_applique.When(*patterns, &procedure)
end