mspec 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,7 +2,7 @@
2
2
 
3
3
  MSPEC_HOME = File.expand_path(File.dirname(__FILE__) + '/../../..')
4
4
 
5
- require 'optparse'
5
+ require 'mspec/version'
6
6
  require 'mspec/utils/options'
7
7
  require 'mspec/utils/script'
8
8
  require 'mspec/helpers/tmp'
@@ -20,24 +20,19 @@ class MSpecMain < MSpecScript
20
20
  end
21
21
 
22
22
  def options(argv=ARGV)
23
- if ["ci", "run", "tag"].include? argv[0]
24
- config[:command] = argv.shift
25
- config[:options] << "-h" if argv.delete("-h") || argv.delete("--help")
26
- config[:options] << "-v" if argv.delete("-v") || argv.delete("--version")
27
- end
23
+ config[:command] = argv.shift if ["ci", "run", "tag"].include?(argv[0])
28
24
 
29
- options = MSpecOptions.new config, "[COMMAND]", "", 28, " "
25
+ options = MSpecOptions.new "mspec [COMMAND] [options] (FILE|DIRECTORY|GLOB)+", 30, config
30
26
 
31
- options.separator ""
32
- options.separator " The mspec command sets up and invokes the sub-commands"
33
- options.separator " (see below) to enable, for instance, running the specs"
34
- options.separator " with different implementations like ruby, jruby, rbx, etc.\n"
27
+ options.doc " The mspec command sets up and invokes the sub-commands"
28
+ options.doc " (see below) to enable, for instance, running the specs"
29
+ options.doc " with different implementations like ruby, jruby, rbx, etc.\n"
35
30
 
36
- options.add_config do |f|
31
+ options.configure do |f|
37
32
  config[:options] << '-B' << f
38
33
  end
39
34
 
40
- options.add_targets
35
+ options.targets
41
36
 
42
37
  options.on("-D", "--gdb", "Run under gdb") do
43
38
  config[:flags] << '--gdb'
@@ -53,21 +48,32 @@ class MSpecMain < MSpecScript
53
48
  config[:multi] = true
54
49
  config[:options] << "-fy"
55
50
  end
56
- options.add_version
57
- options.on("-h", "--help", "Show this message") do
58
- puts options.parser
59
- exit
51
+ options.version MSpec::VERSION do
52
+ if config[:command]
53
+ config[:options] << "-v"
54
+ else
55
+ puts options
56
+ exit
57
+ end
58
+ end
59
+ options.help do
60
+ if config[:command]
61
+ config[:options] << "-h"
62
+ else
63
+ puts options
64
+ exit 1
65
+ end
60
66
  end
61
67
 
62
68
  # The rest of the help output
63
- options.separator "\n where COMMAND is one of:\n"
64
- options.separator " run - Run the specified specs (default)"
65
- options.separator " ci - Run the known good specs"
66
- options.separator " tag - Add or remove tags\n"
67
- options.separator " mspec COMMAND -h for more options\n"
68
-
69
- config[:options] += options.parser.filter! argv
70
- options.parse argv
69
+ options.doc "\n where COMMAND is one of:\n"
70
+ options.doc " run - Run the specified specs (default)"
71
+ options.doc " ci - Run the known good specs"
72
+ options.doc " tag - Add or remove tags\n"
73
+ options.doc " mspec COMMAND -h for more options\n"
74
+
75
+ options.on_extra { |o| config[:options] << o }
76
+ config[:options].concat options.parse(argv)
71
77
  end
72
78
 
73
79
  def register; end
@@ -126,11 +132,14 @@ class MSpecMain < MSpecScript
126
132
 
127
133
  def run
128
134
  ENV['MSPEC_RUNNER'] = '1'
135
+ ENV['RUBY_EXE'] = config[:target]
136
+ ENV['RUBY_FLAGS'] = config[:flags].join " "
129
137
 
130
- argv = ["-v"]
138
+ argv = []
131
139
  argv.concat config[:flags]
132
140
  argv.concat config[:includes]
133
141
  argv.concat config[:requires]
142
+ argv << "-v"
134
143
  argv << "#{MSPEC_HOME}/bin/mspec-#{ config[:command] || "run" }"
135
144
  argv.concat config[:options]
136
145
 
data/lib/mspec/guards.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'mspec/ruby_name'
1
2
  require 'mspec/guards/bug'
2
3
  require 'mspec/guards/compliance'
3
4
  require 'mspec/guards/extensions'
@@ -1,21 +1,6 @@
1
1
  require 'mspec/runner/mspec'
