racket-mvc 0.0.3 → 0.0.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0f2546526bd1f65233e173fab47e53cd5ce9f82e
4
- data.tar.gz: 0d7c4a9ae89bcddbb196cc44705d72d7c4cad104
3
+ metadata.gz: c2780584b9d4ab769bd0e1aef25544d15aee4eab
4
+ data.tar.gz: 391cf9ae48c6ed99634b0d224f3351c0b0b9c1a9
5
5
  SHA512:
6
- metadata.gz: 23baf9988b840912916097826a3d63c3a238d7655756cf4f3af70d3a3c56f4c4f1b901b48ff65f0ff4627f806c1e5953a4721ff67db7338a5ecf35f8fb239f57
7
- data.tar.gz: 0c6f205fef098a042290ff0c8984c6ff944a8de9e615ff7f8e215bdd173aeaf33d9df8c166d28b4017d891a7568c2653668e79160c0f66e82a0be4e0b23c8050
6
+ metadata.gz: 97cacd90e6986b5a0946cfbcb018c993521696aa54e654dc2d593eaf01129554e446ad9c3e4b4709c1186a7443f8ab24c7d9ef209e8c6f760561132b0131d01f
7
+ data.tar.gz: dd360e6ff448e1216008453962ccb8b091dee5c0c01804263b4e7495bd48802d39d546978aa64ee40a269fe6da23afa4083245f3c166316ac6a7053c3b1f00e4
data/lib/racket.rb CHANGED
@@ -30,7 +30,6 @@ require_relative 'racket/router.rb'
30
30
  require_relative 'racket/session.rb'
31
31
  require_relative 'racket/view_cache.rb'
32
32
  require_relative 'racket/utils.rb'
33
- require_relative 'racket/version.rb'
34
33
 
35
34
  module Racket
36
35
  # Requires a file using the current application directory as a base path.
@@ -46,6 +45,7 @@ module Racket
46
45
  #
47
46
  # @return [String]
48
47
  def version
48
+ require_relative 'racket/version.rb'
49
49
  Version.current
50
50
  end
51
51
 
@@ -24,16 +24,76 @@ module Racket
24
24
  # Racket main application class.
25
25
  class Application
26
26
 
27
- attr_reader :options, :router
27
+ @options = nil
28
28
 
29
- @current = nil
29
+ # Returns the internal application object. When called for the first time this method will use
30
+ # Rack::Builder to build
31
+ #
32
+ # @return [Rack::Builder]
33
+ def self.application
34
+ return @application if @application
35
+ @options[:middleware].unshift([Rack::ShowExceptions]) if dev_mode?
36
+ instance = self
37
+ @application = Rack::Builder.new do
38
+ instance.options[:middleware].each do |middleware|
39
+ klass, opts = middleware
40
+ instance.inform_dev("Loading middleware #{klass} with options #{opts}.")
41
+ if opts
42
+ use klass, opts
43
+ else
44
+ use klass
45
+ end
46
+ end
47
+ run lambda { |env|
48
+ static_result = instance.serve_static_file(env)
49
+ return static_result unless static_result.nil? || static_result.first >= 400
50
+ instance.router.route(env)
51
+ }
52
+ end
53
+ end
30
54
 
31
55
  # Called whenever Rack sends a request to the application.
32
56
  #
33
57
  # @param [Hash] env Rack environment
34
58
  # @return [Array] A Rack response array
35
59
  def self.call(env)
36
- @current.call(env)
60
+ application.call(env.dup)
61
+ end
62
+
63
+ # Returns a list of default options for Racket::Application.
64
+ #
65
+ # @return [Hash]
66
+ def self.default_options
67
+ root_dir = Utils.build_path(Dir.pwd)
68
+ {
69
+ controller_dir: Utils.build_path(root_dir, 'controllers'),
70
+ default_action: :index,
71
+ default_layout: '_default.*',
72
+ default_view: nil,
73
+ layout_dir: Utils.build_path(root_dir, 'layouts'),
74
+ logger: Logger.new($stdout),
75
+ middleware: [
76
+ [
77
+ Rack::Session::Cookie,
78
+ {
79
+ key: 'racket.session',
80
+ old_secret: SecureRandom.hex(16),
81
+ secret: SecureRandom.hex(16)
82
+ }
83
+ ]
84
+ ],
85
+ mode: :live,
86
+ public_dir: Utils.build_path(root_dir, 'public'),
87
+ root_dir: root_dir,
88
+ view_dir: Utils.build_path(root_dir, 'views')
89
+ }
90
+ end
91
+
92
+ # Returns whether the application runs in dev mode.
93
+ #
94
+ # @return [true|false]
95
+ def self.dev_mode?
96
+ @options[:mode] == :dev
37
97
  end
38
98
 
39
99
  # Returns a route to the specified controller/action/parameter combination.
@@ -43,27 +103,24 @@ module Racket
43
103
  # @param [Array] params
44
104
  # @return [String]
45
105
  def self.get_route(controller, action, params)
46
- router.get_route(controller, action, params)
106
+ @router.get_route(controller, action, params)
47
107
  end
48
108
 
49
109
  # Initializes a new Racket::Application object with default options.
50
110
  #
111
+ # @param [true|false] reboot
51
112
  # @return [Class]
52
- def self.default
53
- fail 'Application has already been initialized!' if @current
54
- @current = self.new
55
- @current.reload
56
- self
113
+ def self.default(reboot = false)
114
+ init({}, reboot)
57
115
  end
58
116
 
59
- # Sends a message to the logger, but only if the application is running in dev mode.
117
+ # Writes a message to the logger if there is one present.
60
118
  #
61
119
  # @param [String] message
62
120
  # @param [Symbol] level
63
121
  # @return nil
64
- def self.inform_dev(message, level = :info)
65
- @current.inform(message, level) if options[:mode] == :dev
66
- nil
122
+ def self.inform(message, level)
123
+ (@options[:logger].send(level, message) if @options[:logger]) && nil
67
124
  end
68
125
 
69
126
  # Sends a message to the logger.
@@ -72,182 +129,134 @@ module Racket
72
129
  # @param [Symbol] level
73
130
  # @return nil
74
131
  def self.inform_all(message, level = :info)
75
- @current.inform(message, level)
132
+ inform(message, level)
76
133
  end
77
134
 
78
- # Returns options for the currently running Racket::Application.
79
- #
80
- # @return [Hash]
81
- def self.options
82
- @current.options
83
- end
84
-
85
- # Requires a file using the current application directory as a base path.
135
+ # Sends a message to the logger, but only if the application is running in dev mode.
86
136
  #
87
- # @param [Object] args
137
+ # @param [String] message
138
+ # @param [Symbol] level
88
139
  # @return nil
89
- def self.require(*args)
90
- @current.require(*args)
91
- nil
92
- end
93
-
94
- # Returns the router associated with the currenntly running Racket::Application.
95
- #
96
- # @return [Racket::Router]
97
- def self.router
98
- @current.router
140
+ def self.inform_dev(message, level = :debug)
141
+ (inform(message, level) if dev_mode?) && nil
99
142
  end
100
143
 
101
- private_class_method :router
102
-
103
- # Initializes a new Racket::Application object with options specified by +options+.
144
+ # Initializes the Racket application.
104
145
  #
105
146
  # @param [Hash] options
147
+ # @param [true|false] reboot
106
148
  # @return [Class]
107
- def self.using(options)
108
- fail 'Application has already been initialized!' if @current
109
- @current = self.new(options)
110
- @current.reload
149
+ def self.init(options, reboot)
150
+ instance_variables.each { |ivar| instance_variable_set(ivar, nil) } if reboot
151
+ fail 'Application has already been initialized!' if @options
152
+ @options = default_options.merge(options)
153
+ setup_static_server
154
+ reload
111
155
  self
112
156
  end
113
157
 
114
- # Returns the view cache of the currently running application.
115
- #
116
- # @return [ViewCache]
117
- def self.view_cache
118
- @current.view_cache
119
- end
120
-
121
- # Internal dispatch handler. Should not be called directly.
158
+ # Loads controllers and associates each controller with a route.
122
159
  #
123
- # @param [Hash] env Rack environment
124
- # @return [Array] A Rack response array
125
- def call(env)
126
- app.call(env)
160
+ # @return [nil]
161
+ def self.load_controllers
162
+ inform_dev('Loading controllers.')
163
+ @options[:last_added_controller] = []
164
+ @controller = nil
165
+ Dir.chdir(@options[:controller_dir]) do
166
+ files = Pathname.glob(File.join('**', '*.rb'))
167
+ files.map! { |file| file.to_s }
168
+ # Sort by longest path so that the longer paths gets matched first
169
+ # HttpRouter claims to be doing this already, but this "hack" is needed in order
170
+ # for the router to work.
171
+ files.sort! do |a, b|
172
+ b.split('/').length <=> a.split('/').length
173
+ end
174
+ files.each do |file|
175
+ ::Kernel.require File.expand_path(file)
176
+ path = "/#{File.dirname(file)}"
177
+ path = '' if path == '/.'
178
+ @router.map(path, @options[:last_added_controller].pop)
179
+ end
180
+ end
181
+ @options.delete(:last_added_controller)
182
+ inform_dev('Done loading controllers.') && nil
127
183
  end
128
184
 
129
- # Writes a message to the logger if there is one present.
185
+ # Returns options for the currently running Racket::Application.
130
186
  #
131
- # @param [String] message
132
- # @param [Symbol] level
133
- # @return nil
134
- def inform(message, level)
135
- options[:logger].send(level, message) if options[:logger]
136
- nil
187
+ # @return [Hash]
188
+ def self.options
189
+ @options
137
190
  end
138
191
 
139
192
  # Reloads the application, making any changes to the controller configuration visible
140
193
  # to the application.
141
194
  #
142
195
  # @return [nil]
143
- def reload
196
+ def self.reload
144
197
  setup_routes
145
- # @todo: Clear cached views/layouts
146
- nil
198
+ @view_cache = nil
147
199
  end
148
200
 
149
201
  # Requires a file using the current application directory as a base path.
150
202
  #
151
203
  # @param [Object] args
152
- # @return nil
153
- def require(*args)
154
- ::Kernel.require Utils.build_path(*args)
155
- nil
204
+ # @return [nil]
205
+ def self.require(*args)
206
+ (::Kernel.require Utils.build_path(*args)) && nil
156
207
  end
157
208
 
158
- # Returns the ViewCache object associated with the current application.
209
+ # Returns the router associated with the currenntly running Racket::Application.
159
210
  #
160
- # @return [ViewCache]
161
- def view_cache
162
- @view_cache ||= ViewCache.new(options[:layout_dir], options[:view_dir])
163
- end
164
-
165
- private
166
-
167
- def app
168
- @app ||= build_app
211
+ # @return [Racket::Router]
212
+ def self.router
213
+ @router
169
214
  end
170
215
 
171
- def build_app
172
- instance = self
173
- Rack::Builder.new do
174
- instance.options[:middleware].each_pair do |klass, opts|
175
- Application.inform_dev("Loading middleware #{klass} with options #{opts}.")
176
- use klass, opts
177
- end
178
- run lambda { |env| instance.router.route(env) }
179
- end
216
+ # Serves a static file (if Racket is configured to serve static files).
217
+ #
218
+ # @param [Hash] env Rack environment
219
+ # @return [Array|nil] A Rack response array if Rack::File handled the file, nil otherwise.
220
+ def self.serve_static_file(env)
221
+ return nil if @static_server.nil?
222
+ @static_server.call(env)
180
223
  end
181
224
 
182
- # Creates a new instance of Racket::Application.
225
+ # Initializes routing.
183
226
  #
184
- # @param [Hash] options
185
- # @return [Racket::Application]
186
- def initialize(options = {})
187
- @options = default_options.merge(options)
227
+ # @return [nil]
228
+ def self.setup_routes
229
+ @router = Router.new
230
+ load_controllers
188
231
  end
189
232
 
190
- # Returns a list of default options for Racket::Application.
233
+ # Initializes static server (if a public dir is specified).
191
234
  #
