smart_container 0.2.0 → 0.7.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 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
  - - ">="