2
2
  require 'mspec/runner/actions/tally'
3
3
 
4
- unless defined?(RUBY_NAME) and RUBY_NAME
5
- if defined?(RUBY_ENGINE) and RUBY_ENGINE
6
- RUBY_NAME = RUBY_ENGINE
7
- if defined?(ARG0)
8
- RUBY_CLI = /rubinius|rbx/.match(ARG0) ? "shotgun/rubinius" : ARG0
9
- else
10
- RUBY_CLI = RUBY_NAME
11
- end
12
- else
13
- require 'rbconfig'
14
- RUBY_NAME = Config::CONFIG["RUBY_INSTALL_NAME"] || Config::CONFIG["ruby_install_name"]
15
- RUBY_CLI = RUBY_NAME
16
- end
17
- end
18
-
19
4
  class SpecGuard
20
5
  def self.register
21
6
  unless @registered
@@ -81,6 +66,8 @@ class SpecGuard
81
66
  RUBY_NAME =~ /^ruby(1.9)?/ and RUBY_VERSION =~ /^1.9/
82
67
  when :jruby
83
68
  RUBY_NAME =~ /^jruby/
69
+ when :ironruby, :ir
70
+ RUBY_NAME =~ /^ironruby/
84
71
  else
85
72
  false
86
73
  end
data/lib/mspec/helpers.rb CHANGED
@@ -4,3 +4,4 @@ require 'mspec/helpers/io'
4
4
  require 'mspec/helpers/scratch'
5
5
  require 'mspec/helpers/tmp'
6
6
  require 'mspec/helpers/const_lookup'
7
+ require 'mspec/helpers/ruby_exe'
@@ -0,0 +1,101 @@
1
+ require 'mspec/ruby_name'
2
+
3
+ # The ruby_exe helper provides a wrapper for invoking the
4
+ # same Ruby interpreter as the one running the specs and
5
+ # getting the output from running the code. If +code+ is a
6
+ # file that exists, it will be run. Otherwise, +code+ should
7
+ # be Ruby code that will be run with the -e command line
8
+ # option. For example:
9
+ #
10
+ # ruby_exe('path/to/some/file.rb')
11
+ #
12
+ # will be executed as
13
+ #
14
+ # `#{RUBY_EXE} #{'path/to/some/file.rb'}`
15
+ #
16
+ # while
17
+ #
18
+ # ruby_exe('puts "hello, world."')
19
+ #
20
+ # will be executed as
21
+ #
22
+ # `#{RUBY_EXE} -e #{'puts "hello, world."'}`
23
+ #
24
+ # The RUBY_EXE constant can be set explicitly since the value
25
+ # is used each time ruby_exe is invoked. The mspec runner script
26
+ # will set ENV['RUBY_EXE'] to the name of the executable used
27
+ # to invoke the mspec-run script. The value of RUBY_EXE will be
28
+ # constructed as follows:
29
+ #
30
+ # 1. the value of ENV['RUBY_EXE']
31
+ # 2. an explicit value based on RUBY_NAME
32
+ # 3. cwd/(RUBY_NAME + $(EXEEXT) || $(exeext) || '')
33
+ # 4. $(bindir)/$(RUBY_INSTALL_NAME)
34
+ #
35
+ # The value will only be used if the file exists and is executable.
36
+ #
37
+ # These 4 ways correspond to the following scenarios:
38
+ #
39
+ # 1. Using the MSpec runner scripts, the name of the
40
+ # executable is explicitly passed by ENV['RUBY_EXE']
41
+ # so there is no ambiguity.
42
+ #
43
+ # Otherwise, if using RSpec (or something else)
44
+ #
45
+ # 2. Running the specs while developing an alternative
46
+ # Ruby implementation. This explicitly names the
47
+ # executable in the development directory based on
48
+ # the value of RUBY_NAME, which is probably initialized
49
+ # from the value of RUBY_ENGINE.
50
+ # 3. Running the specs within the source directory for
51
+ # some implementation. (E.g. a local build directory.)
52
+ # 4. Running the specs against some installed Ruby
53
+ # implementation.
54
+
55
+ class Object
56
+ def ruby_exe_options(option)
57
+ case option
58
+ when :env
59
+ ENV['RUBY_EXE']
60
+ when :engine
61
+ case RUBY_NAME
62
+ when 'rbx'
63
+ "bin/rbx"
64
+ when 'jruby'
65
+ "bin/jruby"
66
+ when 'ironruby'
67
+ "ir"
68
+ end
69
+ when :name
70
+ bin = RUBY_NAME + (Config::CONFIG['EXEEXT'] || Config::CONFIG['exeext'] || '')
71
+ File.join(".", bin)
72
+ when :install_name
73
+ bin = Config::CONFIG["RUBY_INSTALL_NAME"] || Config::CONFIG["ruby_install_name"]
74
+ bin << (Config::CONFIG['EXEEXT'] || Config::CONFIG['exeext'] || '')
75
+ File.join(Config::CONFIG['bindir'], bin)
76
+ end
77
+ end
78
+
79
+ def resolve_ruby_exe
80
+ [:env, :engine, :name, :install_name].each do |option|
81
+ exe = ruby_exe_options option
82
+ return exe if exe and File.exists?(exe) and File.executable?(exe)
83
+ end
84
+ nil
85
+ end
86
+
87
+ def ruby_exe(code)
88
+ if File.exists?(code) and File.executable?(code)
89
+ `#{RUBY_EXE} #{ENV['RUBY_FLAGS']} #{code}`
90
+ else
91
+ `#{RUBY_EXE} #{ENV['RUBY_FLAGS']} -e #{code.inspect}`
92
+ end
93
+ end
94
+
95
+ unless Object.const_defined?(:RUBY_EXE) and RUBY_EXE
96
+ require 'rbconfig'
97
+
98
+ RUBY_EXE = resolve_ruby_exe or
99
+ raise Exception, "Unable to find a suitable ruby executable."
100
+ end
101
+ end
@@ -0,0 +1,8 @@
1
+ unless Object.const_defined?(:RUBY_NAME) and RUBY_NAME
2
+ if Object.const_defined?(:RUBY_ENGINE) and RUBY_ENGINE
3
+ RUBY_NAME = RUBY_ENGINE
4
+ else
5
+ require 'rbconfig'
6
+ RUBY_NAME = Config::CONFIG["RUBY_INSTALL_NAME"] || Config::CONFIG["ruby_install_name"]
7
+ end
8
+ end
@@ -2,5 +2,6 @@ require 'mspec/runner/actions/tally'
2
2
  require 'mspec/runner/actions/timer'
