fastr 0.0.2 → 0.0.3

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/README.rdoc CHANGED
@@ -14,6 +14,7 @@ Micro web framework for Ruby. Should be used with an EventMachine rack server.
14
14
  The directory structure is similar to rails:
15
15
 
16
16
  * app/(config/controller/views/models)
17
+ * public
17
18
  * lib
18
19
  * test
19
20
 
@@ -27,6 +28,13 @@ The routes are configured in app/config/routes.rb
27
28
  #route.for '/test', :to => 'home#index'
28
29
  end
29
30
 
31
+ == Settings
32
+
33
+ Various settings can be configured in app/config/settings.rb
34
+
35
+ config.log_level = Logger::DEBUG
36
+ config.cache_templates = true
37
+
30
38
  == Controller
31
39
 
32
40
  class HomeController < Fastr::Controller
@@ -34,6 +42,24 @@ The routes are configured in app/config/routes.rb
34
42
  render(:text, "Hello, world!")
35
43
  end
36
44
  end
45
+
46
+ = Request/Response Information
47
+
48
+ == Headers
49
+
50
+ You can set response headers by accessing the attribute headers:
51
+
52
+ self.headers['My-Header'] = 'value'
53
+
54
+ == Cookies
55
+
56
+ You can read cookies by accessing the attribute cookies:
57
+
58
+ puts self.cookies['MY_SESS_COOKIE']
59
+
60
+ Set cookie:
61
+
62
+ set_cookie("sess", myuniquekey, {:expires => Time.now + 3600})
37
63
 
38
64
  == Return a view in a controller
39
65
 
@@ -46,6 +72,10 @@ You can also use the following render methods:
46
72
  With HAML, the template is rendered and any instance variables in your controller are available in the template.
47
73
 
48
74
  render(:haml, :template => "index") # this searches for index.haml in your app/views/ folder
75
+
76
+ JSON:
77
+
78
+ render(:json, {:status => "ok", :message => "done"})
49
79
 
50
80
  == Deferred Responses
51
81
 
@@ -73,9 +103,67 @@ The following is an example of a controller action.
73
103
  response.succeed
74
104
  }
75
105
 
106
+ # This is used to get a callback when the request's connection is closed
107
+ response.closed do
108
+ puts "Connection closed."
109
+ end
110
+
76
111
  response.task(long_task, callback)
77
112
  end
78
113
  end
