substation 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/Changelog.md ADDED
@@ -0,0 +1,12 @@
1
+ # v0.0.2 2013-05-15
2
+
3
+ * [BREAKING CHANGE] Creating a dispatcher requires an application env (snusnu)
4
+
5
+ * Changes `Substation::Dispatcher.coerce(config)` to `Substation::Dispatcher.coerce(config, env)`
6
+ * Changes `Substation::Dispatcher#call(name, input, env)` to `Substation::Dispatcher#call(name, input)`
7
+
8
+ [Compare v0.0.1..v0.0.2](https://github.com/snusnu/substation/compare/v0.0.1...v0.0.2)
9
+
10
+ # v0.0.1 2013-05-14
11
+
12
+ First public release
data/README.md CHANGED
@@ -4,11 +4,13 @@
4
4
  [![Build Status](https://secure.travis-ci.org/snusnu/substation.png?branch=master)][travis]
5
5
  [![Dependency Status](https://gemnasium.com/snusnu/substation.png)][gemnasium]
6
6
  [![Code Climate](https://codeclimate.com/github/snusnu/substation.png)][codeclimate]
7
+ [![Coverage Status](https://coveralls.io/repos/snusnu/substation/badge.png?branch=master)][coveralls]
7
8
 
8
9
  [gem]: https://rubygems.org/gems/substation
9
10
  [travis]: https://travis-ci.org/snusnu/substation
10
11
  [gemnasium]: https://gemnasium.com/snusnu/substation
11
12
  [codeclimate]: https://codeclimate.com/github/snusnu/substation
13
+ [coveralls]: https://coveralls.io/r/snusnu/substation
12
14
 
13
15
  `substation` can be thought of as a domain level request router. It assumes
14
16
  that every usecase in your application has a name and is implemented in a dedicated
@@ -29,9 +31,9 @@ addition to that, `response.success?` is available and will indicate
29
31
  wether invoking the action was successful or not.
30
32
 
31
33
  `Substation::Dispatcher` stores a mapping of action names to the actual
32
- objects implementing the action. Clients can use
33
- `Substation::Dispatcher#call(name, input, env)` to dispatch to any
34
- registered action. For example, a web application could map an http
34
+ objects implementing the action, as well as the application environment.
35
+ Clients can use `Substation::Dispatcher#call(name, input)` to dispatch to
36
+ any registered action. For example, a web application could map an http
35
37
  route to a specific action name and pass relevant http params on to the
36
38
  action.
37
39
 
@@ -191,14 +193,16 @@ For this purpose, we can instantiate a `Substation::Dispatcher` and hand
191
193
  it a configuration hash that describes the various actions by giving
192
194
  them a name, a class that's responsible for implementing the actual
193
195
  usecase, and a list of `0..n` observers that should be invoked depending
194
- on the action response.
196
+ on the action response. In addition to that, we must pass an instance of
197
+ the application's environment. More about application environments can
198
+ be found in the next paragraph.
195
199
 
196
200
  An example configuration for an action without any observers:
197
201
 
198
202
  ```ruby
199
203
  dispatcher = Substation::Dispatcher.coerce({
200
204
  'some_use_case' => { 'action' => 'App::SomeUseCase' }
201
- })
205
+ }, env)
202
206
  ```
203
207
 
204
208
  An example configuration for an action with one observer:
@@ -209,7 +213,7 @@ dispatcher = Substation::Dispatcher.coerce({
209
213
  'action' => 'App::SomeUseCase',
210
214
  'observer' => 'App::SomeUseCaseObserver'
211
215
  }
212
- })
216
+ }, env)
213
217
  ```
214
218
 
215
219
  An example configuration for an action with multiple observers:
@@ -223,7 +227,7 @@ dispatcher = Substation::Dispatcher.coerce({
223
227
  'App::AnotherObserver'
224
228
  ]
225
229
  }
226
- })
230
+ }, env)
227
231
  ```
228
232
 
229
233
  The above configuration examples are tailored towards being read from a
@@ -240,7 +244,7 @@ dispatcher = Substation::Dispatcher.coerce({
240
244
  :action => App::SomeUseCase,
241
245
  :observer => App::SomeUseCaseObserver
242
246
  }
243
- })
247
+ }, env)
244
248
  ```
245
249
 
246
250
  An example configuration using symbol keys and procs for handlers:
@@ -251,7 +255,7 @@ dispatcher = Substation::Dispatcher.coerce({
251
255
  :action => Proc.new { |request| request.success(:foo) },
252
256
  :observer => Proc.new { |response| do_something }
253
257
  }
254
- })
258
+ }, env)
255
259
  ```
256
260
 
257
261
 
@@ -262,8 +266,8 @@ the course of performing a usecase (like a logger or a storage engine
262
266
  abstraction), you can encapsulate these objects within an application
263
267
  specific environment object, and send that along to every action.
264
268
 
265
- Here's a simple example with an environment that encapsulates a logger,
266
- an artificial storage abstraction object and the dispatcher itself.
269
+ Here's a simple example with an environment that encapsulates a logger
270
+ and an artificial storage abstraction object.
267
271
 
268
272
  The example builds on top of the application specific action baseclass
269
273
  shown above:
@@ -272,13 +276,10 @@ shown above:
272
276
  module App
273
277
  class Environment
274
278
  attr_reader :storage
275
- attr_reader :dispatcher
276
279
  attr_reader :logger
277
280
 
278
- def initialize(storage, dispatcher, logger)
279
- @storage = storage
280
- @dispatcher = dispatcher
281
- @logger = logger
281
+ def initialize(storage, logger)
282
+ @storage, @logger = storage, logger
282
283
  end
283
284
  end
284
285
 
@@ -309,12 +310,12 @@ module App
309
310
  end
310
311
  end
311
312
 
313
+ storage = App::Storage.new # some storage abstraction
314
+ env = App::Environment.new(storage, Logger.new($stdout))
315
+
312
316
  dispatcher = Substation::Dispatcher.coerce({
313
317
  'some_use_case' => { 'action' => 'App::SomeUseCase' }
314
- })
315
-
316
- storage = App::Storage.new # some storage abstraction
317
- env = App::Environment.new(storage, dispatcher, Logger.new($stdout))
318
+ }, env)
318
319
 
319
320
  # :some_input is no person, db.save_person will fail
320
321
  response = dispatcher.call(:some_use_case, :some_input, env)
data/config/flay.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  ---
2
2
  threshold: 6
3
- total_score: 51
3
+ total_score: 46
@@ -60,11 +60,37 @@ module Substation
60
60
 
61
61
  # Coerce the given +config+ to a {Dispatcher} instance
62
62
  #
63
+ # @example setup code for all the other examples
64
+ #
65
+ # module App
66
+ # class Environment
67
+ # def initialize(storage, logger)
68
+ # @storage, @logger = storage, logger
69
+ # end
70
+ # end
71
+ #
72
+ # class SomeUseCase
73
+ # def self.call(request)
74
+ # data = perform_work
75
+ # request.success(data)
76
+ # end
77
+ # end
78
+ #
79
+ # class SomeObserver
80
+ # def self.call(response)
81
+ # # do something
82
+ # end
83
+ # end
84
+ # end
85
+ #
86
+ # storage = SomeStorageAbstraction.new
87
+ # env = App::Environment.new(storage, Logger.new($stdout))
88
+ #
63
89
  # @example without observers
64
90
  #
65
91
  # dispatcher = Substation::Dispatcher.coerce({
66
92
  # 'some_use_case' => { 'action' => 'SomeUseCase' }
67
- # })
93
+ # }, env)
68
94
  #
69
95
  # @example with a single observer
70
96
  #
@@ -73,7 +99,7 @@ module Substation
73
99
  # 'action' => 'SomeUseCase',
74
100
  # 'observer' => 'SomeObserver'
75
101
  # }
76
- # })
102
+ # }, env)
77
103
  #
78
104
  # @example with multiple observers
79
105
  #
@@ -85,31 +111,16 @@ module Substation
85
111
  # 'AnotherObserver'
86
112
  # ]
87
113
  # }
88
- # })
114
+ # }, env)
89
115
  #
90
116
  # @example with Symbol keys and const handlers
91
117
  #
92
- # module App
93
- # class SomeUseCase
94
- # def self.call(request)
95
- # data = perform_work
96
- # request.success(data)
97
- # end
98
- # end
99
- #
100
- # class SomeObserver
101
- # def self.call(response)
102
- # # do something
103
- # end
104
- # end
105
- # end
106
- #
107
118
  # dispatcher = Substation::Dispatcher.coerce({
108
119
  # :some_use_case => {
109
120
  # :action => App::SomeUseCase,
110
121
  # :observer => App::SomeObserver
111
122
  # }
112
- # })
123
+ # }, env)
113
124
  #
114
125
  # @example with Symbol keys and proc handlers
115
126
  #
@@ -118,11 +129,14 @@ module Substation
118
129
  # :action => Proc.new { |request| request.success(:foo) },
119
130
  # :observer => Proc.new { |response| do_something }
120
131
  # }
121
- # })
132
+ # }, env)
122
133
  #
123
134
  # @param [Hash<#to_sym, Object>] config
124
135
  # the action configuration
125
136
  #
137
+ # @param [Object] env
138
+ # the application environment
139
+ #
126
140
  # @return [Dispatcher]
127
141
  # the coerced instance
128
142
  #
@@ -133,8 +147,8 @@ module Substation
133
147
  # if action or observer handlers are not coercible
134
148
  #
135
149
  # @api public
136
- def self.coerce(config)
137
- new(normalize_config(config))
150
+ def self.coerce(config, env)
151
+ new(normalize_config(config), env)
138
152
  end
139
153
 
140
154
  # Normalize the given +config+
@@ -160,7 +174,7 @@ module Substation
160
174
 
161
175
  private_class_method :normalize_config
162
176
 
163
- include Concord.new(:actions)
177
+ include Concord.new(:actions, :env)
164
178
  include Adamantium
165
179
 
166
180
  # Invoke the action identified by +name+
@@ -169,8 +183,8 @@ module Substation
169
183
  #
170
184
  # module App
171
185
  # class Environment
172
- # def initialize(dispatcher, logger)
173
- # @dispatcher, @logger = dispatcher, logger
186
+ # def initialize(storage, logger)
187
+ # @storage, @logger = storage, logger
174
188
  # end
175
189
  # end
176
190
  #
@@ -182,11 +196,10 @@ module Substation
182
196
  # end
183
197
  # end
184
198
  #
185
- # dispatcher = Substation::Dispatcher.coerce({
186
- # :some_use_case => { :action => App::SomeUseCase }
187
- # })
188
- #
189
- # env = App::Environment.new(dispatcher, Logger.new($stdout))
199
+ # storage = SomeStorageAbstraction.new
200
+ # env = App::Environment.new(storage, Logger.new($stdout))
201
+ # config = { :some_use_case => { :action => App::SomeUseCase } }
202
+ # dispatcher = Substation::Dispatcher.coerce(config, env)
190
203
  #
191
204
  # response = dispatcher.call(:some_use_case, :some_input, env)
192
205
  # response.success? # => true
@@ -197,9 +210,6 @@ module Substation
197
210
  # @param [Object] input
198
211
  # the input model instance to pass to the action
199
212
  #
200
- # @param [Object] env
201
- # the application environment
202
- #
203
213
  # @return [Response]
204
214
  # the response returned when calling the action
205
215
  #
@@ -207,7 +217,7 @@ module Substation
207
217
  # if no action is registered for +name+
208
218
  #
209
219
  # @api public
210
- def call(name, input, env)
220
+ def call(name, input)
211
221
  fetch(name).call(Request.new(env, input))
212
222
  end
213
223
 
@@ -216,6 +226,12 @@ module Substation
216
226
  # @example
217
227
  #
218
228
  # module App
229
+ # class Environment
230
+ # def initialize(storage, logger)
231
+ # @storage, @logger = storage, logger
232
+ # end
233
+ # end
234
+ #
219
235
  # class SomeUseCase
220
236
  # def self.call(request)
221
237
  # data = perform_work
@@ -224,9 +240,10 @@ module Substation
224
240
  # end
225
241
  # end
226
242
  #
227
- # dispatcher = Substation::Dispatcher.coerce({
228
- # :some_use_case => { :action => App::SomeUseCase }
229
- # })
243
+ # storage = SomeStorageAbstraction.new
244
+ # env = App::Environment.new(storage, Logger.new($stdout))
245
+ # config = { :some_use_case => { :action => App::SomeUseCase } }
246
+ # dispatcher = Substation::Dispatcher.coerce(config, env)
230
247
  #
231
248
  # dispatcher.action_names # => #<Set: {:some_use_case}>
232
249
  #
@@ -1,4 +1,4 @@
1
1
  module Substation
2
2
  # Gem version
3
- VERSION = '0.0.1'.freeze
3
+ VERSION = '0.0.2'.freeze
4
4
  end
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,3 @@
1
- require 'substation'
2
1
  require 'devtools/spec_helper'
3
2
 
4
3
  module Spec
@@ -28,6 +27,25 @@ module Spec
28
27
 
29
28
  end
30
29
 
30
+ if ENV['COVERAGE'] == 'true'
31
+ require 'simplecov'
32
+ require 'coveralls'
33
+
34
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
35
+ SimpleCov::Formatter::HTMLFormatter,
36
+ Coveralls::SimpleCov::Formatter
37
+ ]
38
+
39
+ SimpleCov.start do
40
+ command_name 'spec:unit'
41
+ add_filter 'config'
42
+ add_filter 'spec'
43
+ minimum_coverage 100
44
+ end
45
+ end
46
+
47
+ require 'substation'
48
+
31
49
  include Substation
