scorpion-ioc 0.4.0 → 0.5.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 (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
@@ -32,6 +32,15 @@ module Scorpion
32
32
  dependency_map.chart &block
33
33
  end
34
34
 
35
+ # Expose dependency injection definitions as top-level methods.
36
+ [:hunt_for,:capture,:share].each do |delegate|
37
+ define_method delegate do |*args,&block|
38
+ prepare do |hunter|
39
+ hunter.send delegate, *args, &block
40
+ end
41
+ end
42
+ end
43
+
35
44
  # @see Scorpion#replicate
36
45
  def replicate
37
46
  replica = self.class.new self
@@ -40,16 +49,20 @@ module Scorpion
40
49
  end
41
50
 
42
51
  # @see Scorpion#hunt
43
- def execute( hunt )
52
+ def execute( hunt, explicit_only = false )
44
53
  dependency = dependency_map.find( hunt.contract, hunt.traits )
45
54
  dependency ||= parent.dependency_map.find( hunt.contract, hunt.traits ) if parent
46
- dependency ||= Dependency.define( hunt.contract ) if hunt.traits.blank?
55
+ dependency ||= Dependency.define( hunt.contract ) if hunt.traits.blank? && !explicit_only
47
56
 
48
57
  unsuccessful_hunt( hunt.contract, hunt.traits ) unless dependency
49
58
 
50
59
  dependency.fetch hunt
51
60
  end
52
61
 
62
+ # @see Scorpion#reset
63
+ def reset
64
+ dependency_map.reset
65
+ end
53
66
 
54
67
  end
55
68
  end
@@ -4,6 +4,7 @@ en:
4
4
  messages:
5
5
  unsuccessful_hunt: "Couldn't find a %{contract} builder with traits '%{traits}'"
6
6
  builder_required: A custom builder must be provided to resolve this dependency
7
+ arity_mismatch: The block must accept %{expected} arguments, but actually expects %{actual}
7
8
  warnings:
8
9
  messages:
9
10
  mixed_scorpions: A scorpion has already been assigned. Mixing scorpions can result in unexpected results.
@@ -0,0 +1,24 @@
1
+ module Scorpion
2
+ # Adds a #scorpion method to an object.
3
+ module Method
4
+ # @overload scorpion
5
+ # @return [Scorpion] the object's scorpion used to hunt down dependencies.
6
+ # @overload scorpion( scope )
7
+ # Stings the given `scope` with the current scorpion.
8
+ # @param [#with_scorpion] scope an object that responds to #with_scorpion that
9
+ # receives the current scorpion.
10
+ # @return [scope] stung object.
11
+ def scorpion( scope = nil )
12
+ if scope
13
+ scope.with_scorpion( scorpion )
14
+ else
15
+ @scorpion
16
+ end
17
+ end
18
+
19
+ private def scorpion=( value )
20
+ @scorpion = value
21
+ end
22
+
23
+ end
24
+ end
data/lib/scorpion/nest.rb CHANGED
@@ -33,5 +33,10 @@ module Scorpion
33
33
  @mother = nil
34
34
  end
35
35
 
36
+ # Reset the hunting map and clear all dependencies.
37
+ def reset
38
+ mother.reset
39
+ end
40
+
36
41
  end
37
42
  end
@@ -9,10 +9,7 @@ module Scorpion
9
9
  # @!group Attributes
10
10
  #
11
11
 
12
- # @!attribute
13
- # @return [Scorpion] the scorpion used to hunt down dependencies.
14
- attr_accessor :scorpion
15
- private :scorpion=
12
+ include Scorpion::Method
16
13
 
17
14
  # @!attribute
18
15
  # @return [Scorpion::AttributeSet] the set of injected attributes and their
@@ -35,8 +32,8 @@ module Scorpion
35
32
  send "#{ attribute.name }=", dependency
36
33
  end
37
34
 
38
- # Crown the object as a object and prepare it to be fed.
39
- def self.crown( base )
35
+ # Infest the object with a scoprion and prepare it to be fed.
36
+ def self.infest( base )
40
37
  base.extend Scorpion::Object::ClassMethods
41
38
  if base.is_a? Class
42
39
  base.class_exec do
@@ -44,32 +41,38 @@ module Scorpion
44
41
  # Create a new instance of this class with all non-lazy dependencies
45
42
  # satisfied.
46
43
  # @param [Hunt] hunt that this instance will be used to satisfy.
47
- def self.spawn( hunt, *args, &block )
48
- new( *args, &block ).tap do |object|
49
- object.send :scorpion=, hunt.scorpion
50
-
51
- # Go hunt for dependencies that are not lazy and initialize the
52
- # references.
53
- hunt.inject object
54
- object.send :on_injected
55
- end
44
+ def self.spawn( hunt, *args, **dependencies, &block )
45
+ object =
46
+ if dependencies.any?
47
+ new( *args, **dependencies, &block )
48
+ else
49
+ new( *args, &block )
50
+ end
51
+
52
+
53
+ object.send :scorpion=, hunt.scorpion
54
+
55
+ # Go hunt for dependencies that are not lazy and initialize the
56
+ # references.
57
+ hunt.inject object
58
+ object
56
59
  end
57
60
 
58
61
  end
59
62
 
60
63
  base.subclasses.each do |sub|
61
- crown( sub ) unless sub < Scorpion::Object
64
+ infest( sub ) unless sub < Scorpion::Object
62
65
  end
63
66
  end
64
67
  end
65
68
 
66
69
  def self.included( base )
67
- crown( base )
70
+ infest( base )
68
71
  super
69
72
  end
70
73
 
71
74
  def self.prepended( base )
72
- crown( base )
75
+ infest( base )
73
76
  super
74
77
  end
75
78
 
@@ -117,14 +120,15 @@ module Scorpion
117
120
  # Define an initializer that accepts injections.
118
121
  # @param [Hash] arguments to accept in the initializer.
119
122
  # @yield to initialize itself.
120
- def initialize( arguments, &block )
123
+ def initialize( arguments = {}, &block )
121
124
  Scorpion::ObjectConstructor.new( self, arguments, &block ).define
122
125
  end
123
126
 
124
127
  # Tells a {Scorpion} what to inject into the class when it is constructed
125
128
  # @return [nil]
126
129
  # @see AttributeSet#define
127
- def depend_on( &block )
130
+ def depend_on( arguments = nil, &block )
131
+ Scorpion::ObjectConstructor.new( self, arguments ).define if arguments.present?
128
132
  injected_attributes.define &block
129
133
  build_injected_attributes
130
134
  end
@@ -155,8 +159,11 @@ module Scorpion
155
159
  # @return [Scorpion::AttributeSet] the set of injected attriutes.
156
160
  def initializer_injections
157
161
  @initializer_injections ||= begin
158
- attrs = AttributeSet.new
159
- attrs
162
+ if superclass.respond_to?( :initializer_injections )
163
+ superclass.initializer_injections
164
+ else
165
+ AttributeSet.new
166
+ end
160
167
  end
161
168
  end
162
169
 
@@ -174,7 +181,7 @@ module Scorpion
174
181
  def #{ attr.name }
175
182
  @#{ attr.name } ||= begin
176
183
  attr = injected_attributes[ :#{ attr.name } ]
177
- scorpion.fetch( attr.contract, attr.traits )
184
+ scorpion.fetch_by_traits( attr.contract, attr.traits )
178
185
  end
179
186
  end
180
187
 
@@ -9,10 +9,11 @@ module Scorpion
9
9
  end
10
10
 
11
11
  def define
12
- @signature = []
13
- @body = ""
12
+ @signature = []
13
+ @block_signature = []
14
+ @body = []
14
15
 
15
- build_signature
16
+ define_dependencies
16
17
  build_body
17
18
 
18
19
  add_initialize_block
@@ -20,35 +21,43 @@ module Scorpion
20
21
  end
21
22
 
22
23
  private
23
- attr_reader :base, :arguments, :block, :body, :signature
24
+ attr_reader :base, :arguments, :block, :body
24
25
 
25
- def build_signature
26
- arguments.each do |key,expectation|
27
- signature << key
26
+ def define_dependencies
27
+ # Override the inherited injections cause we're about to define a new
28
+ # initializer.
29
+ base.instance_variable_set :@initializer_injections, AttributeSet.new
28
30
 
31
+ arguments.each do |key,expectation|
29
32
  base.initializer_injections.define_attribute key, *Array( expectation )
30
33
  base.attr_dependency key, *Array( expectation )
31
34
  end
32
35
  end
33
36
 
34
37
  def build_body
35
- arguments.each do |key,expectation|
36
- body << "@#{ key } = #{ key };"
38
+ if arguments.present?
39
+ body << "injections = dependencies.slice( :#{ arguments.keys.join(', :') } )"
40
+ body << "inject_from( dependencies )"
37
41
  end
42
+ body << "super" if base.superclass < Scorpion::Object
38
43
  end
39
44
 
40
45
  def add_initialize_block
41
46
  if block
42
- body << "__initialize_with_block( &block )"
43
- base.send :define_method, :__initialize_with_block, &block
47
+ name = "__initialize_with_block_#{ base.name || base.object_id }"
48
+ body << "#{ name }( *args, **injections, &block )"
49
+ base.send :define_method, :"#{ name }", &block
44
50
  end
45
51
  end
46
52
 
47
53
  def assemble
54
+ source = %Q|def initialize( *args, **dependencies, &block )\n\t#{ body.join( "\n\t" ) }\nend|
55
+
56
+ # puts base.name
57
+ # puts source
58
+
48
59
  base.class_eval <<-RUBY, __FILE__, __LINE__ + 1
49
- def initialize( #{ signature.join( ', ' ) }, &block )
50
- #{ body }
51
- end
60
+ #{ source }
52
61
  RUBY
53
62
  end
54
63
  end
@@ -2,6 +2,7 @@ module Scorpion
2
2
  module Rails
3
3
  require 'scorpion/rails/nest'
4
4
  require 'scorpion/rails/job'
5
+ require 'scorpion/rails/mailer'
5
6
  require 'scorpion/rails/active_record'
6
7
  require 'scorpion/rails/controller'
7
8
  require 'scorpion/rails/railtie'
@@ -10,12 +10,11 @@ module Scorpion
10
10
  # @!group Attributes
11
11
  #
12
12
 
13
- # @!attribute
14
- # @return [Scorpion] the scorpion serving the association.
15
- attr_accessor :scorpion
16
- def scorpion
17
- @scorpion || owner.scorpion
18
- end
13
+ include Scorpion::Method
14
+
15
+ def scorpion( scope = nil )
16
+ super || owner.scorpion( scope )
17
+ end
19
18
 
20
19
  #
21
20
  # @!endgroup Attributes
@@ -9,12 +9,11 @@ module Scorpion
9
9
  def self.prepended( base )
10
10
  # Setup dependency injection
11
11
  base.send :include, Scorpion::Object
12
- base.send :extend, ClassMethods
13
- super
14
- end
12
+ base.singleton_class.class_exec do
13
+ delegate :with_scorpion, to: :all
14
+ end
15
15
 
16
- module ClassMethods
17
- delegate :with_scorpion, to: :all
16
+ super
18
17
  end
19
18
 
20
19
  def association( *args, &block )
@@ -11,9 +11,7 @@ module Scorpion
11
11
  # @!group Attributes
12
12
  #
13
13
 
14
- # @!attribute
15
- # @return [Scorpion] the scorpion serving the relation.
16
- attr_accessor :scorpion
14
+ include Scorpion::Method
17
15
 
18
16
  #
19
17
  # @!endgroup Attributes
@@ -27,7 +25,7 @@ module Scorpion
27
25
  # User.with_scorpion( scorpion ).where( ... )
28
26
  def with_scorpion( scorpion )
29
27
  spawn.tap do |other|
30
- other.scorpion = scorpion
28
+ other.send :scorpion=, scorpion
31
29
  end
32
30
  end
33
31
 
@@ -8,14 +8,41 @@ module Scorpion
8
8
 
9
9
  ENV_KEY = 'scorpion.instance'.freeze
10
10
 
11
- def scorpion
12
- env[ENV_KEY]
11
+
12
+ # Fetch an object from the controller's {#scorpion}.
13
+ # @see Scorpion#fetch
14
+ def fetch( *args, &block )
15
+ scorpion.fetch *args, &block
13
16
  end
17
+ private :fetch
18
+
19
+
20
+ # @overload scorpion
21
+ # @return [Scorpion] the current scorpion
22
+ # @overload scorpion( scope )
23
+ # Stings the given `scope` with the current scorpion.
24
+ # @param [ActiveRecord::Relation,#with_scorpion] an ActiveRecord relation,
25
+ # scope or model class.
26
+ # @return [ActiveRecord::Relation] scorpion scoped relation.
14
27
 
15
28
  def self.included( base )
16
29
  # Setup dependency injection
30
+ base.send :include, Scorpion::Object
17
31
  base.send :include, Scorpion::Rails::Nest
32
+
18
33
  base.around_filter :with_scorpion
34
+
35
+ base.class_eval do
36
+ # Defined here to override the #scorpion method provided by Scorpion::Object.
37
+ def scorpion( scope = nil )
38
+ if scope
39
+ super
40
+ else
41
+ ensure_scorpion( env[ENV_KEY] )
42
+ end
43
+ end
44
+ end
45
+
19
46
  super
20
47
  end
21
48
 
@@ -42,6 +69,8 @@ module Scorpion
42
69
  end
43
70
 
44
71
  def free_scorpion
72
+ scorpion.try( :destroy )
73
+ env.delete ENV_KEY
45
74
  end
46
75
 
47
76
  end
@@ -6,8 +6,10 @@ module Scorpion
6
6
  # Adds a scorpion nest to support injection into rails background worker jobs.
7
7
  module Job
8
8
 
9
+
9
10
  def self.included( base )
10
11
  # Setup dependency injection
12
+ base.send :include, Scorpion::Object
11
13
  base.send :include, Scorpion::Rails::Nest
12
14
  base.send :around_perform do |job, block|
13
15
  job.with_scorpion &block
@@ -25,6 +27,16 @@ module Scorpion
25
27
  end
26
28
  end
27
29
  end
30
+
31
+ attr_reader :scorpion
32
+ def assign_scorpion( scorpion )
33
+ @scorpion = scorpion
34
+ end
35
+
36
+ def free_scorpion
37
+ @scorpion.try( :destroy )
38
+ @scorpion = nil
39
+ end
28
40
  end
29
41
  end
30
42
  end
@@ -0,0 +1,42 @@
1
+ require 'scorpion/nest'
2
+
3
+ module Scorpion
4
+ module Rails
5
+
6
+ # Adds a scorpion nest to support injection into rails mailers.
7
+ module Mailer
8
+
9
+
10
+ def self.included( base )
11
+ # Setup dependency injection
12
+ base.send :include, Scorpion::Object
13
+ base.send :include, Scorpion::Rails::Nest
14
+ base.send :around_filter do |mailer, block|
15
+ mailer.with_scorpion &block
16
+ end
17
+
18
+ super
19
+ end
20
+
21
+ private
22
+
23
+ def prepare_scorpion( scorpion )
24
+ scorpion.prepare do |hunter|
25
+ hunter.hunt_for ActionMailer::Base do
26
+ self
27
+ end
28
+ end
29
+ end
30
+
31
+ attr_reader :scorpion
32
+ def assign_scorpion( scorpion )
33
+ @scorpion = scorpion
34
+ end
35
+
36
+ def free_scorpion
37
+ @scorpion.try( :destroy )
38
+ @scorpion = nil
39
+ end
40
+ end
41
+ end
42
+ end