pakyow-core 0.8rc1 → 0.8.rc4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/pakyow-core/lib/core/app.rb +448 -0
  3. data/pakyow-core/lib/core/base.rb +35 -12
  4. data/pakyow-core/lib/core/{configuration → config}/app.rb +38 -35
  5. data/pakyow-core/lib/core/config/base.rb +30 -0
  6. data/pakyow-core/lib/core/config/cookies.rb +21 -0
  7. data/pakyow-core/lib/core/config/logger.rb +37 -0
  8. data/pakyow-core/lib/core/{configuration → config}/server.rb +3 -1
  9. data/pakyow-core/lib/core/exceptions.rb +3 -0
  10. data/pakyow-core/lib/core/helpers.rb +20 -16
  11. data/pakyow-core/lib/core/loader.rb +1 -1
  12. data/pakyow-core/lib/core/middleware/logger.rb +170 -16
  13. data/pakyow-core/lib/core/middleware/static.rb +20 -8
  14. data/pakyow-core/lib/core/multilog.rb +19 -0
  15. data/pakyow-core/lib/core/request.rb +52 -30
  16. data/pakyow-core/lib/core/route_eval.rb +390 -0
  17. data/pakyow-core/lib/core/route_lookup.rb +17 -5
  18. data/pakyow-core/lib/core/route_set.rb +17 -210
  19. data/pakyow-core/lib/core/route_template_defaults.rb +18 -12
  20. data/pakyow-core/lib/core/router.rb +41 -31
  21. data/pakyow-core/lib/utils/dir.rb +19 -0
  22. data/pakyow-core/lib/utils/hash.rb +14 -4
  23. data/pakyow-core/lib/views/errors/404.html +77 -0
  24. data/pakyow-core/lib/views/errors/500.html +56 -0
  25. metadata +30 -53
  26. data/pakyow-core/bin/pakyow +0 -18
  27. data/pakyow-core/lib/commands/USAGE +0 -9
  28. data/pakyow-core/lib/commands/USAGE-CONSOLE +0 -12
  29. data/pakyow-core/lib/commands/USAGE-NEW +0 -11
  30. data/pakyow-core/lib/commands/USAGE-SERVER +0 -12
  31. data/pakyow-core/lib/commands/console.rb +0 -18
  32. data/pakyow-core/lib/commands/server.rb +0 -8
  33. data/pakyow-core/lib/core/application.rb +0 -330
  34. data/pakyow-core/lib/core/cache.rb +0 -25
  35. data/pakyow-core/lib/core/configuration/base.rb +0 -31
  36. data/pakyow-core/lib/core/fn_context.rb +0 -5
  37. data/pakyow-core/lib/core/log.rb +0 -39
  38. data/pakyow-core/lib/core/middleware/not_found.rb +0 -40
  39. data/pakyow-core/lib/core/middleware/presenter.rb +0 -25
  40. data/pakyow-core/lib/core/middleware/router.rb +0 -33
  41. data/pakyow-core/lib/core/middleware/setup.rb +0 -15
  42. data/pakyow-core/lib/core/presenter_base.rb +0 -11
  43. data/pakyow-core/lib/core/route_template.rb +0 -77
  44. data/pakyow-core/lib/generators/pakyow/app/app_generator.rb +0 -36
  45. data/pakyow-core/lib/generators/pakyow/app/templates/README +0 -54
  46. data/pakyow-core/lib/generators/pakyow/app/templates/app.rb +0 -12
  47. data/pakyow-core/lib/generators/pakyow/app/templates/config.ru +0 -3
  48. data/pakyow-core/lib/generators/pakyow/app/templates/public/favicon.ico +0 -0
  49. data/pakyow-core/lib/generators/pakyow/app/templates/rakefile +0 -2
  50. data/pakyow-core/lib/generators/pakyow/app/templates/views/main.html +0 -1
  51. data/pakyow-core/lib/generators/pakyow/app/templates/views/pakyow.html +0 -12
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6603c6f6c576749182546ee51b5f99a237ba2ab2
4
+ data.tar.gz: a986e3adf2e4c33a16e8565b48984947bc1e00e5
5
+ SHA512:
6
+ metadata.gz: 13942daed315f9bf380e9ff718d07458c89d630918d781377117e11aec1dfcab916c5692c769e6148ea37ec74e931c402ae303be62db75f2a77005b293b95d5c
7
+ data.tar.gz: 81cddf4aadc79a683129e3420cdf95e42f1e863cbfc123dbe93b92685ac830e533f3f76ca0cae149cc02b4300c5c9a5cb08419023763c7192f5b234ed07da3cf
@@ -0,0 +1,448 @@
1
+ module Pakyow
2
+ class App
3
+ class << self
4
+ def reset
5
+ @@routes = {}
6
+ @@config = {}
7
+
8
+ @@stacks = {:before => {}, :after => {}}
9
+ %w(init load process route match error).each {|name|
10
+ @@stacks[:before][name.to_sym] = []
11
+ @@stacks[:after][name.to_sym] = []
12
+ }
13
+ end
14
+
15
+ # Defines an app
16
+ #
17
+ def define(&block)
18
+ # sets the path to the app file so it can be reloaded later
19
+ config.app.path = StringUtils.parse_path_from_caller(caller[0])
20
+
21
+ self.instance_eval(&block)
22
+ end
23
+
24
+ # Defines a route set.
25
+ #
26
+ #TODO default route set should be config option (also for bindings)
27
+ def routes(set_name = :main, &block)
28
+ if set_name && block
29
+ @@routes[set_name] = block
30
+ else
31
+ @@routes
32
+ end
33
+ end
34
+
35
+ # Defines middleware to be loaded.
36
+ #
37
+ def middleware(&block)
38
+ # prevents middleware from being loaded on each
39
+ # request when auto_reload is enabled
40
+ return if prepared?
41
+
42
+ # tell builder about the middleware
43
+ builder.instance_eval(&block)
44
+ end
45
+
46
+ # Creates an environment.
47
+ #
48
+ def configure(env, &block)
49
+ @@config[env] = block
50
+ end
51
+
52
+ # Fetches a stack (before | after) by name.
53
+ #
54
+ def stack(which, name)
55
+ @@stacks[which][name]
56
+ end
57
+
58
+ # Adds a block to the before stack for `stack_name`.
59
+ #
60
+ def before(stack_name, &block)
61
+ @@stacks[:before][stack_name.to_sym] << block
62
+ end
63
+
64
+ # Adds a block to the after stack for `stack_name`.
65
+ #
66
+ def after(stack_name, &block)
67
+ @@stacks[:after][stack_name.to_sym] << block
68
+ end
69
+
70
+ # Runs the application. Accepts the environment(s) to run, for example:
71
+ # run(:development)
72
+ # run([:development, :staging])
73
+ #
74
+ def run(*args)
75
+ return if running?
76
+
77
+ @running = true
78
+ builder.run(prepare(args))
79
+ detect_handler.run(builder, :Host => config.server.host, :Port => config.server.port) do |server|
80
+ trap(:INT) { stop(server) }
81
+ trap(:TERM) { stop(server) }
82
+ end
83
+ end
84
+
85
+ # Stages the application. Everything is loaded but the application is
86
+ # not started. Accepts the same arguments as #run.
87
+ #
88
+ def stage(*args)
89
+ return if staged?
90
+ @staged = true
91
+ prepare(args)
92
+ end
93
+
94
+ def builder
95
+ @builder ||= Rack::Builder.new
96
+ end
97
+
98
+ def prepared?
99
+ @prepared
100
+ end
101
+
102
+ # Returns true if the application is running.
103
+ #
104
+ def running?
105
+ @running
106
+ end
107
+
108
+ # Returns true if the application is staged.
109
+ #
110
+ def staged?
111
+ @staged
112
+ end
113
+
114
+ # Convenience method for base configuration class.
115
+ #
116
+ def config
117
+ Pakyow::Config::Base
118
+ end
119
+
120
+ def load_config(envs)
121
+ # run global config first
122
+ if global_proc = @@config[:global]
123
+ config.instance_eval(&global_proc)
124
+ end
125
+
126
+ # then run other envs
127
+
128
+ envs.each do |env|
129
+ next unless config_proc = @@config[env.to_sym]
130
+ config.instance_eval(&config_proc)
131
+ end
132
+
133
+ config.app.loaded_envs = envs
134
+ end
135
+
136
+ protected
137
+
138
+ # Prepares the application for running or staging and returns an instance
139
+ # of the application.
140
+ def prepare(envs)
141
+ return if prepared?
142
+
143
+ # configure
144
+ envs = envs.empty? || envs.first.nil? ? [config.app.default_environment] : envs
145
+ load_config(envs)
146
+
147
+ # load middleware
148
+ builder.use(Rack::MethodOverride)
149
+ builder.use(Middleware::Static) if config.app.static
150
+ builder.use(Middleware::Logger) if config.app.log
151
+ builder.use(Middleware::Reloader) if config.app.auto_reload
152
+
153
+ @prepared = true
154
+
155
+ $:.unshift(Dir.pwd) unless $:.include? Dir.pwd
156
+
157
+ return self.new
158
+ end
159
+
160
+ def detect_handler
161
+ handlers = ['puma', 'thin', 'mongrel', 'webrick']
162
+ handlers.unshift(config.server.handler) if config.server.handler
163
+
164
+ handlers.each do |handler|
165
+ begin
166
+ return Rack::Handler.get(handler)
167
+ rescue LoadError
168
+ rescue NameError
169
+ end
170
+ end
171
+ end
172
+
173
+ def stop(server)
174
+ if server.respond_to?('stop!')
175
+ server.stop!
176
+ elsif server.respond_to?('stop')
177
+ server.stop
178
+ else
179
+ # exit ungracefully if necessary...
180
+ Process.exit!
181
+ end
182
+ end
183
+ end
184
+
185
+ include Helpers
186
+ include AppHelpers
187
+
188
+ attr_accessor :request, :response
189
+
190
+ def initialize
191
+ Pakyow.app = self
192
+ Pakyow.configure_logger
193
+
194
+ call_stack(:before, :init)
195
+
196
+ load_app
197
+
198
+ call_stack(:after, :init)
199
+ end
200
+
201
+ # Returns the primary (first) loaded env.
202
+ #
203
+ def env
204
+ config.app.loaded_envs[0]
205
+ end
206
+
207
+ def app
208
+ self
209
+ end
210
+
211
+ def call(env)
212
+ dup.process(env)
213
+ end
214
+
215
+ # Called on every request.
216
+ #
217
+ def process(env)
218
+ call_stack(:before, :process)
219
+
220
+ @response = Response.new
221
+ @request = Request.new(env)
222
+ @request.app = self
223
+ @request.setup
224
+
225
+ set_initial_cookies
226
+
227
+ @found = false
228
+ catch(:halt) {
229
+ unless config.app.ignore_routes
230
+ call_stack(:before, :route)
231
+
232
+ @found = @router.perform(@request, self) {
233
+ call_stack(:after, :match)
234
+ }
235
+
236
+ call_stack(:after, :route)
237
+ end
238
+
239
+ unless found?
240
+ handle(404, false)
241
+
242
+ if config.app.errors_in_browser
243
+ @response["Content-Type"] = 'text/html'
244
+
245
+ view_file = File.join(File.expand_path('../../', __FILE__), 'views', 'errors', '404.html')
246
+ content = File.open(view_file).read
247
+
248
+ path = StringUtils.normalize_path(request.path)
249
+ path = '/' if path.empty?
250
+
251
+ content.gsub!('{view_path}', path == '/' ? 'index.html' : "#{path}.html")
252
+ content.gsub!('{route_path}', path)
253
+
254
+ @response.body = []
255
+ @response.body << content
256
+ end
257
+ end
258
+ }
259
+
260
+ set_cookies
261
+
262
+ call_stack(:after, :process)
263
+
264
+ @response.finish
265
+ rescue StandardError => error
266
+ call_stack(:before, :error)
267
+
268
+ @request.error = error
269
+
270
+ handle(500, false) unless found?
271
+
272
+ if config.app.errors_in_browser
273
+ @response["Content-Type"] = 'text/html'
274
+
275
+ view_file = File.join(File.expand_path('../../', __FILE__), 'views', 'errors', '500.html')
276
+ content = File.open(view_file).read
277
+
278
+ path = StringUtils.normalize_path(request.path)
279
+ path = '/' if path.empty?
280
+
281
+ nice_source = error.backtrace[0].match(/^(.+?):(\d+)(|:in `(.+)')$/)
282
+
283
+ content.gsub!('{file}', nice_source[1].gsub(File.expand_path(Config::App.root) + '/', ''))
284
+ content.gsub!('{line}', nice_source[2])
285
+
286
+ content.gsub!('{msg}', error.to_s)
287
+ content.gsub!('{trace}', error.backtrace.join('<br>'))
288
+
289
+ @response.body = []
290
+ @response.body << content
291
+ end
292
+
293
+ call_stack(:after, :error)
294
+
295
+ @response.finish
296
+ end
297
+
298
+ def found?
299
+ @found
300
+ end
301
+
302
+ # This is NOT a useless method, it's a part of the external api
303
+ def reload
304
+ # reload the app file
305
+ load(config.app.path)
306
+
307
+ # reset config
308
+ envs = config.app.loaded_envs
309
+ config.reset!
310
+
311
+ # reload config
312
+ self.class.load_config(envs)
313
+
314
+ load_app
315
+ end
316
+
317
+ # APP ACTIONS
318
+
319
+ # Interrupts the application and returns response immediately.
320
+ #
321
+ def halt
322
+ throw :halt, @response
323
+ end
324
+
325
+ # Routes the request to different logic.
326
+ #
327
+ def reroute(path, method = nil)
328
+ @request.setup(path, method)
329
+
330
+ call_stack(:before, :route)
331
+ call_stack(:after, :match)
332
+ @router.reroute(@request)
333
+ call_stack(:after, :route)
334
+ end
335
+
336
+ # Sends data in the response (immediately). Accepts a string of data or a File,
337
+ # mime-type (auto-detected; defaults to octet-stream), and optional file name.
338
+ #
339
+ # If a File, mime type will be guessed. Otherwise mime type and file name will
340
+ # default to whatever is set in the response.
341
+ #
342
+ def send(file_or_data, type = nil, send_as = nil)
343
+ if file_or_data.class == File
344
+ data = file_or_data.read
345
+
346
+ # auto set type based on file type
347
+ type = Rack::Mime.mime_type("." + StringUtils.split_at_last_dot(file_or_data.path)[1])
348
+ else
349
+ data = file_or_data
350
+ end
351
+
352
+ headers = {}
353
+ headers["Content-Type"] = type if type
354
+ headers["Content-disposition"] = "attachment; filename=#{send_as}" if send_as
355
+
356
+ @response = Response.new(data, @response.status, @response.header.merge(headers))
357
+ halt
358
+ end
359
+
360
+ # Redirects to location (immediately).
361
+ #
362
+ def redirect(location, status_code = 302)
363
+ location = router.path(location) if location.is_a?(Symbol)
364
+
365
+ headers = response ? response.header : {}
366
+ headers = headers.merge({'Location' => location})
367
+
368
+ app.response = Response.new('', status_code, headers)
369
+ halt
370
+ end
371
+
372
+ def handle(name_or_code, from_logic = true)
373
+ call_stack(:before, :route)
374
+ @router.handle(name_or_code, self, from_logic)
375
+ call_stack(:after, :route)
376
+ end
377
+
378
+ # Convenience method for defining routes on an app instance.
379
+ #
380
+ def routes(set_name = :main, &block)
381
+ self.class.routes(set_name, &block)
382
+ load_routes
383
+ end
384
+
385
+ protected
386
+
387
+ def call_stack(which, stack)
388
+ self.class.stack(which, stack).each {|block|
389
+ self.instance_exec(&block)
390
+ }
391
+ end
392
+
393
+ # Reloads all application files in path and presenter (if specified).
394
+ #
395
+ def load_app
396
+ call_stack(:before, :load)
397
+
398
+ # load src files
399
+ @loader = Loader.new
400
+ @loader.load_from_path(config.app.src_dir)
401
+
402
+ # load the routes
403
+ load_routes
404
+
405
+ call_stack(:after, :load)
406
+ end
407
+
408
+ def load_routes
409
+ @router = Router.instance.reset
410
+ self.class.routes.each_pair {|set_name, block|
411
+ @router.set(set_name, &block)
412
+ }
413
+ end
414
+
415
+ def set_cookies
416
+ @request.cookies.each_pair {|k, v|
417
+ @response.unset_cookie(k) if v.nil?
418
+
419
+ # cookie is already set with value, ignore
420
+ next if @initial_cookies.include?(k.to_s) && @initial_cookies[k.to_s] == v
421
+
422
+ # set cookie with defaults
423
+ @response.set_cookie(k, {
424
+ :path => config.cookies.path,
425
+ :expires => config.cookies.expiration,
426
+ :value => v
427
+ })
428
+ }
429
+
430
+ # delete cookies that are no longer present
431
+ @initial_cookies.each {|k|
432
+ @response.delete_cookie(k) unless @request.cookies.key?(k.to_s)
433
+ }
434
+ end
435
+
436
+ # Stores set cookies at beginning of request cycle
437
+ # for comparison at the end of the cycle
438
+ def set_initial_cookies
439
+ @initial_cookies = {}
440
+ @request.cookies.each {|k,v|
441
+ @initial_cookies[k] = v
442
+ }
443
+ end
444
+
445
+ end
446
+
447
+ App.reset
448
+ end
@@ -1,27 +1,25 @@
1
- require 'core/configuration/base'
1
+ require 'core/config/base'
2
+ require 'core/config/app'
3
+ require 'core/config/server'
4
+ require 'core/config/cookies'
5
+ require 'core/config/logger'
2
6
  require 'core/helpers'
3
- require 'core/log'
7
+ require 'core/multilog'
4
8
  require 'core/request'
5
9
  require 'core/response'
6
10
  require 'core/loader'
7
11
  require 'core/router'
8
12
  require 'core/route_set'
9
- require 'core/route_template'
13
+ require 'core/route_eval'
10
14
  require 'core/route_template_defaults'
11
15
  require 'core/route_lookup'
12
- require 'core/application'
13
- require 'core/cache'
14
- require 'core/presenter_base'
15
- require 'core/fn_context'
16
+ require 'core/app'
17
+ require 'core/exceptions'
16
18
 
17
19
  # middlewares
18
20
  require 'core/middleware/logger'
19
21
  require 'core/middleware/static'
20
22
  require 'core/middleware/reloader'
21
- require 'core/middleware/presenter'
22
- require 'core/middleware/not_found'
23
- require 'core/middleware/router'
24
- require 'core/middleware/setup'
25
23
 
26
24
  # utils
27
25
  require 'utils/string'
@@ -29,5 +27,30 @@ require 'utils/hash'
29
27
  require 'utils/dir'
30
28
 
31
29
  module Pakyow
32
- attr_accessor :app
30
+ attr_accessor :app, :logger
31
+
32
+ def configure_logger
33
+ conf = Config::Base
34
+
35
+ logs = []
36
+
37
+ if File.directory?(conf.logger.path)
38
+ log_path = File.join(conf.logger.path, conf.logger.name)
39
+
40
+ begin
41
+ log = File.open(log_path, 'a')
42
+ log.sync if conf.logger.sync
43
+
44
+ logs << log
45
+ rescue StandardError => e
46
+ warn "Error opening '#{log_path}' for writing"
47
+ end
48
+ end
49
+
50
+ logs << $stdout if conf.app.log_output
51
+
52
+ io = logs.count > 1 ? MultiLog.new(*logs) : logs[0]
53
+
54
+ Pakyow.logger = Logger.new(io, conf.logger.level, conf.logger.colorize, conf.logger.auto_flush)
55
+ end
33
56
  end
@@ -1,59 +1,58 @@
1
1
  module Pakyow
2
- module Configuration
2
+ module Config
3
3
  class App
4
+ Config::Base.register_config(:app, self)
5
+
4
6
  class << self
5
- attr_accessor :dev_mode, :log, :public_dir, :root, :log_dir,
6
- :presenter, :default_action, :ignore_routes, :error_level,
7
- :default_environment, :application_path, :log_name, :src_dir,
8
- :auto_reload, :errors_in_browser, :static, :all_views_visible
9
-
10
- # Displays development-specific warnings.
11
- #
12
- def dev_mode
13
- @dev_mode.nil? ? true : @dev_mode
7
+ attr_accessor :log, :resources, :root, :default_action, :ignore_routes,
8
+ :default_environment, :path, :src_dir, :auto_reload, :errors_in_browser,
9
+ :static, :all_views_visible, :loaded_envs, :log_output
10
+
11
+ def method_missing(name, *args)
12
+ if name[-1,1] == '='
13
+ name = name[0..-2]
14
+ instance_variable_set("@#{name}", *args)
15
+ else
16
+ instance_variable_get("@#{name}")
17
+ end
14
18
  end
15
-
19
+
16
20
  def auto_reload
17
21
  @auto_reload.nil? ? true : @auto_reload
18
22
  end
19
-
23
+
20
24
  def errors_in_browser
21
25
  @errors_in_browser.nil? ? true : @errors_in_browser
22
26
  end
23
-
27
+
24
28
  # Log requests?
25
29
  def log
26
30
  @log.nil? ? true : @log
27
31
  end
28
-
32
+
33
+ def log_output
34
+ @log_output || true
35
+ end
36
+
29
37
  # Root directory
30
38
  def root
31
39
  @root || File.dirname('')
32
40
  end
33
-
34
- # Public directory
35
- def public_dir
36
- @public_dir || "#{root}/public"
37
- end
38
-
39
- # Log directory
40
- def log_dir
41
- @log_dir || "#{root}/logs"
42
- end
43
41
 
44
- def log_name
45
- @log_name || "requests.log"
42
+ # Resources directory
43
+ def resources
44
+ @resources ||= { :default => "#{root}/public" }
46
45
  end
47
46
 
48
47
  def src_dir
49
- @src_dir || "#{root}/lib"
48
+ @src_dir || "#{root}/app/lib"
50
49
  end
51
-
50
+
52
51
  # Default action
53
52
  def default_action
54
53
  @default_action || :index
55
54
  end
56
-
55
+
57
56
  # Mockup mode
58
57
  def ignore_routes
59
58
  @ignore_routes.nil? ? false : @ignore_routes
@@ -62,24 +61,28 @@ module Pakyow
62
61
  def all_views_visible
63
62
  @all_views_visible.nil? ? true : @all_views_visible
64
63
  end
65
-
64
+
66
65
  def default_environment
67
66
  @default_environment || :development
68
67
  end
69
-
68
+
70
69
  # The path to the application class
71
- def application_path
72
- @application_path
70
+ def path
71
+ @path
73
72
  end
74
-
73
+
75
74
  # Handle static files?
76
75
  #
77
- # For best performance, should be set to false if static files are
76
+ # For best performance, should be set to false if static files are
78
77
  # handled by a web server (e.g. Nginx)
79
78
  #
80
79
  def static
81
80
  @static || true
82
81
  end
82
+
83
+ def loaded_envs
84
+ @loaded_envs
85
+ end
83
86
  end
84
87
  end
85
88
  end
@@ -0,0 +1,30 @@
1
+ module Pakyow
2
+ module Config
3
+ class Base
4
+ @@configs = {}
5
+ def self.register_config(name, klass)
6
+ @@configs[name] = klass
7
+
8
+ define_singleton_method name do
9
+ @@configs[name]
10
+ end
11
+ end
12
+
13
+ # Resets all config
14
+ def self.reset!
15
+ @@configs.keys.each do |type|
16
+ klass = self.send(type.to_sym)
17
+ klass.instance_variables.each do |var|
18
+ # Assumes path shouldn't be reset, since it's only set
19
+ # once when Pakyow::Application is inherited.
20
+ next if var.to_sym == :'@path'
21
+ begin
22
+ klass.send("#{var.to_s.gsub('@', '')}=", nil)
23
+ rescue
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,21 @@
1
+ module Pakyow
2
+ module Config
3
+ class Cookies
4
+ Config::Base.register_config(:cookies, self)
5
+
6
+ class << self
7
+ attr_accessor :path, :expiration
8
+
9
+ # What path should the cookie be created for?
10
+ def path
11
+ @path || '/'
12
+ end
13
+
14
+ # When does the cookie expire?
15
+ def expiration
16
+ @expiration || Time.now + 60 * 60 * 24 * 7 # one week
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end