Module: Essential::Resource::AttrRelations

Included in:
Essential::Resource::AttrMethods::ClassMethods
Defined in:
lib/essential/resource/attr_relations.rb

Instance Method Summary collapse

Instance Method Details

#attr_relation(pairs) ⇒ Object

Method for defining a mapping between a ‘sid` property and a Class.



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
# File 'lib/essential/resource/attr_relations.rb', line 7

def attr_relation(pairs)
  pairs.each do |method, resource|
    sid_method = method.to_sym

    # Enforces that we're dealing with a `sid` property.
    if method =~ /\A(.+)_sid\Z/
      method = $1.to_sym
    else
      raise ArgumentError, 'attribute must end in _sid'
    end

    unless method_defined?(sid_method)
      # perhaps we didn't explicitly define access to the sid - do so now.
      attr_property sid_method
    end

    # Avoid circular load dependencies:
    #
    # `resource` might be a Class, or it might be a String.
    #
    # If it is a String, we'll attempt to turn it into a CLass.
    # This will be done once - the first time it is accessed -
    # to allow both classes to load completely before constantizing
    # the String.
    my_lazy_resource = format('lazy_%s_resource', method).to_sym
    my_lazy_variable = format('@%s', my_lazy_resource).to_sym
    define_singleton_method(my_lazy_resource) do
      # only resolve this resource once
      lazy_resource = instance_variable_get(my_lazy_variable)
      unless lazy_resource
        lazy_resource = resource

        case lazy_resource
        when String
          if lazy_resource.include?('::')
            # this appears to be a fully-namespaced Resource
            lazy_resource = lazy_resource.split('::')
          else
            # no namespace - assume it's part of the current class.
            namespace = self.name.split('::')
            namespace.pop
            namespace << lazy_resource
            lazy_resource = namespace
          end
          # fetch each module in turn
          lazy_resource = lazy_resource.reduce(Object) {|o,c| o.const_get c}
        end

        # now that we have a class, ensure it is *actually* a Resource!
        unless lazy_resource < Essential::Resource
          raise ArgumentError, format('mapping must be an %s', Essential::Resource.name)
        end

        # cache it.
        instance_variable_set(my_lazy_variable, lazy_resource)
      end

      lazy_resource
    end

    define_method(method) do
      sid = send(sid_method)
      if sid.nil?
        nil
      else
        self.class.send(my_lazy_resource).new(sid: sid, headers: @headers)
      end
    end
  end

end