3
3
  require 'mspec/runner/actions/filter'
4
4
  require 'mspec/runner/actions/tag'
5
+ require 'mspec/runner/actions/taglist'
5
6
  require 'mspec/runner/actions/debug'
6
7
  require 'mspec/runner/actions/gdb'
@@ -24,7 +24,7 @@ class ActionFilter
24
24
  @tfilter = nil
25
25
  return if @tags.empty?
26
26
 
27
- desc = MSpec.read_tags(*@tags).map { |t| t.description }
27
+ desc = MSpec.read_tags(@tags).map { |t| t.description }
28
28
  return if desc.empty?
29
29
 
30
30
  @tfilter = MatchFilter.new(nil, *desc)
@@ -0,0 +1,56 @@
1
+ require 'mspec/runner/actions/filter'
2
+
3
+ # TagListAction - prints out the descriptions for any specs
4
+ # tagged with +tags+. If +tags+ is an empty list, prints out
5
+ # descriptions for any specs that are tagged.
6
+ class TagListAction
7
+ def initialize(tags=nil)
8
+ @tags = tags.nil? || tags.empty? ? nil : Array(tags)
9
+ @filter = nil
10
+ end
11
+
12
+ # Returns true. This enables us to match any tag when loading
13
+ # tags from the file.
14
+ def include?(arg)
15
+ true
16
+ end
17
+
18
+ # Returns true if any tagged descriptions matches +string+.
19
+ def ===(string)
20
+ @filter === string
21
+ end
22
+
23
+ # Prints a banner about matching tagged specs.
24
+ def start
25
+ if @tags
26
+ print "\nListing specs tagged with #{@tags.map { |t| "'#{t}'" }.join(", ") }\n\n"
27
+ else
28
+ print "\nListing all tagged specs\n\n"
29
+ end
30
+ end
31
+
32
+ # Creates a MatchFilter for specific tags or for all tags.
33
+ def load
34
+ @filter = nil
35
+ desc = MSpec.read_tags(@tags || self).map { |t| t.description }
36
+ @filter = MatchFilter.new(nil, *desc) unless desc.empty?
37
+ end
38
+
39
+ # Prints the spec description if it matches the filter.
40
+ def after(state)
41
+ return unless self === state.description
42
+ print state.description, "\n"
43
+ end
44
+
45
+ def register
46
+ MSpec.register :start, self
47
+ MSpec.register :load, self
48
+ MSpec.register :after, self
49
+ end
50
+
51
+ def unregister
52
+ MSpec.unregister :start, self
53
+ MSpec.unregister :load, self
54
+ MSpec.unregister :after, self
55
+ end
56
+ end
@@ -7,7 +7,7 @@ class TagFilter
7
7
  end
8
8
 
9
9
  def load
10
- desc = MSpec.read_tags(*@tags).map { |t| t.description }
10
+ desc = MSpec.read_tags(@tags).map { |t| t.description }
11
11
 
12
12
  @filter = MatchFilter.new(@what, *desc)
13
13
  @filter.register
@@ -237,7 +237,9 @@ module MSpec
237
237
  end
238
238
  end
239
239
 
240
- def self.read_tags(*keys)
240
+ # Returns a list of tags matching any tag string in +keys+ based
241
+ # on the return value of <tt>keys.include?("tag_name")</tt>
242
+ def self.read_tags(keys)
241
243
  tags = []
242
244
  file = tags_file
243
245
  if File.exist? file
@@ -1,215 +1,341 @@
1
- # FIXME: There are essentially two options for these runner
2
- # scripts: 1. all in one file, 2. in separate files. The fact
3
- # that the subcommands share a number of options weighs
4
- # toward having a single script. However, the subcommands
5
- # also take specialized options which can be confusing. Added
6
- # to this the support for switches to specify a different
7
- # implementation and the balance swings to separate scripts.
8
- #
9
- # Now, the fun begins. The main script needs a way to specify
10
- # certain command line options and then pass the rest to the
11
- # invoked subscript. Unfortunately, this possibility does not
12
- # exist in the universe inhabited by OptionParser or GetoptLong.
13
- #
14
- # The #filter! method below does not properly handle optional
15
- # arguments to a switch.
16
- class OptionParser
17
- class Switch
18
- def consume?(opt)
19
- if opt == short.to_s or opt == long.to_s
20
- return arg ? 2 : 1
21
- elsif opt[0..1] == short.to_s and opt.size > 2
22
- return 1
1
+ require 'mspec/version'
2
+
3
+ class MSpecOption
4
+ attr_reader :short, :long, :arg, :description, :block
5
+
6
+ def initialize(short, long, arg, description, block)
7
+ @short = short
8
+ @long = long
9
+ @arg = arg
10
+ @description = description
11
+ @block = block
12
+ end
13
+
14
+ def arg?
15
+ @arg != nil
16
+ end
17
+
18
+ def match?(opt)
19
+ opt == @short or opt == @long
20
+ end
21
+ end
22
+
23
+ # MSpecOptions provides a parser for command line options. It also
24
+ # provides a composable set of options from which the runner scripts
25
+ # can select for their particular functionality.
26
+ class MSpecOptions
27
+ # Raised if incorrect or incomplete formats are passed to #on.
28
+ class OptionError < Exception; end
29
+
30
+ # Raised if an unrecognized option is encountered.
31
+ class ParseError < Exception; end
32
+
33
+ attr_accessor :config, :banner, :width, :options
34
+
35
+ def initialize(banner="", width=30, config=nil)
36
+ @banner = banner
37
+ @config = config
38
+ @width = width
39
+ @options = []
40
+ @doc = []
41
+ @extra = []
42
+ @on_extra = lambda { |x|
43
+ raise ParseError, "Unrecognized option: #{x}" if x[0] == ?-
44
+ @extra << x
45
+ }
46
+
47
+ yield self if block_given?
48
+ end
49
+
50
+ # Registers an option. Acceptable formats for arguments are:
51
+ #
52
+ # on "-a", "description"
53
+ # on "-a", "--abdc", "description"
54
+ # on "-a", "ARG", "description"
55
+ # on "--abdc", "ARG", "description"
56
+ # on "-a", "--abdc", "ARG", "description"
57
+ #
58
+ # If an block is passed, it will be invoked when the option is
59
+ # matched. Not passing a block is permitted, but nonsensical.
60
+ def on(*args, &block)
61
+ raise OptionError, "option and description are required" if args.size < 2
62
+
63
+ description = args.pop
64
+ short, long, argument = nil
65
+ args.each do |arg|
66
+ if arg[0] == ?-
67
+ if arg[1] == ?-
68
+ long = arg
69
+ else
70
+ short = arg
71
+ end
23
72
  else
24
- return 0
73
+ argument = arg
25
74
  end
26
75
  end
76
+
77
+ add short, long, argument, description, block
27
78
  end
28
79
 
