scorpion-ioc 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +0 -1
  3. data/Gemfile +0 -1
  4. data/README.md +4 -1
  5. data/lib/scorpion.rb +38 -11
  6. data/lib/scorpion/attribute.rb +4 -1
  7. data/lib/scorpion/chain_hunter.rb +58 -0
  8. data/lib/scorpion/dependency.rb +7 -3
  9. data/lib/scorpion/dependency/builder_dependency.rb +5 -1
  10. data/lib/scorpion/dependency/class_dependency.rb +10 -7
  11. data/lib/scorpion/dependency_map.rb +11 -2
  12. data/lib/scorpion/error.rb +6 -0
  13. data/lib/scorpion/hunt.rb +32 -21
  14. data/lib/scorpion/hunter.rb +15 -2
  15. data/lib/scorpion/locale/en.yml +1 -0
  16. data/lib/scorpion/method.rb +24 -0
  17. data/lib/scorpion/nest.rb +5 -0
  18. data/lib/scorpion/object.rb +30 -23
  19. data/lib/scorpion/object_constructor.rb +23 -14
  20. data/lib/scorpion/rails.rb +1 -0
  21. data/lib/scorpion/rails/active_record/association.rb +5 -6
  22. data/lib/scorpion/rails/active_record/model.rb +4 -5
  23. data/lib/scorpion/rails/active_record/relation.rb +2 -4
  24. data/lib/scorpion/rails/controller.rb +31 -2
  25. data/lib/scorpion/rails/job.rb +12 -0
  26. data/lib/scorpion/rails/mailer.rb +42 -0
  27. data/lib/scorpion/rails/nest.rb +23 -20
  28. data/lib/scorpion/rails/railtie.rb +1 -0
  29. data/lib/scorpion/rspec/helper.rb +47 -0
  30. data/lib/scorpion/stinger.rb +0 -1
  31. data/lib/scorpion/version.rb +1 -1
  32. data/scorpion.gemspec +1 -0
  33. data/spec/lib/scorpion/attribute_spec.rb +10 -0
  34. data/spec/lib/scorpion/hunt_spec.rb +9 -3
  35. data/spec/lib/scorpion/hunter_spec.rb +13 -2
  36. data/spec/lib/scorpion/object_constructor_spec.rb +63 -6
  37. data/spec/lib/scorpion/object_spec.rb +9 -5
  38. data/spec/lib/scorpion/rails/controller_spec.rb +33 -1
  39. data/spec/lib/scorpion/rspec/helper_spec.rb +10 -0
  40. data/spec/spec_helper.rb +5 -0
  41. metadata +20 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d9f9e729630d83f9132e46118f0a74a9bcc9946f
4
- data.tar.gz: 40a34e024069aaf348ce03b37065d83098920e40
3
+ metadata.gz: 93ebd370c4020bf58ad8fb775368a16a8949ebca
4
+ data.tar.gz: 5d3d644a4688b8f310f065e8d3431574c7fe142c
5
5
  SHA512:
6
- metadata.gz: 32a04845861b9ea89cd5dcd19c667a0a339a424513c7b610f9909c3995e37d45707f837f3b10c8ce2d844ef12698268ffaa50c2afde083f54cd3161c4877d792
7
- data.tar.gz: 435e3fbc679d5ad65fc7f5d2cce00adb802b5c73b91d1995f15c9012f6b0a8a99cee97c70cf7907b391d71fe831b8f8b38958249ef89ced063a9b0654a5f2f2e
6
+ metadata.gz: 2fb8f746aaf8ec843dc91ce3ea468c89d04c8cf1f442aebe65facb4a73a3a71c25f2f2204f3438f389a0c3323781b9565f32f5ef854936774047ffd8a8625847
7
+ data.tar.gz: 49fca90cccdcba2f232709c08a1f703371fbf293c53ee0fd5652eae969cf6a653f3d12a2f347b387b93bdd2070911cded579b867c6699b78c96adfa092160395
data/.rspec CHANGED
@@ -1,3 +1,2 @@
1
1
  --color
2
2
  --format Fuubar
3
-
data/Gemfile CHANGED
@@ -15,7 +15,6 @@ group :test do
15
15
  gem 'simplecov', github: "colszowka/simplecov"
16
16
  gem 'ruby_gntp', '~> 0.3.4'
17
17
  gem 'awesome_print'
18
- gem 'sqlite3'
19
18
 
20
19
  gem "codeclimate-test-reporter", group: :test, require: nil
21
20
  end
data/README.md CHANGED
@@ -8,6 +8,8 @@
8
8
 
