medicine 0.0.1
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 +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +72 -0
- data/Rakefile +2 -0
- data/TODO +12 -0
- data/lib/medicine/version.rb +3 -0
- data/lib/medicine.rb +61 -0
- data/medicine.gemspec +20 -0
- data/spec/medicine_spec.rb +90 -0
- data/spec/spec_helper.rb +28 -0
- metadata +60 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 19f0f8412a402f42bd7b0f91644a187d664e6e29
|
4
|
+
data.tar.gz: ec738b16e8c28da75bd08f4fa660aab74b33b6f7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 73d44fa17647302412a37d9055e8770c0505e5d7468fb30bd8499c6cca3b5df10722a4aaa1e9ce61cd858efdfc8903332413f20967980dde8b261211932e1856
|
7
|
+
data.tar.gz: d40fecb0a6d6d0af603fc698f959887fd5753a9fae779b2f533d5f5c5cc23e463952625b70943a5280232daf1efa7bfe56916fc9f76d8cbd78c1623fe0cfe04d
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.1.5
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Kris Leech
|
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,72 @@
|
|
1
|
+
# Medicine
|
2
|
+
|
3
|
+
Simple Dependency Injection for Ruby
|
4
|
+
|
5
|
+
Find yourself passing dependencies in to the initalizer? Medicine makes this
|
6
|
+
declarative.
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
class CastVote
|
10
|
+
include Medicine.di
|
11
|
+
|
12
|
+
dependency :votes_repo, default: -> { Vote }
|
13
|
+
|
14
|
+
def call(entry_id)
|
15
|
+
vote_repo.create!(entry_id: entry_id)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
cast_vote = CastVote.new
|
21
|
+
cast_vote.call(3)
|
22
|
+
```
|
23
|
+
|
24
|
+
In this example Medicine adds a private method called `vote_repo` which returns `Vote`.
|
25
|
+
|
26
|
+
## Injecting a dependency
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
vote_repo = double('VoteRepo')
|
30
|
+
cast_vote = CastVote.new(vote_repo: vote_repo)
|
31
|
+
|
32
|
+
# or
|
33
|
+
|
34
|
+
cast_vote = CastVote.new(arg1, arg2, vote_repo: vote_repo)
|
35
|
+
```
|
36
|
+
|
37
|
+
## Required dependencies
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
dependency :vote_repo
|
41
|
+
```
|
42
|
+
|
43
|
+
When no default is specified and is not injected an error will be raised on
|
44
|
+
initialization.
|
45
|
+
|
46
|
+
## Default dependencies
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
dependency :vote_repo, default: :Vote
|
50
|
+
dependency :vote_repo, default: 'Vote'
|
51
|
+
dependency :vote_repo, default: -> { Vote }
|
52
|
+
```
|
53
|
+
|
54
|
+
The above examples will expose a method called `vote_repo` which returns the
|
55
|
+
`Vote` class as the default dependency.
|
56
|
+
|
57
|
+
You could also pass an object which responds to call and accepts one argument,
|
58
|
+
the name of the dependency
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
dependency :vote_repo, default: Repo
|
62
|
+
```
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
class Repo
|
66
|
+
def self.call(name)
|
67
|
+
Kernel.constant_get(name.gsub('_repo', ''))
|
68
|
+
end
|
69
|
+
end
|
70
|
+
```
|
71
|
+
|
72
|
+
In this example the `vote_repo` method will return the `Vote` class.
|
data/Rakefile
ADDED
data/TODO
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# TODO
|
2
|
+
|
3
|
+
test that zsuper/super is called
|
4
|
+
module populates class with many private instance methods
|
5
|
+
* think about namespacing them somehow, module/double underscore...
|
6
|
+
refactor tests
|
7
|
+
implement remaining examples in README
|
8
|
+
add example of how to call super if overriding initialize
|
9
|
+
see how it works with inheritence
|
10
|
+
test to make sure state is not shared between objects
|
11
|
+
freeze dependency hash passed to initalize
|
12
|
+
release 0.0.1
|
data/lib/medicine.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require "medicine/version"
|
2
|
+
|
3
|
+
module Medicine
|
4
|
+
def self.di
|
5
|
+
DI
|
6
|
+
end
|
7
|
+
|
8
|
+
module DI
|
9
|
+
ArgumentError = Class.new(::ArgumentError)
|
10
|
+
|
11
|
+
def self.included(base)
|
12
|
+
base.extend(ClassMethods)
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(*args)
|
16
|
+
@dependencies = extract_dependencies(args)
|
17
|
+
assert_all_dependencies_met
|
18
|
+
define_dependency_methods
|
19
|
+
|
20
|
+
super(*args)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def define_dependency_methods
|
26
|
+
self.class.dependencies.each do |name, options|
|
27
|
+
define_singleton_method name do
|
28
|
+
@dependencies.fetch(name) { resolve_dependency(name) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def extract_dependencies(args)
|
34
|
+
args.last.respond_to?(:[]) ? args.pop : {}
|
35
|
+
end
|
36
|
+
|
37
|
+
def assert_all_dependencies_met
|
38
|
+
raise ArgumentError, "initalize with all dependencies without a default" unless all_dependencies_met?
|
39
|
+
end
|
40
|
+
|
41
|
+
def all_dependencies_met?
|
42
|
+
self.class.dependencies.keys.all? do |key|
|
43
|
+
@dependencies.has_key?(key) || self.class.dependencies.fetch(key).has_key?(:default)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def resolve_dependency(name)
|
48
|
+
self.class.dependencies.fetch(name).fetch(:default)
|
49
|
+
end
|
50
|
+
|
51
|
+
module ClassMethods
|
52
|
+
def dependency(name, options = {})
|
53
|
+
dependencies[name] = options
|
54
|
+
end
|
55
|
+
|
56
|
+
def dependencies
|
57
|
+
@dependencies ||= {}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/medicine.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'medicine/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "medicine"
|
8
|
+
spec.version = Medicine::VERSION
|
9
|
+
spec.authors = ["Kris Leech"]
|
10
|
+
spec.email = ["kris.leech@gmail.com"]
|
11
|
+
spec.summary = "Simple Dependency Injection for Ruby"
|
12
|
+
spec.description = "Simple Dependency Injection for Ruby. Find yourself passing dependencies in to the initalizer? Medicine makes this declarative."
|
13
|
+
spec.homepage = "https://github.com/krisleech/medicine"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
RSpec.describe 'Medicine' do
|
2
|
+
let(:klass) do
|
3
|
+
Class.new do
|
4
|
+
include Medicine.di
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '.dependency' do
|
9
|
+
|
10
|
+
describe 'without any options' do
|
11
|
+
before { klass.class_eval { dependency :vote_repo } }
|
12
|
+
|
13
|
+
context 'initialized with no arguments' do
|
14
|
+
subject { klass.new }
|
15
|
+
|
16
|
+
it 'initialization raises error' do
|
17
|
+
expect { subject }.to raise_error(Medicine::DI::ArgumentError)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when initalized with hash' do
|
22
|
+
subject { klass.new(vote_repo: :foo) }
|
23
|
+
|
24
|
+
context 'and hash has key for dependency' do
|
25
|
+
it 'responds to dependency name' do # TODO: method should be private
|
26
|
+
expect(subject).to respond_to(:vote_repo)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'provides method which returns injected value' do
|
30
|
+
expect(subject.vote_repo).to eq :foo
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'hash has no key for dependency' do
|
35
|
+
subject { klass.new({}) }
|
36
|
+
|
37
|
+
it 'raises error' do
|
38
|
+
expect { subject }.to raise_error(Medicine::DI::ArgumentError)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'initalized with arguments and hash' do
|
44
|
+
before do
|
45
|
+
klass.class_eval do
|
46
|
+
def initialize(arg1, arg2, deps)
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'and hash has key for dependency' do
|
53
|
+
subject { klass.new(double, double, vote_repo: :foo) }
|
54
|
+
|
55
|
+
skip 'provides method for dependency' do
|
56
|
+
expect(subject).to respond_to(:vote_repo)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'hash has no key for dependency' do
|
61
|
+
subject { klass.new(double, double, {}) }
|
62
|
+
|
63
|
+
skip 'raises error' do
|
64
|
+
expect { subject }.to raise_error(Medicine::DI::ArgumentError)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe 'with default option' do
|
71
|
+
describe 'of type String' do
|
72
|
+
before { klass.class_eval { dependency :vote_repo, default: 'Object' } }
|
73
|
+
|
74
|
+
context 'and initializer given no arguments' do
|
75
|
+
|
76
|
+
subject { klass.new }
|
77
|
+
|
78
|
+
it 'responds to dependency name' do
|
79
|
+
expect(subject).to respond_to(:vote_repo)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'returns given default typecast to a class' do
|
83
|
+
expect(subject.vote_repo).to be_an_instance_of(Object)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end # with default option
|
88
|
+
|
89
|
+
end # .dependency
|
90
|
+
end # rspec
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'pry'
|
2
|
+
require 'medicine'
|
3
|
+
|
4
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
5
|
+
RSpec.configure do |config|
|
6
|
+
config.expect_with :rspec do |expectations|
|
7
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
8
|
+
end
|
9
|
+
|
10
|
+
config.mock_with :rspec do |mocks|
|
11
|
+
mocks.verify_partial_doubles = true
|
12
|
+
end
|
13
|
+
|
14
|
+
config.filter_run :focus
|
15
|
+
config.run_all_when_everything_filtered = true
|
16
|
+
|
17
|
+
config.disable_monkey_patching!
|
18
|
+
|
19
|
+
config.warnings = true
|
20
|
+
|
21
|
+
if config.files_to_run.one?
|
22
|
+
config.default_formatter = 'doc'
|
23
|
+
end
|
24
|
+
|
25
|
+
config.order = :random
|
26
|
+
|
27
|
+
Kernel.srand config.seed
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: medicine
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kris Leech
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-09 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Simple Dependency Injection for Ruby. Find yourself passing dependencies
|
14
|
+
in to the initalizer? Medicine makes this declarative.
|
15
|
+
email:
|
16
|
+
- kris.leech@gmail.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- ".gitignore"
|
22
|
+
- ".rspec"
|
23
|
+
- ".ruby-version"
|
24
|
+
- Gemfile
|
25
|
+
- LICENSE.txt
|
26
|
+
- README.md
|
27
|
+
- Rakefile
|
28
|
+
- TODO
|
29
|
+
- lib/medicine.rb
|
30
|
+
- lib/medicine/version.rb
|
31
|
+
- medicine.gemspec
|
32
|
+
- spec/medicine_spec.rb
|
33
|
+
- spec/spec_helper.rb
|
34
|
+
homepage: https://github.com/krisleech/medicine
|
35
|
+
licenses:
|
36
|
+
- MIT
|
37
|
+
metadata: {}
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
requirements: []
|
53
|
+
rubyforge_project:
|
54
|
+
rubygems_version: 2.2.2
|
55
|
+
signing_key:
|
56
|
+
specification_version: 4
|
57
|
+
summary: Simple Dependency Injection for Ruby
|
58
|
+
test_files:
|
59
|
+
- spec/medicine_spec.rb
|
60
|
+
- spec/spec_helper.rb
|