Module: Multitype::ClassMethods

Defined in:
lib/multitype.rb

Instance Method Summary collapse

Instance Method Details

#__get_multitype_cache__(type = nil, cat = nil, klass = self.name.to_sym, depth = 0) ⇒ Object



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
# File 'lib/multitype.rb', line 162

def __get_multitype_cache__(type = nil, cat = nil, klass = self.name.to_sym, depth = 0)
  type = type.to_sym if type
  cat  = cat.to_sym  if cat

  # If the klass is not cached just return nil
  return nil unless __multitype_cache__[klass]

  # Return the level of data requested
  result = if type.nil? && cat.nil?
    __multitype_cache__[klass]
  elsif cat.nil? && __multitype_cache__[klass]
    __multitype_cache__[klass][type]
  elsif __multitype_cache__[klass] && __multitype_cache__[klass][type]
    __multitype_cache__[klass][type][cat]
  else
    nil
  end

  # Don't loop through ancestors on lower objects
  return result if depth > 0

  # Iterate over ancestors to use their typesets
  ancestors.inject(result) do |out, ancestor|
    ancestor = ancestor.name.to_sym

    # Skip if ancestor is self or Multitype
    if ancestor == klass || ancestor == 'Multitype'
      out
    else

      # Get data lower down the ancestry tree
      merge_in = __get_multitype_cache__(type, cat, ancestor, depth + 1)

      # Merge in data as required
      if out.nil? && ! merge_in.nil?
        merge_in
      else
        if merge_in.is_a?(Array)
          out.concat(merge_in)
        elsif merge_in.is_a?(Hash)
          out.merge(merge_in)
        else
          out
        end
      end
    end
  end
end

#deftype(type, name, args = {}, &block) ⇒ Object



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
# File 'lib/multitype.rb', line 75

def deftype(type, name, args = {}, &block)
  __create_multitype_datastore__
  type = type.to_sym
  name = name.to_sym

  # Merge in name, type and model for accessable access
  args.merge!(type: type, name: name, model: self)

  # Get the list of accessable keys we need
  accessable = args.keys.map(&:to_sym)

  # Kickstart the type comparators
  type_comparator(type)

  # Get the name of the class to extend if it exists
  type_class = if args[:class]
    klass = if args[:class].is_a?(String)
      get_const(args[:class])
    else
      args[:class]
    end

    klass.is_a?(Object) ? klass : Object
  else
    Object
  end

  # Define the class and mark as a typeset
  object = Class.new(type_class)
  object.class_attribute :__multitype_typeset__
  object.__multitype_typeset__ = true

  # Execute the passed block as if it was class level code
  object.class_exec(&block) if block_given?

  # Instantiate the object
  object = object.new

  # Make the variables passed accessable
  type_accessor(object, *accessable)

  # Set the instance attributes that were passed in
  args.each { |key, val| object.__send__("#{key}=", val) }

  # Add the type to the multitype cache
  __update_multitype_cache__(type, :typesets, {name => object})

  # Create the accessor method if it does not exist
  unless self.respond_to?(type)
    self.__send__(:define_method, type) do
      use_typeset = nil # Object to return

      # Loop through the types to find the one to use
      __get_multitype_cache__(type, :typesets).each do |name, typeset|
        match = true # Did we find a match

        # Loop through the type comparators to find a match
        __get_multitype_cache__(type, :comparators).each do |model_comparator, typeset_comparator|

          # Check the comparator for a match
          match = if typeset.respond_to?(typeset_comparator) && self.respond_to?(model_comparator)
            typeset.__send__(typeset_comparator) == self.__send__(model_comparator)
          else
            false
          end

          # If we fail at least one
          # match then break the loop
          break unless match
        end

        # If we have a match,
        # stop looking for one
        if match
          use_typeset = typeset
          break
        end
      end

      # Return the typeset
      use_typeset
    end
  end

  object
end

#type_accessor(type, *columns) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/multitype.rb', line 60

def type_accessor(type, *columns)
  __create_multitype_datastore__

  # Add accessors to the typeset that is passed or any typesets matching type
  if type.is_a?(Object) && type.respond_to?(:__multitype_typeset__)
    type.class.__send__(:attr_accessor, *columns)
  else
    raise NoTypeError, type.to_s unless __get_multitype_cache__(type)
    raise NoTypeSetError, type.to_s unless __get_multitype_cache__(type, :typesets)
    __get_multitype_cache__(type, :typesets).each do |name, typeset|
      typeset.class.attr_accessor(*columns)
    end
  end
end

#type_alias(type, *aliases) ⇒ Object

Raises:



48
49
50
51
52
53
54
55
56
57
58
# File 'lib/multitype.rb', line 48

def type_alias(type, *aliases)
  __create_multitype_datastore__

  # Get the type name if a typeset is passed as an object
  type = type.type if type.is_a?(Object) && type.respond_to?(:__multitype_typeset__)
  raise NoTypeError, type.to_s unless __get_multitype_cache__(type)

  aliases.each do |_alias|
    alias_method _alias, type
  end
end

#type_comparator(type, *comparators) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/multitype.rb', line 29

def type_comparator(type, *comparators)
  new_comparators = comparators.inject({}) do |out, comparator|
    if comparator.is_a?(Hash)
      out.merge(comparator)
    else
      out.merge({comparator => comparator})
    end
  end

  __send__(:attr_accessor, *new_comparators.keys)
  __create_multitype_datastore__

  # Get the type name if a typeset is passed as an object
  type = type.type if type.is_a?(Object) && type.respond_to?(:__multitype_typeset__)

  # Append to the list comparator columns
  __update_multitype_cache__(type, :comparators, new_comparators)
end