smart_container 0.2.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8724a62bc6365606a5b796bac2899c5ddba96c179548878f12e324291b6862a3
4
- data.tar.gz: 769c81de665be07979bbeb8026398ff6cf6e8885159cbe6ab1aff52d07a64095
3
+ metadata.gz: 3e536932a27a4bcf2ded83ea750978459e8dbf6a34ea35dfac8ef7008db3240d
4
+ data.tar.gz: 2566b21ef936d31bab5ff8eb6d6d51cd6510db97acf62d9dc55992915094a962
5
5
  SHA512:
6
- metadata.gz: b1f138811f9b345b2f735b4f90e06d44ef83b955358e78b786becb9e2d73144de113cbfc0b08d765c245a7c1a4889bba8f5c3ed63219f28fa312554f8834d1ae
7
- data.tar.gz: 69da3a3c4ac8b5d34243a617dff6614654a9c116ef7270f7f313127dafe973a2d102143df21fcad222bb9d94499606f983835b74e711a9c823934c58c8bc42a2
6
+ metadata.gz: d1c443404d20ac8c6f61ea8bf6d37db0acd4e1131626ca00c75bf26f695262f275ee6814aab3ecd4b0a4c17b4d3f50ae449abb6932e658f0cc325e3e87cba8ea
7
+ data.tar.gz: 587dac41ae390e811802ca6c5a6a4610d81d4b7cecd1d5de224a409b1724edf180c98d78acc1a81cd9bc06010343d5a42f769acd219d12d395bef06c4f9420c6
@@ -5,11 +5,11 @@ inherit_gem:
5
5
  - lib/rubocop.rspec.yml
6
6
 
7
7
  AllCops:
8
- TargetRubyVersion: 2.4.9
8
+ TargetRubyVersion: 2.7.1
9
9
  Include:
10
10
  - lib/**/*.rb
11
11
  - spec/**/*.rb
12
12
  - Gemfile
13
13
  - Rakefile
14
- - siege.gemspec
14
+ - smart_container.gemspec
15
15
  - bin/console
@@ -1,28 +1,22 @@
1
1
  ---
2
2
  language: ruby
3
3
  cache: bundler
4
+ os: linux
5
+ dist: xenial
4
6
  before_install:
5
- - gem install bundler -v 2.1.2
7
+ - gem install bundler
6
8
  script:
7
9
  - bundle exec rake rubocop
8
10
  - bundle exec rake rspec
9
11
  jobs:
10
12
  fast_finish: true
11
13
  include:
12
- - rvm: 2.4.9
13
- os: [linux, osx]
14
- - rvm: 2.5.7
15
- os: [linux, osx]
16
- - rvm: 2.6.5
17
- os: [linux, osx]
18
- - rvm: 2.7.0
19
- os: [linux, osx]
14
+ - rvm: 2.4.10
15
+ - rvm: 2.5.8
16
+ - rvm: 2.6.6
17
+ - rvm: 2.7.1
20
18
  - rvm: ruby-head
21
- os: [linux, osx]
22
19
  - rvm: jruby-head
23
- os: [linux, osx]
24
20
  allow_failures:
25
21
  - rvm: ruby-head
26
- os: [linux, osx]
27
22
  - rvm: jruby-head
28
- os: [linux, osx]
@@ -1,25 +1,40 @@
1
1
  # Changelog
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
- ## [0.2.0] - 202-01-05
4
+ ## [0.7.0] - 2020-06-20
5
+ ### Added
6
+ - `SmartCore::Container.define {}` - an ability to avoid explicit class definition that allows
7
+ to create container instances from an anonymous container class imidietly
8
+
9
+ ## [0.6.0] - 2020-01-12
10
+ ### Added
11
+ - Missing memoization flag `:memoize` for runtime-based dependency registration:
12
+ - `memoize: false` by default;
13
+ - signature: `SmartCore::Container#register(dependency_name, memoize: false, &dependency)`
14
+
15
+ ## [0.5.0] - 2020-01-07
16
+ ### Added
17
+ - Key predicates (`#key?(key)`, `#dependency?(path, memoized: nil/true/false)`, `#namespace?(path)`);
18
+
19
+ ## [0.4.0] - 2020-01-06
20
+ ### Added
21
+ - `#keys(all_variants: false)` - return a list of dependency keys
22
+ (`all_variants: true` is mean "including namespace kaeys");
23
+ - `#each_dependency(yield_all: false) { |key, value| }` - iterate over conteiner's dependencies
24
+ (`yield_all: true` will include nested containers to iteration process);
25
+ ### Fixed
26
+ - `SmartCore::Container::ResolvingError` class has incorrect message attribute name;
27
+
28
+ ## [0.3.0] - 2020-01-05
29
+ ### Changed
30
+ - Dependency resolving is not memoized by default (previously: totally memoized 😱);
31
+
32
+ ## [0.2.0] - 2020-01-05
5
33
  ### Changed
6
34
  - (Private API (`SmartCore::Container::RegistryBuilder`)) improved semantics:
7
35
  - `build_state` is renamed to `initialise`;
8
36
  - `build_definitions` is renamed to `define`;