9
9
  Add IoC to rails with minimal fuss and ceremony.
10
10
 
11
+ (Also check out [shog](http://github.com/phallguy/shog) for better rails logs)
12
+
11
13
  <!-- MarkdownTOC depth=4 -->
12
14
 
13
15
  - [Dependency Injection](#dependency-injection)
@@ -519,6 +521,7 @@ class SessionsController < ActionController::Base
519
521
 
520
522
  def create
521
523
  user = User.with_scorpion( scorpion ).find params[:id]
524
+ user = scorpion( User ).find params[:id]
522
525
  sign_in if user.check_password( params[:password] )
523
526
  end
524
527
  end
@@ -538,4 +541,4 @@ end
538
541
 
539
542
  [The MIT License (MIT)](http://opensource.org/licenses/MIT)
540
543
 
541
- Copyright (c) 2015 Paul Alexander
544
+ Copyright (c) 2015 Paul Alexander
data/lib/scorpion.rb CHANGED
@@ -6,10 +6,12 @@ module Scorpion
6
6
 
7
7
  require 'scorpion/version'
8
8
  require 'scorpion/error'
9
+ require 'scorpion/method'
9
10
  require 'scorpion/object'
10
11
  require 'scorpion/object_constructor'
11
12
  require 'scorpion/attribute_set'
12
13
  require 'scorpion/hunter'
14
+ require 'scorpion/chain_hunter'
13
15
  require 'scorpion/dependency_map'
14
16
  require 'scorpion/hunt'
15
17
  require 'scorpion/dependency'
@@ -20,18 +22,19 @@ module Scorpion
20
22
  # Hunts for an object that satisfies the requested `contract` and `traits`.
21
23
  # @param [Class,Module,Symbol] contract describing the desired behavior of the dependency.
22
24
  # @param [Array<Symbol>] traits required of the dependency
25
+ # @param [Hash<Symbol,Object>] dependencies to inject into the object.
23
26
  # @return [Object] an object that satisfies the contract and traits.
24
27
  # @raise [UnsuccessfulHunt] if a matching object cannot be found.
25
- def fetch_by_traits( contract, traits, *args, &block )
26
- hunt = Hunt.new( self, contract, traits, *args, &block )
28
+ def fetch_by_traits( contract, traits, *arguments, **dependencies, &block )
29
+ hunt = Hunt.new( self, contract, traits, *arguments, **dependencies, &block )
27
30
  execute hunt
28
31
  end
29
32
 
30
33
  # Hunts for an object that satisfies the requested `contract` regardless of
31
34
  # traits.
32
35
  # @see #fetch_by_traits
33
- def fetch( contract, *args, &block )
34
- fetch_by_traits( contract, nil, *args, &block )
36
+ def fetch( contract, *arguments, **dependencies, &block )
37
+ fetch_by_traits( contract, nil, *arguments, **dependencies, &block )
35
38
  end
36
39
 
37
40
  # Creates a new object and feeds it it's dependencies.
@@ -39,14 +42,33 @@ module Scorpion
39
42
  # @param [Array<Object>] args to pass to the constructor.
40
43
  # @param [#call] block to pass to the constructor.
41
44
  # @return [Scorpion::Object] the spawned object.
42
- def spawn( hunt, object_class, *args, &block )
45
+ def spawn( hunt, object_class, *arguments, **dependencies, &block )
43
46
  if object_class < Scorpion::Object
44
- object_class.spawn hunt, *args, &block
47
+ object_class.spawn hunt, *arguments, **dependencies, &block
45
48
  else
46
- object_class.new *args, &block
49
+ object_class.new *arguments, &block
47
50
  end
48
51
  end
49
52
 
53
+ # Inject the {#target} with all non-lazy dependencies.
54
+ # @param [Scorpion::Object] target to inject.
55
+ # @return [target]
56
+ def inject( target )
57
+ hunt = Scorpion::Hunt.new self, nil, nil
58
+ hunt.inject target
59
+
60
+ target
61
+ end
62
+
63
+ # Explicitly spawn an instance of {#object_class} and inject it's dependencies.
64
+ # @param [Array<Object>] args to pass to the constructor.
65
+ # @param [#call] block to pass to the constructor.
66
+ # @return [Scorpion::Object] the spawned object.
67
+ def new( object_class, *arguments, **dependencies, &block )
68
+ hunt = Hunt.new( self, object_class, nil, *arguments, **dependencies, &block )
69
+ Scorpion::Dependency::ClassDependency.new( object_class ).fetch( hunt )
70
+ end
71
+
50
72
  # Execute the `hunt` returning the desired dependency.
51
73
  # @param [Hunt] hunt to execute.
52
74
  # @return [Object] an object that satisfies the hunt contract and traits.
@@ -63,6 +85,11 @@ module Scorpion
63
85
 
64
86
  # Free up any captured dependency and release any long-held resources.
65
87
  def destroy
88
+ reset
89
+ end
90
+
91
+ # Reset the hunting map and clear all dependencies.
92
+ def reset
66
93
  end
67
94
 
68
95
  # @return [Scorpion::Nest] a nest that uses this scorpion as the mother.
@@ -106,14 +133,14 @@ module Scorpion
106
133
 
107
134
  # Hunt for dependency from the primary Scorpion {#instance}.
108
135
  # @see #fetch
109
- def self.fetch( *args, &block )
110
- instance.fetch *args, &block
136
+ def self.fetch( dependencies, &block )
137
+ instance.fetch dependencies, &block
111
138
  end
112
139
 
113
140
  # Hunt for dependency from the primary Scorpion {#instance}.
114
141
  # @see #fetch_by_traits
115
- def self.fetch_by_traits( *args, &block )
116
- instance.fetch_by_traits *args, &block
142
+ def self.fetch_by_traits( dependencies, &block )
143
+ instance.fetch_by_traits dependencies, &block
117
144
  end
118
145
 
119
146
  # @!attribute logger
@@ -13,7 +13,10 @@ module Scorpion
13
13
  # @!attribute
14
14
  # @return [Class,Module,Symbol] contract that describes the desired behavior
15
15
  # of the injected object.
16
- attr_reader :contract
16
+ def contract
17
+ @contract = @contract.constantize if @contract.is_a? String
18
+ @contract
19
+ end
17
20
 
18
21
  # @!attribute
19
22
  # @return [Array<Symbol>] traits that must match on instances of the {#contract}
@@ -0,0 +1,58 @@
1
+ module Scorpion
2
+
3
+ # Chains hunting calls to one or more managed scorpions.
4
+ class ChainHunter
5
+ include Scorpion
6
+
7
+ # ============================================================================
8
+ # @!group Attributes
9
+ #
10
+
11
+ # @!attribute
12
+ # @return [Array<Scorpion>] scorpions to chain hunting calls to.
13
+ attr_reader :scorpions
14
+
15
+ #
16
+ # @!endgroup Attributes
17
+
18
+
19
+ def initialize( *scorpions )
20
+ @scorpions = scorpions
21
+ end
22
+
23
+ # Prepare the scorpion for hunting.
24
+ # @see DependencyMap#chart
25
+ def prepare( &block )
26
+ if top = scorpions.first
27
+ top.prepare &block
28
+ end
29
+ end
30
+
31
+ # @see Scorpion#replicate
32
+ def replicate
33
+ self.class.new scorpions
34
+ end
35
+
36
+ # @see Scorpion#hunt
37
+ def execute( hunt )
38
+ # Try explicitly defined dependencies first
39
+ scorpions.each do |hunter|
40
+ begin
41
+ return hunter.execute( hunt, true )
42
+ rescue UnsuccessfulHunt
43
+ end
44
+ end
45
+
46
+ # Then allow implicit
47
+ scorpions.each do |hunter|
48
+ begin
49
+ return hunter.execute( hunt )
50
+ rescue UnsuccessfulHunt
51
+ end
52
+ end
53
+
54
+ unsuccessful_hunt hunt.contract, hunt.traits
55
+ end
56
+
57
+ end
58
+ end
@@ -92,7 +92,7 @@ module Scorpion
92
92
 
93
93
  # Define dependency based on the desired contract and traits.
94
94
  # @return [Dependency] the defined dependency.
95
- def define( contract, traits = nil , &builder )
95
+ def define( contract, traits = nil, &builder )
96
96
  options, traits = extract_options!( traits )
97
97
 
98
98
  if options.key?( :return )
@@ -104,8 +104,12 @@ module Scorpion
104
104
  elsif block_given?
105
105
  Scorpion::Dependency::BuilderDependency.new( contract, traits, builder )
106
106
  elsif contract.respond_to?( :create )
107
- Scorpion::Dependency::BuilderDependency.new( contract, traits ) do |scorpion,*args,&block|
108
- contract.create scorpion, *args, &block
107
+ Scorpion::Dependency::BuilderDependency.new( contract, traits ) do |hunt,*args,**dependencies,&block|
108
+ if dependencies.present?
109
+ contract.create hunt, *args, **dependencies, &block
110
+ else
111
+ contract.create hunt, *args, &block
112
+ end
109
113
  end
110
114
  else
111
115
  dependency_class( contract ).new( contract, traits, &builder )
@@ -24,7 +24,11 @@ module Scorpion
24
24
 
25
25
  # @see Scorpion::Dependency#fetch
26
26
  def fetch( hunt )
27
- builder.call( hunt, *hunt.arguments, &hunt.block )
27
+ if hunt.dependencies.any?
28
+ builder.call( hunt, *hunt.arguments, **hunt.dependencies, &hunt.block )
29
+ else
30
+ builder.call( hunt, *hunt.arguments, &hunt.block )
31
+ end
28
32
  end
29
33
 
30
34
  end
@@ -6,20 +6,23 @@ module Scorpion
6
6
  class ClassDependency < Scorpion::Dependency
7
7
 
8
8
  def fetch( hunt )
9
- hunt.scorpion.spawn hunt, hunt.contract, *resolve_arguments( hunt ), &hunt.block
9
+ resolved = resolve_dependencies( hunt )
10
+ hunt.scorpion.spawn hunt, hunt.contract, *hunt.arguments, **resolved, &hunt.block
10
11
  end
11
12
 
12
13
  private
13
14
 
14
- def resolve_arguments( hunt )
15
- arguments = hunt.arguments
16
- return arguments unless arguments.blank? && hunt.contract < Scorpion::Object
15
+ def resolve_dependencies( hunt )
16
+ dependencies = hunt.dependencies
17
+ return dependencies unless hunt.contract.respond_to? :initializer_injections
17
18
 
18
- hunt.contract.initializer_injections.each_with_object([]) do |attr,args|
19
- args << hunt.fetch_by_traits( attr.contract, attr.traits )
20
- end
19
+ hunt.contract.initializer_injections.each_with_object(dependencies.dup) do |attr,deps|
20
+ next if attr.lazy?
21
21
 
22
+ deps[attr.name] ||= hunt.fetch_by_traits( attr.contract, attr.traits )
23
+ end
22
24
  end
25
+
23
26
  end
24
27
  end
25
28
  end
@@ -30,8 +30,7 @@ module Scorpion
30
30
 
31
31
  def initialize( scorpion )
32
32
  @scorpion = scorpion
33
- @dependency_set = @active_dependency_set = []
34
- @shared_dependency_set = []
33
+ reset
35
34
  end
36
35
 
37
36
  # Find {Dependency} that matches the requested `contract` and `traits`.
@@ -126,6 +125,16 @@ module Scorpion
126
125
  self
127
126
  end
128
127
 
128
+ # Remove all dependency mappings.
129
+ def reset
130
+
131
+ @dependency_set.each &:release if @dependency_set
132
+ @shared_dependency_set.each &:release if @shared_dependency_set
133
+
134
+ @dependency_set = @active_dependency_set = []
135
+ @shared_dependency_set = []
136
+ end
137
+
129
138
  private
130
139
 
131
140
  def define_dependency( contract, traits, &builder )
@@ -21,6 +21,12 @@ module Scorpion
21
21
  end
22
22
  end
23
23
 
24
+ class ArityMismatch < Error
25
+ def initialize( block, expected_count )
26
+ super translate( :arity_mismatch, expected: expected_count, actual: block.arity )
27
+ end
28
+ end
29
+
24
30
  class BuilderRequiredError < Error
25
31
  def initialize( message = nil )
26
32
  super ( message || translate( :builder_required ) )
data/lib/scorpion/hunt.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Scorpion
2
- # Captures state for a specific hunt so that constructor arguments can be
2
+ # Captures state for a specific hunt so that constructor dependencies can be
3
3
  # shared with child dependencies.
4
4
  #
5
5
  # @example
@@ -45,7 +45,7 @@ module Scorpion
45
45
  attr_reader :trip
46
46
  private :trip
47
47
 
48
- delegate [:contract, :traits, :arguments, :block] => :trip
48
+ delegate [:contract, :traits, :dependencies, :arguments, :block] => :trip
49
49
 
50
50
  # @!attribute contract
51
51
  # @return [Class,Module,Symbol] contract being hunted for.
@@ -53,8 +53,11 @@ module Scorpion
53
53
  # @!attribute traits
54
54
  # @return [Array<Symbol>] traits being hunted for.
55
55
 
56
+ # @!attribute [r] dependencies
57
+ # @return [Hash<Symbol,Dependency>] hash of dependencies to pass to initializer of {#contract} when found.
58
+
56
59
  # @!attribute [r] arguments
57
- # @return [Array<Dependency>] dependency to pass to initializer of contract when found.
60
+ # @return [Array<Object>] positional arguments to pass to the initializer of {#contract} when found.
58
61
 
59
62
  # @!attribute block
60
63
  # @return [#call] block to pass to constructor of contract when found.
@@ -62,22 +65,22 @@ module Scorpion
62
65
  #
63
66
  # @!endgroup Attributes
64
67
 
65
- def initialize( scorpion, contract, traits, *arguments, &block )
68
+ def initialize( scorpion, contract, traits, *arguments, **dependencies, &block )
66
69
  @scorpion = scorpion
67
70
  @trips = []
68
- @trip = Trip.new contract, traits, arguments, block
71
+ @trip = Trip.new contract, traits, arguments, dependencies, block
69
72
  end
70
73
 
71
74
  # Hunt for additional dependency to satisfy the main hunt's contract and traits.
72
75
  # @see Scorpion#hunt
73
- def fetch( contract, *arguments, &block )
74
- fetch_by_traits( contract, nil, *arguments, &block )
76
+ def fetch( contract, *arguments, **dependencies, &block )
77
+ fetch_by_traits( contract, nil, *arguments, **dependencies, &block )
75
78
  end
76
79
 
77
80
  # Hunt for additional dependency to satisfy the main hunt's contract and traits.
78
81
  # @see Scorpion#hunt
79
- def fetch_by_traits( contract, traits, *arguments, &block )
80
- push contract, traits, arguments, block
82
+ def fetch_by_traits( contract, traits, *arguments, **dependencies, &block )
83
+ push contract, traits, arguments, dependencies, block
81
84
  execute
82
85
  ensure
83
86
  pop
@@ -88,6 +91,7 @@ module Scorpion
88
91
  # @return [Scorpion::Object] the injected object.
89
92
  def inject( object )
90
93
  trip.object = object
94
+
91
95
  object.injected_attributes.each do |attr|
92
96
  next if object.send "#{ attr.name }?"
93
97
  next if attr.lazy?
@@ -95,14 +99,17 @@ module Scorpion
95
99
  object.send :inject, attr, fetch_by_traits( attr.contract, attr.traits )
96
100
  end
97
101
 
102
+ object.send :on_injected
103
+
98
104
  object
99
105
  end
100
106
 
101
107
  # Allow the hunt to spawn objects.
102
108
  # @see Scorpion#spawn
103
- def spawn( klass, *arguments, &block )
104
- scorpion.spawn( self, klass, *arguments, &block )
109
+ def spawn( klass, *arguments, **dependencies, &block )
110
+ scorpion.spawn( self, klass, *arguments, **dependencies, &block )
105
111
  end
112
+ alias_method :new, :spawn
106
113
 
107
114
  private
108
115
 
@@ -111,12 +118,12 @@ module Scorpion
111
118
  end
112
119
 
113
120
  def execute_from_trips
114
- return if arguments.any?
121
+ return if dependencies.any?
115
122
 
116
123
  trips.each do |trip|
117
124
  return trip.object if contract === trip.object
118
- trip.arguments.each do |arg|
119
- return arg if contract === arg
125
+ trip.dependencies.each do |key,value|
126
+ return value if contract === value
120
127
  end
121
128
  end
122
129
 
@@ -127,10 +134,10 @@ module Scorpion
127
134
  scorpion.execute self
128
135
  end
129
136
 
130
- def push( contract, traits, arguments, block )
137
+ def push( contract, traits, arguments, dependencies, block )
131
138
  trips.push trip
132
139
 
133
- @trip = Trip.new contract, traits, arguments, block
140
+ @trip = Trip.new contract, traits, arguments, dependencies, block
134
141
  end
135
142
 
136
143
  def pop
@@ -141,15 +148,19 @@ module Scorpion
141
148
  attr_reader :contract
142
149
  attr_reader :traits
143
150
  attr_reader :arguments
151
+ attr_reader :dependencies
144
152
  attr_reader :block
145
153
 
146
154
  attr_accessor :object
147
155
 
148
- def initialize( contract, traits, arguments, block )
149
- @contract = contract
150
- @traits = traits
151
- @arguments = arguments
152
- @block = block
156
+ def initialize( contract, traits, arguments, dependencies, block )
157
+ @contract = contract
158
+ @traits = traits
159
+ @arguments = arguments
160
+ @dependencies = dependencies
161
+ @block = block
162
+
163
+ @caller = caller
153
164
  end
154
165
  end
155
166