Skip to content

Instantly share code, notes, and snippets.

@snusnu
Last active December 17, 2015 15:19
Show Gist options
  • Save snusnu/5631008 to your computer and use it in GitHub Desktop.
Save snusnu/5631008 to your computer and use it in GitHub Desktop.
Example dummy app that uses substation, aequitas, ducktrap, anima and mustache. On rack.
require 'pp'
require 'adamantium'
require 'equalizer'
require 'concord'
require 'abstract_type'
require 'aequitas'
require 'ducktrap'
require 'anima'
require 'substation'
require 'multi_json'
require 'mustache'
require 'rack'
class Demo
class Action
include AbstractType
def self.call(request)
new(request).call
end
def initialize(request)
@request = request
@env = @request.env
@input = @request.input
end
abstract_method :call
private
attr_reader :request
attr_reader :env
attr_reader :input
def success(data)
@request.success(data)
end
def error(data)
@request.error(data)
end
end
module Models
class Person
include Anima.new(:name)
end
end
module Validators
NEW_PERSON = Aequitas::Validator.build do
validates_presence_of :name
end
end
module Actions
class CreatePerson < Demo::Action
def call
puts "Executing #{self.class}##{__method__}"
success(request.input)
end
end
end
module Web
class Processor
include AbstractType
include Equalizer.new(:handler)
include Adamantium::Flat
def initialize(handler = nil)
@handler = handler
end
abstract_method :call
abstract_method :result
protected
attr_reader :handler
class Incoming < self
include Substation::Chain::Incoming
end
class Outgoing < self
include Substation::Chain::Outgoing
private
def respond_with(response, output)
response.class.new(response.request, output)
end
end
end
class Sanitizer < Processor::Incoming
def call(request)
result = handler.run(request.input)
if result.successful?
request.success(result.output)
else
request.error(result.output.pretty_dump)
end
end
end
class Validator < Processor::Incoming
def call(request)
result = handler.call(request.input)
if result.valid?
request.success(request.input)
else
request.error(result.violations)
end
end
end
class Pivot < Processor
include Substation::Chain::Pivot
def call(request)
handler.call(request)
end
end
class Presenter < Processor::Outgoing
def call(response)
respond_with(response, handler.new(response.output))
end
end
class Serializer < Processor::Outgoing
module JSON
class Model < Serializer
def call(response)
output = response.output
data = output.class.attributes_hash(output)
respond_with(response, MultiJson.dump(data))
end
end
class Custom < Serializer
def call(response)
respond_with(response, handler.new(response.output).to_json)
end
end
end
end
class Renderer < Processor::Outgoing
def call(response)
respond_with(response, Mustache.render(handler, response.output))
end
end
module Sanitizers
NEW_PERSON = Ducktrap.build do
primitive(Hash)
hash_transform do
dump_key(:name) do
fetch_key('name')
primitive(String)
end
end
anima_load(Models::Person)
end
end
module Presenters
class Base
include Adamantium::Flat
def initialize(data)
@data = data
end
def method_missing(method, *args, &block)
@data.send(method, *args, &block)
end
def respond_to?(method)
super || @data.respond_to?(method)
end
end # class Base
class Person < Base
def name
"Mr. #{super}"
end
end
end
module Views
class Person
TEMPLATE = "<html><body>Hey {{name}}</body></html>"
end
end
module Serializers
module JSON
class Person < Presenters::Base
def to_json(*args)
MultiJson.dump({ 'full_name' => name }, :pretty => true)
end
end
end
end
module Actions
CREATE_PERSON = Substation::Chain.new [
Sanitizer.new(Sanitizers::NEW_PERSON),
Validator.new(Demo::Validators::NEW_PERSON),
Pivot.new(Demo::Actions::CreatePerson),
]
module HTML
CREATE_PERSON = Substation::Chain.new [
Actions::CREATE_PERSON,
Presenter.new(Presenters::Person),
Renderer.new(Views::Person::TEMPLATE)
]
end
module JSON
CREATE_PERSON = Substation::Chain.new [
Actions::CREATE_PERSON,
Serializer::JSON::Model.new
]
CREATE_PERSON_DETAILED = Substation::Chain.new [
Actions::CREATE_PERSON,
Presenter.new(Presenters::Person),
Serializer::JSON::Custom.new(Serializers::JSON::Person)
]
end
end
end
include Concord.new(:dispatcher)
include Adamantium::Flat
def call(name, input = nil)
@dispatcher.call(name, input)
end
ACTIONS = {
:HTML => {
:create_person => Web::Actions::HTML::CREATE_PERSON,
},
:JSON => {
:create_person => Web::Actions::JSON::CREATE_PERSON,
:create_person_detailed => Web::Actions::JSON::CREATE_PERSON_DETAILED,
}
}.freeze
env = Object.new # to be replaced with a proper env
# Two applications, serving either HTML or JSON
HTML = new(Substation::Dispatcher.coerce(ACTIONS[:HTML], env))
JSON = new(Substation::Dispatcher.coerce(ACTIONS[:JSON], env))
end
if __FILE__ == $0
input = { 'name' => 'John' }
response = Demo::HTML.call(:create_person, input)
puts response.success? ? 'SUCCESS' : 'FAILURE'
puts response.output
response = Demo::JSON.call(:create_person, input)
puts response.success? ? 'SUCCESS' : 'FAILURE'
puts response.output
response = Demo::JSON.call(:create_person_detailed, input)
puts response.success? ? 'SUCCESS' : 'FAILURE'
puts response.output
# ruby-1.9.3-p392@demo mungo:demo snusnu$ ruby demo.rb
# Executing Demo::Actions::CreatePerson#call
# SUCCESS
# <html><body>Hey Mr. John</body></html>
# Executing Demo::Actions::CreatePerson#call
# SUCCESS
# {"name":"John"}
# Executing Demo::Actions::CreatePerson#call
# SUCCESS
# {
# "full_name":"Mr. John"
# }
endpoint = lambda { |env|
response = Demo::HTML.call(:create_person, input)
status = response.success? ? 200 : 500
header = {"Content-Type" => "text/html"}
pp env
[ status, header, [ response.output ] ]
}
Rack::Handler::WEBrick.run endpoint
# [2013-05-23 19:20:24] INFO WEBrick 1.3.1
# [2013-05-23 19:20:24] INFO ruby 1.9.3 (2013-02-22) [x86_64-darwin10.8.0]
# [2013-05-23 19:20:24] INFO WEBrick::HTTPServer#start: pid=32645 port=8080
# Executing Demo::Actions::CreatePerson#call
# localhost - - [23/May/2013:19:20:28 CEST] "GET / HTTP/1.1" 200 23
# - -> /
# Executing Demo::Actions::CreatePerson#call
# localhost - - [23/May/2013:19:20:28 CEST] "GET /favicon.ico HTTP/1.1" 200 23
# - -> /favicon.ico
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment