Module: MSpec

Defined in:
lib/mspec/matchers/include.rb,
lib/mspec/version.rb,
lib/mspec/runner/mspec.rb

Overview

Cannot override #include at the toplevel in MRI

Constant Summary collapse

VERSION =
SpecVersion.new "1.8.0"

Class Method Summary collapse

Class Method Details

.actions(action, *args) ⇒ Object


61
62
63
64
# File 'lib/mspec/runner/mspec.rb', line 61

def self.actions(action, *args)
  actions = retrieve(action)
  actions.each { |obj| obj.send action, *args } if actions
end

.clear_currentObject

Sets the toplevel ContextState to nil.


99
100
101
# File 'lib/mspec/runner/mspec.rb', line 99

def self.clear_current
  store :current, nil
end

.clear_expectationsObject

Resets the flag that an expectation has been encountered in an example.


263
264
265
# File 'lib/mspec/runner/mspec.rb', line 263

def self.clear_expectations
  store :expectations, false
end

.clear_modesObject

Clears all registered modes.


157
158
159
# File 'lib/mspec/runner/mspec.rb', line 157

def self.clear_modes
  store :modes, []
end

.currentObject

Returns the toplevel ContextState.


104
105
106
# File 'lib/mspec/runner/mspec.rb', line 104

def self.current
  retrieve :current
end

.delete_tag(tag) ⇒ Object

Deletes tag from the tag file if it exists. Returns true if the tag is deleted, false otherwise. Deletes the tag file if it is empty.


343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/mspec/runner/mspec.rb', line 343

def self.delete_tag(tag)
  deleted = false
  pattern = /#{tag.tag}.*#{Regexp.escape(tag.escape(tag.description))}/
  file = tags_file
  if File.exist? file
    lines = IO.readlines(file)
    File.open(file, "wb") do |f|
      lines.each do |line|
        unless pattern =~ line.chomp
          f.puts line unless line.empty?
        else
          deleted = true
        end
      end
    end
    File.delete file unless File.size? file
  end
  return deleted
end

.delete_tagsObject

Removes the tag file associated with a spec file.


364
365
366
367
# File 'lib/mspec/runner/mspec.rb', line 364

def self.delete_tags
  file = tags_file
  File.delete file if File.exist? file
end

.describe(mod, options = nil, &block) ⇒ Object


30
31
32
33
34
35
36
37
38
# File 'lib/mspec/runner/mspec.rb', line 30

def self.describe(mod, options=nil, &block)
  state = ContextState.new mod, options
  state.parent = current

  MSpec.register_current state
  state.describe(&block)

  state.process unless state.shared? or current
end

.disable_feature(feature) ⇒ Object


170
171
172
# File 'lib/mspec/runner/mspec.rb', line 170

def self.disable_feature(feature)
  retrieve(:features)[feature] = false
end

.enable_feature(feature) ⇒ Object


166
167
168
# File 'lib/mspec/runner/mspec.rb', line 166

def self.enable_feature(feature)
  retrieve(:features)[feature] = true
end

.exit_codeObject

Retrieves the stored exit code.


124
125
126
# File 'lib/mspec/runner/mspec.rb', line 124

def self.exit_code
  retrieve(:exit).to_i
end

.expectationObject

Records that an expectation has been encountered in an example.


253
254
255
# File 'lib/mspec/runner/mspec.rb', line 253

def self.expectation
  store :expectations, true
end

.expectation?Boolean

Returns true if an expectation has been encountered


258
259
260
# File 'lib/mspec/runner/mspec.rb', line 258

def self.expectation?
  retrieve :expectations
end

.feature_enabled?(feature) ⇒ Boolean


174
175
176
# File 'lib/mspec/runner/mspec.rb', line 174

def self.feature_enabled?(feature)
  retrieve(:features)[feature] || false
end

.filesObject


46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/mspec/runner/mspec.rb', line 46

def self.files
  return unless files = retrieve(:files)

  shuffle files if randomize?
  files.each do |file|
    @env = Object.new
    @env.extend MSpec

    store :file, file
    actions :load
    protect("loading #{file}") { Kernel.load file }
    actions :unload
  end
end

.guardObject

Guards can be nested, so a stack is necessary to know when we have exited the toplevel guard.


