chirp 0.2.0 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. data/History.txt +0 -0
  2. data/Manifest.txt +44 -2
  3. data/README.txt +10 -2
  4. data/Rakefile +1 -1
  5. data/bin/chirp +6 -4
  6. data/lib/chirp.rb +7 -3
  7. data/lib/chirp/application.rb +248 -159
  8. data/lib/chirp/context.rb +101 -0
  9. data/lib/chirp/fs_expression.rb +180 -0
  10. data/lib/chirp/path_filter.rb +58 -0
  11. data/lib/chirp/pathmap.rb +200 -0
  12. data/lib/chirp/span.rb +38 -0
  13. data/test/account.dir/input +0 -0
  14. data/test/account.dir/output +0 -0
  15. data/test/account.dir/program.chirp +7 -4
  16. data/test/action.dir/input +0 -0
  17. data/test/action.dir/output +0 -0
  18. data/test/action.dir/program.chirp +2 -2
  19. data/test/beforeafter.dir/input +0 -0
  20. data/test/beforeafter.dir/output +0 -0
  21. data/test/beforeafter.dir/program.chirp +3 -3
  22. data/test/copy1.dir/input +0 -0
  23. data/test/copy1.dir/output +5 -0
  24. data/test/copy1.dir/program.chirp +9 -0
  25. data/test/copy1.dir/source +2 -0
  26. data/test/copy2.dir/input +0 -0
  27. data/test/copy2.dir/output +5 -0
  28. data/test/copy2.dir/program.chirp +9 -0
  29. data/test/copy2.dir/source +2 -0
  30. data/test/copy3.dir/input +0 -0
  31. data/test/copy3.dir/output +5 -0
  32. data/test/copy3.dir/program.chirp +7 -0
  33. data/test/copy3.dir/source +2 -0
  34. data/test/copy_file.dir/a.txt +1 -0
  35. data/test/copy_file.dir/b.txt +1 -0
  36. data/test/copy_file.dir/c.txt +1 -0
  37. data/test/copy_file.dir/input +0 -0
  38. data/test/copy_file.dir/output +2 -0
  39. data/test/copy_file.dir/program.chirp +5 -0
  40. data/test/fields.dir/input +0 -0
  41. data/test/fields.dir/output +0 -0
  42. data/test/fields.dir/program.chirp +2 -2
  43. data/test/fields_sep.dir/input +0 -0
  44. data/test/fields_sep.dir/output +0 -0
  45. data/test/fields_sep.dir/program.chirp +1 -2
  46. data/test/files.dir/a.ignore +0 -0
  47. data/test/files.dir/a.txt +0 -0
  48. data/test/files.dir/b.txt +0 -0
  49. data/test/files.dir/c.stuff +0 -0
  50. data/test/files.dir/input +0 -0
  51. data/test/files.dir/output +3 -0
  52. data/test/files.dir/program.chirp +7 -3
  53. data/test/fs_expr.dir/aa.txt +1 -0
  54. data/test/fs_expr.dir/bb.txt +9 -0
  55. data/test/fs_expr.dir/cc.txt +0 -0
  56. data/test/fs_expr.dir/dir1/dir2/dir3/a.txt +1 -0
  57. data/test/fs_expr.dir/input +0 -0
  58. data/test/fs_expr.dir/output +7 -0
  59. data/test/fs_expr.dir/program.chirp +17 -0
  60. data/test/inplace.dir/a.txt +0 -0
  61. data/test/inplace.dir/b.txt +0 -0
  62. data/test/inplace.dir/input +0 -0
  63. data/test/inplace.dir/output +0 -0
  64. data/test/inplace.dir/program.chirp +4 -6
  65. data/test/line_no.dir/input +0 -0
  66. data/test/line_no.dir/output +1 -0
  67. data/test/line_no.dir/program.chirp +5 -1
  68. data/test/match.dir/input +0 -0
  69. data/test/match.dir/output +0 -0
  70. data/test/match.dir/program.chirp +1 -1
  71. data/test/path.dir/dir1/dir2/dir3/a.txt +1 -0
  72. data/test/path.dir/input +0 -0
  73. data/test/path.dir/new +5 -0
  74. data/test/path.dir/output +20 -0
  75. data/test/path.dir/program.chirp +44 -0
  76. data/test/proc.dir/input +0 -0
  77. data/test/proc.dir/output +0 -0
  78. data/test/proc.dir/program.chirp +2 -2
  79. data/test/rename_file.dir/a.txt +4 -0
  80. data/test/rename_file.dir/b.txt +4 -0
  81. data/test/rename_file.dir/c.txt +4 -0
  82. data/test/rename_file.dir/input +0 -0
  83. data/test/rename_file.dir/output +2 -0
  84. data/test/rename_file.dir/program.chirp +7 -0
  85. data/test/requires.rb +6 -0
  86. data/test/span.dir/input +0 -0
  87. data/test/span.dir/output +0 -0
  88. data/test/span.dir/program.chirp +1 -1
  89. data/test/span2.dir/input +0 -0
  90. data/test/span2.dir/output +0 -0
  91. data/test/span2.dir/program.chirp +3 -1
  92. data/test/string.dir/input +0 -0
  93. data/test/string.dir/output +0 -0
  94. data/test/string.dir/program.chirp +1 -1
  95. data/test/test_application.rb +14 -9
  96. data/test/test_fs_expression.rb +99 -0
  97. data/test/test_span.rb +36 -0
  98. metadata +64 -7
  99. data/lib/chirp/statement.rb +0 -91
  100. data/test/test_statement.rb +0 -118
