capistrano-filter 0.2

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.
@@ -0,0 +1,8 @@
1
+ unless Capistrano::Configuration.respond_to?(:instance)
2
+ abort "capistrano/ext/filter requires Capistrano 2"
3
+ end
4
+
5
+ Capistrano::Configuration.instance.load do
6
+ require 'capistrano/recipes/deploy/strategy/copy_and_filter'
7
+ require 'capistrano/recipes/deploy/strategy/filter'
8
+ end
@@ -0,0 +1,232 @@
1
+ require 'capistrano/recipes/deploy/strategy/base'
2
+ require 'fileutils'
3
+ require 'tempfile' # Dir.tmpdir
4
+ #require 'filter.rb'
5
+
6
+ module Capistrano
7
+ module Deploy
8
+ module Strategy
9
+
10
+ # This class implements the strategy for deployments which work
11
+ # by preparing the source code locally, compressing it, copying the
12
+ # file to each target host, and uncompressing it to the deployment
13
+ # directory.
14
+ #
15
+ # By default, the SCM checkout command is used to obtain the local copy
16
+ # of the source code. If you would rather use the export operation,
17
+ # you can set the :copy_strategy variable to :export.
18
+ #
19
+ # set :copy_strategy, :export
20
+ #
21
+ # For even faster deployments, you can set the :copy_cache variable to
22
+ # true. This will cause deployments to do a new checkout of your
23
+ # repository to a new directory, and then copy that checkout. Subsequent
24
+ # deploys will just resync that copy, rather than doing an entirely new
25
+ # checkout. Additionally, you can specify file patterns to exclude from
26
+ # the copy when using :copy_cache; just set the :copy_exclude variable
27
+ # to a file glob (or an array of globs).
28
+ #
29
+ # set :copy_cache, true
30
+ # set :copy_exclude, ".git/*"
31
+ #
32
+ # Note that :copy_strategy is ignored when :copy_cache is set. Also, if
33
+ # you want the copy cache put somewhere specific, you can set the variable
34
+ # to the path you want, instead of merely 'true':
35
+ #
36
+ # set :copy_cache, "/tmp/caches/myapp"
37
+ #
38
+ # This deployment strategy also supports a special variable,
39
+ # :copy_compression, which must be one of :gzip, :bz2, or
40
+ # :zip, and which specifies how the source should be compressed for
41
+ # transmission to each host.
42
+ class CopyAndFilter < Base
43
+ # Obtains a copy of the source code locally (via the #command method),
44
+ # compresses it to a single file, copies that file to all target
45
+ # servers, and uncompresses it on each of them into the deployment
46
+ # directory.
47
+ def deploy!
48
+ if copy_cache
49
+ if File.exists?(copy_cache)
50
+ logger.debug "refreshing local cache to revision #{revision} at #{copy_cache}"
51
+ system(source.sync(revision, copy_cache))
52
+ else
53
+ logger.debug "preparing local cache at #{copy_cache}"
54
+ system(source.checkout(revision, copy_cache))
55
+ end
56
+
57
+ # Check the return code of last system command and rollback if not 0
58
+ unless $? == 0
59
+ raise Capistrano::Error, "shell command failed with return code #{$?}"
60
+ end
61
+
62
+ logger.debug "copying cache to deployment staging area #{destination}"
63
+ Dir.chdir(copy_cache) do
64
+ FileUtils.mkdir_p(destination)
65
+ queue = Dir.glob("*", File::FNM_DOTMATCH)
66
+ while queue.any?
67
+ item = queue.shift
68
+ name = File.basename(item)
69
+
70
+ next if name == "." || name == ".."
71
+ next if copy_exclude.any? { |pattern| File.fnmatch(pattern, item) }
72
+
73
+ if File.symlink?(item)
74
+ FileUtils.ln_s(File.readlink(File.join(copy_cache, item)), File.join(destination, item))
75
+ elsif File.directory?(item)
76
+ queue += Dir.glob("#{item}/*", File::FNM_DOTMATCH)
77
+ FileUtils.mkdir(File.join(destination, item))
78
+ else
79
+ FileUtils.ln(File.join(copy_cache, item), File.join(destination, item))
80
+ end
81
+ end
82
+ end
83
+ else
84
+ logger.debug "getting (via #{copy_strategy}) revision #{revision} to #{destination}"
85
+ system(command)
86
+
87
+ if copy_exclude.any?
88
+ logger.debug "processing exclusions..."
89
+ if copy_exclude.any?
90
+ copy_exclude.each do |pattern|
91
+ delete_list = Dir.glob(File.join(destination, pattern), File::FNM_DOTMATCH)
92
+ # avoid the /.. trap that deletes the parent directories
93
+ delete_list.delete_if { |dir| dir =~ /\/\.\.$/ }
94
+ FileUtils.rm_rf(delete_list.compact)
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ if filtering_props and not filtering_props.empty? then
101
+ #_cset(:filtering_files, "*") unless filtering_files
102
+ #filtering_files = [*filtering_files]
103
+ _filter_using = filtering_using #FIXME Valor default? ? filtering_using : :ruby
104
+ _filter_files = [*filtering_files]
105
+ logger.info "filtering files #{destination} with pattern #{_filter_files.join(',')}"
106
+ _filter_criteria = filter.from(destination).force().using(_filter_using, filtering_props)
107
+ _filter_files.each do |file|
108
+ _filter_criteria.include(file)
109
+ end
110
+ _filter_criteria.into(destination).run()
111
+ end
112
+
113
+ File.open(File.join(destination, "REVISION"), "w") { |f| f.puts(revision) }
114
+
115
+ logger.trace "compressing #{destination} to #{filename}"
116
+ Dir.chdir(tmpdir) { system(compress(File.basename(destination), File.basename(filename)).join(" ")) }
117
+
118
+ upload(filename, remote_filename)
119
+ run "cd #{configuration[:releases_path]} && #{decompress(remote_filename).join(" ")} && rm #{remote_filename}"
120
+ ensure
121
+ FileUtils.rm filename rescue nil
122
+ FileUtils.rm_rf destination rescue nil
123
+ end
124
+
125
+ def check!
126
+ super.check do |d|
127
+ d.local.command(source.local.command) if source.local.command
128
+ d.local.command(compress(nil, nil).first)
129
+ d.remote.command(decompress(nil).first)
130
+ end
131
+ end
132
+
133
+ # Returns the location of the local copy cache, if the strategy should
134
+ # use a local cache + copy instead of a new checkout/export every
135
+ # time. Returns +nil+ unless :copy_cache has been set. If :copy_cache
136
+ # is +true+, a default cache location will be returned.
137
+ def copy_cache
138
+ @copy_cache ||= configuration[:copy_cache] == true ?
139
+ File.join(Dir.tmpdir, configuration[:application]) :
140
+ configuration[:copy_cache]
141
+ end
142
+
143
+ private
144
+
145
+ # Specify patterns to exclude from the copy. This is only valid
146
+ # when using a local cache.
147
+ def copy_exclude
148
+ @copy_exclude ||= Array(configuration.fetch(:copy_exclude, []))
149
+ end
150
+
151
+ # Returns the basename of the release_path, which will be used to
152
+ # name the local copy and archive file.
153
+ def destination
154
+ @destination ||= File.join(tmpdir, File.basename(configuration[:release_path]))
155
+ end
156
+
157
+ # Returns the value of the :copy_strategy variable, defaulting to
158
+ # :checkout if it has not been set.
159
+ def copy_strategy
160
+ @copy_strategy ||= configuration.fetch(:copy_strategy, :checkout)
161
+ end
162
+
163
+ # Should return the command(s) necessary to obtain the source code
164
+ # locally.
165
+ def command
166
+ @command ||= case copy_strategy
167
+ when :checkout
168
+ source.checkout(revision, destination)
169
+ when :export
170
+ source.export(revision, destination)
171
+ end
172
+ end
173
+
174
+ # Returns the name of the file that the source code will be
175
+ # compressed to.
176
+ def filename
177
+ @filename ||= File.join(tmpdir, "#{File.basename(destination)}.#{compression.extension}")
178
+ end
179
+
180
+ # The directory to which the copy should be checked out
181
+ def tmpdir
182
+ @tmpdir ||= configuration[:copy_dir] || Dir.tmpdir
183
+ end
184
+
185
+ # The directory on the remote server to which the archive should be
186
+ # copied
187
+ def remote_dir
188
+ @remote_dir ||= configuration[:copy_remote_dir] || "/tmp"
189
+ end
190
+
191
+ # The location on the remote server where the file should be
192
+ # temporarily stored.
193
+ def remote_filename
194
+ @remote_filename ||= File.join(remote_dir, File.basename(filename))
195
+ end
196
+
197
+ # A struct for representing the specifics of a compression type.
198
+ # Commands are arrays, where the first element is the utility to be
199
+ # used to perform the compression or decompression.
200
+ Compression = Struct.new(:extension, :compress_command, :decompress_command)
201
+
202
+ # The compression method to use, defaults to :gzip.
203
+ def compression
204
+ remote_tar = configuration[:copy_remote_tar] || 'tar'
205
+ local_tar = configuration[:copy_local_tar] || 'tar'
206
+
207
+ type = configuration[:copy_compression] || :gzip
208
+ case type
209
+ when :gzip, :gz then Compression.new("tar.gz", [local_tar, 'czf'], [remote_tar, 'xzf'])
210
+ when :bzip2, :bz2 then Compression.new("tar.bz2", [local_tar, 'cjf'], [remote_tar, 'xjf'])
211
+ when :zip then Compression.new("zip", %w(zip -qr), %w(unzip -q))
212
+ else raise ArgumentError, "invalid compression type #{type.inspect}"
213
+ end
214
+ end
215
+
216
+ # Returns the command necessary to compress the given directory
217
+ # into the given file.
218
+ def compress(directory, file)
219
+ compression.compress_command + [file, directory]
220
+ end
221
+
222
+ # Returns the command necessary to decompress the given file,
223
+ # relative to the current working directory. It must also
224
+ # preserve the directory structure in the file.
225
+ def decompress(file)
226
+ compression.decompress_command + [file]
227
+ end
228
+ end
229
+
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,423 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with this
3
+ # work for additional information regarding copyright ownership. The ASF
4
+ # licenses this file to you under the Apache License, Version 2.0 (the
5
+ # "License"); you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations under
14
+ # the License.
15
+
16
+
17
+ require 'erb'
18
+ require 'rake'
19
+
20
+ # A filter knows how to copy files from one directory to another, applying mappings to the
21
+ # contents of these files.
22
+ #
23
+ # You can specify the mapping using a Hash, and it will map ${key} fields found in each source
24
+ # file into the appropriate value in the target file. For example:
25
+ #
26
+ # filter.using 'version'=>'1.2', 'build'=>Time.now
27
+ #
28
+ # will replace all occurrences of <tt>${version}</tt> with <tt>1.2</tt>, and <tt>${build}</tt>
29
+ # with the current date/time.
30
+ #
31
+ # You can also specify the mapping by passing a proc or a method, that will be called for
32
+ # each source file, with the file name and content, returning the modified content.
33
+ #
34
+ # Without any mapping, the filter simply copies files from the source directory into the target
35
+ # directory.
36
+ #
37
+ # A filter has one target directory, but you can specify any number of source directories,
38
+ # either when creating the filter or calling #from. Include/exclude patterns are specified
39
+ # relative to the source directories, so:
40
+ # filter.include '*.png'
41
+ # will only include PNG files from any of the source directories.
42
+ # In the same way, you can use regular expressions, so:
43
+ # filter.include /picture_.*\.png/
44
+ # will only include PNG files starting with picture_ from any of the sources directories.
45
+ #
46
+ # See Buildr#filter.
47
+ class Filter
48
+
49
+ def initialize #:nodoc:
50
+ clear
51
+ @force = true
52
+ end
53
+
54
+ # Returns the list of source directories (each being a file task).
55
+ attr_reader :sources
56
+
57
+ # :call-seq:
58
+ # clear => self
59
+ #
60
+ # Clear filter sources and include/exclude patterns
61
+ def clear
62
+ @include = []
63
+ @exclude = []
64
+ @sources = Rake::FileList[]
65
+ @mapper = Mapper.new
66
+ self
67
+ end
68
+
69
+ def force
70
+ @force = true
71
+ self
72
+ end
73
+
74
+ def noforce
75
+ @force = false
76
+ self
77
+ end
78
+
79
+ # :call-seq:
80
+ # from(*sources) => self
81
+ #
82
+ # Adds additional directories from which to copy resources.
83
+ #
84
+ # For example:
85
+ # filter.from('src').into('target').using('build'=>Time.now)
86
+ def from(*sources)
87
+ @sources |= sources.flatten.map { |dir| file(File.expand_path(dir.to_s)) }
88
+ self
89
+ end
90
+
91
+ # The target directory as a file task.
92
+ def target
93
+ return nil unless @target_dir
94
+ unless @target
95
+ @target = file(File.expand_path(@target_dir)) { |task| run if @target == task }
96
+ @target.enhance @include.select {|f| f.is_a?(Rake::FileTask)}
97
+ @target.enhance @exclude.select {|f| f.is_a?(Rake::FileTask)}
98
+ @target.enhance copy_map.values
99
+ end
100
+ @target
101
+ end
102
+
103
+ # :call-seq:
104
+ # into(dir) => self
105
+ #
106
+ # Sets the target directory into which files are copied and returns self.
107
+ #
108
+ # For example:
109
+ # filter.from('src').into('target').using('build'=>Time.now)
110
+ def into(dir)
111
+ @target_dir = dir.to_s
112
+ @target = nil
113
+ self
114
+ end
115
+
116
+ # :call-seq:
117
+ # include(*files) => self
118
+ #
119
+ # Specifies files to include and returns self. See FileList#include.
120
+ #
121
+ # By default all files are included. You can use this method to only include specific
122
+ # files from the source directory.
123
+ def include(*files)
124
+ @include += files.flatten
125
+ self
126
+ end
127
+ alias :add :include
128
+
129
+ # :call-seq:
130
+ # exclude(*files) => self
131
+ #
132
+ # Specifies files to exclude and returns self. See FileList#exclude.
133
+ def exclude(*files)
134
+ @exclude += files.flatten
135
+ self
136
+ end
137
+
138
+ # The mapping. See #using.
139
+ def mapping #:nodoc:
140
+ @mapper.config
141
+ end
142
+
143
+ # The mapper to use. See #using.
144
+ def mapper #:nodoc:
145
+ @mapper.mapper_type
146
+ end
147
+
148
+ # :call-seq:
149
+ # using(mapping) => self
150
+ # using { |file_name, contents| ... } => self
151
+ #
152
+ # Specifies the mapping to use and returns self.
153
+ #
154
+ # The most typical mapping uses a Hash, and the default mapping uses the Maven style, so
155
+ # <code>${key}</code> are mapped to the values. You can change that by passing a different
156
+ # format as the first argument. Currently supports:
157
+ # * :ant -- Map <code>@key@</code>.
158
+ # * :maven -- Map <code>${key}</code> (default).
159
+ # * :ruby -- Map <code>#{key}</code>.
160
+ # * :erb -- Map <code><%= key %></code>.
161
+ # * Regexp -- Maps the matched data (e.g. <code>/=(.*?)=/</code>
162
+ #
163
+ # For example:
164
+ # filter.using 'version'=>'1.2'
165
+ # Is the same as:
166
+ # filter.using :maven, 'version'=>'1.2'
167
+ #
168
+ # You can also pass a proc or method. It will be called with the file name and content,
169
+ # to return the mapped content.
170
+ #
171
+ # Without any mapping, all files are copied as is.
172
+ #
173
+ # To register new mapping type see the Mapper class.
174
+ def using(*args, &block)
175
+ @mapper.using(*args, &block)
176
+ self
177
+ end
178
+
179
+ def recursive_with_dot_files(*dirs)
180
+ FileList[dirs.map { |dir| File.join(dir, '/**/{*,.*}') }].reject { |file| File.basename(file) =~ /^[.]{1,2}$/ }
181
+ end
182
+
183
+ def relative_path(to, from = '.')
184
+ to = Pathname.new(to).cleanpath
185
+ return to.to_s if from.nil?
186
+ to_path = Pathname.new(File.expand_path(to.to_s, "/"))
187
+ from_path = Pathname.new(File.expand_path(from.to_s, "/"))
188
+ to_path.relative_path_from(from_path).to_s
189
+ end
190
+
191
+ # :call-seq:
192
+ # run => boolean
193
+ #
194
+ # Runs the filter.
195
+ def run
196
+ copy_map = copy_map()
197
+
198
+ mkpath target.to_s
199
+ return false if copy_map.empty?
200
+
201
+ copy_map.each do |path, source|
202
+ dest = File.expand_path(path, target.to_s)
203
+ if File.directory?(source)
204
+ mkpath dest
205
+ else
206
+ mkpath File.dirname(dest)
207
+ if @mapper.mapper_type
208
+ mapped = @mapper.transform(File.open(source, 'rb') { |file| file.read }, path)
209
+ File.open(dest, 'wb') { |file| file.write mapped }
210
+ else # no mapping
211
+ cp source, dest
212
+ end
213
+ end
214
+ File.chmod(File.stat(source).mode | 0200, dest)
215
+ end
216
+ touch target.to_s
217
+ true
218
+ end
219
+
220
+ # Returns the target directory.
221
+ def to_s
222
+ target.to_s
223
+ end
224
+
225
+ protected
226
+
227
+ # :call-seq:
228
+ # pattern_match(file, pattern) => boolean
229
+ #
230
+ # This method returns true if the file name matches the pattern.
231
+ # The pattern may be a String, a Regexp or a Proc.
232
+ #
233
+ def pattern_match(file, pattern)
234
+ case
235
+ when pattern.is_a?(Regexp)
236
+ return file.match(pattern)
237
+ when pattern.is_a?(String)
238
+ return File.fnmatch(pattern, file)
239
+ when pattern.is_a?(Proc)
240
+ return pattern.call(file)
241
+ when pattern.is_a?(Rake::FileTask)
242
+ return pattern.to_s.match(file)
243
+ else
244
+ raise "Cannot interpret pattern #{pattern}"
245
+ end
246
+ end
247
+
248
+ private
249
+ def copy_map
250
+ sources.each { |source| raise "Source directory #{source} doesn't exist" unless File.exist?(source.to_s) }
251
+ raise 'No target directory specified, where am I going to copy the files to?' if target.nil?
252
+
253
+ sources.flatten.map(&:to_s).inject({}) do |map, source|
254
+ files = recursive_with_dot_files(source).
255
+ map { |file| relative_path(file, source) }.
256
+ select { |file| @include.empty? || @include.any? { |pattern| pattern_match(file, pattern) } }.
257
+ reject { |file| @exclude.any? { |pattern| pattern_match(file, pattern) } }
258
+ files.each do |file|
259
+ src, dest = File.expand_path(file, source), File.expand_path(file, target.to_s)
260
+ map[file] = src if !File.exist?(dest) || File.stat(src).mtime >= File.stat(dest).mtime || @force
261
+ end
262
+ map
263
+ end
264
+ end
265
+
266
+ # This class implements content replacement logic for Filter.
267
+ #
268
+ # To register a new template engine @:foo@, extend this class with a method like:
269
+ #
270
+ # def foo_transform(content, path = nil)
271
+ # # if this method yields a key, the value comes from the mapping hash
272
+ # content.gsub(/world/) { |str| yield :bar }
273
+ # end
274
+ #
275
+ # Then you can use :foo mapping type on a Filter
276
+ #
277
+ # filter.using :foo, :bar => :baz
278
+ #
279
+ # Or all by your own, simply
280
+ #
281
+ # Mapper.new(:foo, :bar => :baz).transform("Hello world") # => "Hello baz"
282
+ #
283
+ # You can handle configuration arguments by providing a @*_config@ method like:
284
+ #
285
+ # # The return value of this method is available with the :config accessor.
286
+ # def moo_config(*args, &block)
287
+ # raise ArgumentError, "Expected moo block" unless block_given?
288
+ # { :moos => args, :callback => block }
289
+ # end
290
+ #
291
+ # def moo_transform(content, path = nil)
292
+ # content.gsub(/moo+/i) do |str|
293
+ # moos = yield :moos # same than config[:moos]
294
+ # moo = moos[str.size - 3] || str
295
+ # config[:callback].call(moo)
296
+ # end
297
+ # end
298
+ #
299
+ # Usage for the @:moo@ mapper would be something like:
300
+ #
301
+ # mapper = Mapper.new(:moo, 'ooone', 'twoo') do |str|
302
+ # i = nil; str.capitalize.gsub(/\w/) { |s| s.send( (i = !i) ? 'upcase' : 'downcase' ) }
303
+ # end
304
+ # mapper.transform('Moo cow, mooo cows singing mooooo') # => 'OoOnE cow, TwOo cows singing MoOoOo'
305
+ class Mapper
306
+
307
+ attr_reader :mapper_type, :config
308
+
309
+ def initialize(*args, &block) #:nodoc:
310
+ using(*args, &block)
311
+ end
312
+
313
+ def using(*args, &block)
314
+ case args.first
315
+ when Hash # Maven hash mapping
316
+ using :maven, *args
317
+ when Binding # Erb binding
318
+ using :erb, *args
319
+ when Symbol # Mapping from a method
320
+ raise ArgumentError, "Unknown mapping type: #{args.first}" unless respond_to?("#{args.first}_transform", true)
321
+ configure(*args, &block)
322
+ when Regexp # Mapping using a regular expression
323
+ raise ArgumentError, 'Expected regular expression followed by mapping hash' unless args.size == 2 && Hash === args[1]
324
+ @mapper_type, @config = *args
325
+ else
326
+ unless args.empty? && block.nil?
327
+ raise ArgumentError, 'Expected proc, method or a block' if args.size > 1 || (args.first && block)
328
+ @mapper_type = :callback
329
+ config = args.first || block
330
+ raise ArgumentError, 'Expected proc, method or callable' unless config.respond_to?(:call)
331
+ @config = config
332
+ end
333
+ end
334
+ self
335
+ end
336
+
337
+ def transform(content, path = nil)
338
+ type = Regexp === mapper_type ? :regexp : mapper_type
339
+ raise ArgumentError, "Invalid mapper type: #{type.inspect}" unless respond_to?("#{type}_transform", true)
340
+ self.__send__("#{type}_transform", content, path) { |key| config[key] || config[key.to_s.to_sym] }
341
+ end
342
+
343
+ private
344
+ def configure(mapper_type, *args, &block)
345
+ configurer = method("#{mapper_type}_config") rescue nil
346
+ if configurer
347
+ @config = configurer.call(*args, &block)
348
+ else
349
+ raise ArgumentError, "Missing hash argument after :#{mapper_type}" unless args.size == 1 && Hash === args[0]
350
+ @config = {} unless Hash === @config
351
+ args.first.each_pair { |k, v| @config[k] = v.to_s }
352
+ end
353
+ @mapper_type = mapper_type
354
+ end
355
+
356
+ def maven_transform(content, path = nil)
357
+ content.gsub(/\$\{.*?\}/) { |str| yield(str[2..-2]) || str }
358
+ end
359
+
360
+ def ant_transform(content, path = nil)
361
+ content.gsub(/@.*?@/) { |str| yield(str[1..-2]) || str }
362
+ end
363
+
364
+ def ruby_transform(content, path = nil)
365
+ content.gsub(/#\{.*?\}/) { |str| yield(str[2..-2]) || str }
366
+ end
367
+
368
+ def regexp_transform(content, path = nil)
369
+ content.gsub(mapper_type) { |str| yield(str.scan(mapper_type).join) || str }
370
+ end
371
+
372
+ def callback_transform(content, path = nil)
373
+ config.call(path, content)
374
+ end
375
+
376
+ def erb_transform(content, path = nil)
377
+ case config
378
+ when Binding
379
+ bnd = config
380
+ when Hash
381
+ bnd = OpenStruct.new
382
+ table = config.inject({}) { |h, e| h[e.first.to_sym] = e.last; h }
383
+ bnd.instance_variable_set(:@table, table)
384
+ bnd = bnd.instance_eval { binding }
385
+ else
386
+ bnd = config.instance_eval { binding }
387
+ end
388
+ require 'erb'
389
+ ERB.new(content).result(bnd)
390
+ end
391
+
392
+ def erb_config(*args, &block)
393
+ if block_given?
394
+ raise ArgumentError, "Expected block or single argument, but both given." unless args.empty?
395
+ block
396
+ elsif args.size > 1
397
+ raise ArgumentError, "Expected block or single argument."
398
+ else
399
+ args.first
400
+ end
401
+ end
402
+
403
+ end # class Mapper
404
+
405
+ end
406
+
407
+ # :call-seq:
408
+ # filter(*source) => Filter
409
+ #
410
+ # Creates a filter that will copy files from the source directory(ies) into the target directory.
411
+ # You can extend the filter to modify files by mapping <tt>${key}</tt> into values in each
412
+ # of the copied files, and by including or excluding specific files.
413
+ #
414
+ # A filter is not a task, you must call the Filter#run method to execute it.
415
+ #
416
+ # For example, to copy all files from one directory to another:
417
+ # filter('src/files').into('target/classes').run
418
+ # To include only the text files, and replace each instance of <tt>${build}</tt> with the current
419
+ # date/time:
420
+ # filter('src/files').into('target/classes').include('*.txt').using('build'=>Time.now).run
421
+ def filter(*sources)
422
+ Filter.new.from(*sources)
423
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: capistrano-filter
3
+ version: !ruby/object:Gem::Version
4
+ hash: 15
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 2
9
+ version: "0.2"
10
+ platform: ruby
11
+ authors:
12
+ - "@"
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-05-13 00:00:00 -03:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: capistrano
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - "="
27
+ - !ruby/object:Gem::Version
28
+ hash: 61
29
+ segments:
30
+ - 2
31
+ - 5
32
+ - 19
33
+ version: 2.5.19
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ description: ops
37
+ email:
38
+ - "@"
39
+ executables: []
40
+
41
+ extensions: []
42
+
43
+ extra_rdoc_files: []
44
+
45
+ files:
46
+ - lib/capistrano/ext/filter.rb
47
+ - lib/capistrano/recipes/deploy/strategy/filter.rb
48
+ - lib/capistrano/recipes/deploy/strategy/copy_and_filter.rb
49
+ has_rdoc: true
50
+ homepage: ""
51
+ licenses: []
52
+
53
+ post_install_message:
54
+ rdoc_options: []
55
+
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ hash: 3
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ hash: 3
73
+ segments:
74
+ - 0
75
+ version: "0"
76
+ requirements: []
77
+
78
+ rubyforge_project: capistrano-filter
79
+ rubygems_version: 1.5.2
80
+ signing_key:
81
+ specification_version: 3
82
+ summary: possibilita o uso de filtro no deploy pelo capistrano
83
+ test_files: []
84
+