scorpion-ioc 0.3.1 → 0.4.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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.rspec +2 -1
  4. data/README.md +111 -44
  5. data/lib/scorpion/attribute.rb +0 -1
  6. data/lib/scorpion/attribute_set.rb +15 -7
  7. data/lib/scorpion/dependency/argument_dependency.rb +25 -0
  8. data/lib/scorpion/{prey/builder_prey.rb → dependency/builder_dependency.rb} +8 -8
  9. data/lib/scorpion/dependency/captured_dependency.rb +44 -0
  10. data/lib/scorpion/dependency/class_dependency.rb +25 -0
  11. data/lib/scorpion/dependency/module_dependency.rb +14 -0
  12. data/lib/scorpion/dependency.rb +137 -0
  13. data/lib/scorpion/dependency_map.rb +135 -0
  14. data/lib/scorpion/hunt.rb +158 -0
  15. data/lib/scorpion/hunter.rb +21 -20
  16. data/lib/scorpion/locale/en.yml +5 -1
  17. data/lib/scorpion/{king.rb → object.rb} +72 -53
  18. data/lib/scorpion/object_constructor.rb +55 -0
  19. data/lib/scorpion/rails/active_record/association.rb +65 -0
  20. data/lib/scorpion/rails/active_record/model.rb +28 -0
  21. data/lib/scorpion/rails/active_record/relation.rb +66 -0
  22. data/lib/scorpion/rails/active_record.rb +21 -0
  23. data/lib/scorpion/rails/controller.rb +22 -62
  24. data/lib/scorpion/rails/job.rb +30 -0
  25. data/lib/scorpion/rails/nest.rb +86 -0
  26. data/lib/scorpion/rails/railtie.rb +16 -0
  27. data/lib/scorpion/rails.rb +4 -0
  28. data/lib/scorpion/rspec/helper.rb +25 -0
  29. data/lib/scorpion/rspec.rb +17 -0
  30. data/lib/scorpion/stinger.rb +69 -0
  31. data/lib/scorpion/version.rb +1 -1
  32. data/lib/scorpion.rb +91 -44
  33. data/scorpion.gemspec +1 -1
  34. data/spec/internal/app/models/author.rb +17 -0
  35. data/spec/internal/app/models/todo.rb +14 -0
  36. data/spec/internal/db/schema.rb +12 -1
  37. data/spec/lib/scorpion/dependency/argument_dependency_spec.rb +18 -0
  38. data/spec/lib/scorpion/dependency/builder_dependency_spec.rb +41 -0
  39. data/spec/lib/scorpion/dependency/module_dependency_spec.rb +16 -0
  40. data/spec/lib/scorpion/dependency_map_spec.rb +108 -0
  41. data/spec/lib/scorpion/dependency_spec.rb +131 -0
  42. data/spec/lib/scorpion/hunt_spec.rb +93 -0
  43. data/spec/lib/scorpion/hunter_spec.rb +53 -14
  44. data/spec/lib/scorpion/object_constructor_spec.rb +49 -0
  45. data/spec/lib/scorpion/object_spec.rb +214 -0
  46. data/spec/lib/scorpion/rails/active_record/association_spec.rb +26 -0
  47. data/spec/lib/scorpion/rails/active_record/model_spec.rb +33 -0
  48. data/spec/lib/scorpion/rails/active_record/relation_spec.rb +72 -0
  49. data/spec/lib/scorpion/rails/controller_spec.rb +9 -9
  50. data/spec/lib/scorpion/rails/job_spec.rb +34 -0
  51. data/spec/lib/scorpion/rspec/helper_spec.rb +44 -0
  52. data/spec/lib/scorpion_spec.rb +0 -35
  53. data/spec/spec_helper.rb +1 -0
  54. metadata +54 -26
  55. data/lib/scorpion/hunting_map.rb +0 -139
  56. data/lib/scorpion/prey/captured_prey.rb +0 -44
  57. data/lib/scorpion/prey/class_prey.rb +0 -13
  58. data/lib/scorpion/prey/hunted_prey.rb +0 -14
  59. data/lib/scorpion/prey/module_prey.rb +0 -14
  60. data/lib/scorpion/prey.rb +0 -94
  61. data/spec/internal/db/combustion_test.sqlite +0 -0
  62. data/spec/lib/scorpion/hunting_map_spec.rb +0 -126
  63. data/spec/lib/scorpion/instance_spec.rb +0 -5
  64. data/spec/lib/scorpion/king_spec.rb +0 -198
  65. data/spec/lib/scorpion/prey/builder_prey_spec.rb +0 -42
  66. data/spec/lib/scorpion/prey/module_prey_spec.rb +0 -16
  67. data/spec/lib/scorpion/prey_spec.rb +0 -76