File without changes
@@ -5,8 +5,17 @@ Rakefile
5
5
  bin/chirp
6
6
  lib/chirp.rb
7
7
  lib/chirp/application.rb
8
- lib/chirp/statement.rb
9
- test/test_statement.rb
8
+ lib/chirp/pathmap.rb
9
+ lib/chirp/context.rb
10
+ lib/chirp/fs_expression.rb
11
+ lib/chirp/span.rb
12
+ lib/chirp/path_filter.rb
13
+ test/copy_file.dir/output
14
+ test/copy_file.dir/input
15
+ test/copy_file.dir/program.chirp
16
+ test/copy_file.dir/a.txt
17
+ test/copy_file.dir/b.txt
18
+ test/copy_file.dir/c.txt
10
19
  test/test_application.rb
11
20
  test/span.dir/output
12
21
  test/span.dir/input
@@ -53,3 +62,36 @@ test/proc.dir/program.chirp
53
62
  test/account.dir/program.chirp
54
63
  test/account.dir/output
55
64
  test/account.dir/input
65
+ test/path.dir/output
66
+ test/path.dir/input
67
+ test/path.dir/program.chirp
68
+ test/path.dir/new
69
+ test/path.dir/dir1/dir2/dir3/a.txt
70
+ test/test_span.rb
71
+ test/rename_file.dir/output
72
+ test/rename_file.dir/input
73
+ test/rename_file.dir/program.chirp
74
+ test/rename_file.dir/a.txt
75
+ test/rename_file.dir/b.txt
76
+ test/rename_file.dir/c.txt
77
+ test/copy1.dir/program.chirp
78
+ test/copy1.dir/source
79
+ test/copy1.dir/input
80
+ test/copy1.dir/output
81
+ test/copy2.dir/program.chirp
82
+ test/copy2.dir/source
83
+ test/copy2.dir/input
84
+ test/copy2.dir/output
85
+ test/copy3.dir/program.chirp
86
+ test/copy3.dir/source
87
+ test/copy3.dir/input
88
+ test/copy3.dir/output
89
+ test/test_fs_expression.rb
90
+ test/fs_expr.dir/output
91
+ test/fs_expr.dir/input
92
+ test/fs_expr.dir/program.chirp
93
+ test/fs_expr.dir/dir1/dir2/dir3/a.txt
94
+ test/fs_expr.dir/bb.txt
95
+ test/fs_expr.dir/aa.txt
96
+ test/fs_expr.dir/cc.txt
97
+ test/requires.rb
data/README.txt CHANGED
@@ -4,11 +4,19 @@ chirp
4
4
 
