1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
|
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
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)
options = attributes.last.is_a?(Hash) ? attributes.pop : {}
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
value = if inherited_data # value = if inherited_data
if #{!!options[:clobbers]} && #{attr}_set?
Array(@#{attr}) # Array(@#{attr})
else
Array(@#{attr}) + inherited_data.read_inherited_#{attr}_array # inherited_data.read_inherited_sprite_load_paths_array + Array(@sprite_load_paths)
end
elsif #{attr}_set? # elsif sprite_load_paths_set?
Array(@#{attr}) # Array(@#{attr})
else # else
top_level.default_for(#{attr.inspect}) || [] # top_level.default_for(:sprite_load_paths) || []
end # end
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
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{
alias_method :_chained_#{method}, method
def #{method}(*args, &block)
_chained_#{method}(*args, &block)
if inherited_data
inherited_data.#{method}(*args, &block)
end
end
}, __FILE__, line
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 reset_inheritance!
self.inherited_data = nil
end
def with_defaults(data)
inherit_from!(data)
yield
reset_inheritance!
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 any_attributes_set?
@set_attributes && @set_attributes.size > 0
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.nil?
nil
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
# 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)
if !(v = send("#{attribute}_without_default")).nil?
v
else
top_level.default_for(attribute)
end
end
def method_missing(meth, *args, &block)
if inherited_data
inherited_data.send(meth, *args, &block)
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 chain
instances = [self]
instances << instances.last.inherited_data while instances.last.inherited_data
instances
end
def debug
normalized_attrs = {}
(ATTRIBUTES + ARRAY_ATTRIBUTES).each do |prop|
values = []
chain.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),
:resolved => instance.send(prop)
}
end
normalized_attrs[prop] = values
end
normalized_attrs
end
end
end
end
end
|