chirp 0.2.0 → 0.3

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.
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
+