Last active
December 17, 2015 15:19
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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