Skip to content
Browse files

added smart dependency resolution - resolve once on first call

  • Loading branch information...
1 parent eaf1651 commit f93b392a4c96a2955bf22f49b34001fa35dbe390 @ronhopper committed Jun 20, 2012
View
1 README.md
@@ -226,6 +226,7 @@ History
-------
### Edge
+* Default to smart dependency resolution - resolve once on first call
* Mock undefined dependency scope within RSpec or Test::Unit
### Version 0.2.1
View
54 lib/jeeves.rb
@@ -1,51 +1,21 @@
module Jeeves
- autoload :ResolveDependency, "jeeves/resolve_dependency"
- autoload :ResolveMethod, "jeeves/resolve_method"
- autoload :ResolveCallable, "jeeves/resolve_callable"
- autoload :ResolveConstant, "jeeves/resolve_constant"
+ autoload :Import, "jeeves/import"
+ autoload :ResolveDependency, "jeeves/resolve_dependency"
+ autoload :ResolveMethod, "jeeves/resolve_method"
+ autoload :ResolveCallable, "jeeves/resolve_callable"
+ autoload :ResolveConstant, "jeeves/resolve_constant"
+ autoload :DefineImportedMethod, "jeeves/define_imported_method"
+ autoload :StubScope, "jeeves/stub_scope"
def import(*args)
- options = args.last.is_a?(Hash) ? args.pop : {}
- scope = options.fetch(:from) do
- module_names = ancestors.first.to_s.split('::')[0..-2]
- module_names.inject(Object) { |m, c| m.const_get(c) }
- end
- args.each do |name|
- if name.is_a?(Array)
- external_name, internal_name = *name
- else
- external_name = internal_name = name
- end
- if options[:lazy]
- self.class.send(:define_method, internal_name) do |*args, &block|
- delegator = ResolveDependency.call(scope, external_name)
- delegator.call(*args, &block)
- end
- else
- delegator = ResolveDependency.call(scope, external_name)
- self.class.send(:define_method, internal_name) do |*args, &block|
- delegator.call(*args, &block)
- end
- end
- define_method(internal_name) do |*args, &block|
- self.class.send(internal_name, *args, &block)
- end
- end
+ Import.new.call(self, *args)
end
def const_missing(name)
- return super unless Jeeves.in_test_framework?
- Module.new do
- @name = [name]
- class << self
- def const_missing(name)
- @name << name
- self
- end
- def to_s
- @name.join("::")
- end
- end
+ if Jeeves.in_test_framework?
+ StubScope.call(name)
+ else
+ super
end
end
View
56 lib/jeeves/define_imported_method.rb
@@ -0,0 +1,56 @@
+module Jeeves
+ class DefineImportedMethod
+ def call(target, internal_name, options)
+ @target = target
+ @internal_name = internal_name
+ @scope = options.fetch(:from)
+ @external_name = options.fetch(:name)
+ if options[:lazy] == true
+ define_lazy
+ elsif options[:lazy] == false
+ define_non_lazy
+ else
+ define_smart
+ end
+ define_on_instance
+ end
+
+ private
+
+ def define_lazy
+ scope = @scope
+ external_name = @external_name
+ @target.class.send(:define_method, @internal_name) do |*args, &block|
+ delegator = ResolveDependency.call(scope, external_name)
+ delegator.call(*args, &block)
+ end
+ end
+
+ def define_non_lazy
+ delegator = ResolveDependency.call(@scope, @external_name)
+ @target.class.send(:define_method, @internal_name) do |*args, &block|
+ delegator.call(*args, &block)
+ end
+ end
+
+ def define_smart
+ scope = @scope
+ internal_name = @internal_name
+ external_name = @external_name
+ @target.class.send(:define_method, @internal_name) do |*args, &block|
+ delegator = ResolveDependency.call(scope, external_name)
+ self.class.send(:define_method, internal_name) do |*args, &block|
+ delegator.call(*args, &block)
+ end
+ delegator.call(*args, &block)
+ end
+ end
+
+ def define_on_instance
+ internal_name = @internal_name
+ @target.send(:define_method, internal_name) do |*args, &block|
+ self.class.send(internal_name, *args, &block)
+ end
+ end
+ end
+end
View
27 lib/jeeves/import.rb
@@ -0,0 +1,27 @@
+module Jeeves
+ class Import
+ def call(target, *args)
+ @target = target
+ parse_options(args)
+ @names.each do |external_name, internal_name|
+ DefineImportedMethod.new.call(target, internal_name, from: @scope,
+ name: external_name, lazy: @lazy)
+ end
+ end
+
+ private
+
+ def parse_options(args)
+ options = args.last.is_a?(Hash) ? args.pop : {}
+ @names = args.map { |name| name.is_a?(Array) ? name : [name, name] }
+ @scope = options.fetch(:from) { default_scope }
+ @lazy = options.fetch(:lazy, :smart)
+ end
+
+ def default_scope
+ module_names = @target.ancestors.first.to_s.split('::')[0..-2]
+ module_names.inject(Object) { |m, c| m.const_get(c) }
+ end
+ end
+end
+
View
19 lib/jeeves/stub_scope.rb
@@ -0,0 +1,19 @@
+module Jeeves
+ class StubScope
+ def self.call(name)
+ Module.new do
+ @name = [name]
+ class << self
+ def const_missing(name)
+ @name << name
+ self
+ end
+ def to_s
+ @name.join("::")
+ end
+ end
+ end
+ end
+ end
+end
+
View
30 spec/integration/import_spec.rb
@@ -29,10 +29,15 @@ def self.call(*args, &block)
class TestSubject
extend Jeeves
import :my_method, :my_callable, :my_constant, from: OtherScope
- import :marco, :static_callable
+ import :marco, :static_callable, lazy: false
import :lazy_method, lazy: true
+ import :smart_resolver
import [:marco, :say_polo]
end
+
+ def self.smart_resolver
+ :acts_smart
+ end
end
end
@@ -63,15 +68,23 @@ class TestSubject
end
it "resolves lazy dependencies at call time" do
- JeevesTestApp::InnerScope.stub(:lazy_method) { :snooze }
- subject.lazy_method.should == :snooze
+ JeevesTestApp::InnerScope.stub(:lazy_method) { :snooze1 }
+ subject.lazy_method.should == :snooze1
+ JeevesTestApp::InnerScope.stub(:lazy_method) { :snooze2 }
+ subject.lazy_method.should == :snooze2
end
it "resolves non-lazy dependencies at import time" do
JeevesTestApp::InnerScope.stub(:marco) { :not_polo }
subject.marco.should == :polo
end
+ it "defaults to resolving dependencies once at first call" do
+ subject.smart_resolver
+ JeevesTestApp::InnerScope.stub(:smart_resolver) { :snooze }
+ subject.smart_resolver.should == :acts_smart
+ end
+
it "imports under an alias" do
subject.say_polo.should == :polo
end
@@ -83,16 +96,19 @@ class TestSubject
it "raises an error if no importers can find the dependency" do
Jeeves.stub(:in_test_framework?) { false } # to avoid RSpec integration
- expect { subject.class.import :unknown, from: JeevesTestApp::OtherScope }.
- to raise_error(Jeeves::UnresolvedDependency,
- "Dependency 'unknown' was not found in JeevesTestApp::OtherScope")
+ expect do
+ class JeevesTestApp::InnerScope::TestSubject
+ import :unknown, from: JeevesTestApp::OtherScope, lazy: false
+ end
+ end.to raise_error(Jeeves::UnresolvedDependency,
+ "Dependency 'unknown' was not found in JeevesTestApp::OtherScope")
end
it "raises an error if the scope is undefined" do
Jeeves.stub(:in_test_framework?) { false } # to avoid RSpec integration
expect do
class JeevesTestApp::InnerScope::TestSubject
- import :unknown, from: MyUndefined::Scope
+ import :unknown, from: MyUndefined::Scope, lazy: false
end
end.to raise_error(NameError)
end
View
2 spec/unit/resolve_callable_spec.rb
@@ -18,7 +18,6 @@ def call(*args, &block)
module Jeeves
describe ResolveCallable do
-
it "returns an anonymous function which delegates to the static call method of the callable" do
delegator = ResolveCallable.call(JeevesTestApp::CallableTest, :static_callable)
result = delegator.call(:foo, :bar, :baz) { |s| s.capitalize }
@@ -35,7 +34,6 @@ module Jeeves
delegator = ResolveCallable.call(JeevesTestApp::CallableTest, :undefined_callable)
delegator.should be(nil)
end
-
end
end
View
2 spec/unit/resolve_constant_spec.rb
@@ -8,7 +8,6 @@ module ConstantTest
module Jeeves
describe ResolveConstant do
-
it "returns an anonymous function which returns the constant" do
delegator = ResolveConstant.call(JeevesTestApp::ConstantTest, :my_constant)
delegator.call.should == "MY VALUE"
@@ -18,7 +17,6 @@ module Jeeves
delegator = ResolveConstant.call(JeevesTestApp::ConstantTest, :undefined_constant)
delegator.should be(nil)
end
-
end
end
View
2 spec/unit/resolve_method_spec.rb
@@ -10,7 +10,6 @@ def self.my_method(*args, &block)
module Jeeves
describe ResolveMethod do
-
it "returns an anonymous function which delegates to the method" do
delegator = ResolveMethod.call(JeevesTestApp::MethodTest, :my_method)
result = delegator.call(:foo, :bar, :baz) { |s| s.upcase }
@@ -21,7 +20,6 @@ module Jeeves
delegator = ResolveMethod.call(JeevesTestApp::MethodTest, :undefined_method)
delegator.should be(nil)
end
-
end
end

0 comments on commit f93b392

Please sign in to comment.
Something went wrong with that request. Please try again.