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.
- checksums.yaml +4 -4
- data/.rspec +0 -1
- data/Gemfile +0 -1
- data/README.md +4 -1
- data/lib/scorpion.rb +38 -11
- data/lib/scorpion/attribute.rb +4 -1
- data/lib/scorpion/chain_hunter.rb +58 -0
- data/lib/scorpion/dependency.rb +7 -3
- data/lib/scorpion/dependency/builder_dependency.rb +5 -1
- data/lib/scorpion/dependency/class_dependency.rb +10 -7
- data/lib/scorpion/dependency_map.rb +11 -2
- data/lib/scorpion/error.rb +6 -0
- data/lib/scorpion/hunt.rb +32 -21
- data/lib/scorpion/hunter.rb +15 -2
- data/lib/scorpion/locale/en.yml +1 -0
- data/lib/scorpion/method.rb +24 -0
- data/lib/scorpion/nest.rb +5 -0
- data/lib/scorpion/object.rb +30 -23
- data/lib/scorpion/object_constructor.rb +23 -14
- data/lib/scorpion/rails.rb +1 -0
- data/lib/scorpion/rails/active_record/association.rb +5 -6
- data/lib/scorpion/rails/active_record/model.rb +4 -5
- data/lib/scorpion/rails/active_record/relation.rb +2 -4
- data/lib/scorpion/rails/controller.rb +31 -2
- data/lib/scorpion/rails/job.rb +12 -0
- data/lib/scorpion/rails/mailer.rb +42 -0
- data/lib/scorpion/rails/nest.rb +23 -20
- data/lib/scorpion/rails/railtie.rb +1 -0
- data/lib/scorpion/rspec/helper.rb +47 -0
- data/lib/scorpion/stinger.rb +0 -1
- data/lib/scorpion/version.rb +1 -1
- data/scorpion.gemspec +1 -0
- data/spec/lib/scorpion/attribute_spec.rb +10 -0
- data/spec/lib/scorpion/hunt_spec.rb +9 -3
- data/spec/lib/scorpion/hunter_spec.rb +13 -2
- data/spec/lib/scorpion/object_constructor_spec.rb +63 -6
- data/spec/lib/scorpion/object_spec.rb +9 -5
- data/spec/lib/scorpion/rails/controller_spec.rb +33 -1
- data/spec/lib/scorpion/rspec/helper_spec.rb +10 -0
- data/spec/spec_helper.rb +5 -0
- metadata +20 -3
data/lib/scorpion/rails/nest.rb
CHANGED
@@ -5,17 +5,16 @@ module Scorpion
|
|
5
5
|
module Rails
|
6
6
|
# Handles building a scorpion to handle a single request and populating
|
7
7
|
# all the dependencies automatically.
|
8
|
+
#
|
9
|
+
# The host class must respond to #scorpion, #assign_scorpion(scorpion) and
|
10
|
+
# #free_scorpion.
|
8
11
|
module Nest
|
12
|
+
include Scorpion::Object
|
9
13
|
|
10
14
|
# ============================================================================
|
11
15
|
# @!group Attributes
|
12
16
|
#
|
13
17
|
|
14
|
-
# @!attribute
|
15
|
-
# @return [Scorpion] the scorpion used to fetch dependencies.
|
16
|
-
attr_reader :scorpion
|
17
|
-
private :scorpion
|
18
|
-
|
19
18
|
# @!attribute
|
20
19
|
# @return [Scorpion::Nest] the nest used to conceive scorpions.
|
21
20
|
def nest
|
@@ -23,12 +22,17 @@ module Scorpion
|
|
23
22
|
end
|
24
23
|
private :nest
|
25
24
|
|
25
|
+
def scorpion( scope = nil )
|
26
|
+
# Make sure a scorpion is always available. Will be freed on the next
|
27
|
+
# call to #with_scorpion
|
28
|
+
ensure_scorpion( super ) unless scope
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
26
32
|
#
|
27
33
|
# @!endgroup Attributes
|
28
34
|
|
29
35
|
def self.included( base )
|
30
|
-
# Setup dependency injection
|
31
|
-
base.send :include, Scorpion::Object
|
32
36
|
|
33
37
|
# @!attribute [rw]
|
34
38
|
# @return [Scorpion::Nest] the singleton nest used by controllers.
|
@@ -60,27 +64,26 @@ module Scorpion
|
|
60
64
|
# Fetch a scorpion and feed the controller it's dependencies, then yield
|
61
65
|
# to perform the action within the context of that scorpion.
|
62
66
|
def with_scorpion( &block )
|
63
|
-
|
64
|
-
|
65
|
-
prepare_scorpion( @scorpion ) if respond_to?( :prepare_scorpion, true )
|
66
|
-
|
67
|
-
hunt = Scorpion::Hunt.new @scorpion, nil, nil
|
68
|
-
hunt.inject self
|
67
|
+
ensure_scorpion( scorpion )
|
68
|
+
scorpion.inject self
|
69
69
|
|
70
70
|
yield
|
71
71
|
ensure
|
72
72
|
free_scorpion
|
73
73
|
end
|
74
74
|
|
75
|
-
|
75
|
+
def conceive_scorpion
|
76
|
+
nest.conceive
|
77
|
+
end
|
76
78
|
|
77
|
-
|
78
|
-
|
79
|
-
|
79
|
+
def ensure_scorpion( existing )
|
80
|
+
scorpion = existing
|
81
|
+
scorpion = assign_scorpion( conceive_scorpion ) unless existing
|
82
|
+
|
83
|
+
prepare_scorpion( scorpion ) if respond_to?( :prepare_scorpion, true )
|
84
|
+
scorpion
|
85
|
+
end
|
80
86
|
|
81
|
-
def free_scorpion
|
82
|
-
@scorpion = nil
|
83
|
-
end
|
84
87
|
end
|
85
88
|
end
|
86
89
|
end
|
@@ -7,6 +7,7 @@ module Scorpion
|
|
7
7
|
initializer "scorpion.configure" do |app|
|
8
8
|
::ActionController::Base.send :include, Scorpion::Rails::Controller
|
9
9
|
::ActiveJob::Base.send :include, Scorpion::Rails::Job
|
10
|
+
::ActionMailer::Base.send :include, Scorpion::Rails::Mailer
|
10
11
|
|
11
12
|
::Scorpion::Rails::ActiveRecord.install!
|
12
13
|
end
|
@@ -6,18 +6,65 @@ module Scorpion
|
|
6
6
|
base.let( :scorpion ){ Scorpion::Rspec.scorpion_nest.conceive }
|
7
7
|
base.send :extend, Scorpion::Rspec::Helper::Methods
|
8
8
|
|
9
|
+
[ ActionController::Base, ActiveJob::Base, ActionMailer::Base ].each do |intercept|
|
10
|
+
base.infest_nest intercept
|
11
|
+
end
|
12
|
+
|
9
13
|
super
|
10
14
|
end
|
11
15
|
|
12
16
|
|
13
17
|
module Methods
|
14
18
|
|
19
|
+
# Intercept calls to conceive_scorpion in classes that include
|
20
|
+
# {Scorpion::Rails::Nest}
|
21
|
+
# @param [Class] klass that includes {Scorpion::Rails::Nest}
|
22
|
+
# @return [void]
|
23
|
+
def infest_nest( klass )
|
24
|
+
return unless klass < Scorpion::Rails::Nest
|
25
|
+
|
26
|
+
before( :each ) do
|
27
|
+
allow_any_instance_of( klass ).to receive( :conceive_scorpion )
|
28
|
+
.and_wrap_original do |m|
|
29
|
+
# When hunting for dependencies in controllers, jobs, etc. first
|
30
|
+
# consider the dependencies defined in the specs.
|
31
|
+
Scorpion::ChainHunter.new( scorpion, m.call )
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Set up scorpion hunting rules for the spec.
|
15
37
|
def scorpion( &block )
|
16
38
|
before( :each ) do
|
17
39
|
scorpion.prepare &block
|
18
40
|
end
|
19
41
|
end
|
20
42
|
|
43
|
+
# Specify a specific hunting contract and prepare a `let` block of the
|
44
|
+
# same name.
|
45
|
+
def hunt( name, contract, value = :unspecified, &block )
|
46
|
+
block ||= ->{ value == :unspecified ? instance_double( contract ) : value }
|
47
|
+
|
48
|
+
let( name, &block )
|
49
|
+
|
50
|
+
before( :each ) do
|
51
|
+
scorpion.prepare do |hunter|
|
52
|
+
if value == :unspecified
|
53
|
+
hunter.hunt_for contract do
|
54
|
+
send( name )
|
55
|
+
end
|
56
|
+
else
|
57
|
+
hunter.hunt_for contract, return: value
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def hunt!( name, contract, value = :unspecified, &block )
|
64
|
+
hunt name, contract, value, &block
|
65
|
+
before(:each){ send name }
|
66
|
+
end
|
67
|
+
|
21
68
|
end
|
22
69
|
|
23
70
|
end
|
data/lib/scorpion/stinger.rb
CHANGED
data/lib/scorpion/version.rb
CHANGED
data/scorpion.gemspec
CHANGED
@@ -1,8 +1,18 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
module Test
|
4
|
+
class Attr; end
|
5
|
+
end
|
6
|
+
|
7
|
+
|
3
8
|
describe Scorpion::Attribute do
|
4
9
|
it "responds to trait? methods" do
|
5
10
|
attr = Scorpion::Attribute.new :name, :contract, :formatted
|
6
11
|
expect( attr ).to be_formatted
|
7
12
|
end
|
13
|
+
|
14
|
+
it "resolves contract strings to constants" do
|
15
|
+
attr = Scorpion::Attribute.new :name, 'Test::Attr'
|
16
|
+
expect( attr.contract ).to be Test::Attr
|
17
|
+
end
|
8
18
|
end
|
@@ -39,14 +39,14 @@ describe Scorpion::Hunt do
|
|
39
39
|
end
|
40
40
|
|
41
41
|
it "finds matching argument in parent" do
|
42
|
-
hunt.
|
42
|
+
hunt.dependencies[:label] = "Hello"
|
43
43
|
|
44
44
|
expect( hunt.fetch String ).to eq "Hello"
|
45
45
|
end
|
46
46
|
|
47
47
|
it "finds matching argument in grandparent" do
|
48
|
-
hunt = Scorpion::Hunt.new scorpion, String, nil, "Hello"
|
49
|
-
hunt.send :push, Regexp, nil, [], nil
|
48
|
+
hunt = Scorpion::Hunt.new scorpion, String, nil, label: "Hello"
|
49
|
+
hunt.send :push, Regexp, nil, [], {}, nil
|
50
50
|
|
51
51
|
expect( scorpion ).to receive( :execute ) do |hunt|
|
52
52
|
next if hunt.contract == String
|
@@ -88,6 +88,12 @@ describe Scorpion::Hunt do
|
|
88
88
|
hunt.inject target
|
89
89
|
expect( target.sailor? ).to be_falsy
|
90
90
|
end
|
91
|
+
|
92
|
+
it "invokes on_injected" do
|
93
|
+
expect( target ).to receive( :on_injected )
|
94
|
+
hunt.inject target
|
95
|
+
end
|
96
|
+
|
91
97
|
end
|
92
98
|
|
93
99
|
end
|
@@ -94,17 +94,28 @@ describe Scorpion::Hunter do
|
|
94
94
|
expect( hunter.fetch_by_traits Test::Hunter::Implicit, [] ).to be_a Test::Hunter::Implicit
|
95
95
|
end
|
96
96
|
|
97
|
+
it "initialize explicit contracts" do
|
98
|
+
zoo = hunter.new Test::Hunter::Zoo
|
99
|
+
expect( zoo ).to be_a Test::Hunter::Zoo
|
100
|
+
expect( zoo.scorpion ).to eq hunter
|
101
|
+
end
|
102
|
+
|
103
|
+
it "delegates hunting definitions" do
|
104
|
+
hunter.hunt_for Test::Hunter::Zoo, return: :nyc
|
105
|
+
expect( hunter.fetch Test::Hunter::Zoo ).to eq :nyc
|
106
|
+
end
|
107
|
+
|
97
108
|
context "child dependencies" do
|
98
109
|
it "passes initializer args to child dependencies" do
|
99
110
|
zoo = Test::Hunter::Zoo.new
|
100
|
-
city = hunter.fetch Test::Hunter::City, zoo
|
111
|
+
city = hunter.fetch Test::Hunter::City, zoo: zoo
|
101
112
|
|
102
113
|
expect( city.park.zoo ).to be zoo
|
103
114
|
end
|
104
115
|
|
105
116
|
it "passes self to child dependencies" do
|
106
117
|
zoo = Test::Hunter::Zoo.new
|
107
|
-
city = hunter.fetch Test::Hunter::City, zoo
|
118
|
+
city = hunter.fetch Test::Hunter::City, zoo: zoo
|
108
119
|
|
109
120
|
expect( city.park.city ).to be city
|
110
121
|
end
|
@@ -1,14 +1,15 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Scorpion::ObjectConstructor do
|
4
|
-
|
4
|
+
|
5
|
+
it 'defines an initializer' do
|
5
6
|
klass = Class.new do
|
6
7
|
include Scorpion::Object
|
7
8
|
|
8
9
|
initialize logger: String
|
9
10
|
end
|
10
11
|
|
11
|
-
expect( klass.instance_method(:initialize).arity ).to eq 1
|
12
|
+
expect( klass.instance_method(:initialize).arity ).to eq -1
|
12
13
|
end
|
13
14
|
|
14
15
|
it "executes initializer code" do
|
@@ -19,7 +20,7 @@ describe Scorpion::ObjectConstructor do
|
|
19
20
|
initialize label: String, &b
|
20
21
|
end
|
21
22
|
|
22
|
-
klass.new "home"
|
23
|
+
klass.new label: "home"
|
23
24
|
end.to yield_control
|
24
25
|
end
|
25
26
|
|
@@ -27,13 +28,13 @@ describe Scorpion::ObjectConstructor do
|
|
27
28
|
klass = Class.new do
|
28
29
|
include Scorpion::Object
|
29
30
|
|
30
|
-
initialize label: String do
|
31
|
+
initialize label: String do |label, &block|
|
31
32
|
block.call
|
32
33
|
end
|
33
34
|
end
|
34
35
|
|
35
36
|
expect do |b|
|
36
|
-
klass.new "apples", &b
|
37
|
+
klass.new label: "apples", &b
|
37
38
|
end.to yield_control
|
38
39
|
end
|
39
40
|
|
@@ -44,6 +45,62 @@ describe Scorpion::ObjectConstructor do
|
|
44
45
|
initialize label: String
|
45
46
|
end
|
46
47
|
|
47
|
-
expect( klass.new( "apples" ).label ).to eq "apples"
|
48
|
+
expect( klass.new( label: "apples" ).label ).to eq "apples"
|
49
|
+
end
|
50
|
+
|
51
|
+
it "invokes all initializers" do
|
52
|
+
klass = Class.new do
|
53
|
+
include Scorpion::Object
|
54
|
+
|
55
|
+
initialize label: String do |label:|
|
56
|
+
@label = label.reverse
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
derived = Class.new( klass ) do
|
61
|
+
initialize logger: Logger do |logger:|
|
62
|
+
@logger = logger.to_s.reverse.to_sym
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
expect( derived.new( logger: :unset, label: "Super" ).label ).to eq "repuS"
|
67
|
+
expect( derived.new( logger: :unset, label: "Super" ).logger ).to eq :tesnu
|
48
68
|
end
|
69
|
+
|
70
|
+
|
71
|
+
context "inheritance" do
|
72
|
+
let( :klass ) do
|
73
|
+
Class.new do
|
74
|
+
include Scorpion::Object
|
75
|
+
|
76
|
+
initialize label: String do |label:|
|
77
|
+
@label = label.reverse
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
let( :derived ) do
|
83
|
+
Class.new( klass )
|
84
|
+
end
|
85
|
+
|
86
|
+
it "inherits super initializer block" do
|
87
|
+
expect( derived.new( label: "Super" ).label ).to eq "repuS"
|
88
|
+
end
|
89
|
+
|
90
|
+
it "inherits initializer_injections" do
|
91
|
+
expect( klass.initializer_injections.count ).to eq 1
|
92
|
+
expect( klass.initializer_injections ).to eq derived.initializer_injections
|
93
|
+
end
|
94
|
+
|
95
|
+
it "can override initializer_injections" do
|
96
|
+
more_derived = Class.new( derived ) do
|
97
|
+
initialize do
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
expect( more_derived.initializer_injections.count ).to eq 0
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
49
106
|
end
|
@@ -63,6 +63,14 @@ describe Scorpion::Object do
|
|
63
63
|
allow( hunt ).to receive( :scorpion ).and_return scorpion
|
64
64
|
end
|
65
65
|
|
66
|
+
it "stings scopes" do
|
67
|
+
scope = double
|
68
|
+
|
69
|
+
expect( scope ).to receive( :with_scorpion ).with( scorpion )
|
70
|
+
|
71
|
+
Test::Object::Mouse.spawn( hunt ).scorpion( scope )
|
72
|
+
end
|
73
|
+
|
66
74
|
describe ".spawn" do
|
67
75
|
|
68
76
|
it "can spawn" do
|
@@ -76,7 +84,7 @@ describe Scorpion::Object do
|
|
76
84
|
end
|
77
85
|
|
78
86
|
it "can inherit" do
|
79
|
-
mouse = Test::Object::Mouse.spawn hunt, name: 'name'
|
87
|
+
mouse = Test::Object::Mouse.spawn hunt, { name: 'name' }, {}
|
80
88
|
expect( mouse.family ).to eq 'mouse'
|
81
89
|
expect( mouse.options ).to include name: 'name'
|
82
90
|
end
|
@@ -87,10 +95,6 @@ describe Scorpion::Object do
|
|
87
95
|
end.to yield_control
|
88
96
|
end
|
89
97
|
|
90
|
-
it "invokes on_injected" do
|
91
|
-
expect_any_instance_of( Test::Object::Mouse ).to receive( :on_injected )
|
92
|
-
Test::Object::Mouse.spawn hunt
|
93
|
-
end
|
94
98
|
end
|
95
99
|
|
96
100
|
describe "accessors" do
|
@@ -10,7 +10,6 @@ end
|
|
10
10
|
|
11
11
|
describe Scorpion::Rails::Controller, type: :controller do
|
12
12
|
controller ActionController::Base do
|
13
|
-
include Scorpion::Rails::Controller
|
14
13
|
|
15
14
|
depend_on do
|
16
15
|
service Test::Nest::Service
|
@@ -42,6 +41,29 @@ describe Scorpion::Rails::Controller, type: :controller do
|
|
42
41
|
expect( subject ).to respond_to :scorpion
|
43
42
|
end
|
44
43
|
|
44
|
+
it "retrieves scorpion from `env`" do
|
45
|
+
expect( subject.env ).to receive( :[] )
|
46
|
+
.with( Scorpion::Rails::Controller::ENV_KEY )
|
47
|
+
.at_least( :once )
|
48
|
+
.and_call_original
|
49
|
+
|
50
|
+
get :index
|
51
|
+
end
|
52
|
+
|
53
|
+
it "stores the scorpion in `env`" do
|
54
|
+
expect( subject.env ).to receive( :[]= )
|
55
|
+
.with( Scorpion::Rails::Controller::ENV_KEY, kind_of( Scorpion ) )
|
56
|
+
.at_least( :once )
|
57
|
+
.and_call_original
|
58
|
+
|
59
|
+
get :index
|
60
|
+
end
|
61
|
+
|
62
|
+
it "prepares a scorpion outside of a request when accessed" do
|
63
|
+
expect( subject.env[Scorpion::Rails::Controller::ENV_KEY] ).to be_nil
|
64
|
+
expect( subject.scorpion ).not_to be_nil
|
65
|
+
end
|
66
|
+
|
45
67
|
it "initializes non-lazy dependencies" do
|
46
68
|
expect( subject.cache ).to be_present
|
47
69
|
end
|
@@ -107,5 +129,15 @@ describe Scorpion::Rails::Controller, type: :controller do
|
|
107
129
|
|
108
130
|
get :index
|
109
131
|
end
|
132
|
+
|
133
|
+
|
134
|
+
it "scopes relations" do
|
135
|
+
allow( subject ).to receive( :index ) do
|
136
|
+
expect( subject.scorpion( Todo ).all.scorpion ).to be subject.scorpion
|
137
|
+
controller.render nothing: true
|
138
|
+
end
|
139
|
+
|
140
|
+
get :index
|
141
|
+
end
|
110
142
|
end
|
111
143
|
end
|