smart_ioc 0.2.5 → 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -13,10 +13,6 @@ module SmartIoC
13
13
  def clear
14
14
  @container = nil
15
15
  end
16
-
17
- def get_bean(bean_name, package: nil, context: nil)
18
- get_instance.get_bean(bean_name, package: package, context: context)
19
- end
20
16
  end
21
17
 
22
18
  def initialize
@@ -36,8 +32,9 @@ module SmartIoC
36
32
  # @param path [String] bean file absolute path
37
33
  # @param scope [Symbol] scope value
38
34
  # @param context [Symbol] bean context
35
+ # @param after_init [Symbol] name of bean method that will be called after bean initialization
39
36
  # @return [SmartIoC::BeanDefinition] bean definition
40
- def register_bean(bean_name:, klass:, context:, scope:, path:,
37
+ def register_bean(bean_name:, klass:, context:, scope:, path:, after_init:,
41
38
  factory_method: nil, package_name: nil, instance: true)
42
39
  context ||= DEFAULT_CONTEXT
43
40
 
@@ -60,8 +57,6 @@ module SmartIoC
60
57
  raise ArgumentError, "bean scope should be one of #{allowed_scopes.inspect}"
61
58
  end
62
59
 
63
- package_name ||= SmartIoC::BeanLocations.get_bean_package(path)
64
-
65
60
  if !package_name
66
61
  raise ArgumentError, %Q(
67
62
  Package name should be given for bean :#{bean_name}.
@@ -81,7 +76,8 @@ module SmartIoC
81
76
  instance: instance,
82
77
  factory_method: factory_method,
83
78
  context: context,
84
- scope: scope
79
+ scope: scope,
80
+ after_init: after_init,
85
81
  )
86
82
 
87
83
  bean_definitions_storage.push(bean_definition)
@@ -90,10 +86,12 @@ module SmartIoC
90
86
  end
91
87
 
92
88
  # Returns bean definition for specific class
93
- # @param klass [Class] class name
89
+ # @param bean_name [Symbol]
90
+ # @param package [Symbol]
91
+ # @param context [Symbol]
94
92
  # return [BeanDefinition]
95
- def get_bean_definition_by_class(klass)
96
- bean_definitions_storage.find_by_class(klass)
93
+ def get_bean_definition(bean_name, package, context)
94
+ bean_definitions_storage.find_bean(bean_name, package, context)
97
95
  end
98
96
 
99
97
  # Sets new load proc
@@ -116,10 +114,17 @@ module SmartIoC
116
114
 
117
115
  # @param bean_name [Symbol] bean name
118
116
  # @param optional package [Symbol] package name
117
+ # @param optional parent_bean_definition [SmartIoc::BeanDefinition] bean definition of parent bean
119
118
  # @param optional context [Symbol] package context
120
119
  # @return bean instance from container
121
- def get_bean(bean_name, package: nil, context: nil)
122
- bean_factory.get_bean(bean_name, package: package, context: context)
120
+ def get_bean(bean_name, package: nil, context: nil, parent_bean_definition: nil, parent_bean_name: nil)
121
+ bean_factory.get_bean(
122
+ bean_name,
123
+ package: package,
124
+ parent_bean_definition: parent_bean_definition,
125
+ context: context,
126
+ parent_bean_name: parent_bean_name,
127
+ )
123
128
  end
124
129
 
125
130
  def clear_scopes
@@ -136,9 +141,9 @@ module SmartIoC
136
141
  def require_bean(bean_name)
137
142
  bean_factory.bean_file_loader.require_bean(bean_name)
138
143
  end
139
-
144
+
140
145
  private
141
-
146
+
142
147
  def bean_factory
143
148
  @bean_factory ||= SmartIoC::BeanFactory.new(bean_definitions_storage, extra_package_contexts)
144
149
  end
@@ -5,23 +5,21 @@ module SmartIoC::Errors
5
5
  end
6
6
  end
7
7
 
8
- class LoadRecursion < StandardError
9
- def initialize(bean_definition)
10
- super(%Q(
11
- Unable to create bean :#{bean_definitions.name}.
12
- Recursion found during bean load.
13
- #{bean_definition.inspect}
14
- ))
15
- end
16
- end
17
-
18
8
  class AmbiguousBeanDefinition < StandardError
9
+ attr_accessor :parent_bean_definition
10
+
19
11
  def initialize(bean_name, bean_definitions)
20
- super(%Q(
21
- Unable to create bean :#{bean_name}.
22
- Several definitions were found.
23
- #{bean_definitions.map(&:inspect).join("\n\n")}
24
- ))
12
+ @bean_name = bean_name
13
+ @bean_definitions = bean_definitions
14
+ end
15
+
16
+ def message
17
+ <<~EOS
18
+ Unable to inject bean :#{@bean_name}#{@parent_bean_definition ? " into :#{@parent_bean_definition.name} (package: #{@parent_bean_definition.package})" : ""}.
19
+ Several bean definitions with name :#{@bean_name} were found:
20
+
21
+ #{@bean_definitions.map(&:inspect).join("\n\n")}
22
+ EOS
25
23
  end
26
24
  end
27
25
  end
@@ -14,6 +14,10 @@ class SmartIoC::ExtraPackageContexts
14
14
  @data[package_name] = context
15
15
  end
16
16
 
17
+ def package_context(package_name)
18
+ @data[package_name]
19
+ end
20
+
17
21
  def get_context(package_name)
18
22
  @data[package_name] || SmartIoC::Container::DEFAULT_CONTEXT
19
23
  end
@@ -2,6 +2,9 @@
2
2
  # Example of usage:
3
3
  # class Bar
4
4
  # bean :bar
5
+ #
6
+ # def call
7
+ # end
5
8
  # end
6
9
  #
7
10
  # class Foo
@@ -12,6 +15,7 @@
12
15
  # inject :some_bar, ref: bar, from: :repository
13
16
  #
14
17
  # def hello_world
18
+ # some_bar.call
15
19
  # puts 'Hello world'
16
20
  # end
17
21
  # end
@@ -23,17 +27,48 @@ module SmartIoC::Iocify
23
27
  end
24
28
 
25
29
  module ClassMethods
30
+ def package(name)
31
+ raise ArgumentError, "name should be a Symbol" if !name.is_a?(Symbol)
32
+ @package = name
33
+ end
34
+
35
+ def context(name)
36
+ raise ArgumentError, "name should be a Symbol" if !name.is_a?(Symbol)
37
+ @context = name
38
+ end
39
+
40
+ def factory_method(name)
41
+ raise ArgumentError, "name should be a Symbol" if !name.is_a?(Symbol)
42
+ @factory_method = name
43
+ end
44
+
45
+ def scope(name)
46
+ raise ArgumentError, "name should be a Symbol" if !name.is_a?(Symbol)
47
+ @scope = name
48
+ end
49
+
50
+ def instance
51
+ @instance = true
52
+ end
53
+
54
+ def after_init(name)
55
+ raise ArgumentError, "name should be a Symbol" if !name.is_a?(Symbol)
56
+ @after_init = name
57
+ end
58
+
26
59
  # @param bean_name [Symbol] bean name
27
60
  # @param scope [Symbol] bean scope (defaults to :singleton)
28
61
  # @param package [nil or Symbol]
29
62
  # @param factory_method [nil or Symbol] factory method to get bean
30
63
  # @param instance [Boolean] instance based bean or class-based
31
64
  # @param context [Symbol] set bean context (ex: :test)
65
+ # @param after_init [Symbol] name of bean method that will be called after bean initialization (ex: :test)
32
66
  # @return nil
33
- def bean(bean_name, scope: nil, package: nil, instance: true, factory_method: nil, context: nil)
34
- file_path = caller[0].split(':').first
35
-
36
- bean_definition = SmartIoC.get_bean_definition_by_class(self)
67
+ def bean(bean_name, scope: nil, package: nil, instance: true, factory_method: nil, context: nil, after_init: nil, file_path: nil)
68
+ file_path ||= caller[0].split(':').first
69
+ package ||= SmartIoC::BeanLocations.get_bean_package(file_path)
70
+ context ||= SmartIoC::Container::DEFAULT_CONTEXT
71
+ bean_definition = SmartIoC.get_bean_definition(bean_name, package, context)
37
72
 
38
73
  if bean_definition
39
74
  if bean_definition.path == file_path
@@ -55,7 +90,8 @@ module SmartIoC::Iocify
55
90
  package_name: package,
56
91
  instance: instance,
57
92
  factory_method: factory_method,
58
- context: context
93
+ context: context,
94
+ after_init: after_init,
59
95
  )
