diff --git a/lib/compass/app_integration/stand_alone.rb b/lib/compass/app_integration/stand_alone.rb new file mode 100644 index 00000000..e69de29b diff --git a/lib/compass/app_integration/stand_alone/configuration.rb b/lib/compass/app_integration/stand_alone/configuration.rb new file mode 100644 index 00000000..e69de29b diff --git a/lib/compass/commands/installer_command.rb b/lib/compass/commands/installer_command.rb index b59d451b..8d3c2441 100644 --- a/lib/compass/commands/installer_command.rb +++ b/lib/compass/commands/installer_command.rb @@ -6,10 +6,10 @@ module Compass include Compass::Installers def configure! + Compass.add_configuration(installer.default_configuration) read_project_configuration - Compass.configuration.set_maybe(options) - Compass.configuration.default_all(installer.configuration_defaults) - Compass.configuration.set_defaults! + Compass.add_configuration(options) + Compass.add_configuration(installer.completed_configuration) end def installer diff --git a/lib/compass/commands/project_base.rb b/lib/compass/commands/project_base.rb index dc082eeb..0065721a 100644 --- a/lib/compass/commands/project_base.rb +++ b/lib/compass/commands/project_base.rb @@ -23,8 +23,6 @@ module Compass def configure! read_project_configuration - Compass.configuration.set_maybe(options) - Compass.configuration.set_defaults! end def projectize(path) @@ -49,8 +47,8 @@ module Compass # Read the configuration file for this project def read_project_configuration - if file = detect_configuration_file - Compass.configuration.parse(file) if File.readable?(file) + if (file = detect_configuration_file) && File.readable?(file) + Compass.add_configuration(file) end end diff --git a/lib/compass/configuration.rb b/lib/compass/configuration.rb index eaa5b8a4..a495a1fa 100644 --- a/lib/compass/configuration.rb +++ b/lib/compass/configuration.rb @@ -1,324 +1,36 @@ -require 'singleton' - module Compass - class Configuration + module Configuration ATTRIBUTES = [ :project_type, :project_path, - :http_path, :css_dir, :sass_dir, :images_dir, :javascripts_dir, - :output_style, - :environment, - :relative_assets, + :css_path, + :sass_path, + :images_path, + :javascripts_path, + :http_path, + :http_images_dir, + :http_stylesheets_dir, + :http_javascripts_dir, :http_images_path, :http_stylesheets_path, :http_javascripts_path, + :output_style, + :environment, + :relative_assets, :additional_import_paths, - :sass_options + :sass_options, + :asset_host, + :asset_cache_buster ] - attr_accessor *ATTRIBUTES - - attr_accessor :required_libraries - - def initialize - self.required_libraries = [] - end - - # parses a manifest file which is a ruby script - # evaluated in a Manifest instance context - def parse(config_file) - open(config_file) do |f| - parse_string(f.read, config_file) - end - end - - def parse_string(contents, filename) - bind = binding - eval(contents, bind, filename) - ATTRIBUTES.each do |prop| - value = eval(prop.to_s, bind) rescue nil - self.send("#{prop}=", value) if value - end - if @added_import_paths - self.additional_import_paths ||= [] - self.additional_import_paths += @added_import_paths - end - issue_deprecation_warnings - end - - def set_all(options) - ATTRIBUTES.each do |a| - self.send("#{a}=", options[a]) if options.has_key?(a) - end - end - - def set_maybe(options) - ATTRIBUTES.each do |a| - self.send("#{a}=", options[a]) if options[a] - end - end - - def default_all(options) - ATTRIBUTES.each do |a| - set_default_unless_set(a, options[a]) - end - end - - def set_default_unless_set(attribute, value) - self.send("#{attribute}=", value) unless self.send(attribute) - end - - def set_defaults! - ATTRIBUTES.each do |a| - set_default_unless_set(a, default_for(a)) - end - end - - def default_for(attribute) - method = "default_#{attribute}".to_sym - self.send(method) if respond_to?(method) - end - - def default_sass_dir - "src" - end - - def default_css_dir - "stylesheets" - end - - def default_images_dir - "images" - end - - def default_http_path - "/" - end - - def comment_for_http_path - "# Set this to the root of your project when deployed:\n" - end - - def relative_assets? - # the http_images_path is deprecated, but here for backwards compatibility. - relative_assets || http_images_path == :relative - end - - def comment_for_relative_assets - unless relative_assets - %q{# To enable relative paths to assets via compass helper functions. Uncomment: -# relative_assets = true -} - else - "" - end - end - - def default_output_style - if environment == :development - :expanded - else - :compact - end - end - - def default_line_comments - environment == :development - end - - def sass_path - if project_path && sass_dir - File.join(project_path, sass_dir) - end - end - - def css_path - if project_path && css_dir - File.join(project_path, css_dir) - end - end - - def http_root_relative(path) - hp = http_path || default_http_path - hp = hp[0..-2] if hp[-1..-1] == "/" - "#{hp}/#{path}" - end - - def add_import_path(*paths) - # The @added_import_paths variable works around an issue where - # the additional_import_paths gets overwritten during parse - @added_import_paths ||= [] - @added_import_paths += paths - self.additional_import_paths ||= [] - self.additional_import_paths += paths - end - - # When called with a block, defines the asset host url to be used. - # The block must return a string that starts with a protocol (E.g. http). - # The block will be passed the root-relative url of the asset. - # When called without a block, returns the block that was previously set. - def asset_host(&block) - if block_given? - @asset_host = block - else - @asset_host - end - end - - # When called with a block, defines the cache buster strategy to be used. - # The block must return nil or a string that can be appended to a url as a query parameter. - # The returned string must not include the starting '?'. - # The block will be passed the root-relative url of the asset. - # If the block accepts two arguments, it will also be passed a File object - # that points to the asset on disk -- which may or may not exist. - # When called without a block, returns the block that was previously set. - def asset_cache_buster(&block) - if block_given? - @asset_cache_buster = block - else - @asset_cache_buster - end - end - - - def serialize - if asset_cache_buster - raise Compass::Error, "Cannot serialize a configuration with asset_cache_buster set." - end - if asset_host - raise Compass::Error, "Cannot serialize a configuration with asset_host set." - end - contents = "" - required_libraries.each do |lib| - contents << %Q{require '#{lib}'\n} - end - contents << "# Require any additional compass plugins here.\n" - contents << "\n" if required_libraries.any? - ATTRIBUTES.each do |prop| - value = send(prop) - if respond_to?("comment_for_#{prop}") - contents << send("comment_for_#{prop}") - end - if block_given? && (to_emit = yield(prop, value)) - contents << to_emit - else - contents << Configuration.serialize_property(prop, value) unless value.nil? - end - end - contents - end - - def self.serialize_property(prop, value) - %Q(#{prop} = #{value.inspect}\n) - end - - def to_compiler_arguments(additional_options) - [project_path, sass_path, css_path, to_sass_engine_options.merge(additional_options)] - end - - def to_sass_plugin_options - locations = {} - locations[sass_path] = css_path if sass_path && css_path - Compass::Frameworks::ALL.each do |framework| - locations[framework.stylesheets_directory] = css_path || css_dir || "." - end - resolve_additional_import_paths.each do |additional_path| - locations[additional_path] = File.join(css_path || css_dir || ".", File.basename(additional_path)) - end - plugin_opts = {:template_location => locations} - plugin_opts[:style] = output_style if output_style - plugin_opts[:line_comments] = default_line_comments if environment - plugin_opts.merge!(sass_options || {}) - plugin_opts - end - - def resolve_additional_import_paths - (additional_import_paths || []).map do |path| - if project_path && !absolute_path?(path) - File.join(project_path, path) - else - path - end - end - end - - def to_sass_engine_options - engine_opts = {:load_paths => sass_load_paths} - engine_opts[:style] = output_style if output_style - engine_opts[:line_comments] = default_line_comments if environment - engine_opts.merge!(sass_options || {}) - end - - def sass_load_paths - load_paths = [] - load_paths << sass_path if sass_path - Compass::Frameworks::ALL.each do |framework| - load_paths << framework.stylesheets_directory - end - load_paths += resolve_additional_import_paths - load_paths - end - - # Support for testing. - def reset! - ATTRIBUTES.each do |attr| - send("#{attr}=", nil) - end - @asset_cache_buster = nil - @asset_host = nil - @added_import_paths = nil - self.required_libraries = [] - end - - def issue_deprecation_warnings - if http_images_path == :relative - puts "DEPRECATION WARNING: Please set relative_assets = true to enable relative paths." - end - end - - def require(lib) - required_libraries << lib - super - end - - def absolute_path?(path) - # This is only going to work on unix, gonna need a better implementation. - path.index(File::SEPARATOR) == 0 - end end - - module ConfigHelpers - def configuration - @configuration ||= Configuration.new - if block_given? - yield @configuration - end - @configuration - end - - def sass_plugin_configuration - configuration.to_sass_plugin_options - end - - def configure_sass_plugin! - @sass_plugin_configured = true - Sass::Plugin.options.merge!(sass_plugin_configuration) - end - - def sass_plugin_configured? - @sass_plugin_configured - end - - def sass_engine_options - configuration.to_sass_engine_options - end - end - - extend ConfigHelpers - +end + +['adapters', 'comments', 'defaults', 'helpers', 'inheritance', 'serialization', 'data'].each do |lib| + require File.join(File.dirname(__FILE__), 'configuration', lib) end diff --git a/lib/compass/configuration/adapters.rb b/lib/compass/configuration/adapters.rb new file mode 100644 index 00000000..e2dbf7d1 --- /dev/null +++ b/lib/compass/configuration/adapters.rb @@ -0,0 +1,59 @@ +module Compass + module Configuration + # The adapters module provides methods that make configuration data from a compass project + # adapt to various consumers of configuration data + module Adapters + def to_compiler_arguments(additional_options) + [project_path, sass_path, css_path, to_sass_engine_options.merge(additional_options)] + end + + def to_sass_plugin_options + locations = {} + locations[sass_path] = css_path if sass_path && css_path + Compass::Frameworks::ALL.each do |framework| + locations[framework.stylesheets_directory] = css_path || css_dir || "." + end + resolve_additional_import_paths.each do |additional_path| + locations[additional_path] = File.join(css_path || css_dir || ".", File.basename(additional_path)) + end + plugin_opts = {:template_location => locations} + plugin_opts[:style] = output_style if output_style + plugin_opts[:line_comments] = line_comments if environment + plugin_opts.merge!(sass_options || {}) + plugin_opts + end + + def resolve_additional_import_paths + (additional_import_paths || []).map do |path| + if project_path && !absolute_path?(path) + File.join(project_path, path) + else + path + end + end + end + + def absolute_path?(path) + # This is only going to work on unix, gonna need a better implementation. + path.index(File::SEPARATOR) == 0 + end + + def to_sass_engine_options + engine_opts = {:load_paths => sass_load_paths} + engine_opts[:style] = output_style if output_style + engine_opts[:line_comments] = line_comments if environment + engine_opts.merge!(sass_options || {}) + end + + def sass_load_paths + load_paths = [] + load_paths << sass_path if sass_path + Compass::Frameworks::ALL.each do |framework| + load_paths << framework.stylesheets_directory + end + load_paths += resolve_additional_import_paths + load_paths + end + end + end +end diff --git a/lib/compass/configuration/comments.rb b/lib/compass/configuration/comments.rb new file mode 100644 index 00000000..b62f6a92 --- /dev/null +++ b/lib/compass/configuration/comments.rb @@ -0,0 +1,22 @@ +module Compass + module Configuration + # Comments are emitted into the configuration file when serialized and make it easier to understand for new users. + module Comments + + def comment_for_http_path + "# Set this to the root of your project when deployed:\n" + end + + def comment_for_relative_assets + unless relative_assets + %q{# To enable relative paths to assets via compass helper functions. Uncomment: +# relative_assets = true +} + else + "" + end + end + + end + end +end diff --git a/lib/compass/configuration/data.rb b/lib/compass/configuration/data.rb new file mode 100644 index 00000000..755ccfd1 --- /dev/null +++ b/lib/compass/configuration/data.rb @@ -0,0 +1,103 @@ +module Compass + module Configuration + # The Compass configuration data storage class manages configuration data that comes from a variety of + # different sources and aggregates them together into a consistent API + # Some of the possible sources of configuration data: + # * Compass default project structure for stand alone projects + # * App framework specific project structures for rails, etc. + # * User supplied explicit configuration + # * Configuration data provided via the command line interface + # + # There are two kinds of configuration data that doesn't come from the user: + # + # 1. Configuration data that is defaulted as if the user had provided it themselves. + # This is useful for providing defaults that the user is likely to want to edit + # but shouldn't have to provide explicitly when getting started + # 2. Configuration data that is defaulted behind the scenes because _some_ value is + # required. + class Data + + attr_accessor :required_libraries + + include Compass::Configuration::Inheritance + include Compass::Configuration::Serialization + include Compass::Configuration::Adapters + + inherited_accessor *ATTRIBUTES + + def initialize(attr_hash = nil) + self.required_libraries = [] + set_all(attr_hash) if attr_hash + self.top_level = self + end + + def set_all(attr_hash) + # assert_valid_keys!(attr_hash) + attr_hash.each do |a, v| + if self.respond_to?("#{a}=") + self.send("#{a}=", v) + end + end + end + + def add_import_path(*paths) + # The @added_import_paths variable works around an issue where + # the additional_import_paths gets overwritten during parse + @added_import_paths ||= [] + @added_import_paths += paths + self.additional_import_paths ||= [] + self.additional_import_paths += paths + end + + # When called with a block, defines the asset host url to be used. + # The block must return a string that starts with a protocol (E.g. http). + # The block will be passed the root-relative url of the asset. + # When called without a block, returns the block that was previously set. + def asset_host(&block) + if block_given? + @asset_host = block + else + @asset_host + end + end + + # When called with a block, defines the cache buster strategy to be used. + # The block must return nil or a string that can be appended to a url as a query parameter. + # The returned string must not include the starting '?'. + # The block will be passed the root-relative url of the asset. + # If the block accepts two arguments, it will also be passed a File object + # that points to the asset on disk -- which may or may not exist. + # When called without a block, returns the block that was previously set. + def asset_cache_buster(&block) + if block_given? + @asset_cache_buster = block + else + @asset_cache_buster + end + end + + # Require a compass plugin and capture that it occured so that the configuration serialization works next time. + def require(lib) + required_libraries << lib + super + end + + def relative_assets? + # the http_images_path is deprecated, but here for backwards compatibility. + relative_assets || http_images_path == :relative + end + + private + + def assert_valid_keys!(attr_hash) + illegal_attrs = attr_hash.keys - ATTRIBUTES + if illegal_attrs.size == 1 + raise Error, "#{illegal_attrs.first.inspect} is not a valid configuration attribute." + elsif illegal_attrs.size > 0 + raise Error, "Illegal configuration attributes: #{illegal_attrs.map{|a| a.inspect}.join(", ")}" + end + end + + end + end +end \ No newline at end of file diff --git a/lib/compass/configuration/defaults.rb b/lib/compass/configuration/defaults.rb new file mode 100644 index 00000000..b646a412 --- /dev/null +++ b/lib/compass/configuration/defaults.rb @@ -0,0 +1,92 @@ +module Compass + module Configuration + module Defaults + + def http_path_without_default + "/" + end + + def default_output_style + if top_level.environment == :development + :expanded + else + :compact + end + end + + def default_line_comments + top_level.environment == :development + end + + def default_sass_path + if (pp = top_level.project_path) && (dir = top_level.sass_dir) + File.join(pp, dir) + end + end + + def default_css_path + if (pp = top_level.project_path) && (dir = top_level.css_dir) + File.join(pp, dir) + end + end + + def default_images_path + if (pp = top_level.project_path) && (dir = top_level.images_dir) + File.join(pp, dir) + end + end + + def default_javascripts_path + if (pp = top_level.project_path) && (dir = top_level.javascripts_dir) + File.join(pp, dir) + end + end + + def default_http_images_dir + top_level.images_dir + end + + def default_http_images_path + http_root_relative top_level.http_images_dir + end + + def default_http_stylesheets_dir + top_level.css_dir + end + + def default_http_stylesheets_path + http_root_relative top_level.http_stylesheets_dir + end + + def default_http_javascripts_dir + top_level.javascripts_dir + end + + def default_http_javascripts_path + http_root_relative top_level.http_javascripts_dir + end + + # helper functions + + def http_join(*segments) + segments.map do |segment| + segment = http_pathify(segment) + segment[-1..-1] == "/" ? segment[0..-2] : segment + end.join("/") + end + + def http_pathify(path) + if File::SEPARATOR == "/" + path + else + path.gsub(File::SEPARATOR, "/") + end + end + + def http_root_relative(path) + http_join top_level.http_path, path + end + + end + end +end \ No newline at end of file diff --git a/lib/compass/configuration/helpers.rb b/lib/compass/configuration/helpers.rb new file mode 100644 index 00000000..ea8ad504 --- /dev/null +++ b/lib/compass/configuration/helpers.rb @@ -0,0 +1,61 @@ +module Compass + module Configuration + # The helpers are available as methods on the Compass module. E.g. Compass.configuration + module Helpers + def configuration + @configuration ||= default_configuration + if block_given? + yield @configuration + end + @configuration + end + + def default_configuration + Data.new.extend(Defaults).extend(Comments) + end + + def add_configuration(config, filename = nil) + return if config.nil? + data = if config.is_a?(Compass::Configuration::Data) + config + elsif config.respond_to?(:read) + Compass::Configuration::Data.new_from_string(config.read, filename) + elsif config.is_a?(Hash) + Compass::Configuration::Data.new(config) + elsif config.is_a?(String) + Compass::Configuration::Data.new_from_file(config) + else + raise "I don't know what to do with: #{config.inspect}" + end + data.inherit_from!(configuration) + data.on_top! + @configuration = data + end + + # Support for testing. + def reset_configuration! + @configuration = nil + end + + def sass_plugin_configuration + configuration.to_sass_plugin_options + end + + def configure_sass_plugin! + @sass_plugin_configured = true + Sass::Plugin.options.merge!(sass_plugin_configuration) + end + + def sass_plugin_configured? + @sass_plugin_configured + end + + def sass_engine_options + configuration.to_sass_engine_options + end + end + end + + extend Configuration::Helpers + +end diff --git a/lib/compass/configuration/inheritance.rb b/lib/compass/configuration/inheritance.rb new file mode 100644 index 00000000..fd030172 --- /dev/null +++ b/lib/compass/configuration/inheritance.rb @@ -0,0 +1,170 @@ +module Compass + module Configuration + # The inheritance module makes it easy for configuration data to inherit from + # other instances of configuration data. This makes it easier for external code to layer + # bits of configuration from various sources. + module Inheritance + + def self.included(base) + # inherited_data stores configuration data that this configuration object will + # inherit if not provided explicitly. + base.send :attr_accessor, :inherited_data, :set_attributes, :top_level + + base.send(:include, InstanceMethods) + base.extend(ClassMethods) + end + + module ClassMethods + def inherited_writer(*attributes) + attributes.each do |attribute| + line = __LINE__ + 1 + class_eval %Q{ + def #{attribute}=(value) # def css_dir=(value) + @set_attributes ||= {} # @set_attributes ||= {} + @set_attributes[#{attribute.inspect}] = true # @set_attributes[:css_dir] = true + @#{attribute} = value # @css_dir = value + end # end + + def unset_#{attribute}! # def unset_css_dir! + unset!(#{attribute.inspect}) # unset!(:css_dir) + end # end + + def #{attribute}_set? # def css_dir_set? + set?(#{attribute.inspect}) # set?(:css_dir) + end # end + }, __FILE__, line + end + end + + # Defines the default reader to be an inherited_reader that will look at the inherited_data for its + # value when not set. The inherited reader calls to a raw reader that acts like a normal attribute + # reader but prefixes the attribute name with "raw_". + def inherited_reader(*attributes) + attributes.each do |attribute| + line = __LINE__ + 1 + class_eval %Q{ + def raw_#{attribute} # def raw_css_dir + @#{attribute} # @css_dir + end # end + def #{attribute}_without_default # def css_dir_without_default + read_without_default(#{attribute.inspect}) # read_without_default(:css_dir) + end # end + def #{attribute} # def css_dir + read(#{attribute.inspect}) # read(:css_dir) + end # end + }, __FILE__, line + end + end + + def inherited_accessor(*attributes) + inherited_reader(*attributes) + inherited_writer(*attributes) + end + + + end + + module InstanceMethods + + def on_top! + self.set_top_level(self) + end + + def set_top_level(new_top) + self.top_level = new_top + if self.inherited_data.respond_to?(:set_top_level) + self.inherited_data.set_top_level(new_top) + end + end + + + def inherit_from!(data) + if self.inherited_data + self.inherited_data.inherit_from!(data) + else + self.inherited_data = data + end + self + end + + def unset!(attribute) + @set_attributes ||= {} + send("#{attribute}=", nil) + @set_attributes.delete(attribute) + nil + end + + def set?(attribute) + @set_attributes ||= {} + @set_attributes[attribute] + end + + def default_for(attribute) + method = "default_#{attribute}".to_sym + if respond_to?(method) + send(method) + end + end + + # Read an explicitly set value that is either inherited or set on this instance + def read_without_default(attribute) + if set?(attribute) + send("raw_#{attribute}") + elsif inherited_data.respond_to?("#{attribute}_without_default") + inherited_data.send("#{attribute}_without_default") + elsif inherited_data.respond_to?(attribute) + inherited_data.send(attribute) + end + end + + # Read a value that is either inherited or set on this instance, if we get to the bottom-most configuration instance, + # we ask for the default starting at the top level. + def read(attribute) + if !(v = send("#{attribute}_without_default")).nil? + v + else + top_level.default_for(attribute) + end + end + + def method_missing(meth) + if inherited_data + inherited_data.send(meth) + else + raise NoMethodError, meth.to_s + end + end + + def respond_to?(meth) + if super + true + elsif inherited_data + inherited_data.respond_to?(meth) + else + false + end + end + + def debug + instances = [self] + instances << instances.last.inherited_data while instances.last.inherited_data + normalized_attrs = {} + ATTRIBUTES.each do |prop| + values = [] + instances.each do |instance| + values << { + :raw => (instance.send("raw_#{prop}") rescue nil), + :value => (instance.send("#{prop}_without_default") rescue nil), + :default => (instance.send("default_#{prop}") rescue nil), + :resoved => instance.send(prop) + } + end + normalized_attrs[prop] = values + end + normalized_attrs + end + + end + end + end +end diff --git a/lib/compass/configuration/serialization.rb b/lib/compass/configuration/serialization.rb new file mode 100644 index 00000000..4a0e1c0d --- /dev/null +++ b/lib/compass/configuration/serialization.rb @@ -0,0 +1,82 @@ +module Compass + module Configuration + # The serialization module manages reading and writing the configuration file(s). + module Serialization + def self.included(base) + base.send(:include, InstanceMethods) + base.extend ClassMethods + end + + module ClassMethods + def new_from_file(config_file) + data = Data.new + data.parse(config_file) + data + end + def new_from_string(contents, filename) + data = Data.new + data.parse_string(contents, filename) + data + end + end + + module InstanceMethods + # parses a configuration file which is a ruby script + def parse(config_file) + open(config_file) do |f| + parse_string(f.read, config_file) + end + end + + def parse_string(contents, filename) + bind = binding + eval(contents, bind, filename) + ATTRIBUTES.each do |prop| + value = eval(prop.to_s, bind) rescue nil + self.send("#{prop}=", value) if value + end + if @added_import_paths + self.additional_import_paths ||= [] + self.additional_import_paths += @added_import_paths + end + issue_deprecation_warnings + end + + def serialize + contents = "" + required_libraries.each do |lib| + contents << %Q{require '#{lib}'\n} + end + contents << "# Require any additional compass plugins here.\n" + contents << "\n" if required_libraries.any? + ATTRIBUTES.each do |prop| + value = send("#{prop}_without_default") + if value.is_a?(Proc) + $stderr.puts "WARNING: #{prop} is code and cannot be written to a file. You'll need to copy it yourself." + end + if respond_to?("comment_for_#{prop}") + contents << send("comment_for_#{prop}") + end + if block_given? && (to_emit = yield(prop, value)) + contents << to_emit + else + contents << serialize_property(prop, value) unless value.nil? + end + end + contents + end + + def serialize_property(prop, value) + %Q(#{prop} = #{value.inspect}\n) + end + + def issue_deprecation_warnings + if http_images_path == :relative + $stderr.puts "DEPRECATION WARNING: Please set relative_assets = true to enable relative paths." + end + end + + end + end + end +end diff --git a/lib/compass/installers/base.rb b/lib/compass/installers/base.rb index 6d9b6295..67120b40 100644 --- a/lib/compass/installers/base.rb +++ b/lib/compass/installers/base.rb @@ -26,12 +26,16 @@ module Compass define_method dir do Compass.configuration.send(dir) end + define_method "#{dir}_without_default" do + Compass.configuration.send("#{dir}_without_default") + end end # Initializes the project to work with compass def init dirs = manifest.map do |entry| - File.dirname(send("install_location_for_#{entry.type}", entry.to, entry.options)) + loc = send("install_location_for_#{entry.type}", entry.to, entry.options) + File.dirname(loc) end if manifest.has_stylesheet? diff --git a/lib/compass/installers/rails.rb b/lib/compass/installers/rails.rb index f2b7df40..cce33d9e 100644 --- a/lib/compass/installers/rails.rb +++ b/lib/compass/installers/rails.rb @@ -3,16 +3,38 @@ module Compass class RailsInstaller < Base - def configuration_defaults - { - :sass_dir => (sass_dir || prompt_sass_dir), - :css_dir => (css_dir || prompt_css_dir), - :images_dir => default_images_dir, - :javascripts_dir => default_javascripts_dir, - :http_stylesheets_path => default_http_stylesheets_path, - :http_javascripts_path => default_http_javascripts_path, - :http_images_path => default_http_images_path - } + module ConfigurationDefaults + def default_images_dir + File.join("public", "images") + end + + def default_javascripts_dir + File.join("public", "javascripts") + end + + def default_http_images_path + "/images" + end + + def default_http_javascripts_path + "/javascripts" + end + + def default_http_stylesheets_path + "/stylesheets" + end + + end + + def default_configuration + Compass::Configuration::Data.new.extend(ConfigurationDefaults) + end + + def completed_configuration + config = {} + config[:sass_dir] = prompt_sass_dir unless sass_dir_without_default + config[:css_dir] = prompt_css_dir unless css_dir_without_default + config unless config.empty? end def write_configuration_files(config_file = nil) @@ -45,25 +67,6 @@ NEXTSTEPS puts "\n(You are using haml, aren't you?)" end - def default_images_dir - separate("public/images") - end - - def default_javascripts_dir - separate("public/javascripts") - end - - def default_http_images_path - "/images" - end - - def default_http_javascripts_path - "/javascripts" - end - - def default_http_stylesheets_path - "/stylesheets" - end def install_location_for_html(to, options) separate("public/#{pattern_name_as_dir}#{to}") diff --git a/lib/compass/installers/stand_alone.rb b/lib/compass/installers/stand_alone.rb index 5ae61d5d..053670b3 100644 --- a/lib/compass/installers/stand_alone.rb +++ b/lib/compass/installers/stand_alone.rb @@ -3,6 +3,24 @@ module Compass class StandAloneInstaller < Base + module ConfigurationDefaults + def sass_dir_without_default + "src" + end + + def javascripts_dir_without_default + "javascripts" + end + + def css_dir_without_default + "stylesheets" + end + + def images_dir_without_default + "images" + end + end + def init directory targetize("") super @@ -28,9 +46,12 @@ module Compass write_configuration_files unless config_files_exist? end - # We want to rely on the defaults provided by Configuration - def configuration_defaults - {} + def default_configuration + Compass::Configuration::Data.new.extend(ConfigurationDefaults) + end + + def completed_configuration + nil end def finalize(options = {}) diff --git a/test/command_line_helper.rb b/test/command_line_helper.rb index 029994c3..d74b191a 100644 --- a/test/command_line_helper.rb +++ b/test/command_line_helper.rb @@ -88,14 +88,6 @@ module Compass::CommandLineHelper FileUtils.rm_rf(d) end - def capture_output - real_stdout, $stdout = $stdout, StringIO.new - yield - $stdout.string - ensure - $stdout = real_stdout - end - def execute(*arguments) Compass::Exec::Compass.new(arguments).run! end diff --git a/test/command_line_test.rb b/test/command_line_test.rb index 8e621deb..d02ecae7 100644 --- a/test/command_line_test.rb +++ b/test/command_line_test.rb @@ -7,9 +7,10 @@ require 'timeout' class CommandLineTest < Test::Unit::TestCase include Compass::TestCaseHelper include Compass::CommandLineHelper + include Compass::IoHelper def teardown - Compass.configuration.reset! + Compass.reset_configuration! end def test_print_version diff --git a/test/compass_test.rb b/test/compass_test.rb index 89e3f988..e22eec31 100644 --- a/test/compass_test.rb +++ b/test/compass_test.rb @@ -5,7 +5,7 @@ require 'compass' class CompassTest < Test::Unit::TestCase include Compass::TestCaseHelper def setup - Compass.configuration.reset! + Compass.reset_configuration! end def teardown @@ -90,7 +90,7 @@ private def within_project(project_name) @current_project = project_name - Compass.configuration.parse(configuration_file(project_name)) if File.exists?(configuration_file(project_name)) + Compass.add_configuration(configuration_file(project_name)) if File.exists?(configuration_file(project_name)) Compass.configuration.project_path = project_path(project_name) args = Compass.configuration.to_compiler_arguments(:logger => Compass::NullLogger.new) if Compass.configuration.sass_path && File.exists?(Compass.configuration.sass_path) diff --git a/test/configuration_test.rb b/test/configuration_test.rb index 0191d9a6..5974274c 100644 --- a/test/configuration_test.rb +++ b/test/configuration_test.rb @@ -1,71 +1,75 @@ require File.dirname(__FILE__)+'/test_helper' require 'compass' +require 'stringio' class ConfigurationTest < Test::Unit::TestCase + include Compass::IoHelper def setup - Compass.configuration.reset! + Compass.reset_configuration! end def test_parse_and_serialize - contents = <<-CONFIG + contents = StringIO.new(<<-CONFIG) require 'compass' # Require any additional compass plugins here. project_type = :stand_alone - # Set this to the root of your project when deployed: - http_path = "/" css_dir = "css" sass_dir = "sass" images_dir = "img" javascripts_dir = "js" + # Set this to the root of your project when deployed: + http_path = "/" output_style = :nested # To enable relative paths to assets via compass helper functions. Uncomment: # relative_assets = true CONFIG - Compass.configuration.parse_string(contents, "test_parse") + Compass.add_configuration(contents, "test_parse") assert_equal 'sass', Compass.configuration.sass_dir assert_equal 'css', Compass.configuration.css_dir assert_equal 'img', Compass.configuration.images_dir assert_equal 'js', Compass.configuration.javascripts_dir - expected_lines = contents.split("\n").map{|l|l.strip} + expected_lines = contents.string.split("\n").map{|l|l.strip} actual_lines = Compass.configuration.serialize.split("\n").map{|l|l.strip} assert_equal expected_lines, actual_lines end - def test_serialization_fails_with_asset_host_set - contents = <<-CONFIG + def test_serialization_warns_with_asset_host_set + contents = StringIO.new(<<-CONFIG) asset_host do |path| "http://example.com" end CONFIG - Compass.configuration.parse_string(contents, "test_serialization_fails_with_asset_host_set") + Compass.add_configuration(contents, "test_serialization_warns_with_asset_host_set") - assert_raise Compass::Error do + warning = capture_warning do Compass.configuration.serialize end + assert_equal "WARNING: asset_host is code and cannot be written to a file. You'll need to copy it yourself.\n", warning end - def test_serialization_fails_with_asset_cache_buster_set - contents = <<-CONFIG + def test_serialization_warns_with_asset_cache_buster_set + contents = StringIO.new(<<-CONFIG) asset_cache_buster do |path| "http://example.com" end CONFIG - Compass.configuration.parse_string(contents, "test_serialization_fails_with_asset_cache_buster_set") + Compass.add_configuration(contents, "test_serialization_warns_with_asset_cache_buster_set") - assert_raise Compass::Error do + warning = capture_warning do Compass.configuration.serialize end + assert_equal "WARNING: asset_cache_buster is code and cannot be written to a file. You'll need to copy it yourself.\n", warning end def test_additional_import_paths - contents = <<-CONFIG + contents = StringIO.new(<<-CONFIG) http_path = "/" project_path = "/home/chris/my_compass_project" css_dir = "css" @@ -73,7 +77,7 @@ class ConfigurationTest < Test::Unit::TestCase add_import_path "/path/to/my/framework" CONFIG - Compass.configuration.parse_string(contents, "test_additional_import_paths") + Compass.add_configuration(contents, "test_additional_import_paths") assert Compass.configuration.to_sass_engine_options[:load_paths].include?("/home/chris/my_compass_project/../foo") assert Compass.configuration.to_sass_engine_options[:load_paths].include?("/path/to/my/framework"), Compass.configuration.to_sass_engine_options[:load_paths].inspect @@ -83,30 +87,38 @@ class ConfigurationTest < Test::Unit::TestCase expected_serialization = < 'bar'} CONFIG - Compass.configuration.parse_string(contents, "test_sass_options") + Compass.add_configuration(contents, "test_sass_options") assert_equal 'bar', Compass.configuration.to_sass_engine_options[:foo] assert_equal 'bar', Compass.configuration.to_sass_plugin_options[:foo] expected_serialization = <"bar"} diff --git a/test/io_helper.rb b/test/io_helper.rb new file mode 100644 index 00000000..499dec93 --- /dev/null +++ b/test/io_helper.rb @@ -0,0 +1,19 @@ +module Compass + module IoHelper + def capture_output + real_stdout, $stdout = $stdout, StringIO.new + yield + $stdout.string + ensure + $stdout = real_stdout + end + + def capture_warning + real_stderr, $stderr = $stderr, StringIO.new + yield + $stderr.string + ensure + $stderr = real_stderr + end + end +end \ No newline at end of file diff --git a/test/rails_integration_test.rb b/test/rails_integration_test.rb index df144c7a..9f689a1b 100644 --- a/test/rails_integration_test.rb +++ b/test/rails_integration_test.rb @@ -7,9 +7,10 @@ require 'timeout' class RailsIntegrationTest < Test::Unit::TestCase include Compass::TestCaseHelper include Compass::CommandLineHelper + include Compass::IoHelper def setup - Compass.configuration.reset! + Compass.reset_configuration! end def test_rails_install diff --git a/test/test_helper.rb b/test/test_helper.rb index 30ee1245..d731494b 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -18,4 +18,5 @@ require 'compass' require 'test/unit' require File.join(File.dirname(__FILE__), 'test_case_helper') +require File.join(File.dirname(__FILE__), 'io_helper') require File.join(File.dirname(__FILE__), 'command_line_helper')