Skip to content

Commit

Permalink
Merge 6c2b315 into d4a6205
Browse files Browse the repository at this point in the history
  • Loading branch information
snusnu committed Jun 17, 2013
2 parents d4a6205 + 6c2b315 commit a1aebe3
Show file tree
Hide file tree
Showing 24 changed files with 454 additions and 9 deletions.
1 change: 1 addition & 0 deletions .ruby-gemset
@@ -0,0 +1 @@
substation
1 change: 1 addition & 0 deletions .ruby-version
@@ -0,0 +1 @@
1.9.3
1 change: 0 additions & 1 deletion .rvmrc

This file was deleted.

7 changes: 1 addition & 6 deletions .travis.yml
Expand Up @@ -4,13 +4,8 @@ bundler_args: --without yard guard benchmarks
script: "bundle exec rake ci"
rvm:
- 1.9.3
- 1.9.2
- 1.8.7
- ree
- ruby-head
- 2.0.0
- ruby-head
- jruby-19mode
- jruby-18mode
- jruby-head
- rbx-19mode
- rbx-18mode
2 changes: 2 additions & 0 deletions lib/substation.rb
Expand Up @@ -37,5 +37,7 @@ module Substation
require 'substation/response'
require 'substation/observer'
require 'substation/chain'
require 'substation/chain/dsl'
require 'substation/environment'
require 'substation/dispatcher'
require 'substation/support/utils'
17 changes: 15 additions & 2 deletions lib/substation/chain.rb
Expand Up @@ -72,8 +72,6 @@ module Incoming

# The request passed on to the next handler in a {Chain}
#
# @example
#
# @param [Response] response
# the response returned from the previous handler in a {Chain}
#
Expand Down Expand Up @@ -101,15 +99,25 @@ module Outgoing
def result(response)
response
end

private

def respond_with(response, output)
response.class.new(response.request, output)
end
end

# Supports chaining the {Pivot} handler
Pivot = Outgoing

include Enumerable
include Concord.new(:handlers)
include Adamantium::Flat
include Pivot # allow nesting of chains

# Empty chain
EMPTY = Class.new(self).new([])

# Call the chain
#
# Invokes all handlers and returns either the first
Expand Down Expand Up @@ -160,5 +168,10 @@ def call(request)
}
end

def each(&block)
return to_enum unless block
handlers.each(&block)
self
end
end # class Chain
end # module Substation
63 changes: 63 additions & 0 deletions lib/substation/chain/dsl.rb
@@ -0,0 +1,63 @@
module Substation

class Chain

def self.build(dsl, other, &block)
new(dsl.processors(other, &block))
end

class DSL

class Builder
include Adamantium::Flat

def self.call(registry)
new(registry).dsl
end

attr_reader :dsl

def initialize(registry)
@registry = registry
@dsl = compile_dsl
end

private

def compile_dsl
@registry.each_with_object(Class.new(DSL)) { |(name, processor), dsl|
define_dsl_method(name, processor, dsl)
}
end

def define_dsl_method(name, processor, dsl)
dsl.class_eval do
define_method(name) { |*args| use(processor.new(*args)) }
end
end
end

attr_reader :processors

def self.processors(chain, &block)
new(chain, &block).processors
end

def initialize(processors, &block)
@processors = []
chain(processors)
instance_eval(&block) if block
end

def use(processor)
@processors << processor
self
end

def chain(other)
other.each { |handler| use(handler) }
self
end
end
end
end
44 changes: 44 additions & 0 deletions lib/substation/environment.rb
@@ -0,0 +1,44 @@
module Substation

class Environment

class DSL

attr_reader :registry

def self.registry(&block)
new(&block).registry
end

def initialize(&block)
@registry = {}
instance_eval(&block) if block
end

def register(name, processor)
@registry[name] = processor
self
end
end

include Equalizer.new(:registry)
include Adamantium::Flat

def self.build(&block)
new(DSL.registry(&block))
end

def initialize(registry)
@registry = registry
@chain_dsl = Chain::DSL::Builder.call(@registry)
end

def chain(other = Chain::EMPTY, &block)
Chain.build(@chain_dsl, other, &block)
end

protected

attr_reader :registry
end
end
8 changes: 8 additions & 0 deletions spec/spec_helper.rb
@@ -1,5 +1,7 @@
require 'devtools/spec_helper'

require 'concord' # makes spec setup easier

module Spec

def self.response_data
Expand All @@ -25,6 +27,12 @@ def self.call(request)
end
end

class Processor
include Concord.new(:handler)
end

FAKE_HANDLER = Object.new

end

if ENV['COVERAGE'] == 'true'
Expand Down
Empty file added spec/support/app.rb
Empty file.
31 changes: 31 additions & 0 deletions spec/unit/substation/chain/class_methods/build_spec.rb
@@ -0,0 +1,31 @@
require 'spec_helper'

describe Chain, '.build' do

context "when a block is given" do
subject { described_class.build(dsl, other, &block) }

let(:dsl) { Chain::DSL::Builder.call(registry) }
let(:registry) { { :test => Spec::Processor } }
let(:other) { [ processor ] }
let(:processor) { Spec::Processor.new(Spec::FAKE_HANDLER) }
let(:block) { lambda { |_| test(Spec::FAKE_HANDLER) } }

let(:expected) { Chain.new(dsl.processors(other, &block)) }

