callme 0.5.0 → 0.6.0

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.
@@ -1,30 +1,31 @@
1
- # Stores bean specific data: bean class, name,
2
- # scope and bean dependencies
3
- class Callme::BeanMetadata
4
- attr_reader :name, :bean_class, :scope, :instance, :factory_method, :attrs
1
+ # Stores dep specific data: dep class, name, contract,
2
+ # scope and dep dependencies
3
+ class Callme::DepMetadata
4
+ attr_reader :name, :dep_class, :scope, :instance, :factory_method, :attrs, :contract
5
5
 
6
6
  # Constructor
7
- # @param name [Symbol] bean name
8
- # @params options [Hash] includes bean class and scope
9
- # @params &block [Proc] bean dependencies, has the following structure:
7
+ # @param name [Symbol] dep name
8
+ # @params options [Hash] includes dep class and scope
9
+ # @params &block [Proc] dep dependencies, has the following structure:
10
10
  # do |c|
11
11
  # attr :some_dependency, ref: :dependency_name
12
12
  # arg :another_dependency, ref: :another_dependency_name
13
13
  # end
14
14
  # here attr means setter injection, arg means constructon injects
15
- # +some_dependency+ is an attr_accessor defined in the bean class,
15
+ # +some_dependency+ is an attr_accessor defined in the dep class,
16
16
  # +ref+ specifies what dependency from container to use to set the attribute
17
17
  def initialize(name, options, &block)
18
18
  Callme::ArgsValidator.has_key!(options, :class)
19
19
 
20
20
  @name = name
21
- @bean_class = options[:class]
21
+ @dep_class = options[:class]
22
+ @contract = options[:contract]
22
23
  @scope = options[:scope] || :singleton
23
24
  @instance = options[:instance].nil? ? true : options[:instance]
24
25
  @factory_method = options[:factory_method]
25
26
  @attrs = []
26
27
 
27
- fetch_attrs!(@bean_class)
28
+ fetch_attrs!(@dep_class)
28
29
 
29
30
  if block
30
31
  Dsl.new(@attrs).instance_exec(&block)
@@ -35,7 +36,7 @@ class Callme::BeanMetadata
35
36
  if klass.respond_to?(:_callme_injectable_attrs)
36
37
  klass._callme_injectable_attrs.each do |attr, options|
37
38
  options[:ref] ||= attr
38
- @attrs << Callme::BeanMetadata::Attribute.new(attr, options)
39
+ @attrs << Callme::DepMetadata::Attribute.new(attr, options)
39
40
  end
40
41
  end
41
42
  end
@@ -44,6 +45,10 @@ class Callme::BeanMetadata
44
45
  !!@factory_method
45
46
  end
46
47
 
48
+ def has_contract?
49
+ !!@contract
50
+ end
51
+
47
52
  class Attribute
48
53
  attr_reader :name, :ref
49
54
 
@@ -60,11 +65,11 @@ class Callme::BeanMetadata
60
65
  end
61
66
 
62
67
  def attr(name, options)
63
- @attrs << Callme::BeanMetadata::Attribute.new(name, options)
68
+ @attrs << Callme::DepMetadata::Attribute.new(name, options)
64
69
  end
65
70
 
66
71
  def arg(name, options)
67
- @args << Callme::BeanMetadata::Attribute.new(name, options)
72
+ @args << Callme::DepMetadata::Attribute.new(name, options)
68
73
  end
69
74
  end
70
75
  end
@@ -0,0 +1,32 @@
1
+ # Storage of dep metadatas
2
+ class Callme::DepsMetadataStorage
3
+ def initialize(dep_metadatas = {})
4
+ @dep_metadatas = dep_metadatas
5
+ end
6
+
7
+ # Finds dep metadata in storage by it's name
8
+ # @param name [Symbol] dep metadata name
9
+ # @return dep metadata
10
+ def by_name(name)
11
+ @dep_metadatas[name]
12
+ end
13
+
14
+ # Saves a given +dep_metadata+ to the storage
15
+ # @param dep_metadata [DepMetadata] dep metadata for saving
16
+ def put(dep_metadata)
17
+ @dep_metadatas[dep_metadata.name] = dep_metadata
18
+ end
19
+
20
+ def values
21
+ @dep_metadatas.values
22
+ end
23
+
24
+ def keys
25
+ @dep_metadatas.keys
26
+ end
27
+
28
+ # Creates an independent copy of this instance
29
+ def copy
30
+ self.class.new(@dep_metadatas.dup)
31
+ end
32
+ end
@@ -1,10 +1,24 @@
1
1
  module Callme::Errors
