diana 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +36 -9
  3. data/VERSION +1 -1
  4. data/lib/diana/config.rb +7 -4
  5. data/lib/diana.rb +42 -1
  6. metadata +4 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1a01d25056de39c12dc7b080474f96d2e3645294f77fc199e1ea486d9db4045c
4
- data.tar.gz: 89722a0df0f1d63a265fd84e46afc37198ec6311f468ac93b4ef5fec9f0bce0d
3
+ metadata.gz: 5a42ecda41893911ab6703f513107d3789a191d1a378adf3b4c598816a0cd6ab
4
+ data.tar.gz: 40fe0dc12e4b06ffb4250e16d9bb2036936fb1fbf219e5dd0a2c55ea4e808919
5
5
  SHA512:
6
- metadata.gz: 6f45f231f1e32a68559cd6bf8ded95c997e8c58bf346611b3d33a46dc547cc9426af536771104ee798dbce70da2804f1672253146e1a70a6d8a42c430f9a28ba
7
- data.tar.gz: 8e6cfd282fb550df593cd46a04a4e6bc582181dc564e76999c9d41cb9008a5f6bb3c1d0d0c1f453835eb2776a4f913d42816699f30b47a6e3c8e5d15c97a21cf
6
+ metadata.gz: db8f596619a96ee92d7c10e893288213cb8697f8e4b4e6523f54565c4e1a8690f713a8afb323de0d46ac1d193891481c0e15772791a58aa28350615fc4798ff0
7
+ data.tar.gz: 6cb63290c78aa3f332fdb63f996b64ce9b81e6b27e284a8ea7513d6d0eb2616006f376b915ffb473fcc810900b994633a29665d30eb82ebbfe442ac3e379b64b
data/README.md CHANGED
@@ -1,3 +1,8 @@
1
+ [![Gem Version](https://badge.fury.io/rb/diana.svg)](https://badge.fury.io/rb/diana)
2
+ [![GitHub Actions](https://github.com/aglushkov/diana/actions/workflows/main.yml/badge.svg?event=push)](https://github.com/aglushkov/diana/actions/workflows/main.yml)
3
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/9e874aec44744552e642/test_coverage)](https://codeclimate.com/github/aglushkov/diana/test_coverage)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/9e874aec44744552e642/maintainability)](https://codeclimate.com/github/aglushkov/diana/maintainability)
5
+
1
6
  # Diana - Lazy Dependency Injection
2
7
 
3
8
  This module offers a DSL designed for the lazy resolution of dependency injections.
@@ -61,13 +66,30 @@ By default, dependency methods are **private**. You can change this behavior by
61
66
  configuring the Diana module:
62
67
 
63
68
  ```ruby
64
- Diana.methods_visibility = :public
69
+ Diana.methods_visibility = :public # private, public, protected
65
70
  ```
66
71
 
67
- Using public methods can be more convenient in tests, allowing you to access the
68
- real dependency and stub its methods, rather than overwriting the dependency
72
+ Using **public** methods can be more convenient in tests, allowing you to access
73
+ the real dependency and stub its methods, rather than overwriting the dependency
69
74
  entirely. This approach helps ensure you are testing the correct dependency.
70
75
 
76
+ ### Inheritance
77
+
78
+ Classes with included dependencies can be nested. Dependencies from parent and
79
+ child classes are merged.
80
+
81
+ ### Adding dependencies multiple times
82
+
83
+ `Diana.dependencies` method can be used multiple times. In this case
84
+ dependencies are merged.
85
+
86
+ ```ruby
87
+ class SomeClass
88
+ include Diana.dependencies(foo: proc { Foo.new })
89
+ include Diana.dependencies(bar: proc { Bar.new })
90
+ end
91
+ ```
92
+
71
93
  ## How it works
72
94
 
73
95
  - **Dependency Storage**: The `@_diana_dependencies` class variable holds the
@@ -97,17 +119,26 @@ class SomeClass
97
119
  bar: proc { Bar.new }
98
120
  }
99
121
 
122
+ # handles dependency injection
100
123
  def initialize(foo: nil, bar: nil)
101
124
  @foo = foo if foo
102
125
  @bar = bar if bar
103
126
  end
104
127
 
128
+ # handles inheritance
129
+ def self.inherited(subclass)
130
+ subclass.include Diana.dependencies(@_diana_dependencies)
131
+ super
132
+ end
133
+
105
134
  private
106
135
 
136
+ # handles lazy `foo` resolution
107
137
  def foo
108
138
  @foo ||= Diana.resolve(self.class.instance_variable_get(:@_diana_dependencies)[:foo])
109
139
  end
110
140
 
141
+ # handles lazy `bar` resolution
111
142
  def bar
112
143
  @bar ||= Diana.resolve(self.class.instance_variable_get(:@_diana_dependencies)[:bar])
113
144
  end
@@ -149,12 +180,8 @@ SomeClass.include Diana.dependencies(foo: 'utils.foo') # => DI_CONTAINER['utils.
149
180
  method, it will take precedence. In such cases, ensure you include a
150
181
  `super(**deps)` call to override dependencies if needed.
151
182
 
152
- - We do not perform any argument modifications and we do not call `super`
153
- in dependencies `#initialize` method. This approach is chosen to ensure
154
- optimal performance.
155
-
156
- - Dependencies defined in a parent class are not accessible in nested classes.
157
- You should define dependencies within each class where they are required.
183
+ - We avoid calling `super` in the added `#initialize` method to prevent the need
184
+ for arguments modifications, which could negatively impact performance.
158
185
 
159
186
  These limitations ensure that the gem remains predictable and performant,
160
187
  avoiding any hidden complexities or unexpected behaviors.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
data/lib/diana/config.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Diana
4
+ #
5
+ # Defines Diana module configuration methods
6
+ #
4
7
  module Config
5
8
  # The default resolver for dependencies.
6
9
  DEFAULT_RESOLVER = proc { |dependency| dependency.is_a?(Proc) ? dependency.call : dependency }
@@ -58,13 +61,13 @@ module Diana
58
61
  # @example Setting the default visibility of dependency methods to public.
59
62
  # Diana.methods_visibility = :public
60
63
  #
61
- # @param visibility [Symbol] The new visibility for dependency methods (:private or :public).
64
+ # @param visibility [Symbol] The new visibility for dependency methods (:private, :public, :protected).
62
65
  # @return [Symbol] The new default visibility for dependency methods.
63
- # @raise [ArgumentError] If the visibility is not :private or :public.
66
+ # @raise [ArgumentError] If the visibility is not :private, :public, or :protected.
64
67
  #
65
68
  def methods_visibility=(visibility)
66
- if (visibility != :private) && (visibility != :public)
67
- raise ArgumentError, "methods_visibility value must be :private or :public"
69
+ if (visibility != :private) && (visibility != :public) && (visibility != :protected)
70
+ raise ArgumentError, "methods_visibility value must be :private, :public, or :protected"
68
71
  end
69
72
 
70
73
  @methods_visibility = visibility
data/lib/diana.rb CHANGED
@@ -43,11 +43,45 @@ module Diana
43
43
  #
44
44
  def dependencies(deps)
45
45
  Module.new do
46
+ # Adds readable name to this anonymous module when showing `MyClass.ancestors`
46
47
  def self.inspect
47
48
  "<Diana.dependencies:#{object_id.to_s(16)}>"
48
49
  end
49
50
 
51
+ class_mod = Module.new do
52
+ # Adds readable name to this anonymous module when showing `MyClass.singleton_class.ancestors`
53
+ def self.inspect
54
+ "<Diana.inheritance:#{object_id.to_s(16)}>"
55
+ end
56
+
57
+ private
58
+
59
+ def inherited(subclass)
60
+ # When `inherited` is called for the first time, we add the parent's
61
+ # `@_diana_dependencies`. To avoid adding dependencies from
62
+ # ancestors in the `super` call, we check if `@_diana_dependencies`
63
+ # is already defined.
64
+ unless subclass.instance_variable_defined?(:@_diana_dependencies)
65
+ subclass.include Diana.dependencies(@_diana_dependencies)
66
+ end
67
+
68
+ super
69
+ end
70
+ end
71
+
50
72
  define_singleton_method(:included) do |base|
73
+ # Adds .inherited method
74
+ base.extend(class_mod)
75
+
76
+ #
77
+ # Merging dependencies allows to add dependencies multiple times
78
+ #
79
+ # Example:
80
+ # class MyClass
81
+ # include Diana.dependencies(foo: 'foo')
82
+ # include Diana.dependencies(bar: 'bar')
83
+ # end
84
+ #
51
85
  merged_deps =
52
86
  if base.instance_variable_defined?(:@_diana_dependencies)
53
87
  base.instance_variable_get(:@_diana_dependencies).merge!(deps)
@@ -55,6 +89,10 @@ module Diana
55
89
  base.instance_variable_set(:@_diana_dependencies, deps.dup)
56
90
  end
57
91
 
92
+ # Add initialize method
93
+ # Instance variables are set only for not-null dependencies.
94
+ # Using class_eval is slower to define the method, yet it provides the
95
+ # benefit of executing faster than if it were defined using define_method.
58
96
  class_eval(<<~INITIALIZE, __FILE__, __LINE__ + 1)
59
97
  def initialize(#{merged_deps.each_key.map { |dependency| "#{dependency}: nil" }.join(", ")})
60
98
  #{merged_deps.each_key.map { |dependency| " @#{dependency} = #{dependency} if #{dependency}" }.join("\n")}
@@ -62,13 +100,16 @@ module Diana
62
100
  INITIALIZE
63
101
  end
64
102
 
103
+ # Add dependencies attribute readers and set their visibility.
104
+ # Using class_eval is slower to define the method, yet it provides the
105
+ # benefit of executing faster than if it were defined using define_method.
65
106
  deps.each_key do |dependency|
66
107
  class_eval(<<~ATTR_READER, __FILE__, __LINE__ + 1)
67
108
  def #{dependency}
68
109
  @#{dependency} ||= Diana.resolve(self.class.instance_variable_get(:@_diana_dependencies)[:#{dependency}])
69
110
  end
70
111
 
71
- private :#{dependency} if Diana.methods_visibility == :private
112
+ #{Diana.methods_visibility} :#{dependency}
72
113
  ATTR_READER
73
114
  end
74
115
  end
metadata CHANGED
@@ -1,22 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: diana
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey Glushkov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-12-17 00:00:00.000000000 Z
11
+ date: 2024-12-18 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
- This module offers a DSL designed for the lazy resolution of dependency injections.
15
-
16
- It facilitates efficient and deferred initialization of dependencies,
17
- ensuring that resources are only allocated when necessary.
18
-
19
- This approach optimizes performance of application.
14
+ Lazy Dependency Injection.
15
+ Dependencies are allocated only when needed, optimizing performance.
20
16
  email:
21
17
  - aglushkov@shakuro.com
22
18
  executables: []