SmartCore::Container · ·
Thread-safe semanticaly-defined IoC/DI Container with a developer-friendly DSL and API.
Installation
gem 'smart_container'
bundle install
# --- or ---
gem install smart_container
require 'smart_core/container'
Table of cotnents
Functionality
container class creation
class Container < SmartCore::Container
namespace(:database) do
register(:resolver, memoize: true) { SomeDatabaseResolver.new }
namespace(:cache) do
register(:memcached, memoize: true) { MemcachedClient.new }
register(:redis, memoize: true) { RedisClient.new }
end
end
register(:logger, memoize: true) { Logger.new(STDOUT) }
register(:random) { rand(1000) }
end
mixin
class Application
include SmartCore::Container::Mixin
dependencies do
namespace(:database) do
register(:cache) { MemcachedClient.new }
end
end
end
Application.container
Application.new.container
container instantiation and dependency resolving
container = Container.new
container['database.resolver']
container['database.cache.redis']
container['logger']
container.resolve('logger')
container['random']
container['random']
container['database']
container.fetch('database')
container.fetch('database.resolver')
runtime-level dependency/namespace registration
container.namespace(:api) do
register(:provider) { GoogleProvider }
end
container.register('game_api', memoize: true) { 'overwatch' }
container['api.provider']
container['game_api']
container keys (dependency names):
container.keys
[
'database.resolver',
'database.cache.memcached',
'database.cache.redis',
'logger',
'random'
]
container.keys(all_variants: true)
[
'database',
'database.resolver',
'database.cache',
'database.cache.memcached',
'database.cache.redis',
'logger',
'random'
]
key predicates
key?(key)
- has dependency or namespace?namespace?(path)
- has namespace?dependency?(path)
- has dependency?dependency?(path, memoized: true)
- has memoized dependency?dependency?(path, memoized: false)
- has non-memoized dependency?
container.key?('database')
container.key?('database.cache.memcached')
container.dependency?('database')
container.dependency?('database.resolver')
container.namespace?('database')
container.namespace?('database.resolver')
container.dependency?('database.resolver', memoized: true)
container.dependency?('database.resolver', memoized: false)
container.dependency?('random', memoized: true)
container.dependency?('random', memoized: false)
state freeze
- state freeze (
#freeze!
, .#frozen?
):
reloading
hash tree
- hash tree (
#hash_tree
, #hash_tree(resolve_dependencies: true)
):
explicit class definition
SmartCore::Container.define
- avoid explicit class definition (allows to create container instance from an anonymous container class immidietly):
AppContainer = SmartCore::Container.define do
namespace :database do
register(:logger) { Logger.new }
end
end
AppContainer.resolve('database.logger')
AppContainer['database.logger']
class BasicContainer < SmartCore::Container
namespace(:api) do
register(:client) { Kickbox.new }
end
end
AppContainer = BasicContainer.define do
register(:db_driver) { Sequel }
end
AppContainer = SmartCore::Container.define(BasicContainer) do
register(:db_driver) { Sequel }
end
AppContainer['api.client']
AppContainer['db_driver']
subscribe to dependency changements
- features and limitations:
- you can subscribe only on container instances (on container instance changements);
- at this moment only the full entity path patterns are supported (pattern-based pathes are not supported yet);
- you can subscribe on namespace changements (when the full namespace is re-registered) and dependency changement (when some dependency has been changed);
#observe(path, &observer) => observer
- subscribe a custom block to dependency changement events (your proc will be invoked with |path, container|
attributes);#unobserve(observer)
- unsubscribe concrete observer from dependency observing (returns true
(unsubscribed) or false
(nothing to unsubscribe));#clear_observers(entity_path = nil)
- unsubscribe all observers from concrete path or from all pathes (nil
parameters);
- aliases:
#observe
=> #subscribe
;#unobserve
=> #unsubscribe
;#clear_observers
=> #clear_listeners
;
container = SmartCore::Container.define do
namespace(:database) do
register(:stats) { 'stat_db' }
end
end
entity_observer = container.observe('database.stats') do |dependency_path, container|
puts "changed => '#{container[dependency_path]}'"
end
namespace_observer = container.observe('database') do |namespace_path, container|
puts "changed => '#{namespace_path}'"
end
container.fetch('database').register('stats') = 'kek'
container.namespace('database') {}
container.unobserve(observer)
container.clear_observers
container.fetch('database').register('stats') = 'pek'
container.namespace('database') {}
Roadmap
container['dependency.path'] = 'pek'
container.rebind('dependency.path', memoize: true/false) { 'pek' }
container.rebind('dependency.path', memoize: true/false, 'pek')
- pattern-based pathes in dependency changement observing;
container.observe('path.*') { puts 'kek!' }
- support for instant dependency registration:
register('dependency_name') { dependency_value }
register('dependency_name', dependency_value)
- support for memoization ignorance during dependency resolving:
resolve('logger', :allocate)
with(logger: Logger.new, db: DB.new) do
end
Contributing
- Fork it ( https://github.com/smart-rb/smart_container/fork )
- Create your feature branch (
git checkout -b feature/my-new-feature
) - Commit your changes (
git commit -am '[feature_context] Add some feature'
) - Push to the branch (
git push origin feature/my-new-feature
) - Create new Pull Request
License
Released under MIT License.
Supporting
Authors
Rustam Ibragimov