wycats-thor 0.9.8 → 0.10.26
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +26 -3
- data/README.markdown +213 -42
- data/bin/rake2thor +4 -0
- data/bin/thor +1 -1
- data/lib/thor/actions/copy_file.rb +32 -0
- data/lib/thor/actions/create_file.rb +49 -0
- data/lib/thor/actions/directory.rb +76 -0
- data/lib/thor/actions/empty_directory.rb +30 -0
- data/lib/thor/actions/get.rb +58 -0
- data/lib/thor/actions/inject_into_file.rb +93 -0
- data/lib/thor/actions/template.rb +38 -0
- data/lib/thor/actions/templater.rb +195 -0
- data/lib/thor/actions.rb +328 -0
- data/lib/thor/base.rb +520 -0
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +75 -0
- data/lib/thor/core_ext/ordered_hash.rb +102 -0
- data/lib/thor/error.rb +25 -1
- data/lib/thor/group.rb +72 -0
- data/lib/thor/invocation.rb +161 -0
- data/lib/thor/parser/argument.rb +67 -0
- data/lib/thor/parser/arguments.rb +145 -0
- data/lib/thor/parser/option.rb +125 -0
- data/lib/thor/parser/options.rb +135 -0
- data/lib/thor/parser.rb +4 -0
- data/lib/thor/runner.rb +239 -230
- data/lib/thor/shell/basic.rb +221 -0
- data/lib/thor/shell/color.rb +106 -0
- data/lib/thor/shell.rb +72 -0
- data/lib/thor/task.rb +89 -60
- data/lib/thor/tasks/install.rb +35 -0
- data/lib/thor/tasks/package.rb +26 -13
- data/lib/thor/tasks/spec.rb +70 -0
- data/lib/thor/tasks.rb +3 -76
- data/lib/thor/util.rb +213 -41
- data/lib/thor.rb +206 -118
- metadata +38 -13
- data/lib/thor/options.rb +0 -242
- data/lib/thor/ordered_hash.rb +0 -64
- data/lib/thor/task_hash.rb +0 -22
@@ -0,0 +1,35 @@
|
|
1
|
+
class Thor
|
2
|
+
# Creates an install task.
|
3
|
+
#
|
4
|
+
# ==== Parameters
|
5
|
+
# spec<Gem::Specification>
|
6
|
+
#
|
7
|
+
# ==== Options
|
8
|
+
# :dir - The directory where the package is hold before installation. Defaults to ./pkg.
|
9
|
+
#
|
10
|
+
def self.install_task(spec, options={})
|
11
|
+
package_task(spec, options)
|
12
|
+
tasks['install'] = Thor::InstallTask.new(spec, options)
|
13
|
+
end
|
14
|
+
|
15
|
+
class InstallTask < Task
|
16
|
+
attr_accessor :spec, :config
|
17
|
+
|
18
|
+
def initialize(gemspec, config={})
|
19
|
+
super(:install, "Install the gem", "install", {})
|
20
|
+
@spec = gemspec
|
21
|
+
@config = { :dir => File.join(Dir.pwd, "pkg") }.merge(config)
|
22
|
+
end
|
23
|
+
|
24
|
+
def run(instance, args=[])
|
25
|
+
null, sudo, gem = RUBY_PLATFORM =~ /mswin|mingw/ ? ['NUL', '', 'gem.bat'] :
|
26
|
+
['/dev/null', 'sudo', 'gem']
|
27
|
+
|
28
|
+
old_stderr, $stderr = $stderr.dup, File.open(null, "w")
|
29
|
+
instance.invoke(:package)
|
30
|
+
$stderr = old_stderr
|
31
|
+
|
32
|
+
system %{#{sudo} #{Gem.ruby} -S #{gem} install #{config[:dir]}/#{spec.name}-#{spec.version} --no-rdoc --no-ri --no-update-sources}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/thor/tasks/package.rb
CHANGED
@@ -1,18 +1,31 @@
|
|
1
|
-
require "
|
1
|
+
require "fileutils"
|
2
2
|
|
3
|
-
class Thor
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
3
|
+
class Thor
|
4
|
+
# Creates a package task.
|
5
|
+
#
|
6
|
+
# ==== Parameters
|
7
|
+
# spec<Gem::Specification>
|
8
|
+
#
|
9
|
+
# ==== Options
|
10
|
+
# :dir - The package directory. Defaults to ./pkg.
|
11
|
+
#
|
12
|
+
def self.package_task(spec, options={})
|
13
|
+
tasks['package'] = Thor::PackageTask.new(spec, options)
|
11
14
|
end
|
12
15
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
16
|
+
class PackageTask < Task
|
17
|
+
attr_accessor :spec, :config
|
18
|
+
|
19
|
+
def initialize(gemspec, config={})
|
20
|
+
super(:package, "Build a gem package", "package", {})
|
21
|
+
@spec = gemspec
|
22
|
+
@config = {:dir => File.join(Dir.pwd, "pkg")}.merge(config)
|
23
|
+
end
|
24
|
+
|
25
|
+
def run(instance, args=[])
|
26
|
+
FileUtils.mkdir_p(config[:dir])
|
27
|
+
Gem::Builder.new(spec).build
|
28
|
+
FileUtils.mv(spec.file_name, File.join(config[:dir], spec.file_name))
|
29
|
+
end
|
17
30
|
end
|
18
31
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
|
3
|
+
class Thor
|
4
|
+
# Creates a spec task.
|
5
|
+
#
|
6
|
+
# ==== Parameters
|
7
|
+
# files<Array> - Array of files to spec
|
8
|
+
#
|
9
|
+
# ==== Options
|
10
|
+
# :name - The name of the task. It can be rcov or spec. Spec is the default.
|
11
|
+
# :rcov - A hash with rcov specific options.
|
12
|
+
# :rcov_dir - Where rcov reports should be printed.
|
13
|
+
# :verbose - Sets the default value for verbose, although it can be specified
|
14
|
+
# also through the command line.
|
15
|
+
#
|
16
|
+
# All other options are added to rspec.
|
17
|
+
#
|
18
|
+
def self.spec_task(files, options={})
|
19
|
+
name = (options.delete(:name) || 'spec').to_s
|
20
|
+
tasks[name] = Thor::SpecTask.new(name, files, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
class SpecTask < Task
|
24
|
+
attr_accessor :name, :files, :rcov_dir, :rcov_config, :spec_config
|
25
|
+
|
26
|
+
def initialize(name, files, config={})
|
27
|
+
options = { :verbose => Thor::Option.parse(:verbose, config.delete(:verbose) || false) }
|
28
|
+
super(name, "#{name.capitalize} task", name, options)
|
29
|
+
|
30
|
+
@name = name
|
31
|
+
@files = files.map{ |f| %["#{f}"] }.join(" ")
|
32
|
+
@rcov_dir = config.delete(:rdoc_dir) || File.join(Dir.pwd, 'coverage')
|
33
|
+
@rcov_config = config.delete(:rcov) || {}
|
34
|
+
@spec_config = { :format => 'specdoc', :color => true }.merge(config)
|
35
|
+
end
|
36
|
+
|
37
|
+
def run(instance, args=[])
|
38
|
+
rcov_opts = Thor::Options.to_switches(rcov_config)
|
39
|
+
spec_opts = Thor::Options.to_switches(spec_config)
|
40
|
+
|
41
|
+
require 'rbconfig'
|
42
|
+
cmd = RbConfig::CONFIG['ruby_install_name'] << " "
|
43
|
+
|
44
|
+
if rcov?
|
45
|
+
FileUtils.rm_rf(rcov_dir)
|
46
|
+
cmd << "-S #{where('rcov')} -o #{rcov_dir} #{rcov_opts} "
|
47
|
+
end
|
48
|
+
|
49
|
+
cmd << [where('spec'), rcov? ? " -- " : nil, files, spec_opts].join(" ")
|
50
|
+
|
51
|
+
puts cmd if instance.options.verbose?
|
52
|
+
system(cmd)
|
53
|
+
exit($?.exitstatus)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def rcov?
|
59
|
+
name == "rcov"
|
60
|
+
end
|
61
|
+
|
62
|
+
def where(file)
|
63
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
64
|
+
file_with_path = File.join(path, file)
|
65
|
+
next unless File.exist?(file_with_path) && File.executable?(file_with_path)
|
66
|
+
return File.expand_path(file_with_path)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/thor/tasks.rb
CHANGED
@@ -1,77 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
class Thor
|
5
|
-
def self.package_task(spec)
|
6
|
-
desc "package", "package up the gem"
|
7
|
-
define_method :package do
|
8
|
-
FileUtils.mkdir_p(File.join(Dir.pwd, "pkg"))
|
9
|
-
Gem::Builder.new(spec).build
|
10
|
-
FileUtils.mv(spec.file_name, File.join(Dir.pwd, "pkg", spec.file_name))
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.install_task(spec)
|
15
|
-
package_task spec
|
16
|
-
|
17
|
-
null, sudo, gem = RUBY_PLATFORM =~ /w(in)?32$/ ? ['NUL', '', 'gem.bat'] :
|
18
|
-
['/dev/null', 'sudo', 'gem']
|
19
|
-
|
20
|
-
desc "install", "install the gem"
|
21
|
-
define_method :install do
|
22
|
-
old_stderr, $stderr = $stderr.dup, File.open(null, "w")
|
23
|
-
package
|
24
|
-
$stderr = old_stderr
|
25
|
-
system %{#{sudo} #{gem} install pkg/#{spec.name}-#{spec.version} --no-rdoc --no-ri --no-update-sources}
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.spec_task(file_list, opts = {})
|
30
|
-
name = opts.delete(:name) || "spec"
|
31
|
-
rcov_dir = opts.delete(:rcov_dir) || "coverage"
|
32
|
-
file_list = file_list.map {|f| %["#{f}"]}.join(" ")
|
33
|
-
verbose = opts.delete(:verbose)
|
34
|
-
opts = {:format => "specdoc", :color => true}.merge(opts)
|
35
|
-
|
36
|
-
rcov_opts = convert_task_options(opts.delete(:rcov) || {})
|
37
|
-
rcov = !rcov_opts.empty?
|
38
|
-
options = convert_task_options(opts)
|
39
|
-
|
40
|
-
if rcov
|
41
|
-
FileUtils.rm_rf(File.join(Dir.pwd, rcov_dir))
|
42
|
-
end
|
43
|
-
|
44
|
-
desc(name, "spec task")
|
45
|
-
define_method(name) do
|
46
|
-
cmd = "ruby "
|
47
|
-
if rcov
|
48
|
-
cmd << "-S rcov -o #{rcov_dir} #{rcov_opts} "
|
49
|
-
end
|
50
|
-
cmd << `which spec`.chomp
|
51
|
-
cmd << " -- " if rcov
|
52
|
-
cmd << " "
|
53
|
-
cmd << file_list
|
54
|
-
cmd << " "
|
55
|
-
cmd << options
|
56
|
-
puts cmd if verbose
|
57
|
-
system(cmd)
|
58
|
-
exit($?.exitstatus)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
private
|
63
|
-
def self.convert_task_options(opts)
|
64
|
-
opts.map do |key, value|
|
65
|
-
case value
|
66
|
-
when true
|
67
|
-
"--#{key}"
|
68
|
-
when Array
|
69
|
-
value.map {|v| "--#{key} #{v.inspect}"}.join(" ")
|
70
|
-
when nil, false
|
71
|
-
""
|
72
|
-
else
|
73
|
-
"--#{key} #{value.inspect}"
|
74
|
-
end
|
75
|
-
end.join(" ")
|
76
|
-
end
|
1
|
+
# This only loads all tasks inside tasks.
|
2
|
+
Dir[File.join(File.dirname(__FILE__), "tasks", "*.rb")].each do |task|
|
3
|
+
require task
|
77
4
|
end
|
data/lib/thor/util.rb
CHANGED
@@ -1,57 +1,229 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
module ObjectSpace
|
4
|
-
|
5
|
-
class << self
|
6
|
-
|
7
|
-
# @return <Array[Class]> All the classes in the object space.
|
8
|
-
def classes
|
9
|
-
klasses = []
|
10
|
-
ObjectSpace.each_object(Class) {|o| klasses << o}
|
11
|
-
klasses
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
end
|
1
|
+
require 'rbconfig'
|
16
2
|
|
17
3
|
class Thor
|
4
|
+
module Sandbox; end
|
5
|
+
|
6
|
+
# This module holds several utilities:
|
7
|
+
#
|
8
|
+
# 1) Methods to convert thor namespaces to constants and vice-versa.
|
9
|
+
#
|
10
|
+
# Thor::Utils.constant_to_namespace(Foo::Bar::Baz) #=> "foo:bar:baz"
|
11
|
+
# Thor::Utils.namespace_to_constant("foo:bar:baz") #=> Foo::Bar::Baz
|
12
|
+
#
|
13
|
+
# 2) Loading thor files and sandboxing:
|
14
|
+
#
|
15
|
+
# Thor::Utils.load_thorfile("~/.thor/foo")
|
16
|
+
#
|
18
17
|
module Util
|
19
|
-
|
20
|
-
def self.constant_to_thor_path(str, remove_default = true)
|
21
|
-
str = snake_case(str.to_s).squeeze(":")
|
22
|
-
str.gsub!(/^default/, '') if remove_default
|
23
|
-
str
|
24
|
-
end
|
25
18
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
19
|
+
# Receives a namespace and search for it in the Thor::Base subclasses.
|
20
|
+
#
|
21
|
+
# ==== Parameters
|
22
|
+
# namespace<String>:: The namespace to search for.
|
23
|
+
#
|
24
|
+
def self.find_by_namespace(namespace)
|
25
|
+
namespace = 'default' if namespace.empty?
|
32
26
|
|
33
|
-
|
34
|
-
|
35
|
-
|
27
|
+
Thor::Base.subclasses.find do |klass|
|
28
|
+
klass.namespace == namespace
|
29
|
+
end
|
36
30
|
end
|
37
31
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
32
|
+
# Receives a constant and converts it to a Thor namespace. Since Thor tasks
|
33
|
+
# can be added to a sandbox, this method is also responsable for removing
|
34
|
+
# the sandbox namespace.
|
35
|
+
#
|
36
|
+
# This method should not be used in general because it's used to deal with
|
37
|
+
# older versions of Thor. On current versions, if you need to get the
|
38
|
+
# namespace from a class, just call namespace on it.
|
39
|
+
#
|
40
|
+
# ==== Parameters
|
41
|
+
# constant<Object>:: The constant to be converted to the thor path.
|
42
|
+
#
|
43
|
+
# ==== Returns
|
44
|
+
# String:: If we receive Foo::Bar::Baz it returns "foo:bar:baz"
|
45
|
+
#
|
46
|
+
def self.constant_to_namespace(constant, remove_default=true)
|
47
|
+
constant = constant.to_s.gsub(/^Thor::Sandbox::/, "")
|
48
|
+
constant = snake_case(constant).squeeze(":")
|
49
|
+
constant.gsub!(/^default/, '') if remove_default
|
50
|
+
constant
|
44
51
|
end
|
45
52
|
|
46
|
-
|
47
|
-
|
53
|
+
# Given the contents, evaluate it inside the sandbox and returns the thor
|
54
|
+
# classes defined in the sandbox.
|
55
|
+
#
|
56
|
+
# ==== Parameters
|
57
|
+
# contents<String>
|
58
|
+
#
|
59
|
+
# ==== Returns
|
60
|
+
# Array[Object]
|
61
|
+
#
|
62
|
+
def self.namespaces_in_contents(contents, file=__FILE__)
|
63
|
+
old_constants = Thor::Base.subclasses.dup
|
64
|
+
Thor::Base.subclasses.clear
|
65
|
+
|
66
|
+
load_thorfile(file, contents)
|
67
|
+
|
68
|
+
new_constants = Thor::Base.subclasses.dup
|
69
|
+
Thor::Base.subclasses.replace(old_constants)
|
70
|
+
|
71
|
+
new_constants.map!{ |c| c.namespace }
|
72
|
+
new_constants.compact!
|
73
|
+
new_constants
|
48
74
|
end
|
49
|
-
|
75
|
+
|
76
|
+
# Receives a string and convert it to snake case. SnakeCase returns snake_case.
|
77
|
+
#
|
78
|
+
# ==== Parameters
|
79
|
+
# String
|
80
|
+
#
|
81
|
+
# ==== Returns
|
82
|
+
# String
|
83
|
+
#
|
50
84
|
def self.snake_case(str)
|
51
85
|
return str.downcase if str =~ /^[A-Z_]+$/
|
52
86
|
str.gsub(/\B[A-Z]/, '_\&').squeeze('_') =~ /_*(.*)/
|
53
87
|
return $+.downcase
|
54
|
-
end
|
55
|
-
|
88
|
+
end
|
89
|
+
|
90
|
+
# Receives a namespace and tries to retrieve a Thor or Thor::Group class
|
91
|
+
# from it. It first searches for a class using the all the given namespace,
|
92
|
+
# if it's not found, removes the highest entry and searches for the class
|
93
|
+
# again. If found, returns the highest entry as the class name.
|
94
|
+
#
|
95
|
+
# ==== Examples
|
96
|
+
#
|
97
|
+
# class Foo::Bar < Thor
|
98
|
+
# def baz
|
99
|
+
# end
|
100
|
+
# end
|
101
|
+
#
|
102
|
+
# class Baz::Foo < Thor::Group
|
103
|
+
# end
|
104
|
+
#
|
105
|
+
# Thor::Util.namespace_to_thor_class("foo:bar") #=> Foo::Bar, nil # will invoke default task
|
106
|
+
# Thor::Util.namespace_to_thor_class("baz:foo") #=> Baz::Foo, nil
|
107
|
+
# Thor::Util.namespace_to_thor_class("foo:bar:baz") #=> Foo::Bar, "baz"
|
108
|
+
#
|
109
|
+
# ==== Parameters
|
110
|
+
# namespace<String>
|
111
|
+
#
|
112
|
+
# ==== Errors
|
113
|
+
# Thor::Error:: raised if the namespace cannot be found.
|
114
|
+
#
|
115
|
+
# Thor::Error:: raised if the namespace evals to a class which does not
|
116
|
+
# inherit from Thor or Thor::Group.
|
117
|
+
#
|
118
|
+
def self.namespace_to_thor_class(namespace, raise_if_nil=true)
|
119
|
+
klass, task_name = Thor::Util.find_by_namespace(namespace), nil
|
120
|
+
|
121
|
+
if klass.nil? && namespace.include?(?:)
|
122
|
+
namespace = namespace.split(":")
|
123
|
+
task_name = namespace.pop
|
124
|
+
klass = Thor::Util.find_by_namespace(namespace.join(":"))
|
125
|
+
end
|
126
|
+
|
127
|
+
raise Error, "could not find Thor class or task '#{namespace}'" if raise_if_nil && klass.nil?
|
128
|
+
|
129
|
+
return klass, task_name
|
130
|
+
end
|
131
|
+
|
132
|
+
# Receives a path and load the thor file in the path. The file is evaluated
|
133
|
+
# inside the sandbox to avoid namespacing conflicts.
|
134
|
+
#
|
135
|
+
def self.load_thorfile(path, content=nil)
|
136
|
+
content ||= File.read(path)
|
137
|
+
|
138
|
+
begin
|
139
|
+
Thor::Sandbox.class_eval(content, path)
|
140
|
+
rescue Exception => e
|
141
|
+
$stderr.puts "WARNING: unable to load thorfile #{path.inspect}: #{e.message}"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Receives a yaml (hash) and updates all constants entries to namespace.
|
146
|
+
# This was added to deal with deprecated versions of Thor.
|
147
|
+
#
|
148
|
+
# TODO Deprecate this method in the future.
|
149
|
+
#
|
150
|
+
# ==== Returns
|
151
|
+
# TrueClass|FalseClass:: Returns true if any change to the yaml file was made.
|
152
|
+
#
|
153
|
+
def self.convert_constants_to_namespaces(yaml)
|
154
|
+
yaml_changed = false
|
155
|
+
|
156
|
+
yaml.each do |k, v|
|
157
|
+
next unless v[:constants] && v[:namespaces].nil?
|
158
|
+
yaml_changed = true
|
159
|
+
yaml[k][:namespaces] = v[:constants].map{|c| Thor::Util.constant_to_namespace(c)}
|
160
|
+
end
|
161
|
+
|
162
|
+
yaml_changed
|
163
|
+
end
|
164
|
+
|
165
|
+
def self.user_home
|
166
|
+
@@user_home ||= if ENV["HOME"]
|
167
|
+
ENV["HOME"]
|
168
|
+
elsif ENV["USERPROFILE"]
|
169
|
+
ENV["USERPROFILE"]
|
170
|
+
elsif ENV["HOMEDRIVE"] && ENV["HOMEPATH"]
|
171
|
+
File.join(ENV["HOMEDRIVE"], ENV["HOMEPATH"])
|
172
|
+
elsif ENV["APPDATA"]
|
173
|
+
ENV["APPDATA"]
|
174
|
+
else
|
175
|
+
begin
|
176
|
+
File.expand_path("~")
|
177
|
+
rescue
|
178
|
+
if File::ALT_SEPARATOR
|
179
|
+
"C:/"
|
180
|
+
else
|
181
|
+
"/"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Returns the root where thor files are located, dependending on the OS.
|
188
|
+
#
|
189
|
+
def self.thor_root
|
190
|
+
File.join(user_home, ".thor")
|
191
|
+
end
|
192
|
+
|
193
|
+
# Returns the files in the thor root. On Windows thor_root will be something
|
194
|
+
# like this:
|
195
|
+
#
|
196
|
+
# C:\Documents and Settings\james\.thor
|
197
|
+
#
|
198
|
+
# If we don't #gsub the \ character, Dir.glob will fail.
|
199
|
+
#
|
200
|
+
def self.thor_root_glob
|
201
|
+
files = Dir["#{thor_root.gsub(/\\/, '/')}/*"]
|
202
|
+
|
203
|
+
files.map! do |file|
|
204
|
+
File.directory?(file) ? File.join(file, "main.thor") : file
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# Where to look for Thor files.
|
209
|
+
#
|
210
|
+
def self.globs_for(path)
|
211
|
+
["#{path}/Thorfile", "#{path}/*.thor", "#{path}/tasks/*.thor", "#{path}/lib/tasks/*.thor"]
|
212
|
+
end
|
213
|
+
|
214
|
+
# Return the path to the ruby interpreter taking into account multiple
|
215
|
+
# installations and windows extensions.
|
216
|
+
#
|
217
|
+
def self.ruby_command #:nodoc:
|
218
|
+
@ruby_command ||= begin
|
219
|
+
ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
|
220
|
+
ruby << Config::CONFIG['EXEEXT']
|
221
|
+
|
222
|
+
# escape string in case path to ruby executable contain spaces.
|
223
|
+
ruby.sub!(/.*\s.*/m, '"\&"')
|
224
|
+
ruby
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
56
228
|
end
|
57
229
|
end
|