81
82
83
# File 'lib/mspec/runner/mspec.rb', line 81

def self.guard
  @guarded << true
end

.guarded?Boolean


89
90
91
# File 'lib/mspec/runner/mspec.rb', line 89

def self.guarded?
  not @guarded.empty?
end

.include(*expected) ⇒ Object


28
29
30
# File 'lib/mspec/matchers/include.rb', line 28

def include(*expected)
  IncludeMatcher.new(*expected)
end

.make_tag_dir(path) ⇒ Object


303
304
305
306
307
308
309
310
311
312
# File 'lib/mspec/runner/mspec.rb', line 303

def self.make_tag_dir(path)
  parent = File.dirname(path)
  return if File.exist? parent
  begin
    Dir.mkdir(parent)
  rescue SystemCallError
    make_tag_dir(parent)
    Dir.mkdir(parent)
  end
end

.mode?(mode) ⇒ Boolean

Returns true if mode is registered.


162
163
164
# File 'lib/mspec/runner/mspec.rb', line 162

def self.mode?(mode)
  retrieve(:modes).include? mode
end

.processObject


40
41
42
43
44
# File 'lib/mspec/runner/mspec.rb', line 40

def self.process
  actions :start
  files
  actions :finish
end

.protect(location, &block) ⇒ Object


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

def self.protect(location, &block)
  begin
    @env.instance_eval(&block)
    return true
  rescue SystemExit
    raise
  rescue Exception => exc
    register_exit 1
    actions :exception, ExceptionState.new(current && current.state, location, exc)
    return false
  end
end

.randomize(flag = true) ⇒ Object


224
225
226
# File 'lib/mspec/runner/mspec.rb', line 224

def self.randomize(flag=true)
  @randomize = flag
end

.randomize?Boolean


228
229
230
# File 'lib/mspec/runner/mspec.rb', line 228

def self.randomize?
  @randomize == true
end

.read_tags(keys) ⇒ Object

Returns a list of tags matching any tag string in keys based on the return value of keys.include?("tag_name")


287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/mspec/runner/mspec.rb', line 287

def self.read_tags(keys)
  tags = []
  file = tags_file
  if File.exist? file
    File.open(file, "rb") do |f|
      f.each_line do |line|
        line.chomp!
        next if line.empty?
        tag = SpecTag.new line
        tags << tag if keys.include? tag.tag
      end
    end
  end
  tags
end

.register(symbol, action) ⇒ Object

This method is used for registering actions that are run at particular points in the spec cycle:

:start        before any specs are run
:load         before a spec file is loaded
:enter        before a describe block is run
:before       before a single spec is run
:add          while a describe block is adding examples to run later
:expectation  before a 'should', 'should_receive', etc.
:example      after an example block is run, passed the block
:exception    after an exception is rescued
:after        after a single spec is run
:leave        after a describe block is run
:unload       after a spec file is run
:finish       after all specs are run

Objects registered as actions above should respond to a method of the same name. For example, if an object is registered as a :start action, it should respond to a #start method call.

Additionally, there are two “action” lists for filtering specs:

:include  return true if the spec should be run
:exclude  return true if the spec should NOT be run

211
212
213
214
215
216
# File 'lib/mspec/runner/mspec.rb', line 211

def self.register(symbol, action)
  unless value = retrieve(symbol)
    value = store symbol, []
  end
  value << action unless value.include? action
end

.register_current(state) ⇒ Object

Sets the toplevel ContextState to state.


94
95
96
# File 'lib/mspec/runner/mspec.rb', line 94

def self.register_current(state)
  store :current, state
end

.register_exit(code) ⇒ Object

Stores the exit code used by the runner scripts.


119
120
121
# File 'lib/mspec/runner/mspec.rb', line 119

def self.register_exit(code)
  store :exit, code
end

.register_files(files) ⇒ Object

Stores the list of files to be evaluated.


129
130
131
# File 'lib/mspec/runner/mspec.rb', line 129

def self.register_files(files)
  store :files, files
end

.register_mode(mode) ⇒ Object

Registers an operating mode. Modes recognized by MSpec:

:pretend - actions execute but specs are not run
:verify - specs are run despite guards and the result is
          verified to match the expectation of the guard