2
2
  # Thrown when a service cannot be located by name.
3
- class MissingBeanError < StandardError; end
3
+ class MissingDepError < StandardError; end
4
4
 
5
5
  # Thrown when a duplicate service is registered.
6
- class DuplicateBeanError < StandardError; end
6
+ class DuplicateDepError < StandardError; end
7
7
 
8
- # Thrown when an unsupported bean scope is specified.
8
+ # Thrown when an unsupported dep scope is specified.
9
9
  class UnsupportedScopeError < StandardError; end
10
+
11
+ class DependencyContractMissingMethodsException < StandardError; end
12
+
13
+ class DependencyContractInvalidParametersException < StandardError
14
+ def initialize(method, parameters)
15
+ @method = method
16
+ @parameters = parameters
17
+ end
18
+
19
+ def to_s
20
+ parameter_names = @parameters.join(', ')
21
+ "The method signature of method: '#{@method}' does not match the contract parameters: '#{parameter_names}'"
22
+ end
23
+ end
10
24
  end
@@ -1,36 +1,37 @@
1
- # Extend object with the bean injection mechanism
1
+ # Extend object with the dep injection mechanism
2
2
  # Example of usage:
3
3
  # class Bar
4
4
  # end
5
5
  #
6
6
  # class Foo
7
+ # include Callme::Inject
7
8
  # inject :bar
8
9
  # or:
9
10
  # inject :some_bar, ref: bar
10
11
  # end
11
12
  #
12
13
  # ioc_container[:foo].bar == ioc_container[:bar]
13
- class Object
14
-
15
- class << self
16
-
17
- def inject(dependency_name, options = {})
18
- unless dependency_name.is_a?(Symbol)
19
- raise ArgumentError, "dependency name should be a symbol"
14
+ module Callme
15
+ module Inject
16
+ def self.included(base)
17
+ base.instance_eval do
18
+ def inject(dependency_name, options = {})
19
+ unless dependency_name.is_a?(Symbol)
20
+ raise ArgumentError, "dependency name should be a symbol"
21
+ end
22
+ unless options.is_a?(Hash)
23
+ raise ArgumentError, "second argument for inject method should be a Hash"
24
+ end
25
+ unless respond_to?(:_callme_injectable_attrs)
26
+ class_attribute :_callme_injectable_attrs
27
+ self._callme_injectable_attrs = { dependency_name => options.dup }
28
+ else
29
+ self._callme_injectable_attrs =
30
+ self._callme_injectable_attrs.merge(dependency_name => options.dup)
31
+ end
32
+ attr_accessor dependency_name
33
+ end
20
34
  end
21
- unless options.is_a?(Hash)
22
- raise ArgumentError, "second argument for inject method should be a Hash"
23
- end
24
- unless respond_to?(:_callme_injectable_attrs)
25
- class_attribute :_callme_injectable_attrs
26
- self._callme_injectable_attrs = { dependency_name => options.dup }
27
- else
28
- self._callme_injectable_attrs =
29
- self._callme_injectable_attrs.merge(dependency_name => options.dup)
30
- end
31
- attr_accessor dependency_name
32
35
  end
33
-
34
36
  end
35
-
36
37
  end
@@ -1,25 +1,25 @@
1
- # Prototype scope instantiates new bean instance
2
- # on each +get_bean+ call
1
+ # Prototype scope instantiates new dep instance
2
+ # on each +get_dep+ call
3
3
  class Callme::Scopes::PrototypeScope
4
4
 
5
5
  # Constructon
6
- # @param bean_factory bean factory
7
- def initialize(bean_factory)
8
- @bean_factory = bean_factory
6
+ # @param dep_factory dep factory
7
+ def initialize(dep_factory)
8
+ @dep_factory = dep_factory
9
9
  end
10
10
 
11
- # Get new bean instance
12
- # @param bean_metadata [BeanMetadata] bean metadata
13
- # @returns bean instance
14
- def get_bean(bean_metadata)
15
- @bean_factory.create_bean_and_save(bean_metadata, {})
11
+ # Get new dep instance
12
+ # @param dep_metadata [DepMetadata] dep metadata
13
+ # @returns dep instance
14
+ def get_dep(dep_metadata)
15
+ @dep_factory.create_dep_and_save(dep_metadata, {})
16
16
  end
17
17
 
18
- # Delete bean from scope,
19
- # because Prototype scope doesn't store bean
18
+ # Delete dep from scope,
19
+ # because Prototype scope doesn't store dep
20
20
  # then do nothing here
21
21
  #
22
- # @param bean_metadata [BeanMetadata] bean metadata
23
- def delete_bean(bean_metadata)
22
+ # @param dep_metadata [DepMetadata] dep metadata
23
+ def delete_dep(dep_metadata)
24
24
  end
25
25
  end
@@ -1,33 +1,34 @@
1
1
  require 'request_store'
2
2
 
3
- # Request scope instantiates new bean instance
3
+ # Request scope instantiates new dep instance
4
4
  # on each new HTTP request
5
5
  class Callme::Scopes::RequestScope
6
6
 
7
7
  # Constructon
8
- # @param bean_factory bean factory
9
- def initialize(bean_factory)
10
- @bean_factory = bean_factory
8
+ # @param dep_factory dep factory
9
+ def initialize(dep_factory)
10
+ @dep_factory = dep_factory
11
11
  end
12
12
 
13
- # Returns a bean from the +RequestStore+
13
+ # Returns a dep from the +RequestStore+
14
14
  # RequestStore is a wrapper for Thread.current
15
15
  # which clears it on each new HTTP request
16
16
  #
17
- # @param bean_metadata [BeanMetadata] bean metadata
18
- # @returns bean instance
19
- def get_bean(bean_metadata)
20
- RequestStore.store[:_callme_beans] ||= {}
21
- if bean = RequestStore.store[:_callme_beans][bean_metadata.name]
22
- bean
23
- else
24
- @bean_factory.create_bean_and_save(bean_metadata, RequestStore.store[:_callme_beans])
25
- end
17
+ # @param dep_metadata [DepMetadata] dep metadata
18
+ # @returns dep instance
19
+ def get_dep(dep_metadata)
20
+ store[dep_metadata.name] || @dep_factory.create_dep_and_save(dep_metadata, store)
26
21
  end
27
22
 
28
- # Delete bean from scope
29
- # @param bean_metadata [BeanMetadata] bean metadata
30
- def delete_bean(bean_metadata)
31
- RequestStore.store[:_callme_beans].delete(bean_metadata.name)
23
+ # Delete dep from scope
24
+ # @param dep_metadata [DepMetadata] dep metadata
25
+ def delete_dep(dep_metadata)
26
+ store.delete(dep_metadata.name)
27
+ end
28
+
29
+ private
30
+
31
+ def store
32
+ RequestStore.store[:_callme_deps] ||= {}
32
33
  end
33
34
  end
@@ -1,29 +1,30 @@
1
- # Singleton scope returns the same bean instance
1
+ # Singleton scope returns the same dep instance
2
2
  # on each call
3
3
  class Callme::Scopes::SingletonScope
4
4
 
5
5
  # Constructon
6
- # @param bean_factory bean factory
7
- def initialize(bean_factory)
8
- @beans = {}
9
- @bean_factory = bean_factory
6
+ # @param dep_factory dep factory
7
+ def initialize(dep_factory)
8
+ @dep_factory = dep_factory
10
9
  end
11
10
 
12
- # Returns the same bean instance
11
+ # Returns the same dep instance
13
12
  # on each call
14
- # @param bean_metadata [BeanMetadata] bean metadata
15
- # @returns bean instance
16
- def get_bean(bean_metadata)
17
- if bean = @beans[bean_metadata.name]
18
- bean
19
- else
20
- @bean_factory.create_bean_and_save(bean_metadata, @beans)
21
- end
13
+ # @param dep_metadata [DepMetadata] dep metadata
14
+ # @returns dep instance
15
+ def get_dep(dep_metadata)
16
+ store[dep_metadata.name] || @dep_factory.create_dep_and_save(dep_metadata, store)
22
17
  end
23
18
 
24
- # Delete bean from scope
25
- # @param bean_metadata [BeanMetadata] bean metadata
26
- def delete_bean(bean_metadata)
27
- @beans.delete(bean_metadata.name)
19
+ # Delete dep from scope
20
+ # @param dep_metadata [DepMetadata] dep metadata
21
+ def delete_dep(dep_metadata)
22
+ store.delete(dep_metadata.name)
23
+ end
24
+
25
+ private
26
+
27
+ def store
28
+ @deps ||= {}
28
29
  end
29
30
  end
@@ -1,3 +1,3 @@
1
1
  module Callme
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -11,17 +11,17 @@ describe Callme::Container do
11
11
  class Printer
12
12
  end
13
13
 
14
- describe "bean definitions" do
14
+ describe "dep definitions" do
15
15
  let(:container) do
16
16
  container = Callme::Container.new
17
- container.bean(:appender, class: Appender)
18
- container.bean(:logger, class: Logger) do
17
+ container.dep(:appender, class: Appender)
18
+ container.dep(:logger, class: Logger) do
19
19
  attr :appender, ref: :appender
