require 'fileutils' module Beet module FileSystem # Create a new file in the project folder. Specify the # relative path from the project's root. Data is the return value of a block # or a data string. # # ==== Examples # # file("lib/fun_party.rb") do # hostname = ask("What is the virtual hostname I should use?") # "vhost.name = #{hostname}" # end # # file("config/apach.conf", "your apache config") def file(filename, data = nil, log_action = true, &block) log 'file', filename if log_action dir, file = [File.dirname(filename), File.basename(filename)] inside(dir) do File.open(file, "w") do |f| data = block.call if block_given? f.write(remove_blank_start(data)) end end end # Create a new file in the lib/ directory. Code can be specified # in a block or a data string can be given. # # ==== Examples # # lib("crypto.rb") do # "crypted_special_value = '#{rand}--#{Time.now}--#{rand(1337)}--'" # end # # lib("foreign.rb", "# Foreign code is fun") # def lib(filename, data = nil, &block) log 'lib', filename file("lib/#{filename}", data, false, &block) end # Create a new Rakefile with the provided code (either in a block or a string). # # ==== Examples # # rakefile("bootstrap.rake") do # project = ask("What is the UNIX name of your project?") # # <<-TASK # namespace :#{project} do # task :bootstrap do # puts "i like boots!" # end # end # TASK # end # # rakefile("seed.rake", "puts 'im plantin ur seedz'") # def rakefile(filename, data = nil, &block) log 'rakefile', filename file("lib/tasks/#{filename}", data, false, &block) end # Do something in the root of the project or # a provided subfolder; the full path is yielded to the block you provide. # The path is set back to the previous path when the method exits. def inside(dir = '', &block) folder = File.join(root, dir) FileUtils.mkdir_p(folder) unless File.exist?(folder) FileUtils.cd(folder) { block.arity == 1 ? yield(folder) : yield } end def in_root FileUtils.cd(root) { yield } end # Run a regular expression replacement on a file # # ==== Example # # gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1' # def gsub_file(path, regexp, *args, &block) #path = destination_path(relative_destination) content = File.read(path) check_for = args.first || yield('') regex = Regexp.new(regexp.source + Regexp.escape(check_for)) return if content =~ regex # if we can match the text and its leadin regex, don't add again content = content.gsub(regexp, *args, &block) File.open(path, 'wb') { |file| file.write(content) } end # Append text to a file # # ==== Example # # append_file 'config/environments/test.rb', 'config.gem "rspec"' # def append_file(relative_destination, data) path = destination_path(relative_destination) File.open(path, 'ab') { |file| file.write("\n#{data}") } end # Add text after matching line # # ==== Example # # add_after 'config/environment.rb', '# config.gem "aws-s3", :lib => "aws/s3"' # def add_after(filename, matching_text, data=nil, &block) gsub_file filename, /(\s*#{Regexp.escape(matching_text)})/mi do |match| "#{match}\n#{data || block.call}" end end # Add text after matching line, otherwise append to file wrapped in before/after text # # ==== Example # # add_after_or_append 'Gemfile', 'group :test', "gem 'rspec-rails'", "group :test do", "end" # def add_after_or_append(filename, matching_text, text_to_add, before_text, after_text) content = File.read(filename) if content.include?(matching_text) add_after(filename, matching_text, text_to_add) else append_file(filename, [before_text, text_to_add, after_text].join("\n")) end end # Add text before matching line # # ==== Example # # add_before 'config/environment.rb', '# config.gem "aws-s3", :lib => "aws/s3"' # def add_before(filename, matching_text, data=nil, &block) gsub_file filename, /^(\s*#{Regexp.escape(matching_text)})/mi do |match| "#{data || block.call}#{match}" end end protected def destination_path(relative_destination) File.join(root, relative_destination) end # it's common for multi-line strings to be coded in a way so that their first character is a newline # like this # string = %{ # foo # } # string => "\nfoo\n" # This is bad when it needs to be #!/usr/bin/something so we remove that newline in a dumb but effective way for now def remove_blank_start(string) string.reverse.chomp.reverse end end end