Module: Diana

Extended by:
Config
Defined in:
lib/diana.rb,
lib/diana/config.rb,
lib/diana/version.rb

Overview

Dependency Injection DSL

This module offers a DSL designed for the lazy resolution of dependency injections. It facilitates efficient and deferred initialization of dependencies ensuring that resources are only allocated when necessary.

This approach optimizes performance of application.

Examples:

class MyClass
  include Diana.dependencies(
    foo: proc { Foo.new },
    bar: proc { Bar.new }
  )
end

Defined Under Namespace

Modules: Config

Constant Summary collapse

VERSION =

Returns the version of the Diana gem.

Returns:

  • (String)

    The version of the gem in Semantic Versioning (SemVer) format.

File.read(File.join(File.dirname(__FILE__), "../../VERSION")).strip

Class Method Summary collapse

Methods included from Config

methods_visibility, methods_visibility=, resolve, resolver, resolver=

Class Method Details

.dependencies(deps) ⇒ Module Also known as: dependency

Define dependencies for a class.

param deps [Hash] A hash where keys are the names of the dependencies and

values are the dependencies themselves, resolved lazily.

Examples:

class MyClass
  include Diana.dependencies(
    foo: proc { MyFoo.new }
    bar: proc { MyBar.new }
  )
end

Returns:

  • (Module)

    A module to be included in your class, providing the defined dependencies.



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

def dependencies(deps)
  Module.new do
    # Adds readable name to this anonymous module when showing `MyClass.ancestors`
    def self.inspect
      "<Diana.dependencies:#{object_id.to_s(16)}>"
    end

    class_mod = Module.new do
      # Adds readable name to this anonymous module when showing `MyClass.singleton_class.ancestors`
      def self.inspect
        "<Diana.inheritance:#{object_id.to_s(16)}>"
      end

      private

      def inherited(subclass)
        # When `inherited` is called for the first time, we add the parent's
        # `@_diana_dependencies`. To avoid adding dependencies from
        # ancestors in the `super` call, we check if `@_diana_dependencies`
        # is already defined.
        unless subclass.instance_variable_defined?(:@_diana_dependencies)
          subclass.include Diana.dependencies(@_diana_dependencies)
        end

        super
      end
    end

    define_singleton_method(:included) do |base|
      # Adds .inherited method
      base.extend(class_mod)

      #
      # Merging dependencies allows to add dependencies multiple times
      #
      # Example:
      #   class MyClass
      #     include Diana.dependencies(foo: 'foo')
      #     include Diana.dependencies(bar: 'bar')
      #   end
      #
      merged_deps =
        if base.instance_variable_defined?(:@_diana_dependencies)
          base.instance_variable_get(:@_diana_dependencies).merge!(deps)
        else
          base.instance_variable_set(:@_diana_dependencies, deps.dup)
        end

      # Add initialize method
      # Instance variables are set only for not-null dependencies.
      # Using class_eval is slower to define the method, yet it provides the
      # benefit of executing faster than if it were defined using define_method.
      class_eval("        def initialize(\#{merged_deps.each_key.map { |dependency| \"\#{dependency}: nil\" }.join(\", \")})\n        \#{merged_deps.each_key.map { |dependency| \"  @\#{dependency} = \#{dependency} if \#{dependency}\" }.join(\"\\n\")}\n        end\n      INITIALIZE\n    end\n\n    # Add dependencies attribute readers and set their visibility.\n    # Using class_eval is slower to define the method, yet it provides the\n    # benefit of executing faster than if it were defined using define_method.\n    deps.each_key do |dependency|\n      class_eval(<<~ATTR_READER, __FILE__, __LINE__ + 1)\n        def \#{dependency}\n          @\#{dependency} ||= Diana.resolve(self.class.instance_variable_get(:@_diana_dependencies)[:\#{dependency}])\n        end\n\n        \#{Diana.methods_visibility} :\#{dependency}\n      ATTR_READER\n    end\n  end\nend\n", __FILE__, __LINE__ + 1)