fastr 0.0.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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