5
5
  == DESCRIPTION:
6
6
 
7
- Chirp is a Ruby based DSL inspiried by awk.
7
+ Chirp is a Ruby based DSL originally inspiried by awk.
8
8
 
9
9
  == FEATURES/PROBLEMS:
10
10
 
11
- * Need to add documentation
11
+ Chirp is a DSL for manipulating file systems. In the same
12
+ way that rake is aimed at making system buildig easy,
13
+ chirp is designed to make doing things to entire file
14
+ systems easy.
15
+
16
+ Chirp is still very much in development and you should
17
+ be very careful with it: since chirp does operate on
18
+ whole directory trees, it is capable of doing a lot of
19
+ damage.
12
20
 
13
21
  == SYNOPSIS:
14
22
 
data/Rakefile CHANGED
@@ -9,7 +9,7 @@ Hoe.new('chirp', Chirp::VERSION) do |p|
9
9
  p.author = 'Russ Olsen'
10
10
  p.email = 'russ@russolsen.com'
11
11
  p.summary = 'Chirp is a Ruby based text processing DSL similar to awk'
12
- # p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
12
+ p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
13
13
  # p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
14
14
  p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
15
15
  end
data/bin/chirp CHANGED
@@ -12,7 +12,7 @@ program_text=nil
12
12
  verbose=false
13
13
 
14
14
  option_parser = OptionParser.new do |op|
15
- op.banner = "Usage: $0 [-v] [-e chirp_program_text] [chirp_program_file] [input_file ...]"
15
+ op.banner = "Usage: chirp [-v] [-e chirp_program_text] [chirp_program_file] [input_file ...]"
16
16
 
17
17
  op.on('-p', '--program [chirp-program-text]',
18
18
  'Execute this Chirp program') do |string|
@@ -33,10 +33,12 @@ if files.empty? and program_text.nil?
33
33
  end
34
34
 
35
35
  program_text = File.read(files.shift) unless program_text
36
+ ARGV.replace(files)
36
37
 
37
- app = Chirp::Application.new(program_text, verbose)
38
- app.files(files)
39
- app.evaluate
38
+ app = Chirp::Application.new
39
+ app.program(program_text)
40
+ app.verbose(verbose)
41
+ app.execute
40
42
 
41
43
 
42
44
 
@@ -4,11 +4,15 @@
4
4
  # Chirp is distributed under the same license as Ruby itself.
5
5
  #
6
6
 
7
- ChirpLibDir=File.dirname(__FILE__)
7
+ ChirpLibDir=File.dirname(__FILE__) unless self.class.const_defined?(:ChirpLibDir)
8
8
 
9
- require "#{ChirpLibDir}/chirp/statement"
9
+ require "#{ChirpLibDir}/chirp/pathmap"
10
+ require "#{ChirpLibDir}/chirp/fs_expression"
11
+ require "#{ChirpLibDir}/chirp/context"
12
+ require "#{ChirpLibDir}/chirp/path_filter"
13
+ require "#{ChirpLibDir}/chirp/span"
10
14
  require "#{ChirpLibDir}/chirp/application"
11
15
 
12
16
  module Chirp
13
- VERSION = '0.2.0'
17
+ VERSION = '0.3'
14
18
  end
@@ -1,252 +1,341 @@
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'
1
+ require 'pp'
2
+ require 'tempfile'
3
+ require 'set'
4
+ require 'find'
9
5
 
10
6
  module Chirp
11
- def self.redirect_io_and_run(input, output=$stdout, &block)
12
- old_stdout, $stdout = $stdout, output
13
- old_stdin, $stdin = $stdin, input
7
+
8
+ def self.run_with_input_stream(in_stream, &block)
9
+ old_stdin, $stdin = $stdin, in_stream
14
10
  begin
15
11
  block.call
16
12
  ensure