9
- - (Public API) Support for memoized dependencies (all dependencies are memoized by default)
10
- ```ruby
11
- class MyContainer < SmartCore::Container
12
- namespace(:some_naespace) do
13
- # memoized by default
14
- register(:random_number) { rand(1000) }
15
- # explicit memoization
16
- register(:random_number, memoized: true) { rand(1000) }
17
-
18
- # register non-memoizable dependency
19
- register(:random_number, memoized: false) { rand(1000) }
20
- end
21
- end
22
- ```
37
+ - (Public API) Support for memoized dependencies (all dependencies are memoized by default);
23
38
 
24
39
  ## [0.1.0] - 2020-01-02
25
40
 
@@ -1,83 +1,98 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- smart_container (0.2.0)
5
- smart_engine (~> 0.2)
4
+ smart_container (0.6.0)
5
+ smart_engine (~> 0.5)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- armitage-rubocop (0.78.0)
11
- rubocop (= 0.78.0)
10
+ activesupport (6.0.2.2)
11
+ concurrent-ruby (~> 1.0, >= 1.0.2)
12
+ i18n (>= 0.7, < 2)
13
+ minitest (~> 5.1)
14
+ tzinfo (~> 1.1)
15
+ zeitwerk (~> 2.2)
16
+ armitage-rubocop (0.81.0)
17
+ rubocop (= 0.81.0)
12
18
  rubocop-performance (= 1.5.2)
13
- rubocop-rails (= 2.4.1)
14
- rubocop-rake (= 0.5.0)
15
- rubocop-rspec (= 1.37.1)
19
+ rubocop-rails (= 2.5.2)
20
+ rubocop-rake (= 0.5.1)
21
+ rubocop-rspec (= 1.38.1)
16
22
  ast (2.4.0)
17
23
  coderay (1.1.2)
24
+ concurrent-ruby (1.1.6)
18
25
  diff-lcs (1.3)
19
26
  docile (1.3.2)
27
+ i18n (1.8.2)
28
+ concurrent-ruby (~> 1.0)
20
29
  jaro_winkler (1.5.4)
21
- json (2.3.0)
22
- method_source (0.9.2)
30
+ method_source (1.0.0)
31
+ minitest (5.14.0)
23
32
  parallel (1.19.1)
24
- parser (2.7.0.1)
33
+ parser (2.7.1.0)
25
34
  ast (~> 2.4.0)
26
- pry (0.12.2)
27
- coderay (~> 1.1.0)
28
- method_source (~> 0.9.0)
29
- rack (2.0.8)
35
+ pry (0.13.1)
36
+ coderay (~> 1.1)
37
+ method_source (~> 1.0)
38
+ rack (2.2.2)
30
39
  rainbow (3.0.0)
31
40
  rake (13.0.1)
41
+ rexml (3.2.4)
32
42
  rspec (3.9.0)
33
43
  rspec-core (~> 3.9.0)
34
44
  rspec-expectations (~> 3.9.0)
35
45
  rspec-mocks (~> 3.9.0)
36
46
  rspec-core (3.9.1)
37
47
  rspec-support (~> 3.9.1)
38
- rspec-expectations (3.9.0)
48
+ rspec-expectations (3.9.1)
39
49
  diff-lcs (>= 1.2.0, < 2.0)
40
50
  rspec-support (~> 3.9.0)
41
51
  rspec-mocks (3.9.1)
42
52
  diff-lcs (>= 1.2.0, < 2.0)
43
53
  rspec-support (~> 3.9.0)
44
54
  rspec-support (3.9.2)
45
- rubocop (0.78.0)
55
+ rubocop (0.81.0)
46
56
  jaro_winkler (~> 1.5.1)
47
57
  parallel (~> 1.10)
48
- parser (>= 2.6)
58
+ parser (>= 2.7.0.1)
49
59
  rainbow (>= 2.2.2, < 4.0)
60
+ rexml
50
61
  ruby-progressbar (~> 1.7)
51
- unicode-display_width (>= 1.4.0, < 1.7)
62
+ unicode-display_width (>= 1.4.0, < 2.0)
52
63
  rubocop-performance (1.5.2)
53
64
  rubocop (>= 0.71.0)
54
- rubocop-rails (2.4.1)
65
+ rubocop-rails (2.5.2)
66
+ activesupport
55
67
  rack (>= 1.1)
56
68
  rubocop (>= 0.72.0)
57
- rubocop-rake (0.5.0)
69
+ rubocop-rake (0.5.1)
58
70
  rubocop
59
- rubocop-rspec (1.37.1)
71
+ rubocop-rspec (1.38.1)
60
72
  rubocop (>= 0.68.1)
61
73
  ruby-progressbar (1.10.1)
62
- simplecov (0.17.1)
74
+ simplecov (0.18.5)
63
75
  docile (~> 1.1)
64
- json (>= 1.8, < 3)
65
- simplecov-html (~> 0.10.0)
66
- simplecov-html (0.10.2)
67
- smart_engine (0.2.0)
68
- unicode-display_width (1.6.0)
76
+ simplecov-html (~> 0.11)
77
+ simplecov-html (0.12.2)
78
+ smart_engine (0.5.0)
79
+ thread_safe (0.3.6)
80
+ tzinfo (1.2.7)
81
+ thread_safe (~> 0.1)
82
+ unicode-display_width (1.7.0)
83
+ zeitwerk (2.3.0)
69
84
 
