Refactor of the internal datastructures used to access project
configuration. Configuration is now a singly linked list of configuration objects that inherit values and defaults from the next configuration instance. All instances hold a reference to the top of the configuration chain. There is now a consistent API for reading configuration property values: <attr>: Reads the fully-resolved attribute after taking configuration inheritance and defaults into account. raw_<attr>: reads attribute from a configuration object without inheritance or defaults. default_for(<attr>): reads the default value for an attribute default_for_<attr>: specifies the default value for an attribute. <attr>_without_default: reads the inherited attribute without applying defaults. comment_for_<attr>: Specifies a comment that will be emitted above the property when serializing the configuration to a file. Additionally, method_missing and respond_to both work down the configuration chain, so any method that is added to a configuration instance, can be accessed from the top level. The distinction between default and explicitly set values allows compass to more correctly manage the serialization of attributes when creating configuration files for projects. The compass configuration can still be accessed via Compass.configuration, however, the configuration object is no longer a singleton. This means that you can build several configuration chains to track several projects at once. This should ease the use of compass in other frameworks and plugins that want to use compass internally.
This commit is contained in:
parent
2cfc9ef1f9
commit
f59ca512ce
0
lib/compass/app_integration/stand_alone.rb
Normal file
0
lib/compass/app_integration/stand_alone.rb
Normal file
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
59
lib/compass/configuration/adapters.rb
Normal file
59
lib/compass/configuration/adapters.rb
Normal file
@ -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
|
22
lib/compass/configuration/comments.rb
Normal file
22
lib/compass/configuration/comments.rb
Normal file
@ -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
|
103
lib/compass/configuration/data.rb
Normal file
103
lib/compass/configuration/data.rb
Normal file
@ -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
|
92
lib/compass/configuration/defaults.rb
Normal file
92
lib/compass/configuration/defaults.rb
Normal file
@ -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
|
61
lib/compass/configuration/helpers.rb
Normal file
61
lib/compass/configuration/helpers.rb
Normal file
@ -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
|
170
lib/compass/configuration/inheritance.rb
Normal file
170
lib/compass/configuration/inheritance.rb
Normal file
@ -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
|
82
lib/compass/configuration/serialization.rb
Normal file
82
lib/compass/configuration/serialization.rb
Normal file
@ -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
|
@ -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?
|
||||
|
@ -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}")
|
||||
|
@ -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 = {})
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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 = <<EXPECTED
|
||||
# Require any additional compass plugins here.
|
||||
project_path = "/home/chris/my_compass_project"
|
||||
css_dir = "css"
|
||||
sass_dir = "src"
|
||||
images_dir = "images"
|
||||
javascripts_dir = "javascripts"
|
||||
# Set this to the root of your project when deployed:
|
||||
http_path = "/"
|
||||
css_dir = "css"
|
||||
# To enable relative paths to assets via compass helper functions. Uncomment:
|
||||
# relative_assets = true
|
||||
additional_import_paths = ["../foo", "/path/to/my/framework"]
|
||||
EXPECTED
|
||||
assert_equal "/", Compass.configuration.http_path
|
||||
assert_equal expected_serialization, Compass.configuration.serialize
|
||||
assert_equal expected_serialization.split("\n"), Compass.configuration.serialize.split("\n")
|
||||
end
|
||||
|
||||
def test_sass_options
|
||||
contents = <<-CONFIG
|
||||
contents = StringIO.new(<<-CONFIG)
|
||||
sass_options = {:foo => '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 = <<EXPECTED
|
||||
# Require any additional compass plugins here.
|
||||
css_dir = "stylesheets"
|
||||
sass_dir = "src"
|
||||
images_dir = "images"
|
||||
javascripts_dir = "javascripts"
|
||||
# Set this to the root of your project when deployed:
|
||||
http_path = "/"
|
||||
# To enable relative paths to assets via compass helper functions. Uncomment:
|
||||
# relative_assets = true
|
||||
sass_options = {:foo=>"bar"}
|
||||
|
19
test/io_helper.rb
Normal file
19
test/io_helper.rb
Normal file
@ -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
|
@ -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
|
||||
|
@ -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')
|
||||
|
Loading…
Reference in New Issue
Block a user