#
# SourceFile is a base for any Jsus operation.
#
# It contains basic info about source as well as file content.
#
#
module Jsus
class SourceFile
attr_accessor :relative_filename, :filename, :package # :nodoc:
# Constructors
# Basic constructor.
#
# You probably should use SourceFile.from_file instead.
#
# But if you know what you are doing, it accepts the following values:
# * +package+ — an instance of Package, normally passed by a parent
# * +relative_filename+ — used in Package, for generating tree structure of the source files
# * +filename+ — full filename for the given package
# * +content+ — file content of the source file
# * +pool+ — an instance of Pool
def initialize(options = {})
[:package, :header, :relative_filename, :filename, :content, :pool].each do |field|
send("#{field}=", options[field]) if options[field]
end
end
#
# Initializes a SourceFile given the filename and options
#
# options:
# * :pool: — an instance of Pool
# * :package: — an instance of Package
#
# returns either an instance of SourceFile or nil when it's not possible to parse the input
#
def self.from_file(filename, options = {})
if File.exists?(filename)
source = File.read(filename)
yaml_data = source.match(%r(^/\*\s*(---.*?)\*/)m)
if (yaml_data && yaml_data[1] && header = YAML.load(yaml_data[1]))
options[:header] = header
options[:relative_filename] = filename
options[:filename] = File.expand_path(filename)
options[:content] = source
new(options)
else
# puts "WARNING: file #{filename} has invalid format (should be YAML)"
nil
end
else
# puts "WARNING: file #{filename} does not exist"
nil
end
end
# Public API
#
# Returns a header parsed from YAML-formatted source file first comment.
# Contains information about authorship, naming, source files, etc.
#
def header
self.header = {} unless @header
@header
end
#
# A string containing the description of the source file.
#
def description
header["description"]
end
#
# Returns an array of dependencies tags. Unordered.
#
def dependencies
@dependencies
end
alias_method :requires, :dependencies
#
# Returns an array with names of dependencies. Unordered.
# Accepts options:
# * :short: — whether inner dependencies should not prepend package name
# e.g. 'Class' instead of 'Core/Class' when in package 'Core').
# Doesn't change anything for external dependencies
#
def dependencies_names(options = {})
dependencies.map {|d| d.name(options) }
end
alias_method :requires_names, :dependencies_names
#
# Returns an array of external dependencies tags. Unordered.
#
def external_dependencies
dependencies.select {|d| d.external? }
end
#
# Returns an array with names for external dependencies. Unordered.
#
def external_dependencies_names
external_dependencies.map {|d| d.name }
end
#
# Returns an array with provides tags.
#
def provides
@provides
end
#
# Returns an array with provides names.
# Accepts options:
# * :short: — whether provides should not prepend package name
# e.g. 'Class' instead of 'Core/Class' when in package 'Core')
def provides_names(options = {})
provides.map {|p| p.name(options)}
end
#
# Returns a tag for source file, which this one is an extension for.
#
# E.g.: file Foo.js in package Core provides ['Class', 'Hash']. File Bar.js in package Bar
# extends 'Core/Class'. That means its contents would be appended to the Foo.js when compiling
# the result.
#
def extends
@extends
end
#
# Returns whether the source file is an extension.
#
def extension?
extends && !extends.empty?
end
#
# Returns an array of included extensions for given source.
#
def extensions
@extensions ||= []
@extensions = @extensions.flatten.compact.uniq
@extensions
end
def extensions=(new_value) # :nodoc:
@extensions = new_value
end
#
# Looks up for extensions in the #pool and then includes
# extensions for all the provides tag this source file has.
#
def include_extensions!
if pool
provides.each do |p|
extensions << pool.lookup_extensions(p)
end
end
end
#
# Returns an array of files required by this files including all the filenames for extensions.
# SourceFile filename always goes first, all the extensions are unordered.
#
def required_files
[filename, extensions.map {|e| e.filename}].flatten
end
#
# Returns a hash containing basic info with dependencies/provides tags' names
# and description for source file.
#
def to_hash
{
"desc" => description,
"requires" => dependencies_names(:short => true),
"provides" => provides_names(:short => true)
}
end
def inspect # :nodoc:
self.to_hash.inspect
end
# Private API
def header=(new_header) # :nodoc:
@header = new_header
# prepare defaults
@header["description"] ||= ""
# handle tags
@dependencies = [@header["requires"] || []].flatten
@dependencies.map! {|tag_name| Tag.new(tag_name, :package => package) }
@provides = [@header["provides"] || []].flatten
@provides.map! {|tag_name| Tag.new(tag_name, :package => package) }
@extends = (@header["extends"] && !@header["extends"].empty?) ? Tag.new(@header["extends"]) : nil
end
def content=(new_value) # :nodoc:
@content = new_value
end
def content # :nodoc:
[@content, extensions.map {|e| e.content}].flatten.compact.join("\n")
end
# Assigns an instance of Jsus::Pool to the source file.
# Also performs push to that pool.
def pool=(new_value)
@pool = new_value
@pool << self if @pool
end
# A pool which the source file is assigned to. Used in #include_extensions!
def pool
@pool
end
end
end