60
96
 
61
97
  if bean_definition.is_instance?
@@ -66,9 +102,20 @@ module SmartIoC::Iocify
66
102
  )
67
103
  end
68
104
 
105
+ self.instance_variable_set(:@bean_definition, bean_definition)
106
+
69
107
  nil
70
108
  end
71
109
 
110
+ def inject(bean_name, ref: nil, from: nil)
111
+ if @anonymous_bean
112
+ @injects ||= []
113
+ @injects.push({bean_name: bean_name, ref: ref, from: from})
114
+ else
115
+ register_inject(bean_name, ref: ref, from: from)
116
+ end
117
+ end
118
+
72
119
  # @param bean_name [Symbol] injected bean name
73
120
  # @param ref [Symbol] refferece bean to be sef as bean_name
74
121
  # @param from [Symbol] package name
@@ -77,29 +124,44 @@ module SmartIoC::Iocify
77
124
  # @raise [ArgumentError] if ref provided and ref is not a Symbol
78
125
  # @raise [ArgumentError] if from provided and from is not a Symbol
79
126
  # @raise [ArgumentError] if bean with same name was injected before
80
- def inject(bean_name, ref: nil, from: nil)
81
- bean_definition = SmartIoC::Container.get_instance.get_bean_definition_by_class(self)
82
-
83
- if bean_definition.nil?
127
+ def register_inject(bean_name, ref: nil, from: nil)
128
+ if !@bean_definition
84
129
  raise ArgumentError, "#{self.to_s} is not registered as bean. Add `bean :bean_name` declaration"
