smart_ioc 0.1.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.travis.yml +3 -0
  4. data/.yardops +1 -0
  5. data/Gemfile +9 -0
  6. data/Gemfile.lock +32 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +91 -0
  9. data/Rakefile +1 -0
  10. data/lib/smart_ioc/bean_definition.rb +77 -0
  11. data/lib/smart_ioc/bean_definitions_storage.rb +79 -0
  12. data/lib/smart_ioc/bean_dependency.rb +18 -0
  13. data/lib/smart_ioc/bean_factory.rb +230 -0
  14. data/lib/smart_ioc/bean_file_loader.rb +26 -0
  15. data/lib/smart_ioc/bean_locations.rb +71 -0
  16. data/lib/smart_ioc/bean_locator.rb +32 -0
  17. data/lib/smart_ioc/container.rb +143 -0
  18. data/lib/smart_ioc/extra_package_contexts.rb +29 -0
  19. data/lib/smart_ioc/inject_metadata.rb +13 -0
  20. data/lib/smart_ioc/iocify.rb +101 -0
  21. data/lib/smart_ioc/scopes/prototype.rb +26 -0
  22. data/lib/smart_ioc/scopes/request.rb +35 -0
  23. data/lib/smart_ioc/scopes/singleton.rb +31 -0
  24. data/lib/smart_ioc/scopes.rb +2 -0
  25. data/lib/smart_ioc/version.rb +3 -0
  26. data/lib/smart_ioc.rb +44 -0
  27. data/smart_ioc.gemspec +23 -0
  28. data/spec/smart_ioc/bean_definition_spec.rb +29 -0
  29. data/spec/smart_ioc/bean_locator_spec.rb +27 -0
  30. data/spec/smart_ioc/example/admins/repository/admins_dao.rb +20 -0
  31. data/spec/smart_ioc/example/admins/repository/admins_repository.rb +15 -0
  32. data/spec/smart_ioc/example/admins/repository/test/admins_repository.rb +17 -0
  33. data/spec/smart_ioc/example/users/repository/users_dao.rb +17 -0
  34. data/spec/smart_ioc/example/users/repository/users_repository.rb +16 -0
  35. data/spec/smart_ioc/example/users/services/users_creator.rb +15 -0
  36. data/spec/smart_ioc/example/users/user.rb +8 -0
  37. data/spec/smart_ioc/example/utils/config.rb +15 -0
  38. data/spec/smart_ioc/example/utils/logger.rb +21 -0
  39. data/spec/smart_ioc/object_spec.rb +45 -0
  40. data/spec/smart_ioc/smart_ioc_spec.rb +43 -0
  41. data/spec/spec_helper.rb +8 -0
  42. metadata +126 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 651654180ff6efe44a53a5bf8295d0fdc2216efe
