chirp 0.2.0 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +0 -0
- data/Manifest.txt +44 -2
- data/README.txt +10 -2
- data/Rakefile +1 -1
- data/bin/chirp +6 -4
- data/lib/chirp.rb +7 -3
- data/lib/chirp/application.rb +248 -159
- data/lib/chirp/context.rb +101 -0
- data/lib/chirp/fs_expression.rb +180 -0
- data/lib/chirp/path_filter.rb +58 -0
- data/lib/chirp/pathmap.rb +200 -0
- data/lib/chirp/span.rb +38 -0
- data/test/account.dir/input +0 -0
- data/test/account.dir/output +0 -0
- data/test/account.dir/program.chirp +7 -4
- data/test/action.dir/input +0 -0
- data/test/action.dir/output +0 -0
- data/test/action.dir/program.chirp +2 -2
- data/test/beforeafter.dir/input +0 -0
- data/test/beforeafter.dir/output +0 -0
- data/test/beforeafter.dir/program.chirp +3 -3
- data/test/copy1.dir/input +0 -0
- data/test/copy1.dir/output +5 -0
- data/test/copy1.dir/program.chirp +9 -0
- data/test/copy1.dir/source +2 -0
- data/test/copy2.dir/input +0 -0
- data/test/copy2.dir/output +5 -0
- data/test/copy2.dir/program.chirp +9 -0
- data/test/copy2.dir/source +2 -0
- data/test/copy3.dir/input +0 -0
- data/test/copy3.dir/output +5 -0
- data/test/copy3.dir/program.chirp +7 -0
- data/test/copy3.dir/source +2 -0
- data/test/copy_file.dir/a.txt +1 -0
- data/test/copy_file.dir/b.txt +1 -0
- data/test/copy_file.dir/c.txt +1 -0
- data/test/copy_file.dir/input +0 -0
- data/test/copy_file.dir/output +2 -0
- data/test/copy_file.dir/program.chirp +5 -0
- data/test/fields.dir/input +0 -0
- data/test/fields.dir/output +0 -0
- data/test/fields.dir/program.chirp +2 -2
- data/test/fields_sep.dir/input +0 -0
- data/test/fields_sep.dir/output +0 -0
- data/test/fields_sep.dir/program.chirp +1 -2
- data/test/files.dir/a.ignore +0 -0
- data/test/files.dir/a.txt +0 -0
- data/test/files.dir/b.txt +0 -0
- data/test/files.dir/c.stuff +0 -0
- data/test/files.dir/input +0 -0
- data/test/files.dir/output +3 -0
- data/test/files.dir/program.chirp +7 -3
- data/test/fs_expr.dir/aa.txt +1 -0
- data/test/fs_expr.dir/bb.txt +9 -0
- data/test/fs_expr.dir/cc.txt +0 -0
- data/test/fs_expr.dir/dir1/dir2/dir3/a.txt +1 -0
- data/test/fs_expr.dir/input +0 -0
- data/test/fs_expr.dir/output +7 -0
- data/test/fs_expr.dir/program.chirp +17 -0
- data/test/inplace.dir/a.txt +0 -0
- data/test/inplace.dir/b.txt +0 -0
- data/test/inplace.dir/input +0 -0
- data/test/inplace.dir/output +0 -0
- data/test/inplace.dir/program.chirp +4 -6
- data/test/line_no.dir/input +0 -0
- data/test/line_no.dir/output +1 -0
- data/test/line_no.dir/program.chirp +5 -1
- data/test/match.dir/input +0 -0
- data/test/match.dir/output +0 -0
- data/test/match.dir/program.chirp +1 -1
- data/test/path.dir/dir1/dir2/dir3/a.txt +1 -0
- data/test/path.dir/input +0 -0
- data/test/path.dir/new +5 -0
- data/test/path.dir/output +20 -0
- data/test/path.dir/program.chirp +44 -0
- data/test/proc.dir/input +0 -0
- data/test/proc.dir/output +0 -0
- data/test/proc.dir/program.chirp +2 -2
- data/test/rename_file.dir/a.txt +4 -0
- data/test/rename_file.dir/b.txt +4 -0
- data/test/rename_file.dir/c.txt +4 -0
- data/test/rename_file.dir/input +0 -0
- data/test/rename_file.dir/output +2 -0
- data/test/rename_file.dir/program.chirp +7 -0
- data/test/requires.rb +6 -0
- data/test/span.dir/input +0 -0
- data/test/span.dir/output +0 -0
- data/test/span.dir/program.chirp +1 -1
- data/test/span2.dir/input +0 -0
- data/test/span2.dir/output +0 -0
- data/test/span2.dir/program.chirp +3 -1
- data/test/string.dir/input +0 -0
- data/test/string.dir/output +0 -0
- data/test/string.dir/program.chirp +1 -1
- data/test/test_application.rb +14 -9
- data/test/test_fs_expression.rb +99 -0
- data/test/test_span.rb +36 -0
- metadata +64 -7
- data/lib/chirp/statement.rb +0 -91
- data/test/test_statement.rb +0 -118
@@ -0,0 +1,101 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2006-2007 Russell Olsen
|
3
|
+
# Chirp is distributed under the same license as Ruby itself.
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'pathname'
|
7
|
+
require 'ftools'
|
8
|
+
require 'fileutils'
|
9
|
+
#require 'erb'
|
10
|
+
|
11
|
+
module Chirp
|
12
|
+
class Context
|
13
|
+
include FileUtils
|
14
|
+
|
15
|
+
attr_accessor :field_separator, :line_no, :output_path, :paths, :dir
|
16
|
+
attr_reader :path, :line
|
17
|
+
attr_writer :fields, :contents
|
18
|
+
attr_accessor :verbose
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@field_separator = /[ \t]/
|
22
|
+
@verbose = false
|
23
|
+
@dir = '.'
|
24
|
+
end
|
25
|
+
|
26
|
+
def execute(action)
|
27
|
+
instance_eval(&action)
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
def path=(new_path)
|
33
|
+
@path = new_path
|
34
|
+
@full_path = nil
|
35
|
+
@line = nil
|
36
|
+
@fields = nil
|
37
|
+
@path_array = nil
|
38
|
+
@path_name = nil
|
39
|
+
@full_path_name = nil
|
40
|
+
@contents = nil
|
41
|
+
@dirname = nil
|
42
|
+
@extname = nil
|
43
|
+
@name = nil
|
44
|
+
@basename = nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def line=(new_line)
|
48
|
+
@line = new_line
|
49
|
+
@fields = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def full_path
|
53
|
+
@full_path || @full_path = File.join(dir, path)
|
54
|
+
end
|
55
|
+
|
56
|
+
def fields
|
57
|
+
@fields || @fields = line.split(@field_separator)
|
58
|
+
end
|
59
|
+
|
60
|
+
def path_name
|
61
|
+
@path_name || @path_name = Pathname.new(path)
|
62
|
+
end
|
63
|
+
|
64
|
+
def full_path_name
|
65
|
+
@full_path_name || @full_path_name = Pathname.new(File.join(dir,path))
|
66
|
+
end
|
67
|
+
|
68
|
+
def dirname
|
69
|
+
@dirname || @dirname = path_name.dirname.to_s
|
70
|
+
end
|
71
|
+
|
72
|
+
def basename
|
73
|
+
@basename || @basename = path_name.basename.to_s
|
74
|
+
end
|
75
|
+
|
76
|
+
def extname
|
77
|
+
@extname || @extname = path_name.extname.to_s
|
78
|
+
end
|
79
|
+
|
80
|
+
def name
|
81
|
+
return @name if @name
|
82
|
+
@name = basename[0..-extname.size-1]
|
83
|
+
end
|
84
|
+
|
85
|
+
def contents
|
86
|
+
@contents || @contents = File.read(full_path)
|
87
|
+
end
|
88
|
+
|
89
|
+
def comment(*msg)
|
90
|
+
STDERR.puts(msg.join(' '))
|
91
|
+
end
|
92
|
+
|
93
|
+
def debug(*msg)
|
94
|
+
STDERR.puts(msg.join(' ')) if @verbose
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_s
|
98
|
+
return "Context: #{@path}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
|
2
|
+
module Chirp
|
3
|
+
|
4
|
+
class FSExpression
|
5
|
+
def &(other)
|
6
|
+
And.new(self, other)
|
7
|
+
end
|
8
|
+
|
9
|
+
def |(other)
|
10
|
+
Or.new(self, other)
|
11
|
+
end
|
12
|
+
|
13
|
+
def ~
|
14
|
+
Not.new(self)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Named < FSExpression
|
19
|
+
def initialize(name)
|
20
|
+
@name = name
|
21
|
+
end
|
22
|
+
|
23
|
+
def evaluate(path)
|
24
|
+
return true if @name.kind_of?(Regexp) and (@name =~ path)
|
25
|
+
return true if @name.kind_of?(String) and (File.fnmatch(@name, path))
|
26
|
+
false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class All < FSExpression
|
31
|
+
def evaluate(path)
|
32
|
+
true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class None < FSExpression
|
37
|
+
def evaluate(path)
|
38
|
+
false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class Contains < FSExpression
|
43
|
+
def initialize(re)
|
44
|
+
@re = re
|
45
|
+
end
|
46
|
+
|
47
|
+
def evaluate(path)
|
48
|
+
return false unless File.file?(path)
|
49
|
+
File.open(path) do |f|
|
50
|
+
until f.eof?
|
51
|
+
return true if @re =~ f.readline
|
52
|
+
end
|
53
|
+
end
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class IsDir < FSExpression
|
59
|
+
def evaluate(path)
|
60
|
+
File.directory?(path)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class IsFile < FSExpression
|
65
|
+
def evaluate(path)
|
66
|
+
File.file?(path)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Bigger < FSExpression
|
71
|
+
def initialize(size)
|
72
|
+
@size = size
|
73
|
+
end
|
74
|
+
|
75
|
+
def evaluate(path)
|
76
|
+
File.size(path) > @size
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class Smaller < FSExpression
|
81
|
+
def initialize(size)
|
82
|
+
@size = size
|
83
|
+
end
|
84
|
+
|
85
|
+
def evaluate(path)
|
86
|
+
File.size(path) < @size
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class SameSize < FSExpression
|
91
|
+
def initialize(size)
|
92
|
+
@size = size
|
93
|
+
end
|
94
|
+
|
95
|
+
def evaluate(path)
|
96
|
+
File.size(path) == @size
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
class And < FSExpression
|
102
|
+
def initialize(exp1, exp2)
|
103
|
+
@exp1 = exp1
|
104
|
+
@exp2 = exp2
|
105
|
+
end
|
106
|
+
|
107
|
+
def evaluate(path)
|
108
|
+
@exp1.evaluate(path) && @exp2.evaluate(path)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class Or < FSExpression
|
113
|
+
def initialize(exp1, exp2)
|
114
|
+
@exp1 = exp1
|
115
|
+
@exp2 = exp2
|
116
|
+
end
|
117
|
+
|
118
|
+
def evaluate(path)
|
119
|
+
@exp1.evaluate(path) || @exp2.evaluate(path)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class Not < FSExpression
|
124
|
+
def initialize(exp)
|
125
|
+
@exp1 = exp
|
126
|
+
end
|
127
|
+
|
128
|
+
def evaluate(path)
|
129
|
+
not @exp1.evaluate(path)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
module FSSugar
|
134
|
+
|
135
|
+
def all
|
136
|
+
All.new
|
137
|
+
end
|
138
|
+
|
139
|
+
def none
|
140
|
+
None.new
|
141
|
+
end
|
142
|
+
|
143
|
+
def bigger(size)
|
144
|
+
Bigger.new(size)
|
145
|
+
end
|
146
|
+
|
147
|
+
def smaller(size)
|
148
|
+
Smaller.new(size)
|
149
|
+
end
|
150
|
+
|
151
|
+
def named(name)
|
152
|
+
Named.new(name)
|
153
|
+
end
|
154
|
+
|
155
|
+
def contains(pattern)
|
156
|
+
Contains.new(pattern)
|
157
|
+
end
|
158
|
+
|
159
|
+
def length(n)
|
160
|
+
SameSize.new(n)
|
161
|
+
end
|
162
|
+
|
163
|
+
def dir?
|
164
|
+
IsDir.new
|
165
|
+
end
|
166
|
+
|
167
|
+
def file?
|
168
|
+
IsFile.new
|
169
|
+
end
|
170
|
+
|
171
|
+
def except(expr)
|
172
|
+
Not.new(expr)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
#require 'pp'
|
179
|
+
#include Chirp::FSSugar
|
180
|
+
#pp (bigger(100) & contains('foobar')) | (dir? | named('xxx.txt'))
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Chirp
|
2
|
+
class PathFilter
|
3
|
+
def initialize(options={})
|
4
|
+
@regexp = @glob = nil
|
5
|
+
@regexp = options[:path] if options[:path].kind_of?(Regexp)
|
6
|
+
@glob = options[:path] if options[:path].kind_of?(String)
|
7
|
+
@fsexp = options[:fsexp]
|
8
|
+
@contains = options[:contains]
|
9
|
+
@name = options[:name]
|
10
|
+
@dir=options[:dir]
|
11
|
+
@type = options[:type]
|
12
|
+
end
|
13
|
+
|
14
|
+
def passes?(path)
|
15
|
+
#puts "trying filter #{self} on #{path}"
|
16
|
+
if @type == :file
|
17
|
+
return false unless File.file?(path)
|
18
|
+
end
|
19
|
+
if @type == :dir
|
20
|
+
return false unless File.directory?(path)
|
21
|
+
end
|
22
|
+
if @name
|
23
|
+
return false unless File.fnmatch(@name, File.basename(path))
|
24
|
+
end
|
25
|
+
if @fsexp
|
26
|
+
return false unless @fsexp.evaluate(path)
|
27
|
+
end
|
28
|
+
if @glob
|
29
|
+
return false unless File.fnmatch(@glob, path, File::FNM_PATHNAME | File::FNM_DOTMATCH )
|
30
|
+
end
|
31
|
+
if @regexp
|
32
|
+
return false unless @regexp =~ path
|
33
|
+
end
|
34
|
+
if @dir
|
35
|
+
return false unless File.dirname(path) == @dir
|
36
|
+
end
|
37
|
+
if @contains
|
38
|
+
return false unless file_contains(path, @contains)
|
39
|
+
end
|
40
|
+
#puts "passed filter: #{path}"
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
def file_contains(path, re)
|
45
|
+
return false unless File.file?(path)
|
46
|
+
File.open(path) do |f|
|
47
|
+
until f.eof?
|
48
|
+
return true if re =~ f.readline
|
49
|
+
end
|
50
|
+
end
|
51
|
+
false
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_s
|
55
|
+
"Filter: #{@glob} #{@dir} #{@type}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#--
|
4
|
+
|
5
|
+
# Copyright (c) 2003, 2004, 2005, 2006, 2007 Jim Weirich
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
# of this software and associated documentation files (the "Software"), to
|
9
|
+
# deal in the Software without restriction, including without limitation the
|
10
|
+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
11
|
+
# sell copies of the Software, and to permit persons to whom the Software is
|
12
|
+
# furnished to do so, subject to the following conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be included in
|
15
|
+
# all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
22
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
23
|
+
# IN THE SOFTWARE.
|
24
|
+
#++
|
25
|
+
#
|
26
|
+
# = Rake -- Ruby Make
|
27
|
+
#
|
28
|
+
# This is the main file for the Rake application. Normally it is referenced
|
29
|
+
# as a library via a require statement, but it can be distributed
|
30
|
+
# independently as an application.
|
31
|
+
|
32
|
+
module Pathmap
|
33
|
+
# Replace the file extension with +newext+. If there is no extenson on
|
34
|
+
# the string, append the new extension to the end. If the new extension
|
35
|
+
# is not given, or is the empty string, remove any existing extension.
|
36
|
+
#
|
37
|
+
# +ext+ is a user added method for the String class.
|
38
|
+
def ext(newext='')
|
39
|
+
return self.dup if ['.', '..'].include? self
|
40
|
+
if newext != ''
|
41
|
+
newext = (newext =~ /^\./) ? newext : ("." + newext)
|
42
|
+
end
|
43
|
+
dup.sub!(%r(([^/\\])\.[^./\\]*$)) { $1 + newext } || self + newext
|
44
|
+
end
|
45
|
+
|
46
|
+
# Explode a path into individual components. Used by +pathmap+.
|
47
|
+
def pathmap_explode
|
48
|
+
head, tail = File.split(self)
|
49
|
+
return [self] if head == self
|
50
|
+
return [tail] if head == '.' || tail == '/'
|
51
|
+
return [head, tail] if head == '/'
|
52
|
+
return head.pathmap_explode + [tail]
|
53
|
+
end
|
54
|
+
protected :pathmap_explode
|
55
|
+
|
56
|
+
# Extract a partial path from the path. Include +n+ directories from the
|
57
|
+
# front end (left hand side) if +n+ is positive. Include |+n+|
|
58
|
+
# directories from the back end (right hand side) if +n+ is negative.
|
59
|
+
def pathmap_partial(n)
|
60
|
+
target = File.dirname(self)
|
61
|
+
dirs = target.pathmap_explode
|
62
|
+
if n > 0
|
63
|
+
File.join(dirs[0...n])
|
64
|
+
elsif n < 0
|
65
|
+
partial = dirs[n..-1]
|
66
|
+
if partial.nil? || partial.empty?
|
67
|
+
target
|
68
|
+
else
|
69
|
+
File.join(partial)
|
70
|
+
end
|
71
|
+
else
|
72
|
+
"."
|
73
|
+
end
|
74
|
+
end
|
75
|
+
protected :pathmap_partial
|
76
|
+
|
77
|
+
# Preform the pathmap replacement operations on the given path. The
|
78
|
+
# patterns take the form 'pat1,rep1;pat2,rep2...'.
|
79
|
+
def pathmap_replace(patterns, &block)
|
80
|
+
result = self
|
81
|
+
patterns.split(';').each do |pair|
|
82
|
+
pattern, replacement = pair.split(',')
|
83
|
+
pattern = Regexp.new(pattern)
|
84
|
+
if replacement == '*' && block_given?
|
85
|
+
result = result.sub(pattern, &block)
|
86
|
+
elsif replacement
|
87
|
+
result = result.sub(pattern, replacement)
|
88
|
+
else
|
89
|
+
result = result.sub(pattern, '')
|
90
|
+
end
|
91
|
+
end
|
92
|
+
result
|
93
|
+
end
|
94
|
+
protected :pathmap_replace
|
95
|
+
|
96
|
+
# Map the path according to the given specification. The specification
|
97
|
+
# controls the details of the mapping. The following special patterns are
|
98
|
+
# recognized:
|
99
|
+
#
|
100
|
+
# * <b>%p</b> -- The complete path.
|
101
|
+
# * <b>%f</b> -- The base file name of the path, with its file extension,
|
102
|
+
# but without any directories.
|
103
|
+
# * <b>%n</b> -- The file name of the path without its file extension.
|
104
|
+
# * <b>%d</b> -- The directory list of the path.
|
105
|
+
# * <b>%x</b> -- The file extension of the path. An empty string if there
|
106
|
+
# is no extension.
|
107
|
+
# * <b>%X</b> -- Everything *but* the file extension.
|
108
|
+
# * <b>%s</b> -- The alternate file separater if defined, otherwise use
|
109
|
+
# the standard file separator.
|
110
|
+
# * <b>%%</b> -- A percent sign.
|
111
|
+
#
|
112
|
+
# The %d specifier can also have a numeric prefix (e.g. '%2d'). If the
|
113
|
+
# number is positive, only return (up to) +n+ directories in the path,
|
114
|
+
# starting from the left hand side. If +n+ is negative, return (up to)
|
115
|
+
# |+n+| directories from the right hand side of the path.
|
116
|
+
#
|
117
|
+
# Examples:
|
118
|
+
#
|
119
|
+
# 'a/b/c/d/file.txt'.pathmap("%2d") => 'a/b'
|
120
|
+
# 'a/b/c/d/file.txt'.pathmap("%-2d") => 'c/d'
|
121
|
+
#
|
122
|
+
# Also the %d, %p, $f, $n, %x, and %X operators can take a
|
123
|
+
# pattern/replacement argument to perform simple string substititions on a
|
124
|
+
# particular part of the path. The pattern and replacement are speparated
|
125
|
+
# by a comma and are enclosed by curly braces. The replacement spec comes
|
126
|
+
# after the % character but before the operator letter. (e.g.
|
127
|
+
# "%{old,new}d"). Muliple replacement specs should be separated by
|
128
|
+
# semi-colons (e.g. "%{old,new;src,bin}d").
|
129
|
+
#
|
130
|
+
# Regular expressions may be used for the pattern, and back refs may be
|
131
|
+
# used in the replacement text. Curly braces, commas and semi-colons are
|
132
|
+
# excluded from both the pattern and replacement text (let's keep parsing
|
133
|
+
# reasonable).
|
134
|
+
#
|
135
|
+
# For example:
|
136
|
+
#
|
137
|
+
# "src/org/onestepback/proj/A.java".pathmap("%{^src,bin}X.class")
|
138
|
+
#
|
139
|
+
# returns:
|
140
|
+
#
|
141
|
+
# "bin/org/onestepback/proj/A.class"
|
142
|
+
#
|
143
|
+
# If the replacement text is '*', then a block may be provided to perform
|
144
|
+
# some arbitrary calculation for the replacement.
|
145
|
+
#
|
146
|
+
# For example:
|
147
|
+
#
|
148
|
+
# "/path/to/file.TXT".pathmap("%X%{.*,*}x") { |ext|
|
149
|
+
# ext.downcase
|
150
|
+
# }
|
151
|
+
#
|
152
|
+
# Returns:
|
153
|
+
#
|
154
|
+
# "/path/to/file.txt"
|
155
|
+
#
|
156
|
+
def pathmap(spec=nil, &block)
|
157
|
+
return self if spec.nil?
|
158
|
+
result = ''
|
159
|
+
spec.scan(/%\{[^}]*\}-?\d*[sdpfnxX%]|%-?\d+d|%.|[^%]+/) do |frag|
|
160
|
+
case frag
|
161
|
+
when '%f'
|
162
|
+
result << File.basename(self)
|
163
|
+
when '%n'
|
164
|
+
result << File.basename(self).ext
|
165
|
+
when '%d'
|
166
|
+
result << File.dirname(self)
|
167
|
+
when '%x'
|
168
|
+
result << $1 if self =~ /[^\/](\.[^.]+)$/
|
169
|
+
when '%X'
|
170
|
+
if self =~ /^(.+[^\/])(\.[^.]+)$/
|
171
|
+
result << $1
|
172
|
+
else
|
173
|
+
result << self
|
174
|
+
end
|
175
|
+
when '%p'
|
176
|
+
result << self
|
177
|
+
when '%s'
|
178
|
+
result << (File::ALT_SEPARATOR || File::SEPARATOR)
|
179
|
+
when '%-'
|
180
|
+
# do nothing
|
181
|
+
when '%%'
|
182
|
+
result << "%"
|
183
|
+
when /%(-?\d+)d/
|
184
|
+
result << pathmap_partial($1.to_i)
|
185
|
+
when /^%\{([^}]*)\}(\d*[dpfnxX])/
|
186
|
+
patterns, operator = $1, $2
|
187
|
+
result << pathmap('%' + operator).pathmap_replace(patterns, &block)
|
188
|
+
when /^%/
|
189
|
+
fail ArgumentError, "Unknown pathmap specifier #{frag} in '#{spec}'"
|
190
|
+
else
|
191
|
+
result << frag
|
192
|
+
end
|
193
|
+
end
|
194
|
+
result
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
class String
|
199
|
+
include Pathmap
|
200
|
+
end
|