deject 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/deject.gemspec CHANGED
@@ -12,9 +12,9 @@ Gem::Specification.new do |s|
12
12
  s.description = %q{Provides a super simple API for dependency injection}
13
13
 
14
14
  s.rubyforge_project = "deject"
15
-
15
+
16
16
  s.required_ruby_version = "~> 1.9.2"
17
-
17
+
18
18
  s.files = `git ls-files`.split("\n")
19
19
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
20
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
@@ -23,5 +23,4 @@ Gem::Specification.new do |s|
23
23
  # specify any dependencies here; for example:
24
24
  s.add_development_dependency "rspec"
25
25
  s.add_development_dependency "pry"
26
- # s.add_runtime_dependency "rest-client"
27
26
  end
data/lib/deject.rb CHANGED
@@ -1,8 +1,100 @@
1
- case ENV['deject_implementation']
2
- when 'object_oriented'
3
- require 'deject/object_oriented'
4
- else
5
- require 'deject/functional'
1
+ require "deject/version"
2
+
3
+ module Deject
4
+ UninitializedDependency = Class.new StandardError
5
+
6
+ class << self
7
+ def register(name, &initializer)
8
+ raise ArgumentError, "#{name} has been registered multiple times" if registered? name
9
+ raise ArgumentError, "#{name} has been registered with Deject without an initialization block" unless initializer
10
+ @registered[name.intern] = initializer
11
+ end
12
+
13
+ def registered(name)
14
+ @registered[name.intern]
15
+ end
16
+
17
+ def registered?(name)
18
+ @registered.has_key? name.intern
19
+ end
20
+
21
+ def reset
22
+ @registered = {}
23
+ end
24
+ end
25
+
26
+ reset
6
27
  end
7
28
 
8
- require "deject/version"
29
+ # Not a common way of writing code in Ruby, I know.
30
+ # But I tried out several implementations and found this was the easiest to
31
+ # work with within the constraints of the gem (that it doesn't leave traces
32
+ # of itself all over your objects)
33
+ def Deject(klass)
34
+ uninitialized_error = lambda do |meth|
35
+ raise Deject::UninitializedDependency, "#{meth} invoked before being defined"
36
+ end
37
+
38
+ define_instance_methods = lambda do |meth, default_block|
39
+ # define the getter
40
+ define_method meth do
41
+ block = default_block || Deject.registered(meth)
42
+ uninitialized_error[meth] unless block
43
+ value = block.call self
44
+ define_singleton_method(meth) { value }
45
+ send meth
46
+ end
47
+
48
+ # define the override
49
+ define_method :"with_#{meth}" do |value=nil, &block|
50
+
51
+ # redefine getter if given a block
52
+ if block
53
+ define_singleton_method meth do
54
+ value = block.call self
55
+ define_singleton_method(meth) { value }
56
+ send meth
57
+ end
58
+
59
+ # always return value if given a value
60
+ else
61
+ define_singleton_method(meth) { value }
62
+ end
63
+
64
+ self
65
+ end
66
+ self
67
+ end
68
+
69
+ has_dependency = lambda do |meth|
70
+ instance_methods.include?(meth.intern) && instance_methods.include?(:"with_#{meth}")
71
+ end
72
+
73
+
74
+ # define klass.dependency
75
+ klass.define_singleton_method :dependency do |meth, &default_block|
76
+ if instance_exec meth, &has_dependency
77
+ warn "Deprecation: Use .override instead of .dependency to override a dependency"
78
+ end
79
+ instance_exec meth, default_block, &define_instance_methods
80
+ end
81
+
82
+ # define klass.override
83
+ klass.define_singleton_method :override do |meth, &override_block|
84
+ if !override_block
85
+ raise ArgumentError, "Cannot override #{meth} without an override block" unless override_block
86
+ elsif !instance_exec(meth, &has_dependency)
87
+ raise ArgumentError, "#{meth} is not a dependency of #{klass.inspect}"
88
+ else
89
+ instance_exec meth, override_block, &define_instance_methods
90
+ end
91
+ end
92
+
93
+ # override multiple dependencies
94
+ klass.send :define_method, :with_dependencies do |overrides|
95
+ overrides.each { |meth, value| send "with_#{meth}", value }
96
+ self
97
+ end
98
+
99
+ klass
100
+ end
@@ -1,3 +1,3 @@
1
1
  module Deject
2
- VERSION = '0.0.2'
2
+ VERSION = '0.1.0'
3
3
  end
@@ -1,4 +1,4 @@
1
- require 'deject'
1
+ require 'spec_helper'
2
2
 
3
3
  describe Deject, 'acceptance tests' do
4
4
  example 'setting and overriding' do
@@ -22,13 +22,13 @@ describe Deject, 'acceptance tests' do
22
22
 
23
23
  class Service
24
24
  Deject self
25
- dependency(:client) { Client.new credentials }
25
+ dependency(:client) { |service| Client.new service.credentials }
26
26
 