29
- def filter!(argv)
30
- @stack.inject(options=[]) do |list, entry|
31
- entry.list.each do |switch|
32
- next unless switch.kind_of? OptionParser::Switch
33
- list << switch
80
+ # Adds documentation text for an option and adds an +MSpecOption+
81
+ # instance to the list of registered options.
82
+ def add(short, long, arg, description, block)
83
+ s = short ? short.dup : " "
84
+ s << (short ? ", " : " ") if long
85
+ doc " #{s}#{long} #{arg}".ljust(@width-1) + " #{description}"
86
+ @options << MSpecOption.new(short, long, arg, description, block)
87
+ end
88
+
89
+ # Searches all registered options to find a match for +opt+. Returns
90
+ # +nil+ if no registered options match.
91
+ def match?(opt)
92
+ @options.find { |o| o.match? opt }
93
+ end
94
+
95
+ # Processes an option. Calles the #on_extra block (or default) for
96
+ # unrecognized options. For registered options, possibly fetches an
97
+ # argument and invokes the option's block if it is not nil.
98
+ def process(argv, entry, opt, arg)
99
+ unless option = match?(opt)
100
+ @on_extra[entry]
101
+ else
102
+ if option.arg?
103
+ arg = argv.shift if arg.nil?
104
+ raise ParseError, "No argument provided for #{opt}" unless arg
34
105
  end
35
- list
106
+ option.block[arg] if option.block
36
107
  end
108
+ option
109
+ end
37
110
 
38
- filtered = []
39
- recognized = []
40
- consume = nil
41
- until argv.empty?
42
- opt = argv.shift
43
- options.find { |o| consume = o.consume?(opt); consume != 0 }
44
- if consume == 0
45
- filtered << opt
46
- else
47
- recognized << opt
48
- recognized << argv.shift if consume == 2
111
+ # Splits a string at +n+ characters into the +opt+ and the +rest+.
112
+ # The +arg+ is set to +nil+ if +rest+ is an empty string.
113
+ def split(str, n)
114
+ opt = str[0, n]
115
+ rest = str[n, str.size]
116
+ arg = rest == "" ? nil : rest
117
+ return opt, arg, rest
118
+ end
119
+
120
+ # Parses an array of command line entries, calling blocks for
121
+ # registered options.
122
+ def parse(argv=ARGV)
123
+ argv = Array(argv).dup
124
+
125
+ while entry = argv.shift
126
+ # collect everything that is not an option
127
+ if entry[0] != ?- or entry.size < 2
128
+ @on_extra[entry]
129
+ next
130
+ end
131
+
132
+ # this is a long option
133
+ if entry[1] == ?-
134
+ opt, arg = entry.split "="
135
+ process argv, entry, opt, arg
136
+ next
137
+ end
138
+
139
+ # disambiguate short option group from short option with argument
140
+ opt, arg, rest = split entry, 2
141
+
142
+ # process first option
143
+ option = process argv, entry, opt, arg
144
+ next unless option and not option.arg?
145
+
146
+ # process the rest of the options
147
+ while rest.size > 0
148
+ opt, arg, rest = split rest, 1
149
+ opt = "-" + opt
150
+ option = process argv, opt, opt, arg
151
+ break if option.arg?
49
152
  end
50
153
  end
51
- argv.replace recognized
52
- filtered
154
+
155
+ @extra
156
+ rescue ParseError => e
157
+ puts self
158
+ puts e
159
+ exit 1
53
160
  end
54
- end
55
161
 
56
- require 'mspec/version'
162
+ # Adds a string of documentation text inline in the text generated
163
+ # from the options. See #on and #add.
164
+ def doc(str)
165
+ @doc << str
166
+ end
57
167
 
58
- # MSpecOptions wraps OptionParser and provides a composable set of
59
- # options that the runner scripts pick from.
60
- class MSpecOptions
61
- attr_reader :parser
168
+ # Convenience method for providing -v, --version options.
169
+ def version(version, &block)
170
+ show = block || lambda { puts "#{File.basename $0} #{version}"; exit }
171
+ on "-v", "--version", "Show version", &show
172
+ end
62
173
 
63
- def initialize(config, command, *args)
64
- @parser = OptionParser.new(*args) do |opts|
65
- opts.banner = "mspec #{command} [options] (FILE|DIRECTORY|GLOB)+"
66
- opts.separator ""
67
- end
174
+ # Convenience method for providing -h, --help options.
175
+ def help(&block)
176
+ help = block || lambda { puts self; exit 1 }
177
+ on "-h", "--help", "Show this message", &help
178
+ end
68
179
 
69
- @config = config
180
+ # Stores a block that will be called with unrecognized options
181
+ def on_extra(&block)
182
+ @on_extra = block
70
183
  end
