substation 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +37 -0
  2. data/.rspec +4 -0
  3. data/.rvmrc +1 -0
  4. data/.travis.yml +16 -0
  5. data/Gemfile +8 -0
  6. data/Gemfile.devtools +60 -0
  7. data/Guardfile +18 -0
  8. data/LICENSE +20 -0
  9. data/README.md +322 -0
  10. data/Rakefile +6 -0
  11. data/TODO +0 -0
  12. data/config/devtools.yml +2 -0
  13. data/config/flay.yml +3 -0
  14. data/config/flog.yml +2 -0
  15. data/config/mutant.yml +3 -0
  16. data/config/reek.yml +103 -0
  17. data/config/yardstick.yml +2 -0
  18. data/lib/substation/dispatcher.rb +262 -0
  19. data/lib/substation/observer.rb +66 -0
  20. data/lib/substation/request.rb +69 -0
  21. data/lib/substation/response.rb +178 -0
  22. data/lib/substation/support/utils.rb +68 -0
  23. data/lib/substation/version.rb +4 -0
  24. data/lib/substation.rb +40 -0
  25. data/spec/spec_helper.rb +34 -0
  26. data/spec/unit/substation/dispatcher/action/call_spec.rb +23 -0
  27. data/spec/unit/substation/dispatcher/action/class_methods/coerce_spec.rb +46 -0
  28. data/spec/unit/substation/dispatcher/action_names_spec.rb +13 -0
  29. data/spec/unit/substation/dispatcher/call_spec.rb +47 -0
  30. data/spec/unit/substation/dispatcher/class_methods/coerce_spec.rb +18 -0
  31. data/spec/unit/substation/observer/chain/call_spec.rb +26 -0
  32. data/spec/unit/substation/observer/class_methods/coerce_spec.rb +33 -0
  33. data/spec/unit/substation/observer/null/call_spec.rb +12 -0
  34. data/spec/unit/substation/request/env_spec.rb +14 -0
  35. data/spec/unit/substation/request/error_spec.rb +15 -0
  36. data/spec/unit/substation/request/input_spec.rb +14 -0
  37. data/spec/unit/substation/request/success_spec.rb +15 -0
  38. data/spec/unit/substation/response/env_spec.rb +16 -0
  39. data/spec/unit/substation/response/failure/success_predicate_spec.rb +15 -0
  40. data/spec/unit/substation/response/input_spec.rb +16 -0
  41. data/spec/unit/substation/response/output_spec.rb +16 -0
  42. data/spec/unit/substation/response/success/success_predicate_spec.rb +15 -0
  43. data/spec/unit/substation/utils/class_methods/coerce_callable_spec.rb +34 -0
  44. data/spec/unit/substation/utils/class_methods/const_get_spec.rb +46 -0
  45. data/spec/unit/substation/utils/class_methods/symbolize_keys_spec.rb +20 -0
  46. data/substation.gemspec +25 -0
  47. metadata +177 -0