:report - specs that are guarded are reported
:unguarded - all guards are forced off

151
152
153
154
# File 'lib/mspec/runner/mspec.rb', line 151

def self.register_mode(mode)
  modes = retrieve :modes
  modes << mode unless modes.include? mode
end

.register_shared(state) ⇒ Object

Stores the shared ContextState keyed by description.


109
110
111
# File 'lib/mspec/runner/mspec.rb', line 109

def self.register_shared(state)
  @shared[state.to_s] = state
end

.register_tags_patterns(patterns) ⇒ Object

Stores one or more substitution patterns for transforming a spec filename into a tags filename, where each pattern has the form:

[Regexp, String]

See also tags_file.


140
141
142
# File 'lib/mspec/runner/mspec.rb', line 140

def self.register_tags_patterns(patterns)
  store :tags_patterns, patterns
end

.repeatObject


236
237
238
239
240
# File 'lib/mspec/runner/mspec.rb', line 236

def self.repeat
  (@repeat || 1).times do
    yield
  end
end

.repeat=(times) ⇒ Object


232
233
234
# File 'lib/mspec/runner/mspec.rb', line 232

def self.repeat=(times)
  @repeat = times
end

.retrieve(symbol) ⇒ Object


178
179
180
# File 'lib/mspec/runner/mspec.rb', line 178

def self.retrieve(symbol)
  instance_variable_get :"@#{symbol}"
end

.retrieve_shared(desc) ⇒ Object

Returns the shared ContextState matching description.


114
115
116
# File 'lib/mspec/runner/mspec.rb', line 114

def self.retrieve_shared(desc)
  @shared[desc.to_s]
end

.shuffle(ary) ⇒ Object


242
243
244
245
246
247
248
249
250
# File 'lib/mspec/runner/mspec.rb', line 242

def self.shuffle(ary)
  return if ary.empty?

  size = ary.size
  size.times do |i|
    r = rand(size - i - 1)
    ary[i], ary[r] = ary[r], ary[i]
  end
end

.store(symbol, value) ⇒ Object


182
183
184
# File 'lib/mspec/runner/mspec.rb', line 182

def self.store(symbol, value)
  instance_variable_set :"@#{symbol}", value
end

.tags_fileObject

Transforms a spec filename into a tags filename by applying each substitution pattern in :tags_pattern. The default patterns are:

[%r(/spec/), '/spec/tags/'], [/_spec.rb$/, '_tags.txt']

which will perform the following transformation:

path/to/spec/class/method_spec.rb => path/to/spec/tags/class/method_tags.txt

See also register_tags_patterns.


277
278
279
280
281
282
283
# File 'lib/mspec/runner/mspec.rb', line 277

def self.tags_file
  patterns = retrieve(:tags_patterns) ||
             [[%r(spec/), 'spec/tags/'], [/_spec.rb$/, '_tags.txt']]
  patterns.inject(retrieve(:file).dup) do |file, pattern|
    file.gsub(*pattern)
  end
end

.unguardObject


85
86
87
# File 'lib/mspec/runner/mspec.rb', line 85

def self.unguard
  @guarded.pop
end

.unregister(symbol, action) ⇒ Object


218
219
220
221
222
# File 'lib/mspec/runner/mspec.rb', line 218

def self.unregister(symbol, action)
  if value = retrieve(symbol)
    value.delete action
  end
end

.write_tag(tag) ⇒ Object

Writes tag to the tag file if it does not already exist. Returns true if the tag is written, false otherwise.


326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'lib/mspec/runner/mspec.rb', line 326

def self.write_tag(tag)
  tags = read_tags([tag.tag])
  tags.each do |t|
    if t.tag == tag.tag and t.description == tag.description
      return false
    end
  end

  file = tags_file
  make_tag_dir(file)
  File.open(file, "ab") { |f| f.puts tag.to_s }
  return true
end

.write_tags(tags) ⇒ Object

Writes each tag in tags to the tag file. Overwrites the tag file if it exists.


316
317
318
319
320
321
322
# File 'lib/mspec/runner/mspec.rb', line 316

def self.write_tags(tags)
  file = tags_file
  make_tag_dir(file)
  File.open(file, "wb") do |f|
    tags.each { |t| f.puts t }
  end
end