27
27
  attr_accessor :name
28
28
 
29
29
  def initialize(name)
30
30
  self.name = name
31
- end
31
+ end
32
32
 
33
33
  def login
34
34
  client.login name
@@ -51,7 +51,7 @@ describe Deject, 'acceptance tests' do
51
51
  Service.new('sally').with_client(client).login
52
52
 
53
53
  client_class, client = double, double
54
- george = Service.new('george').with_client { client_class.new credentials }
54
+ george = Service.new('george').with_client { |service| client_class.new service.credentials }
55
55
  client_class.should_receive(:new).with(george.credentials).and_return(client)
56
56
  client.should_receive(:login).with('george')
57
57
  george.login
@@ -1,4 +1,4 @@
1
- require 'deject'
1
+ require 'spec_helper'
2
2
 
3
3
 
4
4
  describe 'Klass.dependency' do
@@ -10,7 +10,7 @@ describe 'Klass.dependency' do
10
10
  meth = 'meth'.freeze
11
11
  result = 100
12
12
  before { klass.dependency(meth) { result } }
13
-
13
+
14
14
  it 'adds the instance method' do
15
15
  klass.new.should respond_to meth
16
16
  end
@@ -18,11 +18,20 @@ describe 'Klass.dependency' do
18
18
  specify 'the instance method defaults to the result of the init block' do
19
19
  klass.new.send(meth).should == result
20
20
  end
21
-
22
- specify 'the instance method is evaluated within the context of the instance' do
21
+
22
+ specify 'the instance method is evaluated within the context of the caller' do
23
23
  klass.dependency(:a) { b }
24
24
  instance = klass.new
25
25
  def instance.b() 10 end
26
+ def self.b() 20 end
27
+ instance.a.should == self.b
28
+ end
29
+
30
+ specify 'the instance method is passed the instance' do
31
+ klass.dependency(:a) { |instance| instance.b }
32
+ instance = klass.new
33
+ def instance.b() 10 end
34
+ def self.b() end
26
35
  instance.a.should == instance.b
27
36
  end
28
37
  end
@@ -37,5 +46,43 @@ describe 'Klass.dependency' do
37
46
  klass.dependency :jjjjj
38
47
  expect { klass.new.jjjjj }.to raise_error(Deject::UninitializedDependency, /jjjjj/)
39
48
  end
49
+
50
+ it 'uses the global block if provided, passing it the instance' do
51
+ Deject.register(:meth) { |instance| instance.value }
52
+ klass.dependency :meth
53
+ instance = klass.new
54
+ def instance.value() :value end
55
+ instance.meth.should == :value
56
+ end
57
+ end
58
+
59
+ it 'writes a deprecated warning when using dependency to override an existing dependency' do
60
+ catch_stderr { klass.dependency :dep }.should == ""
61
+ catch_stderr { klass.dependency :dep }.should =~ /deprecat/i
62
+ end
63
+ end
64
+
65
+
66
+ describe 'Klass.override' do
67
+ let(:klass) { Deject Class.new }
68
+
69
+ it "raises an ArgumentError if called for a dependency that doesn't exist" do
70
+ expect { klass.override(:dep) {} }.to raise_error ArgumentError, /dep/
71
+ end
72
+
73
+ it "raises an ArgumentError if called without a block" do
74
+ klass.dependency :dep
75
+ expect { klass.override :dep }.to raise_error ArgumentError, /block/
76
+ end
77
+
78
+ it 'overrides the dependency on the instance' do
79
+ klass.dependency :dep
80
+ klass.override(:dep) { 123 }
81
+ klass.new.dep.should == 123
82
+ end
83
+
84
+ it 'returns the class' do
85
+ klass.dependency :dep
86
+ klass.override(:dep) { 123 }.should equal klass
40
87
  end
41
88
  end
@@ -1,14 +1,20 @@
1
- require 'deject'
1
+ require 'spec_helper'
2
2
 
3
3
  describe 'Deject()' do
4
4
  let(:klass) { Class.new }
5
-
5
+
6
6
  it 'adds the dependency method to the class' do
7
7
  klass.should_not respond_to :dependency
8
8
  Deject klass
9
9
  klass.should respond_to :dependency
10
10
  end
11
11
 
12
+ it 'adds the override_dependency method to the class' do
13
+ klass.should_not respond_to :override
14
+ Deject klass
15
+ klass.should respond_to :override
16
+ end
17
+
12
18
  it 'returns the class' do
13
19
  Deject(klass).should be klass
14
20
  end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe Deject, '.register and registered' do