@@ -1,5 +1,9 @@
1
1
  module Scorpion
2
2
  module Rails
3
+ require 'scorpion/rails/nest'
4
+ require 'scorpion/rails/job'
5
+ require 'scorpion/rails/active_record'
3
6
  require 'scorpion/rails/controller'
7
+ require 'scorpion/rails/railtie'
4
8
  end
5
9
  end
@@ -0,0 +1,25 @@
1
+ module Scorpion
2
+ module Rspec
3
+ module Helper
4
+
5
+ def self.included( base )
6
+ base.let( :scorpion ){ Scorpion::Rspec.scorpion_nest.conceive }
7
+ base.send :extend, Scorpion::Rspec::Helper::Methods
8
+
9
+ super
10
+ end
11
+
12
+
13
+ module Methods
14
+
15
+ def scorpion( &block )
16
+ before( :each ) do
17
+ scorpion.prepare &block
18
+ end
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,17 @@
1
+ require 'scorpion'
2
+
3
+ module Scorpion
4
+ module Rspec
5
+ require 'scorpion/rspec/helper'
6
+
7
+ def self.scorpion_nest
8
+ @scorpion ||= Scorpion::Nest.new
9
+ end
10
+
11
+ # Prepare a root scorpion for testing.
12
+ def self.prepare( &block )
13
+ scorpion_nest.prepare &block
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,69 @@
1
+ module Scorpion
2
+ # Utility methods for propagating a Scorpion to returned objects.
3
+ module Stinger
4
+ @wrappers ||= {}
5
+
6
+ def self.wrap( instance, stinger )
7
+ return instance unless instance
8
+
9
+ klass = @wrappers[instance.class] ||=
10
+ Class.new( instance.class ) do
11
+ def initialize( instance, stinger )
12
+ @__instance__ = instance
13
+ @__stinger__ = stinger
14
+ end
15
+
16
+ def respond_to?( *args )
17
+ @__instance__.respond_to?( *args )
18
+ end
19
+
20
+ private
21
+ def method_missing( *args, &block )
22
+ binding.pry
23
+ @__stinger__.sting! @__instance__.__send__( *args, &block )
24
+ end
25
+ end
26
+
27
+ klass.new instance, stinger
28
+ end
29
+
30
+ # Sting an object so that it will be injected with the scorpion and use it
31
+ # to resolve all dependencies.
32
+ # @param [#scorpion] object to sting.
33
+ # @return [object] the object that was stung.
34
+ def sting!( object )
35
+ return object unless scorpion
36
+
37
+ if object
38
+ assign_scorpion object
39
+ assign_scorpion_to_enumerable object
40
+ end
41
+
42
+ object
43
+ end
44
+
45
+ private
46
+
47
+ def assign_scorpion( object )
48
+ return unless object.respond_to?( :scorpion=, true )
49
+
50
+ # Only set scorpion if it hasn't been set yet.
51
+ current_scorpion = object.send :scorpion
52
+ if current_scorpion
53
+ scorpion.logger.warn I18n.translate :mixed_scorpions, scope: [:scorpion,:warnings,:messages] if current_scorpion != scorpion
54
+ else
55
+ object.send :scorpion=, scorpion
56
+ end
57
+ end
58
+
59
+ def assign_scorpion_to_enumerable( objects )
60
+ return unless objects.respond_to? :each
61
+
62
+ # Don't eager load relations that haven't been loaded yet.
63
+ return if objects.respond_to?( :loaded? ) && ! objects.loaded?
64
+
65
+ objects.each{ |v| sting! v }
66
+ end
67
+
68
+ end
69
+ end
@@ -1,5 +1,5 @@
1
1
  module Scorpion