20
20
  end
21
- container.bean(:printer, class: Printer, instance: false)
21
+ container.dep(:printer, class: Printer, instance: false)
22
22
  container
23
23
  end
24
- it "should instanciate bean and it's dependencies" do
24
+ it "should instanciate dep and it's dependencies" do
25
25
  container[:logger].should be_a(Logger)
26
26
  container[:logger].appender.should be_a(Appender)
27
27
  container[:printer].should be(Printer)
@@ -33,46 +33,46 @@ describe Callme::Container do
33
33
  end
34
34
  end
35
35
 
36
- describe "eager_load_bean_classes" do
36
+ describe "eager_load_dep_classes" do
37
37
  let(:container) do
38
38
  container = Callme::Container.new
39
- container.bean(:appender, class: 'Appender')
40
- container.bean(:logger, class: 'Logger') do
39
+ container.dep(:appender, class: 'Appender')
40
+ container.dep(:logger, class: 'Logger') do
41
41
  attr :appender, ref: :appender
42
42
  end
43
- container.bean(:printer, class: 'Printer', instance: false)
43
+ container.dep(:printer, class: 'Printer', instance: false)
44
44
  container
45
45
  end
46
46
 
47
- it "should eager load bean classes" do
48
- container.eager_load_bean_classes
47
+ it "should eager load dep classes" do
48
+ container.eager_load_dep_classes
49
49
  end
50
50
  end
51
51
 
52
52
 
53
- describe "#replace_bean" do
54
- it "should replace bean definition" do
53
+ describe "#replace_dep" do
54
+ it "should replace dep definition" do
55
55
  container = Callme::Container.new
56
- container.bean(:appender, class: Appender)
56
+ container.dep(:appender, class: Appender)
57
57
  container[:appender].should be_a(Appender)
58
58
 
59
- container.replace_bean(:appender, class: Logger)
59
+ container.replace_dep(:appender, class: Logger)
60
60
  container[:appender].should be_a(Logger)
61
61
  end
62
62
  end
63
63
 
64
- describe "passing bean definitions to container constructor" do
64
+ describe "passing dep definitions to container constructor" do
65
65
  let(:resource) do
66
66
  Proc.new do |c|
67
- c.bean(:appender, class: 'Appender')
68
- c.bean(:logger, class: Logger) do
67
+ c.dep(:appender, class: 'Appender')
68
+ c.dep(:logger, class: Logger) do
69
69
  attr :appender, ref: :appender
70
70
  end
71
71
  end
72
72
  end
73
73
 
74
- it "should instanciate given bean definitions" do
75
- container = Callme::Container.new_with_beans([resource])
74
+ it "should instanciate given dep definitions" do
75
+ container = Callme::Container.new_with_deps([resource])
76
76
  container[:logger].should be_a(Logger)
77
77
  container[:appender].should be_a(Appender)
78
78
  end
@@ -81,6 +81,7 @@ describe Callme::Container do
81
81
 
82
82
  describe "inheritance" do
83
83
  class Form
84
+ include Callme::Inject
84
85
  inject :validator
85
86
  end
86
87
 
@@ -100,11 +101,11 @@ describe Callme::Container do
100
101
 
101
102
  let(:container) do
102
103
  Callme::Container.new do |c|
103
- c.bean(:circle, class: Circle)
104
- c.bean(:rectangle, class: Rectangle)
105
- c.bean(:validator, class: Validator)
106
- c.bean(:circle_validator, class: CircleValidator)
107
- c.bean(:rectangle_validator, class: RectangleValidator)
104
+ c.dep(:circle, class: Circle)
105
+ c.dep(:rectangle, class: Rectangle)
106
+ c.dep(:validator, class: Validator)
107
+ c.dep(:circle_validator, class: CircleValidator)
108
+ c.dep(:rectangle_validator, class: RectangleValidator)
108
109
  end
109
110
  end
110
111
 
@@ -114,8 +115,9 @@ describe Callme::Container do
114
115
  end
115
116
  end
116
117
 
117
- describe "bean scopes" do
118
+ describe "dep scopes" do
118
119
  class ContactsService
120
+ include Callme::Inject
119
121
  inject :contacts_repository
120
122
  inject :contacts_validator
121
123
  end
@@ -126,13 +128,13 @@ describe Callme::Container do
126
128
 
127
129
  let(:container) do
128
130
  container = Callme::Container.new
