jeeves 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/jeeves/{import_callable.rb → resolve_callable.rb} +4 -3
- data/lib/jeeves/{import_constant.rb → resolve_constant.rb} +2 -2
- data/lib/jeeves/resolve_dependency.rb +14 -0
- data/lib/jeeves/{import_method.rb → resolve_method.rb} +2 -2
- data/lib/jeeves/resolve_mock.rb +22 -0
- data/lib/jeeves/version.rb +1 -1
- data/lib/jeeves.rb +22 -8
- data/spec/{acceptance → integration}/import_spec.rb +29 -1
- data/spec/integration/rspec_spec.rb +29 -0
- data/spec/unit/resolve_callable_spec.rb +41 -0
- data/spec/unit/{import_constant_spec.rb → resolve_constant_spec.rb} +4 -4
- data/spec/unit/resolve_dependency_spec.rb +48 -0
- data/spec/unit/{import_method_spec.rb → resolve_method_spec.rb} +4 -4
- metadata +18 -15
- data/lib/jeeves/find_dependencies.rb +0 -22
- data/spec/unit/find_dependencies_spec.rb +0 -57
- data/spec/unit/import_callable_spec.rb +0 -29
@@ -1,9 +1,10 @@
|
|
1
1
|
module Jeeves
|
2
|
-
class
|
3
|
-
def self.call(
|
2
|
+
class ResolveCallable
|
3
|
+
def self.call(scope, name)
|
4
4
|
class_name = camelize(name)
|
5
5
|
if scope.const_defined?(class_name)
|
6
|
-
scope.const_get(class_name)
|
6
|
+
callable = scope.const_get(class_name)
|
7
|
+
callable.respond_to?(:call) ? callable : callable.new
|
7
8
|
end
|
8
9
|
end
|
9
10
|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Jeeves
|
2
|
+
class ResolveDependency
|
3
|
+
RESOLVERS = [ResolveMethod, ResolveCallable, ResolveConstant, ResolveMock]
|
4
|
+
|
5
|
+
def self.call(scope, name)
|
6
|
+
delegator = nil
|
7
|
+
RESOLVERS.each do |resolver|
|
8
|
+
break if delegator = resolver.call(scope, name)
|
9
|
+
end
|
10
|
+
delegator or raise ArgumentError, "Dependency '#{name}' was not found in #{scope}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Jeeves
|
2
|
+
class ResolveMock
|
3
|
+
def self.call(scope, name)
|
4
|
+
if in_test_framework?
|
5
|
+
lambda do |*args, &block|
|
6
|
+
if Jeeves.respond_to?(name)
|
7
|
+
Jeeves.public_send(name, *args, &block)
|
8
|
+
else
|
9
|
+
raise ArgumentError, "Dependency '#{name}' was not found in #{scope}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def self.in_test_framework?
|
18
|
+
defined?(RSpec) || defined?(Test::Unit)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
data/lib/jeeves/version.rb
CHANGED
data/lib/jeeves.rb
CHANGED
@@ -1,18 +1,32 @@
|
|
1
1
|
module Jeeves
|
2
|
-
autoload :
|
3
|
-
autoload :
|
4
|
-
autoload :
|
5
|
-
autoload :
|
2
|
+
autoload :ResolveDependency, "jeeves/resolve_dependency"
|
3
|
+
autoload :ResolveMethod, "jeeves/resolve_method"
|
4
|
+
autoload :ResolveCallable, "jeeves/resolve_callable"
|
5
|
+
autoload :ResolveConstant, "jeeves/resolve_constant"
|
6
|
+
autoload :ResolveMock, "jeeves/resolve_mock"
|
6
7
|
|
7
8
|
def import(*args)
|
8
|
-
options = args.last.
|
9
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
9
10
|
scope = options.fetch(:from) do
|
10
11
|
module_names = ancestors.first.to_s.split('::')[0..-2]
|
11
12
|
module_names.inject(Object) { |m, c| m.const_get(c) }
|
12
13
|
end
|
13
|
-
|
14
|
-
|
15
|
-
|
14
|
+
args.each do |name|
|
15
|
+
if name.is_a?(Array)
|
16
|
+
external_name, internal_name = *name
|
17
|
+
else
|
18
|
+
external_name = internal_name = name
|
19
|
+
end
|
20
|
+
if options[:lazy]
|
21
|
+
define_method(internal_name) do |*args, &block|
|
22
|
+
delegator = ResolveDependency.call(scope, external_name)
|
23
|
+
delegator.call(*args, &block)
|
24
|
+
end
|
25
|
+
else
|
26
|
+
delegator = ResolveDependency.call(scope, external_name)
|
27
|
+
define_method(internal_name) do |*args, &block|
|
28
|
+
delegator.call(*args, &block)
|
29
|
+
end
|
16
30
|
end
|
17
31
|
end
|
18
32
|
end
|
@@ -20,10 +20,18 @@ module JeevesTestApp
|
|
20
20
|
:polo
|
21
21
|
end
|
22
22
|
|
23
|
+
class StaticCallable
|
24
|
+
def self.call(*args, &block)
|
25
|
+
block.call(args.map(&:to_s).join("*"))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
23
29
|
class TestSubject
|
24
30
|
extend Jeeves
|
25
31
|
import :my_method, :my_callable, :my_constant, from: OtherScope
|
26
|
-
import :marco
|
32
|
+
import :marco, :static_callable
|
33
|
+
import :lazy_method, lazy: true
|
34
|
+
import [:marco, :say_polo]
|
27
35
|
end
|
28
36
|
end
|
29
37
|
end
|
@@ -36,6 +44,11 @@ describe "import" do
|
|
36
44
|
result.should == "FOO-BAR-BAZ"
|
37
45
|
end
|
38
46
|
|
47
|
+
it "imports a static callable" do
|
48
|
+
result = subject.static_callable(:foo, :bar, :baz) { |s| s.capitalize }
|
49
|
+
result.should == "Foo*bar*baz"
|
50
|
+
end
|
51
|
+
|
39
52
|
it "imports a callable" do
|
40
53
|
result = subject.my_callable(:foo, :bar, :baz) { |s| s.reverse }
|
41
54
|
result.should == "zab+rab+oof"
|
@@ -49,7 +62,22 @@ describe "import" do
|
|
49
62
|
subject.marco.should == :polo
|
50
63
|
end
|
51
64
|
|
65
|
+
it "resolves lazy dependencies at call time" do
|
66
|
+
JeevesTestApp::InnerScope.stub(:lazy_method) { :snooze }
|
67
|
+
subject.lazy_method.should == :snooze
|
68
|
+
end
|
69
|
+
|
70
|
+
it "resolves non-lazy dependencies at import time" do
|
71
|
+
JeevesTestApp::InnerScope.stub(:marco) { :not_polo }
|
72
|
+
subject.marco.should == :polo
|
73
|
+
end
|
74
|
+
|
75
|
+
it "imports under an alias" do
|
76
|
+
subject.say_polo.should == :polo
|
77
|
+
end
|
78
|
+
|
52
79
|
it "raises an error if no importers can find the dependency" do
|
80
|
+
Jeeves::ResolveMock.stub(:call) # to avoid RSpec integration
|
53
81
|
expect { subject.class.import :unknown, from: JeevesTestApp::OtherScope }.
|
54
82
|
to raise_error(ArgumentError,
|
55
83
|
"Dependency 'unknown' was not found in JeevesTestApp::OtherScope")
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "jeeves"
|
2
|
+
|
3
|
+
module JeevesTestApp
|
4
|
+
module InnerScope
|
5
|
+
class TestSubject
|
6
|
+
extend Jeeves
|
7
|
+
import :my_mock
|
8
|
+
|
9
|
+
def call
|
10
|
+
my_mock(42)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "rspec integration" do
|
17
|
+
subject { JeevesTestApp::InnerScope::TestSubject.new }
|
18
|
+
|
19
|
+
it "wires dependencies directly to Jeeves for easy stubbing" do
|
20
|
+
Jeeves.stub(:my_mock).with(42) { "The answer?" }
|
21
|
+
subject.call.should == "The answer?"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "raises an error if the dependency is not mocked" do
|
25
|
+
expect { subject.call }.to raise_error(ArgumentError,
|
26
|
+
"Dependency 'my_mock' was not found in JeevesTestApp::InnerScope")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "jeeves/resolve_callable"
|
2
|
+
|
3
|
+
module JeevesTestApp
|
4
|
+
module CallableTest
|
5
|
+
class StaticCallable
|
6
|
+
def self.call(*args, &block)
|
7
|
+
block.call(args.map(&:to_s).join("*"))
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class MyCallable
|
12
|
+
def call(*args, &block)
|
13
|
+
block.call(args.map(&:to_s).join("-"))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module Jeeves
|
20
|
+
describe ResolveCallable do
|
21
|
+
|
22
|
+
it "returns an anonymous function which delegates to the static call method of the callable" do
|
23
|
+
delegator = ResolveCallable.call(JeevesTestApp::CallableTest, :static_callable)
|
24
|
+
result = delegator.call(:foo, :bar, :baz) { |s| s.capitalize }
|
25
|
+
result.should == "Foo*bar*baz"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns an anonymous function which delegates to a new instance of the callable" do
|
29
|
+
delegator = ResolveCallable.call(JeevesTestApp::CallableTest, :my_callable)
|
30
|
+
result = delegator.call(:foo, :bar, :baz) { |s| s.upcase }
|
31
|
+
result.should == "FOO-BAR-BAZ"
|
32
|
+
end
|
33
|
+
|
34
|
+
it "returns nil if the the callable class is not defined" do
|
35
|
+
delegator = ResolveCallable.call(JeevesTestApp::CallableTest, :undefined_callable)
|
36
|
+
delegator.should be(nil)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require "jeeves/
|
1
|
+
require "jeeves/resolve_constant"
|
2
2
|
|
3
3
|
module JeevesTestApp
|
4
4
|
module ConstantTest
|
@@ -7,15 +7,15 @@ module JeevesTestApp
|
|
7
7
|
end
|
8
8
|
|
9
9
|
module Jeeves
|
10
|
-
describe
|
10
|
+
describe ResolveConstant do
|
11
11
|
|
12
12
|
it "returns an anonymous function which returns the constant" do
|
13
|
-
delegator =
|
13
|
+
delegator = ResolveConstant.call(JeevesTestApp::ConstantTest, :my_constant)
|
14
14
|
delegator.call.should == "MY VALUE"
|
15
15
|
end
|
16
16
|
|
17
17
|
it "returns nil if the constant is not defined" do
|
18
|
-
delegator =
|
18
|
+
delegator = ResolveConstant.call(JeevesTestApp::ConstantTest, :undefined_constant)
|
19
19
|
delegator.should be(nil)
|
20
20
|
end
|
21
21
|
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Jeeves
|
2
|
+
class ResolveMethod; end
|
3
|
+
class ResolveCallable; end
|
4
|
+
class ResolveConstant; end
|
5
|
+
class ResolveMock; end
|
6
|
+
end
|
7
|
+
require "jeeves/resolve_dependency"
|
8
|
+
|
9
|
+
module Jeeves
|
10
|
+
describe ResolveDependency do
|
11
|
+
let(:scope) { stub("scope", to_s: "ScopeStub") }
|
12
|
+
let(:delegator) { stub("delegator") }
|
13
|
+
|
14
|
+
before do
|
15
|
+
ResolveMethod.stub(:call)
|
16
|
+
ResolveCallable.stub(:call)
|
17
|
+
ResolveConstant.stub(:call)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "maps dependency names to anonymous delegator functions" do
|
21
|
+
ResolveMethod.stub(:call).with(scope, :my_dependency) { delegator }
|
22
|
+
ResolveDependency.call(scope, :my_dependency).should be(delegator)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "uses ResolveCallable if ResolveMethod fails" do
|
26
|
+
ResolveCallable.stub(:call).with(scope, :my_dependency) { delegator }
|
27
|
+
ResolveDependency.call(scope, :my_dependency).should be(delegator)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "uses ResolveConstant if ResolveMethod and ResolveCallable fail" do
|
31
|
+
ResolveConstant.stub(:call).with(scope, :my_dependency) { delegator }
|
32
|
+
ResolveDependency.call(scope, :my_dependency).should be(delegator)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "uses ResolveMock as a last resort" do
|
36
|
+
ResolveMock.stub(:call).with(scope, :my_dependency) { delegator }
|
37
|
+
ResolveDependency.call(scope, :my_dependency).should be(delegator)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "raises an error if all importers fail" do
|
41
|
+
Jeeves::ResolveMock.stub(:call) # to avoid RSpec integration
|
42
|
+
expect { ResolveDependency.call(scope, :my_dependency) }.
|
43
|
+
to raise_error(ArgumentError,
|
44
|
+
"Dependency 'my_dependency' was not found in ScopeStub")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require "jeeves/
|
1
|
+
require "jeeves/resolve_method"
|
2
2
|
|
3
3
|
module JeevesTestApp
|
4
4
|
module MethodTest
|
@@ -9,16 +9,16 @@ module JeevesTestApp
|
|
9
9
|
end
|
10
10
|
|
11
11
|
module Jeeves
|
12
|
-
describe
|
12
|
+
describe ResolveMethod do
|
13
13
|
|
14
14
|
it "returns an anonymous function which delegates to the method" do
|
15
|
-
delegator =
|
15
|
+
delegator = ResolveMethod.call(JeevesTestApp::MethodTest, :my_method)
|
16
16
|
result = delegator.call(:foo, :bar, :baz) { |s| s.upcase }
|
17
17
|
result.should == "FOO-BAR-BAZ"
|
18
18
|
end
|
19
19
|
|
20
20
|
it "returns nil if the scope does not respond to the method" do
|
21
|
-
delegator =
|
21
|
+
delegator = ResolveMethod.call(JeevesTestApp::MethodTest, :undefined_method)
|
22
22
|
delegator.should be(nil)
|
23
23
|
end
|
24
24
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jeeves
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -35,16 +35,18 @@ extensions: []
|
|
35
35
|
extra_rdoc_files: []
|
36
36
|
files:
|
37
37
|
- lib/jeeves.rb
|
38
|
-
- lib/jeeves/
|
39
|
-
- lib/jeeves/import_constant.rb
|
40
|
-
- lib/jeeves/find_dependencies.rb
|
41
|
-
- lib/jeeves/import_method.rb
|
38
|
+
- lib/jeeves/resolve_dependency.rb
|
42
39
|
- lib/jeeves/version.rb
|
43
|
-
-
|
44
|
-
-
|
45
|
-
-
|
46
|
-
-
|
47
|
-
- spec/
|
40
|
+
- lib/jeeves/resolve_constant.rb
|
41
|
+
- lib/jeeves/resolve_method.rb
|
42
|
+
- lib/jeeves/resolve_mock.rb
|
43
|
+
- lib/jeeves/resolve_callable.rb
|
44
|
+
- spec/integration/rspec_spec.rb
|
45
|
+
- spec/integration/import_spec.rb
|
46
|
+
- spec/unit/resolve_method_spec.rb
|
47
|
+
- spec/unit/resolve_callable_spec.rb
|
48
|
+
- spec/unit/resolve_constant_spec.rb
|
49
|
+
- spec/unit/resolve_dependency_spec.rb
|
48
50
|
homepage: http://github.com/ronhopper/jeeves
|
49
51
|
licenses: []
|
50
52
|
post_install_message:
|
@@ -70,8 +72,9 @@ signing_key:
|
|
70
72
|
specification_version: 3
|
71
73
|
summary: Jeeves is a dependency management library for Ruby.
|
72
74
|
test_files:
|
73
|
-
- spec/
|
74
|
-
- spec/
|
75
|
-
- spec/unit/
|
76
|
-
- spec/unit/
|
77
|
-
- spec/unit/
|
75
|
+
- spec/integration/rspec_spec.rb
|
76
|
+
- spec/integration/import_spec.rb
|
77
|
+
- spec/unit/resolve_method_spec.rb
|
78
|
+
- spec/unit/resolve_callable_spec.rb
|
79
|
+
- spec/unit/resolve_constant_spec.rb
|
80
|
+
- spec/unit/resolve_dependency_spec.rb
|
@@ -1,22 +0,0 @@
|
|
1
|
-
module Jeeves
|
2
|
-
class FindDependencies
|
3
|
-
def self.call(scope, *names)
|
4
|
-
names.inject({}) do |dependencies, name|
|
5
|
-
dependencies.update(name => delegator_for(name, scope))
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
private
|
10
|
-
|
11
|
-
IMPORTERS = [ImportMethod, ImportCallable, ImportConstant]
|
12
|
-
|
13
|
-
def self.delegator_for(name, scope)
|
14
|
-
delegator = nil
|
15
|
-
IMPORTERS.each do |importer|
|
16
|
-
break if delegator = importer.call(name, scope)
|
17
|
-
end
|
18
|
-
delegator or raise ArgumentError, "Dependency '#{name}' was not found in #{scope}"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
@@ -1,57 +0,0 @@
|
|
1
|
-
module Jeeves
|
2
|
-
class ImportMethod; end
|
3
|
-
class ImportCallable; end
|
4
|
-
class ImportConstant; end
|
5
|
-
end
|
6
|
-
require "jeeves/find_dependencies"
|
7
|
-
|
8
|
-
module Jeeves
|
9
|
-
describe FindDependencies do
|
10
|
-
let(:scope) { stub("scope", to_s: "ScopeStub") }
|
11
|
-
let(:delegator) { stub("delegator") }
|
12
|
-
|
13
|
-
before do
|
14
|
-
ImportMethod.stub(:call)
|
15
|
-
ImportCallable.stub(:call)
|
16
|
-
ImportConstant.stub(:call)
|
17
|
-
end
|
18
|
-
|
19
|
-
it "maps dependency names to anonymous delegator functions" do
|
20
|
-
ImportMethod.stub(:call).with(:my_dependency, scope) { delegator }
|
21
|
-
FindDependencies.call(scope, :my_dependency).should == {
|
22
|
-
my_dependency: delegator
|
23
|
-
}
|
24
|
-
end
|
25
|
-
|
26
|
-
it "maps multiple dependencies" do
|
27
|
-
delegator2 = stub("delegator 2")
|
28
|
-
ImportMethod.stub(:call).with(:my_dep_1, scope) { delegator }
|
29
|
-
ImportMethod.stub(:call).with(:my_dep_2, scope) { delegator2 }
|
30
|
-
FindDependencies.call(scope, :my_dep_1, :my_dep_2).should == {
|
31
|
-
my_dep_1: delegator,
|
32
|
-
my_dep_2: delegator2
|
33
|
-
}
|
34
|
-
end
|
35
|
-
|
36
|
-
it "uses ImportCallable if ImportMethod fails" do
|
37
|
-
ImportCallable.stub(:call).with(:my_dependency, scope) { delegator }
|
38
|
-
FindDependencies.call(scope, :my_dependency).should == {
|
39
|
-
my_dependency: delegator
|
40
|
-
}
|
41
|
-
end
|
42
|
-
|
43
|
-
it "uses ImportConstant if ImportMethod and ImportCallable fail" do
|
44
|
-
ImportConstant.stub(:call).with(:my_dependency, scope) { delegator }
|
45
|
-
FindDependencies.call(scope, :my_dependency).should == {
|
46
|
-
my_dependency: delegator
|
47
|
-
}
|
48
|
-
end
|
49
|
-
|
50
|
-
it "raises an error if all importers fail" do
|
51
|
-
expect { FindDependencies.call(scope, :my_dependency) }.
|
52
|
-
to raise_error(ArgumentError,
|
53
|
-
"Dependency 'my_dependency' was not found in ScopeStub")
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
@@ -1,29 +0,0 @@
|
|
1
|
-
require "jeeves/import_callable"
|
2
|
-
|
3
|
-
module JeevesTestApp
|
4
|
-
module CallableTest
|
5
|
-
class MyCallable
|
6
|
-
def call(*args, &block)
|
7
|
-
block.call(args.map(&:to_s).join("-"))
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
module Jeeves
|
14
|
-
describe ImportCallable do
|
15
|
-
|
16
|
-
it "returns an anonymous function which delegates to a new instance of the callable" do
|
17
|
-
delegator = ImportCallable.call(:my_callable, JeevesTestApp::CallableTest)
|
18
|
-
result = delegator.call(:foo, :bar, :baz) { |s| s.upcase }
|
19
|
-
result.should == "FOO-BAR-BAZ"
|
20
|
-
end
|
21
|
-
|
22
|
-
it "returns nil if the the callable class is not defined" do
|
23
|
-
delegator = ImportCallable.call(:undefined_callable, JeevesTestApp::CallableTest)
|
24
|
-
delegator.should be(nil)
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|