17
- $stdin, $stdout = old_stdin, old_stdout
13
+ $stdin = old_stdin
18
14
  end
19
15
  end
20
16
 
21
- class Context
22
- include FileUtils
17
+ def self.run_with_output_stream(out_stream, &block)
18
+ old_stdout, $stdout = $stdout, out_stream
19
+ begin
20
+ block.call
21
+ ensure
22
+ $stdout = old_stdout
23
+ end
24
+ end
23
25
 
24
- attr_accessor :field_separator, :line, :line_no, :path, :paths
25
- attr_writer :fields, :contents
26
+ def self.run_with_input_file(in_file, &block)
27
+ File.open(in_file) {|f| run_with_input_stream(f, &block)}
28
+ end
26
29
 
27
- def initialize(field_separator)
28
- @field_separator = field_separator
30
+ def self.run_with_output_file(out_file, create_backups=false, &block)
31
+ temp_file = Tempfile.new('chirp').path
32
+ File.open(temp_file, 'w') do |out_stream|
33
+ run_with_output_stream(out_stream, &block)
29
34
  end
30
-
31
- def execute(action)
32
- instance_eval(&action)
35
+ backup_file = nil
36
+ if create_backups and File.exist?(out_file)
37
+ backup_file = Tempfile.new('chirp').path
38
+ FileUtils.cp(out_file, backup_file) if File.exist?(out_file)
33
39
  end
34
-
35
- def get_binding
36
- binding
40
+ FileUtils.cp(temp_file, out_file)
41
+ FileUtils.rm(temp_file)
42
+ if backup_file
43
+ FileUtils.cp(backup_file, "#{out_file}.bak")
44
+ FileUtils.rm(backup_file)
37
45
  end
46
+ end
38
47
 
39
- def fields
40
- @fields || @fields = line.split(@field_separator)
48
+
49
+ def self.debug(*msg)
50
+ STDERR.puts msg.join(' ')
51
+ end
52
+
53
+
54
+ class PathProcessor
55
+ def initialize(context, options={}, &block)
56
+ @context = context
57
+ @filter = PathFilter.new(options)
58
+ @block = block
41
59
  end
42
-
43
- def contents
44
- @contents || @contents = File.read(@file)
60
+
61
+ def execute
62
+ @context.debug("PathProcessor execute for #{@context.path}")
63
+ if @filter.passes?(@context.path)
64
+ @context.execute(@block)
65
+ else
66
+ @context.debug("Filter blocked #{@context.path}")
67
+ @context.debug("Filter: #{@filter}")
68
+ end
45
69
  end
46
70
  end
47
71
 
48
- class Action
72
+ class LineProcessor
73
+ DEFAULT_ACTION = lambda { puts(line) }
74
+
75
+ def initialize(context, options={}, &block)
76
+
77
+ @context = context
78
+ @expression = options[:expr]
79
+ @pattern = options[:pattern]
80
+ @match = options[:match]
81
+ @number = options[:number]
82
+ if options[:start]
83
+ @span = Span.new(options[:start], options[:end])
84
+ else
85
+ @span=nil
86
+ end
87
+ @options = options
88
+ @block = block || DEFAULT_ACTION
89
+ end
90
+
91
+ def execute
92
+ if @match
93
+ return unless @match == @context.line
94
+ end
95
+ if @pattern
96
+ return unless @pattern =~ @context.line
97
+ end
98
+ if @number
99
+ return unless @number == @context.line_no
100
+ end
101
+ if @expression
102
+ return unless @context.execute(@expression)
103
+ end
104
+ if @span
105
+ return unless @span.evaluate(@context)
106
+ end
107
+ @context.execute(@block)
108
+ end
109
+
110
+ def reset
111
+ @span.reset if @span
112
+ end
113
+ end
114
+
115
+ class NowProcessor
49
116
  def initialize(context, &block)
50
117
  @context = context
51
118
  @block = block
52
119
  end
53
120
 
54
- def evaluate(path)
55
- @context.path = path
121
+ def execute
56
122
  @context.execute(@block)
