deject 0.0.2 → 0.1.0

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.
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