70
85
  PLATFORMS
71
86
  ruby
72
87
 
73
88
  DEPENDENCIES
74
- armitage-rubocop (~> 0.78)
89
+ armitage-rubocop (~> 0.81)
75
90
  bundler (~> 2.1)
76
- pry (~> 0.12)
91
+ pry (~> 0.13)
77
92
  rake (~> 13.0)
78
93
  rspec (~> 3.9)
79
- simplecov (~> 0.17)
94
+ simplecov (~> 0.18)
80
95
  smart_container!
81
96
 
82
97
  BUNDLED WITH
83
- 2.1.2
98
+ 2.1.4
data/README.md CHANGED
@@ -24,35 +24,214 @@ require 'smart_core/container'
24
24
 
25
25
  ## Synopsis (demo)
26
26
 
27
+ - container class creation:
28
+
27
29
  ```ruby
28
30
  class Container < SmartCore::Container
29
31
  namespace(:database) do # support for namespaces
30
- register(:resolver) { SomeDatabaseResolver.new } # dependency registration
32
+ register(:resolver, memoize: true) { SomeDatabaseResolver.new } # dependency registration
31
33
 
32
34
  namespace(:cache) do # support for nested naespaces
33
- register(:memcached) { MemcachedClient.new }
34
- register(:redis) { RedisClient.new }
35
+ register(:memcached, memoize: true) { MemcachedClient.new }
36
+ register(:redis, memoize: true) { RedisClient.new }
35
37
  end
36
38
  end
37
39
 
38
40
  # root dependencies
39
- register(:logger) { Logger.new(STDOUT) }
41
+ register(:logger, memoize: true) { Logger.new(STDOUT) }
40
42
 
41
- # do not memoize registered object
42
- register(:random, memoize: false) { rand(1000) }
43
+ # dependencies are not memoized by default (memoize: false)
44
+ register(:random) { rand(1000) }
43
45
  end
46
+ ```
47
+
48
+ - mixin:
49
+
50
+ ```ruby
51
+ # full documentaiton is coming;
52
+
53
+ class Application
54
+ include SmartCore::Container::Mixin
44
55
 
56
+ dependencies do
57
+ namespace(:database) do
58
+ register(:cache) { MemcachedClient.new }
59
+ end
60
+ end
61
+ end
62
+
63
+ # access:
64
+ Application.container
65
+ Application.new.container # NOTE: the same instance as Application.container
66
+ ```
67
+
68
+ - container instantiation and dependency resolving:
69
+
70
+ ```ruby
45
71
  container = Container.new # create container instance
72
+ ```
46
73
 
74
+ ```ruby
47
75
  container['database.resolver'] # => #<SomeDatabaseResolver:0x00007f0f0f1d6332>
48
76
  container['database.cache.redis'] # => #<RedisClient:0x00007f0f0f1d0158>
49
77
  container['logger'] # => #<Logger:0x00007f5f0f2f0158>
50
78
 
79
+ container.resolve('logger') # #resolve(path) is an alias for #[](path)
80
+
51
81
  # non-memoized dependency
52
82
  container['random'] # => 352
53
83
  container['random'] # => 57
84
+
85
+ # trying to resolve a namespace as dependency
86
+ container['database'] # => SmartCore::Container::ResolvingError
87
+
88
+ # but you can fetch any depenendency type (internal containers and values) via #fetch
89
+ container.fetch('database') # => SmartCore::Container (nested container)
90
+ container.fetch('database.resolver') # => #<SomeDatabaseResolver:0x00007f0f0f1d6332>
91
+ ```
92
+
93
+ - runtime-level dependency/namespace registration:
94
+
95
+ ```ruby
96
+ container.namespace(:api) do
97
+ register(:provider) { GoogleProvider } # without memoization
98
+ end
99
+
100
+ container.register('game_api', memoize: true) { 'overwatch' } # with memoization
101
+
102
+ container['api.provider'] # => GoogleProvider
103
+ container['game_api'] # => 'overwatch'
104
+ ```
105
+
106
+ - container keys (dependency names):
107
+
108
+ ```ruby
109
+ # get dependnecy keys (only dependencies)
110
+ container.keys
111
+ # => result:
112
+ [
113
+ 'database.resolver',
114
+ 'database.cache.memcached',
115
+ 'database.cache.redis',
116
+ 'logger',
117
+ 'random'
118
+ ]
119
+ ```
120
+ ```ruby
121
+ # get all keys (namespaces and dependencies)
122
+ container.keys(all_variants: true)
123
+ # => result:
124
+ [
125
+ 'database', # namespace
126
+ 'database.resolver',
127
+ 'database.cache', # namespace
128
+ 'database.cache.memcached',
129
+ 'database.cache.redis',
130
+ 'logger',
131
+ 'random'
132
+ ]
54
133
  ```
55
134
 