2
- VERSION_NUMBER = "0.3.1"
2
+ VERSION_NUMBER = "0.4.0"
3
3
  VERSION_SUFFIX = ""
4
4
  VERSION = "#{VERSION_NUMBER}#{VERSION_SUFFIX}"
5
5
  end
data/lib/scorpion.rb CHANGED
@@ -3,77 +3,65 @@ require 'i18n'
3
3
  I18n.load_path += Dir[ File.expand_path( '../scorpion/locale/*.yml', __FILE__ ) ]
4
4
 
5
5
  module Scorpion
6
+
6
7
  require 'scorpion/version'
7
8
  require 'scorpion/error'
8
- require 'scorpion/king'
9
+ require 'scorpion/object'
10
+ require 'scorpion/object_constructor'
9
11
  require 'scorpion/attribute_set'
10
12
  require 'scorpion/hunter'
11
- require 'scorpion/hunting_map'
12
- require 'scorpion/prey'
13
+ require 'scorpion/dependency_map'
14
+ require 'scorpion/hunt'
15
+ require 'scorpion/dependency'
13
16
  require 'scorpion/nest'
17
+ require 'scorpion/stinger'
14
18
  require 'scorpion/rails'
15
19
 
16
- # @return [Scorpion] main scorpion for the app.
17
- def self.instance
18
- @instance
19
- end
20
- @instance = Scorpion::Hunter.new
21
-
22
- # Prepare the {#instance} for hunting.
23
- def self.prepare( &block )
24
- instance.prepare &block
25
- end
26
-
27
20
  # Hunts for an object that satisfies the requested `contract` and `traits`.
28
- # @param [Class,Module,Symbol] contract describing the desired behavior of the prey.
29
- # @param [Array<Symbol>] traits required of the prey
30
- # @return [Object] an object that matches the requirements defined in `attribute`.
21
+ # @param [Class,Module,Symbol] contract describing the desired behavior of the dependency.
22
+ # @param [Array<Symbol>] traits required of the dependency
23
+ # @return [Object] an object that satisfies the contract and traits.
31
24
  # @raise [UnsuccessfulHunt] if a matching object cannot be found.
32
- def hunt_by_traits( contract, traits, *args, &block )
33
- fail "Not implemented"
25
+ def fetch_by_traits( contract, traits, *args, &block )
26
+ hunt = Hunt.new( self, contract, traits, *args, &block )
27
+ execute hunt
34
28
  end
35
- alias_method :fetch_by_traits, :hunt_by_traits
36
29
 
37
30
  # Hunts for an object that satisfies the requested `contract` regardless of
38
31
  # traits.
39
- # @see #hunt_by_traits
40
- def hunt( contract, *args, &block )
41
- hunt_by_traits( contract, nil, *args, &block )
42
- end
43
- alias_method :fetch, :hunt
44
-
45
- # Populate given `king` with its expected attributes.
46
- # @param [Scorpion::King] king to be fed.
47
- # @return [Scorpion::King] the populated king.
48
- def feed( king )
49
- king.injected_attributes.each do |attr|
50
- next if king.send "#{ attr.name }?"
51
-
52
- king.send :feed, attr, hunt_by_traits( attr.contract, attr.traits )
53
- end
32
+ # @see #fetch_by_traits
33
+ def fetch( contract, *args, &block )
34
+ fetch_by_traits( contract, nil, *args, &block )
54
35
  end
55
36
 
56
- # Creates a new king and feeds it it's dependencies.
57
- # @param [Class] king_class a class that includes {Scorpion::King}.
37
+ # Creates a new object and feeds it it's dependencies.
38
+ # @param [Class] object_class a class that includes {Scorpion::Object}.
58
39
  # @param [Array<Object>] args to pass to the constructor.
59
40
  # @param [#call] block to pass to the constructor.
