New project creation implemented using manifests and installation strategies.

This commit is contained in:
Chris Eppstein 2009-02-01 14:23:16 -08:00
parent 0ac537a81b
commit 91e081cd50
8 changed files with 316 additions and 19 deletions

View File

@ -0,0 +1,5 @@
stylesheet 'screen.sass'
stylesheet 'print.sass'
stylesheet 'ie.sass'
image 'grid.png'

View File

@ -0,0 +1,3 @@
stylesheet 'screen.sass'
stylesheet 'print.sass'
stylesheet 'ie.sass'

View File

@ -0,0 +1 @@
stylesheet 'screen.sass'

View File

@ -1,38 +1,30 @@
require 'fileutils'
require File.join(File.dirname(__FILE__), 'base')
require File.join(File.dirname(__FILE__), 'update_project')
require File.join(Compass.lib_directory, 'compass', 'installers')
module Compass
module Commands
class CreateProject < ProjectBase
include Compass::Installers
def initialize(working_directory, options)
super(working_directory, options)
end
# all commands must implement perform
def perform
begin
directory nil, options
rescue Compass::Exec::DirectoryExistsError
msg = "Project directory already exists. Run with -u to update or --force to force creation."
raise ::Compass::Exec::DirectoryExistsError.new(msg)
end
src_dir = options[:src_dir] || "src"
css_dir = options[:css_dir] || "stylesheets"
directory src_dir, options.merge(:force => true)
directory css_dir, options.merge(:force => true)
framework_templates.each do |t|
template "project/#{t}", "#{src_dir}/#{t}", options
end
installer.run
UpdateProject.new(working_directory, options).perform
end
def framework_templates
framework_project_dir = File.join(templates_directory, "project")
Dir.chdir(framework_project_dir) do
Dir.glob("*")
end
def installer
StandAloneInstaller.new(project_template_directory, project_directory, options)
end
def project_template_directory
File.join(framework.templates_directory, "project")
end
def skip_project_directory_assertion?

16
lib/compass/installers.rb Normal file
View File

@ -0,0 +1,16 @@
module Compass
module Installers
class InstallationError < Compass::Error
end
class DirectoryExistsError < InstallationError
end
end
end
require File.join(File.dirname(__FILE__), 'installers', 'manifest')
require File.join(File.dirname(__FILE__), 'installers', 'base')
require File.join(File.dirname(__FILE__), 'installers', 'stand_alone')

View File