4
+ data.tar.gz: 7aff613bd666008bf5f444f7aeb1b8641a8c0e0e
5
+ SHA512:
6
+ metadata.gz: 53900c9682dab42dc691fa95e504a3e5f2eb67daa312532cc7a239d33bf2673118b20c9ebc6a50a5151689e04f052bd6faaa1d6bdc9374462988ecf38d51179b
7
+ data.tar.gz: 1b8bc3b293360604e3b32cfe71b381f93d4e920352e482d7764faf3b50fdfdd6821134d7babe443568fada62bf7b7c6acc6f51f170f08afd385284e141b1eddc
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ vendor/
2
+ tags
3
+ .bundle
4
+ .DS_Store
5
+ pkg
6
+ .byebug_history
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ rvm:
2
+ - 2.0.0
3
+ script: "bundle exec rspec spec/"
data/.yardops ADDED
@@ -0,0 +1 @@
1
+ lib/**/*.rb
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in aggregate_builder.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'rspec'
8
+ gem 'byebug'
9
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,32 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ smart_ioc (0.1.13)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ byebug (9.0.6)
10
+ diff-lcs (1.2.5)
11
+ rake (10.1.1)
12
+ rspec (2.14.1)
13
+ rspec-core (~> 2.14.0)
14
+ rspec-expectations (~> 2.14.0)
15
+ rspec-mocks (~> 2.14.0)
16
+ rspec-core (2.14.7)
17
+ rspec-expectations (2.14.4)
18
+ diff-lcs (>= 1.1.3, < 2.0)
19
+ rspec-mocks (2.14.4)
20
+
21
+ PLATFORMS
22
+ ruby
23
+
24
+ DEPENDENCIES
25
+ bundler (~> 1.3)
26
+ byebug
27
+ rake
28
+ rspec
29
+ smart_ioc!
30
+
31
+ BUNDLED WITH
32
+ 1.13.6
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2016 Ruslan Gatiyatov
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,91 @@
1
+ SmartIoC is a smart and really simple IoC container for Ruby applications.
2
+
3
+ ## Installation
4
+ `gem install smart_ioc`
5
+
6
+ ## Setup
7
+ Set package name and source package folder with beans. SmartIoC will parse source files and detect bean definitions automatically for you.
8
+
9
+ ```ruby
10
+ SmartIoC.find_package_beans(:PACKAGE_NAME, File.dirname(__FILE__))
11
+ ```
12
+
13
+ If you have saveral packages in your application (like if you are using [rdm package manager](https://github.com/droidlabs/rdm)) you can run SmartIoC.find_package_beans several time pointing it to source folder and setting different package name.
14
+
15
+ ## Basic information
16
+ 1. Different packages can use beans with same name.
17
+ 2. For a specific package you can declare beans with same name if they have different context.
18
+ ```ruby
19
+ class UsersRepository
20
+ bean :users_repository
21
+ end
22
+
23
+ class Test::UsersRepository
24
+ bean :users_repository, context: :test
25
+ end
26
+ ```
27
+ 3. You can extend `:default` context with any other in the following way:
28
+
29
+ ```ruby
30
+ SmartIoC::Container.get_instance.set_extra_context_for_package(:YOUR_PACKAGE_NAME, :test)
31
+ ```
32
+ This allows to create test implementations that for any package dependencies.
33
+ 4. In order to get bean use `SmartIoC::Container.get_bean(:BEAN_NAME, package: :PACKAGE_NAME, context: :default)`. `package` and `context` are optional arguments.
34
+ 5. If you have name with same bean in different packages you will need to set package directly. You can simply do that in the following way:
35
+ ```ruby
36
+ class UsersCreator
37
+ bean :users_creator
38
+
39
+ inject :users_repository, from: :repositories
40
+
41
+ def create
42
+ user = User.new
43
+ users_repository.put(user)
44
+ end
45
+
46
+ end
47
+ ```
48
+ 6. Change dependency name inside your bean:
49
+ ```ruby
50
+ class UsersCreator
51
+ bean :users_creator
52
+
53
+ inject :repo, ref: :users_repository, from: :repositories
54
+
55
+ def create
56
+ user = User.new
57
+ repo.put(user)
58
+ end
59
+ end
60
+ ```
61
+ 7. Use factory method to instantiate the bean
62
+ ```ruby
63
+ class RepositoryFactory
64
+ bean :users_creator, factory_method: :get_bean
65
+
66
+ inject :config
67
+ inject :users_repository
68
+ inject :admins_repository
69
+
70
+ def get_bean
71
+ if config.admin_access?
72
+ admins_repository
73
+ else
74
+ users_repository
75
+ end
76
+ end
77
+
78
+ def create
79
+ user = User.new
80
+ repo.put(user)
81
+ end
82
+ end
83
+ ```
84
+ 8. Class level beans (object will not be instantiated and class will be used for that bean instead). Set `instance: false`:
85
+
86
+ ```ruby
87
+ class UsersCreator
88
+ bean :users_creator, instance: false
89
+ inject :users_repository
90
+ end
91
+ ```
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,77 @@
1
+ class SmartIoC::BeanDefinition
2
+ attr_reader :name, :package, :path, :klass, :scope, :instance, :factory_method,
3
+ :context, :dependencies
4
+
5
+ def initialize(name:, package:, path:, klass:, scope:, context:, instance:, factory_method:)
6
+ not_nil(name, :name)
7
+ not_nil(package, :package)
8
+ not_nil(path, :path)
9
+ not_nil(klass, :klass)
10
+ not_nil(scope, :scope)
11
+ not_nil(context, :context)
12
+ not_nil(instance, :instance)
13
+
14
+ @name = name
15
+ @package = package
16
+ @path = path
17
+ @klass = klass
18
+ @scope = scope
19
+ @instance = instance
20
+ @factory_method = factory_method
21
+ @context = context
22
+
23
+ @dependencies = []
24
+ end
25
+
26
+ def add_dependency(bean_name:, ref: nil, package: nil)
27
+ if !bean_name.is_a?(Symbol)
28
+ raise ArgumentError, 'bean name should be a Symbol'
29
+ end
30
+
31
+ if ref && !ref.is_a?(Symbol)
32
+ raise ArgumentError, 'ref name should be a Symbol'
33
+ end
34
+
35
+ if package && !package.is_a?(Symbol)
36
+ raise ArgumentError, 'package/from should be a Symbol'
37
+ end
38
+
39
+ @dependencies << SmartIoC::BeanDependency.new(
40
+ bean: bean_name,
41
+ ref: ref,
42
+ package: package
43
+ )
44
+ end
45
+
46
+ def is_instance?
47
+ @instance
48
+ end
49
+
50
+ def has_factory_method?
51
+ !@factory_method.nil?
52
+ end
53
+
54
+ def ==(bean_definition)
55
+ bean_definition.klass == @klass
56
+ end
57
+
58
+ def inspect
59
+ str = []
60
+ str << "class: #{@klass}"
61
+ str << "name: :#{@name}"
62
+ str << "package: :#{@package}"
63
+ str << "context: :#{@context}"
64
+ str << "path: #{@path}"
65
+ str << "instance: #{@instance}"
66
+ str << "factory_method: #{@factory_method}"
67
+ str.join("\n")
68
+ end
69
+
70
+ private
71
+
72
+ def not_nil(value, name)
73
+ if value.nil?
74
+ raise ArgumentError, ":#{name} should not be blank"
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,79 @@
1
+ class SmartIoC::BeanDefinitionsStorage
2
+ def initialize
3
+ @collection = []
4
+ end
5
+
6
+ # @param bean_definition [BeanDefinition]
7
+ def push(bean_definition)
8
+ existing_bd = @collection.detect do |bd|
9
+ bd == bean_definition
10
+ end
11
+
12
+ if existing_bd
13
+ error_msg =
14
+ %Q(Not able to add bean to definitions storage.
15
+ Bean definition already exists.
16
+ New bean details:
17
+ #{bean_definition.inspect}
18
+ Existing bean details:
19
+ #{existing_bd.inspect})
20
+
21
+ raise ArgumentError, error_msg
22
+ end
23
+
24
+ @collection.push(bean_definition)
25
+ end
26
+
27
+ # @param klass [Class] bean class
28
+ # @return bean definition [BeanDefinition] or nil
29
+ def find_by_class(klass)
30
+ @collection.detect {|bd| bd.klass == klass}
31
+ end
32
+
33
+ def filter_by(bean_name, package = nil, context = nil)
34
+ bean_definitions = @collection.select do |bd|
35
+ bd.name == bean_name
36
+ end
37
+
38
+ if package
39
+ bean_definitions = bean_definitions.select do |bd|
40
+ bd.package == package
41
+ end
42
+ end
43
+
44
+ if context
45
+ bean_definitions = bean_definitions.select do |bd|
46
+ bd.context == context
47
+ end
48
+ end
49
+
50
+ bean_definitions
51
+ end
52
+
53
+ # @bean_name [Symbol] bean name
54
+ # @package [Symbol] package name
55
+ # @context [Array[Symbol]] context
56
+ def filter_by_with_drop_to_default_context(bean_name, package = nil, context = nil)
57
+ bean_definitions = @collection.select do |bd|
58
+ bd.name == bean_name
59
+ end
60
+
61
+ if package
62
+ bean_definitions = bean_definitions.select do |bd|
63
+ bd.package == package
64
+ end
65
+ end
66
+
67
+ if context
68
+ context_bean_definitions = bean_definitions.select do |bd|
69
+ bd.context == context
70
+ end
71
+
72
+ if !context_bean_definitions.empty?
73
+ bean_definitions = context_bean_definitions
74
+ end
75
+ end
76
+
77
+ bean_definitions
78
+ end
79
+ end
@@ -0,0 +1,18 @@
1
+ class SmartIoC::BeanDependency
2
+ attr_reader :bean, :ref, :package
3
+ attr_accessor :bean_definition
4
+
5
+ def initialize(bean:, ref:, package:)
6
+ @bean = bean
7
+ @ref = ref
8
+ @package = package
9
+ end
10
+
11
+ def ref
12
+ @ref || @bean
13
+ end
14
+
15
+ def ==(dependency)
16
+ dependency.bean == @bean && dependency.package == @package
17
+ end
18
+ end
@@ -0,0 +1,230 @@
1
+ # Instantiates beans according to their scopes
2
+ class SmartIoC::BeanFactory
3
+ def initialize(bean_definitions_storage, extra_package_contexts)
4
+ @bean_definitions_storage = bean_definitions_storage
5
+ @extra_package_contexts = extra_package_contexts
6
+ @bean_file_loader = SmartIoC::BeanFileLoader.new
7
+ @singleton_scope = SmartIoC::Scopes::Singleton.new
8
+ @prototype_scope = SmartIoC::Scopes::Prototype.new
9
+ @thread_scope = SmartIoC::Scopes::Request.new
10
+ end
11
+
12
+ def clear_scopes
13
+ all_scopes.each(&:clear)
14
+ end
15
+
16
+ def force_clear_scopes
17
+ all_scopes.each(&:force_clear)
18
+ end
19
+
20
+ # Get bean from the container by it's name, package, context
21
+ # @param bean_name [Symbol] bean name
22
+ # @param package [Symbol] package name
23
+ # @param context [Symbol] context
24
+ # @return bean instance
25
+ # @raise [ArgumentError] if bean is not found
26
+ # @raise [ArgumentError] if ambiguous bean definition was found
27
+ def get_bean(bean_name, package: nil, context: SmartIoC::Container::DEFAULT_CONTEXT)
28
+ if !context.is_a?(Symbol)
29
+ raise ArgumentError, 'context should be a Symbol'
30
+ end
31
+
32
+ @bean_file_loader.require_bean(bean_name)
33
+
34
+ bds = @bean_definitions_storage.filter_by(bean_name, package, context)
35
+ if bds.size > 1
36
+ raise ArgumentError, "Unable to create bean :#{bean_name}.\nSeveral definitions were found.\n#{bds.map(&:inspect).join("\n\n")}"
37
+ elsif bds.size == 0
38
+ raise ArgumentError, "Unable to find bean :#{bean_name} in any packages"
39
+ end
40
+
41
+ bean_definition = bds.first
42
+ dependency_cache = {}
43
+ beans_cache = {}
44
+
45
+ autodetect_bean_definitions_for_dependencies(bean_definition, dependency_cache)
46
+ preload_beans(bean_definition, dependency_cache, beans_cache)
47
+ load_bean(bean_definition, dependency_cache, beans_cache)
48
+ end
49
+
50
+ private
51
+
52
+ def autodetect_bean_definitions_for_dependencies(bean_definition, dependency_cache)
53
+ bean_definition.dependencies.each do |dependency|
54
+ next if dependency_cache.has_key?(dependency)
55
+
56
+ @bean_file_loader.require_bean(dependency.ref)
57
+
58
+ bd = autodetect_bean_definition(
59
+ dependency.ref, dependency.package, bean_definition.package
60
+ )
61
+
62
+ dependency_cache[dependency] = bd
63
+
64
+ autodetect_bean_definitions_for_dependencies(bd, dependency_cache)
65
+ end
66
+ end
67
+
68
+ def autodetect_bean_definition(bean, package, parent_bean_package)
69
+ if package
70
+ bean_context = @extra_package_contexts.get_context(package)
71
+ bds = @bean_definitions_storage.filter_by_with_drop_to_default_context(bean, package, bean_context)
72
+
73
+ return bds.first if bds.size == 1
74
+ raise ArgumentError, "bean :#{bean} is not found in package :#{package}"
75
+ end
76
+
77
+ if parent_bean_package
78
+ bean_context = @extra_package_contexts.get_context(parent_bean_package)
79
+ bds = @bean_definitions_storage.filter_by_with_drop_to_default_context(bean, parent_bean_package, bean_context)
80
+
81
+ return bds.first if bds.size == 1
82
+ end
83
+
84
+ bds = @bean_definitions_storage.filter_by(bean)
85
+ bds_by_package = bds.group_by(&:package)
86
+ smart_bds = []
87
+
88
+ bds_by_package.each do |package, bd_list|
89
+ # try to find bean definition with package context
90
+ bd = bd_list.detect {|bd| bd.context == @extra_package_contexts.get_context(bd.package)}
91
+ smart_bds << bd if bd
92
+
93
+ # last try: find for :default context
94
+ if !bd
95
+ bd = bd_list.detect {|bd| bd.context == SmartIoC::Container::DEFAULT_CONTEXT}
96
+ smart_bds << bd if bd
97
+ end
98
+ end
99
+
100
+ if smart_bds.size > 1
101
+ raise ArgumentError, "Unable to autodetect bean :#{bean}.\nSeveral definitions were found.\n#{smart_bds.map(&:inspect).join("\n\n")}. Set package directly for injected dependency"
102
+ end
103
+
104
+ return smart_bds.first
105
+ end
106
+
107
+ def preload_beans(bean_definition, dependency_cache, beans_cache)
108
+ preload_bean_instance(bean_definition, beans_cache)
109
+
110
+ bean_definition.dependencies.each do |dependency|
111
+ bd = dependency_cache[dependency]
112
+
113
+ next if beans_cache.has_key?(bd)
114
+ preload_beans(bd, dependency_cache, beans_cache)
115
+ end
116
+ end
117
+
118
+ def preload_bean_instance(bean_definition, beans_cache)
119
+ return beans_cache[bean_definition] if beans_cache.has_key?(bean_definition)
120
+
121
+ scope = get_scope(bean_definition)
122
+ bean = scope.get_bean(bean_definition.klass)
123
+
124
+ if bean
125
+ beans_cache[bean_definition] = bean
126
+ return bean
127
+ end
128
+
129
+ bean = if bean_definition.is_instance?
130
+ bean_definition.klass.allocate
131
+ else
132
+ bean_definition.klass
133
+ end
134
+
135
+ scope.save_bean(bean_definition.klass, bean)
136
+ beans_cache[bean_definition] = bean
137
+
138
+ bean
139
+ end
140
+
141
+ def load_bean(bean_definition, dependency_cache, beans_cache)
142
+ # first let's setup beans with factory_methods
143
+ zero_dep_bd_with_factory_methods = []
144
+ bd_with_factory_methods = []
145
+
146
+ beans_cache.each do |bean_definition, bean|
147
+ if bean_definition.has_factory_method?
148
+ if bean_definition.dependencies.size == 0
149
+ zero_dep_bd_with_factory_methods << bean_definition
150
+ else
151
+ bd_with_factory_methods << bean_definition
152
+ end
153
+ end
154
+ end
155
+
156
+ bd_with_factory_methods.each do |bean_definition|
157
+ if cross_refference_bd = get_cross_refference(bd_with_factory_methods, bean_definition, dependency_cache)
158
+ raise ArgumentError, "Factory method beans should not cross refference each other. Bean :#{bean_definition.name} cross refferences bean :#{cross_refference_bd.name}."
159
+ end
160
+ end
161
+
162
+ (zero_dep_bd_with_factory_methods + bd_with_factory_methods).each do |bean_definition|
163
+ inject_beans(bean_definition, dependency_cache, beans_cache)
164
+ bean = beans_cache[bean_definition]
165
+ bean = bean.send(bean_definition.factory_method)
166
+ beans_cache[bean_definition] = bean
167
+ end
168
+
169
+ inject_beans(bean_definition, dependency_cache, beans_cache)
170
+
171
+ beans_cache[bean_definition]
172
+ end
173
+
174
+ def inject_beans(bean_definition, dependency_cache, beans_cache)
175
+ bean = beans_cache[bean_definition]
176
+ bean_definition.dependencies.each do |dependency|
177
+ bd = dependency_cache[dependency]
178
+ dep_bean = beans_cache[bd]
179
+ bean.instance_variable_set(:"@#{dependency.bean}", dep_bean)
180
+ inject_beans(bd, dependency_cache, beans_cache)
181
+ end
182
+ end
183
+
184
+ def get_cross_refference(refer_bean_definitions, current_bean_definition, dependency_cache, seen_bean_definitions = [])
185
+ current_bean_definition.dependencies.each do |dependency|
186
+ bd = dependency_cache[dependency]
187
+
188
+ next if seen_bean_definitions.include?(bd)
189
+
190
+ if refer_bean_definitions.include?(bd)
191
+ return bd
192
+ end
193
+
194
+ if crbd = get_cross_refference(refer_bean_definitions, bd, dependency_cache, seen_bean_definitions + [bd])
195
+ return crbd
196
+ end
197
+ end
198
+
199
+ nil
200
+ end
201
+
202
+ def set_bean_dependencies(bean, bean_definition, parent_bean_package)
203
+ bean_definition.dependencies.each do |dependency|
204
+ context = if dependency.package
205
+ @extra_package_contexts.get_context(dependency.package)
206
+ end
207
+
208
+ dependent_bean = get_or_load_bean(dependency.ref, dependency.package, context, bean_definition.package)
209
+
210
+ bean.instance_variable_set(:"@#{dependency.bean}", dependent_bean)
211
+ end
212
+ end
213
+
214
+ def all_scopes
215
+ [@singleton_scope, @prototype_scope, @thread_scope]
216
+ end
217
+
218
+ def get_scope(bean_definition)
219
+ case bean_definition.scope
220
+ when SmartIoC::Scopes::Singleton::VALUE
221
+ @singleton_scope
222
+ when SmartIoC::Scopes::Prototype::VALUE
223
+ @prototype_scope
224
+ when SmartIoC::Scopes::Request::VALUE
225
+ @thread_scope
226
+ else
227
+ raise ArgumentError, "bean definition for :#{bean_definition.name} has unsupported scope :#{bean_definition.scope}"
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,26 @@
1
+ class SmartIoC::BeanFileLoader
2
+ def initialize
3
+ @loaded_locations = {}
4
+ end
5
+
6
+ # @param bean_name [Symbol] bean name
7
+ # return nil
8
+ def require_bean(bean_name)
9
+ locations = SmartIoC::BeanLocations.get_bean_locations(bean_name)
10
+
11
+ # load *.rb file if extra bean location was added or it was not loaded yet
12
+ location_count = locations.values.flatten.size
13
+
14
+ if !@loaded_locations.has_key?(bean_name) || @loaded_locations[bean_name] != location_count
15
+ locations.each do |package_name, locations|
16
+ locations.each do |location|
17
+ require location
18
+ end
19
+ end
20
+
21
+ @loaded_locations[bean_name] = location_count
22
+ end
23
+
24
+ nil
25
+ end
26
+ end
@@ -0,0 +1,71 @@
1
+ # SmartIoC::BeanLocations is a storage for locations of package bean definitions.
2
+ # Storage structure:
3
+ # {
4
+ # PACKAGE_NAME => { BEAN_SYM => BEAN_PATH}
5
+ # }
6
+ # Ex:
7
+ # {
8
+ # repository: {
9
+ # users_repository: ['/app/core/infrastructure/repository/users.rb'],
10
+ # posts_repository: ['/app/core/infrastructure/repository/posts.rb']
11
+ # }
12
+ # }
13
+ class SmartIoC::BeanLocations
14
+ @data = {}
15
+
16
+ class << self
17
+ # @param package_name [Symbol] bean package name (ex: :repository)
18
+ # @param bean [Symbol] bean name (ex: :users_repository)
19
+ # @param bean_path [String] bean name (ex: :users_repository)
20
+ # @return nil
21
+ # @raise [ArgumentError] if bean previous bean definition with same name was found for package
22
+ def add_bean(package_name, bean, bean_path)
23
+ @data[package_name] ||= {}
24
+ package_beans = @data[package_name]
25
+
26
+ package_beans[bean] ||= []
27
+ package_beans[bean].push(bean_path)
28
+
29
+ nil
30
+ end
31
+
32
+ # @param bean [Symbol] bean name (ex: :users_repository)
33
+ # @return Hash[Array[String]] hash of bean definitions from all packages
34
+ def get_bean_locations(bean)
35
+ locations = {}
36
+
37
+ @data.each do |package, bean_locations|
38
+ if bean_locations.has_key?(bean)
39
+ locations[package] ||= []
40
+ locations[package] += bean_locations[bean]
41
+ end
42
+ end
43
+
44
+ locations
45
+ end
46
+
47
+ def load_all
48
+ @data.each do |package, bean_locations|
49
+ bean_locations.each do |bean, path|
50
+ load(path)
51
+ end
52
+ end
53
+ end
54
+
55
+ def clear
56
+ @data = {}
57
+ end
58
+
59
+ # @param path [String] absolute bean path
60
+ # @return [nil or String] package name be absolute bean path
61
+ def get_bean_package(path)
62
+ @data.each do |package, bean_locations|
63
+ if bean_locations.values.flatten.include?(path)
64
+ return package
65
+ end
66
+ end
67
+
68
+ nil
69
+ end
70
+ end
71
+ end