it { should eql(expected) }
end

context "when no block is given" do
subject { described_class.build(dsl, other) }

let(:dsl) { Chain::DSL::Builder.call(registry) }
let(:registry) { { :test => Spec::Processor } }
let(:other) { [ processor ] }
let(:processor) { Spec::Processor.new(Spec::FAKE_HANDLER) }

let(:expected) { Chain.new(dsl.processors(other)) }

it { should eql(expected) }
end
end
19 changes: 19 additions & 0 deletions spec/unit/substation/chain/dsl/builder/class_methods/call_spec.rb
@@ -0,0 +1,19 @@
# encoding: utf-8

require 'spec_helper'

describe Chain::DSL::Builder, '.call' do
subject { described_class.call(registry) }

let(:registry) { { :test => Spec::Processor } }

let(:builder) { mock(:dsl => dsl) }
let(:dsl) { mock }

before do
described_class.should_receive(:new).with(registry).and_return(builder)
builder.should_receive(:dsl).and_return(dsl)
end

it { should be(dsl) }
end
18 changes: 18 additions & 0 deletions spec/unit/substation/chain/dsl/builder/dsl_spec.rb
@@ -0,0 +1,18 @@
# encoding: utf-8

require 'spec_helper'

describe Chain::DSL::Builder, '#dsl' do
subject { dsl.new(processors, &block) }

let(:dsl) { builder.dsl }
let(:builder) { described_class.new(registry) }
let(:registry) { { :test => Spec::Processor } }
let(:processors) { [] }
let(:block) { lambda { |_| test(Spec::FAKE_HANDLER) } }
let(:processor) { Spec::Processor.new(Spec::FAKE_HANDLER) }

it { should be_a(Chain::DSL) }

its(:processors) { should include(processor) }
end
14 changes: 14 additions & 0 deletions spec/unit/substation/chain/dsl/chain_spec.rb
@@ -0,0 +1,14 @@
# encoding: utf-8

require 'spec_helper'

describe Chain::DSL, '#chain' do
subject { object.chain(other) }

let(:object) { described_class.new(chain) }
let(:chain) { Chain::EMPTY }
let(:other) { [ processor ] }
let(:processor) { Spec::Processor.new(Spec::FAKE_HANDLER) }

its(:processors) { should include(processor) }
end
23 changes: 23 additions & 0 deletions spec/unit/substation/chain/dsl/class_methods/processors_spec.rb
@@ -0,0 +1,23 @@
# encoding: utf-8

require 'spec_helper'

describe Chain::DSL, '.processors' do

let(:chain) { Chain::EMPTY }

context "and a block is given" do
subject { described_class.processors(chain, &block) }

let(:block) { lambda { |_| use(Spec::Processor.new(Spec::FAKE_HANDLER)) } }
let(:processor) { Spec::Processor.new(Spec::FAKE_HANDLER) }

it { should include(processor) }
end

context "and no block is given" do
subject { described_class.processors(chain) }

it { should be_empty }
end
end
23 changes: 23 additions & 0 deletions spec/unit/substation/chain/dsl/processors_spec.rb
@@ -0,0 +1,23 @@
# encoding: utf-8

require 'spec_helper'

describe Chain::DSL, '#processors' do
subject { object.processors }

let(:chain) { Chain::EMPTY }

context "and a block is given" do
let(:object) { described_class.new(chain, &block) }
let(:block) { lambda { |_| use(Spec::Processor.new(Spec::FAKE_HANDLER)) } }
let(:processor) { Spec::Processor.new(Spec::FAKE_HANDLER) }

it { should include(processor) }
end

context "and no block is given" do
let(:object) { described_class.new(chain) }

it { should be_empty }
end
end
13 changes: 13 additions & 0 deletions spec/unit/substation/chain/dsl/use_spec.rb
@@ -0,0 +1,13 @@
# encoding: utf-8

require 'spec_helper'

describe Chain::DSL, '#use' do
subject { object.use(processor) }

let(:object) { described_class.new(chain) }
let(:chain) { Chain::EMPTY }
let(:processor) { Spec::Processor.new(Spec::FAKE_HANDLER) }

its(:processors) { should include(processor) }
end
46 changes: 46 additions & 0 deletions spec/unit/substation/chain/each_spec.rb
@@ -0,0 +1,46 @@
# encoding: utf-8

require 'spec_helper'

describe Chain, '#each' do
subject { object.each { |tuple| yields << processor } }

let(:object) { described_class.new(processors) }
let(:processors) { [ processor ] }
let(:processor) { Spec::Processor.new(Spec::FAKE_HANDLER) }
let(:yields) { [] }

before do
object.should be_instance_of(described_class)
end

it_should_behave_like 'an #each method'

it 'yields only processors' do
subject
yields.each { |processor| processor.should be_instance_of(Spec::Processor) }
end

it 'yields only processors with the expected handler' do
expect { subject }.to change { yields.dup }.
from([]).
to([ processor ])
end
end

describe Chain do
subject { described_class.new(processors) }

let(:processors) { [ processor ] }
let(:processor) { Spec::Processor.new(Spec::FAKE_HANDLER) }

before do
subject.should be_instance_of(described_class)
end

it { should be_kind_of(Enumerable) }

it 'case matches Enumerable' do
(Enumerable === subject).should be(true)
end
end

0 comments on commit a1aebe3

Please sign in to comment.