32
50
 
33
51
  RSpec.configure do |config|
@@ -6,8 +6,9 @@ describe Dispatcher, '#action_names' do
6
6
 
7
7
  subject { object.action_names }
8
8
 
9
- let(:object) { described_class.new(config) }
9
+ let(:object) { described_class.new(config, env) }
10
10
  let(:config) { { :test => { :action => Spec::Action::Success } } }
11
+ let(:env) { mock }
11
12
 
12
13
  it { should eql(Set[ :test ]) }
13
14
  end
@@ -4,9 +4,9 @@ require 'spec_helper'
4
4
 
5
5
  describe Dispatcher, '#call' do
6
6
 
7
- subject { object.call(action_name, input, env) }
7
+ subject { object.call(action_name, input) }
8
8
 
9
- let(:object) { described_class.coerce(config) }
9
+ let(:object) { described_class.coerce(config, env) }
10
10
  let(:config) { { 'test' => { 'action' => 'Spec::Action::Success' } } }
11
11
  let(:request) { Request.new(env, input) }
12
12
  let(:input) { mock }
@@ -4,15 +4,17 @@ require 'spec_helper'
4
4
 
5
5
  describe Dispatcher, '.coerce' do
6
6
 
7
- subject { described_class.coerce(config) }
7
+ subject { described_class.coerce(config, env) }
8
8
 
9
9
  let(:config) {{
10
10
  'test' => { 'action' => 'Spec::Action::Success' }
11
11
  }}
12
12
 
13
+ let(:env) { mock }
14
+
13
15
  let(:coerced) {{
14
16
  :test => described_class::Action.coerce(:action => 'Spec::Action::Success')
15
17
  }}
16
18
 
17
- it { should eql(described_class.new(coerced)) }
19
+ it { should eql(described_class.new(coerced, env)) }
18
20
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: substation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-14 00:00:00.000000000 Z
12
+ date: 2013-05-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: adamantium
@@ -105,6 +105,7 @@ files:
105
105
  - .rspec
106
106
  - .rvmrc
107
107
  - .travis.yml
108
+ - Changelog.md
108
109
  - Gemfile
109
110
  - Gemfile.devtools
110
111
  - Guardfile