ioc_rb 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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: