Skip to content

Instantly share code, notes, and snippets.

@cloudhead
Created June 6, 2009 22:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cloudhead/125026 to your computer and use it in GitHub Desktop.
Save cloudhead/125026 to your computer and use it in GitHub Desktop.
require 'json'
def metaclass; class << self; self; end; end
def meta_eval &blk; metaclass.instance_eval &blk; end
# Adds methods to a metaclass
def metadef name, &blk
meta_eval { define_method name, &blk }
end
class String
def / s
end
end
class Symbol
def / s
array = [self, s]
def array./ s
self << s
end
return array
end
end
module Journey
def self.begin name
Application.new
end
class Application
def call env
@request = Rack::Request.new env
@response = Rack::Response.new
@journey = Route.new @request.request_method, @request.path_info
@output = @journey.begin @request.params
if @output[:body]
@response.body = [ @output[:body].to_json ]
@response['Content-Length'] = @response.body.first.size.to_s
end
@response.status = @output[:status].to_s
@response['Content-Type'] = 'application/json'
@response.finish
end
end
class Route
def initialize method, path
@method, @path = method, path.split('/') - ['']
end
def begin input
puts "==========="
puts "LOOKING FOR: #{@path}"
puts "==========="
route = Map.find(@method, @path, input) || Map.find(@method, Map.expand(@path), input)
puts "NOT FOUND" if route.nil?
route.call unless route.nil?
end
end
module Map
SLUG = /[-a-z0-9]+/
NUMBER = /[0-9]+/
@trail = []
@default = []
@routes = { get: [], put: [], post: [], delete: [] }
@input = {}
class << self
attr_accessor :routes, :input, :trail, :default
end
def path name
name = name.to_s
Map.trail << name
puts ""
if block_given?
yield
Map.trail.pop
end
end
def resource *path
path = [path].flatten
# puts "PATH: #{path}"
path.each do |s|
Map.trail << s.to_s << SLUG
Map.default << s.to_s unless Map.default.frozen?
end
puts "TRAIL: #{Map.trail}"
if block_given?
yield
Map.default.freeze
Map.trail.pop(path.size * 2)
else
# Default routes
end
end
def input
Map.input
end
def route method, *patterns, &block
conds = []
patterns << '/' if patterns.empty?
puts "PATTERNS #{patterns}"
patterns.each do |pattern|
puts "ROUTE: #{method.upcase} #{Map.trail} #{pattern}"
pattern = case pattern
when Symbol
SLUG
when String
pattern.gsub(/^\/?(.*?)\/?$/, '\1')
else
raise ArgumentError
end
pattern = Map.trail.select {|i| i.is_a? String }.zip( Map.trail.select{|i| i.is_a? Regexp }).collect do |a, b|
{a => b}
end << pattern
puts "ROUTE-MERGED: #{pattern - [""]}\n\n"
Map.routes[ method ] << {
pattern - [""] => { input: input, conditions: conds, action: block }
}
end
end
def routes
Map.routes
end
def self.expand path
path.zip(Map.default).collect do |a, b|
[b, a]
end.flatten.compact.tap{|i| puts "EXPANDED TRAIL = #{i}"}
end
def self.find method, path, input
Map.input = input
Map.routes[ method ].each do |route|
#puts "ROUTE: #{route}"
patterns = route.keys.flatten.collect do |i|
i.is_a?(Hash)? i.flatten : i
end.flatten.compact #route.keys.first.values.flatten
print "WITH: #{patterns}"
captures = patterns.zip( path ).collect do |a, b|
if a.is_a? Regexp and a =~ b
b
elsif a.is_a? String and a == b
true
else
break []
end
end.compact if patterns.size == path.size
unless captures.nil? || captures.empty?
captures = captures.reject {|i| i == true }
puts " =+=+= MATCHED =+=+="
puts "CAPTURES: #{captures}"
keys = route.keys.flatten.collect {|i| i.keys.flatten if Hash === i }.flatten.compact
puts "KEYS: #{keys}"
action = route.values.first[:action]
keys.zip( captures ) do |a, b| # NEED BETTER DATA STRUCTURE FOR ROUTES
define_method( a ) { b }
end if action.arity == 0 # Need better algorithm
return lambda { action.call( captures ) }
end
puts
end
nil
end
def method_missing method, *path, &block
if Map.routes.keys.include? method
route method, *path, &block
else
super
end
end
end
end
# Example
module Blog
extend Journey::Map
resource :year, :month, :day, :id do
get do
end
end
path 'foo' do
get do
puts 'IN FOO!'
end
end
resource :article do
get '/', '/index' do
end
get :id do
end
resource :comment do
get '/' do
end
get 'vote' do
puts 'VOTING!!!!'
end
put do
puts
puts "PUTTING! #{article} #{comment}"
p input
#puts "#{article}/#{comment}"
end
end
end
resource :dookoo do
resource :plaf do
get do
puts "PLAF #{dookoo} #{plaf}"
end
get 'sink' do
puts 'SINKINGGG'
end
end
end
end
journey = Journey::Route.new(:put, '/my-article/56')
f = journey.begin :woohoo => 'yes'
puts
journey = Journey::Route.new(:get, '/dookoo/56/plaf/444')
f = journey.begin :woohoo => 'yes'
puts
journey = Journey::Route.new(:get, '/dookoo/56/plaf/444/sink')
f = journey.begin :woohoo => 'yes'
puts
journey = Journey::Route.new(:get, '/my-article/56/vote')
f = journey.begin :woohoo => 'yes'
puts
journey = Journey::Route.new(:get, '/foo')
f = journey.begin :woohoo => 'yes'
puts
journey = Journey::Route.new(:get, '/2004/03/04/m-super-article')
f = journey.begin :woohoo => 'yes'
puts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment