automateit 0.71102 → 0.71103
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.tar.gz.sig +1 -2
- data/CHANGES.txt +14 -0
- data/Rakefile +27 -37
- data/bin/aissh +93 -0
- data/bin/aitag +7 -0
- data/lib/automateit.rb +2 -0
- data/lib/automateit/account_manager/etc.rb +3 -3
- data/lib/automateit/account_manager/nscd.rb +14 -11
- data/lib/automateit/address_manager/base.rb +2 -2
- data/lib/automateit/interpreter.rb +2 -19
- data/lib/automateit/package_manager.rb +2 -0
- data/lib/automateit/plugin/driver.rb +1 -1
- data/lib/automateit/plugin/manager.rb +1 -6
- data/lib/automateit/root.rb +1 -1
- data/lib/automateit/tag_manager/tag_parser.rb +14 -17
- data/lib/ext/shell_escape.rb +7 -0
- data/lib/nitpick.rb +29 -0
- data/spec/integration/account_manager_nscd_spec.rb +37 -0
- data/spec/integration/account_manager_spec.rb +39 -4
- data/spec/integration/shell_manager_spec.rb +135 -3
- data/spec/unit/address_manager_spec.rb +49 -0
- data/spec/unit/interpreter_spec.rb +32 -1
- data/spec/unit/nested_error_spec.rb +10 -0
- data/spec/unit/object_spec.rb +18 -0
- data/spec/unit/plugins_spec.rb +113 -0
- data/spec/unit/shell_escape_spec.rb +11 -0
- data/spec/unit/tag_manager_spec.rb +60 -15
- metadata +16 -3
- metadata.gz.sig +1 -2
- data/lib/automateit.rb.orig +0 -65
data.tar.gz.sig
CHANGED
@@ -1,2 +1 @@
|
|
1
|
-
|
2
|
-
�Z���3Ǩ��1P��{��q5��ʚ��s�NN"v����>`�w�p.�F����ر~�z0����#j
|
1
|
+
V]�2��0��Y����� L�������%^6&]�Ǎ���k�կY��
|
data/CHANGES.txt
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
0.71103:
|
2
|
+
Date: Sat, 03 Nov 2007 01:26:13 -0700
|
3
|
+
Desc:
|
4
|
+
- (+) Added "aissh" command, provides an easy way to run commands on a group of hosts. For example, see if there's a Rails process running on all hosts matching the "rails_servers" tag: "aissh -p . rails_servers 'ps -ef | grep rails'"
|
5
|
+
- Improved PackageManager, it can now accept package names as Symbols.
|
6
|
+
- Improved AccountManager spec with additional examples for checking password changes.
|
7
|
+
- Improved AccountManager::NSCD with refactoring and additional tests.
|
8
|
+
- Improved AddressManager spec with additional tests for address conversion routines and code generation helpers.
|
9
|
+
- Improved Plugin spec with more checks against invalid managers and drivers, and unavailable dependencies.
|
10
|
+
- Improved ShellManager spec with more checks against hard links and symbolic links; for #cp when contents, modes or ownership changes; for #rm with :force option: for #umask with and without block, and with caught exceptions.
|
11
|
+
- Added spec for String#shell_escape.
|
12
|
+
- Added spec for Object's extensions for #unique_methods and #args_and_opts.
|
13
|
+
- Added spec for NestedError.
|
14
|
+
|
1
15
|
0.71102:
|
2
16
|
Date: Fri, 02 Nov 2007 02:59:41 -0700
|
3
17
|
Desc:
|
data/Rakefile
CHANGED
@@ -82,50 +82,35 @@ class Numeric
|
|
82
82
|
def commify() (s=self.to_s;x=s.length;s).rjust(x+(3-(x%3))).gsub(/(\d)(?=\d{3}+(\.\d*)?$)/,'\1,').strip end
|
83
83
|
end
|
84
84
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
task :loclines do
|
90
|
-
require 'find'
|
91
|
-
lines = 0
|
92
|
-
bytes = 0
|
93
|
-
Find.find(*%w(bin lib spec Rakefile ../web/Rakefile ../web/src )) do |path|
|
94
|
-
Find.prune if path.match(/.*(\b(.hg|.svn|CVS)\b|(.sw.?|.pyc)$)/)
|
95
|
-
next if File.directory?(path)
|
96
|
-
if path.match(/(\bbin\b|.*\.(env|pl|py|rb|rake|java|sql|ftl|jsp|xml|properties|css|rcss|html|rhtml|erb|po|haml|sass)$)/)
|
97
|
-
data = File.read(path)
|
98
|
-
bytes += data.size
|
99
|
-
### lines += data.scan(/^.*\w.*$/).size # Skip spaces
|
100
|
-
lines += data.scan(/^\s*(?!#)\w.*$/).size # Skip blanks and comments
|
101
|
-
end
|
85
|
+
namespace :loc do
|
86
|
+
desc "Display lines of code using loccount"
|
87
|
+
task :count do
|
88
|
+
sh "loccount bin/* lib/ spec/ examples/ *.rake"
|
102
89
|
end
|
103
|
-
puts "Lines: "+lines.commify
|
104
|
-
puts "Bytes: "+bytes.commify
|
105
|
-
end
|
106
90
|
|
107
|
-
|
108
|
-
|
109
|
-
|
91
|
+
desc "Display the lines of code changed in the repository"
|
92
|
+
task :diff do
|
93
|
+
if File.directory?(".hg")
|
94
|
+
puts "%s lines added and removed through SCM operations" % `hg log --patch`.scan(/^[+-][^+-].+/).size.commify
|
95
|
+
else
|
96
|
+
raise NotImplementedError.new("Sorry, this only works for a Mercurial checkout")
|
97
|
+
end
|
98
|
+
end
|
110
99
|
|
111
|
-
desc "Display
|
112
|
-
task :
|
113
|
-
|
114
|
-
puts "%s lines
|
115
|
-
else
|
116
|
-
raise NotImplementedError.new("Sorry, this only works for a Mercurial checkout")
|
100
|
+
desc "Display lines of churn"
|
101
|
+
task :churn do
|
102
|
+
require 'active_support'
|
103
|
+
puts "%s lines of Hg churn" % (`hg churn`.scan(/^[^\s]+\s+(\d+)\s/).flatten.map(&:to_i).sum).commify
|
117
104
|
end
|
118
|
-
end
|
119
105
|
|
120
|
-
desc "Display lines of
|
121
|
-
task :
|
122
|
-
|
123
|
-
|
106
|
+
desc "Display lines of code based on sloccount"
|
107
|
+
task :sloc do
|
108
|
+
sh "sloccount lib spec misc examples bin"
|
109
|
+
end
|
124
110
|
end
|
125
111
|
|
126
|
-
|
127
|
-
|
128
|
-
end
|
112
|
+
desc "Display the lines of source code and how many lines were changed in the repository"
|
113
|
+
task :loc => ["loc:count", "loc:diff", "loc:churn", "loc:sloc"]
|
129
114
|
|
130
115
|
#---[ RubyGems ]--------------------------------------------------------
|
131
116
|
|
@@ -172,22 +157,26 @@ namespace :gem do
|
|
172
157
|
end
|
173
158
|
end
|
174
159
|
|
160
|
+
desc "Create a gem"
|
175
161
|
task :gem do
|
176
162
|
hoe(:gem)
|
177
163
|
end
|
178
164
|
|
165
|
+
desc "Publish to RubyForge"
|
179
166
|
task :publish do
|
180
167
|
automateit
|
181
168
|
hoe("release VERSION=#{AutomateIt::VERSION}")
|
182
169
|
Rake::Task[:after].invoke
|
183
170
|
end
|
184
171
|
|
172
|
+
desc "Tag a stable release"
|
185
173
|
task :tag do
|
186
174
|
automateit
|
187
175
|
sh "hg tag #{AutomateIt::VERSION}"
|
188
176
|
sh "hg tag -f stable"
|
189
177
|
end
|
190
178
|
|
179
|
+
desc "Push a stable release"
|
191
180
|
task :push do
|
192
181
|
sh "hg push -r stable ../app_stable"
|
193
182
|
end
|
@@ -233,6 +222,7 @@ namespace :install do
|
|
233
222
|
end
|
234
223
|
end
|
235
224
|
|
225
|
+
desc "Uninstall automateit gem"
|
236
226
|
task :uninstall do
|
237
227
|
automateit.package_manager.uninstall "automateit", :with => :gem
|
238
228
|
end
|
data/bin/aissh
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# XXX What can go wrong with this loading approach?
|
4
|
+
libdir = File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
|
5
|
+
if File.directory?(libdir) and File.exists?(File.join(libdir, "automateit.rb"))
|
6
|
+
$LOAD_PATH.unshift(libdir)
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'rubygems'
|
10
|
+
require 'optparse'
|
11
|
+
require 'automateit'
|
12
|
+
include AutomateIt::Constants
|
13
|
+
|
14
|
+
OptionParser.new do |parser|
|
15
|
+
PROG = File.basename($0)
|
16
|
+
opts = {}
|
17
|
+
parser.banner = <<EOB
|
18
|
+
#{PROG} - tool for running command via SSH on hosts matching AutomateIt tags
|
19
|
+
|
20
|
+
Usage: #{PROG} [options] [arguments...]
|
21
|
+
|
22
|
+
IMPORTANT:
|
23
|
+
#{PROG} can only match against tags specified in tags.yml. It cannot
|
24
|
+
match against automatically generated tags like the OS, architecture and such
|
25
|
+
which are created at runtime by PlatformManager or AddressManager. So if you
|
26
|
+
want to run a command against all Ubuntu servers, you must define them
|
27
|
+
explicitly in tags.yml.
|
28
|
+
|
29
|
+
Examples:
|
30
|
+
# Read tags from project in current directory and run "ls" on all hosts
|
31
|
+
# tagged with "apache_servers" or "rails_servers":
|
32
|
+
#{PROG} --project . 'apache_servers | rails_servers' ls
|
33
|
+
|
34
|
+
# Preview SSH commands needed to run "ls" on "apache_servers":
|
35
|
+
#{PROG} -p . -n apache_servers ls
|
36
|
+
|
37
|
+
# Pass arguments to "ls", rather than #{PROG}:
|
38
|
+
#{PROG} -p . -n apache_servers -- ls -la
|
39
|
+
|
40
|
+
# Same as previous but using a quoted string:
|
41
|
+
#{PROG} -p . -n apache_servers 'ls -la'
|
42
|
+
|
43
|
+
# Execute a command which requires the target server to redirect output:
|
44
|
+
#{PROG} -p . -n apache_servers 'ps -ef | grep apache'
|
45
|
+
|
46
|
+
Options:
|
47
|
+
EOB
|
48
|
+
parser.on("-n", "--preview", "Preview without executing commands") do |v|
|
49
|
+
opts[:preview] = v
|
50
|
+
end
|
51
|
+
|
52
|
+
parser.on("-p", "--project PATH", "Set project path") do |v|
|
53
|
+
opts[:project] = v
|
54
|
+
end
|
55
|
+
|
56
|
+
parser.on("-q", "--quiet", "Don't display commands executed") do |v|
|
57
|
+
opts[:verbosity] = Logger::ERROR
|
58
|
+
end
|
59
|
+
|
60
|
+
parser.on("-v", "--version", "Display version") do |v|
|
61
|
+
puts AutomateIt::VERSION
|
62
|
+
exit 0
|
63
|
+
end
|
64
|
+
|
65
|
+
parser.on("-h", "--help", "Display this help message") do |v|
|
66
|
+
puts parser
|
67
|
+
exit
|
68
|
+
end
|
69
|
+
|
70
|
+
args = parser.parse!.dup
|
71
|
+
query = args.shift
|
72
|
+
commands = args
|
73
|
+
|
74
|
+
if commands.blank?
|
75
|
+
puts parser
|
76
|
+
puts "\nERROR: No commands specified"
|
77
|
+
exit 1
|
78
|
+
end
|
79
|
+
|
80
|
+
interpreter = AutomateIt.new(
|
81
|
+
:project => opts[:project],
|
82
|
+
:verbosity => opts[:verbosity]
|
83
|
+
)
|
84
|
+
|
85
|
+
interpreter.preview true if opts[:preview]
|
86
|
+
|
87
|
+
for host in interpreter.hosts_tagged_with(query).sort
|
88
|
+
cmd = "ssh %s %s" % [host, commands.join(" ").shell_escape]
|
89
|
+
interpreter.sh(cmd)
|
90
|
+
end
|
91
|
+
|
92
|
+
exit 0
|
93
|
+
end
|
data/bin/aitag
CHANGED
@@ -18,6 +18,13 @@ OptionParser.new do |parser|
|
|
18
18
|
|
19
19
|
Usage: #{PROG} [options] [arguments...]
|
20
20
|
|
21
|
+
IMPORTANT:
|
22
|
+
#{PROG} can only match against tags specified in tags.yml. It cannot
|
23
|
+
match against automatically generated tags like the OS, architecture and such
|
24
|
+
which are created at runtime by PlatformManager or AddressManager. So if you
|
25
|
+
want to run a command against all Ubuntu servers, you must define them
|
26
|
+
explicitly in tags.yml.
|
27
|
+
|
21
28
|
Examples:
|
22
29
|
# Load 'myproject' and see if it's tagged with 'apache' or 'svn':
|
23
30
|
#{PROG} -p myproject 'apache || svn'
|
data/lib/automateit.rb
CHANGED
@@ -35,6 +35,7 @@ Hash.module_eval{include ActiveSupport::CoreExtensions::Hash::Keys}
|
|
35
35
|
# Extensions
|
36
36
|
require 'ext/object.rb'
|
37
37
|
require 'ext/metaclass.rb'
|
38
|
+
require 'ext/shell_escape.rb'
|
38
39
|
|
39
40
|
# Helpers
|
40
41
|
require 'hashcache'
|
@@ -42,6 +43,7 @@ require 'queued_logger'
|
|
42
43
|
require 'tempster'
|
43
44
|
require 'helpful_erb'
|
44
45
|
require 'nested_error'
|
46
|
+
require 'nitpick'
|
45
47
|
|
46
48
|
# Core
|
47
49
|
require 'automateit/root'
|
@@ -15,8 +15,8 @@ class ::AutomateIt::AccountManager::Etc< ::AutomateIt::AccountManager::BaseDrive
|
|
15
15
|
# the 'etc' module?
|
16
16
|
def self.has_etc?
|
17
17
|
begin
|
18
|
-
require '
|
19
|
-
return defined?(Etc)
|
18
|
+
require 'etc'
|
19
|
+
return defined?(::Etc)
|
20
20
|
rescue LoadError
|
21
21
|
return false
|
22
22
|
end
|
@@ -24,7 +24,7 @@ class ::AutomateIt::AccountManager::Etc< ::AutomateIt::AccountManager::BaseDrive
|
|
24
24
|
|
25
25
|
# Alias for AccountManager::Etc.has_etc?
|
26
26
|
def has_etc?
|
27
|
-
self.has_etc?
|
27
|
+
self.class.has_etc?
|
28
28
|
end
|
29
29
|
|
30
30
|
#.......................................................................
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# AccountManager driver for invalidating records stored in the NSCD, Name
|
4
4
|
# Service Cache Daemon, found on Unix-like systems.
|
5
5
|
class ::AutomateIt::AccountManager::NSCD < ::AutomateIt::AccountManager::BaseDriver
|
6
|
-
depends_on :programs => %w(nscd ps),
|
6
|
+
depends_on :programs => %w(nscd ps),
|
7
7
|
:callbacks => [lambda{`ps -ef`.match(%r{/usr/sbin/nscd$})}]
|
8
8
|
|
9
9
|
def suitability(method, *args) # :nodoc:
|
@@ -11,18 +11,21 @@ class ::AutomateIt::AccountManager::NSCD < ::AutomateIt::AccountManager::BaseDri
|
|
11
11
|
return available? ? 5 : 0
|
12
12
|
end
|
13
13
|
|
14
|
+
# Returns the NSCD database for the specified shorthand +query+.
|
15
|
+
def database_for(query)
|
16
|
+
case query.to_sym
|
17
|
+
when :user, :users, :passwd, :password
|
18
|
+
:passwd
|
19
|
+
when :group, :groups
|
20
|
+
:group
|
21
|
+
else
|
22
|
+
raise ArgumentError.new("Unknown cache database: #{query}")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
14
26
|
def invalidate(database)
|
15
27
|
return false unless available?
|
16
28
|
|
17
|
-
|
18
|
-
case database.to_sym
|
19
|
-
when :user, :users, :passwd
|
20
|
-
:passwd
|
21
|
-
when :group, :groups
|
22
|
-
:group
|
23
|
-
else
|
24
|
-
raise ArgumentError.new("Unknown cache database: #{database}")
|
25
|
-
end
|
26
|
-
interpreter.sh("nscd -i #{name}")
|
29
|
+
interpreter.sh("nscd -i #{database_for(database)}")
|
27
30
|
end
|
28
31
|
end
|
@@ -155,7 +155,7 @@ class AutomateIt::AddressManager::BaseDriver< AutomateIt::Plugin::Driver
|
|
155
155
|
ipcmd = "ifconfig"
|
156
156
|
ipcmd << " " << _interface_and_label(opts)
|
157
157
|
if helper_opts[:prepend]
|
158
|
-
ipcmd << " " << helper_opts[:prepend].join(" ")
|
158
|
+
ipcmd << " " << [helper_opts[:prepend]].flatten.join(" ")
|
159
159
|
end
|
160
160
|
ipcmd << " %s" % opts[:address]
|
161
161
|
ipcmd << " netmask %s" % opts[:mask] if opts[:mask]
|
@@ -164,7 +164,7 @@ class AutomateIt::AddressManager::BaseDriver< AutomateIt::Plugin::Driver
|
|
164
164
|
ipcmd << " down" if action == :del
|
165
165
|
end
|
166
166
|
if helper_opts[:append]
|
167
|
-
ipcmd << " " << helper_opts[:append].join(" ")
|
167
|
+
ipcmd << " " << [helper_opts[:append]].flatten.join(" ")
|
168
168
|
end
|
169
169
|
return ipcmd
|
170
170
|
end
|
@@ -82,6 +82,8 @@ module AutomateIt
|
|
82
82
|
# how to more easily dispatch commands from your program to the Interpreter
|
83
83
|
# instance.
|
84
84
|
class Interpreter < Common
|
85
|
+
include Nitpick
|
86
|
+
|
85
87
|
# Plugin instance that instantiated the Interpreter.
|
86
88
|
attr_accessor :parent
|
87
89
|
private :parent
|
@@ -627,24 +629,5 @@ module AutomateIt
|
|
627
629
|
text = ::HelpfulERB.new(template).result(binding)
|
628
630
|
object.instance_eval(text)
|
629
631
|
end
|
630
|
-
|
631
|
-
# Use to manage nitpick message for debugging AutomateIt internals.
|
632
|
-
#
|
633
|
-
# Arguments:
|
634
|
-
# * nil -- Returns boolean of whether nitpick messages will be displayed.
|
635
|
-
# * Boolean -- Sets nitpick state.
|
636
|
-
# * String or Symbol -- Displays nitpick message if state is on.
|
637
|
-
#
|
638
|
-
# Example:
|
639
|
-
# nitpick true
|
640
|
-
# nitpick "I'm nitpicking"
|
641
|
-
def nitpick(value=nil)
|
642
|
-
case value
|
643
|
-
when NilClass: @nitpick
|
644
|
-
when TrueClass, FalseClass: @nitpick = value
|
645
|
-
when String, Symbol: puts "%% #{value}" if @nitpick
|
646
|
-
else raise TypeError.new("Unknown nitpick type: #{value.class}")
|
647
|
-
end
|
648
|
-
end
|
649
632
|
end
|
650
633
|
end
|
@@ -222,6 +222,8 @@ class AutomateIt::PackageManager::BaseDriver < AutomateIt::Plugin::Driver
|
|
222
222
|
result = packages.map(&:to_s).grep(LIST_NORMALIZER_RE)
|
223
223
|
when Hash
|
224
224
|
result = packages.stringify_keys
|
225
|
+
when Symbol, String
|
226
|
+
result = packages.to_s
|
225
227
|
else
|
226
228
|
raise TypeError.new("Unknown input type: #{packages.class}")
|
227
229
|
end
|
@@ -199,12 +199,7 @@ module AutomateIt
|
|
199
199
|
# instance found, else raises a NotImplementedError if no suitable driver
|
200
200
|
# is found.
|
201
201
|
def driver_for(method, *args, &block)
|
202
|
-
|
203
|
-
driver, level = driver_suitability_levels_for(method, *args, &block).sort_by{|k,v| v}.last
|
204
|
-
rescue IndexError
|
205
|
-
driver = nil
|
206
|
-
level = -1
|
207
|
-
end
|
202
|
+
driver, level = driver_suitability_levels_for(method, *args, &block).sort_by{|k,v| v}.last
|
208
203
|
if driver and level > 0
|
209
204
|
return @drivers[driver]
|
210
205
|
else
|
data/lib/automateit/root.rb
CHANGED
@@ -2,13 +2,13 @@
|
|
2
2
|
#
|
3
3
|
# Helper class for parsing tags. Not useful for users -- for internal use only.
|
4
4
|
class AutomateIt::TagManager::TagParser
|
5
|
+
include Nitpick
|
6
|
+
|
5
7
|
attr_accessor :struct
|
6
|
-
attr_accessor :is_trace # FIXME replace is_trace with nitpick
|
7
8
|
|
8
9
|
# Create a parser for the +struct+, a hash of tag keys to values with arrays of items.
|
9
|
-
def initialize(struct
|
10
|
+
def initialize(struct)
|
10
11
|
self.struct = struct
|
11
|
-
self.is_trace = is_trace
|
12
12
|
normalize!
|
13
13
|
end
|
14
14
|
|
@@ -33,11 +33,6 @@ class AutomateIt::TagManager::TagParser
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
# Display debugging information if +is_trace+ is enabled.
|
37
|
-
def trace(msg)
|
38
|
-
puts msg if is_trace
|
39
|
-
end
|
40
|
-
|
41
36
|
HOSTS_FOR_VALUE = /(.+?)/
|
42
37
|
HOSTS_FOR_INCLUDE_TAG_RE = /^INCLUDE_TAG #{HOSTS_FOR_VALUE}$/
|
43
38
|
HOSTS_FOR_EXCLUDE_TAG_RE = /^EXCLUDE_TAG #{HOSTS_FOR_VALUE}$/
|
@@ -45,27 +40,29 @@ class AutomateIt::TagManager::TagParser
|
|
45
40
|
|
46
41
|
# Return array of hosts for the +tag+.
|
47
42
|
def hosts_for(tag)
|
48
|
-
raise IndexError.new("Unknown tag - #{tag}") unless struct
|
49
|
-
|
43
|
+
raise IndexError.new("Unknown tag - #{tag}") unless struct.has_key?(tag)
|
44
|
+
return [] if struct[tag].nil? # Tag has no leaves
|
45
|
+
|
46
|
+
nitpick "\nAA %s" % tag
|
50
47
|
hosts = Set.new
|
51
48
|
for item in struct[tag]
|
52
49
|
case item
|
53
50
|
when HOSTS_FOR_INCLUDE_TAG_RE
|
54
|
-
|
51
|
+
nitpick "+g %s" % $1
|
55
52
|
hosts.merge(hosts_for($1))
|
56
53
|
when HOSTS_FOR_EXCLUDE_TAG_RE
|
57
|
-
|
54
|
+
nitpick "-g %s" % $1
|
58
55
|
hosts.subtract(hosts_for($1))
|
59
56
|
when HOSTS_FOR_EXCLUDE_HOST_RE
|
60
|
-
|
57
|
+
nitpick "-h %s" % $1
|
61
58
|
hosts.delete($1)
|
62
59
|
else
|
63
|
-
|
60
|
+
nitpick "+h %s" % item
|
64
61
|
hosts << item
|
65
62
|
end
|
66
63
|
end
|
67
64
|
result = hosts.to_a
|
68
|
-
|
65
|
+
nitpick "ZZ %s for %s" % [result.inspect, tag]
|
69
66
|
return result
|
70
67
|
end
|
71
68
|
|
@@ -90,7 +87,7 @@ class AutomateIt::TagManager::TagParser
|
|
90
87
|
end
|
91
88
|
|
92
89
|
# Expand the +struct+.
|
93
|
-
def self.expand(struct
|
94
|
-
self.new(struct
|
90
|
+
def self.expand(struct)
|
91
|
+
self.new(struct).expand
|
95
92
|
end
|
96
93
|
end
|