60
- # @return [Scorpion::King] the spawned king.
61
- def spawn( king_class, *args, &block )
62
- if king_class < Scorpion::King
63
- king_class.spawn self, *args, &block
41
+ # @return [Scorpion::Object] the spawned object.
42
+ def spawn( hunt, object_class, *args, &block )
43
+ if object_class < Scorpion::Object
44
+ object_class.spawn hunt, *args, &block
64
45
  else
65
- king_class.new *args, &block
46
+ object_class.new *args, &block
66
47
  end
67
48
  end
68
49
 
50
+ # Execute the `hunt` returning the desired dependency.
51
+ # @param [Hunt] hunt to execute.
52
+ # @return [Object] an object that satisfies the hunt contract and traits.
53
+ def execute( hunt )
54
+ fail "Not implemented"
55
+ end
56
+
69
57
  # Creates a new {Scorpion} copying the current configuration any any currently
70
- # captured prey.
58
+ # captured dependency.
71
59
  # @return [Scorpion] the replicated scorpion.
72
60
  def replicate( &block )
73
61
  fail "Not implemented"
74
62
  end
75
63
 
76
- # Free up any captured prey and release any long-held resources.
64
+ # Free up any captured dependency and release any long-held resources.
77
65
  def destroy
78
66
  end
79
67
 
@@ -82,6 +70,65 @@ module Scorpion
82
70
  Scorpion::Nest.new( self )
83
71
  end
84
72
 
73
+ # @!attribute
74
+ # @return [Logger] logger for the scorpion to use.
75
+ def logger
76
+ @logger || Scorpion.logger
77
+ end
78
+ def logger=( value )
79
+ @logger = value
80
+ end
81
+
82
+ # ============================================================================
83
+ # @!group Convenience Methods
84
+ #
85
+ # Module methods to make it easier to work with scorpion configurations. These
86
+ # _should not_ be used by library level classes. Instead only application
87
+ # level code (controllers, scripts, etc.) should explicitly access these
88
+ # methods.
89
+
90
+ # @return [Scorpion] main scorpion for the app.
91
+ def self.instance
92
+ @instance
93
+ end
94
+ @instance = Scorpion::Hunter.new
95
+
96
+ # Prepare the {#instance} for hunting.
97
+ # @param [Boolean] reset true to free all existing resource and initialize a
98
+ # new scorpion.
99
+ def self.prepare( reset = false, &block )
100
+ if reset
101
+ @instance.destroy
102
+ @instance = Scorpion::Hunter.new
103
+ end
104
+ instance.prepare &block
105
+ end
106
+
107
+ # Hunt for dependency from the primary Scorpion {#instance}.
108
+ # @see #fetch
109
+ def self.fetch( *args, &block )
110
+ instance.fetch *args, &block
111
+ end
112
+
113
+ # Hunt for dependency from the primary Scorpion {#instance}.
114
+ # @see #fetch_by_traits
115
+ def self.fetch_by_traits( *args, &block )
116
+ instance.fetch_by_traits *args, &block
117
+ end
118
+
119
+ # @!attribute logger
120
+ # @return [Logger] logger for the Scorpion framework to use.
121
+ def self.logger
122
+ @logger ||= defined?( ::Rails ) ? ::Rails.logger : Logger.new( STDOUT )
123
+ end
124
+ def self.logger=( value )
125
+ @logger = value
126
+ end
127
+
128
+ #
129
+ # @!endgroup Convenience Methods
130
+
131
+
85
132
  private
86
133
 
87
134
  # Used by concrete scorpions to notify the caller that the hunt was
data/scorpion.gemspec CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_dependency 'rails', '~> 4.0'
22
- spec.required_ruby_version = '>= 1.9.2'
22
+ spec.required_ruby_version = '>= 2.0.0'
23
23
 
24
24
  spec.add_development_dependency "bundler", "~> 1.6"
25
25
  spec.add_development_dependency "rake", '~> 10'
