mattock 0.3.4 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|