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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: faf1a687610d2cc6e25623650bb6ff881938bd28
4
- data.tar.gz: 34208f444623db8654a9ff4501ef2c70b581aa5a
2
+ SHA256:
3
+ metadata.gz: 01e3fac1f32289e170915d3251e88d937f970f6396ef7d6a8c7b2163696001f2
4
+ data.tar.gz: 15f36e9c706c0cfdb59c86e8f40cf1106361346b0f0def709f9618b3821cc865
5
5
  SHA512:
6
- metadata.gz: bdf1e74fcc8965d8ccd77c106cebca346200d49d9419b5530d3964c5048d76e6b6798da6cadd9ab656614c340bd7657cbbb96a141b425e1e77ff6a89331a0b8a
7
- data.tar.gz: 34933c3ad8765c8e9dd94fa6b80539a9c4b2c4227f1d344f413c0fec5fe55ae602db8e3e5017d87ebd9c7c07a846a0127833323e31b60ef082e36c9d4b3ea9eb
6
+ metadata.gz: 11fae8af77f705ecdbd3a6039dd5c148abd28fc5ef8e584644179f76f23f1c061f4dc4c049a593f308037ef0e1f033c0cd6641e3d761447e51a97c6ea3699efd
7
+ data.tar.gz: 82ea8d86866fe7eace9a1db47da5fd87457cd499425387aa977d086ac514525463902e8f4f8261ae91c4ba9e35ddc0b4f25761303552e35c63c05edd34d2ba7a
data/Gemfile CHANGED
@@ -6,4 +6,5 @@ gemspec
6
6
  group :test do
7
7
  gem 'rspec'
8
8
  gem 'simplecov'
9
+ gem 'byebug'
9
10
  end
@@ -1,44 +1,45 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- smart_ioc (0.2.5)
4
+ smart_ioc (0.3.6)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- codecov (0.1.10)
9
+ byebug (11.1.3)
10
+ codecov (0.2.5)
11
+ colorize
10
12
  json
11
13
  simplecov
12
- url
13
- diff-lcs (1.3)
14
- docile (1.1.5)
15
- json (2.0.3)
16
- rake (12.0.0)
17
- rspec (3.5.0)
18
- rspec-core (~> 3.5.0)
19
- rspec-expectations (~> 3.5.0)
20
- rspec-mocks (~> 3.5.0)
21
- rspec-core (3.5.4)
22
- rspec-support (~> 3.5.0)
23
- rspec-expectations (3.5.0)
14
+ colorize (0.8.1)
15
+ diff-lcs (1.4.4)
16
+ docile (1.3.2)
17
+ json (2.3.1)
18
+ rake (13.0.1)
19
+ rspec (3.9.0)
20
+ rspec-core (~> 3.9.0)
21
+ rspec-expectations (~> 3.9.0)
22
+ rspec-mocks (~> 3.9.0)
23
+ rspec-core (3.9.2)
24
+ rspec-support (~> 3.9.3)
25
+ rspec-expectations (3.9.2)
24
26
  diff-lcs (>= 1.2.0, < 2.0)
25
- rspec-support (~> 3.5.0)
26
- rspec-mocks (3.5.0)
27
+ rspec-support (~> 3.9.0)
28
+ rspec-mocks (3.9.1)
27
29
  diff-lcs (>= 1.2.0, < 2.0)
28
- rspec-support (~> 3.5.0)
29
- rspec-support (3.5.0)
30
- simplecov (0.14.1)
31
- docile (~> 1.1.0)
32
- json (>= 1.8, < 3)
33
- simplecov-html (~> 0.10.0)
34
- simplecov-html (0.10.0)
35
- url (0.3.2)
30
+ rspec-support (~> 3.9.0)
31
+ rspec-support (3.9.3)
32
+ simplecov (0.18.5)
33
+ docile (~> 1.1)
34
+ simplecov-html (~> 0.11)
35
+ simplecov-html (0.12.2)
36
36
 
