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