4
+ let(:value) { 12 }
5
+
6
+ after { Deject.reset }
7
+
8
+ it 'accepts a string' do
9
+ Deject.register("abc") { value }
10
+ Deject.registered("abc").call.should == value
11
+ Deject.registered(:abc).call.should == value
12
+ end
13
+
14
+ it 'accepts a symbol' do
15
+ Deject.register(:abc) { value }
16
+ Deject.registered("abc").call.should == value
17
+ Deject.registered(:abc).call.should == value
18
+ end
19
+
20
+ it 'register raises an ArgumentError if not provided with a block' do
21
+ expect { Deject.register :abc }.to raise_error ArgumentError, /block/i
22
+ end
23
+
24
+ it 'remembers what it was registered with' do
25
+ i = 0
26
+ Deject.register(:abc) { i += 1 }
27
+ Deject.registered(:abc).call.should == 1
28
+ i.should == 1
29
+ end
30
+
31
+ it 'returns nil when asked for something not registered' do
32
+ Deject.registered(:abc).should == nil
33
+ end
34
+
35
+ it 'raises an ArgumentError error if registration clobbers a previously set value' do
36
+ Deject.register(:abc){}
37
+ expect { Deject.register(:abc){} }.to raise_error ArgumentError, /abc/
38
+ end
39
+
40
+ it 'knows what has been registered' do
41
+ Deject.should_not be_registered :abc
42
+ Deject.should_not be_registered 'abc'
43
+ Deject.register(:abc) {}
44
+ Deject.should be_registered :abc
45
+ Deject.should be_registered 'abc'
46
+ end
47
+ end
@@ -1,4 +1,4 @@
1
- require 'deject'
1
+ require 'spec_helper'
2
2
 
3
3
  describe 'after initializing a dependency' do
4
4
  let(:klass) { Deject Class.new }
@@ -27,7 +27,7 @@ describe 'after initializing a dependency' do
27
27
  klass.dependency(:number1) { 1 }
28
28
  klass.dependency(:number2) { 2 }
29
29
  i = 0
30
- instance = klass.new.with_number2 { i += number1 }
30
+ instance = klass.new.with_number2 { |instance| i += instance.number1 }
31
31
  instance.number2.should == instance.number1
32
32
  instance.number2.should == instance.number1
33
33
  i.should == instance.number1
@@ -43,4 +43,11 @@ describe 'after initializing a dependency' do
43
43
  instance.a.should == 10
44
44
  instance.b.should == 20
45
45
  end
46
+
47
+ specify '#with_<dependency> is passed the instance' do
48
+ klass.dependency(:a) { |instance| instance.b }
49
+ instance = klass.new
50
+ def instance.b() 10 end
51
+ instance.a.should == instance.b
52
+ end
46
53
  end
@@ -0,0 +1,19 @@
1
+ require 'deject'
2
+
3
+ catch_stderr = Module.new do
4
+ require 'stringio'
5
+
6
+ def catch_stderr
7
+ old_stderr = $stderr
8
+ $stderr = StringIO.new
9
+ yield
10
+ ensure
11
+ to_return = $stderr.string
12
+ $stderr = old_stderr
13
+ return to_return
14
+ end
15
+ end
16
+
17
+ RSpec.configure do |config|
18
+ config.include catch_stderr
19
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deject
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-05 00:00:00.000000000Z
12
+ date: 2012-04-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &70212469081760 !ruby/object:Gem::Requirement
16
+ requirement: &70114295054960 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70212469081760
24
+ version_requirements: *70114295054960
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: pry
27
- requirement: &70212469080980 !ruby/object:Gem::Requirement
27
+ requirement: &70114295054020 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,7 +32,7 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70212469080980
35
+ version_requirements: *70114295054020
36
36
  description: Provides a super simple API for dependency injection
37
37
  email:
38
38
  - josh.cheek@gmail.com
@@ -46,13 +46,13 @@ files:
46
46
  - Readme.md
47
47
  - deject.gemspec
48
48
  - lib/deject.rb
49
- - lib/deject/functional.rb
50
- - lib/deject/object_oriented.rb
51
49
  - lib/deject/version.rb
52
50
  - spec/acceptance_spec.rb
53
51
  - spec/class_methods_spec.rb
54
52
  - spec/deject_function_spec.rb
53
+ - spec/global_registration_spec.rb
55
54
  - spec/instance_methods_spec.rb
55
+ - spec/spec_helper.rb
56
56
  homepage: https://github.com/JoshCheek/deject
57
57
  licenses: []
58
58
  post_install_message:
@@ -73,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
73
73
  version: '0'
74
74
  requirements: []
75
75
  rubyforge_project: deject
76
- rubygems_version: 1.8.10
76
+ rubygems_version: 1.8.11
77
77
  signing_key:
78
78
  specification_version: 3
79
79
  summary: Simple dependency injection
@@ -81,4 +81,7 @@ test_files:
81
81
  - spec/acceptance_spec.rb
82
82
  - spec/class_methods_spec.rb
83
83
  - spec/deject_function_spec.rb
84
+ - spec/global_registration_spec.rb
84
85
  - spec/instance_methods_spec.rb
86
+ - spec/spec_helper.rb
87
+ has_rdoc:
@@ -1,48 +0,0 @@
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
@@ -1,82 +0,0 @@
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