Module: FPM::Cookery::InheritableAttr

Included in:
BaseRecipe
Defined in:
lib/fpm/cookery/inheritable_attr.rb

Overview

Provides inheritance of class-level attributes. Attributes are cloned from the superclass, except for non-clonable attributes, which are assigned directly.

This module will automatically define class methods for keeping track of inheritable attributes, as follows:

+attr_rw+       => +klass.scalar_attrs+
+attr_rw_list+  => +klass.list_attrs+
+attr_rw_hash+  => +klass.hash_attrs+
+attr_rw_path+  => +klass.path_attrs+

Examples:

class Foo
  extend FPM::Cookery::InheritableAttr

  attr_rw       :name, :rank
  attr_rw_list  :favorite_things
  attr_rw_hash  :meta
  attr_rw_path  :home

  name("J.J. Jingleheimer-Schmidt")
  favorite_things("brown paper packages", "raindrops on roses")
  meta[:data] = "la la la la la la la la"
  home = "/home/jjschmidt"
end

Bar = Class.new(Foo)
Bar.home                #=> #<FPM::Cookery::Path:/home/jjschmidt>
Bar.home = "/home/free" #=> #<FPM::Cookery::Path:/home/free>
Foo.home                #=> #<FPM::Cookery::Path:/home/jjschmidt>

Foo.scalar_attrs        #=> [:name, :rank]
Foo.list_attrs          #=> [:favorite_things]
Foo.hash_attrs          #=> [:meta]
Foo.path_attrs          #=> [:home]

Defined Under Namespace

Classes: DeepClone

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.extended(klass) ⇒ Object



194
195
196
197
198
# File 'lib/fpm/cookery/inheritable_attr.rb', line 194

def extended(klass)
  # Inject the +attr_registry+ attribute into any class that extends
  # this module.
  klass.attr_rw_hash(:attr_registry)
end

.inherit_for(klass, name) ⇒ Object



189
190
191
192
# File 'lib/fpm/cookery/inheritable_attr.rb', line 189

def inherit_for(klass, name)
  return unless klass.superclass.respond_to?(name)
  DeepClone.(klass.superclass.send(name))
end

Instance Method Details

#attr_rw(*attrs) ⇒ Object

Create ‘scalar’ (i.e. non-collection) attributes.



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/fpm/cookery/inheritable_attr.rb', line 85

def attr_rw(*attrs)
  attrs.each do |attr|
    class_eval %Q{
      def self.#{attr}(value = nil)
        if value.nil?
          return @#{attr} if instance_variable_defined?(:@#{attr})
          @#{attr} = InheritableAttr.inherit_for(self, :#{attr})
        else
          @#{attr} = value
        end
      end

      def #{attr}
        self.class.#{attr}
      end
    }
  end

  register_attrs(:scalar, *attrs)
end

#attr_rw_hash(*attrs) ⇒ Object

Create Hash-style attributes. Supports both hash and argument assignment:

attr_method[:attr1] = xxxx
attr_method :xxxx=>1, :yyyy=>2


141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/fpm/cookery/inheritable_attr.rb', line 141

def attr_rw_hash(*attrs)
  attrs.each do |attr|
    class_eval %Q{
      def self.#{attr}(args = {})
        unless instance_variable_defined?(:@#{attr})
          @#{attr} = InheritableAttr.inherit_for(self, :#{attr})
        end

        (@#{attr} ||= {}).merge!(args)
      end

      def #{attr}
        self.class.#{attr}
      end
    }
  end

  register_attrs(:hash, *attrs)
end

#attr_rw_list(*attrs) ⇒ Object

Create list-style attributes, backed by Arrays. nil entries will be filtered, non-unique entries will be culled to one instance only, and the list will be flattened.



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
# File 'lib/fpm/cookery/inheritable_attr.rb', line 109

def attr_rw_list(*attrs)
  attrs.each do |attr|
    class_eval %Q{
      def self.#{attr}(*list)
        unless instance_variable_defined?(:@#{attr})
          @#{attr} = InheritableAttr.inherit_for(self, :#{attr})
        end

        @#{attr} ||= []

        unless list.empty?
          @#{attr} << list
          @#{attr}.flatten!
          @#{attr}.uniq!
        end

        @#{attr}
      end

      def #{attr}
        self.class.#{attr}
      end
    }
  end

  register_attrs(:list, *attrs)
end

#attr_rw_path(*attrs) ⇒ Object

Create methods for attributes representing paths. Arguments to writer methods will be converted to FPM::Cookery::Path objects.



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/fpm/cookery/inheritable_attr.rb', line 163

def attr_rw_path(*attrs)
  attrs.each do |attr|
    class_eval %Q{
      def self.#{attr}
        return @#{attr} if instance_variable_defined?(:@#{attr})
        @#{attr} = InheritableAttr.inherit_for(self, :#{attr})
      end

      def self.#{attr}=(value)
        @#{attr} = FPM::Cookery::Path.new(value)
      end

      def #{attr}=(value)
        self.class.#{attr} = value
      end

      def #{attr}(path = nil)
        self.class.#{attr}(path)
      end
    }
  end

  register_attrs(:path, *attrs)
end

#register_attrs(type, *attrs) ⇒ Object

Adds a list of attributes keyed to the type key of an internal hash tracking class attributes. Also defines the method “#{type}_attrs”, which will return the list of attribute names keyed to type.



74
75
76
77
78
79
80
81
82
# File 'lib/fpm/cookery/inheritable_attr.rb', line 74

def register_attrs(type, *attrs)
  (attr_registry[type] ||= []).concat(attrs)

  unless respond_to?(type_reader = :"#{type}_attrs")
    (class << self ; self ; end).send(:define_method, type_reader) do
      attr_registry.fetch(type, []).dup
    end
  end
end