@ -0,0 +1,179 @@
module Compass
module Installers
class Base
attr_accessor :template_path, :target_path, :working_path
attr_accessor :options
attr_accessor :manifest
attr_accessor :logger
attr_accessor :css_dir, :sass_dir, :images_dir, :javascripts_dir
def initialize(template_path, target_path, options = {})
@template_path = template_path
@target_path = target_path
@working_path = Dir.getwd
@options = options
@manifest = Manifest.new(manifest_file)
configure_option_with_default :logger
end
def manifest_file
@manifest_file ||= File.join(template_path, "manifest.rb")
end
# Runs the installer.
# Every installer must conform to the installation strategy of configure, prepare, install, and then finalize.
# A default implementation is provided for each step.
def run
configure
prepare
install
finalize
end
# The default configure method -- it sets up directories from the options
# and corresponding default_* methods for those not found in the options hash.
# It can be overridden it or augmented for reading config files,
# prompting the user for more information, etc.
def configure
[:css_dir, :sass_dir, :images_dir, :javascripts_dir].each do |opt|
configure_option_with_default opt
end
end
# The default prepare method -- it is a no-op.
# Generally you would create required directories, etc.
def prepare
end
def configure_option_with_default(opt)
value = options[opt]
value ||= begin
default_method = "default_#{opt}".to_sym
send(default_method) if respond_to?(default_method)
end
send("#{opt}=", value)
end
# The default install method. Calls install_<type> methods in the order specified by the manifest.
def install
manifest.each do |entry|
send("install_#{entry.type}", entry.from, entry.options)
end
end
# The default finalize method -- it is a no-op.
# This could print out a message or something.
def finalize
end
def install_stylesheet(from, options)
to = options[:to] || from
copy from, "#{sass_dir}/#{to}"
end
def install_image(from, options)
to = options[:to] || from
copy from, "#{images_dir}/#{to}"
end
def install_script(from, options)
to = options[:to] || from
copy from, "#{javascripts_dir}/#{to}"
end
def install_file(from, options)
to = options[:to] || from
copy from, to
end
def default_logger
Compass::Logger.new
end
# returns an absolute path given a path relative to the current installation target.
# Paths can use unix style "/" and will be corrected for the current platform.
def targetize(path)
File.join(target_path, separate(path))
end
# returns an absolute path given a path relative to the current template.
# Paths can use unix style "/" and will be corrected for the current platform.
def templatize(path)
File.join(template_path, separate(path))
end
# Write paths like we're on unix and then fix it
def separate(path)
path.gsub(%r{/}, File::SEPARATOR)
end
# copy/process a template in the compass template directory to the project directory.
def copy(from, to, options = nil)
options ||= self.options
from = templatize(from)
to = targetize(to)
if File.exists?(to) && !options[:force]
#TODO: Detect differences & provide an overwrite prompt
msg = "#{basename(to)} already exists."
raise InstallationError.new(msg)
elsif File.exists?(to)
logger.record :overwrite, basename(to)
FileUtils.rm to unless options[:dry_run]
FileUtils.cp from, to unless options[:dry_run]
else
logger.record :create, basename(to)
FileUtils.cp from, to unless options[:dry_run]
end
end
# create a directory and all the directories necessary to reach it.
def directory(dir, options = nil)
options ||= self.options
dir = targetize(dir)
if File.exists?(dir) && File.directory?(dir)
logger.record :exists, basename(dir)
elsif File.exists?(dir)
msg = "#{basename(dir)} already exists and is not a directory."
raise InstallationError.new(msg)
else
logger.record :directory, basename(dir)
FileUtils.mkdir_p(dir) unless options[:dry_run]
end
end
def write_file(file_name, contents, options = nil)
options ||= self.options
file_name = targetize(file_name)
if File.exists?(file_name) && !options[:force]
msg = "File #{basename(file_name)} already exists. Run with --force to force creation."
raise InstallationError.new(msg)
end
if File.exists?(file_name)
logger.record :overwrite, basename(file_name)
else
logger.record :create, basename(file_name)
end
open(file_name,'w') do |file|
file.write(contents)
end
end
def basename(file)
relativize(file) {|f| File.basename(file)}
end
def relativize(path)
if path.index(working_path+File::SEPARATOR) == 0
path[(working_path+File::SEPARATOR).length..-1]
elsif block_given?
yield path
else
path
end
end
end
end
end

View File

@ -0,0 +1,50 @@
module Compass
module Installers
class Manifest
# A Manifest entry
Entry = Struct.new(:type, :from, :options)
def initialize(manifest_file = nil)
@entries = []
parse(manifest_file) if manifest_file
end
def self.type(t)
eval <<-END
def #{t}(from, options = {})
@entries << Entry.new(:#{t}, from, options)
end
def has_#{t}?
@entries.detect {|e| e.type == :#{t}}
end
END
end
type :stylesheet
type :image
type :javascript
type :file
# Enumerates over the manifest files
def each
@entries.each {|e| yield e}
end
protected
# parses a manifest file which is a ruby script
# evaluated in a Manifest instance context
def parse(manifest_file)
open(manifest_file) do |f|
eval(f.read, instance_binding, manifest_file)
end
end
def instance_binding
binding
end
end
end
end

View File

@ -0,0 +1,51 @@
module Compass
module Installers
class StandAloneInstaller < Base
def configure
@config = Compass::Configuration.new
if File.exists?(config_file)
@config.parse(config_file)
elsif File.exists?(old_config_file)
@config.parse(old_config_file)
end
super
end
def prepare
directory ""
directory css_dir
directory sass_dir
directory images_dir if manifest.has_image?
directory javascripts_dir if manifest.has_javascript?
end
def default_css_dir
@config.css_dir || "stylesheets"
end
def default_sass_dir
@config.sass_dir ||"src"
end
def default_images_dir
@config.images_dir || "images"
end
def default_javascripts_dir
@config.javascripts_dir || "javascripts"
end
# Read the configuration file for this project
def config_file
@config_file ||= targetize('config.rb')
end
def old_config_file
@old_config_file ||= targetize('src/config.rb')
end
end
end
end