@@ -0,0 +1,17 @@
1
+ class Author < ActiveRecord::Base
2
+
3
+ # @!attribute name
4
+ # @return [String]
5
+
6
+
7
+ # @!attribute
8
+ # @return [ActiveRecord::Relation<Todo>]
9
+ has_many :todos, inverse_of: :author
10
+
11
+ scope :named, ->( name ) { where( name: name ) } do
12
+ def alphabetical
13
+ order( :name )
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1,14 @@
1
+ class Todo < ActiveRecord::Base
2
+
3
+ # @!attribute name
4
+ # @return [String]
5
+
6
+
7
+ # @!attribute
8
+ # @return [Author] name
9
+ belongs_to :author, inverse_of: :todos
10
+
11
+
12
+
13
+
14
+ end
@@ -1,3 +1,14 @@
1
1
  ActiveRecord::Schema.define do
2
- #
2
+ #ActiveRecord::Schema.define do
3
+ create_table( :todos, :force => true ) do |t|
4
+ t.string :name
5
+ t.references :author
6
+ t.timestamps null: false
7
+ end
8
+
9
+ create_table( :authors, :force => true ) do |t|
10
+ t.string :name
11
+ t.timestamps null: false
12
+ end
13
+
3
14
  end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Scorpion::Dependency::ArgumentDependency do
