diana 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|