ioc_rb 0.0.2

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.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ vendor/
2
+ tags
3
+ .bundle
4
+ .DS_Store
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ rvm:
2
+ - 2.0.0
3
+ script: "bundle exec rspec spec/"
data/.yardops ADDED
@@ -0,0 +1 @@
1
+ lib/**/*.rb
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in aggregate_builder.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'rspec'
8
+ gem 'debugger'
9
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,51 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ioc_rb (0.0.2)
5
+ activesupport
6
+ request_store
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (4.0.2)
12
+ i18n (~> 0.6, >= 0.6.4)
13
+ minitest (~> 4.2)
14
+ multi_json (~> 1.3)
15
+ thread_safe (~> 0.1)
16
+ tzinfo (~> 0.3.37)
17
+ atomic (1.1.14)
18
+ columnize (0.3.6)
19
+ debugger (1.6.5)
20
+ columnize (>= 0.3.1)
21
+ debugger-linecache (~> 1.2.0)
22
+ debugger-ruby_core_source (~> 1.3.1)
23
+ debugger-linecache (1.2.0)
24
+ debugger-ruby_core_source (1.3.1)
25
+ diff-lcs (1.2.5)
26
+ i18n (0.6.9)
27
+ minitest (4.7.5)
28
+ multi_json (1.8.4)
29
+ rake (10.1.1)
30
+ request_store (1.0.5)
31
+ rspec (2.14.1)
32
+ rspec-core (~> 2.14.0)
33
+ rspec-expectations (~> 2.14.0)
34
+ rspec-mocks (~> 2.14.0)
35
+ rspec-core (2.14.7)
36
+ rspec-expectations (2.14.4)
37
+ diff-lcs (>= 1.1.3, < 2.0)
38
+ rspec-mocks (2.14.4)
39
+ thread_safe (0.1.3)
40
+ atomic
41
+ tzinfo (0.3.38)
42
+
43
+ PLATFORMS
44
+ ruby
45
+
46
+ DEPENDENCIES
47
+ bundler (~> 1.3)
48
+ debugger
49
+ ioc_rb!
50
+ rake
51
+ rspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Albert Gazizov
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,94 @@
1
+ # IocRb [![Build Status](https://travis-ci.org/AlbertGazizov/ioc_rb.png)](https://travis-ci.org/AlbertGazizov/ioc_rb) [![Code Climate](https://codeclimate.com/github/AlbertGazizov/ioc_rb.png)](https://codeclimate.com/github/AlbertGazizov/ioc_rb)
2
+
3
+
4
+
5
+ IocRb is an Inversion of Control container for Ruby.
6
+ It takes advantage of the dynamic nature of Ruby to provide a rich and flexible approach to injecting dependencies.
7
+ It's inspired by SpringIoc and tries to give you the same features.
8
+
9
+ ## Usage
10
+ Lets say you have a Logger which has the Appender dependency
11
+
12
+ ```ruby
13
+ class Logger
14
+ attr_accessor :appender
15
+
16
+ def info(message)
17
+ # do some work with appender
18
+ end
19
+ end
20
+
21
+ class Appender
22
+ end
23
+ ```
24
+ To use Logger you need to inject the instance of Appender class, for example
25
+ using setter injection:
26
+ ```ruby
27
+ logger = Logger.new
28
+ logger.appender = Appender.new
29
+ logger.info('some message')
30
+ ```
31
+
32
+ IocRb eliminates the manual injection step and injects dependencies by itself.
33
+ To use it you need to instantiate IocRb::Container and pass dependency definitions(we call them beans) to it:
34
+ ```ruby
35
+ container = IocRb::Container.new do |c|
36
+ c.bean(:appender, class: Appender)
37
+ c.bean(:logger, class: Logger) do
38
+ attr :appender, ref: :appender
39
+ end
40
+ end
41
+ ```
42
+ Now you can get the Logger instance from container with already set dependencies and use it:
43
+ ```ruby
44
+ logger = container[:logger]
45
+ logger.info('some message')
46
+ ```
47
+
48
+ To simplify injection IocRb allows you specify dependencies inside of your class:
49
+ ```ruby
50
+ class Logger
51
+ inject :appender
52
+
53
+ def info(message)
54
+ # do some work with appender
55
+ end
56
+ end
57
+
58
+ class Appender
59
+ end
60
+ ```
61
+ With `inject` keyword you won't need to specify class dependencies in bean definition:
62
+ ```ruby
63
+ container = IocRb::Container.new do |c|
64
+ c.bean(:appender, class: Appender)
65
+ c.bean(:logger, class: Logger)
66
+ end
67
+ ```
68
+
69
+ ## Installation
70
+
71
+ Add this line to your application's Gemfile:
72
+
73
+ gem 'ioc_rb'
74
+
75
+ And then execute:
76
+
77
+ $ bundle
78
+
79
+ Or install it yourself as:
80
+
81
+ $ gem install ioc_rb
82
+
83
+ ## Contributing
84
+
85
+ 1. Fork it
86
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
87
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
88
+ 4. Push to the branch (`git push origin my-new-feature`)
89
+ 5. Create new Pull Request
90
+
91
+ # TODO
92
+ 1. Constructor based injection
93
+ 2. Scope registration, refactor BeanFactory. IocRb:Container.register_scope(SomeScope)
94
+ 3. Write documentation with more examples
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/ioc_rb.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ioc_rb/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ioc_rb"
8
+ spec.version = IocRb::VERSION
9
+ spec.authors = ["Albert Gazizov", "Ruslan Gatiyatov"]
10
+ spec.email = ["deeper4k@gmail.com", "ruslan.gatiyatov@gmail.com"]
11
+ spec.description = %q{Inversion of Controll Container}
12
+ spec.summary = %q{Inversion of Controll Container}
13
+ spec.homepage = "http://github.com/deeper4k/ioc_rb"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(spec)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "request_store"
22
+ spec.add_dependency "activesupport"
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ end
@@ -0,0 +1,49 @@
1
+ # Helper class for arguments validation
2
+ module IocRb::ArgsValidator
3
+ class << self
4
+
5
+ # Checks that specifid +obj+ is a symbol
6
+ # @param obj some object
7
+ # @param obj_name object's name, used to clarify error causer in exception
8
+ def is_symbol!(obj, obj_name)
9
+ unless obj.is_a?(Symbol)
10
+ raise ArgumentError, "#{obj_name} should be a Symbol"
11
+ end
12
+ end
13
+
14
+ # Checks that specifid +obj+ is an Array
15
+ # @param obj some object
16
+ # @param obj_name object's name, used to clarify error causer in exception
17
+ def is_array!(obj, obj_name)
18
+ unless obj.is_a?(Array)
19
+ raise ArgumentError, "#{obj_name} should be an Array"
20
+ end
21
+ end
22
+
23
+ # Checks that specifid +obj+ is a Hash
24
+ # @param obj some object
25
+ # @param obj_name object's name, used to clarify error causer in exception
26
+ def is_hash!(obj, obj_name)
27
+ unless obj.is_a?(Hash)
28
+ raise ArgumentError, "#{obj_name} should be a Hash"
29
+ end
30
+ end
31
+
32
+ # Checks that specifid +hash+ has a specified +key+
33
+ # @param hash some hash
34
+ # @param key hash's key
35
+ def has_key!(hash, key)
36
+ unless hash.has_key?(key)
37
+ raise ArgumentError, "#{hash} should has #{key} key"
38
+ end
39
+ end
40
+
41
+ # Checks that specified +block+ is given
42
+ # @param block some block
43
+ def block_given!(block)
44
+ unless block
45
+ raise ArgumentError, "Block should be given"
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,89 @@
1
+ require 'ioc_rb/scopes'
2
+ require 'ioc_rb/scopes/singleton_scope'
3
+ require 'ioc_rb/scopes/prototype_scope'
4
+ require 'ioc_rb/scopes/request_scope'
5
+
6
+ # Instantiates beans according to their scopes
7
+ class IocRb::BeanFactory
8
+
9
+ # Constructor
10
+ # @param beans_metadata_storage [BeansMetadataStorage] storage of bean metadatas
11
+ def initialize(beans_metadata_storage)
12
+ @beans_metadata_storage = beans_metadata_storage
13
+ @singleton_scope = IocRb::Scopes::SingletonScope.new(self)
14
+ @prototype_scope = IocRb::Scopes::PrototypeScope.new(self)
15
+ @request_scope = IocRb::Scopes::RequestScope.new(self)
16
+ end
17
+
18
+ # Get bean from the container by it's +name+.
19
+ # According to the bean scope it will be newly created or returned already
20
+ # instantiated bean
21
+ # @param [Symbol] bean name
22
+ # @return bean instance
23
+ # @raise MissingBeanError if bean with the specified name is not found
24
+ def get_bean(name)
25
+ bean_metadata = @beans_metadata_storage.by_name(name)
26
+ unless bean_metadata
27
+ raise IocRb::Errors::MissingBeanError, "Bean with name :#{name} is not defined"
28
+ end
29
+ get_bean_with_metadata(bean_metadata)
30
+ end
31
+
32
+ # Get bean by the specified +bean metadata+
33
+ # @param [BeanMetadata] bean metadata
34
+ # @return bean instance
35
+ def get_bean_with_metadata(bean_metadata)
36
+ case bean_metadata.scope
37
+ when :singleton
38
+ @singleton_scope.get_bean(bean_metadata)
39
+ when :prototype
40
+ @prototype_scope.get_bean(bean_metadata)
41
+ when :request
42
+ @request_scope.get_bean(bean_metadata)
43
+ else
44
+ raise IocRb::Errors::UnsupportedScopeError, "Bean with name :#{bean_metadata.name} has unsupported scope :#{bean_metadata.scope}"
45
+ end
46
+ end
47
+
48
+ # Create new bean instance according
49
+ # to the specified +bean_metadata+
50
+ # @param [BeanMetadata] bean metadata
51
+ # @return bean instance
52
+ # @raise MissingBeanError if some of bean dependencies are not found
53
+ def create_bean_and_save(bean_metadata, beans_storage)
54
+ if bean_metadata.bean_class.is_a?(Class)
55
+ bean_class = bean_metadata.bean_class
56
+ else
57
+ bean_class = bean_metadata.bean_class.split('::').inject(Object) do |mod, class_name|
58
+ mod.const_get(class_name)
59
+ end
60
+ end
61
+ bean = bean_metadata.instance ? bean_class.new : bean_class
62
+
63
+ # put to container first to prevent circular dependencies
64
+ beans_storage[bean_metadata.name] = bean
65
+
66
+ bean_metadata.attrs.each do |attr|
67
+ bean_metadata = @beans_metadata_storage.by_name(attr.ref)
68
+ unless bean_metadata
69
+ raise IocRb::Errors::MissingBeanError, "Bean with name :#{attr.ref} is not defined"
70
+ end
71
+ case bean_metadata.scope
72
+ when :singleton
73
+ bean.send("#{attr.name}=", get_bean(attr.ref))
74
+ when :prototype
75
+ bean.instance_variable_set(:@_ioc_rb_bean_factory, self)
76
+ bean.define_singleton_method(attr.name) do
77
+ @_ioc_rb_bean_factory.get_bean(attr.ref)
78
+ end
79
+ when :request
80
+ bean.instance_variable_set(:@_ioc_rb_bean_factory, self)
81
+ bean.define_singleton_method(attr.name) do
82
+ @_ioc_rb_bean_factory.get_bean(attr.ref)
83
+ end
84
+ end
85
+ end
86
+ bean
87
+ end
88
+
89
+ end
@@ -0,0 +1,61 @@
1
+ # Stores bean specific data: bean class, name,
2
+ # scope and bean dependencies
3
+ class IocRb::BeanMetadata
4
+ attr_reader :name, :bean_class, :scope, :instance, :attrs
5
+
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:
10
+ # do |c|
11
+ # attr :some_dependency, ref: :dependency_name
12
+ # arg :another_dependency, ref: :another_dependency_name
13
+ # end
14
+ # here attr means setter injection, arg means constructon injects
15
+ # +some_dependency+ is an attr_accessor defined in the bean class,
16
+ # +ref+ specifies what dependency from container to use to set the attribute
17
+ def initialize(name, options, &block)
18
+ IocRb::ArgsValidator.has_key!(options, :class)
19
+
20
+ @name = name
21
+ @bean_class = options[:class]
22
+ @scope = options[:scope] || :singleton
23
+ @instance = options[:instance].nil? ? true : options[:instance]
24
+ @attrs = []
25
+
26
+ if @bean_class.respond_to?(:_iocrb_injectable_attrs)
27
+ @bean_class._iocrb_injectable_attrs.each do |attr, options|
28
+ options[:ref] ||= attr
29
+ @attrs << IocRb::BeanMetadata::Attribute.new(attr, options)
30
+ end
31
+ end
32
+
33
+ if block
34
+ Dsl.new(@attrs).instance_exec(&block)
35
+ end
36
+ end
37
+
38
+ class Attribute
39
+ attr_reader :name, :ref
40
+
41
+ def initialize(name, options)
42
+ IocRb::ArgsValidator.has_key!(options, :ref)
43
+ @name = name
44
+ @ref = options[:ref]
45
+ end
46
+ end
47
+
48
+ class Dsl
49
+ def initialize(attrs)
50
+ @attrs = attrs
51
+ end
52
+
53
+ def attr(name, options)
54
+ @attrs << IocRb::BeanMetadata::Attribute.new(name, options)
55
+ end
56
+
57
+ def arg(name, options)
58
+ @args << IocRb::BeanMetadata::Attribute.new(name, options)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,19 @@
1
+ # Storage of bean metadatas
2
+ class IocRb::BeansMetadataStorage
3
+ def initialize
4
+ @bean_metadatas = {}
5
+ end
6
+
7
+ # Finds bean metadata in storage by it's name
8
+ # @param name [Symbol] bean metadata name
9
+ # @return bean metadata
10
+ def by_name(name)
11
+ @bean_metadatas[name]
12
+ end
13
+
14
+ # Saves a given +bean_metadata+ to the storage
15
+ # @param bean_metadata [BeanMetadata] bean metadata for saving
16
+ def put(bean_metadata)
17
+ @bean_metadatas[bean_metadata.name] = bean_metadata
18
+ end
19
+ end
@@ -0,0 +1,65 @@
1
+ require 'ioc_rb/errors'
2
+ require 'ioc_rb/args_validator'
3
+ require 'ioc_rb/bean_metadata'
4
+ require 'ioc_rb/beans_metadata_storage'
5
+ require 'ioc_rb/bean_factory'
6
+
7
+ module IocRb
8
+
9
+ # IocRb::Container is the central data store for registering objects
10
+ # used for dependency injection. Users register classes by
11
+ # providing a name and a class to create the object(we call them beans). Beans
12
+ # may be retrieved by asking for them by name (via the [] operator)
13
+ class Container
14
+
15
+ # Constructor
16
+ # @param resources [Array] array of procs with container's beans definitions
17
+ # @param &block [Proc] optional proc with container's beans definitions
18
+ def initialize(resources = nil, &block)
19
+ @beans_metadata_storage = IocRb::BeansMetadataStorage.new
20
+ @bean_factory = IocRb::BeanFactory.new(@beans_metadata_storage)
21
+
22
+ if resources
23
+ IocRb::ArgsValidator.is_array!(resources, :resources)
24
+ load_bean_definitions(resources)
25
+ end
26
+ if block_given?
27
+ load_bean_definitions([block])
28
+ end
29
+ end
30
+
31
+ # Registers new bean in container
32
+ # @param bean_name [Symbol] bean name
33
+ # @param options [Hash] includes bean class and bean scope
34
+ # @param &block [Proc] the block which describes bean dependencies,
35
+ # see more in the BeanMetadata
36
+ def bean(bean_name, options, &block)
37
+ IocRb::ArgsValidator.is_symbol!(bean_name, :bean_name)
38
+ IocRb::ArgsValidator.is_hash!(options, :options)
39
+
40
+ bean = IocRb::BeanMetadata.new(bean_name, options, &block)
41
+ @beans_metadata_storage.put(bean)
42
+ end
43
+
44
+ # Returns bean instance from the container
45
+ # by the specified bean name
46
+ # @param name [Symbol] bean name
47
+ # @return bean instance
48
+ def [](name)
49
+ IocRb::ArgsValidator.is_symbol!(name, :bean_name)
50
+ @bean_factory.get_bean(name)
51
+ end
52
+
53
+ private
54
+
55
+ # Evaluates the given array of blocks on the container instance
56
+ # what adds new bean definitions to the container
57
+ # @param resources [Array] array of procs with container's beans definitions
58
+ def load_bean_definitions(resources)
59
+ resources.each do |resource|
60
+ resource.call(self)
61
+ end
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,10 @@
1
+ module IocRb::Errors
2
+ # Thrown when a service cannot be located by name.
3
+ class MissingBeanError < StandardError; end
4
+
5
+ # Thrown when a duplicate service is registered.
6
+ class DuplicateBeanError < StandardError; end
7
+
8
+ # Thrown when an unsupported bean scope is specified.
9
+ class UnsupportedScopeError < StandardError; end
10
+ end
@@ -0,0 +1,35 @@
1
+ # Extend object with the bean injection mechanism
2
+ # Example of usage:
3
+ # class Bar
4
+ # end
5
+ #
6
+ # class Foo
7
+ # inject :bar
8
+ # or:
9
+ # inject :some_bar, ref: bar
10
+ # end
11
+ #
12
+ # 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"
20
+ 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?(:_iocrb_injectable_attrs)
25
+ class_attribute :_iocrb_injectable_attrs
26
+ self._iocrb_injectable_attrs = { dependency_name => options.dup }
27
+ else
28
+ self._iocrb_injectable_attrs = self._iocrb_injectable_attrs.merge(dependency_name => options.dup)
29
+ end
30
+ attr_accessor dependency_name
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,17 @@
1
+ # Prototype scope instantiates new bean instance
2
+ # on each +get_bean+ call
3
+ class IocRb::Scopes::PrototypeScope
4
+
5
+ # Constructon
6
+ # @param bean_factory bean factory
7
+ def initialize(bean_factory)
8
+ @bean_factory = bean_factory
9
+ end
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, {})
16
+ end
17
+ end
@@ -0,0 +1,27 @@
1
+ require 'request_store'
2
+
3
+ # Request scope instantiates new bean instance
4
+ # on each new HTTP request
5
+ class IocRb::Scopes::RequestScope
6
+
7
+ # Constructon
8
+ # @param bean_factory bean factory
9
+ def initialize(bean_factory)
10
+ @bean_factory = bean_factory
11
+ end
12
+
13
+ # Returns a bean from the +RequestStore+
14
+ # RequestStore is a wrapper for Thread.current
15
+ # which clears it on each new HTTP request
16
+ #
17
+ # @param bean_metadata [BeanMetadata] bean metadata
18
+ # @returns bean instance
19
+ def get_bean(bean_metadata)
20
+ RequestStore.store[:_iocrb_beans] ||= {}
21
+ if bean = RequestStore.store[:_iocrb_beans][bean_metadata.name]
22
+ bean
23
+ else
24
+ @bean_factory.create_bean_and_save(bean_metadata, RequestStore.store[:_iocrb_beans])
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,23 @@
1
+ # Singleton scope returns the same bean instance
2
+ # on each call
3
+ class IocRb::Scopes::SingletonScope
4
+
5
+ # Constructon
6
+ # @param bean_factory bean factory
7
+ def initialize(bean_factory)
8
+ @beans = {}
9
+ @bean_factory = bean_factory
10
+ end
11
+
12
+ # Returns the same bean instance
13
+ # 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
22
+ end
23
+ end
@@ -0,0 +1,2 @@
1
+ module IocRb::Scopes
2
+ end
@@ -0,0 +1,3 @@
1
+ module IocRb
2
+ VERSION = "0.0.2"
3
+ end
data/lib/ioc_rb.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+ require 'ioc_rb/version'
3
+ require 'ioc_rb/inject'
4
+ require 'ioc_rb/container'
@@ -0,0 +1,122 @@
1
+ require 'spec_helper'
2
+ require 'ioc_rb'
3
+
4
+ describe IocRb::Container do
5
+
6
+ class Logger
7
+ attr_accessor :appender
8
+ end
9
+ class Appender
10
+ end
11
+ class Printer
12
+ end
13
+
14
+ describe "bean definitions" do
15
+ let(:container) do
16
+ container = IocRb::Container.new
17
+ container.bean(:appender, class: Appender)
18
+ container.bean(:logger, class: Logger) do
19
+ attr :appender, ref: :appender
20
+ end
21
+ container.bean(:printer, class: Printer, instance: false)
22
+ container
23
+ end
24
+ it "should instanciate bean and it's dependencies" do
25
+ container[:logger].should be_a(Logger)
26
+ container[:logger].appender.should be_a(Appender)
27
+ container[:printer].should be(Printer)
28
+ end
29
+
30
+ it "container should return the same instance on each call" do
31
+ logger = container[:logger]
32
+ container[:logger].should == logger
33
+ end
34
+ end
35
+
36
+ describe "passing bean definitions to container constructor" do
37
+ let(:resource) do
38
+ Proc.new do |c|
39
+ c.bean(:appender, class: 'Appender')
40
+ c.bean(:logger, class: Logger) do
41
+ attr :appender, ref: :appender
42
+ end
43
+ end
44
+ end
45
+
46
+ it "should instanciate given bean definitions" do
47
+ container = IocRb::Container.new([resource])
48
+ container[:logger].should be_a(Logger)
49
+ container[:appender].should be_a(Appender)
50
+ end
51
+
52
+ end
53
+
54
+ describe "inheritance" do
55
+ class Form
56
+ inject :validator
57
+ end
58
+
59
+ class Circle < Form
60
+ inject :circle_validator
61
+ end
62
+ class Rectangle < Form
63
+ inject :rectangle_validator
64
+ end
65
+
66
+ class Validator
67
+ end
68
+ class CircleValidator
69
+ end
70
+ class RectangleValidator
71
+ end
72
+
73
+ let(:container) do
74
+ IocRb::Container.new do |c|
75
+ c.bean(:circle, class: Circle)
76
+ c.bean(:rectangle, class: Rectangle)
77
+ c.bean(:validator, class: Validator)
78
+ c.bean(:circle_validator, class: CircleValidator)
79
+ c.bean(:rectangle_validator, class: RectangleValidator)
80
+ end
81
+ end
82
+
83
+ it "dependencies in subclasses shouldn't affect on each other" do
84
+ container[:circle].circle_validator.should be_a(CircleValidator)
85
+ container[:rectangle].rectangle_validator.should be_a(RectangleValidator)
86
+ end
87
+ end
88
+
89
+ describe "bean scopes" do
90
+ class ContactsService
91
+ inject :contacts_repository
92
+ inject :contacts_validator
93
+ end
94
+ class ContactsRepository
95
+ end
96
+ class ContactsValidator
97
+ end
98
+
99
+ let(:container) do
100
+ container = IocRb::Container.new
101
+ container.bean(:contacts_repository, class: ContactsRepository, scope: :request)
102
+ container.bean(:contacts_service, class: ContactsService, scope: :singleton)
103
+ container.bean(:contacts_validator, class: ContactsValidator, scope: :prototype)
104
+ container
105
+ end
106
+
107
+ it "should instanciate bean with :request scope on each request" do
108
+ first_repo = container[:contacts_service].contacts_repository
109
+ second_repo = container[:contacts_service].contacts_repository
110
+ first_repo.should == second_repo
111
+ RequestStore.clear! # new request
112
+ third_repo = container[:contacts_service].contacts_repository
113
+ first_repo.should_not == third_repo
114
+ end
115
+
116
+ it "should instanciate bean with :prototype scope on each call" do
117
+ first_validator = container[:contacts_service].contacts_validator
118
+ second_validator = container[:contacts_service].contacts_validator
119
+ first_validator.should_not == second_validator
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+ require 'ioc_rb'
3
+
4
+ # Ensures that :inject keyword works as it should
5
+ describe "Object.inject" do
6
+ class ContactBook
7
+ inject :contacts_repository
8
+ inject :validator, ref: :contact_validator
9
+ end
10
+ class ContactsRepository
11
+ end
12
+ class ContactValidator
13
+ end
14
+
15
+ let(:container) do
16
+ IocRb::Container.new do |c|
17
+ c.bean(:contacts_repository, class: ContactsRepository)
18
+ c.bean(:contact_validator, class: ContactValidator)
19
+ c.bean(:contact_book, class: ContactBook)
20
+ end
21
+ end
22
+
23
+ it "should autowire dependencies" do
24
+ container[:contact_book].contacts_repository.should be_a(ContactsRepository)
25
+ container[:contact_book].validator.should be_a(ContactValidator)
26
+ end
27
+
28
+ it "should raise ArgumentError if non-symbol passed as dependency name" do
29
+ expect do
30
+ class SomeClass
31
+ inject 'bar'
32
+ end
33
+ end.to raise_error(ArgumentError, "dependency name should be a symbol")
34
+ end
35
+
36
+ it "inject should define instance variable" do
37
+ container[:contact_book].instance_variable_get(:@contacts_repository).should be_a(ContactsRepository)
38
+ end
39
+
40
+ it "inject should not define class variable" do
41
+ expect do
42
+ container[:contact_book].class.contacts_repository
43
+ end.to raise_error(NoMethodError)
44
+ end
45
+
46
+ end
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'debugger'
4
+
5
+ RSpec.configure do |config|
6
+ config.color_enabled = true
7
+ end
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ioc_rb
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.2
6
+ platform: ruby
7
+ authors:
8
+ - Albert Gazizov
9
+ - Ruslan Gatiyatov
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2014-02-24 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ version_requirements: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ! '>='
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ none: false
22
+ name: request_store
23
+ type: :runtime
24
+ prerelease: false
25
+ requirement: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ none: false
31
+ - !ruby/object:Gem::Dependency
32
+ version_requirements: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ! '>='
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ none: false
38
+ name: activesupport
39
+ type: :runtime
40
+ prerelease: false
41
+ requirement: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ none: false
47
+ - !ruby/object:Gem::Dependency
48
+ version_requirements: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ~>
51
+ - !ruby/object:Gem::Version
52
+ version: '1.3'
53
+ none: false
54
+ name: bundler
55
+ type: :development
56
+ prerelease: false
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ none: false
63
+ - !ruby/object:Gem::Dependency
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ none: false
70
+ name: rake
71
+ type: :development
72
+ prerelease: false
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ none: false
79
+ description: Inversion of Controll Container
80
+ email:
81
+ - deeper4k@gmail.com
82
+ - ruslan.gatiyatov@gmail.com
83
+ executables: []
84
+ extensions: []
85
+ extra_rdoc_files: []
86
+ files:
87
+ - .gitignore
88
+ - .travis.yml
89
+ - .yardops
90
+ - Gemfile
91
+ - Gemfile.lock
92
+ - LICENSE.txt
93
+ - README.md
94
+ - Rakefile
95
+ - ioc_rb.gemspec
96
+ - lib/ioc_rb.rb
97
+ - lib/ioc_rb/args_validator.rb
98
+ - lib/ioc_rb/bean_factory.rb
99
+ - lib/ioc_rb/bean_metadata.rb
100
+ - lib/ioc_rb/beans_metadata_storage.rb
101
+ - lib/ioc_rb/container.rb
102
+ - lib/ioc_rb/errors.rb
103
+ - lib/ioc_rb/inject.rb
104
+ - lib/ioc_rb/scopes.rb
105
+ - lib/ioc_rb/scopes/prototype_scope.rb
106
+ - lib/ioc_rb/scopes/request_scope.rb
107
+ - lib/ioc_rb/scopes/singleton_scope.rb
108
+ - lib/ioc_rb/version.rb
109
+ - spec/ioc_rb/container_spec.rb
110
+ - spec/ioc_rb/inject_spec.rb
111
+ - spec/spec_helper.rb
112
+ homepage: http://github.com/deeper4k/ioc_rb
113
+ licenses:
114
+ - MIT
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ! '>='
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ none: false
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ! '>='
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ none: false
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 1.8.23
134
+ signing_key:
135
+ specification_version: 3
136
+ summary: Inversion of Controll Container
137
+ test_files:
138
+ - spec/ioc_rb/container_spec.rb
139
+ - spec/ioc_rb/inject_spec.rb
140
+ - spec/spec_helper.rb
141
+ has_rdoc: