pakyow-core 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/pakyow-core/CHANGES +3 -0
  2. data/pakyow-core/MIT-LICENSE +20 -0
  3. data/pakyow-core/README +1 -0
  4. data/pakyow-core/bin/pakyow +14 -0
  5. data/pakyow-core/lib/commands/USAGE +8 -0
  6. data/pakyow-core/lib/commands/USAGE-NEW +11 -0
  7. data/pakyow-core/lib/commands/USAGE-SERVER +12 -0
  8. data/pakyow-core/lib/commands/server.rb +8 -0
  9. data/pakyow-core/lib/core/application.rb +463 -0
  10. data/pakyow-core/lib/core/base.rb +17 -0
  11. data/pakyow-core/lib/core/configuration/app.rb +73 -0
  12. data/pakyow-core/lib/core/configuration/base.rb +32 -0
  13. data/pakyow-core/lib/core/configuration/server.rb +19 -0
  14. data/pakyow-core/lib/core/helpers.rb +22 -0
  15. data/pakyow-core/lib/core/loader.rb +35 -0
  16. data/pakyow-core/lib/core/log.rb +35 -0
  17. data/pakyow-core/lib/core/presenter_base.rb +7 -0
  18. data/pakyow-core/lib/core/request.rb +66 -0
  19. data/pakyow-core/lib/core/route_store.rb +117 -0
  20. data/pakyow-core/lib/generators/pakyow/app/app_generator.rb +26 -0
  21. data/pakyow-core/lib/generators/pakyow/app/templates/README +54 -0
  22. data/pakyow-core/lib/generators/pakyow/app/templates/app/lib/application_controller.rb +6 -0
  23. data/pakyow-core/lib/generators/pakyow/app/templates/app/views/main.html +1 -0
  24. data/pakyow-core/lib/generators/pakyow/app/templates/app/views/pakyow.html +12 -0
  25. data/pakyow-core/lib/generators/pakyow/app/templates/config/application.rb +18 -0
  26. data/pakyow-core/lib/generators/pakyow/app/templates/config.ru +10 -0
  27. data/pakyow-core/lib/generators/pakyow/app/templates/logs/requests.log +0 -0
  28. data/pakyow-core/lib/generators/pakyow/app/templates/public/favicon.ico +0 -0
  29. data/pakyow-core/lib/generators/pakyow/app/templates/rakefile +5 -0
  30. data/pakyow-core/lib/pakyow-core.rb +14 -0
  31. data/pakyow-core/lib/utils/dir.rb +18 -0
  32. data/pakyow-core/lib/utils/hash.rb +31 -0
  33. data/pakyow-core/lib/utils/string.rb +37 -0
  34. metadata +113 -0
@@ -0,0 +1,3 @@
1
+ = 0.6.0 / 2011-08-20
2
+
3
+ * Initial gem release of 0.6.0 codebase
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Bryan Powell
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1 @@
1
+ TODO
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ CORE_PATH = File.expand_path('../../lib', __FILE__)
4
+
5
+ if ARGV.first == 'new'
6
+ ARGV.shift
7
+ require 'generators/pakyow/app/app_generator'
8
+ Pakyow::Generators::AppGenerator.start
9
+ elsif ARGV.first == 'server'
10
+ ARGV.shift
11
+ require 'commands/server'
12
+ elsif ARGV.first == '--help' || ARGV.first == '-h' || ARGV.empty?
13
+ puts File.open(File.join(CORE_PATH, 'commands/USAGE')).read
14
+ end
@@ -0,0 +1,8 @@
1
+ Usage:
2
+ pakyow command
3
+
4
+ There are two commands:
5
+ new Create a new Pakyow application
6
+ server Start the application server
7
+
8
+ Run a command with -h for more information.
@@ -0,0 +1,11 @@
1
+ Usage:
2
+ pakyow new application
3
+
4
+ Description:
5
+ The 'pakyow new' command creates a new Pakyow application at the path
6
+ specified.
7
+
8
+ Example:
9
+ pakyow new path/to/application
10
+
11
+ This generates a new Pakyow application in path/to/application.
@@ -0,0 +1,12 @@
1
+ Usage:
2
+ pakyow server [environment]
3
+
4
+ Description:
5
+ The 'pakyow server' command starts the application server. If environment
6
+ is not specified, the default_environment defined by your application
7
+ will be used.
8
+
9
+ Example:
10
+ pakyow server development
11
+
12
+ This starts the application server with the 'development' configuration.
@@ -0,0 +1,8 @@
1
+ if ARGV.first == '--help' || ARGV.first == '-h'
2
+ puts File.open(File.join(CORE_PATH, 'commands/USAGE-SERVER')).read
3
+ else
4
+ $:.unshift(Dir.pwd)
5
+
6
+ require File.join('config', 'application')
7
+ PakyowApplication::Application.run(ARGV.first)
8
+ end
@@ -0,0 +1,463 @@
1
+ module Pakyow
2
+ class Application
3
+ class << self
4
+ attr_accessor :routes_proc, :middleware_proc, :configurations, :error_handlers
5
+
6
+ # Sets the path to the application file so it can be reloaded later.
7
+ #
8
+ def inherited(subclass)
9
+ Pakyow::Configuration::App.application_path = caller[0].split(':')[0]
10
+ end
11
+
12
+ # Runs the application. Accepts the environment(s) to run, for example:
13
+ # run(:development)
14
+ # run([:development, :staging])
15
+ #
16
+ def run(*args)
17
+ self.load_config args.empty? || args.first.nil? ? [Configuration::Base.app.default_environment] : args
18
+ return if running?
19
+
20
+ @running = true
21
+
22
+ builder = Rack::Builder.new
23
+ builder.use(Rack::MethodOverride)
24
+ builder.instance_eval(&self.middleware_proc) if self.middleware_proc
25
+ builder.run(self.new)
26
+ detect_handler.run(builder, :Host => Pakyow::Configuration::Base.server.host, :Port => Pakyow::Configuration::Base.server.port)
27
+ end
28
+
29
+ # Stages the application. Everything is loaded but the application is
30
+ # not started. Accepts the same arguments as #run.
31
+ #
32
+ def stage(*args)
33
+ load_config args.empty? || args.first.nil? ? [Configuration::Base.app.default_environment] : args
34
+ return if staged?
35
+
36
+ app = self.new
37
+
38
+ @staged = true
39
+ end
40
+
41
+ # Returns true if the application is running.
42
+ #
43
+ def running?
44
+ @running
45
+ end
46
+
47
+ # Returns true if the application is staged.
48
+ #
49
+ def staged?
50
+ @staged
51
+ end
52
+
53
+ # Convenience method for base configuration class.
54
+ #
55
+ def config
56
+ Pakyow::Configuration::Base
57
+ end
58
+
59
+ # Creates configuration for a particular environment. Example:
60
+ # configure(:development) { app.auto_reload = true }
61
+ #
62
+ def configure(environment, &block)
63
+ self.configurations ||= {}
64
+ self.configurations[environment] = block
65
+ end
66
+
67
+ # Creates routes. Example:
68
+ # routes { get '/' { # do something } }
69
+ #
70
+ def routes(&block)
71
+ self.routes_proc = block
72
+ end
73
+
74
+ # Creates an error handler (currently 404 and 500 errors are handled).
75
+ # The handler can be created one of two ways:
76
+ #
77
+ # Define a controller/action for a particular error:
78
+ # error(404, :ApplicationController, :handle_404)
79
+ #
80
+ # Specify a block for a particular error:
81
+ # error(404) { # handle error }
82
+ #
83
+ def error(*args, &block)
84
+ self.error_handlers ||= {}
85
+ code, controller, action = args
86
+
87
+ if block
88
+ self.error_handlers[code] = block
89
+ else
90
+ self.error_handlers[code] = {
91
+ :controller => controller,
92
+ :action => action
93
+ }
94
+ end
95
+ end
96
+
97
+ def middleware(&block)
98
+ self.middleware_proc = block
99
+ end
100
+
101
+ protected
102
+
103
+ def load_config(args)
104
+ if self.configurations
105
+ args << Configuration::Base.app.default_environment if args.empty?
106
+ args.each do |env|
107
+ next unless config = self.configurations[env]
108
+ Configuration::Base.instance_eval(&config)
109
+ end
110
+ end
111
+ end
112
+
113
+ def detect_handler
114
+ ['thin', 'mongrel', 'webrick'].each do |server|
115
+ begin
116
+ return Rack::Handler.get(server)
117
+ rescue LoadError
118
+ rescue NameError
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ include Helpers
125
+
126
+ attr_accessor :request, :response, :presenter, :route_store, :restful_routes
127
+
128
+ def initialize
129
+ Pakyow.app = self
130
+
131
+ # Create static handler
132
+ @static_handler = Rack::File.new(Configuration::Base.app.public_dir)
133
+
134
+ # This configuration option will be set if a presenter is to be used
135
+ if Configuration::Base.app.presenter
136
+ # Create a new instance of the presenter
137
+ self.presenter = Configuration::Base.app.presenter.new
138
+ end
139
+
140
+ # Load application files
141
+ load_app
142
+ end
143
+
144
+ # Interrupts the application and returns response immediately.
145
+ #
146
+ def interrupt!
147
+ @interrupted = true
148
+ throw :halt, self.response
149
+ end
150
+
151
+ # Called on every request.
152
+ #
153
+ def call(env)
154
+ start_time = Time.now.to_f
155
+
156
+ # Handle static files
157
+ if env['PATH_INFO'] =~ /\.(.*)$/ && File.exists?(File.join(Configuration::Base.app.public_dir, env['PATH_INFO']))
158
+ @static = true
159
+ @static_handler.call(env)
160
+ else
161
+ # The request object
162
+ self.request = Request.new(env)
163
+
164
+ # Reload application files
165
+ load_app
166
+
167
+ if Configuration::Base.app.presenter
168
+ # Handle presentation for this request
169
+ self.presenter.present_for_request(request)
170
+ end
171
+
172
+ Log.enter "Processing #{env['PATH_INFO']} (#{env['REMOTE_ADDR']} at #{Time.now}) [#{env['REQUEST_METHOD']}]"
173
+
174
+ # The response object
175
+ self.response = Rack::Response.new
176
+ rhs = nil
177
+ just_the_path, format = StringUtils.split_at_last_dot(self.request.path)
178
+ self.request.format = ((format && (format[format.length - 1, 1] == '/')) ? format[0, format.length - 1] : format)
179
+ catch(:halt) do
180
+ rhs, packet = @route_store.get_block(just_the_path, self.request.method)
181
+ packet[:vars].each_pair { |var, val|
182
+ request.params[var] = val
183
+ }
184
+ self.request.route_spec = packet[:data][:route_spec] if packet[:data]
185
+ restful_info = packet[:data][:restful] if packet[:data]
186
+ self.request.restful = restful_info
187
+ rhs.call() if rhs && !Pakyow::Configuration::App.ignore_routes
188
+ end
189
+
190
+ if !self.interrupted?
191
+ if Configuration::Base.app.presenter
192
+ self.response.body = [self.presenter.content]
193
+ end
194
+
195
+ # 404 if no facts matched and no views were found
196
+ if !rhs && (!self.presenter || !self.presenter.presented?)
197
+ self.handle_error(404)
198
+
199
+ Log.enter "[404] Not Found"
200
+ self.response.status = 404
201
+ end
202
+ end
203
+
204
+ return finish!
205
+ end
206
+ rescue StandardError => error
207
+ self.request.error = error
208
+ self.handle_error(500)
209
+
210
+ Log.enter "[500] #{error}\n"
211
+ Log.enter error.backtrace.join("\n") + "\n\n"
212
+
213
+ self.response = Rack::Response.new
214
+
215
+ if Configuration::Base.app.errors_in_browser
216
+ # Show errors in browser
217
+ self.response.body << "<h4>#{CGI.escapeHTML(error.to_s)}</h4>"
218
+ self.response.body << error.backtrace.join("<br />")
219
+ end
220
+
221
+ self.response.status = 500
222
+ return finish!
223
+ ensure
224
+ unless @static
225
+ end_time = Time.now.to_f
226
+ difference = ((end_time - start_time) * 1000).to_f
227
+
228
+ Log.enter "Completed in #{difference}ms | #{self.response.status} | [#{self.request.url}]"
229
+ Log.enter
230
+ end
231
+ end
232
+
233
+ # Sends a file in the response (immediately). Accepts a File object. Mime
234
+ # type is automatically detected.
235
+ #
236
+ def send_file(source_file, send_as = nil, type = nil)
237
+ path = source_file.is_a?(File) ? source_file.path : source_file
238
+ send_as ||= path
239
+ type ||= Rack::Mime.mime_type(".#{send_as.split('.')[-1]}")
240
+
241
+ data = ""
242
+ File.open(path, "r").each_line { |line| data << line }
243
+
244
+ self.response = Rack::Response.new(data, self.response.status, self.response.header.merge({ "Content-Type" => type }))
245
+ interrupt!
246
+ end
247
+
248
+ # Sends data in the response (immediately). Accepts the data, mime type,
249
+ # and optional file name.
250
+ #
251
+ def send_data(data, type, file_name = nil)
252
+ status = self.response ? self.response.status : 200
253
+
254
+ headers = self.response ? self.response.header : {}
255
+ headers = headers.merge({ "Content-Type" => type })
256
+ headers = headers.merge({ "Content-disposition" => "attachment; filename=#{file_name}"}) if file_name
257
+
258
+ self.response = Rack::Response.new(data, status, headers)
259
+ interrupt!
260
+ end
261
+
262
+ # Redirects to location (immediately).
263
+ #
264
+ def redirect_to(location, status_code = 302)
265
+ headers = self.response ? self.response.header : {}
266
+ headers = headers.merge({'Location' => location})
267
+
268
+ self.response = Rack::Response.new('', status_code, headers)
269
+ interrupt!
270
+ end
271
+
272
+ # Registers a route for GET requests. Route can be defined one of two ways:
273
+ # get('/', :ControllerClass, :action_method)
274
+ # get('/') { # do something }
275
+ #
276
+ # Routes for namespaced controllers (e.g. Admin::ControllerClass) can be defined like this:
277
+ # get('/', :Admin_ControllerClass, :action_method)
278
+ #
279
+ def get(route, controller = nil, action = nil, &block)
280
+ register_route(route, block, :get, controller, action)
281
+ end
282
+
283
+ # Registers a route for POST requests (see #get).
284
+ #
285
+ def post(route, controller = nil, action = nil, &block)
286
+ register_route(route, block, :post, controller, action)
287
+ end
288
+
289
+ # Registers a route for PUT requests (see #get).
290
+ #
291
+ def put(route, controller = nil, action = nil, &block)
292
+ register_route(route, block, :put, controller, action)
293
+ end
294
+
295
+ # Registers a route for DELETE requests (see #get).
296
+ #
297
+ def delete(route, controller = nil, action = nil, &block)
298
+ register_route(route, block, :delete, controller, action)
299
+ end
300
+
301
+ # Registers the default route (see #get).
302
+ #
303
+ def default(controller = nil, action = nil, &block)
304
+ register_route('/', block, :get, controller, action)
305
+ end
306
+
307
+ # Creates REST routes for a resource. Arguments: url, controller, model
308
+ #
309
+ def restful(*args, &block)
310
+ url, controller, model = args
311
+
312
+ with_scope(:url => url.gsub(/^[\/]+|[\/]+$/,""), :model => model) do
313
+ nest_scope(&block) if block_given?
314
+
315
+ @restful_routes ||= {}
316
+ @restful_routes[model] ||= {} if model
317
+
318
+ @@restful_actions.each do |opts|
319
+ action_url = current_path
320
+ if suffix = opts[:url_suffix]
321
+ action_url = File.join(action_url, suffix)
322
+ end
323
+
324
+ # Create the route
325
+ register_route(action_url, nil, opts[:method], controller, opts[:action], true)
326
+
327
+ # Store url for later use (currently used by Binder#action)
328
+ @restful_routes[model][opts[:action]] = action_url if model
329
+ end
330
+
331
+ remove_scope
332
+ end
333
+ end
334
+
335
+ @@restful_actions = [
336
+ { :action => :index, :method => :get },
337
+ { :action => :show, :method => :get, :url_suffix => ':id' },
338
+ { :action => :new, :method => :get, :url_suffix => 'new' },
339
+ { :action => :create, :method => :post },
340
+ { :action => :edit, :method => :get, :url_suffix => 'edit/:id' },
341
+ { :action => :update, :method => :put, :url_suffix => ':id' },
342
+ { :action => :delete, :method => :delete, :url_suffix => ':id' }
343
+ ]
344
+
345
+ protected
346
+
347
+ def interrupted?
348
+ @interrupted
349
+ end
350
+
351
+ # Handles route registration.
352
+ #
353
+ def register_route(route, block, method, controller = nil, action = nil, restful = false)
354
+ if controller
355
+ controller = eval(controller.to_s)
356
+ action ||= Configuration::Base.app.default_action
357
+
358
+ block = lambda {
359
+ instance = controller.new
360
+ request.controller = instance
361
+ request.action = action
362
+
363
+ instance.send(action)
364
+ }
365
+ end
366
+
367
+ data = {:route_spec=>route}
368
+ if restful
369
+ data[:restful] = {:restful_action=>action}
370
+ end
371
+ @route_store.add_route(route, block, method, data)
372
+ end
373
+
374
+ def with_scope(opts)
375
+ @scope ||= {}
376
+ @scope[:path] ||= []
377
+ @scope[:model] = opts[:model]
378
+
379
+ @scope[:path] << opts[:url]
380
+
381
+ yield
382
+ end
383
+
384
+ def remove_scope
385
+ @scope[:path].pop
386
+ end
387
+
388
+ def nest_scope(&block)
389
+ @scope[:path].insert(-1, ":#{StringUtils.underscore(@scope[:model].to_s)}_id")
390
+ yield
391
+ @scope[:path].pop
392
+ end
393
+
394
+ def current_path
395
+ @scope[:path].join('/')
396
+ end
397
+
398
+ def handle_error(code)
399
+ return unless self.class.error_handlers
400
+ return unless handler = self.class.error_handlers[code]
401
+
402
+ if handler.is_a? Proc
403
+ handler.call
404
+ else
405
+ c = eval(handler[:controller].to_s).new
406
+ c.send(handler[:action])
407
+ end
408
+ end
409
+
410
+ def set_cookies
411
+ if self.request.cookies && self.request.cookies != {}
412
+ self.request.cookies.each do |key, value|
413
+ if value.is_a?(Hash)
414
+ self.response.set_cookie(key, {:path => '/', :expires => Time.now + 604800}.merge(value))
415
+ elsif value.is_a?(String)
416
+ self.response.set_cookie(key, {:path => '/', :expires => Time.now + 604800}.merge({:value => value}))
417
+ else
418
+ self.response.set_cookie(key, {:path => '/', :expires => Time.now + 604800 * -1 }.merge({:value => value}))
419
+ end
420
+ end
421
+ end
422
+ end
423
+
424
+ # Reloads all application files in application_path and presenter (if specified).
425
+ #
426
+ def load_app
427
+ return if @loaded && !Configuration::Base.app.auto_reload
428
+ @loaded = true
429
+
430
+ # Reload Application
431
+ load(Configuration::App.application_path)
432
+
433
+ @loader = Loader.new unless @loader
434
+ @loader.load!(Configuration::Base.app.src_dir)
435
+
436
+ load_routes
437
+
438
+ # Reload views
439
+ if Configuration::Base.app.presenter
440
+ self.presenter.reload!
441
+ end
442
+ end
443
+
444
+ def load_routes
445
+ @route_store = RouteStore.new
446
+ self.instance_eval(&self.class.routes_proc) if self.class.routes_proc
447
+ end
448
+
449
+ # Send the response and cleanup.
450
+ #
451
+ def finish!
452
+ @interrupted = false
453
+ @static = false
454
+
455
+ # Set cookies
456
+ set_cookies
457
+
458
+ # Finished
459
+ self.response.finish
460
+ end
461
+
462
+ end
463
+ end
@@ -0,0 +1,17 @@
1
+ module Pakyow
2
+ autoload :Configuration, 'core/configuration/base'
3
+ autoload :Helpers, 'core/helpers'
4
+ autoload :GeneralHelpers, 'core/helpers'
5
+ autoload :Log, 'core/log'
6
+ autoload :Request, 'core/request'
7
+ autoload :Loader, 'core/loader'
8
+ autoload :Application, 'core/application'
9
+ autoload :RouteStore, 'core/route_store'
10
+
11
+ # utils
12
+ autoload :StringUtils, 'utils/string'
13
+ autoload :HashUtils, 'utils/hash'
14
+ autoload :DirUtils, 'utils/dir'
15
+
16
+ attr_accessor :app
17
+ end
@@ -0,0 +1,73 @@
1
+ module Pakyow
2
+ module Configuration
3
+ class App
4
+ 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
9
+
10
+ # Displays development-specific warnings.
11
+ #
12
+ def dev_mode
13
+ @dev_mode.nil? ? true : @dev_mode
14
+ end
15
+
16
+ def auto_reload
17
+ @auto_reload.nil? ? true : @auto_reload
18
+ end
19
+
20
+ def errors_in_browser
21
+ @errors_in_browser.nil? ? true : @errors_in_browser
22
+ end
23
+
24
+ # Log requests?
25
+ def log
26
+ @log.nil? ? true : @log
27
+ end
28
+
29
+ # Root directory
30
+ def root
31
+ @root || File.dirname('')
32
+ 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
+
44
+ def log_name
45
+ @log_name || "requests.log"
46
+ end
47
+
48
+ def src_dir
49
+ @src_dir || "#{root}/app/lib"
50
+ end
51
+
52
+ # Default action
53
+ def default_action
54
+ @default_action || :index
55
+ end
56
+
57
+ # Mockup mode
58
+ def ignore_routes
59
+ @ignore_routes.nil? ? false : @ignore_routes
60
+ end
61
+
62
+ def default_environment
63
+ @default_environment || :development
64
+ end
65
+
66
+ # The path to the application class
67
+ def application_path
68
+ @application_path
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,32 @@
1
+ module Pakyow
2
+ module Configuration
3
+ autoload :App, 'core/configuration/app'
4
+ autoload :Server, 'core/configuration/server'
5
+
6
+ class Base
7
+ # Fetches the server configuration
8
+ def self.server
9
+ Configuration::Server
10
+ end
11
+
12
+ # Fetches to application configuration
13
+ def self.app
14
+ Configuration::App
15
+ end
16
+
17
+ # Resets all configuration
18
+ def self.reset!
19
+ %w[app server].each do |type|
20
+ klass = self.send(type.to_sym)
21
+ klass.instance_variables.each do |var|
22
+ # Assumes application_path shouldn't be reset, since it's only set
23
+ # once when Pakyow::Application is inherited.
24
+ next if var == '@application_path'
25
+
26
+ klass.send("#{var.gsub('@', '')}=", nil)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,19 @@
1
+ module Pakyow
2
+ module Configuration
3
+ class Server
4
+ class << self
5
+ attr_accessor :port, :host
6
+
7
+ # On what port does the application run?
8
+ def port
9
+ @port || 3000
10
+ end
11
+
12
+ # On what host does the application run?
13
+ def host
14
+ @host || '0.0.0.0'
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end