Created
June 6, 2009 22:18
-
-
Save cloudhead/125026 to your computer and use it in GitHub Desktop.
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 '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