85
130
  end
86
131
 
87
- bean_definition.add_dependency(
132
+ bd = @bean_definition
133
+
134
+ bd.add_dependency(
88
135
  bean_name: bean_name,
89
136
  ref: ref,
90
137
  package: from
91
138
  )
92
139
 
93
- if bean_definition.is_instance?
94
- class_eval %Q(
95
- private
96
- attr_reader :#{bean_name}
140
+ bean_method = Proc.new do
141
+ bean = instance_variable_get(:"@#{bean_name}")
142
+ return bean if bean
143
+
144
+ klass = self.is_a?(Class) ? self : self.class
145
+
146
+ bean = SmartIoC::Container.get_instance.get_bean(
147
+ ref || bean_name,
148
+ package: from,
149
+ parent_bean_definition: bd,
150
+ parent_bean_name: bd.name,
97
151
  )
152
+
153
+ instance_variable_set(:"@#{bean_name}", bean)
154
+ end
155
+
156
+ if bd.is_instance?
157
+ define_method bean_name, &bean_method
158
+ private bean_name
98
159
  else
160
+ define_singleton_method bean_name, &bean_method
161
+
99
162
  class_eval %Q(
100
163
  class << self
101
- private
102
- attr_reader :#{bean_name}
164
+ private :#{bean_name}
103
165
  end
104
166
  )
105
167
  end
@@ -1,3 +1,3 @@
1
1
  module SmartIoC
2
- VERSION = "0.2.5"
2
+ VERSION = "0.3.6"
3
3
  end
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(spec)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_development_dependency 'bundler', '~> 1.3'
21
+ spec.add_development_dependency 'bundler'
22
22
  spec.add_development_dependency 'rake'
23
23
  spec.add_development_dependency "codecov"
24
24
  end
@@ -4,19 +4,19 @@ describe SmartIoC::BeanDefinition do
4
4
  describe "::inspect" do
5
5
  it {
6
6
  bd = SmartIoC::BeanDefinition.new(
7
- name: :test_bean,
8
- package: :test_package,
9
- path: 'current_dir',
10
- klass: Object,
11
- scope: :singleton,
12
- context: :default,
13
- instance: false,
14
- factory_method: nil
7
+ name: :test_bean,
8
+ package: :test_package,
9
+ path: 'current_dir',
10
+ klass: Object,
11
+ scope: :singleton,
12
+ context: :default,
13
+ instance: false,
14
+ factory_method: nil,
15
+ after_init: nil,
15
16
  )
16
17
 
17
18
  str =
18
- "class: Object
19
- name: :test_bean
19
+ "name: :test_bean
20
20
  package: :test_package
21
21
  context: :default
22
22
  path: current_dir
@@ -4,27 +4,42 @@ describe SmartIoC::BeanFactory do
4
4
  before :all do
5
5
  SmartIoC.clear
6
6
 
7
- class Repo
8
- include SmartIoC::Iocify
9
- bean :repo, context: :default, package: :bean_factory
7
+ bean :repo do
8
+ context :default
9
+ package :bean_factory
10
+
11
+ def call
12
+ 'default'
13
+ end
10
14
  end
11
15
 
12
- class TestRepo
13
- include SmartIoC::Iocify
14
- bean :repo, context: :test, package: :bean_factory
16
+ bean :repo do
17
+ context :test
18
+ package :bean_factory
19
+
20
+ inject :dao
21
+
22
+ def call
23
+ dao.call
24
+ 'test'
25
+ end
15
26
  end
16
27
 
17
- class DAO
18
- include SmartIoC::Iocify
19
- bean :dao, context: :default, package: :bean_factory
28
+ bean :dao do
29
+ context :default
30
+ package :bean_factory
31
+
32
+ def call
33
+ 'dao'
34
+ end
20
35
  end
21
36
 
22
37
  class TestObject
23
38
  end
24
39
 
25
- class Factory
26
- include SmartIoC::Iocify
27
- bean :factory, factory_method: :build_bean, package: :bean_factory
40
+ bean :factory do
41
+ factory_method :build_bean
42
+ package :bean_factory
28
43
 
29
44
  def build_bean
30
45
  TestObject.new
@@ -32,6 +47,15 @@ describe SmartIoC::BeanFactory do
32
47
  end
33
48
  end
34
49
 
50
+ it 'returns benchmark time to load bean' do
51
+ SmartIoC.benchmark_mode(true)
52
+
53
+ SmartIoC.set_extra_context_for_package(:bean_factory, :test)
54
+ SmartIoC.get_bean(:repo)
55
+
56
+ SmartIoC.benchmark_mode(false)
57
+ end
58
+
35
59
  it 'returns same instance for singleton scope' do
36
60
  SmartIoC.set_extra_context_for_package(:bean_factory, :test)
37
61
  instance1 = SmartIoC.get_bean(:repo)
@@ -47,17 +71,17 @@ describe SmartIoC::BeanFactory do
47
71
 
48
72
  it 'returns proper bean for test context' do
49
73
  SmartIoC.set_extra_context_for_package(:bean_factory, :test)
50
- expect(SmartIoC.get_bean(:repo)).to be_a(TestRepo)
74
+ expect(SmartIoC.get_bean(:repo).call).to eq('test')
51
75
  end
52
76
 
53
77
  it 'returns proper bean for default context' do
54
78
  SmartIoC.set_extra_context_for_package(:bean_factory, :default)
55
- expect(SmartIoC.get_bean(:repo)).to be_a(Repo)
79
+ expect(SmartIoC.get_bean(:repo).call).to eq('default')
56
80
  end
57
81
 
58
82
  it 'returns proper bean for test context with fallback to default context' do
59
83
  SmartIoC.set_extra_context_for_package(:bean_factory, :test)
60
- expect(SmartIoC.get_bean(:dao)).to be_a(DAO)
84
+ expect(SmartIoC.get_bean(:dao).call).to eq('dao')
61
85
  end
62
86
 
63
87
  it 'updates dependencies' do
@@ -67,7 +91,7 @@ describe SmartIoC::BeanFactory do
67
91
 
68
92
  inject :prototype_bean
69
93
 
70
- attr_reader :prototype_bean
94
+ public :prototype_bean
71
95
  end
72
96
 
73
97
  class SecondSingletonBean
@@ -81,7 +105,7 @@ describe SmartIoC::BeanFactory do
81
105
 
82
106
  inject :second_singleton_bean
83
107
 
84
- attr_reader :second_singleton_bean
108
+ public :second_singleton_bean
85
109
  end
86
110
 
87
111
  bean1 = SmartIoC.get_bean(:singleton_bean, package: :test)
@@ -95,7 +119,7 @@ describe SmartIoC::BeanFactory do
95
119
  second_singleton_bean2_object_id = bean2.prototype_bean.second_singleton_bean.object_id
96
120
 
97
121
  expect(bean1_object_id).to eq(bean2_object_id)
98
- expect(prototype_bean1_object_id).not_to eq(prototype_bean2_object_id)
122
+ expect(prototype_bean1_object_id).to eq(prototype_bean2_object_id)
99
123
  expect(second_singleton_bean1_object_id).to eq(second_singleton_bean2_object_id)
100
124
  end
101
125
 
@@ -108,7 +132,7 @@ describe SmartIoC::BeanFactory do
108
132
  inject :prototype_service1
109
133
  inject :prototype_service2
110
134
 
111
- attr_reader :prototype_service1, :prototype_service2
135
+ public :prototype_service1, :prototype_service2
112
136
  end
113
137
 
114
138
  class PrototypeService1
@@ -118,7 +142,7 @@ describe SmartIoC::BeanFactory do
118
142
  inject :prototype_repo
119
143
  inject :singleton_repo
120
144
 
121
- attr_reader :prototype_repo, :singleton_repo
145
+ public :prototype_repo, :singleton_repo
122
146
  end
123
147
 
124
148
  class PrototypeService2
@@ -128,22 +152,24 @@ describe SmartIoC::BeanFactory do
128
152
  inject :prototype_repo
129
153
  inject :singleton_repo
130
154
 
131
- attr_reader :prototype_repo, :singleton_repo
155
+ public :prototype_repo, :singleton_repo
132
156
  end
133
157
 
134
158
  class PrototypeRepo
135
159
  include SmartIoC::Iocify
160
+
136
161
  bean :prototype_repo, scope: :prototype, package: :prototype
137
162
  end
138
163
 
139
164
  class SingletonRepo
140
165
  include SmartIoC::Iocify
166
+
141
167
  bean :singleton_repo, scope: :singleton, package: :prototype
142
168
  end
143
169
  end
144
170
 
145
171
  it 'injects prototype beans with different object id' do
146
- prototype_bean = SmartIoC.get_bean(:prototype_bean)
172
+ prototype_bean = SmartIoC.get_bean(:prototype_bean, package: :prototype)
147
173
  repo1_object_id = prototype_bean.prototype_service1.prototype_repo.object_id
148
174
  repo2_object_id = prototype_bean.prototype_service2.prototype_repo.object_id
149
175
 
@@ -151,7 +177,7 @@ describe SmartIoC::BeanFactory do
151
177
  end
152
178
 
153
179
  it 'injects singleton beans with same object id' do
154
- prototype_bean = SmartIoC.get_bean(:prototype_bean)
180
+ prototype_bean = SmartIoC.get_bean(:prototype_bean, package: :prototype)
155
181
  repo1_object_id = prototype_bean.prototype_service1.singleton_repo.object_id
156
182
  repo2_object_id = prototype_bean.prototype_service2.singleton_repo.object_id
157
183