From 9deed54d00556b41b52395765de95c9925f2294b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 30 Aug 2009 15:48:05 +0200 Subject: [PATCH] Added inject_into_class. --- lib/thor/actions/file_manipulation.rb | 46 +++++++++++++++++------- lib/thor/actions/inject_into_file.rb | 48 +++++++++++++++++--------- spec/actions/file_manipulation_spec.rb | 43 +++++++++++++---------- spec/actions/inject_into_file_spec.rb | 16 +++++++-- spec/fixtures/application.rb | 2 ++ 5 files changed, 105 insertions(+), 50 deletions(-) create mode 100644 spec/fixtures/application.rb diff --git a/lib/thor/actions/file_manipulation.rb b/lib/thor/actions/file_manipulation.rb index 74c157ba8..730149c66 100644 --- a/lib/thor/actions/file_manipulation.rb +++ b/lib/thor/actions/file_manipulation.rb @@ -100,7 +100,7 @@ def chmod(path, mode, config={}) FileUtils.chmod_R(mode, path) unless options[:pretend] end - # Prepend text to a file. + # Prepend text to a file. Since it depends on inject_into_file, it's reversible. # # ==== Parameters # path:: path of the file to be changed @@ -112,18 +112,16 @@ def chmod(path, mode, config={}) # prepend_file 'config/environments/test.rb', 'config.gem "rspec"' # def prepend_file(path, data=nil, config={}, &block) - return unless behavior == :invoke - path = File.expand_path(path, destination_root) - say_status :prepend, relative_to_original_destination_root(path), config.fetch(:verbose, true) + config.merge!(:after => /\A/) - unless options[:pretend] - content = data || block.call - content << File.read(path) - File.open(path, 'wb') { |file| file.write(content) } + if block_given? + inject_into_file(path, config, &block) + else + inject_into_file(path, data, config) end end - # Append text to a file. + # Append text to a file. Since it depends on inject_into_file, it's reversible. # # ==== Parameters # path:: path of the file to be changed @@ -135,10 +133,32 @@ def prepend_file(path, data=nil, config={}, &block) # append_file 'config/environments/test.rb', 'config.gem "rspec"' # def append_file(path, data=nil, config={}, &block) - return unless behavior == :invoke - path = File.expand_path(path, destination_root) - say_status :append, relative_to_original_destination_root(path), config.fetch(:verbose, true) - File.open(path, 'ab') { |file| file.write(data || block.call) } unless options[:pretend] + config.merge!(:before => /\z/) + + if block_given? + inject_into_file(path, config, &block) + else + inject_into_file(path, data, config) + end + end + + # Injects text right after the class definition. Since it depends on + # inject_into_file, it's reversible. + # + # ==== Parameters + # path:: path of the file to be changed + # klass:: the class to be manipulated + # data:: the data to append to the class, can be also given as a block. + # config:: give :verbose => false to not log the status. + # + def inject_into_class(path, klass, data=nil, config={}, &block) + config.merge!(:after => /class #{klass}\n|class #{klass} .*\n/) + + if block_given? + inject_into_file(path, config, &block) + else + inject_into_file(path, data, config) + end end # Run a regular expression replacement on a file. diff --git a/lib/thor/actions/inject_into_file.rb b/lib/thor/actions/inject_into_file.rb index e0a561e13..c6db1335e 100644 --- a/lib/thor/actions/inject_into_file.rb +++ b/lib/thor/actions/inject_into_file.rb @@ -3,10 +3,8 @@ class Thor module Actions - # Injects the given content into a file. Different from append_file, - # prepend_file and gsub_file, this method is reversible. By this reason, - # the flag can only be strings. gsub_file is your friend if you need to - # deal with more complex cases. + # Injects the given content into a file. Different from gsub_file, this + # method is reversible. # # ==== Parameters # destination:: Relative path to the destination root @@ -35,43 +33,61 @@ def inject_into_file(destination, *args, &block) end class InjectIntoFile < EmptyDirectory #:nodoc: - attr_reader :replacement + attr_reader :replacement, :flag, :behavior def initialize(base, destination, data, config) super(base, destination, { :verbose => true }.merge(config)) + + @behavior, @flag = if @config.key?(:after) + [:after, @config.delete(:after)] + else + [:before, @config.delete(:before)] + end + @replacement = data.is_a?(Proc) ? data.call : data + @flag = Regexp.escape(@flag) unless @flag.is_a?(Regexp) end def invoke! - say_status :inject, config[:verbose] + say_status :invoke - flag = if @config.key?(:after) - content = '\0' + replacement - @config[:after] + content = if @behavior == :after + '\0' + replacement else - content = replacement + '\0' - @config[:before] + replacement + '\0' end replace!(flag, content) end def revoke! - say_status :deinject, config[:verbose] + say_status :revoke - flag = if @config.key?(:after) + regexp = if @behavior == :after content = '\1\2' - /(#{Regexp.escape(@config[:after])})(.*)(#{Regexp.escape(replacement)})/m + /(#{flag})(.*)(#{Regexp.escape(replacement)})/m else content = '\2\3' - /(#{Regexp.escape(replacement)})(.*)(#{Regexp.escape(@config[:before])})/m + /(#{Regexp.escape(replacement)})(.*)(#{flag})/m end - replace!(flag, content) + replace!(regexp, content) end protected + def say_status(behavior) + status = if flag == /\A/ + behavior == :invoke ? :prepend : :unprepend + elsif flag == /\z/ + behavior == :invoke ? :append : :unappend + else + behavior == :invoke ? :inject : :deinject + end + + super(status, config[:verbose]) + end + # Adds the content to the file. # def replace!(regexp, string) diff --git a/spec/actions/file_manipulation_spec.rb b/spec/actions/file_manipulation_spec.rb index d832b21df..56c5a2e53 100644 --- a/spec/actions/file_manipulation_spec.rb +++ b/spec/actions/file_manipulation_spec.rb @@ -1,5 +1,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +class Application; end + describe Thor::Actions do def runner(options={}) @runner ||= MyCounter.new([1], options, { :destination_root => destination_root }) @@ -193,12 +195,6 @@ def file File.open(file).read.must == "__start__\nREADME\n__end__\nEND\n" end - it "does not append if pretending" do - runner(:pretend => true) - action :append_file, "doc/README", "END\n" - File.open(file).read.must == "__start__\nREADME\n__end__\n" - end - it "accepts a block" do action(:append_file, "doc/README"){ "END\n" } File.open(file).read.must == "__start__\nREADME\n__end__\nEND\n" @@ -207,10 +203,6 @@ def file it "logs status" do action(:append_file, "doc/README", "END").must == " append doc/README\n" end - - it "does not log status if required" do - action(:append_file, "doc/README", nil, :verbose => false){ "END" }.must be_empty - end end describe "#prepend_file" do @@ -219,12 +211,6 @@ def file File.open(file).read.must == "START\n__start__\nREADME\n__end__\n" end - it "does not prepend if pretending" do - runner(:pretend => true) - action :prepend_file, "doc/README", "START\n" - File.open(file).read.must == "__start__\nREADME\n__end__\n" - end - it "accepts a block" do action(:prepend_file, "doc/README"){ "START\n" } File.open(file).read.must == "START\n__start__\nREADME\n__end__\n" @@ -233,9 +219,30 @@ def file it "logs status" do action(:prepend_file, "doc/README", "START").must == " prepend doc/README\n" end + end - it "does not log status if required" do - action(:prepend_file, "doc/README", "START", :verbose => false).must be_empty + describe "#inject_into_class" do + def file + File.join(destination_root, "application.rb") + end + + it "appends content to a class" do + action :inject_into_class, "application.rb", Application, " filter_parameters :password\n" + File.open(file).read.must == "class Application < Base\n filter_parameters :password\nend\n" + end + + it "accepts a block" do + action(:inject_into_class, "application.rb", Application){ " filter_parameters :password\n" } + File.open(file).read.must == "class Application < Base\n filter_parameters :password\nend\n" + end + + it "logs status" do + action(:inject_into_class, "application.rb", Application, " filter_parameters :password\n").must == " inject application.rb\n" + end + + it "does not append if class name does not match" do + action :inject_into_class, "application.rb", "App", " filter_parameters :password\n" + File.open(file).read.must == "class Application < Base\nend\n" end end end diff --git a/spec/actions/inject_into_file_spec.rb b/spec/actions/inject_into_file_spec.rb index 07853d7a7..c9b77e4b0 100644 --- a/spec/actions/inject_into_file_spec.rb +++ b/spec/actions/inject_into_file_spec.rb @@ -61,14 +61,12 @@ def file it "deinjects the destination file after injection" do invoke! "doc/README", "\nmore content", :after => "__start__" revoke! "doc/README", "\nmore content", :after => "__start__" - File.read(file).must == "__start__\nREADME\n__end__\n" end it "deinjects the destination file before injection" do invoke! "doc/README", "more content\n", :before => "__start__" revoke! "doc/README", "more content\n", :before => "__start__" - File.read(file).must == "__start__\nREADME\n__end__\n" end @@ -76,7 +74,6 @@ def file invoke! "doc/README", "\nmore content", :after => "__start__" invoke! "doc/README", "\nanother stuff", :after => "__start__" revoke! "doc/README", "\nmore content", :after => "__start__" - File.read(file).must == "__start__\nanother stuff\nREADME\n__end__\n" end @@ -84,10 +81,23 @@ def file invoke! "doc/README", "more content\n", :before => "__start__" invoke! "doc/README", "another stuff\n", :before => "__start__" revoke! "doc/README", "more content\n", :before => "__start__" + File.read(file).must == "another stuff\n__start__\nREADME\n__end__\n" + end + it "deinjects when prepending" do + invoke! "doc/README", "more content\n", :after => /\A/ + invoke! "doc/README", "another stuff\n", :after => /\A/ + revoke! "doc/README", "more content\n", :after => /\A/ File.read(file).must == "another stuff\n__start__\nREADME\n__end__\n" end + it "deinjects when appending" do + invoke! "doc/README", "more content\n", :before => /\z/ + invoke! "doc/README", "another stuff\n", :before => /\z/ + revoke! "doc/README", "more content\n", :before => /\z/ + File.read(file).must == "__start__\nREADME\n__end__\nanother stuff\n" + end + it "shows progress information to the user" do invoke!("doc/README", "\nmore content", :after => "__start__") revoke!("doc/README", "\nmore content", :after => "__start__").must == " deinject doc/README\n" diff --git a/spec/fixtures/application.rb b/spec/fixtures/application.rb new file mode 100644 index 000000000..50d2faed9 --- /dev/null +++ b/spec/fixtures/application.rb @@ -0,0 +1,2 @@ +class Application < Base +end