racket-mvc 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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