# = ratchets/architect/scaffold.rb # # TODO Use ProjectInfo class rather than faking it? (trans) # The Architect tool provides a means for building up new # project layout, including all the typical standard # directories and files, or other pre-packaged project parts. # # New project layouts are automatically built to standard # specifications and can be wrapped in additional subversion # and/or website layers. # # Project parts can be retrived from remote sources and will # be automitacally decompressed and merged into the project. # If a scaffold.rb file is included in the parts package then # conditional building can take place. # # WARNING! This is a very new feature and should be used with # the expectaton that it is not 100% bug-free, and will certainly # warrent design changes in the future. require 'rbconfig' require 'tmpdir' require 'ostruct' require 'rbconfig' require 'fileutils' require 'yaml' #require 'facets/core/array/to_path.rb' module ProUtils def self.scaffold(type,opts) Scaffold.new(type,opts).create end # Scaffold class creates the project scaffolding. # class Scaffold include FileUtils def initialize( type, opts={} ) @type = type.downcase # dry run? if opts['dryrun'] || opts['noharm'] || opts['n'] extend FileUtils::DryRun else extend FileUtils end # use typical svn layout @svn = opts['svn'] || opts['subversion'] scaffold_script = File.join(scaffolds_directory, 'scaffold.rb') if File.file?(scaffold_script) extend Module.new do eval File.read(scaffold_script) end end unless File.directory?(scaffold_directory) puts "Unknown scaffolding type." exit -1 end end def data_directory @scaffolds_directory ||= File.join(Config::CONFIG['datadir'], 'architect') end def scaffolds_directory @scaffolds_directory ||= File.join(data_directory, 'scaffolds') end def scaffold_directory File.join(scaffolds_directory, @type, 'scaffold') end # Create a new conventional project layout. # # Generates a project directory layout within the current # directory. The scaffolding includes all the standard # features such as directories lib/ bin/ doc/ # and files such as README. # # To use this task you need to first create an new empty # directory and change into it. This command will not # create a new project directory for you. As a safegaurd, # this command will not generate scaffolding in a directory # with files already present, unless you use the -f (force) # option. # # You can specify a subversion layout by setting +svn+ to # true. You can also specify a web layout via the by setting # +web+ to true. # def create pre_create setup pre_build build post_build cleanup post_create puts "Project ready." end def pre_create; end def setup # project file if file = project_file? file = File.expand_path(file) else file = form() file = File.expand_path(file) if ENV['EDITOR'] `#{ENV['EDITOR']} #{file}` end end @project_file = file @project = YAML::load(File.open(@project_file)) # TODO need to lowercase all keys @name = @project['name'] || @project['project'] if @svn mkdir_p 'branches' mkdir_p 'tags' mkdir_p 'trunk' cd 'trunk' end #mkdir_p @name #cd name end def pre_build; end # def build glob_files = Dir.glob(File.join(scaffolds_directory, @type, '**/*')) glob_files.each do |file| if File.exist?(file) answer = ask("Overwrite #{file}?", 'Yn') case answer.downcase when 'y', 'yes' cp(file, '.') action_print "replace", file else action_print "skip", file end else mkdir_p(File.dirname(file)) cp(file, '.') action_print "write", file end end end def post_build; end def cleanup #if @project_file # move project file to source folder if differnt mv(@project_file,'.') unless Dir.pwd == File.dirname(@project_file) #else # # create project information file # form() #end end def post_create; end private # Creates a ProjectInfo file in the current directory. # It will not overwrite a ProjectInfo file if one is already # present. The file can be named ProjectInfo, PROJECT or # project.yaml. def form( opts={} ) #opts = OpenStruct.new(opts) name = opts['name'] || 'ProjectInfo' if name !~ /project(info)?(.yaml|.yml)?/i raise ArgumentError, "not a recognized project information file name" end f = nil files = Dir.glob("{[Pp]roject,PROJECT}{INFO,[Ii]nfo,.yml,.yaml,}") if f = files[0] puts "Project file '#{f}' already exists." return end file = File.join(datadir, 'scaffolds', 'project.yaml') install(file, name) unless opts['noharm'] File.open(name,'a') do |f| date = Time.now.strftime("%Y-%m-%d") f << "created: #{date}" end end unless opts['quiet'] puts "Created '#{name}'." puts "Please edit to suit your project." end return name end # Is there a project information file? def project_file? f = nil files = Dir.glob("{[Pp]roject,PROJECT}{INFO,[Ii]nfo,.yml,.yaml,}") return files[0] end # Project contains content? def project_content? content = Dir.entries('.') - ['.', '..'] content -= [project_file?] return !content.empty? end # Action print def action_print(action, file) action = (' ' * (12 - action.size)) + action puts "#{action} #{file}" end end end =begin def create( opts={} ) #opts = OpenStruct.new(opts) if project_content? and not opts['force'] puts "Directory already has content. Use -f option to force new scaffolding." return end # change 'standard' to 'ruby' in case other languages may # be supported in future? type = opts['type'] || 'standard' web = opts['web'] svn = opts['svn'] || opts['subversion'] type = 'standard' if type == 'std' if file = project_file? file = File.expand_path(file) else file = form() file = File.expand_path(file) if ENV['EDITOR'] `#{ENV['EDITOR']} #{file}` end end proj = YAML::load(File.open(file)) # set scaffolding dir dir = File.join( datadir, 'scaffolds' ) if web from = File.join(dir, 'website', '.') cp_r(from, '.') cd('src') end if svn mkdir_p('branches') mkdir_p('tags') mkdir_p('trunk') cd('trunk') end build end =end # def seed( keys, &yld ) # keys = (keys||yld).to_openobject # # name = keys.name # type = keys.type # site = keys.site # meta = keys.meta # web = keys.web # # raise ArgumentError, "missing field -- name or type" unless name # # if site # url = File.join(site, name) # scaffold(url, keys) # else # case name.to_s.downcase # when 'std', 'standard', 'svn', 'subversion' # new_project(keys) # when 'project', 'projectinfo', 'project.yaml', 'project.yml' # projectinfo_template(name, meta) # #when 'setup', 'setup.rb' # # setup_rb # else # url = File.join( Ratchets.datadir, 'scaffolding', name ) # scaffold(url, keys) # end # end # end # # private # def new_project( keys, &yld ) # keys = (keys||yld).to_openobject # # type = keys.type || keys.name # meta = keys.meta # web = keys.web # # raise ArgumentError, "Must supply new project type." unless type # # content = Dir.entries('.') - [ '.', '..' ] # if not content.empty? and not project.force? # puts "Directory already has content. Use -f option to force new scaffolding." # return nil # end # # local_store = File.join( Ratchets.datadir, 'scaffolding' ) # # type = 'standard' if type == 'std' # type = 'subversion' if type == 'svn' # # if web # from = File.join( local_store, 'website', '.' ) # FileUtils.cp_r( from, '.' ) # Dir.chdir( 'src' ) # end # # if type == 'subversion' or type == 'svn' # mkdir_p( 'branches' ) # mkdir_p( 'tags' ) # mkdir_p( 'trunk' ) # Dir.chdir( 'trunk' ) # end # # # copies typical project layout # from = File.join( local_store, 'standard', '.' ) # FileUtils.cp_r( from, '.' ) # # # create project.yaml template. # projectinfo_template(nil,meta) # # puts "Project ready." # end =begin # Generate pre-built targets in project's target folder. def targets( keys, &yld ) #( folder, targets=nil ) keys = (keys||yld).to_openobject folder = keys.folder || 'script' targets = keys.targets || keys.target match = File.join(File.dirname(__FILE__), 'targets', '*') files = Dir.glob(match) names = files.collect{ |f| File.basename(f).chomp('.rb') } cross = Hash[*names.zip(files).flatten] case targets when String targets = targets.split(',') when Array targets = [targets].flatten else targets = names end # only specific targets that exist? #targets = (AUTOTARGETS & targets) unless (diff=(targets-names)).empty? puts "project: unknown targets " + diff.join(',') return end mkdir_p(folder) targets.each do |name| file1 = cross[name] file2 = File.join(folder, name) unless (File.exist?(file2) or project.dryrun?) install(file1, file2, :mode=>0754) end end return folder end =end