fastr 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +182 -6
- data/bin/fastr +46 -10
- data/lib/fastr.rb +5 -0
- data/lib/fastr/application.rb +59 -152
- data/lib/fastr/async.rb +11 -0
- data/lib/fastr/controller.rb +67 -24
- data/lib/fastr/cookie.rb +37 -0
- data/lib/fastr/deferrable.rb +9 -9
- data/lib/fastr/dispatch.rb +151 -0
- data/lib/fastr/extensions/string.rb +8 -0
- data/lib/fastr/filter.rb +118 -11
- data/lib/fastr/http.rb +58 -0
- data/lib/fastr/plugin.rb +8 -8
- data/lib/fastr/router.rb +56 -7
- data/lib/fastr/settings.rb +9 -5
- data/lib/fastr/template.rb +83 -24
- data/lib/fastr/template/erubis.rb +23 -0
- data/lib/fastr/template/haml.rb +23 -0
- data/lib/fastr/test.rb +9 -0
- data/lib/fastr/test/application.rb +25 -0
- data/lib/fastr/test/controller.rb +78 -0
- data/lib/fastr/test/logger.rb +5 -0
- data/lib/fastr/test/tasks.rb +3 -0
- data/lib/fastr/test/tasks/test.rake +10 -0
- data/test/fastr_app/app/config/settings.rb +1 -0
- data/test/fastr_app/app/controllers/dispatch.rb +31 -0
- data/test/helper.rb +6 -4
- data/test/test_application.rb +13 -9
- data/test/test_cookie.rb +65 -0
- data/test/test_deferrable.rb +7 -7
- data/test/test_deferrable_response.rb +8 -8
- data/test/test_dispatch.rb +125 -0
- metadata +62 -11
- data/lib/fastr/session.rb +0 -5
- data/lib/fastr/session/cookie.rb +0 -0
data/lib/fastr/http.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
module Fastr
|
2
|
+
module HTTP
|
3
|
+
|
4
|
+
# Parses the query string.
|
5
|
+
#
|
6
|
+
# @param qs [String]
|
7
|
+
# @return [Hash]
|
8
|
+
def self.parse_query_string(qs)
|
9
|
+
params = {}
|
10
|
+
return params if not qs
|
11
|
+
CGI::parse(qs).each do |k,v|
|
12
|
+
if v.length == 1
|
13
|
+
params[k] = v[0]
|
14
|
+
else
|
15
|
+
params[k] = v
|
16
|
+
end
|
17
|
+
end
|
18
|
+
return params
|
19
|
+
end
|
20
|
+
|
21
|
+
# Builds a query string of parameters.
|
22
|
+
#
|
23
|
+
# @param params [Hash] The parameters to put in the query string.
|
24
|
+
# @return [String] Query string with proper URL encoding for values.
|
25
|
+
def self.build_query_string(params)
|
26
|
+
qs = []
|
27
|
+
params.each do |k,v|
|
28
|
+
qs << "#{k}=#{CGI::escape(v)}"
|
29
|
+
end
|
30
|
+
qs.join('&')
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.method?(env, method)
|
34
|
+
return false if not env['REQUEST_METHOD']
|
35
|
+
return env['REQUEST_METHOD'].downcase.to_sym == method
|
36
|
+
end
|
37
|
+
|
38
|
+
# Parses the HTTP cookie.
|
39
|
+
#
|
40
|
+
# @param env [Hash]
|
41
|
+
# @return [Hash]
|
42
|
+
def self.parse_cookies(env)
|
43
|
+
if env.has_key? "HTTP_COOKIE"
|
44
|
+
cookies = env['HTTP_COOKIE'].split(';')
|
45
|
+
c = {}
|
46
|
+
cookies.each do |cookie|
|
47
|
+
info = cookie.strip.split("=")
|
48
|
+
if info.length == 2
|
49
|
+
c[info[0].strip] = info[1].strip
|
50
|
+
end
|
51
|
+
end
|
52
|
+
c
|
53
|
+
else
|
54
|
+
{}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/fastr/plugin.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
module Fastr
|
2
2
|
module Plugin
|
3
3
|
include Fastr::Log
|
4
|
-
|
4
|
+
|
5
5
|
PLUGIN_PATH = "custom/plugins"
|
6
|
-
|
6
|
+
|
7
7
|
def self.load(app)
|
8
8
|
logger.debug "Loading plugins..."
|
9
9
|
|
@@ -11,7 +11,7 @@ module Fastr
|
|
11
11
|
load_plugins_dir(app)
|
12
12
|
end
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
def self.load_plugins_dir(app)
|
16
16
|
Dir.foreach("#{app.app_path}/#{PLUGIN_PATH}") do |filename|
|
17
17
|
if filename != '.' and filename != '..'
|
@@ -19,23 +19,23 @@ module Fastr
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
def self.load_plugin(app, name, dir)
|
24
24
|
plugin_name = "#{name.camelcase}Plugin"
|
25
25
|
logger.debug "Loading plugin #{plugin_name}..."
|
26
|
-
|
26
|
+
|
27
27
|
begin
|
28
28
|
require("#{dir}/plugin.rb")
|
29
29
|
m = Module.const_get(plugin_name)
|
30
|
-
|
30
|
+
|
31
31
|
if File.directory? "#{dir}/lib"
|
32
32
|
Dir.glob(File.join("#{dir}/lib/**", "*.rb")).each { |f| require("#{f}") }
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
app.plugins << m
|
36
36
|
rescue => e
|
37
37
|
logger.error "Unable to load plugin: #{e}"
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
41
|
-
end
|
41
|
+
end
|
data/lib/fastr/router.rb
CHANGED
@@ -1,8 +1,27 @@
|
|
1
1
|
module Fastr
|
2
|
+
# The router manages the routes for an application.
|
3
|
+
#
|
4
|
+
# Routes are configured in the <b>app/config/routes.rb</b> file.
|
5
|
+
#
|
6
|
+
# = Example
|
7
|
+
#
|
8
|
+
# router.draw do |route|
|
9
|
+
# route.for '/:controller/:action'
|
10
|
+
# route.for '/home/:action', :action => '[A-Za-z]+'
|
11
|
+
# route.for '/test', :to => 'home#index'
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# @author Chris Moos
|
2
15
|
class Router
|
3
16
|
include Fastr::Log
|
4
17
|
|
5
|
-
|
18
|
+
# The routes for this router.
|
19
|
+
# @return [Array]
|
20
|
+
attr_accessor :routes
|
21
|
+
|
22
|
+
# The full path to the routes file.
|
23
|
+
# @return [String]
|
24
|
+
attr_accessor :route_file
|
6
25
|
|
7
26
|
def initialize(app)
|
8
27
|
@app = app
|
@@ -11,8 +30,32 @@ module Fastr
|
|
11
30
|
setup_watcher
|
12
31
|
end
|
13
32
|
|
33
|
+
# Searches the routes for a match given a Rack env.
|
34
|
+
#
|
35
|
+
# {#match} looks in the {#routes} to find a match.
|
36
|
+
#
|
37
|
+
# This method looks at PATH_INFO in +env+ to get the current request's path.
|
38
|
+
#
|
39
|
+
# == Return
|
40
|
+
#
|
41
|
+
# No Match:
|
42
|
+
#
|
43
|
+
# {:error => :not_found}
|
44
|
+
#
|
45
|
+
# Match:
|
46
|
+
#
|
47
|
+
# {:ok => {:controller => 'controller', :action => 'action', :var => 'value'}}
|
48
|
+
#
|
49
|
+
# @param env [Hash]
|
50
|
+
# @return [Hash]
|
14
51
|
def match(env)
|
15
52
|
self.routes.each do |info|
|
53
|
+
|
54
|
+
# If the route didn't specify method(s) to limit by, then all HTTP methods are valid.
|
55
|
+
# If the route specified method(s), we check the request's HTTP method and validate
|
56
|
+
# that it exists in that list.
|
57
|
+
next unless info[:methods].nil? or info[:methods].include?(env["REQUEST_METHOD"].downcase.to_sym)
|
58
|
+
|
16
59
|
match = env['PATH_INFO'].match(info[:regex])
|
17
60
|
|
18
61
|
# See if a route matches
|
@@ -33,6 +76,7 @@ module Fastr
|
|
33
76
|
{:error => :not_found}
|
34
77
|
end
|
35
78
|
|
79
|
+
# Loads the routes from {#route_file} and evaluates it within the context of {Fastr::Router}.
|
36
80
|
def load
|
37
81
|
log.debug "Loading routes from: #{self.route_file}"
|
38
82
|
self.routes = []
|
@@ -41,24 +85,29 @@ module Fastr
|
|
41
85
|
@app.instance_eval(file.read)
|
42
86
|
end
|
43
87
|
|
88
|
+
# Adds a route for a path and arguments.
|
89
|
+
#
|
90
|
+
# @param path [String]
|
91
|
+
# @param args [Array]
|
44
92
|
def for(path, *args)
|
45
93
|
arg = args[0]
|
46
94
|
log.debug "Adding route, path: #{path}, args: #{args.inspect}"
|
47
95
|
|
48
96
|
match = get_regex_for_route(path, arg)
|
49
97
|
hash = get_to_hash(arg)
|
98
|
+
route_info = {:regex => match[:regex], :args => arg, :vars => match[:vars], :hash => hash}
|
50
99
|
|
51
|
-
|
100
|
+
# Add the HTTP methods for this route if they exist in options
|
101
|
+
route_info[:methods] = arg[:methods] if not arg.nil? and arg.has_key? :methods
|
102
|
+
|
103
|
+
self.routes.push(route_info)
|
52
104
|
end
|
53
105
|
|
106
|
+
# Evaluates the block in the context of the router.
|
54
107
|
def draw(&block)
|
55
108
|
block.call(self)
|
56
109
|
end
|
57
110
|
|
58
|
-
def file_modified
|
59
|
-
puts "changed!"
|
60
|
-
end
|
61
|
-
|
62
111
|
private
|
63
112
|
|
64
113
|
def get_to_hash(args)
|
@@ -84,7 +133,7 @@ module Fastr
|
|
84
133
|
if args.has_key? varName.to_sym
|
85
134
|
match = args[varName.to_sym]
|
86
135
|
end
|
87
|
-
regexRoute.gsub!(":#{
|
136
|
+
regexRoute.gsub!(":#{varName}", "(#{match.to_s})")
|
88
137
|
vars.push(varName.to_sym)
|
89
138
|
end
|
90
139
|
{:regex => "^#{regexRoute}$", :vars => vars}
|
data/lib/fastr/settings.rb
CHANGED
@@ -1,16 +1,20 @@
|
|
1
|
-
module Fastr
|
1
|
+
module Fastr
|
2
2
|
class Settings
|
3
3
|
include Fastr::Log
|
4
|
-
|
4
|
+
|
5
5
|
attr_accessor :cache_templates
|
6
|
-
|
6
|
+
|
7
7
|
def initialize(app)
|
8
8
|
@app = app
|
9
9
|
@cache_templates = true
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
def log_level=(level)
|
13
13
|
Fastr::Log.level = level
|
14
14
|
end
|
15
|
+
|
16
|
+
def plugins
|
17
|
+
@app.plugins
|
18
|
+
end
|
15
19
|
end
|
16
|
-
end
|
20
|
+
end
|
data/lib/fastr/template.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
|
-
require 'haml'
|
2
1
|
require 'json'
|
3
2
|
|
4
3
|
module Fastr
|
5
4
|
module Template
|
6
|
-
|
7
|
-
|
5
|
+
EXTENSIONS = {} unless defined?(EXTENSIONS)
|
6
|
+
TEMPLATE_CACHE = {} unless defined?(TEMPLATE_CACHE)
|
8
7
|
|
9
8
|
def self.included(kls)
|
10
9
|
kls.extend(ClassMethods)
|
@@ -14,37 +13,97 @@ module Fastr
|
|
14
13
|
|
15
14
|
end
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
16
|
+
# Finds the engine for a particular path.
|
17
|
+
#
|
18
|
+
# ==== Parameters
|
19
|
+
# path<String>:: The path of the file to find an engine for.
|
20
|
+
#
|
21
|
+
# ==== Returns
|
22
|
+
# Class:: The engine.
|
23
|
+
def engine_for(path)
|
24
|
+
path = File.expand_path(path)
|
25
|
+
EXTENSIONS[path.match(/\.([^\.]*)$/)[1]]
|
24
26
|
end
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
+
# Get all known template extensions
|
29
|
+
#
|
30
|
+
# ==== Returns
|
31
|
+
# Array:: Extension strings.
|
32
|
+
def template_extensions
|
33
|
+
EXTENSIONS.keys
|
28
34
|
end
|
29
35
|
|
30
|
-
|
31
|
-
|
36
|
+
# Registers the extensions that will trigger a particular templating
|
37
|
+
# engine.
|
38
|
+
#
|
39
|
+
# ==== Parameters
|
40
|
+
# engine<Class>:: The class of the engine that is being registered
|
41
|
+
# extensions<Array[String]>::
|
42
|
+
# The list of extensions that will be registered with this templating
|
43
|
+
# language
|
44
|
+
#
|
45
|
+
# ==== Raises
|
46
|
+
# ArgumentError:: engine does not have a compile_template method.
|
47
|
+
#
|
48
|
+
# ==== Returns
|
49
|
+
# nil
|
50
|
+
#
|
51
|
+
# ==== Example
|
52
|
+
# Fastr::Template.register_extensions(Fastr::Template::Erubis, ["erb"])
|
53
|
+
def self.register_extensions(engine, extensions)
|
54
|
+
raise ArgumentError, "The class you are registering does not have a result method" unless
|
55
|
+
engine.respond_to?(:result)
|
56
|
+
extensions.each{|ext| EXTENSIONS[ext] = engine }
|
57
|
+
Fastr::Controller.class_eval <<-HERE
|
58
|
+
include #{engine}::Mixin
|
59
|
+
HERE
|
32
60
|
end
|
33
61
|
|
34
|
-
def
|
35
|
-
|
62
|
+
def render(kind, tpl, opts={})
|
63
|
+
# Read the cache template settings for this application, unless it is passed in
|
64
|
+
opts[:cache_template] = self.app.settings.cache_templates unless opts[:cache_template]
|
36
65
|
|
37
|
-
|
38
|
-
|
66
|
+
case kind.to_sym
|
67
|
+
when :template then
|
68
|
+
render_template(tpl, opts)
|
69
|
+
when :partial then
|
70
|
+
render_template_to_string(tpl, opts)
|
71
|
+
when :text then
|
72
|
+
render_text(tpl, opts)
|
73
|
+
when :json then
|
74
|
+
render_json(tpl, opts)
|
39
75
|
else
|
40
|
-
|
41
|
-
|
42
|
-
|
76
|
+
raise ArgumentError, "Unknown render type: #{kind.inspect}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def render_template(tpl_path, opts={})
|
81
|
+
self.headers['Content-Type'] = 'text/html'
|
82
|
+
@response_code = opts[:response_code] || 200
|
83
|
+
|
84
|
+
[ @response_code, @headers, [render_template_to_string(tpl_path, opts)] ]
|
85
|
+
end
|
86
|
+
|
87
|
+
def render_template_to_string(tpl_path, opts={})
|
88
|
+
unless engine = engine_for(tpl_path)
|
89
|
+
raise ArgumentError, "No template engine registered for #{tpl_path}"
|
43
90
|
end
|
44
|
-
|
45
|
-
resp = haml_engine.render(self)
|
46
91
|
|
47
|
-
[
|
92
|
+
@vars = opts[:vars] || {}
|
93
|
+
engine.result(tpl_path, binding(), opts[:cache_template])
|
94
|
+
end
|
95
|
+
|
96
|
+
def render_text(text, opts={})
|
97
|
+
self.headers['Content-Type'] = 'text/plain'
|
98
|
+
@response_code = opts[:response_code] || 200
|
99
|
+
[ @response_code, @headers, [text] ]
|
48
100
|
end
|
101
|
+
|
102
|
+
def render_json(obj, opts={})
|
103
|
+
self.headers['Content-Type'] = 'application/json'
|
104
|
+
@response_code = opts[:response_code] || 200
|
105
|
+
[ @response_code, @headers, [obj.to_json.to_s] ]
|
106
|
+
end
|
107
|
+
|
49
108
|
end
|
50
109
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'erubis'
|
2
|
+
|
3
|
+
module Fastr
|
4
|
+
module Template
|
5
|
+
class Erubis
|
6
|
+
|
7
|
+
def self.result(tpl_path, _binding, cache_template)
|
8
|
+
eruby = Fastr::Template::TEMPLATE_CACHE[tpl_path]
|
9
|
+
unless eruby and cache_template
|
10
|
+
eruby = ::Erubis::Eruby.new(File.read("app/views/#{tpl_path}"))
|
11
|
+
Fastr::Template::TEMPLATE_CACHE[tpl_path] = eruby
|
12
|
+
end
|
13
|
+
eruby.result(_binding)
|
14
|
+
end
|
15
|
+
|
16
|
+
module Mixin
|
17
|
+
end
|
18
|
+
|
19
|
+
Fastr::Template.register_extensions(self, %w[erb])
|
20
|
+
|
21
|
+
end # Erubis
|
22
|
+
end # Template
|
23
|
+
end # Fastr
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'haml'
|
2
|
+
|
3
|
+
module Fastr
|
4
|
+
module Template
|
5
|
+
class Haml
|
6
|
+
|
7
|
+
def self.result(tpl_path, _binding)
|
8
|
+
engine = Fastr::Template::TEMPLATE_CACHE[tpl_path]
|
9
|
+
unless engine and cache_template
|
10
|
+
engine = ::Haml::Engine.new(File.read("app/views/#{tpl_path}"))
|
11
|
+
Fastr::Template::TEMPLATE_CACHE[tpl_path] = engine
|
12
|
+
end
|
13
|
+
engine.render(_binding)
|
14
|
+
end
|
15
|
+
|
16
|
+
module Mixin
|
17
|
+
end
|
18
|
+
|
19
|
+
Fastr::Template.register_extensions(self, %w[haml])
|
20
|
+
|
21
|
+
end # Haml
|
22
|
+
end # Template
|
23
|
+
end # Fastr
|
data/lib/fastr/test.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Fastr
|
2
|
+
module Test
|
3
|
+
class Application < Fastr::Application
|
4
|
+
include Fastr::Log
|
5
|
+
|
6
|
+
attr_accessor :route
|
7
|
+
|
8
|
+
def boot
|
9
|
+
load_settings
|
10
|
+
Fastr::Plugin.load(self)
|
11
|
+
load_app_classes
|
12
|
+
setup_router
|
13
|
+
|
14
|
+
@booting = false
|
15
|
+
|
16
|
+
plugin_after_boot
|
17
|
+
app_init
|
18
|
+
end
|
19
|
+
|
20
|
+
def dispatch_controller(route, env)
|
21
|
+
super(self.route, env)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|