diana 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +36 -9
- data/VERSION +1 -1
- data/lib/diana/config.rb +7 -4
- data/lib/diana.rb +42 -1
- metadata +4 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a42ecda41893911ab6703f513107d3789a191d1a378adf3b4c598816a0cd6ab
|
4
|
+
data.tar.gz: 40fe0dc12e4b06ffb4250e16d9bb2036936fb1fbf219e5dd0a2c55ea4e808919
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db8f596619a96ee92d7c10e893288213cb8697f8e4b4e6523f54565c4e1a8690f713a8afb323de0d46ac1d193891481c0e15772791a58aa28350615fc4798ff0
|
7
|
+
data.tar.gz: 6cb63290c78aa3f332fdb63f996b64ce9b81e6b27e284a8ea7513d6d0eb2616006f376b915ffb473fcc810900b994633a29665d30eb82ebbfe442ac3e379b64b
|
data/README.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
[](https://badge.fury.io/rb/diana)
|
2
|
+
[](https://github.com/aglushkov/diana/actions/workflows/main.yml)
|
3
|
+
[](https://codeclimate.com/github/aglushkov/diana/test_coverage)
|
4
|
+
[](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
|
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
|
153
|
-
|
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.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
|
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 :
|
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 :
|
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
|
-
|
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.
|
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-
|
11
|
+
date: 2024-12-18 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |
|
14
|
-
|
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: []
|