71
184
 
72
- def add_config(&block)
73
- on("-B", "--config FILE", String,
185
+ # Returns a string representation of the options and doc strings.
186
+ def to_s
187
+ @banner + "\n\n" + @doc.join("\n") + "\n"
188
+ end
189
+
190
+ # The methods below provide groups of options that
191
+ # are composed by the particular runners to provide
192
+ # their functionality
193
+
194
+ def configure(&block)
195
+ on("-B", "--config", "FILE",
74
196
  "Load FILE containing configuration options", &block)
75
197
  end
76
198
 
77
- def add_name
78
- on("-n", "--name RUBY_NAME", String,
199
+ def name
200
+ on("-n", "--name", "RUBY_NAME",
79
201
  "Set the value of RUBY_NAME (used to determine the implementation)") do |n|
80
202
  Object.const_set :RUBY_NAME, n
81
203
  end
82
204
  end
83
205
 
84
- def add_targets
85
- on("-t", "--target TARGET", String,
206
+ def targets
207
+ on("-t", "--target", "TARGET",
86
208
  "Implementation to run the specs, where:") do |t|
87
209
  case t
88
210
  when 'r', 'ruby'
89
- @config[:target] = 'ruby'
90
- @config[:flags] << '-v'
211
+ config[:target] = 'ruby'
91
212
  when 'r19', 'ruby19'
92
- @config[:target] = 'ruby19'
213
+ config[:target] = 'ruby19'
93
214
  when 'x', 'rubinius'
94
- @config[:target] = 'shotgun/rubinius'
215
+ config[:target] = './bin/rbx'
95
216
  when 'X', 'rbx'
96
- @config[:target] = 'rbx'
217
+ config[:target] = 'rbx'
97
218
  when 'j', 'jruby'
98
- @config[:target] = 'jruby'
219
+ config[:target] = 'jruby'
220
+ when 'i','ironruby'
221
+ config[:target] = 'ir'
99
222
  else
100
- @config[:target] = t
223
+ config[:target] = t
101
224
  end
102
225
  end
103
226
 
104
- separator ""
105
- separator " 'r' or 'ruby' invokes ruby in PATH"
106
- separator " 'r19' or 'ruby19' invokes ruby19 in PATH"
107
- separator " 'x' or 'rubinius' invokes ./shotgun/rubinius"
108
- separator " 'X' or 'rbx' invokes rbx in PATH"
109
- separator " 'j' or 'jruby' invokes jruby in PATH\n"
227
+ doc ""
228
+ doc " r or ruby invokes ruby in PATH"
229
+ doc " r19 or ruby19 invokes ruby19 in PATH"
230
+ doc " x or rubinius invokes ./bin/rbx"
231
+ doc " X or rbx invokes rbx in PATH"
232
+ doc " j or jruby invokes jruby in PATH"
233
+ doc " i or ironruby invokes ir in PATH\n"
110
234
 
111
- on("-T", "--target-opt OPT", String,
235
+ on("-T", "--target-opt", "OPT",
112
236
  "Pass OPT as a flag to the target implementation") do |t|
113
- @config[:flags] << t
237
+ config[:flags] << t
114
238
  end
115
- on("-I", "--include DIR", String,
239
+ on("-I", "--include", "DIR",
116
240
  "Pass DIR through as the -I option to the target") do |d|
117
- @config[:includes] << "-I#{d}"
241
+ config[:includes] << "-I#{d}"
118
242
  end
119
- on("-r", "--require LIBRARY", String,
243
+ on("-r", "--require", "LIBRARY",
120
244
  "Pass LIBRARY through as the -r option to the target") do |f|
121
- @config[:requires] << "-r#{f}"
245
+ config[:requires] << "-r#{f}"
122
246
  end
123
247
  end
124
248
 
125
- def add_formatters
126
- on("-f", "--format FORMAT", String,
249
+ def formatters
250
+ on("-f", "--format", "FORMAT",
127
251
  "Formatter for reporting, where FORMAT is one of:") do |o|
128
252
  case o
129
253
  when 's', 'spec', 'specdoc'
130
- @config[:formatter] = SpecdocFormatter
254
+ config[:formatter] = SpecdocFormatter
131
255
  when 'h', 'html'
132
- @config[:formatter] = HtmlFormatter
256
+ config[:formatter] = HtmlFormatter
133
257
  when 'd', 'dot', 'dotted'
134
- @config[:formatter] = DottedFormatter
258
+ config[:formatter] = DottedFormatter
135
259
  when 'u', 'unit', 'unitdiff'
136
- @config[:formatter] = UnitdiffFormatter
260
+ config[:formatter] = UnitdiffFormatter
137
261
  when 'm', 'summary'
138
- @config[:formatter] = SummaryFormatter
262
+ config[:formatter] = SummaryFormatter
139
263
  when 'a', '*', 'spin'
140
- @config[:formatter] = SpinnerFormatter
264
+ config[:formatter] = SpinnerFormatter
141
265
  when 'y', 'yaml'
142
- @config[:formatter] = YamlFormatter
266
+ config[:formatter] = YamlFormatter
143
267
  else
144
268
  puts "Unknown format: #{o}"
145
269
  puts @parser
146
270
  exit
147
271
  end
148
272
  end
149
- separator("")
150
- separator(" s, spec, specdoc SpecdocFormatter")
151
- separator(" h, html, HtmlFormatter")
152
- separator(" d, dot, dotted DottedFormatter")
153
- separator(" u, unit, unitdiff UnitdiffFormatter")
154
- separator(" m, summary SummaryFormatter")
155
- separator(" a, *, spin SpinnerFormatter")
156
- separator(" y, yaml YamlFormatter\n")
157
- on("-o", "--output FILE", String,
273
+
274
+ doc ""
275
+ doc " s, spec, specdoc SpecdocFormatter"
276
+ doc " h, html, HtmlFormatter"
277
+ doc " d, dot, dotted DottedFormatter"
278
+ doc " u, unit, unitdiff UnitdiffFormatter"
279
+ doc " m, summary SummaryFormatter"
280
+ doc " a, *, spin SpinnerFormatter"
281
+ doc " y, yaml YamlFormatter\n"
282
+
283
+ on("-o", "--output", "FILE",
158
284
  "Write formatter output to FILE") do |f|
159
- @config[:output] = f
285
+ config[:output] = f
160
286
  end
161
287
  end
162
288
 
163
- def add_filters
164
- on("-e", "--example STR", String,
289
+ def filters
290
+ on("-e", "--example", "STR",
165
291
  "Run examples with descriptions matching STR") do |o|
166
- @config[:includes] << o
292
+ config[:includes] << o
167
293
  end
168
- on("-E", "--exclude STR", String,
294
+ on("-E", "--exclude", "STR",
169
295
  "Exclude examples with descriptions matching STR") do |o|
170
- @config[:excludes] << o
296
+ config[:excludes] << o
171
297
  end
172
- on("-p", "--pattern PATTERN", Regexp,
298
+ on("-p", "--pattern", "PATTERN",
173
299
  "Run examples with descriptions matching PATTERN") do |o|
174
- @config[:patterns] << o
300
+ config[:patterns] << Regexp.new(o)
175
301
  end
176
- on("-P", "--excl-pattern PATTERN", Regexp,
302
+ on("-P", "--excl-pattern", "PATTERN",
177
303
  "Exclude examples with descriptions matching PATTERN") do |o|
178
- @config[:xpatterns] << o
304
+ config[:xpatterns] << Regexp.new(o)
179
305
  end
180
- on("-g", "--tag TAG", String,
306
+ on("-g", "--tag", "TAG",
181
307
  "Run examples with descriptions matching ones tagged with TAG") do |o|
182
- @config[:tags] << o
308
+ config[:tags] << o
183
309
  end
184
- on("-G", "--excl-tag TAG", String,
310
+ on("-G", "--excl-tag", "TAG",
185
311
  "Exclude examples with descriptions matching ones tagged with TAG") do |o|
186
- @config[:xtags] << o
312
+ config[:xtags] << o
187
313
  end
188
- on("-w", "--profile FILE", String,
314
+ on("-w", "--profile", "FILE",
189
315
  "Run examples for methods listed in the profile FILE") do |f|
190
- @config[:profiles] << f
316
+ config[:profiles] << f
191
317
  end
192
- on("-W", "--excl-profile FILE", String,
318
+ on("-W", "--excl-profile", "FILE",
193
319
  "Exclude examples for methods listed in the profile FILE") do |f|
194
- @config[:xprofiles] << f
320
+ config[:xprofiles] << f
195
321
  end
196
322
  end
197
323
 
198
- def add_pretend
324
+ def pretend
199
325
  on("-Z", "--dry-run",
200
326
  "Invoke formatters and other actions, but don't execute the specs") do
201
327
  MSpec.register_mode :pretend
202
328
  end
203
329
  end
204
330
 
205
- def add_randomize
331
+ def randomize
206
332
  on("-H", "--random",
207
333
  "Randomize the list of spec files") do
208
334
  MSpec.randomize
209
335
  end
210
336
  end
211
337
 
212
- def add_verbose
338
+ def verbose
213
339
  on("-V", "--verbose", "Output the name of each file processed") do
214
340
  obj = Object.new
215
341
  def obj.start
@@ -223,7 +349,7 @@ class MSpecOptions
223
349
  MSpec.register :load, obj
224
350
  end
225
351
 
226
- on("-m", "--marker MARKER", String,
352
+ on("-m", "--marker", "MARKER",
227
353
  "Output MARKER for each file processed") do |o|
228
354
  obj = Object.new
229
355
  obj.instance_variable_set :@marker, o
@@ -234,104 +360,41 @@ class MSpecOptions
234
360
  end
235
361
  end
236
362
 
237
- def add_interrupt
363
+ def interrupt
238
364
  on("--int-spec", "Control-C interupts the current spec only") do
239
- @config[:abort] = false
365
+ config[:abort] = false
240
366
  end
241
367
  end
242
368
 
243
- def add_verify
369
+ def verify
244
370
  on("-Y", "--verify",
245
371
  "Verify that guarded specs pass and fail as expected") do
246
- MSpec.set_mode :verify
372
+ MSpec.register_mode :verify
247
373
  end
248
374
  on("-O", "--report", "Report guarded specs") do
249
- MSpec.set_mode :report
250
- end
251
- end
252
-
253
- def add_tagging
254
- on("-N", "--add TAG", String,
255
- "Add TAG with format 'tag' or 'tag(comment)' (see -Q, -F, -L)") do |o|
256
- @config[:tagger] = :add
257
- @config[:tag] = "#{o}:"
258
- end
259
- on("-R", "--del TAG", String,
260
- "Delete TAG (see -Q, -F, -L)") do |o|
261
- @config[:tagger] = :del
262
- @config[:tag] = "#{o}:"
263
- @config[:outcome] = :pass
264
- end
265
- on("-Q", "--pass", "Apply action to specs that pass (default for --del)") do
266
- @config[:outcome] = :pass
267
- end
268
- on("-F", "--fail", "Apply action to specs that fail (default for --add)") do
269
- @config[:outcome] = :fail
270
- end
271
- on("-L", "--all", "Apply action to all specs") do
272
- @config[:outcome] = :all
375
+ MSpec.register_mode :report
273
376
  end
274
377
  end
275
378
 
276
- def add_action_filters
277
- on("-K", "--action-tag TAG", String,
379
+ def action_filters
380
+ on("-K", "--action-tag", "TAG",
278
381
  "Spec descriptions marked with TAG will trigger the specified action") do |o|
279
- @config[:atags] << o
382
+ config[:atags] << o
280
383
  end
281
- on("-S", "--action-string STR", String,
384
+ on("-S", "--action-string", "STR",
282
385
  "Spec descriptions matching STR will trigger the specified action") do |o|
283
- @config[:astrings] << o
386
+ config[:astrings] << o
284
387
  end
285
388
  end
286
389
 
287
- def add_actions
390
+ def actions
288
391
  on("--spec-debug",
289
392
  "Invoke the debugger when a spec description matches (see -K, -S)") do
290
- @config[:debugger] = true
393
+ config[:debugger] = true
291
394
  end
292
395
  on("--spec-gdb",
293
396
  "Invoke Gdb when a spec description matches (see -K, -S)") do
294
- @config[:gdb] = true
397
+ config[:gdb] = true
295
398
  end
296
399
  end
297
-
298
- def add_version
299
- on("-v", "--version", "Show version") do
300
- puts "#{File.basename $0} #{MSpec::VERSION}"
301
- exit
302
- end
303
- end
304
-
305
- def add_help
306
- on("-h", "--help", "Show this message") do
307
- puts @parser
308
- exit
309
- end
310
- end
311
-
312
- def on(*args, &block)
313
- @parser.on(*args, &block)
314
- end
315
-
316
- def separator(str)
317
- @parser.separator str
318
- end
319
-
320
- def parse(argv=ARGV)
321
- result = @parser.parse argv
322
-
323
- if (@config[:debugger] || @config[:gdb]) &&
324
- @config[:atags].empty? && @config[:astrings].empty?
325
- puts "Missing --action-tag or --action-string."
326
- puts @parser
327
- exit 1
328
- end
329
-
330
- result
331
- rescue OptionParser::ParseError => e
332
- puts @parser
333
- puts
334
- puts e
335
- exit 1
336
- end
337
400
  end