129
- container.bean(:contacts_repository, class: ContactsRepository, scope: :request)
130
- container.bean(:contacts_service, class: ContactsService, scope: :singleton)
131
- container.bean(:contacts_validator, class: ContactsValidator, scope: :prototype)
131
+ container.dep(:contacts_repository, class: ContactsRepository, scope: :request)
132
+ container.dep(:contacts_service, class: ContactsService, scope: :singleton)
133
+ container.dep(:contacts_validator, class: ContactsValidator, scope: :prototype)
132
134
  container
133
135
  end
134
136
 
135
- it "should instanciate bean with :request scope on each request" do
137
+ it "should instanciate dep with :request scope on each request" do
136
138
  first_repo = container[:contacts_service].contacts_repository
137
139
  second_repo = container[:contacts_service].contacts_repository
138
140
  first_repo.should == second_repo
@@ -141,7 +143,7 @@ describe Callme::Container do
141
143
  first_repo.should_not == third_repo
142
144
  end
143
145
 
144
- it "should instanciate bean with :prototype scope on each call" do
146
+ it "should instanciate dep with :prototype scope on each call" do
145
147
  first_validator = container[:contacts_service].contacts_validator
146
148
  second_validator = container[:contacts_service].contacts_validator
147
149
  first_validator.should_not == second_validator
@@ -161,21 +163,23 @@ describe Callme::Container do
161
163
 
162
164
  let(:container) do
163
165
  Callme::Container.new do |c|
164
- c.bean :config, class: Test::ConfigsFactory, factory_method: :load_config
166
+ c.dep :config, class: Test::ConfigsFactory, factory_method: :load_config
165
167
  end
166
168
  end
167
169
 
168
- it "should instantiate bean using factory method" do
170
+ it "should instantiate dep using factory method" do
169
171
  container[:config].should be_instance_of(Test::Config)
170
172
  end
171
173
  end
172
174
 
173
175
  describe "parent container" do
174
176
  class ContactBook
177
+ include Callme::Inject
175
178
  inject :contacts_repository
176
179
  inject :validator, ref: :contact_validator
177
180
  end
178
181
  class ContactBookService
182
+ include Callme::Inject
179
183
  inject :contacts_repository
180
184
  inject :validator, ref: :contact_validator
181
185
  end
@@ -192,20 +196,20 @@ describe Callme::Container do
192
196
 
193
197
  let(:parent){
194
198
  Callme::Container.new do |c|
195
- c.bean(:contacts_repository, class: ContactsRepository)
196
- c.bean(:contact_validator, class: ContactValidator)
197
- c.bean(:contact_book, class: ContactBook)
198
- c.bean(:contact_book_service, class: "ContactBookService")
199
+ c.dep(:contacts_repository, class: ContactsRepository)
200
+ c.dep(:contact_validator, class: ContactValidator)
201
+ c.dep(:contact_book, class: ContactBook)
202
+ c.dep(:contact_book_service, class: "ContactBookService")
199
203
  end
200
204
  }
201
205
 
202
206
  let(:container){
203
207
  Callme::Container.with_parent(parent) do |c|
204
- c.bean(:contact_validator, class: TestContactValidator)
208
+ c.dep(:contact_validator, class: TestContactValidator)
205
209
  end
206
210
  }
207
211
 
208
- it "works for direct beans" do
212
+ it "works for direct deps" do
209
213
  expect(container[:contact_validator]).to be_a(TestContactValidator)
210
214
  expect(container[:contact_book_service].validator).to be_a(TestContactValidator)
211
215
  end
@@ -216,7 +220,7 @@ describe Callme::Container do
216
220
 
217
221
  it "does not consider changes to parent" do
218
222
  expect(parent[:contact_book_service].validator).to be_a(ContactValidator)
219
- parent.replace_bean(:contact_validator, class: AnotherTestContactValidator)
223
+ parent.replace_dep(:contact_validator, class: AnotherTestContactValidator)
220
224
  expect(parent[:contact_validator]).to be_a(AnotherTestContactValidator)
221
225
  parent.reset!
222
226
  expect(parent[:contact_book_service].validator).to be_a(AnotherTestContactValidator)
@@ -225,7 +229,7 @@ describe Callme::Container do
225
229
 
226
230
  it "changes in child container do not affect parent container" do
227
231
  expect(parent[:contact_book_service].validator).to be_a(ContactValidator)
228
- container.replace_bean(:contact_validator, class: AnotherTestContactValidator)
232
+ container.replace_dep(:contact_validator, class: AnotherTestContactValidator)
229
233
  parent.reset!
230
234
  container.reset!
231
235
  expect(parent[:contact_validator]).to be_a(ContactValidator)