57
123
  end
124
+ end
58
125
 
59
- def evaluate_paths(paths)
60
- @context.paths = paths
61
- @context.execute(@block)
126
+ class StaringWithProcessor
127
+ def initialize(context, path)
128
+ @context = context
129
+ @path = path
62
130
  end
63
131
 
132
+ def execute
133
+ @context.dir = @path
134
+ end
64
135
  end
65
136
 
66
- class PathAction
67
- DEFAULT_ACTION = lambda {puts line}
137
+ class EachProcessor
68
138
 
69
- attr_accessor :verbose
139
+ include Chirp::FSSugar
70
140
 
71
- def initialize(glob, context, &block)
72
- @glob = glob
141
+ def initialize(context, options={}, &block)
73
142
  @context = context
74
- @edit_in_place = false
75
- @before_actions = []
76
- @match_statements=[]
77
- @after_actions = []
78
- @field_separator = /[ \t]/
79
- instance_eval(&block)
80
- end
81
-
82
- def field_separator(sep=nil)
83
- @field_separator=sep
143
+ @filter = PathFilter.new(options)
144
+ @field_separator = options[:field_separator]
145
+ @block = block
146
+ @output_spec = options[:output]
147
+ @create_backups = options[:create_backups]
148
+ @redirect_output = options.key?(:redirect_output) ? options[:redirect_output] : true
149
+ @context = context
150
+ @before_processors = []
151
+ @content_processors = []
152
+ @after_processors = []
153
+ instance_eval(&block) if block
84
154
  end
85
155
 
86
- def edit_in_place(new_value=true)
87
- @edit_in_place = new_value
156
+ def before(options={}, &block)
157
+ @before_processors << PathProcessor.new(@context, options, &block)
88
158
  end
89
159
 
90
- def evaluate_paths(paths)
91
- @context.paths = paths
92
- paths.each do |path|
93
- evaluate_path(path)
94
- end
160
+ def after(options={}, &block)
161
+ @after_processors << PathProcessor.new(@context, options, &block)
95
162
  end
96
163
 
97
- def line(arg1=nil, arg2=nil, &block)
98
- action = block || DEFAULT_ACTION
99
-
100
- if arg1.nil? and arg2.nil?
101
- @match_statements << Statement.new( MatchPredicate.new(/.*/), action)
102
- elsif arg2.nil?
103
- @match_statements << Statement.new( predicate_for(arg1), action)
104
- else
105
- @match_statements << Statement.new(
106
- SpanPredicate.new(predicate_for(arg1), predicate_for(arg2)), action)
107
- end
164
+ def path(options={}, &block)
165
+ before(options, &block)
108
166
  end
109
167
 
110
- def before(&block)
111
- @before_actions << block
112
- end
113
-
114
- def after(&block)
115
- @after_actions << block
168
+ def file(options={}, &block)
169
+ new_options = options.clone
170
+ new_options[:type] = :file
171
+ before(new_options, &block)
116
172
  end
117
173
 
118
- def applies_to(path)
119
- File.fnmatch(@glob, path)
174
+ def dir(options={}, &block)
175
+ new_options = options.clone
176
+ new_options[:type] = :dir
177
+ before(new_options, &block)
120
178
  end
121
179
 
122
- def evaluate_path(path)
123
- return nil unless applies_to(path)
124
- unless @edit_in_place
125
- File.open(path) do |in_stream|
126
- Chirp::redirect_io_and_run(in_stream, $stdout) {evaluate(path)}
127
- end
128
- else
129
- backup_path = path + ".bak"
130
- File.copy(path, backup_path)
131
- File.open(backup_path) do |in_stream|
132
- File.open(path, 'w') do |out_stream|
133
- Chirp::redirect_io_and_run(in_stream, out_stream) {evaluate(path)}
180
+ def line(options={}, &block)
181
+ @content_processors << LineProcessor.new(@context, options, &block)
182
+ end
183
+
184
+ def execute
185
+ @context.paths = filtered_files(@context.dir)
186
+ @context.paths.each do |path|
187
+ @context.debug( "Trying out processor #{self} for #{path} and #{@filter}" )
188
+ @context.path = path
189
+ @context.debug( "Executing processor #{self} for #{path}" )
190
+ @context.output_path = @context.path.pathmap(@output_spec) if @output_spec
191
+ if @output_spec and @redirect_output
192
+ Chirp::run_with_output_file(@context.output_path, @create_backups) {process}
193
+ else
194
+ process
134
195
  end