37
37
  PLATFORMS
38
38
  ruby
39
39
 
40
40
  DEPENDENCIES
41
- bundler (~> 1.3)
41
+ bundler
42
+ byebug
42
43
  codecov
43
44
  rake
44
45
  rspec
@@ -46,4 +47,4 @@ DEPENDENCIES
46
47
  smart_ioc!
47
48
 
48
49
  BUNDLED WITH
49
- 1.16.1
50
+ 1.17.2
@@ -1,4 +1,5 @@
1
1
  require 'smart_ioc/version'
2
+ require 'benchmark'
2
3
 
3
4
  module SmartIoC
4
5
  autoload :Args, 'smart_ioc/args'
@@ -29,15 +30,32 @@ module SmartIoC
29
30
  require 'smart_ioc/railtie' if defined?(Rails)
30
31
 
31
32
  class << self
33
+ def is_benchmark_mode
34
+ @benchmark_mode
35
+ end
36
+
32
37
  # @param package_name [String or Symbol] package name for bean definitions
33
38
  # @param dir [String] absolute path with bean definitions
34
39
  # @return nil
35
40
  def find_package_beans(package_name, dir)
36
- bean_locator = SmartIoC::BeanLocator.new
37
- bean_locator.locate_beans(package_name.to_sym, dir)
41
+ time = Benchmark.realtime do
42
+ bean_locator = SmartIoC::BeanLocator.new
43
+ bean_locator.locate_beans(package_name.to_sym, dir)
44
+ end
45
+
46
+ time *= 1000
47
+
48
+ if is_benchmark_mode
49
+ puts "Search finished for '#{package_name}'. Time taken: #{"%.2f ms" % time}"
50
+ end
51
+
38
52
  nil
39
53
  end
40
54
 
55
+ def benchmark_mode(flag)
56
+ @benchmark_mode = !!flag
57
+ end
58
+
41
59
  # Load all beans (usually required for production env)
42
60
  def load_all_beans
43
61
  BeanLocations.all_bean_names.each do |bean|
@@ -55,7 +73,7 @@ module SmartIoC
55
73
  Container.get_instance
56
74
  end
57
75
 