4
+ let( :dependency ) { Scorpion::Dependency::ArgumentDependency.new( arg ) }
5
+ let( :arg ) { "Hello" }
6
+
7
+ it "matches the same type" do
8
+ expect( dependency.satisfies?( String ) ).to be_truthy
9
+ end
10
+
11
+ it "doesn't match different types" do
12
+ expect( dependency.satisfies?( Regexp ) ).to be_falsy
13
+ end
14
+
15
+ it "doesn't match traits" do
16
+ expect( dependency.satisfies?( String, :password ) ).to be_falsy
17
+ end
18
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ module Test
4
+ module BuilderDependency
5
+ class ClassDelegate
6
+ def call( hunt, *args, &block )
7
+ Test
8
+ end
9
+ end
10
+
11
+ module ModDelegate
12
+ module_function
13
+
14
+ def call( hunt, *args, &block )
15
+ Test
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ describe Scorpion::Dependency::BuilderDependency do
22
+ let( :scorpion ){ double }
23
+ let( :hunt ){ Scorpion::Hunt.new( scorpion, String, nil ) }
24
+
25
+ it "supports class hunting delegates" do
26
+ dependency = Scorpion::Dependency::BuilderDependency.new( String, nil, Test::BuilderDependency::ClassDelegate.new )
27
+ expect( dependency.fetch( hunt ) ).to be Test
28
+ end
29
+
30
+ it "supports module hunting delegates" do
31
+ dependency = Scorpion::Dependency::BuilderDependency.new( String, nil, Test::BuilderDependency::ModDelegate )
32
+ expect( dependency.fetch( hunt ) ).to be Test
33
+ end
34
+
35
+ it "supports block hunting delegates" do
36
+ dependency = Scorpion::Dependency::BuilderDependency.new( String, nil ) do
37
+ Test
38
+ end
39
+ expect( dependency.fetch( hunt ) ).to be Test
40
+ end
41
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ module Test
4
+ module ModuleDependency
5
+ module Example; end
6
+ end
7
+ end
8
+ describe Scorpion::Dependency::ModuleDependency do
9
+ let( :scorpion ){ double }
10
+ let( :dependency ) { Scorpion::Dependency::ModuleDependency.new( Test::ModuleDependency::Example ) }
11
+
12
+ it "returns the module itself" do
13
+ expect( dependency.fetch( scorpion ) ).to be Test::ModuleDependency::Example
14
+ end
15
+
16
+ end
@@ -0,0 +1,108 @@
1
+ require 'spec_helper'
2
+
3
+ module Test
4
+ module DependencyMap
5
+ class Weapon; end
6
+ class Armor; end
7
+ end
8
+ end
9
+
10
+ describe Scorpion::DependencyMap do
11
+ let( :scorpion ){ double Scorpion }
12
+ let( :map ){ Scorpion::DependencyMap.new scorpion }
13
+
14
+ describe "#chart" do
15
+ it "yields itself when arg expected" do
16
+ map.chart do |itself|
17
+ expect( map ).to be itself
18
+ end
19
+ end
20
+ end
21
+
22
+ describe "#find" do
23
+ it "returns a match" do
24
+ map.chart do
25
+ hunt_for Test::DependencyMap::Weapon
26
+ hunt_for Test::DependencyMap::Armor
27
+ end
28
+
29
+ expect( map.find( Test::DependencyMap::Armor ).contract ).to eq Test::DependencyMap::Armor
30
+ end
31
+
32
+ it "returns nil when no match" do
33
+ map.chart do
34
+ hunt_for Test::DependencyMap::Weapon
35
+ end
36
+
37
+ expect( map.find( Test::DependencyMap::Armor ) ).to be_nil
38
+ end
39
+
40
+ it "returns nil when no match" do
41
+ map.chart do
42
+ hunt_for Test::DependencyMap::Weapon
43
+ end
44
+
45
+ expect( map.find( Test::DependencyMap::Armor ) ).to be_nil
46
+ end
47
+
48
+
49
+ context "multiple possible matches" do
50
+ before( :each ) do
51
+ map.chart do
52
+ hunt_for Test::DependencyMap::Weapon, [ :sharp, :one_handed ]
53
+ hunt_for Test::DependencyMap::Weapon, [ :blunt, :one_handed ]
54
+ end
55
+ end
56
+
57
+ it "returns the last dependency that matches one trait" do
58
+ expect( map.find( Test::DependencyMap::Weapon, :one_handed ).traits ).to include :blunt
59
+ end
60
+
61
+ it "returns the last dependency that matches all of the traits" do
62
+ expect( map.find( Test::DependencyMap::Weapon, [ :one_handed, :blunt ] ).traits ).to include :blunt
63
+ end
64
+
65
+ it "returns the last dependency that matches a unique trait" do
66
+ expect( map.find( Test::DependencyMap::Weapon, :blunt ).traits ).to include :blunt
67
+ end
68
+ end
69
+ end
70
+
71
+ describe "#hunt_for" do
72
+ it "adds a Dependency" do
73
+ expect( map ).to be_empty
74
+
75
+ map.chart do
76
+ hunt_for Test::DependencyMap::Weapon
77
+ end
78
+
79
+ expect( map.first ).to be_a Scorpion::Dependency
80
+ end
81
+ end
82
+
83
+ describe "#replicate_from" do
84
+ it "does not dup shared dependency" do
85
+ map.chart do
86
+ share do
87
+ capture Test::DependencyMap::Weapon
88
+ end
89
+ end
90
+
91
+ replica = Scorpion::DependencyMap.new scorpion
92
+ replica.replicate_from( map )
93
+
94
+ expect( replica ).to be_empty
95
+ end
96
+
97
+ it "dups captured dependency" do
98
+ map.chart do
99
+ capture Test::DependencyMap::Weapon
100
+ end
101
+
102
+ replica = Scorpion::DependencyMap.new scorpion
103
+ replica.replicate_from( map )
104
+
105
+ expect( replica ).not_to be_empty
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,131 @@
1
+ require 'spec_helper'
2
+
3
+ module Test
4
+ module Dependency
5
+ module Mod; end
6
+ class Base; end
7
+ class Derived < Base
8
+ include Mod
9
+ end
10
+
11
+ class Footwear
12
+ def self.create( scorpion, *args, &block )
13
+ yield
14
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+
20
+ describe Scorpion::Dependency do
21
+ context "inheritance" do
22
+ let( :dependency ){ Scorpion::Dependency.new( Test::Dependency::Derived ) }
23
+
24
+ it "matches more derived class when looking for base class" do
25
+ expect( dependency.satisfies? Test::Dependency::Base ).to be_truthy
26
+ end
27
+
28
+ it "matches same class when looking for base class" do
29
+ expect( dependency.satisfies? Test::Dependency::Derived ).to be_truthy
30
+ end
31
+
32
+ it "does not inherit symbols" do
33
+ expect( Scorpion::Dependency.new( :a ).satisfies? :b ).to be_falsy
34
+ end
35
+
36
+ it "can satisfy a module with a class" do
37
+ expect( dependency.satisfies? Test::Dependency::Mod ).to be_truthy
38
+ end
39
+
40
+ end
41
+
42
+ describe "traits" do
43
+
44
+ context "symbolic" do
45
+ let( :dependency ){ Scorpion::Dependency.new Test::Dependency::Base, :apples }
46
+ it "satisfies matched traits" do
47
+ expect( dependency.satisfies? Test::Dependency::Base, :apples ).to be_truthy
48
+ end
49
+
50
+ it "doesn't satisfy mis-matched traits" do
51
+ expect( dependency.satisfies? Test::Dependency::Base, :oranges ).to be_falsy
52
+ end
53
+ end
54
+
55
+ context "module" do
56
+ let( :dependency ){ Scorpion::Dependency.new Test::Dependency::Derived }
57
+
58
+ it "satisfies module traits" do
59
+ expect( dependency.satisfies? Test::Dependency::Base, Test::Dependency::Derived ).to be_truthy
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+ it "can satisfy symbol contracts" do
66
+ expect( Scorpion::Dependency.new( :symbol ).satisfies? :symbol ).to be_truthy
67
+ end
68
+
69
+ it "satisfies ignores tail hash traits" do
70
+ expect( Scorpion::Dependency.new( Test::Dependency::Base ).satisfies?( Test::Dependency::Base, ) )
71
+ end
72
+
73
+ describe "equality" do
74
+ let( :dependency ) { Scorpion::Dependency.new( Test::Dependency::Derived ) }
75
+ let( :same ) { Scorpion::Dependency.new( Test::Dependency::Derived ) }
76
+ let( :different ) { Scorpion::Dependency.new( Test::Dependency::Base ) }
77
+
78
+ specify{ expect( dependency ).to eq same }
79
+ specify{ expect( dependency ).not_to eq different }
80
+ specify{ expect( dependency.hash ).to eq same.hash }
81
+ end
82
+
83
+ describe ".define" do
84
+ let( :scorpion ){ double Scorpion }
85
+ let( :hunt ) { Scorpion::Hunt.new scorpion, String, nil }
86
+
87
+ it "is a ClassDependency for class hunts" do
88
+ dependency = Scorpion::Dependency.define String
89
+ expect( dependency ).to be_a Scorpion::Dependency::ClassDependency
90
+ end
91
+
92
+ it "is a ModuleDependency for module hunts" do
93
+ dependency = Scorpion::Dependency.define Test::Dependency::Mod
94
+ expect( dependency ).to be_a Scorpion::Dependency::ModuleDependency
95
+ end
96
+
97
+ it "is a BuilderDependency for return: instances" do
98
+ dependency = Scorpion::Dependency.define String, return: "AWESEOME"
99
+
100
+ expect( dependency ).to be_a Scorpion::Dependency::BuilderDependency
101
+ expect( dependency.fetch hunt ).to eq "AWESEOME"
102
+ end
103
+
104
+ it "is a BuilderDependency for block hunts" do
105
+ dependency = Scorpion::Dependency.define String do
106
+ "YASS"
107
+ end
108
+
109
+ expect( dependency ).to be_a Scorpion::Dependency::BuilderDependency
110
+ end
111
+
112
+ it "is a BuilderDependency for with: option" do
113
+ dependency = Scorpion::Dependency.define String, with: ->(scorpion,*args,&block){ "YASSS" }
114
+
115
+ expect( dependency ).to be_a Scorpion::Dependency::BuilderDependency
116
+ expect( dependency.fetch hunt ).to eq "YASSS"
117
+ end
118
+
119
+ it "is a BuilderDependency when hunted class implements #create" do
120
+ dependency = Scorpion::Dependency.define Test::Dependency::Footwear
121
+ hunt = Scorpion::Hunt.new scorpion, String, nil do
122
+ "Nike"
123
+ end
124
+
125
+ expect( dependency ).to be_a Scorpion::Dependency::BuilderDependency
126
+ expect( dependency.fetch( hunt ) ).to eq "Nike"
127
+ end
128
+
129
+ end
130
+
131
+ end