135
196
  end
136
- end
137
197
  end
138
198
 
139
- def evaluate(path)
140
- @context.path = path
199
+ def process
200
+ @before_processors.each {|p| p.execute}
201
+ process_contents
202
+ @after_processors.each {|p| p.execute}
203
+ end
204
+
205
+ def process_contents
206
+ return if @content_processors.empty?
207
+ return unless File.file?(@context.full_path)
208
+
209
+ @content_processors.each {|p| p.reset}
141
210
  @context.field_separator = @field_separator
142
- @before_actions.each {|a| @context.execute(a)}
143
- unless @match_statements.empty?
211
+
212
+ Chirp::run_with_input_file(@context.full_path) do
144
213
  @context.line_no = 1
145
214
  while not $stdin.eof?
146
- @context.fields=nil
147
215
  @context.line = $stdin.readline.chomp
148
- @match_statements.each do |statement|
149
- statement.evaluate(@context)
150
- end
216
+ @content_processors.each {|p| p.execute}
151
217
  @context.line_no += 1
152
218
  end
219
+ @context.contents = nil
153
220
  end
154
- @context.contents = nil
155
- @after_actions.each {|a| @context.execute(a)}
156
221
  end
157
222
 
158
- def predicate_for(arg)
159
- return LineNumberPredicate.new(arg) if arg.kind_of?(Numeric)
160
- return StringPredicate.new(arg) if arg.kind_of?(String)
161
- return MatchPredicate.new(arg) if arg.kind_of?(Regexp)
162
- return RangePredicate.new(arg) if arg.kind_of?(Range)
163
- return LambdaPredicate.new(arg) if arg.kind_of?(Proc)
164
- raise "Unknown argument: #{arg}"
223
+ def filtered_files(dir)
224
+ results = Set.new
225
+ FileUtils.chdir(dir) do
226
+ Find.find('.') do |p|
227
+ path = p.sub(/^\.\//, '')
228
+ next if path == '.'
229
+ next if path == '..'
230
+ next unless @filter.passes?(path)
231
+ results << path
232
+ end
233
+ end
234
+ results.sort
165
235
  end
166
- end
167
-
168
236
 
237
+ end
169
238
 
170
239
  class Application
240
+ include Chirp::FSSugar
171
241
 
172
- DEFAULT_ACTION = lambda {puts line}
242
+ def initialize
243
+ @processors = []
244
+ @context = Context.new
245
+ end
173
246
 
174
- attr_accessor :verbose
247
+ def program(chirp_string, file='-')
248
+ instance_eval(chirp_string, file)
249
+ end
175
250
 
176
- def initialize(program_string, verbose=false)
177
- @glob = nil
178
- @actions=[]
179
- @verbose=verbose
180
- @context = Chirp::Context.new(/[ \t]/)
181
- self.program=program_string if program_string
251
+ def dir(path)
252
+ @processors << StaringWithProcessor.new(@context, path)
182
253
  end
183
254
 
184
- def setup(args)
185
- self.program=File.read(args[0])
255
+ def verbose(setting=true)
256
+ @context.verbose = setting
186
257
  end
187
258
 
188
- def program=(chirp_program_string)
189
- instance_eval(chirp_program_string, 'user program')
259
+ def now(&block)
260
+ @context.debug('Processing now command:', block)
261
+ @processors << NowProcessor.new(@context, &block)
190
262
  end
191
263
 
192
- def run(&block)
193
- @actions << Action.new(@context, &block)
264
+ def comment(*args)
265
+ @context.comment(*args)
194
266
  end
195
267
 
196
- def file(glob='*', &block)
197
- @actions << PathAction.new(glob, @context, &block)
268
+ def each(options={}, &block)
269
+ @context.debug('Processing each command:', block)
270
+ @processors << EachProcessor.new(@context, options, &block)
198
271
  end
199
272
 
200
- def files(args)
201
- @glob = args unless args.empty?
202
- @glob
273
+ def mv(options={})
274
+ @context.debug('Processing mv command:', options)
275
+ options = options.clone
276
+ options[:create_backups] = false unless options.key?(:create_backups)
277
+ options[:redirect_output]= false
278
+ each_path options do
279
+ raise "Output path not specified for rename" unless output_path
280
+ mv full_path, output_path
281
+ end
203
282
  end
204
283
 
205
- def evaluate(in_stream=$stdin, out_stream=$stdout)
206
- log('Running Chirp program')
207
- Chirp::redirect_io_and_run(in_stream, out_stream) do
208
- log('Running before actions')
209
- if not @glob
210
- process_stream(in_stream, out_stream, '-')
211
- else
212
- process_files(@glob)
213
- end
214
- log('Running after actions')
284
+ def copy(options={})
285
+ options = options.clone
286
+ options[:create_backups] = false unless options.key?(:create_backups)
287
+ options[:redirect_output]= false
288
+ @context.debug('Processing copy command:', options)
289
+ each_path options do
290
+ raise "Output path not specified for copy" unless output_path
291
+ cp full_path, output_path unless options[:recursive]
292
+ cp_r full_path, output_path if options[:recursive]
215
293
  end
216
- log('Chirp program done')
217
294
  end
218
295
 
219
- def process_stream(in_stream, outstream, name)
220
- @actions.each do |path_action|
221
- Chirp::redirect_io_and_run(in_stream, outstream) do
222
- path_action.evaluate(name)
223
- end
296
+ def each_path(options={}, &block)
297
+ @context.debug('Processing each_path command:', options, block)
298
+ each(options) do
299
+ before(&block)
224
300
  end
225
301
  end
226
302
 
227
- def process_files(globs)
228
- @actions.each do |action|
229
- paths = expand_globs(globs)
230
- action.evaluate_paths(paths)
303
+ def each_file(options={}, &block)
304
+ @context.debug('Processing each_file command:', options, block)
305
+ new_options = options.clone
306
+ new_options[:type] = :file
307
+ each_path(new_options, &block)
308
+ end
309
+
310
+ def each_line(options={}, &block)
311
+ @context.debug('Processing each_line command:', options, block)
312
+ each(options) do
313
+ line(options,&block)
231
314
  end
232
315
  end
233
316
 
234
- def log(*args)
235
- return unless @verbose
236
- STDERR.puts args.join(' ')
317
+ def each_dir(options={}, &block)
318
+ @context.debug('Processing each_dir command:', options, block)
319
+ new_options = options.clone
320
+ new_options[:type] = :dir
321
+ each_path(new_options, &block)
237
322
  end
238
323
 
239
- def expand_globs(globs)
240
- results = []
241
- globs.each do |path_pattern|
242
- this_glob_results = []
243
- Dir.glob(path_pattern) do |path|
244
- this_glob_results << path if Pathname.new(path).file?
324
+ def execute(in_stream=$stdin, out_stream=$stdout)
325
+ @context.debug('Application execute')
326
+
327
+ temp_file = nil
328
+ @context.debug('Sources: ', *@context.paths)
329
+ Chirp::run_with_input_stream(in_stream) do
330
+ Chirp::run_with_output_stream(out_stream) do
331
+ @processors.each do |processor|
332
+ @context.debug('Calling ', processor)
333
+ processor.execute
334
+ end
245
335
  end
246
- this_glob_results.sort!
247
- results << this_glob_results
248
336
  end
249
- results.flatten!
337
+ temp_file.unlink if temp_file
250
338
  end
251
339
  end
252
340
  end
341
+