192
- # @return [Hash]
193
- def default_options
194
- root_dir = Utils.build_path(Dir.pwd)
195
- {
196
- controller_dir: Utils.build_path(root_dir, 'controllers'),
197
- default_action: :index,
198
- default_layout: '_default.*',
199
- default_view: nil,
200
- layout_dir: Utils.build_path(root_dir, 'layouts'),
201
- logger: Logger.new($stdout),
202
- middleware: {
203
- Rack::Session::Cookie => {
204
- key: 'racket.session',
205
- old_secret: SecureRandom.hex(16),
206
- secret: SecureRandom.hex(16)
207
- }
208
- },
209
- mode: :live,
210
- root_dir: root_dir,
211
- view_dir: Utils.build_path(root_dir, 'views')
212
- }
235
+ # @return [nil]
236
+ def self.setup_static_server
237
+ @static_server = nil
238
+ return nil unless (public_dir = @options[:public_dir]) && Utils.dir_readable?(public_dir)
239
+ inform_dev("Setting up static server to serve files from #{public_dir}.")
240
+ (@static_server = Rack::File.new(public_dir)) && nil
213
241
  end
214
242
 
215
- # Loads controllers and associates each controller with a route.
243
+ # Initializes a new Racket::Application object with options specified by +options+.
216
244
  #
217
- # @return [nil]
218
- def load_controllers
219
- Application.inform_dev('Loading controllers.')
220
- options[:last_added_controller] = []
221
- @controller = nil
222
- Dir.chdir(@options[:controller_dir]) do
223
- files = Pathname.glob(File.join('**', '*.rb'))
224
- files.map! { |file| file.to_s }
225
- # Sort by longest path so that the longer paths gets matched first
226
- # HttpRouter claims to be doing this already, but this "hack" is needed in order
227
- # for the router to work.
228
- files.sort! do |a, b|
229
- b.split('/').length <=> a.split('/').length
230
- end
231
- files.each do |file|
232
- ::Kernel.require File.expand_path(file)
233
- path = "/#{File.dirname(file)}"
234
- path = '' if path == '/.'
235
- @router.map(path, options[:last_added_controller].pop)
236
- end
237
- end
238
- options.delete(:last_added_controller)
239
- Application.inform_dev('Done loading controllers.')
240
- nil
245
+ # @param [Hash] options
246
+ # @param [true|false] reboot
247
+ # @return [Class]
248
+ def self.using(options, reboot = false)
249
+ init(options, reboot)
241
250
  end
242
251
 
243
- # Initializes routing.
252
+ # Returns the view cache of the currently running application.
244
253
  #
245
- # @return [nil]
246
- def setup_routes
247
- @router = Router.new
248
- load_controllers
249
- nil
254
+ # @return [ViewCache]
255
+ def self.view_cache
256
+ @view_cache ||= ViewCache.new(@options[:layout_dir], @options[:view_dir])
250
257
  end
251
258
 
259
+ private_class_method :application, :default_options, :inform, :init, :load_controllers,
260
+ :setup_routes, :setup_static_server
252
261
  end
253
262
  end
@@ -29,7 +29,7 @@ module Racket
29
29
  # @param [Array] methods
30
30
  # @param [Proc] blk
31
31
  # @return [nil]
32
- def self.add_hook(type, methods, blk)
32
+ def self.__register_hook(type, methods, blk)
33
33
  key = "#{type}_hooks".to_sym
34
34
  meths = public_instance_methods(false)
35
35
  meths = meths & methods.map { |method| method.to_sym} unless methods.empty?
@@ -39,7 +39,7 @@ module Racket
39
39
  nil
40
40
  end
41
41
 
42
- private_class_method :add_hook
42
+ private_class_method :__register_hook
43
43
 
44
44
  # Adds a before hook to one or more actions. Actions should be given as a list of symbols.
45
45
  # If no symbols are provided, *all* actions on the controller is affected.
@@ -47,7 +47,7 @@ module Racket
47
47
  # @param [Array] methods
48
48
  # @return [nil]
49
49
  def self.after(*methods, &blk)
50
- add_hook(:after, methods, blk) if block_given?
50
+ __register_hook(:after, methods, blk) if block_given?
51
51
  end
