jeeves 0.1.0 → 0.2.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/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
|
-
|