module RSpec::Rails module BaseHelper def self.included(base) base.extend ClassMethods base.class_eval do include RSpec::Rails::App include Thor::Actions end end module ClassMethods def aliases_for name multi_alias name, :create => :new, :insert_into => [:inject_into, :update], :read => :X_content, :remove => :delete, :options => :after end end def type_postfix type "_#{type}" if ![:model].include?(type) end def artifact_path name, type, dir=nil dir ||= send :"#{type}_dir" File.join(dir, "#{name}#{type_postfix type}.rb") end # CREATE def create_artifact name, options={}, &block type = get_type options file = file_name name, type return if File.exist?(file) debug "create #{type}: #{name}" debug "file: #{file}" # make dir dir = File.dirname(file) FileUtils.mkdir_p dir if !File.directory?(dir) content = options[:content] content ||= yield if block content_method = :"new_#{type}_content" debug "inner content: #{content}, to inject using ##{content_method}" raise "Content method #{content_method} not found #{orm_notify}" if !respond_to?(content_method) insert_content = send content_method, name, content debug "content to insert: #{insert_content}" return if insert_content.blank? File.open(file, 'w') do |f| f.puts insert_content end debug "file created and content inserted" end # READ def read_artifact(name, options, &block) type = get_type options file_name = existing_file_name(name, type) debug "reading from: #{file_name}" content = File.new(file_name).try :read debug "read content: #{content}" yield content if block content end # UPDATE def insert_content(name, options={}, &block) type = get_type options file = existing_file_name(name, type) file_insertion file, marker(name, type, options), options, &block end # DELETE def remove_artifact name, type debug "remove artifact: #{name}" file = file_name name, type debug "removed" if File.exist?(file) && FileUtils.rm_f(file) end def remove_artifacts type,*names names.flatten.each{|name| send :"remove_#{type}", name } end def new_artifact_content name, type, content=nil, &block content ||= yield if block %Q{class #{marker(name, type)} #{content} end} end protected def get_type options = {} raise ArgumentError, "No artifact type specified" if !options[:type] options[:type] end def debug? RSpec::Generator.debug? end def debug msg puts msg if debug? end def set options, type options.merge!(:type => type) options end def orm_notify '' end def marker name, type, options={} send :"#{type}_marker", name, options end def file_name name, type, options={} send :"#{type}_file_name", name, options end def existing_file_name name, type # first try finder method finder_method = :"find_#{type}" found = send finder_method, name if respond_to? finder_method # default file_name(name, type) if !found end def file_insertion(file_name, marker, options={}, &block) debug "file insertion: marker = #{marker}" return if !marker file = File.new(file_name) return if !File.exist?(file) insert_content = options[:content] || (yield if block) # already inserted? return if insert_content.blank? || (file.read =~ /#{insert_content}/) place = options[:before] ? :before : :after debug "inserted #{place}: '#{marker}'" debug "content: #{insert_content}" mutate_file file.path, marker, place do insert_content end end def mutate_file file, marker, place, &block raise ArgumentError, "You must define a replacement marker for a :before or :after key" if !marker replace_in_file file, /(#{Regexp.escape(marker)})/mi do |match| place == :after ? "#{match}#{yield}" : "#{yield}#{match}" end end def replace_in_file(path, regexp, *args, &block) content = File.read(path).gsub(regexp, *args, &block) File.open(path, 'wb') { |file| file.write(content) } end end end