scorpion-ioc 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 19c5f928b91989643e0db1c9fef98016958d5813
4
- data.tar.gz: 7e03d874e6714dae07b4da0fd3481404087df760
3
+ metadata.gz: e60f46d66df09f4c6f7615fd2710d4fe3527c965
4
+ data.tar.gz: 57c9523e72aa665056649295c6453c7ce255b724
5
5
  SHA512:
6
- metadata.gz: 3e8b8889dafadd148472b73921eb1a1da3af9a0f006870c083abfb9bb600b2e9b21fdb24ab696f6f258621cd4ad3d6a497e0c143817399621f6dc0f2383758ad
7
- data.tar.gz: bfee710c8d61409b0915dc98a206e0493d9c4b30a5406f41ab1817a0077c46418b27be3d960899b6bcb1904becde4978ab5ec9d1f34b6cff21af97f062fa38fb
6
+ metadata.gz: 09b2275087d17e93753be0d51c44306b8dc3c18586ab7120821eb5aac16fbdb82b1a9df4ae5a2c67bdb411fe4ea6c629524fdbc2b5bdfd3fdb2d2401552989aa
7
+ data.tar.gz: 6188ca0ac77ef49985acf0e5d044eec89210d243479ade15d63d6f51289b273ceab9c4ca5f5fe5969353f79082a9d4e4b913341ff8e3c3f04adb1c723d750703
data/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/scorpion-ioc.svg)](http://badge.fury.io/rb/scorpion-ioc)
4
4
  [![Code Climate](https://codeclimate.com/github/phallguy/scorpion.png)](https://codeclimate.com/github/phallguy/scorpion)
5
5
  [![Test Coverage](https://codeclimate.com/github/phallguy/scorpion/badges/coverage.svg)](https://codeclimate.com/github/phallguy/scorpion/coverage)
6
+ [![Inch CI](https://inch-ci.org/github/phallguy/scorpion.svg?branch=master)](https://inch-ci.org/github/phallguy/scorpion)
6
7
  [![Circle CI](https://circleci.com/gh/phallguy/scorpion.svg?style=svg)](https://circleci.com/gh/phallguy/scorpion)
7
8
 
8
9
  Add IoC to rails with minimal fuss and ceremony.
@@ -32,13 +32,13 @@ module Scorpion
32
32
  hunting_map.chart &block
33
33
  end
34
34
 
35
- # @see Scorpion#hunt!
36
- def hunt_by_traits!( contract, traits = nil, *args, &block )
35
+ # @see Scorpion#hunt
36
+ def hunt_by_traits( contract, traits = nil, *args, &block )
37
37
  unless prey = hunting_map.find( contract, traits )
38
- return parent.hunt_by_traits! contract, traits if parent
38
+ return parent.hunt_by_traits contract, traits if parent
39
39
 
40
- prey = Scorpion::Prey::ClassPrey.new( contract, nil ) if contract.is_a?( Class ) && traits.nil?
41
- unsuccessful_hunt!( contract, traits ) unless prey
40
+ prey = Scorpion::Prey::ClassPrey.new( contract, nil ) if contract.is_a?( Class ) && traits.blank?
41
+ unsuccessful_hunt( contract, traits ) unless prey
42
42
  end
43
43
  prey.fetch self, *args, &block
44
44
  end
data/lib/scorpion/king.rb CHANGED
@@ -2,7 +2,7 @@ require 'scorpion/attribute_set'
2
2
 
3
3
  module Scorpion
4
4
  # Identifies objects that are served by {Scorpion scorpions} that feed on
5
- # {Scorpion#hunt! hunted} prey.
5
+ # {Scorpion#hunt hunted} prey.
6
6
  module King
7
7
 
8
8
  # ============================================================================
@@ -48,7 +48,7 @@ module Scorpion
48
48
  king.instance_variable_set :@scorpion, scorpion
49
49
  # Go hunt for dependencies that are not lazy and initialize the
50
50
  # references.
51
- scorpion.feed! king
51
+ scorpion.feed king
52
52
  king.send :on_fed
53
53
  end
54
54
  end
@@ -76,17 +76,49 @@ module Scorpion
76
76
  end
77
77
 
78
78
  # Convenience method to ask the {#scorpion} to hunt for an object.
79
- # @see Scorpion#hunt!
80
- def hunt!( contract, *args, &block )
81
- scorpion.hunt! contract, *args, &block
79
+ # @see Scorpion#hunt
80
+ def hunt( contract, *args, &block )
81
+ scorpion.hunt contract, *args, &block
82
82
  end
83
83
 
84
84
  # Convenience method to ask the {#scorpion} to hunt for an object.
85
- # @see Scorpion#hunt_by_traits!
86
- def hunt_by_traits!( contract, traits, *args, &block )
87
- scorpion.hunt_by_traits! contract, *args, &block
85
+ # @see Scorpion#hunt_by_traits
86
+ def hunt_by_traits( contract, traits, *args, &block )
87
+ scorpion.hunt_by_traits contract, *args, &block
88
88
  end
89
89
 
90
+ # Feed dependencies from a hash into their associated attributes.
91
+ # @param [Hash] dependencies hash describing attributes to inject.
92
+ # @param [Boolean] overwrite existing attributes with values in in the hash.
93
+ def feast_on( dependencies, overwrite = false )
94
+ injected_attributes.each do |attr|
95
+ next unless dependencies.key? attr.name
96
+
97
+ if overwrite || !self.send( "#{ attr.name }?" )
98
+ self.send( "#{ attr.name }=", dependencies[ attr.name ] )
99
+ end
100
+ end
101
+
102
+ dependencies
103
+ end
104
+ alias_method :inject_from, :feast_on
105
+
106
+ # Injects dependenices from the hash and removes them from the hash.
107
+ # @see #feast_on
108
+ def feast_on!( dependencies, overwrite = false )
109
+ injected_attributes.each do |attr|
110
+ next unless dependencies.key? attr.name
111
+ val = dependencies.delete( attr.name )
112
+
113
+ if overwrite || !self.send( "#{ attr.name }?" )
114
+ self.send( "#{ attr.name }=", val )
115
+ end
116
+ end
117
+
118
+ dependencies
119
+ end
120
+ alias_method :inject_from!, :feast_on!
121
+
90
122
  module ClassMethods
91
123
 
92
124
  # Tells a {Scorpion} what to inject into the class when it is constructed
@@ -113,36 +145,42 @@ module Scorpion
113
145
 
114
146
  def build_injected_attributes
115
147
  injected_attributes.each do |attr|
116
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
117
- def #{ attr.name }
118
- @#{ attr.name } ||= begin
119
- attr = injected_attributes[ :#{ attr.name } ]
120
- scorpion.hunt!( attr.contract, attr.traits )
121
- end
122
- end
123
-
124
- def #{ attr.name }=( value )
125
- @#{ attr.name } = value
126
- end
148
+ build_injected_attribute attr
149
+ set_injected_attribute_visibility attr
150
+ end
151
+ end
127
152
 
128
- def #{ attr.name }?
129
- !!@#{ attr.name }
153
+ def build_injected_attribute( attr )
154
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
155
+ def #{ attr.name }
156
+ @#{ attr.name } ||= begin
157
+ attr = injected_attributes[ :#{ attr.name } ]
158
+ scorpion.hunt( attr.contract, attr.traits )
130
159
  end
131
- RUBY
160
+ end
132
161
 
133
- unless attr.public?
134
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
135
- private :#{ attr.name }=
136
- private :#{ attr.name }?
137
- RUBY
162
+ def #{ attr.name }=( value )
163
+ @#{ attr.name } = value
138
164
  end
139
165
 
140
- if attr.private?
141
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
142
- private :#{ attr.name }
143
- RUBY
166
+ def #{ attr.name }?
167
+ !!@#{ attr.name }
144
168
  end
169
+ RUBY
170
+ end
145
171
 
172
+ def set_injected_attribute_visibility( attr )
173
+ unless attr.public?
174
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
175
+ private :#{ attr.name }=
176
+ private :#{ attr.name }?
177
+ RUBY
178
+ end
179
+
180
+ if attr.private?
181
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
182
+ private :#{ attr.name }
183
+ RUBY
146
184
  end
147
185
  end
148
186
  end
@@ -71,14 +71,14 @@ module Scorpion
71
71
  end
72
72
  # Allow dependencies to access the current request/response
73
73
  hunter.hunt_for ActionDispatch::Request do |hunter|
74
- hunter.hunt!( AbstractController::Base ).request
74
+ hunter.hunt( AbstractController::Base ).request
75
75
  end
76
76
  hunter.hunt_for ActionDispatch::Response do |hunter|
77
- hunter.hunt!( AbstractController::Base ).response
77
+ hunter.hunt( AbstractController::Base ).response
78
78
  end
79
79
  end
80
80
 
81
- @scorpion.feed! self
81
+ @scorpion.feed self
82
82
 
83
83
  yield
84
84
  ensure
@@ -1,5 +1,5 @@
1
1
  module Scorpion
2
- VERSION_NUMBER = "0.1.0"
2
+ VERSION_NUMBER = "0.2.0"
3
3
  VERSION_SUFFIX = ""
4
4
  VERSION = "#{VERSION_NUMBER}#{VERSION_SUFFIX}"
5
5
  end
data/lib/scorpion.rb CHANGED
@@ -29,25 +29,27 @@ module Scorpion
29
29
  # @param [Array<Symbol>] traits required of the prey
30
30
  # @return [Object] an object that matches the requirements defined in `attribute`.
31
31
  # @raise [UnsuccessfulHunt] if a matching object cannot be found.
32
- def hunt_by_traits!( contract, traits, *args, &block )
32
+ def hunt_by_traits( contract, traits, *args, &block )
33
33
  fail "Not implemented"
34
34
  end
35
- alias_method :fetch_by_traits!, :hunt_by_traits!
35
+ alias_method :fetch_by_traits, :hunt_by_traits
36
36
 
37
37
  # Hunts for an object that satisfies the requested `contract` regardless of
38
38
  # traits.
39
- # @see #hunt_by_traits!
40
- def hunt!( contract, *args, &block )
41
- hunt_by_traits!( contract, nil, *args, &block )
39
+ # @see #hunt_by_traits
40
+ def hunt( contract, *args, &block )
41
+ hunt_by_traits( contract, nil, *args, &block )
42
42
  end
43
- alias_method :fetch!, :hunt!
43
+ alias_method :fetch, :hunt
44
44
 
45
45
  # Populate given `king` with its expected attributes.
46
46
  # @param [Scorpion::King] king to be fed.
47
47
  # @return [Scorpion::King] the populated king.
48
- def feed!( king )
48
+ def feed( king )
49
49
  king.injected_attributes.each do |attr|
50
- king.send :feed, attr, hunt_by_traits!( attr.contract, attr.traits )
50
+ next if king.send "#{ attr.name }?"
51
+
52
+ king.send :feed, attr, hunt_by_traits( attr.contract, attr.traits )
51
53
  end
52
54
  end
53
55
 
@@ -84,7 +86,7 @@ module Scorpion
84
86
 
85
87
  # Used by concrete scorpions to notify the caller that the hunt was
86
88
  # unsuccessful.
87
- def unsuccessful_hunt!( contract, traits )
89
+ def unsuccessful_hunt( contract, traits )
88
90
  fail UnsuccessfulHunt.new contract, traits
89
91
  end
90
92
 
@@ -39,31 +39,35 @@ describe Scorpion::Hunter do
39
39
  end
40
40
 
41
41
  it "spawns prey" do
42
- expect( hunter.hunt! Test::Hunter::Beast ).to be_a Test::Hunter::Bear
42
+ expect( hunter.hunt Test::Hunter::Beast ).to be_a Test::Hunter::Bear
43
43
  end
44
44
 
45
45
  it "spawns a new instance for multiple requests" do
46
- first = hunter.hunt! Test::Hunter::Beast
47
- expect( hunter.hunt! Test::Hunter::Beast ).not_to eq first
46
+ first = hunter.hunt Test::Hunter::Beast
47
+ expect( hunter.hunt Test::Hunter::Beast ).not_to eq first
48
48
  end
49
49
 
50
50
  it "spawns the same instance for captured prey" do
51
- first = hunter.hunt_by_traits! Test::Hunter::Beast, :tame
52
- expect( hunter.hunt_by_traits! Test::Hunter::Beast, :tame ).to be first
51
+ first = hunter.hunt_by_traits Test::Hunter::Beast, :tame
52
+ expect( hunter.hunt_by_traits Test::Hunter::Beast, :tame ).to be first
53
53
  end
54
54
 
55
55
  it "injects nested kings" do
56
- zoo = hunter.hunt! Test::Hunter::Zoo
56
+ zoo = hunter.hunt Test::Hunter::Zoo
57
57
  expect( zoo.bear ).to be_a Test::Hunter::Bear
58
58
  end
59
59
 
60
60
  it "accepts arguments that are passed to constructor" do
61
- obj = hunter.hunt! Test::Hunter::Argumented, :awesome
61
+ obj = hunter.hunt Test::Hunter::Argumented, :awesome
62
62
  expect( obj.arg ).to eq :awesome
63
63
  end
64
64
 
65
65
  it "implicitly spawns Class contracts" do
66
- expect( hunter.hunt! Test::Hunter::Implicit ).to be_a Test::Hunter::Implicit
66
+ expect( hunter.hunt Test::Hunter::Implicit ).to be_a Test::Hunter::Implicit
67
+ end
68
+
69
+ it "implicitly spawns Class contracts with empty traits" do
70
+ expect( hunter.hunt_by_traits Test::Hunter::Implicit, [] ).to be_a Test::Hunter::Implicit
67
71
  end
68
72
 
69
73
  end
@@ -13,7 +13,8 @@ module Test
13
13
  def initialize( family, parent = nil, options={}, &block )
14
14
  @family = family
15
15
  @parent = parent
16
- @options = options
16
+ @options = feast_on! options
17
+
17
18
 
18
19
  yield if block_given?
19
20
  end
@@ -55,7 +56,7 @@ describe Scorpion::King do
55
56
  let( :scorpion ){ double Scorpion }
56
57
 
57
58
  before( :each ) do
58
- allow( scorpion ).to receive( :feed! )
59
+ allow( scorpion ).to receive( :feed )
59
60
  end
60
61
 
61
62
  describe ".spawn" do
@@ -65,6 +66,11 @@ describe Scorpion::King do
65
66
  expect( mamal ).to be_a Test::King::Mamal
66
67
  end
67
68
 
69
+ it "calls feed" do
70
+ expect( scorpion ).to receive( :feed )
71
+ Test::King::Mouse.spawn scorpion
72
+ end
73
+
68
74
  it "can inherit" do
69
75
  mouse = Test::King::Mouse.spawn scorpion, name: 'name'
70
76
  expect( mouse.family ).to eq 'mouse'
@@ -125,5 +131,68 @@ describe Scorpion::King do
125
131
 
126
132
  end
127
133
 
134
+ describe "feasting" do
135
+ let( :logger ) { Test::King::Logger.new }
136
+ let( :options ) { { manager: logger, color: :red } }
137
+ let( :king ) { Test::King::Mouse.new name: 'mighty' }
138
+
139
+
140
+ describe "#feast_on" do
141
+ it "assigns attributes" do
142
+ king.send :feast_on, options
143
+ expect( king.manager ).to be logger
144
+ end
145
+
146
+ it "doesn't overwrite" do
147
+ king.manager = Test::King::Logger.new
148
+ king.send :feast_on, options
149
+ expect( king.manager ).not_to be logger
150
+ end
151
+
152
+ it "overwrites when asked" do
153
+ king.manager = Test::King::Logger.new
154
+ king.send :feast_on, options, true
155
+ expect( king.manager ).to be logger
156
+ end
157
+ end
158
+
159
+ describe "feast_on!" do
160
+ it "assigns attributes" do
161
+ king.send :feast_on!, options
162
+ expect( king.manager ).to be logger
163
+ end
164
+
165
+ it "doesn't overwrite" do
166
+ king.manager = Test::King::Logger.new
167
+ king.send :feast_on!, options
168
+ expect( king.manager ).not_to be logger
169
+ end
170
+
171
+ it "overwrites when asked" do
172
+ king.manager = Test::King::Logger.new
173
+ king.send :feast_on!, options, true
174
+ expect( king.manager ).to be logger
175
+ end
176
+
177
+ it "removes injected attributes" do
178
+ expect( options ).to have_key :manager
179
+ king.send :feast_on!, options
180
+ expect( options ).not_to have_key :manager
181
+ end
182
+
183
+ it "removes injected attribute even if already set" do
184
+ expect( options ).to have_key :manager
185
+ king.manager = Test::King::Logger.new
186
+ king.send :feast_on!, options
187
+ expect( options ).not_to have_key :manager
188
+ end
189
+
190
+ it "doesn't remove other options" do
191
+ king.send :feast_on!, options
192
+ expect( options ).to have_key :color
193
+ end
194
+
195
+ end
196
+ end
128
197
 
129
198
  end
@@ -18,8 +18,8 @@ describe Scorpion::Rails::Controller, type: :controller do
18
18
  end
19
19
 
20
20
  def index
21
- @guard1 = scorpion.hunt! Test::Nest::Guard
22
- @guard2 = scorpion.hunt! Test::Nest::Guard
21
+ @guard1 = scorpion.hunt Test::Nest::Guard
22
+ @guard2 = scorpion.hunt Test::Nest::Guard
23
23
  render nothing: true
24
24
  end
25
25
  end
@@ -62,8 +62,8 @@ describe Scorpion::Rails::Controller, type: :controller do
62
62
 
63
63
  it "spawns the same service during the same request" do
64
64
  allow( subject ).to receive( :index ) do
65
- service = subject.service
66
- expect( subject.scorpion.hunt! Test::Nest::Service ).to be service
65
+ service = subject.scorpion.hunt Test::Nest::Service
66
+ expect( subject.scorpion.hunt Test::Nest::Service ).to be service
67
67
  controller.render nothing: true
68
68
  end
69
69
 
@@ -74,7 +74,7 @@ describe Scorpion::Rails::Controller, type: :controller do
74
74
  service = subject.service
75
75
 
76
76
  allow( subject ).to receive( :index ) do
77
- expect( subject.scorpion.hunt! Test::Nest::Service ).not_to be service
77
+ expect( subject.scorpion.hunt Test::Nest::Service ).not_to be service
78
78
  controller.render nothing: true
79
79
  end
80
80
 
@@ -83,7 +83,7 @@ describe Scorpion::Rails::Controller, type: :controller do
83
83
 
84
84
  it "hunts for controller" do
85
85
  allow( subject ).to receive( :index ) do
86
- expect( subject.scorpion.hunt!( AbstractController::Base ) ).to be subject
86
+ expect( subject.scorpion.hunt( AbstractController::Base ) ).to be subject
87
87
  controller.render nothing: true
88
88
  end
89
89
 
@@ -92,7 +92,7 @@ describe Scorpion::Rails::Controller, type: :controller do
92
92
 
93
93
  it "hunts for response" do
94
94
  allow( subject ).to receive( :index ) do
95
- expect( subject.scorpion.hunt!( ActionDispatch::Response ) ).to be subject.response
95
+ expect( subject.scorpion.hunt( ActionDispatch::Response ) ).to be subject.response
96
96
  controller.render nothing: true
97
97
  end
98
98
 
@@ -101,7 +101,7 @@ describe Scorpion::Rails::Controller, type: :controller do
101
101
 
102
102
  it "hunts for request" do
103
103
  allow( subject ).to receive( :index ) do
104
- expect( subject.scorpion.hunt!( ActionDispatch::Request ).object_id ).to be subject.request.object_id
104
+ expect( subject.scorpion.hunt( ActionDispatch::Request ).object_id ).to be subject.request.object_id
105
105
  controller.render nothing: true
106
106
  end
107
107
 
@@ -1,4 +1,40 @@
1
1
  require 'spec_helper'
2
2
 
3
+ module Test
4
+ module Scorpion
5
+ class Logger; end
6
+
7
+ class Target
8
+ include ::Scorpion::King
9
+
10
+ feed_on do
11
+ logger Logger, public: true
12
+ end
13
+ end
14
+ end
15
+ end
16
+
3
17
  describe Scorpion do
18
+ let( :scorpion ){ Scorpion::Hunter.new }
19
+ let( :target ) do
20
+ Test::Scorpion::Target.new.tap do |target|
21
+ target.instance_variable_set :@scorpion, scorpion
22
+ end
23
+ end
24
+
25
+ describe "#feed" do
26
+ it "injects attributes" do
27
+ scorpion.feed target
28
+
29
+ expect( target.logger ).to be_a Test::Scorpion::Logger
30
+ end
31
+
32
+ it "does not overwrite existing attributes" do
33
+ logger = Test::Scorpion::Logger.new
34
+ target.logger = logger
35
+ scorpion.feed target
36
+
37
+ expect( target.logger ).to be logger
38
+ end
39
+ end
4
40
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scorpion-ioc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Alexander
@@ -174,7 +174,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
174
174
  version: '0'
175
175
  requirements: []
176
176
  rubyforge_project:
177
- rubygems_version: 2.4.5
177
+ rubygems_version: 2.4.6
178
178
  signing_key:
179
179
  specification_version: 4
180
180
  summary: Add IoC to rails with minimal fuss and ceremony