135
+ - key predicates:
136
+ - `key?(key)` - has dependency or namespace?
137
+ - `namespace?(path)` - has namespace?
138
+ - `dependency?(path)` - has dependency?
139
+ - `dependency?(path, memoized: true)` - has memoized dependency?
140
+ - `dependency?(path, memoized: false)` - has non-memoized dependency?
141
+
142
+ ```ruby
143
+ container.key?('database') # => true
144
+ container.key?('database.cache.memcached') # => true
145
+
146
+ container.dependency?('database') # => false
147
+ container.dependency?('database.resolver') # => true
148
+
149
+ container.namespace?('database') # => true
150
+ container.namespace?('database.resolver') # => false
151
+
152
+ container.dependency?('database.resolver', memoized: true) # => true
153
+ container.dependency?('database.resolver', memoized: false) # => false
154
+
155
+ container.dependency?('random', memoized: true) # => false
156
+ container.dependency?('random', memoized: false) # => true
157
+ ```
158
+
159
+ - state freeze (`#freeze!`, `.#frozen?`):
160
+
161
+ ```ruby
162
+ # documentation is coming;
163
+ ```
164
+
165
+ - reloading (`#reload!):
166
+
167
+ ```ruby
168
+ # documentation is coming;
169
+ ```
170
+
171
+ - hash tree (`#hash_tree`, `#hash_tree(resolve_dependencies: true)`):
172
+
173
+ ```ruby
174
+ # documentation is coming`;
175
+ ```
176
+
177
+ - `SmartCore::Container.define` - avoid explicit class definition (allows to create container instance from an anonymous container class immidietly):
178
+
179
+ ```ruby
180
+ # - create from empty container class -
181
+
182
+ AppContainer = SmartCore::Container.define do
183
+ namespace :database do
184
+ register(:logger) { Logger.new }
185
+ end
186
+ end # => an instance of Class<SmartCore::Container>
187
+
188
+ AppContainer.resolve('database.logger') # => #<Logger:0x00007f5f0f2f0158>
189
+ AppContainer['database.logger'] # => #<Logger:0x00007f5f0f2f0158>
190
+ ```
191
+
192
+ ```ruby
193
+ # - create from another container class with a custom sub-definitions -
194
+
195
+ class BasicContainer < SmartCore::Container
196
+ namespace(:api) do
197
+ register(:client) { Kickbox.new }
198
+ end
199
+ end
200
+
201
+ AppContainer = BasicContainer.define do
202
+ register(:db_driver) { Sequel }
203
+ end
204
+ # --- or ---
205
+ AppContainer = SmartCore::Container.define(BasicContainer) do
206
+ register(:db_driver) { Sequel }
207
+ end
208
+
209
+ AppContainer['api.client'] # => #<Kickbox:0x00007f5f0f2f0158> (BasicContainer dependency)
210
+ AppContainer['db_driver'] # => Sequel (AppContainer dependency)
211
+ ```
212
+
213
+ ---
214
+
215
+ ## Roadmap
216
+
217
+ - support for instant dependency registration:
218
+
219
+ ```ruby
220
+ # common (dynamic) way:
221
+ register('dependency_name') { dependency_value }
222
+
223
+ # instant way:
224
+ register('dependency_name', dependency_value)
225
+ ```
226
+
227
+ - support for memoization ignorance during dependency resolving:
228
+
229
+ ```ruby
230
+ resolve('logger', :allocate) # Draft
231
+ ```
232
+
233
+ - container composition;
234
+
56
235
  ---
57
236
 
58
237
  ## Contributing
data/Rakefile CHANGED
@@ -4,6 +4,7 @@ require 'bundler/gem_tasks'
4
4
  require 'rspec/core/rake_task'
5
5
  require 'rubocop'
6
6
  require 'rubocop/rake_task'
7
+ require 'rubocop-rails'
7
8
  require 'rubocop-performance'
8
9
  require 'rubocop-rspec'
9
10
  require 'rubocop-rake'
@@ -11,8 +12,8 @@ require 'rubocop-rake'
11
12
  RuboCop::RakeTask.new(:rubocop) do |t|
12
13
  config_path = File.expand_path(File.join('.rubocop.yml'), __dir__)
13
14
  t.options = ['--config', config_path]
14
- t.requires << 'rubocop-performance'
15
15
  t.requires << 'rubocop-rspec'
16
+ t.requires << 'rubocop-performance'
16
17
  t.requires << 'rubocop-rake'
17
18
  end
18
19
 
@@ -20,6 +20,27 @@ module SmartCore
20
20
  require_relative 'container/dependency_resolver'
21
21
  require_relative 'container/mixin'
22
22
 
23
+ class << self
24
+ # @param initial_container_klass [Class<SmartCore::Container>]
25
+ # @param container_definitions [Block]
26
+ # @return [SmartCore::Container]
27
+ #
28
+ # @api public
29
+ # @since 0.7.0
30
+ def define(initial_container_klass = self, &container_definitions)
31
+ unless initial_container_klass <= SmartCore::Container
32
+ raise(SmartCore::Container::ArgumentError, <<~ERROR_MESSAGE)
33
+ Base class should be a type of SmartCore::Container
34
+ ERROR_MESSAGE
35
+ end
36
+
37
+ Class.new(initial_container_klass, &container_definitions).new
38
+ end
39
+ end
40
+
41
+ # @since 0.4.0
42
+ include ::Enumerable
43
+
23
44
  # @since 0.1.0
24
45
  include DefinitionDSL
25
46
 
@@ -44,8 +65,14 @@ module SmartCore
44
65
  #
45
66
  # @api public
46
67
  # @sicne 0.1.0
47
- def register(dependency_name, &dependency_definition)
48
- thread_safe { registry.register_dependency(dependency_name, &dependency_definition) }
68
+ def register(
69
+ dependency_name,
70
+ memoize: SmartCore::Container::Registry::DEFAULT_MEMOIZATION_BEHAVIOR,
71
+ &dependency_definition
72
+ )
73
+ thread_safe do
74
+ registry.register_dependency(dependency_name, memoize: memoize, &dependency_definition)
75
+ end
49
76
  end
50
77
 
51
78
  # @param namespace_name [String, Symbol]
@@ -102,6 +129,61 @@ module SmartCore
102
129
  thread_safe { build_registry! }
103
130
  end
104
131
 
132
+ # @option all_variants [Boolean]
133
+ # @return [Array<String>]
134
+ #
135
+ # @api public
136
+ # @since 0.4.0
137
+ def keys(all_variants: SmartCore::Container::Registry::DEFAULT_KEY_EXTRACTION_BEHAVIOUR)
138
+ thread_safe { registry.keys(all_variants: all_variants) }
139
+ end
140
+
141
+ # @param key [String, Symbol]
142
+ # @return [Boolean]
143
+ #
144
+ # @api public
145
+ # @since 0.5.0
146
+ def key?(key)
147
+ thread_safe { DependencyResolver.key?(self, key) }
148
+ end
149
+
150
+ # @param namespace_path [String, Symbol]
151
+ # @return [Boolean]
152
+ #
153
+ # @api public
154
+ # @since 0.5.0
155
+ def namespace?(namespace_path)
156
+ thread_safe { DependencyResolver.namespace?(self, namespace_path) }
157
+ end
158
+
159
+ # @param dependency_path [String, Symbol]
160
+ # @option memoized [NilClass, Boolean]
161
+ # @return [Boolean]
162
+ #
163
+ # @api public
164
+ # @since 0.5.0
165
+ def dependency?(dependency_path, memoized: nil)
166
+ thread_safe { DependencyResolver.dependency?(self, dependency_path, memoized: memoized) }
167
+ end
168
+
169
+ # @option yield_all [Boolean]
170
+ # @param block [Block]
171
+ # @yield [dependency_name, dependency_value]
172
+ # @yield_param dependency_name [String]
173
+ # @yield_param dependency_value [Any, SmartCore::Container]
174
+ # @return [Enumerable]
175
+ #
176
+ # @api public
177
+ # @since 0.4.0
178
+ def each_dependency(
179
+ yield_all: SmartCore::Container::Registry::DEFAULT_ITERATION_YIELD_BEHAVIOUR,
180
+ &block
181
+ )
182
+ thread_safe { registry.each_dependency(yield_all: yield_all, &block) }
183
+ end
184
+ alias_method :each, :each_dependency
185
+ alias_method :each_pair, :each_dependency
186
+
105
187
  # @option resolve_dependencies [Boolean]
106
188
  # @return [Hash<String|Symbol,SmartCore::Container::Entities::Base|Any>]
107
189
  #
@@ -69,8 +69,12 @@ class SmartCore::Container
69
69
  #
70
70
  # @api public
71
71
  # @since 0.1.0
72
- # @version 0.2.0
73
- def register(dependency_name, memoize: true, &dependency_definition)
72
+ # @version 0.3.0
73
+ def register(
74
+ dependency_name,
75
+ memoize: SmartCore::Container::Registry::DEFAULT_MEMOIZATION_BEHAVIOR,
76
+ &dependency_definition
77
+ )
74
78
  @__container_definition_lock__.thread_safe do
75
79
  DependencyCompatability::Definition.prevent_namespace_overlap!(self, dependency_name)
76
80
 
@@ -36,7 +36,7 @@ module SmartCore::Container::DefinitionDSL::Commands::Definition
36
36
  # @since 0.1.0
37
37
  # @version 0.2.0
38
38
  def call(registry)
39
- registry.register_dependency(dependency_name, memoize, &dependency_definition)
39
+ registry.register_dependency(dependency_name, memoize: memoize, &dependency_definition)
40
40
  end
41
41
 
42
42
  # @return [SmartCore::Container::DefinitionDSL::Commands::Definition::Register]
@@ -5,6 +5,12 @@
5
5
  module SmartCore::Container::DependencyResolver
6
6
  require_relative 'dependency_resolver/route'
7
7
 
8
+ # @return [String]
9
+ #
10
+ # @api private
11
+ # @since 0.4.0
12
+ PATH_PART_SEPARATOR = '.'
13
+
8
14
  class << self
9
15
  # @param container [SmartCore::Container]
10
16
  # @param dependency_path [String, Symbol]
@@ -20,6 +26,54 @@ module SmartCore::Container::DependencyResolver
20
26
  container.registry.resolve(dependency_path).reveal
21
27
  end
22
28
 
29
+ # @param container [SmartCore::Container]
30
+ # @param key [String, Symbol]
31
+ # @return [Boolean]
32
+ #
33
+ # @api private
34
+ # @since 0.5.0
35
+ def key?(container, key)
36
+ extract(container, key)
37
+ true
38
+ rescue SmartCore::Container::ResolvingError
39
+ false
40
+ end
41
+
42
+ # @param container [SmartCore::Container]
43
+ # @param namespace_path [String, Symbol]
44
+ # @return [Boolean]
45
+ #
46
+ # @api private
47
+ # @since 0.5.0
48
+ def namespace?(container, namespace_path)
49
+ extract(container, namespace_path).is_a?(SmartCore::Container::Entities::Namespace)
50
+ rescue SmartCore::Container::ResolvingError
51
+ false
52
+ end
53
+
54
+ # @param container [SmartCore::Container]
55
+ # @param dependency_path [String, Symbol]
56
+ # @option memoized [NilClass, Boolean]
57
+ # @return [Boolean]
58
+ #
59
+ # @api private
60
+ # @since 0.5.0
61
+ def dependency?(container, dependency_path, memoized: nil)
62
+ entity = extract(container, dependency_path)
63
+
64
+ case
65
+ when memoized.nil?
66
+ entity.is_a?(SmartCore::Container::Entities::Dependency)
67
+ when !!memoized == true
68
+ entity.is_a?(SmartCore::Container::Entities::MemoizedDependency)
69
+ when !!memoized == false
70
+ entity.is_a?(SmartCore::Container::Entities::Dependency) &&
71
+ !entity.is_a?(SmartCore::Container::Entities::MemoizedDependency)
72
+ end
73
+ rescue SmartCore::Container::ResolvingError
74
+ false
75
+ end
76
+
23
77
  # @param container [SmartCore::Container]
24
78
  # @param dependency_path [String, Symbol]
25
79
  # @return [SmartCore::Container, Any]
@@ -32,12 +86,11 @@ module SmartCore::Container::DependencyResolver
32
86
  #
33
87
  # @api private
34
88
  # @since 0.1.0
35
- # @version 0.1.0
36
89
  def resolve(container, dependency_path)
37
90
  entity = container
38
91
  Route.build(dependency_path).each do |cursor|
39
92
  entity = entity.registry.resolve(cursor.current_path)
40
- prevent_cursor_overflow!(cursor, entity)
93
+ prevent_ambiguous_resolving!(cursor, entity)
41
94
  entity = entity.reveal
42
95
  end
43
96
  entity
@@ -47,6 +100,25 @@ module SmartCore::Container::DependencyResolver
47
100
 
48
101
  private
49
102
 
103
+ # @param container [SmartCore::Container]
104
+ # @param entity_path [String, Symbol]
105
+ # @return [SmartCore::Container::Entities::Base]
106
+ #
107
+ # @api private
108
+ # @since 0.5.0
109
+ def extract(container, entity_path)
110
+ resolved_entity = container
111
+ extracted_entity = container
112
+
113
+ Route.build(entity_path).each do |cursor|
114
+ resolved_entity = resolved_entity.registry.resolve(cursor.current_path)
115
+ extracted_entity = resolved_entity
116
+ resolved_entity = resolved_entity.reveal
117
+ end
118
+
119
+ extracted_entity
120
+ end
121
+
50
122
  # @param cursor [SmartCore::Container::DependencyResolver::Route::Cursor]
51
123
  # @param entity [SmartCore::Container::Entities::Base]
52
124
  # @return [void]
@@ -54,8 +126,8 @@ module SmartCore::Container::DependencyResolver
54
126
  # @raise [SmartCore::Container::ResolvingError]
55
127
  #
56
128
  # @api private
57
- # @since 0.1.0
58
- def prevent_cursor_overflow!(cursor, entity)
129
+ # @since 0.5.0
130
+ def prevent_ambiguous_resolving!(cursor, entity)
59
131
  if cursor.last? && entity.is_a?(SmartCore::Container::Entities::Namespace)
60
132
  raise(
61
133
  SmartCore::Container::ResolvingError.new(
@@ -84,10 +156,9 @@ module SmartCore::Container::DependencyResolver
84
156
  # @api private
85
157
  # @since 0.1.0
86
158
  def process_resolving_error(dependency_path, error)
87
- full_dependency_path = Route.build_path(dependency_path, error.path_part)
88
- raise(SmartCore::Container::ResolvingError.new(<<~MESSAGE, path_part: full_dependency_path))
89
- #{error.message} (incorrect path: "#{full_dependency_path}")
90
- MESSAGE
159
+ full_dependency_path = Route.build_path(error.path_part)
160
+ message = "#{error.message} (incorrect path: \"#{full_dependency_path}\")"
161
+ raise(SmartCore::Container::ResolvingError.new(message, path_part: full_dependency_path))
91
162
  end
92
163
  end
93
164
  end
@@ -2,18 +2,13 @@
2
2
 
3
3
  # @api private
4
4
  # @since 0.1.0
5
+ # @version 0.4.0
5
6
  class SmartCore::Container::DependencyResolver::Route
6
7
  require_relative 'route/cursor'
7
8
 
8
9
  # @since 0.1.0
9
10
  include Enumerable
10
11
 
11
- # @return [String]
12
- #
13
- # @api private
14
- # @since 0.1.0
15
- PATH_PART_SEPARATOR = '.'
16
-
17
12
  class << self
18
13
  # @param path [String, Symbol]
19
14
  # @return [SmartCore::Container::DependencyResolver::Route]
@@ -28,8 +23,9 @@ class SmartCore::Container::DependencyResolver::Route
28
23
  #
29
24
  # @api private
30
25
  # @since 0.1.0
26
+ # @version 0.4.0
31
27
  def build_path(*path_parts)
32
- path_parts.join(PATH_PART_SEPARATOR)
28
+ path_parts.join(SmartCore::Container::DependencyResolver::PATH_PART_SEPARATOR)
33
29
  end
34
30
  end
35
31
 
@@ -50,9 +46,10 @@ class SmartCore::Container::DependencyResolver::Route
50
46
  #
51
47
  # @api private
52
48
  # @since 0.1.0
49
+ # @version 0.4.0
53
50
  def initialize(path)
54
51
  @path = path
55
- @path_parts = path.split(PATH_PART_SEPARATOR).freeze
52
+ @path_parts = path.split(SmartCore::Container::DependencyResolver::PATH_PART_SEPARATOR).freeze
56
53
  @size = @path_parts.size
57
54
  end
58
55
 
@@ -57,7 +57,8 @@ class SmartCore::Container
57
57
  #
58
58
  # @api private
59
59
  # @since 0.1.0
60
- def initialize(messegae = nil, path_part:)
60
+ # @version 0.4.0
61
+ def initialize(message = nil, path_part:)
61
62
  @path_part = path_part
62
63
  super(message)
63
64
  end
@@ -7,6 +7,24 @@ class SmartCore::Container::Registry
7
7
  # @since 0.1.0
8
8
  include Enumerable
9
9
 
10
+ # @return [Boolean]
11
+ #
12
+ # @api private
13
+ # @since 0.3.0
14
+ DEFAULT_MEMOIZATION_BEHAVIOR = false
15
+
16
+ # @return [Boolean]
17
+ #
18
+ # @api private
19
+ # @since 0.4.0
20
+ DEFAULT_ITERATION_YIELD_BEHAVIOUR = false
21
+
22
+ # @return [Boolean]
23
+ #
24
+ # @api private
25
+ # @since 0.4.0
26
+ DEFAULT_KEY_EXTRACTION_BEHAVIOUR = false
27
+
10
28
  # @return [Hash<Symbol,SmartCore::Container::Entity>]
11
29
  #
12
30
  # @api private
@@ -38,8 +56,8 @@ class SmartCore::Container::Registry
38
56
  #
39
57
  # @api private
40
58
  # @since 0.1.0
41
- # @version 0.2.0
42
- def register_dependency(name, memoize = true, &dependency_definition)
59
+ # @version 0.3.0
60
+ def register_dependency(name, memoize: DEFAULT_MEMOIZATION_BEHAVIOR, &dependency_definition)
43
61
  thread_safe { add_dependency(name, dependency_definition, memoize) }
44
62
  end
45
63
 
@@ -78,6 +96,30 @@ class SmartCore::Container::Registry
78
96
  thread_safe { enumerate(&block) }
79
97
  end
80
98
 
99
+ # @param root_dependency_name [NilClass, String]
100
+ # @option yield_all [Boolean]
101
+ # @param block [Block]
102
+ # @return [Enumerable]
103
+ #
104
+ # @api private
105
+ # @since 0.4.0
106
+ def each_dependency(
107
+ root_dependency_name = nil,
108
+ yield_all: DEFAULT_ITERATION_YIELD_BEHAVIOUR,
109
+ &block
110
+ )
111
+ thread_safe { iterate(root_dependency_name, yield_all: yield_all, &block) }
112
+ end
113
+
114
+ # @option all_variants [Boolean]
115
+ # @return [Array<String>]
116
+ #
117
+ # @api private
118
+ # @since 0.4.0
119
+ def keys(all_variants: DEFAULT_KEY_EXTRACTION_BEHAVIOUR)
120
+ thread_safe { extract_keys(all_variants: all_variants) }
121
+ end
122
+
81
123
  # @return [Hash<String|Symbol,SmartCore::Container::Entities::Base|Any>]
82
124
  #
83
125
  # @api private
@@ -152,9 +194,8 @@ class SmartCore::Container::Registry
152
194
  dependency_name = indifferently_accessable_name(entity_path)
153
195
  registry.fetch(dependency_name)
154
196
  rescue KeyError
155
- raise(SmartCore::Container::ResolvingError.new(<<~MESSAGE, path_part: dependency_name))
156
- Entity with \"#{dependency_name}\" name does not exist
157
- MESSAGE
197
+ error_message = "Entity with \"#{dependency_name}\" name does not exist"
198
+ raise(SmartCore::Container::ResolvingError.new(error_message, path_part: dependency_name))
158
199
  end
159
200
 
160
201
  # @param dependency_name [String, Symbol]
@@ -209,6 +250,58 @@ class SmartCore::Container::Registry
209
250
  namespace_entity.tap { namespace_entity.append_definitions(dependencies_definition) }
210
251
  end
211
252
 
253
+ # @param root_dependency_name [String, NilClass]
254
+ # @param block [Block]
255
+ # @option yield_all [Boolean]
256
+ # @yield [dependency_name, dependency]
257
+ # @yield_param dependency_name [String]
258
+ # @yield_param dependency [Any]
259
+ # @return [Enumerable]
260
+ #
261
+ # @api private
262
+ # @since 0.4.0
263
+ def iterate(root_dependency_name = nil, yield_all: DEFAULT_ITERATION_YIELD_BEHAVIOUR, &block)
264
+ enumerator = Enumerator.new do |yielder|
265
+ registry.each_pair do |dependency_name, dependency|
266
+ final_dependency_name =
267
+ if root_dependency_name
268
+ "#{root_dependency_name}" \
269
+ "#{SmartCore::Container::DependencyResolver::PATH_PART_SEPARATOR}" \
270
+ "#{dependency_name}"
271
+ else
272
+ dependency_name
273
+ end
274
+
275
+ case dependency
276
+ when SmartCore::Container::Entities::Dependency
277
+ yielder.yield(final_dependency_name, dependency.reveal)
278
+ when SmartCore::Container::Entities::Namespace
279
+ yielder.yield(final_dependency_name, dependency.reveal) if yield_all
280
+ dependency.reveal.registry.each_dependency(
281
+ final_dependency_name,
282
+ yield_all: yield_all,
283
+ &block
284
+ )
285
+ end
286
+ end
287
+ end
288
+
289
+ block_given? ? enumerator.each(&block) : enumerator.each
290
+ end
291
+
292
+ # @option all_variants [Boolean]
293
+ # @return [Array<String>]
294
+ #
295
+ # @api private
296
+ # @since 0.4.0
297
+ def extract_keys(all_variants: DEFAULT_KEY_EXTRACTION_BEHAVIOUR)
298
+ Set.new.tap do |dependency_names|
299
+ iterate(yield_all: all_variants) do |dependency_name, _dependency|
300
+ dependency_names << dependency_name
301
+ end
302
+ end.to_a
303
+ end
304
+
212
305
  # @param name [String, Symbol]
213
306
  # @return [void]
214
307
  #
@@ -4,7 +4,7 @@ module SmartCore
4
4
  class Container
5
5
  # @api public
6
6
  # @since 0.1.0
7
- # @version 0.2.0
8
- VERSION = '0.2.0'
7
+ # @version 0.7.0
8
+ VERSION = '0.7.0'
9
9
  end
10
10
  end
@@ -3,7 +3,7 @@
3
3
  require_relative 'lib/smart_core/container/version'
4
4
 
5
5
  Gem::Specification.new do |spec|
6
- spec.required_ruby_version = Gem::Requirement.new('>= 2.4.9')
6
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.4.10')
7
7
 
8
8
  spec.name = 'smart_container'
9
9
  spec.version = SmartCore::Container::VERSION
@@ -27,12 +27,12 @@ Gem::Specification.new do |spec|
27
27
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
28
  spec.require_paths = ['lib']
29
29
 
30
- spec.add_dependency 'smart_engine', '~> 0.2'
30
+ spec.add_dependency 'smart_engine', '~> 0.5'
31
31
 
32
32
  spec.add_development_dependency 'bundler', '~> 2.1'
33
33
  spec.add_development_dependency 'rake', '~> 13.0'
34
34
  spec.add_development_dependency 'rspec', '~> 3.9'
35
- spec.add_development_dependency 'armitage-rubocop', '~> 0.78'
36
- spec.add_development_dependency 'simplecov', '~> 0.17'
37
- spec.add_development_dependency 'pry', '~> 0.12'
35
+ spec.add_development_dependency 'armitage-rubocop', '~> 0.81'
36
+ spec.add_development_dependency 'simplecov', '~> 0.18'
37
+ spec.add_development_dependency 'pry', '~> 0.13'
38
38
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_container
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rustam Ibragimov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-01-05 00:00:00.000000000 Z
11
+ date: 2020-06-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: smart_engine
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.2'
19
+ version: '0.5'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0.2'
26
+ version: '0.5'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -72,42 +72,42 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0.78'
75
+ version: '0.81'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0.78'
82
+ version: '0.81'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: simplecov
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0.17'
89
+ version: '0.18'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0.17'
96
+ version: '0.18'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: pry
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0.12'
103
+ version: '0.13'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '0.12'
110
+ version: '0.13'
111
111
  description: Thread-safe semanticaly-defined IoC/DI Container
112
112
  email:
113
113
  - iamdaiver@gmail.com
@@ -178,7 +178,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
178
178
  requirements:
179
179
  - - ">="
180
180
  - !ruby/object:Gem::Version
181
- version: 2.4.9
181
+ version: 2.4.10
182
182
  required_rubygems_version: !ruby/object:Gem::Requirement
183
183
  requirements:
184
184
  - - ">="