Skip to content
Browse files

Allow setting options in the file itself via $frag-config lines.

  • Loading branch information...
1 parent 0e86e44 commit f1dbe7f38fa68f953c77cc20caf68e22df6a0e1c @oggy committed Aug 11, 2012
Showing with 338 additions and 45 deletions.
  1. +1 −0 CHANGELOG
  2. +108 −34 lib/frag/app.rb
  3. +229 −11 test/unit/test_app.rb
View
1 CHANGELOG
@@ -1,5 +1,6 @@
== LATEST
+ * Allow setting options in the file itself via $frag-config lines.
* Fix error messages about missing delimters.
== 0.1.0 2012-08-05
View
142 lib/frag/app.rb
@@ -1,54 +1,28 @@
require 'tempfile'
require 'fileutils'
require 'optparse'
+require 'shellwords'
module Frag
class App
def initialize(args, input=STDIN, output=STDOUT, error=STDERR)
@input, @output, @error = input, output, error
@status = 0
- beginning = 'frag:'
- ending = 'frag end'
- leader = '#'
- trailer = ''
- @backup_prefix = @backup_suffix = nil
-
- parser = OptionParser.new do |parser|
- parser.banner = "USAGE: #$0 [options] file ..."
-
- parser.on '-b', '--begin DELIMITER' do |value|
- beginning = Regexp.escape(value)
- end
- parser.on '-e', '--end DELIMITER' do |value|
- ending = Regexp.escape(value)
- end
- parser.on '-l', '--leader STRING' do |value|
- leader = Regexp.escape(value)
- end
- parser.on '-t', '--trailer STRING' do |value|
- trailer = Regexp.escape(value)
- end
- parser.on '-p', '--backup-prefix PREFIX' do |value|
- @backup_prefix = value
- end
- parser.on '-s', '--backup-suffix SUFFIX' do |value|
- @backup_suffix = value
- end
- end
+ @state = State.new('frag:', 'frag end', '#', '', nil, nil)
parser.parse!(args)
args.size > 0 or
return error "no files given"
- @begin_line = Regexp.new(['^', leader, beginning, '(.*)', trailer, '$'].reject(&:empty?).join('\\s*'))
- @end_line = Regexp.new(['^', leader, ending, trailer, '$'].reject(&:empty?).join('\\s*'))
@input_paths = args
end
def run
return @status if @status != 0
+ global_state = @state.dup
@input_paths.each do |input_path|
+ @state = global_state.dup
manage_files(input_path) do |input, output|
process(input, output)
end
@@ -66,6 +40,42 @@ def error(message, status=1)
false
end
+ def parser
+ @parser ||= OptionParser.new do |parser|
+ parser.banner = "USAGE: #$0 [options] file ..."
+
+ parser.on '-b', '--begin DELIMITER' do |value|
+ @state.beginning = value
+ end
+ parser.on '-e', '--end DELIMITER' do |value|
+ @state.ending = value
+ end
+ parser.on '-l', '--leader STRING' do |value|
+ @state.leader = value
+ end
+ parser.on '-t', '--trailer STRING' do |value|
+ @state.trailer = value
+ end
+ parser.on '-p', '--backup-prefix PREFIX' do |value|
+ @state.backup_prefix = value
+ end
+ parser.on '-s', '--backup-suffix SUFFIX' do |value|
+ @state.backup_suffix = value
+ end
+
+ def parser.parse_subconfig!(args)
+ # OptionParser will error on an argument like like "-->".
+ if args.last =~ /\A--?(?:\W|\z)/
+ last_arg = args.pop
+ parse!(args)
+ args << last_arg
+ else
+ parse!(args)
+ end
+ end
+ end
+ end
+
def manage_files(input_path)
File.exist?(input_path) or
return error "file not found: #{input_path}"
@@ -82,8 +92,8 @@ def manage_files(input_path)
output
end
end
- if @backup_prefix || @backup_suffix
- backup_path = "#{@backup_prefix}#{File.expand_path(input_path)}#{@backup_suffix}"
+ if @state.backup_prefix || @state.backup_suffix
+ backup_path = "#{@state.backup_prefix}#{File.expand_path(input_path)}#{@state.backup_suffix}"
FileUtils.mkdir_p File.dirname(backup_path)
FileUtils.cp input_path, backup_path
end
@@ -98,12 +108,12 @@ def process(input, output)
output.puts line
end
case line
- when @begin_line
+ when @state.begin_line
region_start.nil? or
return error "#{input.lineno}: nested region"
command = $1
region_start = input.lineno
- when @end_line
+ when @state.end_line
region_start or
return error "#{input.lineno}: unmatched end delimiter"
output.puts `#{command}`
@@ -112,6 +122,13 @@ def process(input, output)
end
region_start = nil
output.puts line
+ when /\A\s*(?:(\S+)\s*)?\$frag-config:\s*(.*)$/
+ args = Shellwords.shellsplit($2)
+ parser.parse_subconfig!(args)
+ args.size <= 1 or
+ return error "#{input.lineno}: unexpected argument(s): #{args[0..-2].join(' ')}"
+ @state.leader = $1 || ''
+ @state.trailer = args.first || ''
end
end
if region_start
@@ -120,4 +137,61 @@ def process(input, output)
true
end
end
+
+ class State
+ def initialize(beginning, ending, leader, trailer, backup_prefix, backup_suffix)
+ @beginning = beginning
+ @ending = ending
+ @leader = leader
+ @trailer = trailer
+ @backup_prefix = backup_prefix
+ @backup_suffix = backup_suffix
+ end
+
+ attr_reader :beginning, :ending, :leader, :trailer, :backup_prefix, :backup_suffix
+
+ def beginning=(value)
+ @beginning = value
+ @begin_line = nil
+ end
+
+ def ending=(value)
+ @ending = value
+ @end_line = nil
+ end
+
+ def leader=(value)
+ @leader = value
+ @begin_line = @end_line = nil
+ end
+
+ def trailer=(value)
+ @trailer = value
+ @begin_line = @end_line = nil
+ end
+
+ attr_writer :backup_prefix, :backup_suffix
+
+ def begin_line
+ @begin_line ||= build_begin_line
+ end
+
+ def end_line
+ @end_line ||= build_end_line
+ end
+
+ def build_begin_line
+ leader = Regexp.escape(@leader)
+ beginning = Regexp.escape(@beginning)
+ trailer = Regexp.escape(@trailer)
+ @begin_line = Regexp.new(['^', leader, beginning, '(.*)', trailer, '$'].reject(&:empty?).join('\\s*'))
+ end
+
+ def build_end_line
+ leader = Regexp.escape(@leader)
+ ending = Regexp.escape(@ending)
+ trailer = Regexp.escape(@trailer)
+ @end_line = Regexp.new(['^', leader, ending, trailer, '$'].reject(&:empty?).join('\\s*'))
+ end
+ end
end
View
240 test/unit/test_app.rb
@@ -156,34 +156,252 @@ def frag(*args)
['-t', '--trailer'].each do |option|
it "uses the delimiter trailer given by #{option}" do
write_file 'input', <<-EOS.demargin
- |# frag: echo one !!
- |# frag end !!
+ |# frag: echo one @@
+ |# frag end @@
EOS
- frag(option, '!!', 'input').must_equal 0
+ frag(option, '@@', 'input').must_equal 0
(output.string + error.string).must_equal ''
File.read('input').must_equal <<-EOS.demargin
- |# frag: echo one !!
+ |# frag: echo one @@
|one
- |# frag end !!
+ |# frag end @@
EOS
end
end
it "supports using delimiter options together" do
write_file 'input', <<-EOS.demargin
- |<!-- BEGIN echo one -->
- |<!-- END -->
+ |/* BEGIN echo one */
+ |/* END */
EOS
- frag('-b', 'BEGIN', '-e', 'END', '-l', '<!--', '-t', '-->', 'input').must_equal 0
+ frag('-b', 'BEGIN', '-e', 'END', '-l', '/*', '-t', '*/', 'input').must_equal 0
(output.string + error.string).must_equal ''
File.read('input').must_equal <<-EOS.demargin
- |<!-- BEGIN echo one -->
+ |/* BEGIN echo one */
|one
- |<!-- END -->
+ |/* END */
EOS
end
end
+ describe "a $frag-config line" do
+ it "honors the --begin option" do
+ write_file 'input', <<-EOS.demargin
+ |# BEGIN echo one
+ |# frag: echo one
+ |# frag end
+ |# $frag-config: --begin BEGIN
+ |# BEGIN echo one
+ |# frag: echo one
+ |# frag end
+ EOS
+ frag('input').must_equal 0
+ (output.string + error.string).must_equal ''
+ File.read('input').must_equal <<-EOS.demargin
+ |# BEGIN echo one
+ |# frag: echo one
+ |one
+ |# frag end
+ |# $frag-config: --begin BEGIN
+ |# BEGIN echo one
+ |one
+ |# frag end
+ EOS
+ end
+
+ it "honors the --end option" do
+ write_file 'input', <<-EOS.demargin
+ |# frag: echo one
+ |# END
+ |# frag end
+ |# $frag-config: --end END
+ |# frag: echo one
+ |# END
+ |# frag end
+ EOS
+ frag('input').must_equal 0
+ (output.string + error.string).must_equal ''
+ File.read('input').must_equal <<-EOS.demargin
+ |# frag: echo one
+ |one
+ |# frag end
+ |# $frag-config: --end END
+ |# frag: echo one
+ |one
+ |# END
+ |# frag end
+ EOS
+ end
+
+ it "infers the leader and trailer" do
+ write_file 'input', <<-EOS.demargin
+ |/* frag: echo one */
+ |/* frag end */
+ |/* $frag-config: */
+ |/* frag: echo one */
+ |/* frag end */
+ EOS
+ frag('input').must_equal 0
+ (output.string + error.string).must_equal ''
+ File.read('input').must_equal <<-EOS.demargin
+ |/* frag: echo one */
+ |/* frag end */
+ |/* $frag-config: */
+ |/* frag: echo one */
+ |one
+ |/* frag end */
+ EOS
+ end
+
+ it "can nullify the leader and trailer" do
+ write_file 'input', <<-EOS.demargin
+ |frag: echo one
+ |frag end
+ |$frag-config:
+ |frag: echo one
+ |frag end
+ EOS
+ frag('input').must_equal 0
+ (output.string + error.string).must_equal ''
+ File.read('input').must_equal <<-EOS.demargin
+ |frag: echo one
+ |frag end
+ |$frag-config:
+ |frag: echo one
+ |one
+ |frag end
+ EOS
+ end
+
+ it "handles trailers that start with '-', which can trick optparse" do
+ write_file 'input', <<-EOS.demargin
+ |<!-- frag: echo one -->
+ |<!-- frag end -->
+ |<!-- $frag-config: -->
+ |<!-- frag: echo one -->
+ |<!-- frag end -->
+ EOS
+ frag('input').must_equal 0
+ (output.string + error.string).must_equal ''
+ File.read('input').must_equal <<-EOS.demargin
+ |<!-- frag: echo one -->
+ |<!-- frag end -->
+ |<!-- $frag-config: -->
+ |<!-- frag: echo one -->
+ |one
+ |<!-- frag end -->
+ EOS
+ end
+
+ it "honors the --backup-prefix option" do
+ write_file 'input', <<-EOS.demargin
+ |# $frag-config: --backup-prefix path/to/backups
+ |# frag: echo new
+ |old
+ |# frag end
+ EOS
+ frag('input').must_equal 0
+ (output.string + error.string).must_equal ''
+ File.read('input').must_equal <<-EOS.demargin
+ |# $frag-config: --backup-prefix path/to/backups
+ |# frag: echo new
+ |new
+ |# frag end
+ EOS
+ File.read("path/to/backups/#{File.expand_path('input')}").must_equal <<-EOS.demargin
+ |# $frag-config: --backup-prefix path/to/backups
+ |# frag: echo new
+ |old
+ |# frag end
+ EOS
+ end
+
+ it "honors the --backup-suffix option" do
+ write_file 'input', <<-EOS.demargin
+ |# $frag-config: --backup-suffix .backup
+ |# frag: echo new
+ |old
+ |# frag end
+ EOS
+ frag('input').must_equal 0
+ (output.string + error.string).must_equal ''
+ File.read('input').must_equal <<-EOS.demargin
+ |# $frag-config: --backup-suffix .backup
+ |# frag: echo new
+ |new
+ |# frag end
+ EOS
+ File.read('input.backup').must_equal <<-EOS.demargin
+ |# $frag-config: --backup-suffix .backup
+ |# frag: echo new
+ |old
+ |# frag end
+ EOS
+ end
+
+ it "scopes options to the file it appears in" do
+ write_file 'a', <<-EOS.demargin
+ |/* $frag-config: -b BEGIN -e END */
+ |/* BEGIN echo a */
+ |/* END */
+ EOS
+ write_file 'b', <<-EOS.demargin
+ |/* BEGIN echo b */
+ |/* END */
+ |# frag: echo b
+ |# frag end
+ EOS
+ frag('a', 'b').must_equal 0
+ (output.string + error.string).must_equal ''
+ File.read('a').must_equal <<-EOS.demargin
+ |/* $frag-config: -b BEGIN -e END */
+ |/* BEGIN echo a */
+ |a
+ |/* END */
+ EOS
+ File.read('b').must_equal <<-EOS.demargin
+ |/* BEGIN echo b */
+ |/* END */
+ |# frag: echo b
+ |b
+ |# frag end
+ EOS
+ end
+
+ it "can be used more than once in a file" do
+ write_file 'input', <<-EOS.demargin
+ |// $frag-config:
+ |// frag: echo one
+ |// frag end
+ |
+ |/* $frag-config: */
+ |/* frag: echo one */
+ |/* frag end */
+ EOS
+ frag('input', '--trailer', '1').must_equal 0
+ (output.string + error.string).must_equal ''
+ File.read('input').must_equal <<-EOS.demargin
+ |// $frag-config:
+ |// frag: echo one
+ |one
+ |// frag end
+ |
+ |/* $frag-config: */
+ |/* frag: echo one */
+ |one
+ |/* frag end */
+ EOS
+ end
+
+ it "errors if there are stray arguments" do
+ write_file 'input', <<-EOS.demargin
+ |# $frag-config: arg trailer
+ EOS
+ frag('input').must_equal 1
+ (output.string + error.string).must_match /unexpected argument/
+ end
+ end
+
describe "when the backup options are used" do
['-p', '--backup-prefix'].each do |option|
it "backs up the input file with the prefix given by #{option}" do
@@ -249,7 +467,7 @@ def frag(*args)
EOS
end
- it "does not back up files which produces errors" do
+ it "does not back up files which produce errors" do
write_file 'a', <<-EOS.demargin
|# frag: true
|# frag end

0 comments on commit f1dbe7f

Please sign in to comment.
Something went wrong with that request. Please try again.