jeeves 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,9 +1,10 @@
1
1
  module Jeeves
2
- class ImportCallable
3
- def self.call(name, scope)
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).new
6
+ callable = scope.const_get(class_name)
7
+ callable.respond_to?(:call) ? callable : callable.new
7
8
  end
8
9
  end
9
10
 
@@ -1,6 +1,6 @@
1
1
  module Jeeves
2
- class ImportConstant
3
- def self.call(name, scope)
2
+ class ResolveConstant
3
+ def self.call(scope, name)
4
4
  const_name = name.upcase
5
5
  if scope.const_defined?(const_name)
6
6
  const_value = scope.const_get(const_name)
@@ -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
+
@@ -1,6 +1,6 @@
1
1
  module Jeeves
2
- class ImportMethod
3
- def self.call(name, scope)
2
+ class ResolveMethod
3
+ def self.call(scope, name)
4
4
  if scope.respond_to?(name)
5
5
  scope.method(name)
6
6
  end
@@ -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
+
@@ -1,4 +1,4 @@
1
1
  module Jeeves
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
4
4
 
data/lib/jeeves.rb CHANGED
@@ -1,18 +1,32 @@
1
1
  module Jeeves
2
- autoload :FindDependencies, "jeeves/find_dependencies"
3
- autoload :ImportMethod, "jeeves/import_method"
4
- autoload :ImportCallable, "jeeves/import_callable"
5
- autoload :ImportConstant, "jeeves/import_constant"
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.respond_to?(:fetch) ? args.pop : {}
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
- FindDependencies.call(scope, *args).each do |name, delegator|
14
- define_method(name) do |*args, &block|
15
- delegator.call(*args, &block)
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/import_constant"
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 ImportConstant do
10
+ describe ResolveConstant do
11
11
 
12
12
  it "returns an anonymous function which returns the constant" do
13
- delegator = ImportConstant.call(:my_constant, JeevesTestApp::ConstantTest)
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 = ImportConstant.call(:undefined_constant, JeevesTestApp::ConstantTest)
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/import_method"
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 ImportMethod do
12
+ describe ResolveMethod do
13
13
 
14
14
  it "returns an anonymous function which delegates to the method" do
15
- delegator = ImportMethod.call(:my_method, JeevesTestApp::MethodTest)
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 = ImportMethod.call(:undefined_method, JeevesTestApp::MethodTest)
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.1.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/import_callable.rb
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
- - spec/acceptance/import_spec.rb
44
- - spec/unit/import_callable_spec.rb
45
- - spec/unit/import_method_spec.rb
46
- - spec/unit/find_dependencies_spec.rb
47
- - spec/unit/import_constant_spec.rb
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/acceptance/import_spec.rb
74
- - spec/unit/import_callable_spec.rb
75
- - spec/unit/import_method_spec.rb
76
- - spec/unit/find_dependencies_spec.rb
77
- - spec/unit/import_constant_spec.rb
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
-