smart_ioc 0.1.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.travis.yml +3 -0
- data/.yardops +1 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +32 -0
- data/LICENSE.txt +22 -0
- data/README.md +91 -0
- data/Rakefile +1 -0
- data/lib/smart_ioc/bean_definition.rb +77 -0
- data/lib/smart_ioc/bean_definitions_storage.rb +79 -0
- data/lib/smart_ioc/bean_dependency.rb +18 -0
- data/lib/smart_ioc/bean_factory.rb +230 -0
- data/lib/smart_ioc/bean_file_loader.rb +26 -0
- data/lib/smart_ioc/bean_locations.rb +71 -0
- data/lib/smart_ioc/bean_locator.rb +32 -0
- data/lib/smart_ioc/container.rb +143 -0
- data/lib/smart_ioc/extra_package_contexts.rb +29 -0
- data/lib/smart_ioc/inject_metadata.rb +13 -0
- data/lib/smart_ioc/iocify.rb +101 -0
- data/lib/smart_ioc/scopes/prototype.rb +26 -0
- data/lib/smart_ioc/scopes/request.rb +35 -0
- data/lib/smart_ioc/scopes/singleton.rb +31 -0
- data/lib/smart_ioc/scopes.rb +2 -0
- data/lib/smart_ioc/version.rb +3 -0
- data/lib/smart_ioc.rb +44 -0
- data/smart_ioc.gemspec +23 -0
- data/spec/smart_ioc/bean_definition_spec.rb +29 -0
- data/spec/smart_ioc/bean_locator_spec.rb +27 -0
- data/spec/smart_ioc/example/admins/repository/admins_dao.rb +20 -0
- data/spec/smart_ioc/example/admins/repository/admins_repository.rb +15 -0
- data/spec/smart_ioc/example/admins/repository/test/admins_repository.rb +17 -0
- data/spec/smart_ioc/example/users/repository/users_dao.rb +17 -0
- data/spec/smart_ioc/example/users/repository/users_repository.rb +16 -0
- data/spec/smart_ioc/example/users/services/users_creator.rb +15 -0
- data/spec/smart_ioc/example/users/user.rb +8 -0
- data/spec/smart_ioc/example/utils/config.rb +15 -0
- data/spec/smart_ioc/example/utils/logger.rb +21 -0
- data/spec/smart_ioc/object_spec.rb +45 -0
- data/spec/smart_ioc/smart_ioc_spec.rb +43 -0
- data/spec/spec_helper.rb +8 -0
- 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
data/.travis.yml
ADDED
data/.yardops
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
lib/**/*.rb
|
data/Gemfile
ADDED
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
|