@@ -0,0 +1,34 @@
1
+ require 'substation'
2
+ require 'devtools/spec_helper'
3
+
4
+ module Spec
5
+
6
+ def self.response_data
7
+ :data
8
+ end
9
+
10
+ class Observer
11
+ def self.call(response)
12
+ end
13
+ end
14
+
15
+ class Action
16
+ class Success
17
+ def self.call(request)
18
+ request.success(Spec.response_data)
19
+ end
20
+ end
21
+
22
+ class Failure
23
+ def self.call(request)
24
+ request.error(Spec.response_data)
25
+ end
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+ include Substation
32
+
33
+ RSpec.configure do |config|
34
+ end
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Dispatcher::Action, '#call' do
6
+
7
+ subject { object.call(request) }
8
+
9
+ let(:object) { described_class.new(klass, observer) }
10
+ let(:klass) { mock }
11
+ let(:observer) { mock }
12
+ let(:request) { Request.new(env, input) }
13
+ let(:env) { mock }
14
+ let(:input) { mock }
15
+ let(:response) { mock }
16
+
17
+ before do
18
+ klass.should_receive(:call).with(request).and_return(response)
19
+ observer.should_receive(:call).with(response)
20
+ end
21
+
22
+ it { should eql(response) }
23
+ end
@@ -0,0 +1,46 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Dispatcher::Action, '.coerce' do
6
+
7
+ subject { described_class.coerce(config) }
8
+
9
+ let(:action) { Spec::Action::Success }
10
+ let(:coerced) { Dispatcher::Action.new(action, observer) }
11
+
12
+ context "when coercion is possible" do
13
+
14
+ let(:config) {{
15
+ :action => action,
16
+ :observer => observer_value
17
+ }}
18
+
19
+ before do
20
+ Observer.should_receive(:coerce).with(observer_value).and_return(observer)
21
+ Utils.should_receive(:coerce_callable).with(action).and_return(action)
22
+ end
23
+
24
+ context 'with an action and an observer' do
25
+ let(:observer_value) { observer }
26
+ let(:observer) { Spec::Observer }
27
+
28
+ it { should eql(coerced) }
29
+ end
30
+
31
+ context 'with an action and no observer' do
32
+ let(:observer_value) { nil }
33
+ let(:observer) { Observer::NULL }
34
+
35
+ it { should eql(coerced) }
36
+ end
37
+ end
38
+
39
+ context 'with no action' do
40
+ let(:config) { {} }
41
+
42
+ specify {
43
+ expect { subject }.to raise_error(described_class::MissingHandlerError)
44
+ }
45
+ end
46
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Dispatcher, '#action_names' do
6
+
7
+ subject { object.action_names }
8
+
9
+ let(:object) { described_class.new(config) }
10
+ let(:config) { { :test => { :action => Spec::Action::Success } } }
11
+
12
+ it { should eql(Set[ :test ]) }
13
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Dispatcher, '#call' do
6
+
7
+ subject { object.call(action_name, input, env) }
8
+
9
+ let(:object) { described_class.coerce(config) }
10
+ let(:config) { { 'test' => { 'action' => 'Spec::Action::Success' } } }
11
+ let(:request) { Request.new(env, input) }
12
+ let(:input) { mock }
13
+ let(:env) { mock }
14
+
15
+ let(:expected_response) do
16
+ Spec::Action::Success.call(request)
17
+ end
18
+
19
+ context 'when the action is registered' do
20
+ let(:action_name) { :test }
21
+
22
+ context 'without callbacks' do
23
+ it { should eql(expected_response) }
24
+ end
25
+
26
+ context 'with observers' do
27
+ let(:config) {
28
+ config = super()
29
+ config['test']['observer'] = 'Spec::Observer'
30
+ config
31
+ }
32
+
33
+ it 'should call callback with response' do
34
+ Spec::Observer.should_receive(:call).with(expected_response)
35
+ should eql(expected_response)
36
+ end
37
+ end
38
+ end
39
+
40
+ context 'when the action is not registered' do
41
+ let(:action_name) { :unknown }
42
+
43
+ specify do
44
+ expect { subject }.to raise_error(described_class::UnknownActionError)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Dispatcher, '.coerce' do
6
+
7
+ subject { described_class.coerce(config) }
8
+
9
+ let(:config) {{
10
+ 'test' => { 'action' => 'Spec::Action::Success' }
11
+ }}
12
+
13
+ let(:coerced) {{
14
+ :test => described_class::Action.coerce(:action => 'Spec::Action::Success')
15
+ }}
16
+
17
+ it { should eql(described_class.new(coerced)) }
18
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Observer::Chain, '#call' do
4
+ subject { object.call(response) }
5
+
6
+ let(:object) { Observer::Chain.new(observers) }
7
+
8
+ let(:response) { mock('Response') }
9
+ let(:observer_a) { mock('Observer A') }
10
+ let(:observer_b) { mock('Observer B') }
11
+
12
+ let(:observers) { [observer_a, observer_b] }
13
+
14
+ it_should_behave_like 'a command method'
15
+
16
+ before do
17
+ observer_a.stub(:call)
18
+ observer_b.stub(:call)
19
+ end
20
+
21
+ it 'should call observers' do
22
+ observer_a.should_receive(:call).with(response)
23
+ observer_b.should_receive(:call).with(response)
24
+ subject
25
+ end
26
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe Observer, '.coerce' do
4
+
5
+ subject { object.coerce(input) }
6
+
7
+ let(:object) { described_class }
8
+
9
+ context 'with nil input' do
10
+ let(:input) { nil }
11
+
12
+ it { should be(described_class::NULL) }
13
+ end
14
+
15
+ context 'with array input' do
16
+ let(:input) { ['Spec::Observer', nil] }
17
+
18
+ let(:observers) { [Spec::Observer, described_class::NULL] }
19
+
20
+ it { should eql(described_class::Chain.new(observers)) }
21
+ end
22
+
23
+ context 'with other input' do
24
+ let(:input) { mock }
25
+ let(:coerced) { mock }
26
+
27
+ before do
28
+ Utils.should_receive(:coerce_callable).with(input).and_return(coerced)
29
+ end
30
+
31
+ it { should eql(coerced) }
32
+ end
33
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe Observer::NULL, '#call' do
4
+ subject { object.call(input) }
5
+
6
+ let(:object) { described_class }
7
+ let(:input) { mock }
8
+
9
+ it_should_behave_like 'a command method'
10
+
11
+ it { should be_kind_of(Observer) }
12
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Request, '#env' do
6
+
7
+ subject { object.env }
8
+
9
+ let(:object) { described_class.new(env, input) }
10
+ let(:env) { mock }
11
+ let(:input) { mock }
12
+
13
+ it { should equal(env) }
14
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Request, '#error' do
6
+
7
+ subject { object.error(output) }
8
+
9
+ let(:object) { described_class.new(env, input) }
10
+ let(:env) { mock }
11
+ let(:input) { mock }
12
+ let(:output) { mock }
13
+
14
+ it { should eql(Response::Failure.new(object, output)) }
15
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Request, '#input' do
6
+
7
+ subject { object.input }
8
+
9
+ let(:object) { described_class.new(env, input) }
10
+ let(:env) { mock }
11
+ let(:input) { mock }
12
+
13
+ it { should equal(input) }
14
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Request, '#success' do
6
+
7
+ subject { object.success(output) }
8
+
9
+ let(:object) { described_class.new(env, input) }
10
+ let(:env) { mock }
11
+ let(:input) { mock }
12
+ let(:output) { mock }
13
+
14
+ it { should eql(Response::Success.new(object, output)) }
15
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Response, '#env' do
6
+
7
+ subject { object.env }
8
+
9
+ let(:object) { Class.new(described_class).new(request, output) }
10
+ let(:request) { Request.new(env, input) }
11
+ let(:env) { mock }
12
+ let(:input) { mock }
13
+ let(:output) { mock }
14
+
15
+ it { should equal(env) }
16
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Response::Failure, '#success?' do
6
+ subject { object.success? }
7
+
8
+ let(:object) { described_class.new(request, output) }
9
+ let(:request) { Request.new(env, input) }
10
+ let(:env) { mock }
11
+ let(:input) { mock }
12
+ let(:output) { mock }
13
+
14
+ it { should be(false) }
15
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Response, '#input' do
6
+
7
+ subject { object.input }
8
+
9
+ let(:object) { Class.new(described_class).new(request, output) }
10
+ let(:request) { Request.new(env, input) }
11
+ let(:env) { mock }
12
+ let(:input) { mock }
13
+ let(:output) { mock }
14
+
15
+ it { should equal(input) }
16
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Response, '#output' do
6
+
7
+ subject { object.output }
8
+
9
+ let(:object) { Class.new(described_class).new(request, output) }
10
+ let(:request) { Request.new(env, input) }
11
+ let(:env) { mock }
12
+ let(:input) { mock }
13
+ let(:output) { mock }
14
+
15
+ it { should equal(output) }
16
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Response::Success, '#success?' do
6
+ subject { object.success? }
7
+
8
+ let(:object) { described_class.new(request, output) }
9
+ let(:request) { Request.new(env, input) }
10
+ let(:env) { mock }
11
+ let(:input) { mock }
12
+ let(:output) { mock }
13
+
14
+ it { should be(true) }
15
+ end
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Utils, '.coerce_callable' do
6
+
7
+ subject { described_class.coerce_callable(handler) }
8
+
9
+ context "with a String handler" do
10
+ let(:handler) { 'Spec::Action::Success' }
11
+
12
+ it { should be(Spec::Action::Success) }
13
+ end
14
+
15
+ context "with a Symbol handler" do
16
+ let(:handler) { :'Spec::Action::Success' }
17
+
18
+ it { should be(Spec::Action::Success) }
19
+ end
20
+
21
+ context "with a Proc handler" do
22
+ let(:handler) { Proc.new { |response| respone } }
23
+
24
+ it { should be(handler) }
25
+ end
26
+
27
+ context "with an unsupported handler" do
28
+ let(:handler) { mock }
29
+
30
+ specify do
31
+ expect { subject }.to raise_error(ArgumentError)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,46 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Utils, '.const_get' do
6
+
7
+ subject { described_class.const_get(name) }
8
+
9
+ context "with a toplevel constant" do
10
+ context "given as String" do
11
+ let(:name) { 'Substation' }
12
+
13
+ it { should == Substation }
14
+ end
15
+
16
+ context "given as Symbol" do
17
+ let(:name) { :Substation }
18
+
19
+ it { should == Substation }
20
+ end
21
+ end
22
+
23
+ context "with a FQN toplevel constant" do
24
+ let(:name) { '::Substation' }
25
+
26
+ it { should == Substation }
27
+ end
28
+
29
+ context "with a nested constant" do
30
+ let(:name) { 'Substation::Request' }
31
+
32
+ it { should == Substation::Request }
33
+ end
34
+
35
+ context "with a non-existant nested constant" do
36
+ let(:name) { 'Substation::Foo' }
37
+
38
+ before do
39
+ Substation.should_receive(:const_missing).with('Foo').and_raise(NameError)
40
+ end
41
+
42
+ specify do
43
+ expect { subject }.to raise_error(NameError)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Utils, '.symbolize_keys' do
6
+
7
+ subject { described_class.symbolize_keys(hash) }
8
+
9
+ context "with no nested hash" do
10
+ let(:hash) { { 'foo' => 'bar' } }
11
+
12
+ it { should == { :foo => 'bar'} }
13
+ end
14
+
15
+ context "with a nested hash" do
16
+ let(:hash) { { 'foo' => { 'bar' => 'baz' } } }
17
+
18
+ it { should == { :foo => { :bar => 'baz'} } }
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require File.expand_path('../lib/substation/version', __FILE__)
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "substation"
7
+ gem.version = Substation::VERSION.dup
8
+ gem.authors = [ "Martin Gamsjaeger (snusnu)" ]
9
+ gem.email = [ "gamsnjaga@gmail.com" ]
10
+ gem.description = "Implement application boundary interfaces with dedicated classes"
11
+ gem.summary = "Think of it as a domain level request router. It assumes that every usecase in your application has a name and is implemented in a dedicated action handler (class)."
12
+ gem.homepage = "https://github.com/snusnu/substation"
13
+
14
+ gem.require_paths = [ "lib" ]
15
+ gem.files = `git ls-files`.split("\n")
16
+ gem.test_files = `git ls-files -- {spec}/*`.split("\n")
17
+ gem.extra_rdoc_files = %w[LICENSE README.md TODO]
18
+
19
+ gem.add_dependency 'adamantium', '~> 0.0.7'
20
+ gem.add_dependency 'equalizer', '~> 0.0.5'
21
+ gem.add_dependency 'abstract_type', '~> 0.0.5'
22
+ gem.add_dependency 'concord', '~> 0.0.3'
23
+
24
+ gem.add_development_dependency 'bundler', '~> 1.3.5'
25
+ end