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/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
- attr_accessor :routes, :route_file
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
- self.routes.push({:regex => match[:regex], :args => arg, :vars => match[:vars], :hash => hash})
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!(":#{var}", "(#{match.to_s})")
136
+ regexRoute.gsub!(":#{varName}", "(#{match.to_s})")
88
137
  vars.push(varName.to_sym)
89
138
  end
90
139
  {:regex => "^#{regexRoute}$", :vars => vars}
@@ -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
@@ -1,10 +1,9 @@
1
- require 'haml'
2
1
  require 'json'
3
2
 
4
3
  module Fastr
5
4
  module Template
6
- @@tpl_cache = {}
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
- def render(type, *args)
18
- method = "render_#{type}".to_sym
19
- if self.respond_to? method
20
- self.send(method, args)
21
- else
22
- raise Exception.new("No render found for: #{type}")
23
- end
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
- def render_text(*args)
27
- [200, {"Content-Type" => 'text/plain'}, [args[0]]]
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
- def render_json(*args)
31
- [200, {"Content-Type" => 'application/json'}, args[0][0].to_json.to_s]
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 render_haml(args)
35
- tpl = args[0][:template]
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
- if @@tpl_cache.has_key? tpl
38
- haml_engine = @@tpl_cache[tpl]
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
- tpl_data = File.read("app/views/#{tpl}.haml")
41
- haml_engine = Haml::Engine.new(tpl_data)
42
- @@tpl_cache[tpl] = haml_engine if self.app.settings.cache_templates
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
- [200, {"Content-Type" => "text/html"}, [resp]]
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,9 @@
1
+ module Fastr
2
+ module Test
3
+ ROOT = File.expand_path(File.dirname(__FILE__))
4
+
5
+ autoload :Controller, "#{ROOT}/test/controller"
6
+ autoload :Application, "#{ROOT}/test/application"
7
+ autoload :Logger, "#{ROOT}/test/logger"
8
+ end
9
+ end
@@ -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