detroit 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.ruby +45 -0
- data/COPYING.rdoc +19 -0
- data/EXAMPLE.md +188 -0
- data/GPL3.txt +675 -0
- data/HISTORY.rdoc +14 -0
- data/README.rdoc +139 -0
- data/bin/detroit +9 -0
- data/lib/detroit.rb +67 -0
- data/lib/detroit.yml +45 -0
- data/lib/detroit/application.rb +427 -0
- data/lib/detroit/assembly.rb +80 -0
- data/lib/detroit/config.rb +197 -0
- data/lib/detroit/control.rb +124 -0
- data/lib/detroit/core_ext.rb +139 -0
- data/lib/detroit/custom.rb +65 -0
- data/lib/detroit/dsl.rb +55 -0
- data/lib/detroit/schedule.rb +187 -0
- data/lib/detroit/service.rb +188 -0
- data/lib/detroit/standard_assembly.rb +52 -0
- data/lib/detroit/tool.rb +216 -0
- data/lib/detroit/tool/core_ext.rb +3 -0
- data/lib/detroit/tool/core_ext/facets.rb +11 -0
- data/lib/detroit/tool/core_ext/filetest.rb +29 -0
- data/lib/detroit/tool/core_ext/shell_extensions.rb +7 -0
- data/lib/detroit/tool/core_ext/to_actual_filename.rb +19 -0
- data/lib/detroit/tool/core_ext/to_console.rb +97 -0
- data/lib/detroit/tool/core_ext/to_list.rb +29 -0
- data/lib/detroit/tool/core_ext/to_yamlfrag.rb +9 -0
- data/lib/detroit/tool/core_ext/unfold_paragraphs.rb +27 -0
- data/lib/detroit/tool/email_utils.rb +288 -0
- data/lib/detroit/tool/project_utils.rb +41 -0
- data/lib/detroit/tool/shell_utils.rb +235 -0
- data/qed/01_schedule/02_initialize.md +57 -0
- data/qed/99_plugins/rdoc/rdoc-plugin.rdoc +22 -0
- data/qed/99_plugins/rdoc/sample/Syckfile +6 -0
- data/qed/99_plugins/rdoc/sample/lib/sandbox/.xxx +1 -0
- data/qed/99_plugins/rdoc/sample/lib/sandbox/hello.rb +5 -0
- data/qed/99_plugins/rdoc/sample/lib/sandbox/xxx.rb +6 -0
- data/qed/99_plugins/rdoc/sample/lib/xxx/bye.rb +4 -0
- data/qed/99_plugins/rdoc/sample/meta/name +1 -0
- data/qed/99_plugins/rdoc/sample/meta/version +1 -0
- data/qed/samples/example_project/.ruby +0 -0
- data/qed/samples/example_project/Schedule +9 -0
- data/qed/samples/example_project/lib/foo/.xxx +1 -0
- data/qed/samples/example_project/lib/foo/hello.rb +7 -0
- data/qed/samples/example_project/lib/foo/xxx.rb +6 -0
- data/qed/samples/example_project/lib/foo/xxx/bye.rb +4 -0
- data/qed/samples/example_project/meta/name +1 -0
- data/qed/samples/example_project/meta/version +1 -0
- data/qed/samples/example_schedule.rb +57 -0
- metadata +139 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
module Detroit
|
2
|
+
|
3
|
+
# All assemblies and tracks have a maintenance sub-track.
|
4
|
+
# For this reason stop names `reset`, `clean` and `purge`
|
5
|
+
# are reserved names and MUST not be used as stop names
|
6
|
+
# in defining custom lines.
|
7
|
+
MAINTENANCE_TRACK = [:reset, :clean, :purge]
|
8
|
+
|
9
|
+
# Returns Hash of name and Circuit instance pairs.
|
10
|
+
def self.assemblies
|
11
|
+
@assemblies ||= {}
|
12
|
+
end
|
13
|
+
|
14
|
+
# Define a new assembly.
|
15
|
+
def self.assembly(name, &block)
|
16
|
+
assemblies[name.to_sym] = Assembly.new(name, &block)
|
17
|
+
end
|
18
|
+
|
19
|
+
# The Assembly class encapsulates an *assembly system* which consists of
|
20
|
+
# a set of interrelated assembly lines, or tracks.
|
21
|
+
class Assembly
|
22
|
+
|
23
|
+
# Name of the assembly system.
|
24
|
+
attr :name
|
25
|
+
|
26
|
+
# Returns a Hash of track names mapped to list of stops.
|
27
|
+
attr :lines
|
28
|
+
|
29
|
+
# Lines are also called `tracks`.
|
30
|
+
alias_method :tracks, :lines
|
31
|
+
|
32
|
+
# Create a new instance.
|
33
|
+
def initialize(name, &block)
|
34
|
+
@name = name.to_sym
|
35
|
+
@lines = {:maintenance => MAINTENANCE_TRACK}
|
36
|
+
instance_eval(&block) if block
|
37
|
+
end
|
38
|
+
|
39
|
+
# Define an assembly line.
|
40
|
+
def line(name, *stops)
|
41
|
+
if stops.empty?
|
42
|
+
@lines[name.to_sym]
|
43
|
+
else
|
44
|
+
@lines[name.to_sym] = stops.map{ |s| s.to_sym }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Lines are also called tracks.
|
49
|
+
alias_method :track, :line
|
50
|
+
|
51
|
+
# Lookup track by name and (optional) stop. If the stop belongs
|
52
|
+
# to the maintenance sub-track then the maintenance sub-track will
|
53
|
+
# be returned instead of the track itself.
|
54
|
+
#
|
55
|
+
# The Application class uses this to simplify track lookup.
|
56
|
+
def get_track(name, stop=nil)
|
57
|
+
name = name.to_sym
|
58
|
+
if stop
|
59
|
+
stop = stop.to_sym
|
60
|
+
if MAINTENANCE_TRACK.include?(stop.to_sym)
|
61
|
+
track = MAINTENANCE_TRACK
|
62
|
+
else
|
63
|
+
track = tracks[name]
|
64
|
+
raise "Unknown track `#{name}'." unless track
|
65
|
+
unless track.include?(stop)
|
66
|
+
raise "Unknown stop `#{stop}` for track `#{name}'."
|
67
|
+
end
|
68
|
+
end
|
69
|
+
else
|
70
|
+
track = tracks[name]
|
71
|
+
end
|
72
|
+
track
|
73
|
+
end
|
74
|
+
|
75
|
+
# Did I mention that `line` and `track` are synonyms?
|
76
|
+
alias_method :get_line, :get_track
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
module Detroit
|
2
|
+
|
3
|
+
# Detroit configuration. Configuration comes from a main +Routine+
|
4
|
+
# and/or +.routine+ files.
|
5
|
+
class Config
|
6
|
+
#instance_methods.each{ |m| private m unless /^__/ =~ m.to_s }
|
7
|
+
|
8
|
+
# Configuration directory name (most likely a hidden "dot" directory).
|
9
|
+
DIRECTORY = "detroit"
|
10
|
+
|
11
|
+
# File identifier used to find a project's Schedule(s).
|
12
|
+
FILE_EXTENSION = "schedule"
|
13
|
+
|
14
|
+
# Current POM::Project object.
|
15
|
+
#attr :project
|
16
|
+
|
17
|
+
# The list of a project's routine files.
|
18
|
+
#
|
19
|
+
# @return [Array<String>] routine files
|
20
|
+
attr :schedules
|
21
|
+
|
22
|
+
# Service configurations from Schedule or *.schedule files.
|
23
|
+
#
|
24
|
+
# @return [Hash] service settings
|
25
|
+
attr :services
|
26
|
+
|
27
|
+
# Service defaults. This is a mapping of service names to
|
28
|
+
# default settings. Very useful for when using the same
|
29
|
+
# service more than once.
|
30
|
+
#
|
31
|
+
# @return [Hash] default settings
|
32
|
+
attr :defaults
|
33
|
+
|
34
|
+
#
|
35
|
+
def initialize(schedule_files=nil)
|
36
|
+
if schedule_files && !schedule_files.empty?
|
37
|
+
@schedule_filenames = schedule_files
|
38
|
+
else
|
39
|
+
@schedule_filenames = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
@schedules = {}
|
43
|
+
@services = {}
|
44
|
+
@defaults = {}
|
45
|
+
|
46
|
+
@loaded_plugins = {}
|
47
|
+
|
48
|
+
load_plugins
|
49
|
+
load_defaults
|
50
|
+
load_schedules
|
51
|
+
end
|
52
|
+
|
53
|
+
#--
|
54
|
+
# TODO: Use this, or pass in via initialize?
|
55
|
+
#++
|
56
|
+
def project
|
57
|
+
Detroit.project
|
58
|
+
end
|
59
|
+
|
60
|
+
# Load a plugin.
|
61
|
+
def load_plugin(name)
|
62
|
+
@loaded_plugins[name] ||= (
|
63
|
+
require "detroit-#{name}"
|
64
|
+
name
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Pre-load plugins using `.detroit/plugins.rb`.
|
69
|
+
def load_plugins
|
70
|
+
if file = project.root.glob('{.,}#{DIRECTORY}/plugins{,.rb}').first
|
71
|
+
require file
|
72
|
+
else
|
73
|
+
self.defaults = {}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Load defaults from `.detroit/defaults.yml`.
|
78
|
+
def load_defaults
|
79
|
+
if file = project.root.glob('{.,}#{DIRECTORY}/defaults{,.yml,.yaml}').first
|
80
|
+
self.defaults = YAML.load(File.new(file))
|
81
|
+
else
|
82
|
+
self.defaults = {}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
def load_schedules
|
88
|
+
schedule_filenames.each do |file|
|
89
|
+
load_schedule_file(file)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
def load_schedule_file(file)
|
95
|
+
@schedules[file] = Schedule.load(File.new(file))
|
96
|
+
@services.merge!(schedules[file].services)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Set defaults.
|
100
|
+
def defaults=(hash)
|
101
|
+
@defaults = hash.to_h
|
102
|
+
end
|
103
|
+
|
104
|
+
# If a `Schedule` or `.schedule` file exists, then it is returned. Otherwise
|
105
|
+
# all `*.schedule` files are loaded. To load `*.schedule` files from another
|
106
|
+
# directory add the directory to config options file.
|
107
|
+
def schedule_filenames
|
108
|
+
@schedule_filenames ||= (
|
109
|
+
files = []
|
110
|
+
## match 'Schedule' or '.schedule' file
|
111
|
+
files = project.root.glob("{,.,*.}#{FILE_EXTENSION}{,.rb,.yml,.yaml}", :casefold)
|
112
|
+
## only files
|
113
|
+
files = files.select{ |f| File.file?(f) }
|
114
|
+
##
|
115
|
+
if files.empty?
|
116
|
+
## match '.detroit/*.schedule' or 'detroit/*.schedule'
|
117
|
+
files += project.root.glob("{,.}#{DIRECTORY}/*.#{FILE_EXTENSION}", :casefold)
|
118
|
+
## match 'task/*.schedule' (OLD SCHOOL)
|
119
|
+
files += project.root.glob("{task,tasks}/*.#{FILE_EXTENSION}", :casefold)
|
120
|
+
## only files
|
121
|
+
files = files.select{ |f| File.file?(f) }
|
122
|
+
end
|
123
|
+
files
|
124
|
+
)
|
125
|
+
end
|
126
|
+
|
127
|
+
#
|
128
|
+
def each(&block)
|
129
|
+
services.each(&block)
|
130
|
+
end
|
131
|
+
|
132
|
+
#
|
133
|
+
def size
|
134
|
+
services.size
|
135
|
+
end
|
136
|
+
|
137
|
+
=begin
|
138
|
+
# If using a `Routine` file and want to import antoher file then use
|
139
|
+
# `import:` entry.
|
140
|
+
def load_detroit_file(file)
|
141
|
+
#@dir = File.dirname(file)
|
142
|
+
|
143
|
+
schedules[file] =
|
144
|
+
|
145
|
+
# TODO: can we just read the first line of the file and go from there?
|
146
|
+
#text = File.read(file).strip
|
147
|
+
|
148
|
+
## if yaml vs. ruby file
|
149
|
+
#if (/\A---/ =~ text || /\.(yml|yaml)$/ =~ File.extname(file))
|
150
|
+
# #data = parse_detroit_file_yaml(text, file)
|
151
|
+
# YAML.load(text)
|
152
|
+
#else
|
153
|
+
# data = parse_detroit_file_ruby(text, file)
|
154
|
+
#end
|
155
|
+
|
156
|
+
## extract defaults
|
157
|
+
#if defaults = data.delete('defaults')
|
158
|
+
# @defaults.merge!(defaults)
|
159
|
+
#end
|
160
|
+
|
161
|
+
## import other files
|
162
|
+
#if import = data.delete('import')
|
163
|
+
# [import].flatten.each do |glob|
|
164
|
+
# routine(glob)
|
165
|
+
# end
|
166
|
+
#end
|
167
|
+
|
168
|
+
## require plugins
|
169
|
+
#if plugins = data.delete('plugins')
|
170
|
+
# [plugins].flatten.each do |file|
|
171
|
+
# require file
|
172
|
+
# end
|
173
|
+
#end
|
174
|
+
|
175
|
+
#@services.update(data)
|
176
|
+
end
|
177
|
+
=end
|
178
|
+
|
179
|
+
## Parse a YAML-based routine.
|
180
|
+
#def parse_detroit_file_yaml(text, file)
|
181
|
+
# YAMLParser.parse(self, text, file)
|
182
|
+
#end
|
183
|
+
|
184
|
+
## Parse a Ruby-based routine.
|
185
|
+
#def parse_detroit_file_ruby(text, file)
|
186
|
+
# RubyParser.parse(self, text, file)
|
187
|
+
#end
|
188
|
+
|
189
|
+
## TODO: Should the +dir+ be relative to the file or project.root?
|
190
|
+
#def routine(glob)
|
191
|
+
# pattern = File.join(@dir, glob)
|
192
|
+
# Dir[pattern].each{ |f| load_detroit_file(f) }
|
193
|
+
#end
|
194
|
+
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module Detroit
|
2
|
+
|
3
|
+
# The control module is a function module that extends
|
4
|
+
# the toplevel Detroit namespace module.
|
5
|
+
module Control
|
6
|
+
|
7
|
+
# Location of standard plugins.
|
8
|
+
#PLUGIN_DIRECTORY = File.dirname(__FILE__) + '/plugins'
|
9
|
+
|
10
|
+
# Returns Array of standard plugin file names.
|
11
|
+
#def standard_plugins
|
12
|
+
# Dir[PLUGIN_DIRECTORY + '/*.rb']
|
13
|
+
#end
|
14
|
+
|
15
|
+
# Universal acccess to the current project.
|
16
|
+
#
|
17
|
+
# TODO: Is Control#project being used?
|
18
|
+
def project
|
19
|
+
@project ||= POM::Project.find
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns Application given options.
|
23
|
+
def application(options={})
|
24
|
+
Application.new(options)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Run the command line interface.
|
28
|
+
def cli(*argv)
|
29
|
+
cli_options = {
|
30
|
+
:schedules => [],
|
31
|
+
:trace=>nil, :trial=>nil, :debug=>nil, :quiet=>nil, :verbose=>nil,
|
32
|
+
:force=>nil, :multitask=>nil, :skip=>[]
|
33
|
+
}
|
34
|
+
|
35
|
+
cli_usage(cli_options).parse!(argv)
|
36
|
+
|
37
|
+
#if /\.schedule$/ =~ argv[0]
|
38
|
+
# job = argv[1]
|
39
|
+
# begin
|
40
|
+
# application(cli_options).runscript(argv[0], job)
|
41
|
+
# rescue => error
|
42
|
+
# $stderr.puts error.message
|
43
|
+
# exit -1
|
44
|
+
# end
|
45
|
+
#else
|
46
|
+
begin
|
47
|
+
application(cli_options).start(*argv)
|
48
|
+
rescue => error
|
49
|
+
if $DEBUG
|
50
|
+
raise error
|
51
|
+
else
|
52
|
+
$stderr.puts error.message
|
53
|
+
exit -1
|
54
|
+
end
|
55
|
+
end
|
56
|
+
#end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns an instance of OptionParser.
|
60
|
+
def cli_usage(options)
|
61
|
+
@usage ||= (
|
62
|
+
OptionParser.new do |usage|
|
63
|
+
usage.banner = "Usage: detroit [<track>:]<stop> [options]"
|
64
|
+
usage.on('-m', '--multitask', "Run work elements in parallel.") do
|
65
|
+
options[:multitask] = true
|
66
|
+
end
|
67
|
+
usage.on('-S', '--skip [SERVICE]', 'Skip a service.') do |skip|
|
68
|
+
options[:skip] << skip
|
69
|
+
end
|
70
|
+
|
71
|
+
usage.on('-a', '--assembly=NAME', "Select assembly. Default is `standard'.") do |assembly|
|
72
|
+
options[:assembly] = assembly
|
73
|
+
end
|
74
|
+
usage.on('-s', '--schedule [FILE]', 'Use specific schedule file(s).') do |file|
|
75
|
+
options[:schedules] << file
|
76
|
+
end
|
77
|
+
|
78
|
+
usage.on('-F', '--force', "Force operations.") do
|
79
|
+
options[:force] = true
|
80
|
+
end
|
81
|
+
usage.on('--trace', "Run in TRACE mode.") do
|
82
|
+
#$TRACE = true
|
83
|
+
options[:trace] = true
|
84
|
+
end
|
85
|
+
usage.on('--trial', "Run in TRIAL mode (no disk writes).") do
|
86
|
+
#$TRIAL = true
|
87
|
+
options[:trial] = true
|
88
|
+
end
|
89
|
+
# TODO: do we really need verbose?
|
90
|
+
usage.on('--verbose', "Provided extra output.") do
|
91
|
+
options[:verbose] = true
|
92
|
+
end
|
93
|
+
usage.on('-q', '--quiet', "Run silently.") do
|
94
|
+
options[:quiet] = true
|
95
|
+
end
|
96
|
+
|
97
|
+
usage.on('-I=PATH', "Add directory to $LOAD_PATH") do |dirs|
|
98
|
+
dirs.to_list.each do |dir|
|
99
|
+
$LOAD_PATH.unshift(dir)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
usage.on('--debug', "Run with $DEBUG set to true.") do
|
103
|
+
$DEBUG = true
|
104
|
+
options[:debug] = true # DEPRECATE?
|
105
|
+
end
|
106
|
+
usage.on('--warn', "Run with $VERBOSE set to true.") do
|
107
|
+
$VERBOSE = true # wish this were called $WARN
|
108
|
+
end
|
109
|
+
usage.on_tail('--help', "Display this help message.") do
|
110
|
+
puts usage
|
111
|
+
exit
|
112
|
+
end
|
113
|
+
usage.on_tail('--config', "Produce a configuration template.") do
|
114
|
+
puts application.config_template.to_yaml
|
115
|
+
exit
|
116
|
+
end
|
117
|
+
end
|
118
|
+
)
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
extend Control
|
124
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
#__DIR__ = File.dirname(__FILE__)
|
2
|
+
|
3
|
+
#Dir[File.join(__DIR__, 'core_ext', '*.rb')].each do |file|
|
4
|
+
# require file
|
5
|
+
#end
|
6
|
+
|
7
|
+
#require 'facets'
|
8
|
+
require 'facets/to_hash'
|
9
|
+
require 'facets/module/basename'
|
10
|
+
require 'facets/module/alias_accessor'
|
11
|
+
require 'facets/pathname'
|
12
|
+
#require 'facets/boolean'
|
13
|
+
|
14
|
+
class Array
|
15
|
+
|
16
|
+
def to_list
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
class NilClass
|
23
|
+
|
24
|
+
def to_list
|
25
|
+
[]
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
class String
|
31
|
+
|
32
|
+
# Helper method for cleaning list options.
|
33
|
+
# This will split the option on ':' or ';'
|
34
|
+
# if it is a string, rather than an array.
|
35
|
+
# And it will make sure there are no nil elements.
|
36
|
+
|
37
|
+
def to_list
|
38
|
+
split(/[:;,\n]/).map{ |s| s.strip }
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
# TODO: Replace these with facets/shellwords !!!
|
44
|
+
|
45
|
+
# TODO: Belongs in Redtools, no?
|
46
|
+
|
47
|
+
#
|
48
|
+
class Array #:nodoc:
|
49
|
+
|
50
|
+
# Convert an array into commandline parameters.
|
51
|
+
# The array is accepted in the format of Ruby
|
52
|
+
# method arguments --ie. [arg1, arg2, ..., hash]
|
53
|
+
|
54
|
+
def to_console
|
55
|
+
#flags = (Hash===last ? pop : {})
|
56
|
+
#flags = flags.to_console
|
57
|
+
#flags + ' ' + join(" ")
|
58
|
+
to_argv.join(' ')
|
59
|
+
end
|
60
|
+
|
61
|
+
# TODO: DEPRECATE
|
62
|
+
alias_method :to_params, :to_console
|
63
|
+
|
64
|
+
#
|
65
|
+
def to_argv
|
66
|
+
flags = (Hash===last ? pop : {})
|
67
|
+
flags = flags.to_argv
|
68
|
+
flags + self
|
69
|
+
end
|
70
|
+
|
71
|
+
# def to_console
|
72
|
+
# flags = (Hash===last ? pop : {})
|
73
|
+
# flags = flags.collect do |f,v|
|
74
|
+
# m = f.to_s.size == 1 ? '-' : '--'
|
75
|
+
# case v
|
76
|
+
# when Array
|
77
|
+
# v.collect{ |e| "#{m}#{f} '#{e}'" }.join(' ')
|
78
|
+
# when true
|
79
|
+
# "#{m}#{f}"
|
80
|
+
# when false, nil
|
81
|
+
# ''
|
82
|
+
# else
|
83
|
+
# "#{m}#{f} '#{v}'"
|
84
|
+
# end
|
85
|
+
# end
|
86
|
+
# return (flags + self).join(" ")
|
87
|
+
# end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
class Hash
|
92
|
+
|
93
|
+
# Convert a Hash into command line arguments.
|
94
|
+
# The array is accepted in the format of Ruby
|
95
|
+
# method arguments --ie. [arg1, arg2, ..., hash]
|
96
|
+
def to_console
|
97
|
+
to_argv.join(' ')
|
98
|
+
end
|
99
|
+
|
100
|
+
# Convert a Hash into command line parameters.
|
101
|
+
# The array is accepted in the format of Ruby
|
102
|
+
# method arguments --ie. [arg1, arg2, ..., hash]
|
103
|
+
def to_argv
|
104
|
+
flags = map do |f,v|
|
105
|
+
m = f.to_s.size == 1 ? '-' : '--'
|
106
|
+
case v
|
107
|
+
when Array
|
108
|
+
v.collect{ |e| "#{m}#{f}='#{e}'" }.join(' ')
|
109
|
+
when true
|
110
|
+
"#{m}#{f}"
|
111
|
+
when false, nil
|
112
|
+
''
|
113
|
+
else
|
114
|
+
"#{m}#{f}='#{v}'"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Turn a hash into arguments.
|
120
|
+
#
|
121
|
+
# h = { :list => [1,2], :base => "HI" }
|
122
|
+
# h.argumentize #=> [ [], { :list => [1,2], :base => "HI" } ]
|
123
|
+
# h.argumentize(:list) #=> [ [1,2], { :base => "HI" } ]
|
124
|
+
#
|
125
|
+
def argumentize(args_field=nil)
|
126
|
+
config = dup
|
127
|
+
if args_field
|
128
|
+
args = [config.delete(args_field)].flatten.compact
|
129
|
+
else
|
130
|
+
args = []
|
131
|
+
end
|
132
|
+
args << config
|
133
|
+
return args
|
134
|
+
end
|
135
|
+
|
136
|
+
alias_method :command_vector, :argumentize
|
137
|
+
|
138
|
+
end
|
139
|
+
|