Allow some config attributes that are arrays to be inherited properly
from inherited config data.
This commit is contained in:
parent
d642ae7b74
commit
a6c045785d
93
lib/compass/commands/project_structure.rb
Normal file
93
lib/compass/commands/project_structure.rb
Normal file
@ -0,0 +1,93 @@
|
||||
require 'compass/commands/project_base'
|
||||
require 'compass/commands/update_project'
|
||||
|
||||
module Compass
|
||||
module Commands
|
||||
module StructureOptionsParser
|
||||
def set_options(opts)
|
||||
opts.banner = %Q{
|
||||
Usage: compass structure [path/to/project] [options]
|
||||
|
||||
Description:
|
||||
Display the import structure of your stylesheets.
|
||||
|
||||
Options:
|
||||
}.strip.split("\n").map{|l| l.gsub(/^ {0,10}/,'')}.join("\n")
|
||||
|
||||
super
|
||||
end
|
||||
end
|
||||
class ProjectStats < UpdateProject
|
||||
|
||||
register :structure
|
||||
|
||||
def initialize(working_path, options)
|
||||
super
|
||||
assert_project_directory_exists!
|
||||
end
|
||||
|
||||
def perform
|
||||
@compiler = new_compiler_instance
|
||||
(options[:sass_files] || sorted_sass_files).each do |sass_file|
|
||||
print_tree(Compass.projectize(sass_file))
|
||||
end
|
||||
end
|
||||
|
||||
def print_tree(file, depth = 0, importer = @compiler.importer)
|
||||
puts ((depth > 0 ? "| " : " ") * depth) + "+- " + Compass.deprojectize(file)
|
||||
@compiler.staleness_checker.send(:compute_dependencies, file, importer).each do |(dep, dep_importer)|
|
||||
print_tree(dep, depth + 1, dep_importer)# unless Compass.deprojectize(dep)[0...1] == "/"
|
||||
end
|
||||
end
|
||||
|
||||
def sorted_sass_files
|
||||
sass_files = @compiler.sass_files
|
||||
sass_files.map! do |s|
|
||||
filename = Compass.deprojectize(s, File.join(Compass.configuration.project_path, Compass.configuration.sass_dir))
|
||||
[s, File.dirname(filename), File.basename(filename)]
|
||||
end
|
||||
sass_files = sass_files.sort_by do |s,d,f|
|
||||
File.join(d, f[0] == ?_ ? f[1..-1] : f)
|
||||
end
|
||||
sass_files.map!{|s,d,f| s}
|
||||
end
|
||||
|
||||
class << self
|
||||
|
||||
def option_parser(arguments)
|
||||
parser = Compass::Exec::CommandOptionParser.new(arguments)
|
||||
parser.extend(Compass::Exec::GlobalOptionsParser)
|
||||
parser.extend(Compass::Exec::ProjectOptionsParser)
|
||||
parser.extend(StructureOptionsParser)
|
||||
end
|
||||
|
||||
def usage
|
||||
option_parser([]).to_s
|
||||
end
|
||||
|
||||
def description(command)
|
||||
"Report statistics about your stylesheets"
|
||||
end
|
||||
|
||||
def primary; false; end
|
||||
|
||||
def parse!(arguments)
|
||||
parser = option_parser(arguments)
|
||||
parser.parse!
|
||||
parse_arguments!(parser, arguments)
|
||||
parser.options
|
||||
end
|
||||
|
||||
def parse_arguments!(parser, arguments)
|
||||
if arguments.size > 0
|
||||
parser.options[:project_name] = arguments.shift if File.directory?(arguments.first)
|
||||
parser.options[:sass_files] = arguments
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -25,7 +25,6 @@ module Compass
|
||||
attributes_for_directory(:fonts),
|
||||
attributes_for_directory(:extensions, nil),
|
||||
# Compilation options
|
||||
:sprite_load_path,
|
||||
:output_style,
|
||||
:environment,
|
||||
:relative_assets,
|
||||
@ -44,6 +43,12 @@ module Compass
|
||||
:chunky_png_options
|
||||
].flatten
|
||||
|
||||
ARRAY_ATTRIBUTES = [
|
||||
:sprite_load_path,
|
||||
:required_libraries,
|
||||
:loaded_frameworks,
|
||||
:framework_path
|
||||
]
|
||||
# Registers a new configuration property.
|
||||
# Extensions can use this to add new configuration options to compass.
|
||||
#
|
||||
|
@ -57,10 +57,11 @@ module Compass
|
||||
chained_method :run_stylesheet_error
|
||||
|
||||
inherited_accessor *ATTRIBUTES
|
||||
inherited_accessor :required_libraries, :loaded_frameworks, :framework_path #XXX we should make these arrays add up cumulatively.
|
||||
|
||||
strip_trailing_separator *ATTRIBUTES.select{|a| a.to_s =~ /dir|path/}
|
||||
|
||||
inherited_array *ARRAY_ATTRIBUTES
|
||||
|
||||
def initialize(name, attr_hash = nil)
|
||||
raise "I need a name!" unless name
|
||||
@name = name
|
||||
@ -91,7 +92,9 @@ module Compass
|
||||
# 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)
|
||||
@set_attributes ||= {}
|
||||
if block_given?
|
||||
@set_attributes[:asset_host] = true
|
||||
@asset_host = block
|
||||
else
|
||||
if @asset_host
|
||||
@ -116,16 +119,19 @@ module Compass
|
||||
#
|
||||
# asset_cache_buster :none
|
||||
def asset_cache_buster(simple = nil, &block)
|
||||
@set_attributes ||= {}
|
||||
if block_given?
|
||||
@set_attributes[:asset_cache_buster] = true
|
||||
@asset_cache_buster = block
|
||||
elsif !simple.nil?
|
||||
if simple == :none
|
||||
@set_attributes[:asset_cache_buster] = true
|
||||
@asset_cache_buster = Proc.new {|_,_| nil}
|
||||
else
|
||||
raise ArgumentError, "Unexpected argument: #{simple.inspect}"
|
||||
end
|
||||
else
|
||||
if @asset_cache_buster
|
||||
if set?(:asset_cache_buster)
|
||||
@asset_cache_buster
|
||||
elsif inherited_data.respond_to?(:asset_cache_buster)
|
||||
inherited_data.asset_cache_buster
|
||||
@ -173,7 +179,7 @@ module Compass
|
||||
private
|
||||
|
||||
def assert_valid_keys!(attr_hash)
|
||||
illegal_attrs = attr_hash.keys - ATTRIBUTES
|
||||
illegal_attrs = attr_hash.keys - ATTRIBUTES - ARRAY_ATTRIBUTES
|
||||
if illegal_attrs.size == 1
|
||||
raise Error, "#{illegal_attrs.first.inspect} is not a valid configuration attribute."
|
||||
elsif illegal_attrs.size > 0
|
||||
|
@ -61,6 +61,97 @@ module Compass
|
||||
inherited_writer(*attributes)
|
||||
end
|
||||
|
||||
class ArrayProxy
|
||||
def initialize(data, attr)
|
||||
@data, @attr = data, attr
|
||||
end
|
||||
def to_ary
|
||||
@data.send(:"read_inherited_#{@attr}_array")
|
||||
end
|
||||
def to_a
|
||||
to_ary
|
||||
end
|
||||
def <<(v)
|
||||
@data.send(:"add_to_#{@attr}", v)
|
||||
end
|
||||
def >>(v)
|
||||
@data.send(:"remove_from_#{@attr}", v)
|
||||
end
|
||||
def serialize_to_config(prop)
|
||||
if v = @data.raw(prop)
|
||||
"#{prop} = #{v.inspect}"
|
||||
else
|
||||
s = ""
|
||||
if added = @data.instance_variable_get("@added_to_#{@attr}")
|
||||
added.each do |a|
|
||||
s << "#{prop} << #{a.inspect}\n"
|
||||
end
|
||||
end
|
||||
if removed = @data.instance_variable_get("@removed_from_#{@attr}")
|
||||
removed.each do |r|
|
||||
s << "#{prop} >> #{r.inspect}\n"
|
||||
end
|
||||
end
|
||||
if s[-1..-1] == "\n"
|
||||
s[0..-2]
|
||||
else
|
||||
s
|
||||
end
|
||||
end
|
||||
end
|
||||
def method_missing(m, *args, &block)
|
||||
a = to_ary
|
||||
if a.respond_to?(m)
|
||||
a.send(m,*args, &block)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def inherited_array(*attributes)
|
||||
inherited_reader(*attributes)
|
||||
inherited_writer(*attributes)
|
||||
attributes.each do |attr|
|
||||
line = __LINE__ + 1
|
||||
class_eval %Q{
|
||||
def #{attr} # def sprite_load_paths
|
||||
ArrayProxy.new(self, #{attr.inspect}) # ArrayProxy.new(self, :sprite_load_paths)
|
||||
end # end
|
||||
def #{attr}=(value) # def sprite_load_paths=(value)
|
||||
@set_attributes ||= {} # @set_attributes ||= {}
|
||||
@set_attributes[#{attr.inspect}] = true # @set_attributes[:sprite_load_paths] = true
|
||||
@#{attr} = Array(value) # @sprite_load_paths = Array(value)
|
||||
@added_to_#{attr} = [] # @added_to_sprite_load_paths = []
|
||||
@removed_from_#{attr} = [] # @removed_from_sprite_load_paths = []
|
||||
end # end
|
||||
def read_inherited_#{attr}_array # def read_inherited_sprite_load_paths_array
|
||||
if #{attr}_set? # if sprite_load_paths_set?
|
||||
@#{attr} # Array(@#{attr})
|
||||
else # else
|
||||
value = Array(read(#{attr.inspect})) # value = Array(read(:sprite_load_paths))
|
||||
value -= Array(@removed_from_#{attr}) # value -= Array(@removed_from_sprite_load_paths)
|
||||
Array(@added_to_#{attr}) + value # Array(@added_to_sprite_load_paths) + value
|
||||
end # end
|
||||
end # end
|
||||
def add_to_#{attr}(v) # def add_to_sprite_load_paths(v)
|
||||
if #{attr}_set? # if sprite_load_paths_set?
|
||||
raw_#{attr} << v # raw_sprite_load_paths << v
|
||||
else # else
|
||||
(@added_to_#{attr} ||= []) << v # (@added_to_sprite_load_paths ||= []) << v
|
||||
end # end
|
||||
end # end
|
||||
def remove_from_#{attr}(v) # def remove_from_sprite_load_paths(v)
|
||||
if #{attr}_set? # if sprite_load_paths_set?
|
||||
raw_#{attr}.reject!{|e| e == v} # raw_sprite_load_path.reject!{|e| e == v}s
|
||||
else # else
|
||||
(@removed_from_#{attr} ||= []) << v # (@removed_from_sprite_load_paths ||= []) << v
|
||||
end # end
|
||||
end # end
|
||||
}, __FILE__, line
|
||||
end
|
||||
end
|
||||
|
||||
def chained_method(method)
|
||||
line = __LINE__ + 1
|
||||
class_eval %Q{
|
||||
@ -146,6 +237,12 @@ module Compass
|
||||
end
|
||||
end
|
||||
|
||||
# Reads the raw value that was set on this object.
|
||||
# you generally should call raw_<attribute>() instead.
|
||||
def raw(attribute)
|
||||
instance_variable_get("@#{attribute}")
|
||||
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)
|
||||
|
@ -16,13 +16,19 @@ module Compass
|
||||
end
|
||||
end
|
||||
|
||||
def get_binding
|
||||
binding
|
||||
end
|
||||
def parse_string(contents, filename)
|
||||
bind = binding
|
||||
bind = get_binding
|
||||
eval(contents, bind, filename)
|
||||
ATTRIBUTES.each do |prop|
|
||||
value = eval(prop.to_s, bind) rescue nil
|
||||
local_vars_set = eval("local_variables", bind)
|
||||
local_vars_set.each do |local_var|
|
||||
if (ATTRIBUTES+ARRAY_ATTRIBUTES).include?(local_var)
|
||||
value = eval(local_var.to_s, bind)
|
||||
value = value.to_s if value.is_a?(Pathname)
|
||||
self.send("#{prop}=", value) unless value.nil?
|
||||
self.send("#{local_var}=", value)
|
||||
end
|
||||
end
|
||||
if @added_import_paths
|
||||
self.additional_import_paths ||= []
|
||||
@ -45,7 +51,7 @@ module Compass
|
||||
end
|
||||
contents << "# Require any additional compass plugins here.\n"
|
||||
contents << "\n" if (required_libraries || []).any?
|
||||
ATTRIBUTES.each do |prop|
|
||||
(ATTRIBUTES + ARRAY_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."
|
||||
@ -64,8 +70,12 @@ module Compass
|
||||
end
|
||||
|
||||
def serialize_property(prop, value)
|
||||
if value.respond_to?(:serialize_to_config)
|
||||
value.serialize_to_config(prop) + "\n"
|
||||
else
|
||||
%Q(#{prop} = #{value.inspect}\n)
|
||||
end
|
||||
end
|
||||
|
||||
def issue_deprecation_warnings
|
||||
if http_images_path == :relative
|
||||
|
@ -68,6 +68,84 @@ class ConfigurationTest < Test::Unit::TestCase
|
||||
assert_equal "WARNING: asset_host is code and cannot be written to a file. You'll need to copy it yourself.\n", warning
|
||||
end
|
||||
|
||||
class TestData < Compass::Configuration::Data
|
||||
def initialize
|
||||
super(:test)
|
||||
end
|
||||
inherited_array :stuff
|
||||
end
|
||||
|
||||
def test_inherited_array_can_clobber
|
||||
data1 = TestData.new
|
||||
data1.stuff = [:a]
|
||||
data2 = TestData.new
|
||||
data2.stuff = [:b]
|
||||
data2.inherit_from!(data1)
|
||||
assert_equal [:b], data2.stuff.to_a
|
||||
end
|
||||
|
||||
def test_inherited_array_can_append
|
||||
data1 = TestData.new
|
||||
data1.stuff = [:a]
|
||||
data2 = TestData.new
|
||||
data2.stuff << :b
|
||||
data2.inherit_from!(data1)
|
||||
assert_equal [:b, :a], data2.stuff.to_a
|
||||
end
|
||||
|
||||
def test_inherited_array_can_remove
|
||||
data1 = TestData.new
|
||||
data1.stuff = [:a]
|
||||
data2 = TestData.new
|
||||
data2.stuff >> :a
|
||||
data2.inherit_from!(data1)
|
||||
assert_equal [], data2.stuff.to_a
|
||||
end
|
||||
|
||||
def test_inherited_array_combined_augmentations
|
||||
data1 = TestData.new
|
||||
data1.stuff = [:a]
|
||||
data2 = TestData.new
|
||||
data2.stuff >> :a
|
||||
data2.stuff << :b
|
||||
data2.inherit_from!(data1)
|
||||
assert_equal [:b], data2.stuff.to_a
|
||||
end
|
||||
|
||||
def test_inherited_array_long_methods
|
||||
data1 = TestData.new
|
||||
data1.stuff = [:a]
|
||||
data2 = TestData.new
|
||||
data2.remove_from_stuff(:a)
|
||||
data2.add_to_stuff(:b)
|
||||
data2.inherit_from!(data1)
|
||||
assert_equal [:b], data2.stuff.to_a
|
||||
end
|
||||
|
||||
def test_inherited_array_augmentations_can_be_clobbered
|
||||
data1 = TestData.new
|
||||
data1.stuff = [:a]
|
||||
data2 = TestData.new
|
||||
data2.stuff >> :a
|
||||
data2.stuff << :b
|
||||
data2.stuff = [:c]
|
||||
data2.inherit_from!(data1)
|
||||
assert_equal [:c], data2.stuff.to_a
|
||||
end
|
||||
|
||||
def test_inherited_array_augmentations_after_clobbering
|
||||
data1 = TestData.new
|
||||
data1.stuff = [:a]
|
||||
data2 = TestData.new
|
||||
data2.stuff >> :a
|
||||
data2.stuff << :b
|
||||
data2.stuff = [:c, :d]
|
||||
data2.stuff << :e
|
||||
data2.stuff >> :c
|
||||
data2.inherit_from!(data1)
|
||||
assert_equal [:d, :e], data2.stuff.to_a
|
||||
end
|
||||
|
||||
def test_serialization_warns_with_asset_cache_buster_set
|
||||
contents = StringIO.new(<<-CONFIG)
|
||||
asset_cache_buster do |path|
|
||||
@ -77,12 +155,36 @@ class ConfigurationTest < Test::Unit::TestCase
|
||||
|
||||
Compass.add_configuration(contents, "test_serialization_warns_with_asset_cache_buster_set")
|
||||
|
||||
assert_kind_of Proc, Compass.configuration.asset_cache_buster_without_default
|
||||
assert_equal "http://example.com", Compass.configuration.asset_cache_buster_without_default.call("whatever")
|
||||
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_inherited_arrays_augmentations_serialize
|
||||
inherited = TestData.new
|
||||
inherited.stuff << :a
|
||||
d = TestData.new
|
||||
d.stuff << :b
|
||||
d.stuff >> :c
|
||||
assert_equal <<CONFIG, d.serialize_property(:stuff, d.stuff)
|
||||
stuff << :b
|
||||
stuff >> :c
|
||||
CONFIG
|
||||
end
|
||||
def test_inherited_arrays_clobbering_with_augmentations_serialize
|
||||
inherited = TestData.new
|
||||
inherited.stuff << :a
|
||||
d = TestData.new
|
||||
d.stuff << :b
|
||||
d.stuff = [:c, :d]
|
||||
d.stuff << :e
|
||||
assert_equal <<CONFIG, d.serialize_property(:stuff, d.stuff)
|
||||
stuff = [:c, :d, :e]
|
||||
CONFIG
|
||||
end
|
||||
def test_additional_import_paths
|
||||
contents = StringIO.new(<<-CONFIG)
|
||||
http_path = "/"
|
||||
@ -190,6 +292,36 @@ http_path = \"/\"
|
||||
# To disable debugging comments that display the original location of your selectors. Uncomment:
|
||||
# line_comments = false
|
||||
|
||||
EXPECTED
|
||||
|
||||
assert_correct(expected_serialization, Compass.configuration.serialize)
|
||||
end
|
||||
|
||||
def test_sprite_load_path_clobbers
|
||||
contents = StringIO.new(<<-CONFIG)
|
||||
sprite_load_path = ["/Users/chris/Projects/my_compass_project/images/sprites"]
|
||||
CONFIG
|
||||
|
||||
Compass.add_configuration(contents, "test_sass_options")
|
||||
|
||||
assert_equal ["/Users/chris/Projects/my_compass_project/images/sprites"], Compass.configuration.sprite_load_path.to_a
|
||||
|
||||
expected_serialization = <<EXPECTED
|
||||
# Require any additional compass plugins here.
|
||||
|
||||
# Set this to the root of your project when deployed:
|
||||
http_path = "/"
|
||||
|
||||
# You can select your preferred output style here (can be overridden via the command line):
|
||||
# output_style = :expanded or :nested or :compact or :compressed
|
||||
|
||||
# To enable relative paths to assets via compass helper functions. Uncomment:
|
||||
# relative_assets = true
|
||||
|
||||
# To disable debugging comments that display the original location of your selectors. Uncomment:
|
||||
# line_comments = false
|
||||
|
||||
sprite_load_path = ["/Users/chris/Projects/my_compass_project/images/sprites"]
|
||||
EXPECTED
|
||||
|
||||
assert_correct(expected_serialization, Compass.configuration.serialize)
|
||||
|
Loading…
Reference in New Issue
Block a user