File: inheritance.rb

package info (click to toggle)
ruby-compass 1.0.3~dfsg-4
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 8,184 kB
  • ctags: 1,789
  • sloc: ruby: 12,904; makefile: 100; perl: 43; xml: 14; sh: 4
file content (307 lines) | stat: -rw-r--r-- 12,324 bytes parent folder | download | duplicates (2)
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