require 'yaml' #require 'facet/hash/traverse' require 'facet/hash/graph' require 'facet/string/tabto' require 'facet/dir/self/ascend' #require 'facet/basicobject' require 'facet/opencascade' # Project information. # # ProjectInfo is a Singleton. Access it via the # ProjectInfo.instance method. class ProjectInfo include Enumerable INFO_FILES = [ 'ProjectInfo','projectinfo' ] class << self # Singelton Pattern. private :new def instance( &block ) @instance ||= block_given? ? new( &block ) : load end # Move to file's location. def prime_location( fpath=nil ) if fpath Dir.chdir( File.dirname( fpath ) ) else fpath = find if fpath Dir.chdir( File.dirname( fpath ) ) end end end # Find project information file. def find info_dir, info_file = nil, nil Dir.ascend(Dir.pwd) do |info_dir| info_file = INFO_FILES.find{ |f| File.file?( File.join( info_dir, f ) ) } break if info_file end return nil unless info_file return File.join( info_dir, info_file ) end # Load the project information from a file. Generally # no file needs to be specified; the file will be found # by ascending up the current path until a default # file name is found (eg. ProjectInfo or Reapfile). def load( fpath=nil ) unless fpath fpath = find if fpath Dir.chdir( File.dirname( fpath ) ) end end new.read( fpath ) end def exist? @instance.info_file ? true : false end alias_method :exists?, :exist? end attr_reader :info, :info_stream, :info_dir, :info_file def initialize( &block ) @info = {} @info_stream = nil # define( &block ) if block_given? end # Define project information programmatically. # def define( &block ) # return unless block # # @info = HashBuilder.new( &block ).to_h # @info_stream = @info.to_yaml # @info_dir = Dir.pwd #? # @info_file = nil # # #validate # defaults # # self # end # Load project information from YAML file. def read( fpath ) return self unless fpath @info_dir = File.dirname( fpath ) @info_file = fpath #File.basename( fpath ) @info_stream = File.read( fpath ).strip @info = YAML::load( info_stream ) @info = @info.graph{ |k,v| [k.to_s.downcase, v] } #validate defaults =begin # register the task entries @info.each do |key, val| #value = @info[key] = {} if value.nil? if Reap::TaskDef === val val.name = key.to_s end end =end self end # # Update project information. # # def update( info=nil, &block ) # if info # @info.update( info.traverse{ |k,v| [k.to_s.downcase, v] } ) # end # if block_given? # @info.update( HashBuilder.new( &block ).to_h ) # end # @info_stream = @info.to_yaml # # #validate # defaults # # self # end # Project information file exists? (may need to change to info exists?) def exists? @info_file end alias_method :exist?, :exists? # Validate project information. def validate #( *fields ) val = true (puts "NAME is required in project information file."; val=false) unless info['name'] (puts "VERSION is required in project information file."; val=false) unless info['version'] exit -1 unless val end # Project information defaults. def defaults self['title'] ||= self['name'].capitalize self['series'] ||= '1' self['date'] ||= Time.now.strftime("%Y-%m-%d") self['author'] ||= "Anonymous" self['maintainer'] ||= self['author'] self['arch'] ||= 'Any' self['license'] ||= 'Ruby/GPL' self['status'] ||= 'Beta' self['project'] ||= self['rubyforge'] ? self['rubyforge']['project'] : nil self['homepage'] ||= self['rubyforge'] ? self['rubyforge']['homepage'] : nil self['trunk'] ||= 'trunk' if File.directory?( File.join( @info_dir, 'trunk' )) end # Convert to a CascadinOpenObject. def to_opencascade OpenCascade.new( @info ) end # Information fetch. def [](name) @info[name] end # Information store. def []=(name, x) @info[name] = x end def each( &block ) @info.each( &block ) end # Information to hash. def to_h @info.dup end end