58
- [:register_bean, :get_bean_definition_by_class,
76
+ [:register_bean, :get_bean_definition,
59
77
  :set_extra_context_for_package, :get_bean, :clear_scopes,
60
78
  :force_clear_scopes, :set_load_proc].each do |name|
61
79
  define_method name do |*args, &block|
@@ -64,3 +82,5 @@ module SmartIoC
64
82
  end
65
83
  end
66
84
  end
85
+
86
+ require 'smart_ioc/bean'
@@ -1,7 +1,7 @@
1
1
  module SmartIoC::Args
2
2
  def check_arg(value, name, klass)
3
3
  if !value.is_a?(klass)
4
- raise ArgumentError, ":#{name} should be a #{klass}"
4
+ raise ArgumentError, ":#{name} should be a #{klass}. Got #{value.class}: #{value.inspect}"
5
5
  end
6
6
  end
7
7
 
@@ -0,0 +1,33 @@
1
+ def bean(bean_name, &proc)
2
+ raise ArgumentError, "name should be a Symbol" if !bean_name.is_a?(Symbol)
3
+ raise ArgumentError, "proc should be provided" if !block_given?
4
+
5
+ klass = Class.new do
6
+ include SmartIoC::Iocify
7
+ end
8
+
9
+ klass.instance_variable_set(:@anonymous_bean, true)
10
+ klass.instance_exec(&proc)
11
+
12
+ file_path = caller[0].split(':').first
13
+ package = SmartIoC::BeanLocations.get_bean_package(file_path)
14
+
15
+ klass.instance_exec do
16
+ bean(
17
+ bean_name,
18
+ file_path: file_path,
19
+ scope: instance_variable_get(:@scope) || nil,
20
+ package: instance_variable_get(:@package) || package,
21
+ instance: instance_variable_get(:@instance) || false,
22
+ factory_method: instance_variable_get(:@factory_method) || nil,
23
+ context: instance_variable_get(:@context) || nil,
24
+ after_init: instance_variable_get(:@after_init) || nil
25
+ )
26
+ end
27
+
28
+ (klass.instance_variable_get(:@injects) || []).each do |inject|
29
+ klass.register_inject(inject[:bean_name], ref: inject[:ref], from: inject[:from])
30
+ end
31
+
32
+ klass
33
+ end
@@ -2,9 +2,9 @@ class SmartIoC::BeanDefinition
2
2
  include SmartIoC::Args
3
3
 
4
4
  attr_reader :name, :package, :path, :klass, :scope, :instance, :factory_method,
5
- :context, :dependencies
5
+ :context, :dependencies, :after_init
6
6
 
7
- def initialize(name:, package:, path:, klass:, scope:, context:, instance:, factory_method:)
7
+ def initialize(name:, package:, path:, klass:, scope:, context:, instance:, factory_method:, after_init:)
8
8
  not_nil(name, :name)
9
9
  not_nil(package, :package)
10
10
  not_nil(path, :path)
@@ -20,6 +20,7 @@ class SmartIoC::BeanDefinition
20
20
  @scope = scope
21
21
  @instance = instance
22
22
  @factory_method = factory_method
23
+ @after_init = after_init
23
24
  @context = context
24
25
 
25
26
  @dependencies = []
@@ -46,12 +47,15 @@ class SmartIoC::BeanDefinition
46
47
  end
47
48
 
48
49
  def ==(bean_definition)
49
- bean_definition.klass == @klass
50
+ bean_definition.name == @name && bean_definition.package == @package && bean_definition.context == @context
51
+ end
52
+
53
+ def singleton?
54
+ SmartIoC::Scopes::Singleton::VALUE == @scope
50
55
  end
51
56
 
52
57
  def inspect
53
58
  str = []
54
- str << "class: #{@klass}"
55
59
  str << "name: :#{@name}"
56
60
  str << "package: :#{@package}"
57
61
  str << "context: :#{@context}"
@@ -21,12 +21,14 @@ class SmartIoC::BeanDefinitionsStorage
21
21
 
22
22
  if existing_bd
23
23
  error_msg =
24
- %Q(Not able to add bean to definitions storage.
24
+ %Q(Unable to add bean to definitions storage.
25
25
  Bean definition already exists.
26
+
26
27
  New bean details:
27
- #{bean_definition.inspect}
28
+ #{bean_definition.inspect}
29
+
28
30
  Existing bean details:
29
- #{existing_bd.inspect})
31
+ #{existing_bd.inspect})
30
32
 
31
33
  raise ArgumentError, error_msg
32
34
  end
@@ -46,8 +48,16 @@ Existing bean details:
46
48
  # @param klass [Class] bean class
47
49
  # @return bean definition [BeanDefinition] or nil
48
50
  def find_by_class(klass)
49
- klass_str = klass.to_s
50
- @collection.detect {|bd| bd.klass.to_s == klass_str}
51
+ @collection.detect {|bd| bd.klass == klass}
52
+ end
53
+
54
+ # Returns bean definition for specific class
55
+ # @param bean_name [Symbol]
56
+ # @param package [Symbol]
57
+ # @param context [Symbol]
58
+ # @return bean definition [BeanDefinition] or nil
59
+ def find_bean(bean_name, package, context)
60
+ @collection.detect {|bd| bd.name == bean_name && bd.package == package && bd.context == context}
51
61
  end
52
62
 
53
63
  def filter_by(bean_name, package = nil, context = nil)
@@ -73,10 +83,21 @@ Existing bean details:
73
83
  # @bean_name [Symbol] bean name
74
84
  # @package [Symbol, nil] package name
75
85
  # @context [Symbol, nil] context
86
+ # @package [Symbol, nil] parent_package name of parent package
76
87
  # @raises AmbiguousBeanDefinition if multiple bean definitions are found
77
- def find(bean_name, package = nil, context = nil)
88
+ def find(bean_name, package = nil, context = nil, parent_package = nil)
78
89
  bds = filter_by_with_drop_to_default_context(bean_name, package, context)
79
90
 
91
+ if bds.size > 1 && parent_package
92
+ bean_definition = bds.detect do |bd|
93
+ bd.package == parent_package
94
+ end
95
+
96
+ if bean_definition
97
+ bds = [bean_definition]
98
+ end
99
+ end
100
+
80
101
  if bds.size > 1
81
102
  raise AmbiguousBeanDefinition.new(bean_name, bds)
82
103
  elsif bds.size == 0
@@ -1,5 +1,3 @@
1
- require 'thread'
2
-
3
1
  # Instantiates beans according to their scopes
4
2
  class SmartIoC::BeanFactory
5
3
  include SmartIoC::Errors
@@ -28,142 +26,68 @@ class SmartIoC::BeanFactory
28
26
  # Get bean from the container by it's name, package, context
29
27
  # @param bean_name [Symbol] bean name
30
28
  # @param package [Symbol] package name
29
+ # @param parent_bean_definition [SmartIoC::BeanDefinition] parent bean definition
31
30
  # @param context [Symbol] context
32
31
  # @return bean instance
33
32
  # @raise [ArgumentError] if bean is not found
34
33
  # @raise [ArgumentError] if ambiguous bean definition was found
35
- def get_bean(bean_name, package: nil, context: nil)
34
+ def get_bean(bean_name, package: nil, parent_bean_definition: nil, context: nil, parent_bean_name: nil)
36
35
  check_arg(bean_name, :bean_name, Symbol)
37
36
  check_arg(package, :package, Symbol) if package
37
+ check_arg(parent_bean_definition, :parent_bean_definition, SmartIoC::BeanDefinition) if parent_bean_definition
38
38
  check_arg(context, :context, Symbol) if context
39
39
 
40
- @semaphore.synchronize do
41
- result = get_or_build_bean(bean_name, package, context)
42
- end
43
- end
44
-
45
- private
46
-
47
- def get_or_build_bean(bean_name, package, context, history = Set.new)
48
40
  @bean_file_loader.require_bean(bean_name)
49
41
 
50
- context = autodetect_context(bean_name, package, context)
51
- bean_definition = @bean_definitions_storage.find(bean_name, package, context)
52
- scope = get_scope(bean_definition)
53
- scope_bean = scope.get_bean(bean_definition.klass)
54
- is_recursive = history.include?(bean_name)
55
-
56
- history << bean_name
57
-
58
- if scope_bean && scope_bean.loaded
59
- update_dependencies(scope_bean.bean, bean_definition)
60
- scope_bean.bean
61
- else
62
- if is_recursive
63
- raise LoadRecursion.new(bean_definition)
64
- end
65
-
66
- beans_cache = init_bean_definition_cache(bean_definition)
42
+ parent_package_name = parent_bean_definition ? parent_bean_definition.package : nil
43
+ context = autodetect_context(bean_name, package, parent_package_name, context, parent_bean_name)
44
+ bean_definition = @bean_definitions_storage.find(bean_name, package, context, parent_package_name)
45
+ scope = get_scope(bean_definition)
46
+ bean = scope.get_bean(bean_definition.klass)
67
47
 
68
- autodetect_bean_definitions_for_dependencies(bean_definition)
69
- preload_beans(bean_definition, beans_cache[bean_definition])
70
- load_bean(bean_definition, beans_cache)
48
+ if !bean
49
+ bean = init_bean(bean_definition)
71
50
  end
72
- end
73
51
 
74
- def load_bean(bean_definition, beans_cache)
75
- bd_opts = beans_cache[bean_definition]
76
- scope_bean = bd_opts[:scope_bean]
52
+ scope.save_bean(bean_definition.klass, bean)
53
+ bean
54
+ rescue SmartIoC::Errors::AmbiguousBeanDefinition => e
55
+ e.parent_bean_definition = parent_bean_definition
56
+ raise e
57
+ end
77
58
 
78
- bean_definition.dependencies.each do |dependency|
79
- bd = dependency.bean_definition
80
- dep_db_opts = bd_opts[:dependencies][dependency.bean_definition]
81
- dep_scope_bean = dep_db_opts[:scope_bean]
82
- dep_bean = load_bean(bd, bd_opts[:dependencies])
59
+ private
83
60
 
84
- scope_bean.bean.instance_variable_set(:"@#{dependency.bean}", dep_bean)
61
+ def init_bean(bean_definition)
62
+ bean = if bean_definition.is_instance?
63
+ bean_definition.klass.allocate
64
+ else
65
+ bean_definition.klass
85
66
  end
86
67
 
87
- if !scope_bean.loaded
88
- scope_bean.set_bean(scope_bean.bean.send(bean_definition.factory_method), true)
68
+ if bean_definition.has_factory_method?
69
+ bean = bean.send(bean_definition.factory_method)
89
70
  end
90
71
 
91
- scope_bean.bean
92
- end
93
-
94
- def inject_beans(bean_definition, beans_cache)
95
- bean = beans_cache[:scope_bean].bean
96
- bean_definition.dependencies.each do |dependency|
97
- bd = dependency.bean_definition
98
- dep_bean = beans_cache[:dependencies][bd][:scope_bean].bean
99
- bean.instance_variable_set(:"@#{dependency.bean}", dep_bean)
100
- inject_beans(bd, beans_cache[:dependencies][bd])
72
+ if bean_definition.after_init
73
+ bean.send(bean_definition.after_init)
101
74
  end
102
- end
103
-
104
- def init_bean_definition_cache(bean_definition)
105
- {
106
- bean_definition => {
107
- scope_bean: nil,
108
- dependencies: {
109
- }
110
- }
111
- }
112
- end
113
-
114
- def update_dependencies(bean, bean_definition, updated_beans = {})
115
- bean_definition.dependencies.each do |dependency|
116
- bd = autodetect_bean_definition(
117
- dependency.ref, dependency.package, bean_definition.package
118
- )
119
-
120
- scope = get_scope(bean_definition)
121
- dep_bean = updated_beans[bd]
122
-
123
- if !dep_bean && scope_bean = scope.get_bean(bd.klass)
124
- dep_bean = scope_bean.bean
125
- end
126
-
127
- if !dep_bean
128
- dep_bean = get_or_build_bean(bd.name, bd.package, bd.context)
129
75
 
130
- bean.instance_variable_set(:"@#{dependency.bean}", dep_bean)
131
-
132
- if !scope.is_a?(SmartIoC::Scopes::Prototype)
133
- updated_beans[bd] = dep_bean
134
- end
135
- else
136
- update_dependencies(dep_bean, bd, updated_beans)
137
- end
138
- end
76
+ bean
139
77
  end
140
78
 
141
- def autodetect_context(bean_name, package, context)
79
+ def autodetect_context(bean_name, package, parent_bean_package, context, parent_bean_name)
142
80
  return context if context
143
81
 
144
82
  if package
145
83
  @extra_package_contexts.get_context(package)
146
84
  else
147
- bean_definition = autodetect_bean_definition(bean_name, package, nil)
148
- bean_definition.context
149
- end
150
- end
151
-
152
- def autodetect_bean_definitions_for_dependencies(bean_definition)
153
- bean_definition.dependencies.each do |dependency|
154
- next if dependency.bean_definition
155
-
156
- @bean_file_loader.require_bean(dependency.ref)
157
-
158
- dependency.bean_definition = autodetect_bean_definition(
159
- dependency.ref, dependency.package, bean_definition.package
160
- )
161
-
162
- autodetect_bean_definitions_for_dependencies(dependency.bean_definition)
85
+ bean_definition = autodetect_bean_definition(bean_name, package, parent_bean_package, parent_bean_name)
86
+ @extra_package_contexts.get_context(bean_definition.package)
163
87
  end
164
88
  end
165
89
 
166
- def autodetect_bean_definition(bean, package, parent_bean_package)
90
+ def autodetect_bean_definition(bean, package, parent_bean_package, parent_bean_name)
167
91
  if package
168
92
  bean_context = @extra_package_contexts.get_context(package)
169
93
  bds = @bean_definitions_storage.filter_by_with_drop_to_default_context(bean, package, bean_context)
@@ -196,7 +120,13 @@ class SmartIoC::BeanFactory
196
120
  end
197
121
 
198
122
  if smart_bds.size > 1
199
- 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"
123
+ raise ArgumentError.new(
124
+ %Q(Unable to autodetect bean :#{bean}#{parent_bean_name ? " for bean :#{parent_bean_name}" : ''}.
125
+ Several definitions were found:\n
126
+ #{smart_bds.map(&:inspect).join("\n\n")}.
127
+ Set package directly for injected dependency
128
+ )
129
+ )
200
130
  end
201
131
 
202
132
  if smart_bds.size == 0
@@ -206,77 +136,6 @@ class SmartIoC::BeanFactory
206
136
  return smart_bds.first
207
137
  end
208
138
 
209
- def preload_beans(bean_definition, beans_cache)
210
- scope = get_scope(bean_definition)
211
-
212
- if scope_bean = scope.get_bean(bean_definition.klass)
213
- beans_cache[:scope_bean] = scope_bean
214
- else
215
- preload_bean_instance(bean_definition, beans_cache)
216
- end
217
-
218
- bean_definition.dependencies.each do |dependency|
219
- bd = dependency.bean_definition
220
-
221
- next if beans_cache[:dependencies].has_key?(bd)
222
-
223
- dep_bean_cache = init_bean_definition_cache(bd)
224
- beans_cache[:dependencies].merge!(dep_bean_cache)
225
- preload_beans(bd, dep_bean_cache[bd])
226
- end
227
- end
228
-
229
- def preload_bean_instance(bean_definition, beans_cache)
230
- return if beans_cache[:scope_bean]
231
-
232
- scope = get_scope(bean_definition)
233
- scope_bean = scope.get_bean(bean_definition.klass)
234
-
235
- if scope_bean
236
- beans_cache[:scope_bean] = scope_bean
237
- return scope_bean
238
- end
239
-
240
- bean = if bean_definition.is_instance?
241
- bean_definition.klass.allocate
242
- else
243
- bean_definition.klass
244
- end
245
-
246
- scope_bean = SmartIoC::Scopes::Bean.new(bean, !bean_definition.has_factory_method?)
247
-
248
- scope.save_bean(bean_definition.klass, scope_bean)
249
- beans_cache[:scope_bean] = scope_bean
250
-
251
- scope_bean
252
- end
253
-
254
- def init_factory_bean(bean_definition, bd_opts)
255
- scope_bean = bd_opts[:scope_bean]
256
-
257
- if !scope_bean.loaded
258
- scope_bean.set_bean(scope_bean.bean.send(bean_definition.factory_method), true)
259
- end
260
- end
261
-
262
- def get_cross_refference(refer_bean_definitions, current_bean_definition, seen_bean_definitions = [])
263
- current_bean_definition.dependencies.each do |dependency|
264
- bd = dependency.bean_definition
265
-
266
- next if seen_bean_definitions.include?(bd)
267
-
268
- if refer_bean_definitions.include?(bd)
269
- return bd
270
- end
271
-
272
- if crbd = get_cross_refference(refer_bean_definitions, bd, seen_bean_definitions + [bd])
273
- return crbd
274
- end
275
- end
276
-
277
- nil
278
- end
279
-
280
139
  def all_scopes
281
140
  [@singleton_scope, @prototype_scope, @thread_scope]
282
141
  end