52
52
 
53
53
  # Adds an after hook to one or more actions. Actions should be given as a list of symbols.
@@ -56,7 +56,7 @@ module Racket
56
56
  # @param [Array] methods
57
57
  # @return [nil]
58
58
  def self.before(*methods, &blk)
59
- add_hook(:before, methods, blk) if block_given?
59
+ __register_hook(:before, methods, blk) if block_given?
60
60
  end
61
61
 
62
62
  # :nodoc:
@@ -113,14 +113,28 @@ module Racket
113
113
  Application.get_route(controller, action, params)
114
114
  end
115
115
 
116
- # Redirects the client.
116
+ # Redirects the client. After hooks are
117
117
  #
118
- # @param [String] target
119
- # @param [Fixnum] status
118
+ # @param [String] target URL to redirect to
119
+ # @param [Fixnum] status HTTP status to send
120
+ # @param [true|false] run_after_hook Whether after hook should be run before redirecting
120
121
  # @return [Object]
121
- def redirect(target, status = 302)
122
- racket.redirected = true
122
+ def redirect(target, status = 302, run_after_hook = true)
123
123
  response.redirect(target, status)
124
+ respond(response.status, response.headers, '', run_after_hook);
125
+ end
126
+
127
+ # Stop processing request and send a custom response. After calling this method, no further
128
+ # processing of the request is done unless +run_after_hook+ is set to true. If +run_after_hook+
129
+ # is set to true, any after hook associated with the action (but no other code) will be run.
130
+ #
131
+ # @param [Fixnum] status
132
+ # @param [Hash] headers
133
+ # @param [String] body
134
+ # @param [true|false] run_after_hook Whether after hook should be run
135
+ def respond(status = 200, headers = {}, body = '', run_after_hook = false)
136
+ __run_hook(:after, racket.action) if run_after_hook
137
+ throw :response, [status, headers, body]
124
138
  end
125
139
 
126
140
  # Renders an action.
@@ -135,13 +149,16 @@ module Racket
135
149
  private
136
150
 
137
151
  def __execute(action)
138
- before_hooks = controller_option(:before_hooks) || {}
139
- self.instance_eval &before_hooks[action] if before_hooks.key?(action)
152
+ __run_hook(:before, action)
140
153
  meth = method(action)
141
154
  params = racket.params[0...meth.parameters.length]
142
155
  racket.action_result = meth.call(*params)
143
- after_hooks = controller_option(:after_hooks) || {}
144
- self.instance_eval &after_hooks[action] if after_hooks.key?(action)
156
+ __run_hook(:after, action)
157
+ end
158
+
159
+ def __run_hook(type, action)
160
+ hooks = controller_option("#{type}_hooks".to_sym) || {}
161
+ self.instance_eval &hooks[action] if hooks.key?(action)
145
162
  end
146
163
 
147
164
  end
@@ -25,7 +25,7 @@ module Racket
25
25
  class Current
26
26
  # Holds Racket internal state, available to the controller instance but mostly used for keeping
27
27
  # track of things that don't belong to the actual request.
28
- State = Struct.new(:action, :action_result, :params, :redirected)
28
+ State = Struct.new(:action, :action_result, :params)
29
29
 
30
30
  # Called whenever a new request needs to be processed.
31
31
  #
@@ -34,7 +34,7 @@ module Racket
34
34
  # @param [Array] params Parameters sent to the action
35
35
  # @return [Module] A module encapsulating all state relating to the current request
36
36
  def self.init(env, action, params)
37
- racket = State.new(action, nil, params, false)
37
+ racket = State.new(action, nil, params)
38
38
  request = Request.new(env)
39
39
  response = Response.new
40
40
  session = Session.new(env['rack.session']) if env.key?('rack.session')
data/lib/racket/router.rb CHANGED
@@ -76,8 +76,14 @@ module Racket
76
76
  end
77
77
 
78
78
  # @todo: Allow the user to set custom handlers for different errors
