diana 0.1.1 → 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 +31 -9
- data/VERSION +1 -1
- data/lib/diana/config.rb +7 -4
- data/lib/diana.rb +42 -1
- metadata +6 -4
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
@@ -66,13 +66,30 @@ By default, dependency methods are **private**. You can change this behavior by
|
|
66
66
|
configuring the Diana module:
|
67
67
|
|
68
68
|
```ruby
|
69
|
-
Diana.methods_visibility = :public
|
69
|
+
Diana.methods_visibility = :public # private, public, protected
|
70
70
|
```
|
71
71
|
|
72
|
-
Using public methods can be more convenient in tests, allowing you to access
|
73
|
-
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
|
74
74
|
entirely. This approach helps ensure you are testing the correct dependency.
|
75
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
|
+
|
76
93
|
## How it works
|
77
94
|
|
78
95
|
- **Dependency Storage**: The `@_diana_dependencies` class variable holds the
|
@@ -102,17 +119,26 @@ class SomeClass
|
|
102
119
|
bar: proc { Bar.new }
|
103
120
|
}
|
104
121
|
|
122
|
+
# handles dependency injection
|
105
123
|
def initialize(foo: nil, bar: nil)
|
106
124
|
@foo = foo if foo
|
107
125
|
@bar = bar if bar
|
108
126
|
end
|
109
127
|
|
128
|
+
# handles inheritance
|
129
|
+
def self.inherited(subclass)
|
130
|
+
subclass.include Diana.dependencies(@_diana_dependencies)
|
131
|
+
super
|
132
|
+
end
|
133
|
+
|
110
134
|
private
|
111
135
|
|
136
|
+
# handles lazy `foo` resolution
|
112
137
|
def foo
|
113
138
|
@foo ||= Diana.resolve(self.class.instance_variable_get(:@_diana_dependencies)[:foo])
|
114
139
|
end
|
115
140
|
|
141
|
+
# handles lazy `bar` resolution
|
116
142
|
def bar
|
117
143
|
@bar ||= Diana.resolve(self.class.instance_variable_get(:@_diana_dependencies)[:bar])
|
118
144
|
end
|
@@ -154,12 +180,8 @@ SomeClass.include Diana.dependencies(foo: 'utils.foo') # => DI_CONTAINER['utils.
|
|
154
180
|
method, it will take precedence. In such cases, ensure you include a
|
155
181
|
`super(**deps)` call to override dependencies if needed.
|
156
182
|
|
157
|
-
- We
|
158
|
-
|
159
|
-
optimal performance.
|
160
|
-
|
161
|
-
- Dependencies defined in a parent class are not accessible in nested classes.
|
162
|
-
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.
|
163
185
|
|
164
186
|
These limitations ensure that the gem remains predictable and performant,
|
165
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,16 +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
|
-
description:
|
13
|
+
description: |
|
14
|
+
Lazy Dependency Injection.
|
15
|
+
Dependencies are allocated only when needed, optimizing performance.
|
14
16
|
email:
|
15
17
|
- aglushkov@shakuro.com
|
16
18
|
executables: []
|
@@ -26,7 +28,7 @@ homepage: https://github.com/aglushkov/diana
|
|
26
28
|
licenses:
|
27
29
|
- MIT
|
28
30
|
metadata:
|
29
|
-
|
31
|
+
homepage_uri: https://github.com/aglushkov/diana
|
30
32
|
changelog_uri: https://github.com/aglushkov/diana/blob/master/CHANGELOG.md
|
31
33
|
post_install_message:
|
32
34
|
rdoc_options: []
|