114
+
115
+ == Plugins
116
+
117
+ Fastr searches the custom/plugins directory in your application's root directory for loading plugins.
118
+
119
+ Example structure:
120
+
121
+ * custom/plugins/my_test/plugin.rb
122
+ * custom/plugins/my_test/lib/*.rb
123
+
124
+ When a plugin is found, the plugin.rb file is loaded. It should contain a module that matches the name of your plugin, ending with the word plugin.
125
+
126
+ Example:
127
+
128
+ Directory: my_test
129
+
130
+ Module name: MyTestPlugin
131
+
132
+ Here is an example plugin and what is currently supported:
133
+
134
+ module MyTestPlugin
135
+ def self.after_boot(app)
136
+ puts "booted: #{app}"
137
+ end
138
+
139
+ def self.before_dispatch(app, env)
140
+ env
141
+ end
142
+
143
+ def self.after_dispatch(app, env, response)
144
+ response
145
+ end
146
+ end
147
+
148
+ == Static Files
149
+
150
+ Anything stored in the public folder in your project's root directory will be served as a static file. This directory is checked before the routes. The mime type is set based on the file's extension.
151
+
152
+ == Change Log
153
+
154
+ 0.3
155
+
156
+ * Added kqueue/epoll.
157
+ * Fixed dependencies.
158
+ * Added loading of model and lib directories.
159
+ * Added settings file.
160
+ * Added controller params.
161
+ * Added ability to disable template caching.
162
+ * Added closed callback for deferred responses.
163
+ * Added static file serving.
164
+ * Added JSON rendering.
165
+ * Added response headers and cookie handling.
166
+ * Added plugins.
79
167
 
80
168
  == Current Status
81
169
 
data/bin/fastr CHANGED
@@ -11,7 +11,7 @@ module Fastr
11
11
 
12
12
  # Directory Structre
13
13
 
14
- dirs = ['app/config', 'app/controllers', 'app/views', 'app/models', 'lib', 'test']
14
+ dirs = ['app/config', 'app/controllers', 'app/views', 'app/models', 'lib', 'test', 'public', 'custom/plugins']
15
15
  dirs.each do |dir|
16
16
  FileUtils.mkdir_p("#{app_name}/#{dir}")
17
17
  end
@@ -29,7 +29,8 @@ module Fastr
29
29
  # Rack file
30
30
  File.open("#{app_name}/config.ru", "w") do |f|
31
31
  f.puts("require 'fastr'")
32
- f.puts("EM.kqueue = true # OS X")
32
+ f.puts("EM.kqueue = true if EM.kqueue?")
33
+ f.puts("EM.epoll = true if EM.epoll?")
33
34
  f.puts("fastrApp = Fastr::Application.new(File.expand_path(File.dirname(__FILE__)))")
34
35
  f.puts("app = lambda { |env|")
35
36
  f.puts("\tfastrApp.dispatch(env)")
@@ -38,6 +39,13 @@ module Fastr
38
39
  f.close
39
40
  end
40
41
 
42
+ # Settings file
43
+ File.open("#{app_name}/app/config/settings.rb", "w") do |f|
44
+ f.puts("config.log_level = Logger::DEBUG")
45
+ f.puts("config.cache_templates = true")
46
+ f.close
47
+ end
48
+
41
49
  # Gemfile
42
50
  File.open("#{app_name}/Gemfile", 'a') do |f|
43
51
  f.puts "source :gemcutter"
@@ -1,32 +1,88 @@
1
1
  require 'logger'
2
+ require 'cgi'
3
+ require 'mime/types'
2
4
 
3
5
  module Fastr
4
6
  class Application
5
7
  include Fastr::Log
6
-
7
- attr_accessor :router, :app_path
8
8
 
9
+ SETTINGS_FILE = "app/config/settings.rb"
10
+ INIT_FILE = "app/config/init.rb"
11
+ PUBLIC_FOLDER = "public"
12
+
13
+ attr_accessor :router, :app_path, :settings, :plugins
14
+
15
+ # These are resources we are watching to change.
16
+ # They will be reloaded upon change.
17
+ @@load_paths = {
18
+ :controller => "app/controllers/*.rb",
19
+ :model => "app/models/*.rb",
20
+ :lib => "lib/*.rb"
21
+ }
22
+
23
+ # Sets the application's initial state to booting and then kicks off the boot.
9
24
  def initialize(path)
10
25
  self.app_path = path
11
-
26
+ self.settings = Fastr::Settings.new(self)
27
+ self.plugins = []
12
28
  @booting = true
13
29
  boot
14
30
  end
15
31
 
32
+ # Convenience wrapper for do_dispatch
33
+ # This is the heart of the server, called indirectly by a Rack aware server.
16
34
  def dispatch(env)
17
35
  return [500, {}, "Server Not Ready"] if @booting
18
36
 
19
37
  begin
20
- do_dispatch(env)
38
+ new_env = plugin_before_dispatch(env)
39
+ plugin_after_dispatch(new_env, do_dispatch(new_env))
21
40
  rescue Exception => e
22
41
  bt = e.backtrace.join("\n")
23
42
  [500, {}, "Exception: #{e}\n\n#{bt}"]
24
43
  end
25
44
  end
26
45
 
27
- def do_dispatch(env)
46
+ def plugin_before_dispatch(env)
47
+ new_env = env
48
+
49
+ self.plugins.each do |plugin|
50
+ if plugin.respond_to? :before_dispatch
51
+ new_env = plugin.send(:before_dispatch, self, env)
52
+ end
53
+ end
54
+
55
+ new_env
56
+ end
57
+
58
+ def plugin_after_dispatch(env, response)
59
+ new_response = response
60
+
61
+ self.plugins.each do |plugin|
62
+ if plugin.respond_to? :after_dispatch
63
+ new_response = plugin.send(:after_dispatch, self, env, response)
64
+ end
65
+ end
66
+
67
+ new_response
68
+ end
69
+
70
+ def plugin_after_boot
71
+ self.plugins.each do |plugin|
72
+ if plugin.respond_to? :after_boot
73
+ new_env = plugin.send(:after_boot, self)
74
+ end
75
+ end
76
+ end
77
+
78
+ # Route, instantiate controller, return response from controller's action.
79
+ def do_dispatch(env)
28
80
  path = env['PATH_INFO']
29
81
 
82
+ # Try to serve a public file
83
+ ret = dispatch_public(env, path)
84
+ return ret if not ret.nil?
85
+
30
86
  log.debug "Checking for routes that match: #{path}"
31
87
  route = router.match(env)
32
88
 
@@ -43,12 +99,14 @@ module Fastr
43
99
  log.info "Routing to controller: #{klass}, action: #{action}"
44
100
 
45
101
  obj = Module.const_get(klass).new
46
- obj.env = env
102
+ setup_controller(obj, env)
47
103
 
48
- ret = obj.send(action)
104
+ code, hdrs, body = *obj.send(action)
105
+
106
+ # Merge headers with anything specified in the controller
107
+ hdrs.merge!(obj.headers)
49
108
 
50
- #[200, {}, "ok"]
51
- ret
109
+ [code, hdrs, body]
52
110
  else
53
111
  [404, {"Content-Type" => "text/plain"}, "404 Not Found: #{path}"]
54
112
  end
@@ -56,6 +114,57 @@ module Fastr
56
114
 
57
115
  private
58
116
 
117
+ def dispatch_public(env, path)
118
+ path = "#{self.app_path}/#{PUBLIC_FOLDER}/#{path[1..(path.length - 1)]}"
119
+ if not File.directory? path and File.exists? path
120
+ f = File.open(path)
121
+ hdrs = {}
122
+
123
+ type = MIME::Types.type_for(File.basename(path))
124
+
125
+ hdrs["Content-Type"] = type.to_s if not type.nil?
126
+
127
+ return [200, hdrs, f.read]
128
+ else
129
+ return nil
130
+ end
131
+ end
132
+
133
+ def setup_controller(controller, env)
134
+ controller.env = env
135
+ controller.params = {}
136
+ controller.headers = {}
137
+
138
+ CGI::parse(env['QUERY_STRING']).each do |k,v|
139
+ if v.length == 1
140
+ controller.params[k] = v[0]
141
+ else
142
+ controller.params[k] = v
143
+ end
144
+ end
145
+
146
+ controller.cookies = get_cookies(env)
147
+
148
+
149
+ controller.app = self
150
+ end
151
+
152
+ def get_cookies(env)
153
+ if env.has_key? "HTTP_COOKIE"
154
+ cookies = env['HTTP_COOKIE'].split(';')
155
+ c = {}
156
+ cookies.each do |cookie|
157
+ info = cookie.strip.split("=")
158
+ if info.length == 2
159
+ c[info[0].strip] = info[1].strip
160
+ end
161
+ end
162
+ c
163
+ else
164
+ {}
165
+ end
166
+ end
167
+
59
168
  #
60
169
  # This is used to initialize the application.
61
170
  # It runs in a thread because startup depends on EventMachine running
@@ -65,11 +174,20 @@ module Fastr
65
174
  sleep 1 until EM.reactor_running?
66
175
 
67
176
  begin
177
+ log.info "Loading application..."
178
+
179
+ load_settings
180
+ Fastr::Plugin.load(self)
68
181
  load_app_classes
69
182
  setup_router
70
183
  setup_watcher
71
184
 
185
+ log.info "Application loaded successfully."
186
+
72
187
  @booting = false
188
+
189
+ plugin_after_boot
190
+ app_init
73
191
  rescue Exception => e
74
192
  log.error "#{e}"
75
193
  puts e.backtrace
@@ -79,33 +197,59 @@ module Fastr
79
197
  end
80
198
  end
81
199
 
200
+ # Initializes the router and loads the routes.
82
201
  def setup_router
83
202
  self.router = Fastr::Router.new(self)
84
203
  self.router.load
85
204
  end
86
205
 
206
+ # Loads all application classes. Called on startup.
87
207
  def load_app_classes
88
- log.debug "Loading application classes..."
89
- Dir["#{self.app_path}/app/controllers/*.rb"].each do |f|
90
- log.debug "Loading: #{f}"
91
- load(f)
208
+ @@load_paths.each do |name, path|
209
+ log.debug "Loading #{name} classes..."
210
+
211
+ Dir["#{self.app_path}/#{path}"].each do |f|
212
+ log.debug "Loading: #{f}"
213
+ load(f)
214
+ end
92
215
  end
93
216
  end
94
217
 
218
+ def app_init
219
+ return if not File.exists? INIT_FILE
220
+
221
+ init_file = File.open(INIT_FILE)
222
+ self.instance_eval(init_file.read)
223
+ end
224
+
225
+ def load_settings
226
+ return if not File.exists? SETTINGS_FILE
227
+
228
+ config_file = File.open(SETTINGS_FILE)
229
+ self.instance_eval(config_file.read)
230
+ end
231
+
232
+ # Watch for any file changes in the load paths.
95
233
  def setup_watcher
96
234
  this = self
97
235
  Handler.send(:define_method, :app) do
98
236
  this
99
237
  end
100
238
 
101
- Dir["#{self.app_path}/app/controllers/*.rb"].each do |f|
102
- EM.watch_file(f, Handler)
239
+ @@load_paths.each do |name, path|
240
+ Dir["#{self.app_path}/#{path}"].each do |f|
241
+ EM.watch_file(f, Handler)
242
+ end
103
243
  end
104
244
  end
105
245
 
246
+ def config
247
+ return self.settings
248
+ end
249
+
106
250
  module Handler
107
251
  def file_modified
108
- app.log.info "Reloading file: #{path}"
252
+ app.log.debug "Reloading file: #{path}"
109
253
  load(path)
110
254
  end
111
255
  end
@@ -1,6 +1,6 @@
1
1
  module Fastr
2
2
  class Controller
3
- attr_accessor :env
3
+ attr_accessor :env, :params, :app, :headers, :cookies
4
4
 
5
5
  include Fastr::Template
6
6
  include Fastr::Deferrable
@@ -8,5 +8,28 @@ module Fastr
8
8
  def self.inherited(kls)
9
9
  kls.instance_eval('include Fastr::Log')
10
10
  end
11
+
12
+ def set_cookie(key, value, options={})
13
+ cookie = ["#{key}=#{value};"]
14
+
15
+
16
+ if options.has_key? :expires and options[:expires].kind_of? Time
17
+ options[:expires] = options[:expires].utc.strftime('%a, %d-%b-%Y %H:%M:%S GMT')
18
+ end
19
+
20
+ options.each do |k,v|
21
+ cookie << "#{k}=#{v.to_s};"
22
+ end
23
+
24
+
25
+
26
+ cookie_val = cookie.join(' ')
27
+
28
+ if self.headers['Set-Cookie'].nil?
29
+ self.headers['Set-Cookie'] = [cookie_val]
30
+ else
31
+ self.headers['Set-Cookie'] << cookie_val
32
+ end
33
+ end
11
34
  end
12
35
  end
@@ -23,6 +23,10 @@ module Fastr
23
23
  EM.defer(operation, callback)
24
24
  end
25
25
 
26
+ def closed(&cb)
27
+ self.errback(&cb)
28
+ end
29
+
26
30
  def finish
27
31
  self.succeed
28
32
  end
@@ -0,0 +1,10 @@
1
+ class String
2
+ def camelcase
3
+ split = self.split('_')
4
+ newStr = []
5
+
6
+ split.each { |s| newStr << s.capitalize }
7
+
8
+ newStr.join('')
9
+ end
10
+ end
@@ -0,0 +1,44 @@
1
+ module Fastr
2
+ module Filter
3
+ def self.included(kls)
4
+
5
+ kls.extend(ClassMethods)
6
+ puts kls
7
+ puts kls.instance_variable_get(:@filters).inspect
8
+ end
9
+
10
+ module ClassMethods
11
+ def before_filter(*args)
12
+ methods = []
13
+ options = {}
14
+ puts self
15
+ args.each do |arg|
16
+ if arg.kind_of? Symbol
17
+ methods << arg
18
+ elsif arg.kind_of? Hash
19
+ options = arg
20
+ end
21
+ end
22
+
23
+ setup_filter(methods, options)
24
+ end
25
+
26
+ def setup_filter(methods, options)
27
+ if self.instance_variable_defined? :@filters
28
+ f = self.instance_variable_get(:@filters)
29
+ else
30
+ f = []
31
+ self.instance_variable_set(:@filters, f)
32
+ end
33
+
34
+ if options.has_key? :only and options[:only].kind_of? Array
35
+ f << {:methods => methods, :only => options[:only]}
36
+ elsif options.has_key? :except and options[:except].kind_of? Array
37
+ f << {:methods => methods, :except => options[:except]}
38
+ else
39
+ f << {:methods => methods, :all => true}
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
data/lib/fastr/logger.rb CHANGED
@@ -2,11 +2,19 @@ require 'logger'
2
2
 
3
3
  module Fastr
4
4
  module Log
5
+ @log_level = Logger::DEBUG
6
+ @log_location = STDOUT
7
+ @log_classes = []
8
+
5
9
  def self.included(kls)
10
+ level = @log_level
11
+ log_classes = @log_classes
12
+ log_location = @log_location
13
+
6
14
  kls.instance_eval do
7
- @logger = Logger.new(STDOUT)
8
- @logger.level = Logger::DEBUG
9
- @logger.formatter = Fastr::Log::Formatter.new(kls)
15
+ @logger = Fastr::Log.create_logger(log_location, level, kls)
16
+
17
+ log_classes << @logger
10
18
 
11
19
  def logger
12
20
  @logger
@@ -19,6 +27,20 @@ module Fastr
19
27
  end
20
28
  end
21
29
  end
30
+
31
+ def self.create_logger(location, level, kls)
32
+ logger = Logger.new(location)
33
+ logger.level = level
34
+ logger.formatter = Fastr::Log::Formatter.new(kls)
35
+ logger
36
+ end
37
+
38
+ def self.level=(level)
39
+ @log_level = level
40
+ @log_classes.each do |log|
41
+ log.level = level
42
+ end
43
+ end
22
44
 
23
45
  class Formatter < Logger::Formatter
24
46
  attr_accessor :progname
@@ -0,0 +1,41 @@
1
+ module Fastr
2
+ module Plugin
3
+ include Fastr::Log
4
+
5
+ PLUGIN_PATH = "custom/plugins"
6
+
7
+ def self.load(app)
8
+ logger.debug "Loading plugins..."
9
+
10
+ if File.directory? "#{app.app_path}/#{PLUGIN_PATH}"
11
+ load_plugins_dir(app)
12
+ end
13
+ end
14
+
15
+ def self.load_plugins_dir(app)
16
+ Dir.foreach("#{app.app_path}/#{PLUGIN_PATH}") do |filename|
17
+ if filename != '.' and filename != '..'
18
+ load_plugin(app, filename, "#{app.app_path}/#{PLUGIN_PATH}/#{filename}")
19
+ end
20
+ end
21
+ end
22
+
23
+ def self.load_plugin(app, name, dir)
24
+ plugin_name = "#{name.camelcase}Plugin"
25
+ logger.debug "Loading plugin #{plugin_name}..."
26
+
27
+ begin
28
+ require("#{dir}/plugin.rb")
29
+ m = Module.const_get(plugin_name)
30
+
31
+ if File.directory? "#{dir}/lib"
32
+ Dir.glob(File.join("#{dir}/lib/**", "*.rb")).each { |f| require("#{f}") }
33
+ end
34
+
35
+ app.plugins << m
36
+ rescue => e
37
+ logger.error "Unable to load plugin: #{e}"
38
+ end
39
+ end
40
+ end
41
+ end
File without changes
@@ -0,0 +1,5 @@
1
+ module Fastr
2
+ module Session
3
+
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ module Fastr
2
+ class Settings
3
+ include Fastr::Log
4
+
5
+ attr_accessor :cache_templates
6
+
7
+ def initialize(app)
8
+ @app = app
9
+ @cache_templates = true
10
+ end
11
+
12
+ def log_level=(level)
13
+ Fastr::Log.level = level
14
+ end
15
+ end
16
+ end
@@ -1,4 +1,5 @@
1
1
  require 'haml'
2
+ require 'json'
2
3
 
3
4
  module Fastr
4
5
  module Template
@@ -22,8 +23,12 @@ module Fastr
22
23
  end
23
24
  end
24
25
 
25
- def render_text(txt)
26
- [200, {"Content-Type" => 'text/plain'}, txt]
26
+ def render_text(*args)
27
+ [200, {"Content-Type" => 'text/plain'}, [args[0]]]
28
+ end
29
+
30
+ def render_json(*args)
31
+ [200, {"Content-Type" => 'application/json'}, args[0][0].to_json.to_s]
27
32
  end
28
33
 
29
34
  def render_haml(args)
@@ -34,12 +39,12 @@ module Fastr
34
39
  else
35
40
  tpl_data = File.read("app/views/#{tpl}.haml")
36
41
  haml_engine = Haml::Engine.new(tpl_data)
37
- @@tpl_cache[tpl] = haml_engine
42
+ @@tpl_cache[tpl] = haml_engine if self.app.settings.cache_templates
38
43
  end
39
44
 
40
45
  resp = haml_engine.render(self)
41
46
 
42
- [200, {"Content-Type" => "text/html"}, resp]
47
+ [200, {"Content-Type" => "text/html"}, [resp]]
43
48
  end
44
49
  end
45
50
  end
data/lib/fastr.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  module Fastr
2
2
  ROOT = File.expand_path(File.dirname(__FILE__))
3
3
 
4
+ require "#{ROOT}/fastr/extensions/string"
5
+
4
6
  autoload :Application, "#{ROOT}/fastr/application"
5
7
  autoload :Log, "#{ROOT}/fastr/logger"
6
8
  autoload :Router, "#{ROOT}/fastr/router"
@@ -8,4 +10,6 @@ module Fastr
8
10
  autoload :Controller, "#{ROOT}/fastr/controller"
9
11
  autoload :Template, "#{ROOT}/fastr/template"
10
12
  autoload :Deferrable, "#{ROOT}/fastr/deferrable"
13
+ autoload :Settings, "#{ROOT}/fastr/settings"
14
+ autoload :Plugin, "#{ROOT}/fastr/plugin"
11
15
  end
@@ -0,0 +1,5 @@
1
+ router.draw do |route|
2
+ route.for '/:controller/:action'
3
+ route.for '/home/:action', :action => '[A-Za-z]+'
4
+ route.for '/test', :to => 'home#index'
5
+ end
@@ -0,0 +1,2 @@
1
+ class FastrAppController
2
+ end
data/test/helper.rb CHANGED
@@ -1,10 +1,46 @@
1
1
  require 'rubygems'
2
+ require 'eventmachine'
2
3
  require 'test/unit'
3
4
  require 'shoulda'
5
+ require 'mocha'
4
6
 
5
7
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
8
  $LOAD_PATH.unshift(File.dirname(__FILE__))
9
+
10
+ APP_PATH = "#{File.expand_path(File.dirname(__FILE__))}/fastr_app"
11
+
7
12
  require 'fastr'
13
+ EM.kqueue = true
14
+
15
+ class NueteredBootingApplication < Fastr::Application
16
+ attr_reader :booted
17
+ def boot
18
+ @booted = true
19
+ end
20
+ end
21
+
22
+ class ManualBootingApplication < Fastr::Application
23
+ include Fastr::Log
24
+
25
+ def initialize(path)
26
+ self.app_path = path
27
+ self.plugins = []
28
+ @booting = true
29
+ end
30
+ end
8
31
 
9
32
  class Test::Unit::TestCase
33
+ def em_setup
34
+ EM.run do
35
+ yield
36
+ EM.stop
37
+ end
38
+ end
10
39
  end
40
+
41
+ class Fastr::Log::Formatter
42
+ def call(severity, time, progname, msg)
43
+ #block all logging output during testing
44
+ #puts "[#{severity}] [#{self.progname}]: #{msg}"
45
+ end
46
+ end
@@ -0,0 +1,39 @@
1
+ require 'helper'
2
+
3
+ class TestApplication < Test::Unit::TestCase
4
+
5
+ context "New Application" do
6
+ setup do
7
+ em_setup{ @application = NueteredBootingApplication.new("/some/path") }
8
+ end
9
+
10
+ should "store startup path" do
11
+ assert_equal "/some/path",@application.app_path
12
+ end
13
+
14
+ should "boot application" do
15
+ assert @application.booted
16
+ end
17
+ end
18
+
19
+ context "Application Boot" do
20
+ setup do
21
+ em_setup {
22
+ @application = ManualBootingApplication.new(APP_PATH)
23
+ @application.send(:boot).join
24
+ }
25
+ end
26
+
27
+ should "load app controllers" do
28
+ assert defined?(FastrAppController)
29
+ end
30
+
31
+ should "create router" do
32
+ assert_not_nil @application.router
33
+ end
34
+
35
+ should "setup routes from route file" do
36
+ assert_equal 3,@application.router.routes.size
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,55 @@
1
+ require 'helper'
2
+
3
+ class TestDeferrable < Test::Unit::TestCase
4
+
5
+ context "Deferrable includer" do
6
+ setup do
7
+ @deferrable = DeferrableHost.new
8
+ em_setup do
9
+ @deferrable.defer_response(200, {"Content-Type" => "text/plain"}) do |response|
10
+ response.send_data("hey\n")
11
+
12
+ long_task = proc {
13
+ response.send_data("processing...\n")
14
+ return "finished"
15
+ }
16
+
17
+ callback = proc { |result|
18
+ response.send_data("#{result}\n")
19
+ response.succeed
20
+ }
21
+
22
+ response.task(long_task, callback)
23
+ end
24
+ end
25
+ end
26
+
27
+ should "pass sent data data through to server callback" do
28
+ assert_not_nil @deferrable.callbacks.index("hey\n")
29
+ end
30
+
31
+ should "run long task sent through response" do
32
+ assert_not_nil @deferrable.callbacks.index("processing...\n")
33
+ end
34
+ end
35
+ end
36
+
37
+ class DeferrableHost
38
+ include Fastr::Deferrable
39
+ attr_accessor :callbacks
40
+
41
+ def initialize
42
+ @callbacks = []
43
+ end
44
+
45
+ def env
46
+ {"async.callback"=>
47
+ Proc.new{|array|
48
+ response = array[2]
49
+ response.each do |arg|
50
+ @callbacks << arg
51
+ end
52
+ }
53
+ }
54
+ end
55
+ end
@@ -0,0 +1,35 @@
1
+ require 'helper'
2
+
3
+ class TestDeferrableResponse < Test::Unit::TestCase
4
+
5
+ context "DeferrableResponse" do
6
+ setup do
7
+ Fastr::Deferrable
8
+ @response = Fastr::DeferrableResponse.new
9
+ end
10
+
11
+ should "hold onto callback to push data through to" do
12
+ @response.each {|data| assert_equal "Some Data",data }
13
+ @response.send_data("Some Data")
14
+ end
15
+
16
+ should "execute deferred tasks" do
17
+ Object.expects(:touch!).times(2)
18
+ em_setup do
19
+ task = proc { Object.touch! }
20
+ callback = proc { Object.touch! }
21
+ @response.task(task,callback)
22
+ end
23
+ end
24
+
25
+ should "call success callback when told to finish" do
26
+ callback_path = nil
27
+ em_setup do
28
+ @response.callback { callback_path = "success" }
29
+ @response.errback { callback_path = "failure" }
30
+ @response.finish
31
+ end
32
+ assert_equal "success",callback_path
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,100 @@
1
+ require 'helper'
2
+
3
+ class TestRouter < Test::Unit::TestCase
4
+ context "" do
5
+ setup { Fastr::Router.any_instance.stubs(:setup_watcher) }
6
+
7
+ context "Newly Created Router" do
8
+ setup do
9
+ @router = Fastr::Router.new(NueteredBootingApplication.new(APP_PATH))
10
+ end
11
+
12
+ should "initialize an empty routes structure" do
13
+ assert_equal [],@router.routes
14
+ end
15
+
16
+ should "infer directory of routes file" do
17
+ assert_equal File.join(APP_PATH,"/app/config/routes.rb"),@router.route_file
18
+ end
19
+
20
+ context "when matching routes" do
21
+ setup { @router.for('/:controller/:action') }
22
+
23
+ should "map vars on valid route" do
24
+ assert_equal({:ok => {:controller=>"test_controller",:action=>"test_action"}}, @router.match({'PATH_INFO' => "/test_controller/test_action"}))
25
+ end
26
+
27
+ should "return error map on invalid route" do
28
+ assert_equal({:error => :not_found},@router.match({'PATH_INFO' => "/2o4598g7vher0023801293479/123twretbnsf g//sdfb s/test_action"}))
29
+ end
30
+ end
31
+
32
+ context "when parsing mapping" do
33
+ context "for standard controller" do
34
+ setup { parse_route('/:controller/:action') }
35
+
36
+ should "build proper regex" do
37
+ assert_equal "^/(\\w+)/(\\w+)$",@route_map[:regex]
38
+ end
39
+
40
+ should "have no extra arguments" do
41
+ assert_nil @route_map[:args]
42
+ end
43
+
44
+ should "have var for controller and action" do
45
+ assert_equal [:controller,:action],@route_map[:vars]
46
+ end
47
+
48
+ should "have an empty hash" do
49
+ assert @route_map[:hash].empty?
50
+ end
51
+ end
52
+
53
+ context "with a regex match" do
54
+ setup { parse_route("/home/:action",:action => '[A-Za-z]+') }
55
+
56
+ should "build proper regex" do
57
+ assert_equal "^/home/([A-Za-z]+)$",@route_map[:regex]
58
+ end
59
+
60
+ should "have one argument for the action regex" do
61
+ assert_equal({:action=>"[A-Za-z]+"},@route_map[:args])
62
+ end
63
+
64
+ should "have var for the action matcher" do
65
+ assert_equal [:action],@route_map[:vars]
66
+ end
67
+
68
+ should "have an empty hash" do
69
+ assert @route_map[:hash].empty?
70
+ end
71
+ end
72
+
73
+ context "with an exact match" do
74
+ setup { parse_route("/test",:to => 'home#index') }
75
+
76
+ should "build proper regex" do
77
+ assert_equal "^/test$",@route_map[:regex]
78
+ end
79
+
80
+ should "have one argument for the direct route" do
81
+ assert_equal({:to=>"home#index"},@route_map[:args])
82
+ end
83
+
84
+ should "have no vars" do
85
+ assert @route_map[:vars].empty?
86
+ end
87
+
88
+ should "have a hash for the mapping" do
89
+ assert_equal({:controller=>"home", :action=>"index"},@route_map[:hash])
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ def parse_route(route,*args)
97
+ @router.for(route,*args)
98
+ @route_map = @router.routes.first
99
+ end
100
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastr
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 2
10
- version: 0.0.2
9
+ - 3
10
+ version: 0.0.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Chris Moos
@@ -15,10 +15,52 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-06-08 00:00:00 -07:00
18
+ date: 2010-06-20 00:00:00 -07:00
19
19
  default_executable: fastr
20
- dependencies: []
21
-
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: mime-types
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 47
30
+ segments:
31
+ - 1
32
+ - 16
33
+ version: "1.16"
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: eventmachine
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: json
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ type: :runtime
63
+ version_requirements: *id003
22
64
  description: A fast, micro-framework for Ruby that should be run under EventMachine servers (thin)
23
65
  email: chris@tech9computers.com
24
66
  executables:
@@ -34,13 +76,24 @@ files:
34
76
  - lib/fastr/controller.rb
35
77
  - lib/fastr/deferrable.rb
36
78
  - lib/fastr/exception.rb
79
+ - lib/fastr/extensions/string.rb
80
+ - lib/fastr/filter.rb
37
81
  - lib/fastr/logger.rb
82
+ - lib/fastr/plugin.rb
38
83
  - lib/fastr/router.rb
84
+ - lib/fastr/session.rb
85
+ - lib/fastr/session/cookie.rb
86
+ - lib/fastr/settings.rb
39
87
  - lib/fastr/template.rb
40
88
  - LICENSE
41
89
  - README.rdoc
90
+ - test/fastr_app/app/config/routes.rb
91
+ - test/fastr_app/app/controllers/fastr_app_controller.rb
42
92
  - test/helper.rb
43
- - test/test_fastr.rb
93
+ - test/test_application.rb
94
+ - test/test_deferrable.rb
95
+ - test/test_deferrable_response.rb
96
+ - test/test_router.rb
44
97
  - bin/fastr
45
98
  has_rdoc: true
46
99
  homepage: http://github.com/chrismoos/fastr
@@ -77,5 +130,10 @@ signing_key:
77
130
  specification_version: 3
78
131
  summary: Another rack web framework for Ruby.
79
132
  test_files:
133
+ - test/fastr_app/app/config/routes.rb
134
+ - test/fastr_app/app/controllers/fastr_app_controller.rb
80
135
  - test/helper.rb
81
- - test/test_fastr.rb
136
+ - test/test_application.rb
137
+ - test/test_deferrable.rb
138
+ - test/test_deferrable_response.rb
139
+ - test/test_router.rb
data/test/test_fastr.rb DELETED
@@ -1,7 +0,0 @@
1
- require 'helper'
2
-
3
- class TestFastr < Test::Unit::TestCase
4
- should "probably rename this file and start testing for real" do
5
- flunk "hey buddy, you should probably rename this file and start testing for real"
6
- end
7
- end