79
- def render_404(message = '404 Not found')
80
- [404, { 'Content-Type' => 'text/plain' }, message]
79
+ def render_error(status, error = nil)
80
+ # If running in dev mode, let Rack::ShowExceptions handle the error.
81
+ raise error if error && Application.dev_mode?
82
+
83
+ # Not running in dev mode, let us handle the error ourselves.
84
+ response = Response.new([], status, { 'Content-Type' => 'text/plain' })
85
+ response.write("#{status} #{Rack::Utils::HTTP_STATUS_CODES[status]}")
86
+ response.finish
81
87
  end
82
88
 
83
89
  # Routes a request and renders it.
@@ -85,28 +91,34 @@ module Racket
85
91
  # @param [Hash] env Rack environment
86
92
  # @return [Array] A Rack response triplet
87
93
  def route(env)
88
- # Find controller in map
89
- # If controller exists, call it
90
- # Otherwise, send a 404
91
- matching_routes = @router.recognize(env)
92
- unless matching_routes.first.nil?
93
- target_klass = matching_routes.first.first.route.dest
94
- params = matching_routes.first.first.param_values.first.reject { |e| e.empty? }
95
- action = params.empty? ? target_klass.get_option(:default_action) : params.shift.to_sym
94
+ begin
95
+ catch :response do # Catches early exits from Controller.respond.
96
+ # Find controller in map
97
+ # If controller exists, call it
98
+ # Otherwise, send a 404
99
+ matching_routes = @router.recognize(env)
100
+ unless matching_routes.first.nil?
101
+ target_klass = matching_routes.first.first.route.dest
102
+ params = matching_routes.first.first.param_values.first.reject { |e| e.empty? }
103
+ action = params.empty? ? target_klass.get_option(:default_action) : params.shift.to_sym
96
104
 
97
- # Check if action is available on target
98
- return render_404 unless @actions_by_controller[target_klass].include?(action)
105
+ # Check if action is available on target
106
+ return render_error(404) unless @actions_by_controller[target_klass].include?(action)
99
107
 
100
- # Initialize target
101
- target = target_klass.new
102
- # @fixme: File.dirname should not be used on urls!
103
- 1.upto(params.count) do
104
- env['PATH_INFO'] = File.dirname(env['PATH_INFO'])
108
+ # Initialize target
109
+ target = target_klass.new
110
+ # @fixme: File.dirname should not be used on urls!
111
+ 1.upto(params.count) do
112
+ env['PATH_INFO'] = File.dirname(env['PATH_INFO'])
113
+ end
114
+ target.extend(Current.init(env, action, params))
115
+ target.render(action)
116
+ else
117
+ render_error(404)
118
+ end
105
119
  end
106
- target.extend(Current.init(env, action, params))
107
- target.render(action)
108
- else
109
- render_404
120
+ rescue => err
121
+ render_error(500, err)
110
122
  end
111
123
  end
112
124
 
data/lib/racket/utils.rb CHANGED
@@ -21,5 +21,10 @@ module Racket
21
21
  end
22
22
  path.cleanpath.expand_path.to_s
23
23
  end
24
+
25
+ def self.dir_readable?(path)
26
+ pathname = Pathname.new(path)
27
+ pathname.exist? && pathname.directory? && pathname.readable?
28
+ end
24
29
  end
25
30
  end
@@ -19,11 +19,19 @@ along with Racket. If not, see <http://www.gnu.org/licenses/>.
19
19
  =end
20
20
 
21
21
  module Racket
22
+ # The only purpose of this module is to keep track of the current Racket version. It is *not*
23
+ # loaded automatically unless you make an explicit call to Racket.version.
22
24
  module Version
25
+ # Major version
23
26
  MAJOR = 0
27
+ # Minor version
24
28
  MINOR = 0
25
- TEENY = 3
29
+ # Teeny version
30
+ TEENY = 5
26
31
 
32
+ # Returns the current version of Racket as a string.
33
+ #
34
+ # @return [String]
27
35
  def current
28
36
  [MAJOR, MINOR, TEENY].join('.')
29
37
  end
@@ -37,19 +37,17 @@ module Racket
37
37
  # @param [Controller] controller
38
38
  # @return [Hash]
39
39
  def render(controller)
