Module: Tina4::Container

Defined in:
lib/tina4/container.rb

Overview

Lightweight dependency injection container.

Tina4::Container.register(:mailer) { MailService.new } # transient — new instance each get
Tina4::Container.singleton(:db) { Database.new(ENV["DB_URL"]) } # singleton — memoised
Tina4::Container.register(:cache, RedisCacheInstance)  # concrete instance (always same)
Tina4::Container.get(:db)                              # => Database instance

Class Method Summary collapse

Class Method Details

.get(name) ⇒ Object

Resolve a service by name. Singletons and concrete instances return the same object each time. Transient factories (register with block) return a new object each time.

Raises:

  • (KeyError)


42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/tina4/container.rb', line 42

def get(name)
  entry = registry[name.to_sym]
  raise KeyError, "service not registered: #{name}" unless entry

  # Concrete instance (register with value)
  return entry[:instance] if entry[:instance] && entry[:factory].nil?

  if entry[:factory]
    if entry[:singleton]
      # Singleton — call once, memoize
      entry[:instance] ||= entry[:factory].call
      entry[:instance]
    else
      # Transient — call every time
      entry[:factory].call
    end
  else
    entry[:instance]
  end
end

.has(name) ⇒ Object

Check if a service is registered.



64
65
66
# File 'lib/tina4/container.rb', line 64

def has(name)
  registry.key?(name.to_sym)
end

.register(name, instance = nil, &factory) ⇒ Object

Register a service by name. Pass a concrete instance directly, or a block for transient instantiation. Blocks are called on every get() — use singleton() for memoised factories.

Raises:

  • (ArgumentError)


20
21
22
23
24
25
26
27
28
29
# File 'lib/tina4/container.rb', line 20

def register(name, instance = nil, &factory)
  raise ArgumentError, "provide an instance or a block, not both" if instance && factory
  raise ArgumentError, "provide an instance or a block" unless instance || factory

  registry[name.to_sym] = if factory
    { factory: factory, singleton: false, instance: nil }
  else
    { factory: nil, singleton: false, instance: instance }
  end
end

.registryObject



13
14
15
# File 'lib/tina4/container.rb', line 13

def registry
  @registry ||= {}
end

.resetObject

Remove all registrations (useful in tests).



69
70
71
# File 'lib/tina4/container.rb', line 69

def reset
  @registry = {}
end

.singleton(name, &factory) ⇒ Object

Register a singleton factory by name. The block is called once on first resolve() and the result is memoised.

Raises:

  • (ArgumentError)


33
34
35
36
37
# File 'lib/tina4/container.rb', line 33

def singleton(name, &factory)
  raise ArgumentError, "singleton requires a block" unless factory

  registry[name.to_sym] = { factory: factory, singleton: true, instance: nil }
end