Module: MinDI::Container

Defined in:
lib/mindi.rb

Constant Summary collapse

PROXY_METHODS =

:nodoc:

["__send__", "__id__", "method_missing", "call"]

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args, &bl) ⇒ Object

For declarative style container definitions (“shortcuts”).



200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/mindi.rb', line 200

def method_missing(meth, *args, &bl) # :nodoc:
  super unless bl

  case bl.arity
  when 0
    singleton(meth, *args, &bl)
  when 1
    multiton(meth, *args, &bl)
  else
    # note that this includes the case of a block with _no_ args, i.e.
    # { value }, which has arity -1, indistinguishabe from {|*args|}.
    multikey_multiton(meth, *args, &bl)
  end
end

Instance Method Details

#deferred(name, &impl) ⇒ Object

Define a singleton service with deferred instantiation. Syntax and semantics are the same as #singleton, except that the block is not called when the service is requested, but only when a method is called on the service.



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

def deferred(name, &impl)  # :yields:
  impl_name = Container.impl_method_name(name)
  define_implementation(impl_name, impl)
  
  proxy_name = Container.impl_method_name("#{name}_proxy")

  ivname = Container.iv(name)
  proxy_ivname = Container.iv("#{name}_proxy")
  
  define_method(name) do
    instance_variable_get(ivname) || send(proxy_name)
  end
  
  define_method(proxy_name) do
    proxy = instance_variable_get(proxy_ivname)
    
    unless proxy
      proxy = proc {instance_variable_set(ivname, send(impl_name))}
      def proxy.method_missing(*args, &block)
        call.__send__(*args, &block)
      end
      instance_variable_set(proxy_ivname, proxy)
      class << proxy; self; end.class_eval do
        (proxy.methods - PROXY_METHODS).each do |m|
          undef_method m
        end
      end
    end

    proxy
  end
end

#generic(name, &impl) ⇒ Object

Define a generic service, which has no built-in rules for existence or uniqueness. There is no shortcut for generic service definition. Calling the service simply calls the associated block. This is also known as a prototype service.



50
51
52
# File 'lib/mindi.rb', line 50

def generic(name, &impl)  # :yields:
  define_implementation(name, impl)
end

#multikey_multiton(name, &impl) ⇒ Object

Define a multiton service with multiple keys:

multiton(:service_name) { |arg0, arg1, ...| ... }

The block will be called once per distinct (in the sense of hash keys) argument list to instantiate a unique value corresponding to the argument list. The shortcut for defining a multikey_multiton with multiple keys is:

service_name { |arg0, arg1, ...| ... }

The service is invoked as service_name(arg0, arg1, ...). Variable length argument lists, using the splat notation, are permitted.



115
116
117
118
119
120
121
122
123
124
125
# File 'lib/mindi.rb', line 115

def multikey_multiton(name, &impl) # :yields: arg0, arg1, ...
  impl_name = Container.impl_method_name(name)
  define_implementation(impl_name, impl)

  ivname = Container.iv(name)
  define_method(name) do |*key|
    map = instance_variable_get(ivname)
    map ||= instance_variable_set(ivname, {})
    map.key?(key) ?  map[key] : map[key] = send(impl_name, *key)
  end
end

#multiton(name, &impl) ⇒ Object

Define a multiton service:

multiton(:service_name) { |arg| ... }

The block will be called once per distinct (in the sense of hash keys) argument to instantiate a unique value corresponding to the argument. The shortcut for defining a multiton is:

service_name { |arg| ... }

The service is invoked as service_name(arg).



90
91
92
93
94
95
96
97
98
99
100
# File 'lib/mindi.rb', line 90

def multiton(name, &impl) # :yields: arg
  impl_name = Container.impl_method_name(name)
  define_implementation(impl_name, impl)
  
  ivname = Container.iv(name)
  define_method(name) do |key|
    map = instance_variable_get(ivname)
    map ||= instance_variable_set(ivname, {})
    map.key?(key) ?  map[key] : map[key] = send(impl_name, key)
  end
end

#singleton(name, &impl) ⇒ Object

Define a singleton service:

singleton(:service_name) { ... }

The block will be called at most once to instantiate a unique value for the service. The shortcut for defining a singleton is:

service_name { ... }

The service is invoked as service_name.



65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/mindi.rb', line 65

def singleton(name, &impl)  # :yields:
  impl_name = Container.impl_method_name(name)
  define_implementation(impl_name, impl)

  ivname = Container.iv(name)
  define_method(name) do
    box = instance_variable_get(ivname)
    box ||= instance_variable_set(ivname, [])
    box << send(impl_name) if box.empty?
    box.first
  end
end

#threaded(name, &impl) ⇒ Object

Define a service with per-thread instantiation. For each thread, the service appears to be a singleton service. The block will be called at most once per thread. There is no shortcut. The block may take a single argument, in which case it will be passed the current thread.

threaded(:service_name) { |thr| ... }


141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/mindi.rb', line 141

def threaded(name, &impl)  # :yields: thread
  impl_name = Container.impl_method_name(name)
  define_implementation(impl_name, impl)
  arity = impl.arity

  ivname = Container.iv(name)
  define_method(name) do
    key = Thread.current
    map = instance_variable_get(ivname)
    map ||= instance_variable_set(ivname, {})
    map[key] ||= (arity == 1 ? send(impl_name, key) : send(impl_name))
  end
end