40
- unless controller.racket.redirected
41
- template =
42
- find_template(controller.request.path, controller.controller_option(:default_view))
43
- if template
44
- output = Tilt.new(template).render(controller)
45
- layout =
46
- find_layout(controller.request.path, controller.controller_option(:default_layout))
47
- output = Tilt.new(layout).render(controller) { output } if layout
48
- else
49
- output = controller.racket.action_result
50
- end
51
- controller.response.write(output)
40
+ template =
41
+ find_template(controller.request.path, controller.controller_option(:default_view))
42
+ if template
43
+ output = Tilt.new(template).render(controller)
44
+ layout =
45
+ find_layout(controller.request.path, controller.controller_option(:default_layout))
46
+ output = Tilt.new(layout).render(controller) { output } if layout
47
+ else
48
+ output = controller.racket.action_result
52
49
  end
50
+ controller.response.write(output)
53
51
  controller.response.finish
54
52
  end
55
53
 
@@ -90,7 +88,7 @@ module Racket
90
88
  file_path = File.join(base_file_path, url_path)
91
89
  action = File.basename(file_path)
92
90
  file_path = File.dirname(file_path)
93
- return nil unless File.exists?(file_path) && File.directory?(file_path)
91
+ return nil unless Utils.dir_readable?(file_path)
94
92
  Dir.chdir(file_path) do
95
93
  files = Pathname.glob("#{action}.*")
96
94
  if files.empty?
data/spec/_custom.rb CHANGED
@@ -1,7 +1,10 @@
1
- describe 'The custom Racket test Application' do
1
+ describe 'A custom Racket test Application' do
2
2
  extend Rack::Test::Methods
3
3
  def app
4
- @app ||= Racket::Application.using(default_layout: 'zebra.*', view_dir: 'templates')
4
+ @app ||= Racket::Application.using(
5
+ { default_layout: 'zebra.*', logger: nil, mode: :dev, view_dir: 'templates' },
6
+ true
7
+ )
5
8
  end
6
9
 
7
10
  it 'should set requested options' do
@@ -19,7 +22,7 @@ describe 'The custom Racket test Application' do
19
22
  it 'should return a 404 on a nonexisting url' do
20
23
  get '/nosuchurl'
21
24
  last_response.status.should.equal(404)
22
- last_response.body.should.equal('404 Not found')
25
+ last_response.body.should.equal('404 Not Found')
23
26
  end
24
27
 
25
28
  it 'should be able to render a template and a layout' do
@@ -45,4 +48,11 @@ describe 'The custom Racket test Application' do
45
48
  response = JSON.parse(last_response.body)
46
49
  response.should.equal(["Data added in before block", "Data added in action"])
47
50
  end
51
+
52
+ it 'should let Rack::ShowExceptions handle the error' do
53
+ get '/sub1/epic_fail'
54
+ last_response.status.should.equal(500)
55
+ last_response.headers['Content-Type'].should.equal('text/plain')
56
+ last_response.body.should.match(%r(^RuntimeError: Epic fail!))
57
+ end
48
58
  end
data/spec/_default.rb CHANGED
@@ -1,4 +1,4 @@
1
- describe 'The default Racket test Application' do
1
+ describe 'A default Racket test Application' do
2
2
  extend Rack::Test::Methods
3
3
 
4
4
  def app
@@ -28,7 +28,7 @@ describe 'The default Racket test Application' do
28
28
  actions_by_controller[DefaultRootController].include?(:index).should.equal(true)
29
29
  actions_by_controller[DefaultRootController].include?(:my_first_route).should.equal(true)
30
30
  actions_by_controller[DefaultRootController].include?(:my_second_route).should.equal(true)
31
- actions_by_controller[DefaultSubController1].length.should.equal(3)
31
+ actions_by_controller[DefaultSubController1].length.should.equal(4)
32
32
  actions_by_controller[DefaultSubController1].include?(:route_to_root).should.equal(true)
33
33
  actions_by_controller[DefaultSubController1].include?(:route_to_nonexisting).should.equal(true)
34
34
  actions_by_controller[DefaultSubController2].length.should.equal(3)
@@ -79,11 +79,11 @@ describe 'The default Racket test Application' do
79
79
  it 'should return 404 on calling nonexisting action' do
