Skip to content

Commit

Permalink
Merge pull request #4 from snusnu/chain-dsl
Browse files Browse the repository at this point in the history
Introduce a DSL to construct a chain
  • Loading branch information
snusnu committed Jun 17, 2013
2 parents d4a6205 + 8d91705 commit 1442978
Show file tree
Hide file tree
Showing 26 changed files with 661 additions and 12 deletions.
1 change: 1 addition & 0 deletions .ruby-gemset
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
substation
1 change: 1 addition & 0 deletions .ruby-version
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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: 1 addition & 1 deletion config/flay.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
---
threshold: 6
total_score: 69
total_score: 98
2 changes: 1 addition & 1 deletion config/flog.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
threshold: 10.9
threshold: 13.3
6 changes: 5 additions & 1 deletion config/reek.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,24 @@ FeatureEnvy:
exclude:
- Substation::Chain#call # loops over instance state
- Substation::Chain::Incoming#result # defined in a module
- Substation::Chain::Outgoing#respond_with #defined in a module
IrresponsibleModule:
enabled: true
exclude: []
LongParameterList:
enabled: true
exclude:
- Substation::Dispatcher#call
- Substation::Chain::DSL::Builder#define_dsl_method
max_params: 2
LongYieldList:
enabled: true
exclude: []
max_params: 2
NestedIterators:
enabled: true
exclude: []
exclude:
- Substation::Chain::DSL::Builder#define_dsl_method
max_allowed_nesting: 1
ignore_iterators: []
NilCheck:
Expand Down Expand Up @@ -106,4 +109,5 @@ UtilityFunction:
enabled: true
exclude:
- Substation::Chain::Incoming#result # defined in a module
- Substation::Chain::Outgoing#respond_with # defined in a module
max_helper_calls: 0
2 changes: 2 additions & 0 deletions lib/substation.rb
Original file line number Diff line number Diff line change
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'
41 changes: 39 additions & 2 deletions lib/substation/chain.rb
Original file line number Diff line number Diff line change
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,36 @@ module Outgoing
def result(response)
response
end

private

# Build a new {Response} based on +response+ and +output+
#
# @param [Response] response
# the original response
#
# @param [Object] output
# the data to be wrapped within the new {Response}
#
# @return [Response]
#
# @api 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 +179,23 @@ def call(request)
}
end

# Iterate over all processors
#
# @param [Proc] block
# a block passed to {#handlers} each method
#
# @yield [handler]
#
# @yieldparam [#call] handler
# each handler in the chain
#
# @return [self]
#
# @api private
def each(&block)
return to_enum unless block
handlers.each(&block)
self
end
end # class Chain
end # module Substation
165 changes: 165 additions & 0 deletions lib/substation/chain/dsl.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
module Substation

class Chain

# Build a new chain instance
#
# @param [DSL] dsl
# the dsl klass to use for defining the chain
#
# @param [Chain] other
# another chain to build on top of
#
# @param [Proc] block
# a block to instance_eval in the context of +dsl+
#
# @return [Chain]
#
# @api private
def self.build(dsl, other, &block)
new(dsl.processors(other, &block))
end

# The DSL class used to define chains in an {Environment}
class DSL

# The class that builds a DSL class suitable for an {Environment}
class Builder
include Adamantium::Flat

# Build a new {DSL} subclass targeted for an {Environment}
#
# @param [Hash<Symbol, #call>] registry
# the registry of processors used in an {Environment}
#
# @return [Class<DSL>]
#
# @api private
def self.call(registry)
new(registry).dsl
end

# The built DSL subclass
#
# @return [Class<DSL>]
#
# @api private
attr_reader :dsl

# Initialize a new instance
#
# @param [Hash<Symbol, #call>] registry
# the registry of processors used in an {Environment}
#
# @return [undefined]
#
# @api private
def initialize(registry)
@registry = registry
@dsl = compile_dsl
end

private

# Compile a new DSL class
#
# @return [Class<DSL>]
#
# @api private
def compile_dsl
@registry.each_with_object(Class.new(DSL)) { |(name, processor), dsl|
define_dsl_method(name, processor, dsl)
}
end


# Define a new instance method on the +dsl+ class
#
# @param [Symbol] name
# the name of the method
#
# @param [#call] processor
# the processor to use within the chain
#
# @param [Class<DSL>] dsl
# the {DSL} subclass to define the method on
#
# @return [undefined]
#
# @api private
def define_dsl_method(name, processor, dsl)
dsl.class_eval do
define_method(name) { |*args| use(processor.new(*args)) }
end
end

end # class Builder

# The processors to be used within a {Chain}
#
# @return [Array<#call>]
#
# @api private
attr_reader :processors

# The processors to be used within a {Chain}
#
# @param [Chain] chain
# the chain to build on top of
#
# @param [Proc] block
# a block to be instance_eval'ed
#
# @return [Array<#call>]
#
# @api private
def self.processors(chain, &block)
new(chain, &block).processors
end

# Initialize a new instance
#
# @param [#each<#call>] processors
# an enumerable of processors to build on top of
#
# @param [Proc] block
# a block to be instance_eval'ed
#
# @return [undefined]
#
# @api private
def initialize(processors, &block)
@processors = []
chain(processors)
instance_eval(&block) if block
end

# Use the given +processor+ within a chain
#
# @param [#call] processor
# a processor to use within a chain
#
# @return [self]
#
# @api private
def use(processor)
@processors << processor
self
end

# Nest the given chain within another one
#
# @param [#each<#call>] other
# another chain to be nested within a chain
#
# @return [self]
#
# @api private
def chain(other)
other.each { |handler| use(handler) }
self
end

end # class DSL
end # class Chain
end # module Substation

0 comments on commit 1442978

Please sign in to comment.