mattock 0.3.4 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/mattock/bundle-command-task.rb +1 -1
- data/lib/mattock/cascading-definition.rb +3 -4
- data/lib/mattock/command-line.rb +31 -4
- data/lib/mattock/command-task.rb +34 -9
- data/lib/mattock/configurable.rb +84 -3
- data/lib/mattock/remote-command-task.rb +32 -26
- data/lib/mattock/task.rb +68 -75
- data/lib/mattock/tasklib.rb +10 -3
- data/lib/mattock/template-host.rb +16 -38
- data/lib/mattock/testing/rake-example-group.rb +5 -5
- data/spec/command-task.rb +2 -2
- data/spec/configurable.rb +107 -0
- data/spec/template-host.rb +1 -3
- metadata +33 -13
@@ -23,12 +23,12 @@ module Mattock
|
|
23
23
|
module CascadingDefinition
|
24
24
|
include Configurable
|
25
25
|
|
26
|
-
def
|
26
|
+
def setup_cascade(*other_definitions)
|
27
27
|
@runtime = false
|
28
28
|
setup_defaults
|
29
29
|
|
30
30
|
confirm_steps(:default_configuration, :resolve_configuration, :confirm_configuration) do
|
31
|
-
default_configuration(*
|
31
|
+
default_configuration(*other_definitions)
|
32
32
|
|
33
33
|
yield self if block_given?
|
34
34
|
|
@@ -93,10 +93,9 @@ module Mattock
|
|
93
93
|
mod.setting(:configuration_block, proc{})
|
94
94
|
end
|
95
95
|
|
96
|
-
def
|
96
|
+
def setup_deferred
|
97
97
|
@runtime = false
|
98
98
|
@finalized = false
|
99
|
-
super
|
100
99
|
end
|
101
100
|
|
102
101
|
def runtime_definition(&block)
|
data/lib/mattock/command-line.rb
CHANGED
@@ -77,7 +77,7 @@ module Mattock
|
|
77
77
|
alias_method :command_environment, :env
|
78
78
|
|
79
79
|
def verbose
|
80
|
-
Rake.verbose && Rake.verbose != Rake::FileUtilsExt::DEFAULT
|
80
|
+
::Rake.verbose && ::Rake.verbose != ::Rake::FileUtilsExt::DEFAULT
|
81
81
|
end
|
82
82
|
|
83
83
|
def name
|
@@ -136,8 +136,8 @@ module Mattock
|
|
136
136
|
def collect_result(pid, host_stdout, host_stderr)
|
137
137
|
pid, status = Process.wait2(pid)
|
138
138
|
|
139
|
-
stdout = host_stdout
|
140
|
-
stderr = host_stderr
|
139
|
+
stdout = consume_buffer(host_stdout)
|
140
|
+
stderr = consume_buffer(host_stderr)
|
141
141
|
result = CommandRunResult.new(command, status, {1 => stdout, 2 => stderr})
|
142
142
|
host_stdout.close
|
143
143
|
host_stderr.close
|
@@ -145,6 +145,24 @@ module Mattock
|
|
145
145
|
return result
|
146
146
|
end
|
147
147
|
|
148
|
+
#Gets all the data out of buffer, even if somehow it doesn't have an EOF
|
149
|
+
#Escpecially useful for programs (e.g. ssh) that sometime set their stderr
|
150
|
+
#to O_NONBLOCK
|
151
|
+
def consume_buffer(io)
|
152
|
+
accumulate = []
|
153
|
+
waits = 3
|
154
|
+
begin
|
155
|
+
while chunk = io.read_nonblock(4096)
|
156
|
+
accumulate << chunk
|
157
|
+
end
|
158
|
+
rescue IO::WaitReadable => ex
|
159
|
+
retry if (waits -= 1) > 0
|
160
|
+
end
|
161
|
+
return accumulate.join
|
162
|
+
rescue EOFError
|
163
|
+
return accumulate.join
|
164
|
+
end
|
165
|
+
|
148
166
|
#If I wasn't worried about writing my own limited shell, I'd say e.g.
|
149
167
|
#Pipeline would be an explicit chain of pipes... which is probably as
|
150
168
|
#originally intended :/
|
@@ -152,11 +170,20 @@ module Mattock
|
|
152
170
|
collect_result(*spawn_process)
|
153
171
|
end
|
154
172
|
|
173
|
+
#Run a command in the background. The command can survive the caller
|
174
|
+
def spin_off
|
175
|
+
pid, out, err = spawn_process
|
176
|
+
Process.detach(pid)
|
177
|
+
return pid, out, err
|
178
|
+
end
|
179
|
+
|
180
|
+
#Run a command in parallel with the parent process - will kill it if it
|
181
|
+
#outlasts us
|
155
182
|
def background
|
156
183
|
pid, out, err = spawn_process
|
184
|
+
Process.detach(pid)
|
157
185
|
at_exit do
|
158
186
|
kill_process(pid)
|
159
|
-
Process.detach(pid)
|
160
187
|
end
|
161
188
|
return pid, out, err
|
162
189
|
end
|
data/lib/mattock/command-task.rb
CHANGED
@@ -2,13 +2,14 @@ require 'mattock/task'
|
|
2
2
|
require 'mattock/command-line'
|
3
3
|
|
4
4
|
module Mattock
|
5
|
-
|
5
|
+
module CommandTaskMixin
|
6
6
|
include CommandLineDSL
|
7
|
-
extend CommandLineDSL
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
def self.included(sub)
|
9
|
+
sub.extend CommandLineDSL
|
10
|
+
sub.runtime_setting(:verify_command, nil)
|
11
|
+
sub.runtime_setting(:command)
|
12
|
+
end
|
12
13
|
|
13
14
|
def resolve_runtime_configuration
|
14
15
|
super
|
@@ -38,17 +39,41 @@ module Mattock
|
|
38
39
|
command
|
39
40
|
end
|
40
41
|
|
41
|
-
def action
|
42
|
+
def action(args)
|
42
43
|
decorated(command).must_succeed!
|
43
44
|
end
|
44
45
|
|
45
46
|
def needed?
|
46
47
|
finalize_configuration
|
47
|
-
|
48
|
-
!decorated(verify_command).succeeds?
|
49
|
-
else
|
48
|
+
if verify_command.nil?
|
50
49
|
super
|
50
|
+
else
|
51
|
+
!decorated(verify_command).succeeds?
|
51
52
|
end
|
52
53
|
end
|
53
54
|
end
|
55
|
+
|
56
|
+
class Rake::CommandTask < Rake::Task
|
57
|
+
include CommandTaskMixin
|
58
|
+
setting :task_name, :run
|
59
|
+
end
|
60
|
+
|
61
|
+
class Rake::FileCommandTask < Rake::FileTask
|
62
|
+
include CommandTaskMixin
|
63
|
+
|
64
|
+
setting :target_path
|
65
|
+
|
66
|
+
def resolve_configuration
|
67
|
+
super
|
68
|
+
self.target_path ||= task_name
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class CommandTask < DeprecatedTaskAPI
|
73
|
+
def target_class; Rake::CommandTask; end
|
74
|
+
end
|
75
|
+
|
76
|
+
class FileCommandTask < DeprecatedTaskAPI
|
77
|
+
def target_class; Rake::FileCommandTask; end
|
78
|
+
end
|
54
79
|
end
|
data/lib/mattock/configurable.rb
CHANGED
@@ -10,6 +10,15 @@ module Mattock
|
|
10
10
|
#
|
11
11
|
#@example (see ClassMethods)
|
12
12
|
module Configurable
|
13
|
+
class Exception < ::StandardError
|
14
|
+
end
|
15
|
+
|
16
|
+
class NoDefaultValue < Exception
|
17
|
+
def initialize(field_name, klass)
|
18
|
+
super("No default value for field #{field_name} on class #{klass.name}")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
13
22
|
class FieldMetadata
|
14
23
|
attr_accessor :name, :default_value
|
15
24
|
|
@@ -132,7 +141,7 @@ module Mattock
|
|
132
141
|
attr_reader :source, :field
|
133
142
|
|
134
143
|
def inspect
|
135
|
-
"#{self.class.name.split(':').last}: #{
|
144
|
+
"#{self.class.name.split(':').last}: #{source.class.name}.#{field.inspect}"
|
136
145
|
end
|
137
146
|
end
|
138
147
|
|
@@ -222,6 +231,13 @@ module Mattock
|
|
222
231
|
# ce.hoo = "hallo"
|
223
232
|
# ce.check_required #=> raises error because :must and :foo aren't set
|
224
233
|
module ClassMethods
|
234
|
+
def inspect_instance(instance, indent="")
|
235
|
+
field_names.map do |name|
|
236
|
+
meta = field_metadata(name)
|
237
|
+
"#{indent}#{meta.inspect} => #{meta.immediate_value_on(instance).inspect}(#{meta.value_on(instance).inspect})"
|
238
|
+
end.join("\n")
|
239
|
+
end
|
240
|
+
|
225
241
|
def default_values
|
226
242
|
@default_values ||= []
|
227
243
|
end
|
@@ -244,6 +260,13 @@ module Mattock
|
|
244
260
|
end
|
245
261
|
end
|
246
262
|
|
263
|
+
#@raises NoDefaultValue
|
264
|
+
def default_value_for(name)
|
265
|
+
field = field_metadata(name)
|
266
|
+
raise NoDefaultValue.new(name,self) unless field.is?(:defaulting)
|
267
|
+
return field.default_value
|
268
|
+
end
|
269
|
+
|
247
270
|
#Creates an anonymous Configurable - useful in complex setups for nested
|
248
271
|
#settings
|
249
272
|
#@example SSH options
|
@@ -297,8 +320,8 @@ module Mattock
|
|
297
320
|
|
298
321
|
if existing = default_values.find{|field| field.name == name} and existing.default_value != default_value
|
299
322
|
source_line = caller.drop_while{|line| /#{__FILE__}/ =~ line}.first
|
300
|
-
|
301
|
-
|
323
|
+
warn "Changing default value of #{self.name}##{name} from #{existing.default_value.inspect} to #{default_value.inspect}"
|
324
|
+
" (at: #{source_line})"
|
302
325
|
end
|
303
326
|
default_values << metadata
|
304
327
|
metadata
|
@@ -413,6 +436,64 @@ module Mattock
|
|
413
436
|
|
414
437
|
extend ClassMethods
|
415
438
|
|
439
|
+
module DirectoryStructure
|
440
|
+
module ClassMethods
|
441
|
+
RequiredField = ::Mattock::Configurable::ClassMethods::RequiredField
|
442
|
+
|
443
|
+
attr_accessor :path_heirarchy
|
444
|
+
attr_accessor :path_fields
|
445
|
+
|
446
|
+
def dir(field_name, *args)
|
447
|
+
rel_path = RequiredField
|
448
|
+
if String === args.first
|
449
|
+
rel_path = args.shift
|
450
|
+
end
|
451
|
+
parent_field = path(field_name, rel_path)
|
452
|
+
self.path_heirarchy += args.map do |child_field|
|
453
|
+
[parent_field, child_field]
|
454
|
+
end
|
455
|
+
return parent_field
|
456
|
+
end
|
457
|
+
|
458
|
+
def path(field_name, rel_path=RequiredField)
|
459
|
+
field = setting(field_name, nested{
|
460
|
+
required_field :absolute_path
|
461
|
+
setting :relative_path, rel_path
|
462
|
+
})
|
463
|
+
path_fields << field
|
464
|
+
return field
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
def self.included(sub)
|
469
|
+
sub.extend ClassMethods
|
470
|
+
sub.path_heirarchy = []
|
471
|
+
sub.path_fields = []
|
472
|
+
end
|
473
|
+
|
474
|
+
def resolve_paths
|
475
|
+
missing_relatives = []
|
476
|
+
self.class.path_heirarchy.reverse.each do |parent_field, child_field|
|
477
|
+
child = child_field.value_on(self)
|
478
|
+
next unless child.field_unset?(:absolute_path)
|
479
|
+
if child.field_unset?(:relative_path)
|
480
|
+
missing_relatives << child_field
|
481
|
+
next
|
482
|
+
end
|
483
|
+
parent = parent_field.value_on(self)
|
484
|
+
child.absolute_path = File::join(parent.absolute_path, child.relative_path)
|
485
|
+
end
|
486
|
+
unless missing_relatives.empty?
|
487
|
+
raise "Required field#{missing_relatives.length == 1 ? "" : "s"} #{missing_relatives.map{|field| "#{field.name}.relative_path".inspect}.join(", ")} unset on #{self.inspect}"
|
488
|
+
end
|
489
|
+
self.class.path_fields.each do |field|
|
490
|
+
value = field.value_on(self)
|
491
|
+
next unless value.field_unset?(:relative_path)
|
492
|
+
value.relative_path = value.absolute_path
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
416
497
|
def copy_settings
|
417
498
|
SettingsCopier.new(self)
|
418
499
|
end
|
@@ -1,39 +1,45 @@
|
|
1
1
|
require 'mattock/command-task'
|
2
2
|
module Mattock
|
3
|
-
|
4
|
-
|
5
|
-
:
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
3
|
+
module Rake
|
4
|
+
class RemoteCommandTask < CommandTask
|
5
|
+
runtime_setting(:remote_server, nested(
|
6
|
+
:address => nil,
|
7
|
+
:user => nil
|
8
|
+
))
|
9
|
+
setting(:ssh_options, [])
|
10
|
+
nil_fields(:id_file, :free_arguments)
|
11
|
+
runtime_setting(:remote_target)
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
def resolve_runtime_configuration
|
14
|
+
super
|
15
|
+
self.remote_target ||= [remote_server.user, remote_server.address].compact.join('@') unless remote_server.address.nil?
|
16
|
+
end
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
def ssh_option(name, value)
|
19
|
+
ssh_options << "\"#{name}=#{value}\""
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
-
|
22
|
+
def decorated(command_on_remote)
|
23
|
+
fail "Need remote server for #{self.class.name}" unless remote_server.address
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
raise "Empty remote command" if command_on_remote.nil?
|
26
|
+
Mattock::WrappingChain.new do |cmd|
|
27
|
+
cmd.add Mattock::CommandLine.new("ssh") do |cmd|
|
28
|
+
cmd.options << "-i #{id_file}" if id_file
|
29
|
+
unless ssh_options.empty?
|
30
|
+
ssh_options.each do |opt|
|
31
|
+
cmd.options << "-o #{opt}"
|
32
|
+
end
|
31
33
|
end
|
34
|
+
cmd.options << remote_target
|
32
35
|
end
|
33
|
-
cmd.
|
36
|
+
cmd.add Mattock::ShellEscaped.new(command_on_remote)
|
34
37
|
end
|
35
|
-
cmd.add Mattock::ShellEscaped.new(command_on_remote)
|
36
38
|
end
|
37
39
|
end
|
38
40
|
end
|
41
|
+
|
42
|
+
class RemoteCommandTask < DeprecatedTaskAPI
|
43
|
+
def target_class; Rake::RemoteCommandTask; end
|
44
|
+
end
|
39
45
|
end
|
data/lib/mattock/task.rb
CHANGED
@@ -9,114 +9,107 @@ module Mattock
|
|
9
9
|
# A configurable subclass of Rake::Task, such that you can use a
|
10
10
|
# configuration block to change how a common task behaves, while still
|
11
11
|
# overriding Rake API methods like Task#needed? and Task#timestamp
|
12
|
-
|
13
|
-
|
12
|
+
module ConfigurableTask
|
13
|
+
include Configurable
|
14
14
|
include CascadingDefinition
|
15
15
|
include DeferredDefinition
|
16
16
|
|
17
|
-
setting :task_name
|
18
|
-
setting :task_args
|
19
|
-
|
20
17
|
module ClassMethods
|
21
18
|
def default_taskname(name)
|
22
19
|
setting(:task_name, name)
|
23
20
|
end
|
24
|
-
end
|
25
21
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
22
|
+
def define_task(*args)
|
23
|
+
configs = args.take_while{|arg| Configurable === arg}
|
24
|
+
extracted_task_args = args[configs.length..-1]
|
25
|
+
if extracted_task_args.any?{|arg| Configurable === arg}
|
26
|
+
raise "Mattock::Task classes should be created with parent configs, then Rake task args"
|
27
|
+
end
|
28
|
+
|
29
|
+
if extracted_task_args.empty?
|
30
|
+
extracted_task_args = [default_value_for(:task_name)]
|
31
|
+
end
|
32
|
+
|
33
|
+
task = ::Rake.application.define_task(self, *extracted_task_args) do |task, args|
|
34
|
+
task.finalize_configuration
|
35
|
+
task.action(args)
|
36
|
+
end
|
37
|
+
|
38
|
+
unless self === task
|
39
|
+
raise "Task already defined for #{task.name} - attempted to redefine with #{self.name}"
|
40
|
+
end
|
41
|
+
|
42
|
+
task.setup_deferred
|
43
|
+
task.setup_cascade(*configs) do |t|
|
44
|
+
t.task_name = task.name
|
45
|
+
t.task_args = extracted_task_args
|
46
|
+
|
47
|
+
yield(t) if block_given?
|
48
|
+
end
|
31
49
|
end
|
32
50
|
end
|
33
51
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
raise "Mattock::Task classes should be created with parent configs, then Rake task args"
|
39
|
-
end
|
40
|
-
super(*configs)
|
52
|
+
def self.included(sub)
|
53
|
+
sub.extend ClassMethods
|
54
|
+
Configurable.included(sub)
|
55
|
+
DeferredDefinition.add_settings(sub)
|
41
56
|
end
|
42
57
|
|
58
|
+
setting :task_name
|
59
|
+
setting :task_args
|
60
|
+
|
61
|
+
attr_accessor :base_task
|
62
|
+
|
43
63
|
def resolve_configuration
|
44
64
|
super
|
45
|
-
if @extracted_task_args.empty?
|
46
|
-
self.task_args = [task_name]
|
47
|
-
else
|
48
|
-
self.task_args = @extracted_task_args
|
49
|
-
end
|
50
65
|
end
|
51
66
|
|
52
|
-
def action
|
67
|
+
def action(*task_args)
|
53
68
|
end
|
54
69
|
|
70
|
+
def inspect
|
71
|
+
"#{self.class.name}: #{self.task_args.inspect}\nConfiguration:\n#{self.class.inspect_instance(self, " ")}"
|
72
|
+
end
|
73
|
+
end
|
55
74
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
def inspect
|
64
|
-
"From: " + source_task.inspect
|
75
|
+
class DeprecatedTaskAPI
|
76
|
+
def self.deprecated(message)
|
77
|
+
@deprecations ||= {}
|
78
|
+
unless @deprecations.has_key?(message)
|
79
|
+
warn message
|
80
|
+
@deprecations[message] = :delivered
|
65
81
|
end
|
66
82
|
end
|
67
83
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
# instantiates the Task - so in wanting to be able to override members of
|
72
|
-
# Task, it's hard to get the virtues of CascadingDefinition as well (maybe
|
73
|
-
# the virtues could be had without the actual mixin?)
|
74
|
-
#
|
75
|
-
# So, what we're doing is to dynamically create a child class and then
|
76
|
-
# carry forward the Rake::Task#initialize
|
77
|
-
=end
|
78
|
-
def task_class
|
79
|
-
return @task_class if @task_class
|
80
|
-
@task_class = Class.new(self.class) do
|
81
|
-
define_method :initialize, Rake::Task.instance_method(:initialize)
|
82
|
-
include ChildTask
|
83
|
-
end
|
84
|
+
def initialize(*args, &block)
|
85
|
+
self.class.deprecated "#{self.class.name}.new(...) is deprecated - instead use #{target_class.name}.define_task(...)\n (from #{caller[0]})"
|
86
|
+
target_class.define_task(*args, &block)
|
84
87
|
end
|
88
|
+
end
|
85
89
|
|
86
|
-
|
87
|
-
|
90
|
+
module Rake
|
91
|
+
class Task < ::Rake::Task
|
92
|
+
include ConfigurableTask
|
88
93
|
end
|
89
94
|
|
90
|
-
|
91
|
-
|
92
|
-
self.rake_task = task_class.define_task(*task_args) do
|
93
|
-
finalize_configuration
|
94
|
-
copy_settings_to(rake_task)
|
95
|
-
rake_task.action
|
96
|
-
end
|
97
|
-
copy_settings_to(rake_task)
|
98
|
-
rake_task.source_task = self
|
95
|
+
class FileTask < ::Rake::FileTask
|
96
|
+
include ConfigurableTask
|
99
97
|
end
|
100
|
-
end
|
101
98
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
##needed? There's a lot of client code that relies on this pattern now,
|
106
|
-
#though.
|
107
|
-
class Task < Rake::Task
|
108
|
-
include TaskMixin
|
109
|
-
end
|
99
|
+
class FileCreationTask < ::Rake::FileCreationTask
|
100
|
+
include ConfigurableTask
|
101
|
+
end
|
110
102
|
|
111
|
-
|
112
|
-
|
103
|
+
class MultiTask < ::Rake::MultiTask
|
104
|
+
include ConfigurableTask
|
105
|
+
end
|
113
106
|
end
|
114
107
|
|
115
|
-
class
|
116
|
-
|
108
|
+
class Task < DeprecatedTaskAPI
|
109
|
+
def target_class; Rake::Task; end
|
117
110
|
end
|
118
111
|
|
119
|
-
class
|
120
|
-
|
112
|
+
class FileTask < DeprecatedTaskAPI
|
113
|
+
def target_class; Rake::FileTask; end
|
121
114
|
end
|
122
115
|
end
|
data/lib/mattock/tasklib.rb
CHANGED
@@ -37,7 +37,7 @@ module Mattock
|
|
37
37
|
#
|
38
38
|
#The configuration handling is provided by {CascadingDefinition}, and
|
39
39
|
#configuration options are built using {Configurable}
|
40
|
-
class TaskLib < Rake::TaskLib
|
40
|
+
class TaskLib < ::Rake::TaskLib
|
41
41
|
include CascadingDefinition
|
42
42
|
|
43
43
|
attr_writer :namespace_name
|
@@ -50,9 +50,9 @@ module Mattock
|
|
50
50
|
|
51
51
|
attr_reader :tasks
|
52
52
|
|
53
|
-
def initialize(*toolkits)
|
53
|
+
def initialize(*toolkits, &block)
|
54
54
|
@tasks = []
|
55
|
-
|
55
|
+
setup_cascade(*toolkits, &block)
|
56
56
|
end
|
57
57
|
|
58
58
|
#Records tasks as they are created
|
@@ -62,11 +62,18 @@ module Mattock
|
|
62
62
|
return a_task
|
63
63
|
end
|
64
64
|
|
65
|
+
#Shorthand for
|
66
|
+
# task name => before
|
67
|
+
# task after => name
|
68
|
+
#Which ensures that if "after" is ever invoked,
|
69
|
+
#the execution will be before, name, then after
|
65
70
|
def bracket_task(before, name, after)
|
66
71
|
task self[name] => before
|
67
72
|
task after => self[name]
|
68
73
|
end
|
69
74
|
|
75
|
+
#Builds a series of tasks in a sequence - the idea is that
|
76
|
+
#dependant tasklibs can depend on stages of a larger process
|
70
77
|
def task_spine(*list)
|
71
78
|
task list.first
|
72
79
|
list.each_cons(2) do |first, second|
|
@@ -4,49 +4,29 @@ require 'valise'
|
|
4
4
|
module Mattock
|
5
5
|
module ValiseManager
|
6
6
|
def default_valise(*dirs)
|
7
|
-
Valise::
|
8
|
-
dirs.each do |dir|
|
9
|
-
ro dir
|
10
|
-
end
|
11
|
-
end
|
7
|
+
Valise::read_only(*dirs)
|
12
8
|
end
|
13
9
|
|
14
10
|
def rel_dir(base_path = nil, up_to = nil)
|
15
|
-
base_path
|
16
|
-
up_to ||= "lib"
|
17
|
-
|
18
|
-
abs_path = File::expand_path(base_path)
|
19
|
-
base_path = abs_path.split(File::Separator)
|
20
|
-
until base_path.empty?
|
21
|
-
unless base_path.last == up_to
|
22
|
-
base_path.pop
|
23
|
-
else
|
24
|
-
break
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
if base_path.empty?
|
29
|
-
raise "Relative root #{up_to.inspect} not found in #{abs_path.inspect}"
|
30
|
-
end
|
31
|
-
|
32
|
-
return base_path
|
11
|
+
Valise::Unpath.up_to(up_to, base_path)
|
33
12
|
end
|
34
13
|
module_function :rel_dir, :default_valise
|
35
14
|
public :rel_dir, :default_valise
|
36
15
|
end
|
37
16
|
|
38
17
|
module TemplateHost
|
39
|
-
def self.template_cache
|
40
|
-
@template_cache ||= Tilt::Cache.new
|
41
|
-
end
|
42
|
-
|
43
18
|
attr_accessor :valise
|
44
19
|
|
20
|
+
def templates_are_in(valise)
|
21
|
+
self.valise = valise.templates
|
22
|
+
end
|
23
|
+
|
24
|
+
#XXX Better to let clients stem or subset
|
45
25
|
def find_template(path)
|
46
|
-
valise.find(
|
26
|
+
valise.find(path)
|
47
27
|
end
|
48
28
|
|
49
|
-
def
|
29
|
+
def template(path)
|
50
30
|
find_template(path).contents
|
51
31
|
end
|
52
32
|
|
@@ -54,19 +34,13 @@ module Mattock
|
|
54
34
|
find_template(path).full_path
|
55
35
|
end
|
56
36
|
|
57
|
-
def render(path
|
58
|
-
template = TemplateHost::template_cache.fetch(path) do
|
59
|
-
Tilt.new(path, template_options || {}) do |tmpl|
|
60
|
-
template_contents(tmpl.file)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
37
|
+
def render(path)
|
64
38
|
locals = {}
|
65
39
|
if block_given?
|
66
40
|
yield locals
|
67
41
|
end
|
68
42
|
|
69
|
-
template.render(self, locals)
|
43
|
+
template(path).render(self, locals)
|
70
44
|
end
|
71
45
|
end
|
72
46
|
|
@@ -74,10 +48,14 @@ module Mattock
|
|
74
48
|
include TemplateHost
|
75
49
|
|
76
50
|
def template_task(template_source, destination_path, template_options = nil)
|
51
|
+
unless template_options.nil?
|
52
|
+
valise.add_serialization_handler(template_source, :tilt, :template_options => template_options)
|
53
|
+
end
|
54
|
+
|
77
55
|
file template_path(template_source)
|
78
56
|
file destination_path => template_path(template_source) do
|
79
57
|
File::open(destination_path, "w") do |file|
|
80
|
-
file.write(render(template_source
|
58
|
+
file.write(render(template_source))
|
81
59
|
end
|
82
60
|
end
|
83
61
|
end
|
@@ -4,20 +4,20 @@ module Mattock
|
|
4
4
|
module RakeExampleGroup
|
5
5
|
SavedEnvironmentVariables = %w{APPDATA HOME HOMEDRIVE HOMEPATH RAKE_COLUMNS RAKE_SYSTEM RAKEOPT USERPROFILE}
|
6
6
|
DeletedEnvironmentVariables = %w{RAKE_COLUMNS RAKE_SYSTEM RAKEOPT}
|
7
|
-
include Rake::DSL
|
7
|
+
include ::Rake::DSL
|
8
8
|
#include FileUtils
|
9
9
|
|
10
10
|
class TaskManager
|
11
|
-
include Rake::TaskManager
|
11
|
+
include ::Rake::TaskManager
|
12
12
|
end
|
13
13
|
|
14
14
|
def self.included(mod)
|
15
15
|
mod.class_eval do
|
16
16
|
let! :rake do
|
17
|
-
Rake.application = Rake::Application.new
|
18
|
-
Rake::TaskManager.record_task_metadata = true
|
17
|
+
::Rake.application = ::Rake::Application.new
|
18
|
+
::Rake::TaskManager.record_task_metadata = true
|
19
19
|
RakeFileUtils.verbose_flag = false
|
20
|
-
Rake.application
|
20
|
+
::Rake.application
|
21
21
|
end
|
22
22
|
|
23
23
|
before :each do
|
data/spec/command-task.rb
CHANGED
@@ -9,7 +9,7 @@ describe Mattock::RemoteCommandTask do
|
|
9
9
|
|
10
10
|
let! :remote_task do
|
11
11
|
namespace :test do
|
12
|
-
Mattock::RemoteCommandTask.
|
12
|
+
Mattock::Rake::RemoteCommandTask.define_task do |t|
|
13
13
|
t.remote_server.address = "nowhere.com"
|
14
14
|
t.command = Mattock::PrereqChain.new do |prereq|
|
15
15
|
prereq.add Mattock::CommandLine.new("cd", "a_dir")
|
@@ -42,7 +42,7 @@ describe Mattock::BundleCommandTask do
|
|
42
42
|
include Mattock::CommandLineExampleGroup
|
43
43
|
|
44
44
|
let! :bundle_task do
|
45
|
-
Mattock::BundleCommandTask.
|
45
|
+
Mattock::BundleCommandTask.define_task(:bundle_test) do |t|
|
46
46
|
t.command = cmd("bundle", "install", "--standalone")
|
47
47
|
end
|
48
48
|
end
|
data/spec/configurable.rb
CHANGED
@@ -51,6 +51,113 @@ describe Mattock::Configurable do
|
|
51
51
|
end.to_not raise_error
|
52
52
|
end
|
53
53
|
|
54
|
+
describe "with DirectoryStructure" do
|
55
|
+
class DirectoryThing
|
56
|
+
include Mattock::Configurable
|
57
|
+
include DirectoryStructure
|
58
|
+
|
59
|
+
dir(:ephemeral_mountpoint,
|
60
|
+
dir(:bundle_workdir, "bundle_workdir",
|
61
|
+
path(:bundle_manifest),
|
62
|
+
path(:credentials_archive, "aws-creds.tar.gz"),
|
63
|
+
dir(:credentials_dir, "aws-creds",
|
64
|
+
path(:private_key_file, "pk.pem"),
|
65
|
+
path(:certificate_file, "cert.pem")
|
66
|
+
)
|
67
|
+
)
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
def subject
|
72
|
+
DirectoryThing.new.tap do |thing|
|
73
|
+
thing.setup_defaults
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should complain about missing fields" do
|
78
|
+
expect do
|
79
|
+
subject.check_required
|
80
|
+
end.to raise_error /Required field/
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "with root path configured, but missing a relative path" do
|
84
|
+
def subject
|
85
|
+
DirectoryThing.new.tap do |thing|
|
86
|
+
thing.setup_defaults
|
87
|
+
thing.ephemeral_mountpoint.absolute_path = "/tmp"
|
88
|
+
thing.resolve_paths
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should complain about missing fields" do
|
93
|
+
expect do
|
94
|
+
subject.check_required
|
95
|
+
end.to raise_error /Required field/
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "with required paths configured" do
|
100
|
+
def subject
|
101
|
+
DirectoryThing.new.tap do |thing|
|
102
|
+
thing.setup_defaults
|
103
|
+
thing.ephemeral_mountpoint.absolute_path = "/tmp"
|
104
|
+
thing.bundle_manifest.relative_path = "image.manifest.xml"
|
105
|
+
thing.resolve_paths
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should not complain about required fields" do
|
110
|
+
expect do
|
111
|
+
subject.check_required
|
112
|
+
end.not_to raise_error
|
113
|
+
end
|
114
|
+
|
115
|
+
its("certificate_file.absolute_path"){ should == "/tmp/bundle_workdir/aws-creds/cert.pem" }
|
116
|
+
its("bundle_manifest.absolute_path"){ should == "/tmp/bundle_workdir/image.manifest.xml" }
|
117
|
+
its("credentials_dir.absolute_path"){ should == "/tmp/bundle_workdir/aws-creds" }
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "multiple instances" do
|
122
|
+
class MultiSource
|
123
|
+
include Mattock::Configurable
|
124
|
+
|
125
|
+
setting :one, 1
|
126
|
+
setting :nest, nested{
|
127
|
+
setting :two, 2
|
128
|
+
}
|
129
|
+
end
|
130
|
+
|
131
|
+
let :first do
|
132
|
+
MultiSource.new.setup_defaults
|
133
|
+
end
|
134
|
+
|
135
|
+
let :second do
|
136
|
+
MultiSource.new.setup_defaults
|
137
|
+
end
|
138
|
+
|
139
|
+
before :each do
|
140
|
+
first.one = "one"
|
141
|
+
first.nest.two = "two"
|
142
|
+
second
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should not have any validation errors" do
|
146
|
+
expect do
|
147
|
+
first.check_required
|
148
|
+
second.check_required
|
149
|
+
end.not_to raise_error
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should accurately reflect settings" do
|
153
|
+
first.one.should == "one"
|
154
|
+
second.one.should == 1
|
155
|
+
|
156
|
+
first.nest.two.should == "two"
|
157
|
+
second.nest.two.should == 2
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
54
161
|
describe "copying settings" do
|
55
162
|
class LeftStruct
|
56
163
|
include Mattock::Configurable
|
data/spec/template-host.rb
CHANGED
@@ -9,9 +9,7 @@ describe Mattock::TemplateHost do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
it "should be able to do easy templating" do
|
12
|
-
template_host.
|
12
|
+
template_host.templates_are_in(Mattock::ValiseManager.default_valise("spec_help"))
|
13
13
|
template_host.render("test.erb").should == "Templated: A test value\n"
|
14
|
-
|
15
14
|
end
|
16
|
-
|
17
15
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mattock
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-03-30 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: corundum
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,24 +21,37 @@ dependencies:
|
|
21
21
|
version: 0.0.1
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.0.1
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: valise
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ! '>='
|
31
36
|
- !ruby/object:Gem::Version
|
32
|
-
version: '0.
|
37
|
+
version: '0.9'
|
33
38
|
segments:
|
34
39
|
- 0
|
35
|
-
-
|
40
|
+
- 9
|
36
41
|
type: :runtime
|
37
42
|
prerelease: false
|
38
|
-
version_requirements:
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0.9'
|
49
|
+
segments:
|
50
|
+
- 0
|
51
|
+
- 9
|
39
52
|
- !ruby/object:Gem::Dependency
|
40
53
|
name: tilt
|
41
|
-
requirement:
|
54
|
+
requirement: !ruby/object:Gem::Requirement
|
42
55
|
none: false
|
43
56
|
requirements:
|
44
57
|
- - ! '>'
|
@@ -48,7 +61,14 @@ dependencies:
|
|
48
61
|
- 0
|
49
62
|
type: :runtime
|
50
63
|
prerelease: false
|
51
|
-
version_requirements:
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>'
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
segments:
|
71
|
+
- 0
|
52
72
|
description: ! " If Rake won't do it by itself, you oughtta Mattock.\n\n If you
|
53
73
|
survived the pun, you might enjoy this gem.\n\n Features:\n\n * Extensions to
|
54
74
|
Tasklibs to support powerful deerpaths.\n * A commandline library that supports
|
@@ -102,7 +122,7 @@ rdoc_options:
|
|
102
122
|
- --main
|
103
123
|
- doc/README
|
104
124
|
- --title
|
105
|
-
- mattock-0.
|
125
|
+
- mattock-0.4.0 RDoc
|
106
126
|
require_paths:
|
107
127
|
- lib/
|
108
128
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -113,7 +133,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
113
133
|
version: '0'
|
114
134
|
segments:
|
115
135
|
- 0
|
116
|
-
hash:
|
136
|
+
hash: 725889327
|
117
137
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
138
|
none: false
|
119
139
|
requirements:
|
@@ -122,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
142
|
version: '0'
|
123
143
|
requirements: []
|
124
144
|
rubyforge_project: mattock
|
125
|
-
rubygems_version: 1.8.
|
145
|
+
rubygems_version: 1.8.24
|
126
146
|
signing_key:
|
127
147
|
specification_version: 3
|
128
148
|
summary: A powerful companion to Rake
|