80
80
  get '/nonono'
81
81
  last_response.status.should.equal(404)
82
- last_response.body.should.equal('404 Not found')
82
+ last_response.body.should.equal('404 Not Found')
83
83
 
84
84
  get '/sub2/nonono'
85
85
  last_response.status.should.equal(404)
86
- last_response.body.should.equal('404 Not found')
86
+ last_response.body.should.equal('404 Not Found')
87
87
  end
88
88
 
89
89
  it 'should be able to find routes within the same controller' do
@@ -172,4 +172,18 @@ describe 'The default Racket test Application' do
172
172
  )
173
173
  end
174
174
 
175
+ it 'should be able to serve static files' do
176
+ get '/hello.txt'
177
+ last_response.status.should.equal(200)
178
+ last_response.headers['Content-Type'].should.equal('text/plain')
179
+ last_response.body.should.equal("Hello there\n")
180
+ end
181
+
182
+ it 'should handle exceptions correctly' do
183
+ get '/sub1/epic_fail'
184
+ last_response.status.should.equal(500)
185
+ last_response.headers['Content-Type'].should.equal('text/plain')
186
+ last_response.body.should.equal('500 Internal Server Error')
187
+ end
188
+
175
189
  end
data/spec/_invalid.rb ADDED
@@ -0,0 +1,14 @@
1
+ describe 'An invalid Racket test Application' do
2
+ extend Rack::Test::Methods
3
+
4
+ def app
5
+ @app ||= Racket::Application.default
6
+ end
7
+
8
+ it 'should never initialize' do
9
+ lambda { get '/' }
10
+ .should.raise(RuntimeError)
11
+ .message.should.equal('Application has already been initialized!')
12
+ end
13
+
14
+ end
data/spec/racket.rb CHANGED
@@ -18,7 +18,6 @@ require 'rack/test'
18
18
  require 'bacon'
19
19
 
20
20
  Dir.chdir(TEST_DEFAULT_APP_DIR) { require_relative '_default.rb' }
21
-
22
- Racket::Application.class_eval { @current = nil }
23
-
24
21
  Dir.chdir(TEST_CUSTOM_APP_DIR) { require_relative '_custom.rb' }
22
+
23
+ require_relative '_invalid.rb'
@@ -11,5 +11,9 @@ class CustomSubController1 < Racket::Controller
11
11
  def route_to_nonexisting
12
12
  r(CustomInheritedController, :nonono, :with, :params)
13
13
  end
14
+
15
+ def epic_fail
16
+ fail 'Epic fail!'
17
+ end
14
18
 
15
19
  end
@@ -12,4 +12,8 @@ class DefaultSubController1 < Racket::Controller
12
12
  r(DefaultInheritedController, :nonono, :with, :params)
13
13
  end
14
14
 
15
+ def epic_fail
16
+ fail 'Epic fail!'
17
+ end
18
+
15
19
  end
@@ -0,0 +1 @@
1
+ Hello there
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: racket-mvc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lars Olsson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-28 00:00:00.000000000 Z
11
+ date: 2015-07-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http_router
@@ -158,6 +158,7 @@ files:
158
158
  - lib/racket/view_cache.rb
159
159
  - spec/_custom.rb
160
160
  - spec/_default.rb
161
+ - spec/_invalid.rb
161
162
  - spec/racket.rb
162
163
  - spec/test_custom_app/controllers/sub1/custom_sub_controller_1.rb
163
164
  - spec/test_custom_app/controllers/sub2/custom_sub_controller_2.rb
@@ -172,6 +173,7 @@ files:
172
173
  - spec/test_default_app/controllers/sub2/default_sub_controller_2.rb
173
174
  - spec/test_default_app/controllers/sub3/default_sub_controller_3.rb
174
175
  - spec/test_default_app/controllers/sub3/inherited/default_inherited_controller.rb
176
+ - spec/test_default_app/public/hello.txt
175
177
  homepage: https://github.com/lasso/racket
176
178
  licenses:
177
179
  - GNU AFFERO GENERAL PUBLIC LICENSE, version 3