hypo 0.7.0 → 0.8.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 +88 -29
- data/lib/hypo/component.rb +8 -14
- data/lib/hypo/container.rb +16 -19
- data/lib/hypo/container_error.rb +0 -3
- data/lib/hypo/instance.rb +7 -4
- data/lib/hypo/lifetime/scope.rb +33 -0
- data/lib/hypo/{life_cycle → lifetime}/singleton.rb +6 -2
- data/lib/hypo/lifetime/transient.rb +13 -0
- data/lib/hypo/lifetime_friendly.rb +11 -0
- data/lib/hypo/scope_friendly.rb +15 -0
- data/lib/hypo/version.rb +1 -1
- metadata +7 -5
- data/lib/hypo/chainable.rb +0 -7
- data/lib/hypo/life_cycle/transient.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f95c8eb6eb6a8fdcef40c219b52f93ef7f019e57
|
4
|
+
data.tar.gz: 3026722dd03cdb35709b13c0325b72fd248c3841
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea62c01f490442eb5456e23294dda1559b2c96837e85c3f04fe9a2aa4b2b263f24fb16c5c086d3171bd67176f0c94041db39883039d14325a236b064f9bf5c23
|
7
|
+
data.tar.gz: 1f26a3b5bedefd8a8ab10a5527e20228769e910402686519dd291c55b461bcd88d5a389eba52595dd170fcb3d4abd34279310e6c26d96e4765fa1d450ce22dce
|
data/README.md
CHANGED
@@ -62,7 +62,7 @@ and if you registered both of them, you can do:
|
|
62
62
|
# user.company is resolved as well
|
63
63
|
```
|
64
64
|
|
65
|
-
Sometimes you're not able to manage a type
|
65
|
+
Sometimes you're not able to manage a type lifetime, i.e. when you use 3rd-party static stuff, like:
|
66
66
|
```ruby
|
67
67
|
class DB
|
68
68
|
def self.connect
|
@@ -74,37 +74,21 @@ In that case you can register an instance instead of a type:
|
|
74
74
|
```ruby
|
75
75
|
connection = DB.connect
|
76
76
|
container.register_instance(connection, :connection)
|
77
|
-
```
|
78
|
-
|
79
|
-
|
77
|
+
```
|
78
|
+
|
79
|
+
## Advanced Usage
|
80
|
+
### Component Lifetime
|
81
|
+
By default all registered components have lifetime Hypo::Lifetime::Transient (:transient).
|
80
82
|
It means, every time when you resolve a component Hypo returns new instance of its type.
|
81
83
|
If you wanna change this behavior then you can replace lifetime strategy.
|
82
|
-
Out of the box Hypo provides Hypo::Singleton (:singleton) strategy, you can use it when register a component:
|
84
|
+
Out of the box Hypo provides Hypo::Lifetime::Singleton (:singleton) strategy, you can use it when register a component:
|
83
85
|
|
84
86
|
```ruby
|
85
|
-
container.register(User).
|
87
|
+
container.register(User).using_lifetime(:singleton)
|
86
88
|
```
|
87
|
-
Actually you can implement your own life cycle,
|
88
|
-
i.e. makes sense to think about HttpRequest strategy for your web applications.
|
89
|
-
|
90
|
-
**Instances support only :singleton life cycle.**
|
91
|
-
|
92
|
-
Sometimes you need to manage a component life cycle manually. Especially it can be useful for "instances".
|
93
|
-
For example, you're going to inject new instance of request parameters every http request in your web application:
|
94
|
-
|
95
|
-
```ruby
|
96
|
-
# somewhere in Rack application
|
97
|
-
# ...
|
98
|
-
query_string = env['QUERY_STRING']
|
99
|
-
container.register_instance(query_string, :query_string)
|
100
|
-
|
101
|
-
# handle the request
|
102
89
|
|
103
|
-
|
104
|
-
|
105
|
-
```
|
106
|
-
|
107
|
-
Hypo resolves components with different life cycle strategies independently.
|
90
|
+
#### Lifetime compatibility
|
91
|
+
Hypo resolves components with different lifetime strategies independently.
|
108
92
|
In other words you can inject a dependency with less lifespan than acceptor type. I.e.:
|
109
93
|
|
110
94
|
```ruby
|
@@ -117,14 +101,89 @@ class B
|
|
117
101
|
end
|
118
102
|
|
119
103
|
|
120
|
-
container.register(A, :type_a).
|
121
|
-
container.register(B, :type_b).
|
104
|
+
container.register(A, :type_a).using_lifetime(:transient)
|
105
|
+
container.register(B, :type_b).using_lifetime(:singleton)
|
122
106
|
|
107
|
+
container.resolve(:type_b)
|
123
108
|
```
|
124
109
|
|
125
110
|
According to :transient strategy every time when you try to resolve a singleton
|
126
111
|
you retrieve exactly the same instance of the singleton **but with new instance** of transient dependency.
|
127
112
|
|
113
|
+
#### Custom Lifetime
|
114
|
+
Actually you can implement your own lifetime,
|
115
|
+
i.e. makes sense to think about HttpRequest strategy for your web applications. You can do that using "add_lifetime" method:
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
# somewhere in Rack application: application initialization
|
119
|
+
lifetime = Lifetime::Request.new
|
120
|
+
container.add_lifetime(lifetime, :request)
|
121
|
+
```
|
122
|
+
|
123
|
+
A class of new lifetime must respond to "instance" method. This method just a factory method which creates new instance according to your strategy. For example, Lifetime::Request could cache instanes of a components during http request lifespan. Take a look to [:singleton implementation](https://github.com/cylon-v/hypo/blob/master/lib/hypo/lifetime/singleton.rb). You can manually purge internal state of components registry according your strategy. In case of http-request lifetime you could clean up it right after request has done:
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
# somewhere in Rack application: application initialization
|
127
|
+
# ...
|
128
|
+
container
|
129
|
+
.register(SQLTransation, :transaction)
|
130
|
+
.using_lifetime(:request)
|
131
|
+
```
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
# somewhere in Rack application: request handling
|
135
|
+
container.register_instance(query_string, :query_string)
|
136
|
+
|
137
|
+
# handle the request
|
138
|
+
# ...
|
139
|
+
|
140
|
+
lifetime.purge
|
141
|
+
```
|
142
|
+
|
143
|
+
#### Lifetime :scope
|
144
|
+
For most of cases when you need to bind dependency lifetime to lifetime of another item of your application
|
145
|
+
you can use Hypo::Lifetime::Scope (:scope) strategy.
|
146
|
+
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
# somewhere in Rack application: application initialization
|
150
|
+
# ...
|
151
|
+
container
|
152
|
+
.register(SQLTransation, :transaction)
|
153
|
+
.using_lifetime(:scope)
|
154
|
+
.bound_to(:request) # you can use symbol
|
155
|
+
```
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
# somewhere in Rack application: request handling
|
159
|
+
container
|
160
|
+
.register_instance(request, :request)
|
161
|
+
.using_lifetime(:scope)
|
162
|
+
.bound_to(request) # an object
|
163
|
+
|
164
|
+
# handle the request
|
165
|
+
# ...
|
166
|
+
|
167
|
+
lifetime.purge(request)
|
168
|
+
```
|
169
|
+
|
170
|
+
### Remove components
|
171
|
+
Sometimes you need to manage a component lifetime manually. Especially it can be useful for "instances".
|
172
|
+
For example, you're going to inject new instance of request parameters every http request in your web application:
|
173
|
+
|
174
|
+
```ruby
|
175
|
+
# somewhere in Rack application: request handling
|
176
|
+
# ...
|
177
|
+
query_string = env['QUERY_STRING']
|
178
|
+
container.register_instance(query_string, :query_string)
|
179
|
+
|
180
|
+
# handle the request
|
181
|
+
# ...
|
182
|
+
|
183
|
+
container.remove(:query_string)
|
184
|
+
# ...
|
185
|
+
```
|
186
|
+
|
128
187
|
## Development
|
129
188
|
|
130
189
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -137,4 +196,4 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERN
|
|
137
196
|
|
138
197
|
## License
|
139
198
|
|
140
|
-
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
199
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/lib/hypo/component.rb
CHANGED
@@ -1,22 +1,24 @@
|
|
1
|
-
require 'hypo/
|
2
|
-
require 'hypo/
|
1
|
+
require 'hypo/lifetime/transient'
|
2
|
+
require 'hypo/scope_friendly'
|
3
|
+
require 'hypo/lifetime_friendly'
|
3
4
|
|
4
5
|
module Hypo
|
5
6
|
class Component
|
6
|
-
include
|
7
|
+
include ScopeFriendly
|
8
|
+
include LifetimeFriendly
|
7
9
|
|
8
|
-
attr_reader :name, :type, :container, :
|
10
|
+
attr_reader :name, :type, :container, :lifetime, :scope
|
9
11
|
|
10
12
|
def initialize(type, container, name = nil)
|
11
13
|
@container = container
|
12
14
|
@type = type
|
13
15
|
@name = name || type.name.gsub(/(.)([A-Z](?=[a-z]))/, '\1_\2').delete('::').downcase.to_sym
|
14
|
-
@
|
16
|
+
@lifetime = container.lifetimes[:transient]
|
15
17
|
@dependency_names = @type.instance_method(:initialize).parameters.map {|p| p[1]}
|
16
18
|
end
|
17
19
|
|
18
20
|
def instance
|
19
|
-
instance = @
|
21
|
+
instance = @lifetime.instance(self)
|
20
22
|
|
21
23
|
@dependency_names.each do |dependency|
|
22
24
|
instance.instance_variable_set "@#{dependency}".to_sym, @container.resolve(dependency)
|
@@ -28,13 +30,5 @@ module Hypo
|
|
28
30
|
def dependencies
|
29
31
|
@dependency_names.map { |dependency| @container.resolve(dependency) }
|
30
32
|
end
|
31
|
-
|
32
|
-
def use_life_cycle(life_cycle)
|
33
|
-
@life_cycle = container.life_cycles[life_cycle]
|
34
|
-
|
35
|
-
self
|
36
|
-
end
|
37
|
-
|
38
|
-
alias using_life_cycle use_life_cycle
|
39
33
|
end
|
40
34
|
end
|
data/lib/hypo/container.rb
CHANGED
@@ -1,18 +1,19 @@
|
|
1
1
|
require 'hypo/container_error'
|
2
2
|
require 'hypo/component'
|
3
3
|
require 'hypo/instance'
|
4
|
-
require 'hypo/
|
5
|
-
require 'hypo/
|
4
|
+
require 'hypo/lifetime/transient'
|
5
|
+
require 'hypo/lifetime/singleton'
|
6
6
|
|
7
7
|
module Hypo
|
8
8
|
class Container
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :lifetimes
|
10
10
|
|
11
11
|
def initialize
|
12
12
|
@components = {}
|
13
|
-
@
|
14
|
-
|
15
|
-
|
13
|
+
@lifetimes = {}
|
14
|
+
add_lifetime(Lifetime::Transient.new, :transient)
|
15
|
+
add_lifetime(Lifetime::Singleton.new, :singleton)
|
16
|
+
add_lifetime(Lifetime::Scope.new, :scope)
|
16
17
|
register_instance self, :container
|
17
18
|
end
|
18
19
|
|
@@ -24,11 +25,11 @@ module Hypo
|
|
24
25
|
|
25
26
|
component = Component.new(item, self, name)
|
26
27
|
|
27
|
-
|
28
|
-
|
28
|
+
unless @components.key?(component.name)
|
29
|
+
@components[component.name] = component
|
29
30
|
end
|
30
31
|
|
31
|
-
@components[component.name]
|
32
|
+
@components[component.name]
|
32
33
|
end
|
33
34
|
|
34
35
|
def register_instance(item, name)
|
@@ -37,12 +38,12 @@ module Hypo
|
|
37
38
|
'If you wanna register a type please use method "register".'
|
38
39
|
end
|
39
40
|
|
40
|
-
|
41
|
-
|
41
|
+
unless @components.key?(name)
|
42
|
+
instance = Instance.new(item, self, name)
|
43
|
+
@components[name] = instance
|
42
44
|
end
|
43
45
|
|
44
|
-
|
45
|
-
@components[name] = instance
|
46
|
+
@components[name]
|
46
47
|
end
|
47
48
|
|
48
49
|
def resolve(name)
|
@@ -53,12 +54,8 @@ module Hypo
|
|
53
54
|
@components[name].instance
|
54
55
|
end
|
55
56
|
|
56
|
-
def
|
57
|
-
@
|
58
|
-
end
|
59
|
-
|
60
|
-
def add_life_cycle(life_cycle, name)
|
61
|
-
@life_cycles[name] = life_cycle
|
57
|
+
def add_lifetime(lifetime, name)
|
58
|
+
@lifetimes[name] = lifetime
|
62
59
|
|
63
60
|
self
|
64
61
|
end
|
data/lib/hypo/container_error.rb
CHANGED
data/lib/hypo/instance.rb
CHANGED
@@ -1,21 +1,24 @@
|
|
1
|
-
require 'hypo/
|
1
|
+
require 'hypo/scope_friendly'
|
2
|
+
require 'hypo/lifetime_friendly'
|
2
3
|
|
3
4
|
module Hypo
|
4
5
|
class Instance
|
5
|
-
include
|
6
|
+
include ScopeFriendly
|
7
|
+
include LifetimeFriendly
|
6
8
|
|
7
|
-
attr_reader :name, :container
|
9
|
+
attr_reader :name, :container, :scope, :object
|
8
10
|
|
9
11
|
def initialize(object, container, name)
|
10
12
|
raise ContainerError, 'Registered object should have a name' if name.nil?
|
11
13
|
|
12
14
|
@object = object
|
13
15
|
@container = container
|
16
|
+
@lifetime = container.lifetimes[:transient]
|
14
17
|
@name = name
|
15
18
|
end
|
16
19
|
|
17
20
|
def instance
|
18
|
-
@
|
21
|
+
@lifetime.instance(self)
|
19
22
|
end
|
20
23
|
end
|
21
24
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Hypo
|
2
|
+
module Lifetime
|
3
|
+
class Scope
|
4
|
+
def initialize
|
5
|
+
@instances = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def instance(component)
|
9
|
+
if component.scope.nil?
|
10
|
+
raise ContainerError, "Component \"#{component.name}\" must be bound to a scope" \
|
11
|
+
" according to Hupo::Lifetime::Scope lifetime strategy"
|
12
|
+
end
|
13
|
+
|
14
|
+
scope = component.scope.object_id
|
15
|
+
@instances[scope] = {} unless @instances.key? scope
|
16
|
+
|
17
|
+
unless @instances[scope].key? component.name
|
18
|
+
if component.respond_to? :type
|
19
|
+
@instances[scope][component.name] = component.type.new(*component.dependencies)
|
20
|
+
else
|
21
|
+
@instances[scope][component.name] = component.object
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
@instances[scope][component.name]
|
26
|
+
end
|
27
|
+
|
28
|
+
def purge(scope)
|
29
|
+
@instances.delete scope.object_id
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Hypo
|
2
|
-
module
|
2
|
+
module Lifetime
|
3
3
|
class Singleton
|
4
4
|
def initialize
|
5
5
|
@instances = {}
|
@@ -7,7 +7,11 @@ module Hypo
|
|
7
7
|
|
8
8
|
def instance(component)
|
9
9
|
unless @instances.key? component.name
|
10
|
-
|
10
|
+
if component.respond_to? :type
|
11
|
+
@instances[component.name] = component.type.new(*component.dependencies)
|
12
|
+
else
|
13
|
+
@instances[component.name] = component.object
|
14
|
+
end
|
11
15
|
end
|
12
16
|
|
13
17
|
@instances[component.name]
|
data/lib/hypo/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hypo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vladimir Kalinkin
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-09-
|
11
|
+
date: 2017-09-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -70,13 +70,15 @@ files:
|
|
70
70
|
- bin/setup
|
71
71
|
- hypo.gemspec
|
72
72
|
- lib/hypo.rb
|
73
|
-
- lib/hypo/chainable.rb
|
74
73
|
- lib/hypo/component.rb
|
75
74
|
- lib/hypo/container.rb
|
76
75
|
- lib/hypo/container_error.rb
|
77
76
|
- lib/hypo/instance.rb
|
78
|
-
- lib/hypo/
|
79
|
-
- lib/hypo/
|
77
|
+
- lib/hypo/lifetime/scope.rb
|
78
|
+
- lib/hypo/lifetime/singleton.rb
|
79
|
+
- lib/hypo/lifetime/transient.rb
|
80
|
+
- lib/hypo/lifetime_friendly.rb
|
81
|
+
- lib/hypo/scope_friendly.rb
|
80
82
|
- lib/hypo/version.rb
|
81
83
|
homepage: https://github.com/cylon-v/hypo
|
82
84
|
licenses:
|
data/lib/hypo/chainable.rb
DELETED