deject 0.0.1

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
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in deject.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/Readme.md ADDED
@@ -0,0 +1,212 @@
1
+ About
2
+ =====
3
+
4
+ Simple dependency injection
5
+
6
+ Install
7
+ =======
8
+
9
+ On most systems:
10
+
11
+ $ gem install deject # coming soon
12
+
13
+ On some systems:
14
+
15
+ $ sudo gem install deject
16
+
17
+ If you have to use sudo and you don't know why, it's because you need to set your GEM_HOME environment variable.
18
+
19
+ Example
20
+ =======
21
+
22
+ require 'deject'
23
+
24
+ # Represents some client like https://github.com/voloko/twitter-stream
25
+ # Some client that will be used by our service
26
+ class Client
27
+ def initialize(credentials)
28
+ @credentials = credentials
29
+ end
30
+
31
+ def login(name)
32
+ @login = name
33
+ end
34
+
35
+ def has_logged_in?(name) # !> `&' interpreted as argument prefix
36
+ @login == name
37
+ end
38
+
39
+ def initialized_with?(credentials)
40
+ @credentials == credentials
41
+ end
42
+ end
43
+
44
+
45
+ class Service
46
+ Deject self # <-- we'll talk more about this later
47
+
48
+ # you can basically think of the block as a factory that
49
+ # returns a client. It is evaluated in the context of the instance
50
+ # ...though I'm not sure that's a good strategy to employ
51
+ # (I suspect it would be better that these return constants as much as possible)
52
+ dependency(:client) { Client.new credentials }
53
+
54
+ attr_accessor :name
55
+
56
+ def initialize(name)
57
+ self.name = name
58
+ end
59
+
60
+ def login
61
+ client.login name
62
+ end
63
+
64
+ def credentials
65
+ # a login key or something, would probably be dejected as well
66
+ # to retrieve the result from some config file or service
67
+ 'skj123@#KLFNV9ajv'
68
+ end
69
+ end
70
+
71
+ # using the default
72
+ service = Service.new('josh')
73
+ service.login
74
+ service.client # => #<Client:0x007ff97a92d9b8 @credentials="skj123@#KLFNV9ajv", @login="josh">
75
+ service.client.has_logged_in? 'josh' # => true
76
+ service.client.initialized_with? service.credentials # => true
77
+
78
+ # overriding the default at instance level
79
+ client_mock = Struct.new :recordings do
80
+ def method_missing(*args)
81
+ self.recordings ||= []
82
+ recordings << args
83
+ end
84
+ end
85
+ client = client_mock.new
86
+ sally = Service.new('sally').with_client client # <-- you can also override with a block
87
+
88
+ sally.login
89
+ client.recordings # => [[:login, "sally"]]
90
+
91
+ sally.login
92
+ client.recordings # => [[:login, "sally"], [:login, "sally"]]
93
+
94
+ Reasons
95
+ =======
96
+
97
+ Why write this?
98
+ ---------------
99
+
100
+ Hard dependencies kick ass. They make your code clear and easy to understand.
101
+ But, of course, they're hard, you can't change them (or can't reasonably change them).
102
+ So when you go to test, it sucks. When you want to reuse, it sucks. How to get around this?
103
+ Inject your dependencies.
104
+
105
+ And while it's not the worst thing in the world to do custom dependency injection in Ruby,
106
+ it can still get obnoxious. What do you do? There's basically two options:
107
+
108
+ 1. Add it as an argument when initializing (or _possibly_ when invoking your method). This works
109
+ fine if you aren't already doing anything complicated with your arguments. If you can just throw
110
+ an optional arg in there for the dependency, giving it a default of the hard dependency, then
111
+ it's not too big of a deal. But what if you have two dependencies? Then you can't use optional
112
+ arguments, because how will you know which is which? What if you're already taking optional args?
113
+ Then again, you can't pass this in optionally. So you have to set it to an ordinal argument, which
114
+ means that everywhere you use the thing, you have to deal with the dependency. It's cumbersome and ugly.
115
+ Or you can pass it in with an options hash, but what if you're already taking a hash (as I was when
116
+ I decided I wanted this) and it's not for this object? Then you have to namespace the common options
117
+ such that you can tell them apart, it's gross (e.g. passing html options to a form in Rails), and you
118
+ only need to do it for something that users shouldn't need to care about unless they really want to.
119
+
120
+ 2. Defining methods to return the dependency that can be overridden by setting the value. This is a heavier
121
+ choice than the above, but it can become necessary. Define an `attr_writer :whatever` and a getter
122
+ whose body looks like `@whatever ||= HardDependency.new`. Not the worst thing in the world, but it takes
123
+ about four lines and clutters things up. What's more, it must be set with a setter, and setters always
124
+ return the RHS of the assignment. So to override it, you have to have three lines where you probably only want one.
125
+ And of course, having a real method in there is a statement. It says "this is the implementation", people
126
+ don't override methods all willy nilly, I'd give dirty looks to colleagues if they overrode it as was convenient.
127
+ For instance, say you _always_ want to override the default (e.g. a FakeUser in the test environment and User in
128
+ development/production environments). Then you have to open the class and redefine it in an initialization file.
129
+ Not cool.
130
+
131
+ Deject handles these shortcomings with the default ways to inject dependencies. Declaring something a dependency
132
+ inherently identifies it as overridable. Overriding it by environment is not shocking or unexpected, and only requires one line,
133
+ and has the advantage of closures during overriding -- as opposed to having to metaprogramming to set that default.
134
+
135
+ It makes it very easy to declare and to override dependencies, by adding an inline call to the override.
136
+ You don't have to deal with arguments, you don't have to define methods, it defines the methods for you
137
+ and gives you an easy way to inject a new value. In the end, it's simpler and easier to understand.
138
+
139
+
140
+ Statements I am trying to make by writing this
141
+ ----------------------------------------------
142
+
143
+ Dependencies should be soft by default, dependency injection can have a place in Ruby
144
+ (even though I'll probably get made fun of for it). I acknowledge that I really enjoyed
145
+ the post [Why I love everything you hate about Java](http://magicscalingsprinkles.wordpress.com/2010/02/08/why-i-love-everything-you-hate-about-java/).
146
+ Though I agreed with a lot of the rebuttals in the comments as well.
147
+
148
+ I intentionally didn't do this with module inclusion. Module inclusion has become a cancer
149
+ (I'll probably write a blog about that later). _Especially_ the way people abuse the `self.included` hook.
150
+ I wanted to show people that you don't _HAVE_ to do that. There's no reason your module can't have
151
+ a method that is truthful about its purpose, something like `MyModule.apply_to MyClass`, it can include and extend
152
+ in there all it wants. That's fine, that's obvious, it isn't lying. But when people `include MyModule`
153
+ just so they can get into the included hook (where they almost never need to) and then **EXTEND** the class... grrrrr.
154
+
155
+ And of course, after I decided I wasn't going to directly include / extend the module, I began
156
+ thinking about how to get Deject onto the class. `Deject.dejectify SomeClass`? Couldn't think of
157
+ a good verb. But wait, do I _really_ need a verb? I went and read
158
+ [Execution in the Kingdom of Nouns](http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html)
159
+ and decided I was okay with having a method that applies it, hence `Deject SomeClass`. Not a usual practice
160
+ but not everything needs to be OO. Which led to the next realization that I didn't need a module at all.
161
+
162
+ So, there's two implementations. You can set which one you want to use with an environment variable (not that you care).
163
+ The first is "functional" which is to say that I was trying to channel functional ideas when writing it. It's really just one
164
+ big function that defines and redefines methods. Initially I hated this, I found it very difficult to read (might have been
165
+ better if Ruby had macros), I had to add comments in to keep track of what was happening.
166
+ But then I wrote the object oriented implementation, and it was pretty opaque as well.
167
+ Plus there were a lot of things I wanted to do that were very difficult to accomplish, and it was much longer.
168
+
169
+ So in the end, I'm hoping someone takes the time to look at both implementations and gives me feedback on their thoughts
170
+ Is one better? Are they each better in certain ways? Can this code be made simpler? Any feedback is welcome.
171
+
172
+ Oh, I also intentionally used closures over local variables rather than instance variables, because I
173
+ wanted to make people realize it's better to use setters and getters than to directly access instance variables
174
+ (to be fair, there are some big names that [disagree with](http://www.ruby-forum.com/topic/211544#919648) me on this).
175
+ I think most people directly access ivars because they haven't found themselves in a situation where it mattered.
176
+ But what if `attr_accessor` wound up changing implementations such that it didn't use ivars? "Ridiculous" I can hear
177
+ people saying, but it's not so ridiculous when you realize that you can remove 4 redundant lines by inheriting from
178
+ a Struct. If you use indirect access, everything still works just fine. And structs aren't the only place this occurs,
179
+ think about ActiveRecord::Base, it doesn't use ivars, so if you use attr_accessor in your model somewhere, you need to
180
+ know how a given attribute was defined so that you can know if you should use ivars or not... terrible. Deject's functional
181
+ implementation does not use ivars, you **must** use the getter and the overrider (there isn't currently a setter).
182
+ That is intentional (though I used ivars in the OO implementation).
183
+
184
+
185
+ Todo
186
+ ====
187
+
188
+ Maybe raise an error if you call `with_whatever` and pass no args.
189
+ Maybe add a setter rather than only provide the overrider.
190
+
191
+ License
192
+ =======
193
+
194
+ Copyright (c) 2012 Joshua Cheek
195
+
196
+ Permission is hereby granted, free of charge, to any person obtaining a copy
197
+ of this software and associated documentation files (the "Software"), to deal
198
+ in the Software without restriction, including without limitation the rights
199
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
200
+ copies of the Software, and to permit persons to whom the Software is
201
+ furnished to do so, subject to the following conditions:
202
+
203
+ The above copyright notice and this permission notice shall be included in
204
+ all copies or substantial portions of the Software.
205
+
206
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
207
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
208
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
209
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
210
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
211
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
212
+ THE SOFTWARE.
data/deject.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "deject/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "deject"
7
+ s.version = Deject::VERSION
8
+ s.authors = ["Josh Cheek"]
9
+ s.email = ["josh.cheek@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Simple dependency injection}
12
+ s.description = %q{Provides a super simple API for dependency injection}
13
+
14
+ s.rubyforge_project = "deject"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "rspec"
23
+ s.add_development_dependency "pry"
24
+ # s.add_runtime_dependency "rest-client"
25
+ end
data/lib/deject.rb ADDED
@@ -0,0 +1,8 @@
1
+ case ENV['deject_implementation']
2
+ when 'object_oriented'
3
+ require 'deject/object_oriented'
4
+ else
5
+ require 'deject/functional'
6
+ end
7
+
8
+ require "deject/version"
@@ -0,0 +1,48 @@
1
+ module Deject
2
+ UninitializedDependency = Class.new StandardError
3
+ end
4
+
5
+ def Deject(klass)
6
+ uninitialized_error = lambda do |meth|
7
+ raise Deject::UninitializedDependency, "#{meth} invoked before being defined"
8
+ end
9
+
10
+ # define klass.dependency
11
+ klass.define_singleton_method :dependency do |meth, &default_block|
12
+
13
+ # define the getter
14
+ define_method meth do
15
+ uninitialized_error[meth] unless default_block
16
+ value = instance_eval &default_block
17
+ define_singleton_method(meth) { value }
18
+ send meth
19
+ end
20
+
21
+ # define the override
22
+ define_method :"with_#{meth}" do |value=nil, &block|
23
+
24
+ # redefine getter if given a block
25
+ if block
26
+ define_singleton_method meth do
27
+ value = instance_eval &block
28
+ define_singleton_method(meth) { value }
29
+ send meth
30
+ end
31
+
32
+ # always return value if given a value
33
+ else
34
+ define_singleton_method(meth) { value }
35
+ end
36
+
37
+ self
38
+ end
39
+ end
40
+
41
+ # override multiple dependencies
42
+ klass.send :define_method, :with_dependencies do |overrides|
43
+ overrides.each { |meth, value| send "with_#{meth}", value }
44
+ self
45
+ end
46
+
47
+ klass
48
+ end
@@ -0,0 +1,82 @@
1
+ def Deject(klass)
2
+ klass.extend Deject
3
+ end
4
+
5
+ module Deject
6
+ UninitializedDependency = Class.new StandardError
7
+
8
+ def dependency(meth, &block)
9
+ InstanceMethods.for self, meth, block
10
+ end
11
+
12
+
13
+ class InstanceMethods
14
+ attr_accessor :klass, :meth, :initializer, :ivar
15
+
16
+ def self.for(klass, meth, initializer)
17
+ instance = new klass, meth, initializer
18
+ instance.define_getter
19
+ instance.define_override
20
+ instance.define_multi_override
21
+ end
22
+
23
+ def initialize(klass, meth, initializer)
24
+ self.klass, self.meth, self.initializer, self.ivar = klass, meth, initializer, "@#{meth}"
25
+ end
26
+
27
+ def define_getter
28
+ ivar, meth, initializer = self.ivar, self.meth, self.initializer
29
+ klass.send :define_method, meth do
30
+ unless instance_variable_defined? ivar
31
+ instance_variable_set ivar, Deject::Dependency.new(self, meth, initializer)
32
+ end
33
+ instance_variable_get(ivar).invoke
34
+ end
35
+ end
36
+
37
+ def define_override
38
+ ivar, meth = self.ivar, self.meth
39
+ klass.send :define_method, "with_#{meth}" do |value=nil, &initializer|
40
+ initializer ||= Proc.new { value }
41
+ instance_variable_set ivar, Deject::Dependency.new(self, meth, initializer)
42
+ self
43
+ end
44
+ end
45
+
46
+ def define_multi_override
47
+ klass.send :define_method, :with_dependencies do |overrides|
48
+ overrides.each { |meth, value| send "with_#{meth}", value }
49
+ self
50
+ end
51
+ end
52
+ end
53
+
54
+
55
+ class Dependency < Struct.new(:instance, :dependency, :initializer)
56
+ attr_accessor :result
57
+
58
+ def invoke
59
+ validate_initializer!
60
+ memoized_result
61
+ end
62
+
63
+ def validate_initializer!
64
+ return if initializer
65
+ raise UninitializedDependency, "#{dependency} invoked before being defined"
66
+ end
67
+
68
+ def memoized_result
69
+ memoize! unless memoized?
70
+ result
71
+ end
72
+
73
+ def memoized?
74
+ @memoized
75
+ end
76
+
77
+ def memoize!
78
+ @memoized = true
79
+ self.result = instance.instance_eval &initializer
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,3 @@
1
+ module Deject
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,78 @@
1
+ require 'deject'
2
+
3
+ describe Deject, 'acceptance tests' do
4
+ example 'setting and overriding' do
5
+ class Client
6
+ def initialize(credentials)
7
+ @credentials = credentials
8
+ end
9
+
10
+ def login(name)
11
+ @login = name
12
+ end
13
+
14
+ def has_logged_in?(name)
15
+ @login == name
16
+ end
17
+
18
+ def initialized_with?(credentials)
19
+ @credentials == credentials
20
+ end
21
+ end
22
+
23
+ class Service
24
+ Deject self
25
+ dependency(:client) { Client.new credentials }
26
+
27
+ attr_accessor :name
28
+
29
+ def initialize(name)
30
+ self.name = name
31
+ end
32
+
33
+ def login
34
+ client.login name
35
+ end
36
+
37
+ def credentials
38
+ 'skj123@#KLFNV9ajv' # a login key or something, would probably be dejected as well
39
+ end
40
+ end
41
+
42
+ # using the default
43
+ service = Service.new('josh')
44
+ service.login
45
+ service.client.should have_logged_in 'josh'
46
+ service.client.should be_initialized_with service.credentials
47
+
48
+ # overriding the default at instance level
49
+ client = double('Mock Client 1')
50
+ client.should_receive(:login).with('sally')
51
+ Service.new('sally').with_client(client).login
52
+
53
+ client_class, client = double, double
54
+ george = Service.new('george').with_client { client_class.new credentials }
55
+ client_class.should_receive(:new).with(george.credentials).and_return(client)
56
+ client.should_receive(:login).with('george')
57
+ george.login
58
+
59
+ # class default remains the same
60
+ Service.new('josh').client.should be_a_kind_of Client
61
+
62
+ # overriding the default at class level
63
+ client = double('Mock Client 2')
64
+ client.should_receive(:login).with('mei')
65
+ Service.dependency(:client) { client }
66
+ Service.new('mei').login
67
+ end
68
+
69
+ example 'avoid all dependencies by omitting the default' do
70
+ klass = Class.new do
71
+ Deject self
72
+ dependency :client
73
+ end
74
+ expect { klass.new.client }.to raise_error Deject::UninitializedDependency
75
+ client = double
76
+ klass.new.with_client(client).client.should be client
77
+ end
78
+ end
@@ -0,0 +1,41 @@
1
+ require 'deject'
2
+
3
+
4
+ describe 'Klass.dependency' do
5
+ let(:klass) { Deject Class.new }
6
+
7
+ before { klass.new.should_not respond_to :dependency }
8
+
9
+ context 'with a block' do
10
+ meth = 'meth'.freeze
11
+ result = 100
12
+ before { klass.dependency(meth) { result } }
13
+
14
+ it 'adds the instance method' do
15
+ klass.new.should respond_to meth
16
+ end
17
+
18
+ specify 'the instance method defaults to the result of the init block' do
19
+ klass.new.send(meth).should == result
20
+ end
21
+
22
+ specify 'the instance method is evaluated within the context of the instance' do
23
+ klass.dependency(:a) { b }
24
+ instance = klass.new
25
+ def instance.b() 10 end
26
+ instance.a.should == instance.b
27
+ end
28
+ end
29
+
30
+ context 'without a block' do
31
+ it 'adds the instance method' do
32
+ klass.dependency :abc
33
+ klass.new.should respond_to :abc
34
+ end
35
+
36
+ it 'raises an error if called before setting it' do
37
+ klass.dependency :jjjjj
38
+ expect { klass.new.jjjjj }.to raise_error(Deject::UninitializedDependency, /jjjjj/)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,15 @@
1
+ require 'deject'
2
+
3
+ describe 'Deject()' do
4
+ let(:klass) { Class.new }
5
+
6
+ it 'adds the dependency method to the class' do
7
+ klass.should_not respond_to :dependency
8
+ Deject klass
9
+ klass.should respond_to :dependency
10
+ end
11
+
12
+ it 'returns the class' do
13
+ Deject(klass).should be klass
14
+ end
15
+ end
@@ -0,0 +1,46 @@
1
+ require 'deject'
2
+
3
+ describe 'after initializing a dependency' do
4
+ let(:klass) { Deject Class.new }
5
+
6
+ specify '#<dependency> returns the result, initialized for each instance' do
7
+ i = 0
8
+ klass.dependency(:number) { i += 1 }
9
+ klass.new.number.should == 1
10
+ klass.new.number.should == 2
11
+ end
12
+
13
+ it 'memoizes the result' do
14
+ i = 0
15
+ klass.dependency(:number) { i += 1 }
16
+ instance = klass.new
17
+ instance.number.should == 1
18
+ instance.number.should == 1
19
+ end
20
+
21
+ specify '#with_<dependency> overrides the result' do
22
+ klass.dependency(:number) { 5 }
23
+ klass.new.with_number(6).number.should == 6
24
+ end
25
+
26
+ specify '#with_<dependency> can take a value or an init block' do
27
+ klass.dependency(:number1) { 1 }
28
+ klass.dependency(:number2) { 2 }
29
+ i = 0
30
+ instance = klass.new.with_number2 { i += number1 }
31
+ instance.number2.should == instance.number1
32
+ instance.number2.should == instance.number1
33
+ i.should == instance.number1
34
+ end
35
+
36
+ example 'you can override multiple defaults from the instance level by using with_dependencies' do
37
+ klass = Class.new do
38
+ Deject self
39
+ dependency(:a) { 1 }
40
+ dependency(:b) { 2 }
41
+ end
42
+ instance = klass.new.with_dependencies(a: 10, b: 20)
43
+ instance.a.should == 10
44
+ instance.b.should == 20
45
+ end
46
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: deject
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Josh Cheek
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70320438529780 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70320438529780
25
+ - !ruby/object:Gem::Dependency
26
+ name: pry
27
+ requirement: &70320438528180 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70320438528180
36
+ description: Provides a super simple API for dependency injection
37
+ email:
38
+ - josh.cheek@gmail.com
39
+ executables: []
40
+ extensions: []
41
+ extra_rdoc_files: []
42
+ files:
43
+ - .gitignore
44
+ - Gemfile
45
+ - Rakefile
46
+ - Readme.md
47
+ - deject.gemspec
48
+ - lib/deject.rb
49
+ - lib/deject/functional.rb
50
+ - lib/deject/object_oriented.rb
51
+ - lib/deject/version.rb
52
+ - spec/acceptance_spec.rb
53
+ - spec/class_methods_spec.rb
54
+ - spec/deject_function_spec.rb
55
+ - spec/instance_methods_spec.rb
56
+ homepage: ''
57
+ licenses: []
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project: deject
76
+ rubygems_version: 1.8.10
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Simple dependency injection
80
+ test_files:
81
+ - spec/acceptance_spec.rb
82
+ - spec/class_methods_spec.rb
83
+ - spec/deject_function_spec.rb
84
+ - spec/instance_methods_spec.rb