smart_ioc 0.2.5 → 0.3.6

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
- 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