substation 0.0.1

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 (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