Capcode 0.9.8 → 0.9.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,881 @@
1
+ # Please read the README.rdoc file !
2
+
3
+ require 'rubygems'
4
+ require 'rack'
5
+ require 'rack/mime'
6
+ require 'logger'
7
+ Logger.class_eval { alias :write :<< } unless Logger.instance_methods.include? "write"
8
+ require 'optparse'
9
+ require 'irb'
10
+ require 'capcode/version'
11
+ require 'capcode/core_ext'
12
+ require 'capcode/helpers/auth'
13
+ require 'capcode/render/text'
14
+ require 'capcode/configuration'
15
+ require 'capcode/filters'
16
+ require 'capcode/ext/rack/urlmap'
17
+
18
+ module Capcode
19
+ class ParameterError < ArgumentError #:nodoc: all
20
+ end
21
+
22
+ class RouteError < ArgumentError #:nodoc: all
23
+ end
24
+
25
+ class RenderError < ArgumentError #:nodoc: all
26
+ end
27
+
28
+ class MissingLibrary < Exception #:nodoc: all
29
+ end
30
+
31
+ # Views is an empty module in which you can store your markaby or xml views.
32
+ module Views; end
33
+
34
+ # Helpers contains methods available in your controllers
35
+ module Helpers
36
+ def self.args
37
+ @args ||= nil
38
+ end
39
+ def self.args=(x)
40
+ @args = x
41
+ end
42
+
43
+ # Render a view
44
+ #
45
+ # render's parameter can be a Hash or a string. Passing a string is equivalent to do
46
+ # render( :text => string )
47
+ #
48
+ # If you want to use a specific renderer, use one of this options :
49
+ #
50
+ # * :markaby => :my_func : :my_func must be defined in Capcode::Views
51
+ # * :erb => :my_erb_file : this suppose that's my_erb_file.rhtml exist in erb_path
52
+ # * :haml => :my_haml_file : this suppose that's my_haml_file.haml exist in haml_path
53
+ # * :sass => :my_sass_file : this suppose that's my_sass_file.sass exist in sass_path
54
+ # * :text => "my text"
55
+ # * :json => MyObject : this suppose that's MyObject respond to .to_json
56
+ # * :static => "my_file.xxx" : this suppose that's my_file.xxx exist in the static directory
57
+ # * :xml => :my_func : :my_func must be defined in Capcode::Views
58
+ # * :webdav => /path/to/root
59
+ #
60
+ # Or you can use a "HTTP code" renderer :
61
+ #
62
+ # render 200 => "Ok", :server => "Capcode #{Capcode::CAPCOD_VERION}", ...
63
+ #
64
+ # If you want to use a specific layout, you can specify it with option
65
+ # :layout
66
+ #
67
+ # If you want to change the Content-Type, you can specify it with option
68
+ # :content_type
69
+ # Note that this will not work with the JSON renderer
70
+ #
71
+ # If you use the WebDav renderer, you can use the option
72
+ # :resource_class (see http://github.com/georgi/rack_dav for more informations)
73
+ def render( hash )
74
+ if hash.class == Hash
75
+ render_type = nil
76
+ possible_code_renderer = nil
77
+
78
+ hash.keys.each do |key|
79
+ begin
80
+ gem "capcode-render-#{key.to_s}"
81
+ require "capcode/render/#{key.to_s}"
82
+ rescue Gem::LoadError
83
+ nil
84
+ rescue LoadError
85
+ raise Capcode::RenderError, "Hum... The #{key} renderer is malformated! Please try to install a new version or use an other renderer!", caller
86
+ end
87
+
88
+ if self.respond_to?("render_#{key.to_s}")
89
+ unless render_type.nil?
90
+ raise Capcode::RenderError, "Can't use multiple renderer (`#{render_type}' and `#{key}') !", caller
91
+ end
92
+ render_type = key
93
+ end
94
+
95
+ if key.class == Fixnum
96
+ possible_code_renderer = key
97
+ end
98
+ end
99
+
100
+ if render_type.nil? and possible_code_renderer.nil?
101
+ raise Capcode::RenderError, "Renderer type not specified!", caller
102
+ end
103
+
104
+ unless self.respond_to?("render_#{render_type.to_s}")
105
+ if possible_code_renderer.nil?
106
+ raise Capcode::RenderError, "#{render_type} renderer not present ! please require 'capcode/render/#{render_type}'", caller
107
+ else
108
+ code = possible_code_renderer
109
+ body = hash.delete(possible_code_renderer)
110
+ header = {}
111
+ hash.each do |k, v|
112
+ k = k.to_s.split(/_/).map{|e| e.capitalize}.join("-")
113
+ header[k] = v
114
+ end
115
+
116
+ [code, header, body]
117
+ end
118
+ else
119
+ render_name = hash.delete(render_type)
120
+ content_type = hash.delete(:content_type)
121
+ unless content_type.nil?
122
+ @response['Content-Type'] = content_type
123
+ end
124
+
125
+ begin
126
+ self.send( "render_#{render_type.to_s}", render_name, hash )
127
+ rescue => e
128
+ raise Capcode::RenderError, "Error rendering `#{render_type.to_s}' : #{e.message}", caller
129
+ end
130
+ end
131
+ else
132
+ render( :text => hash )
133
+ end
134
+ end
135
+
136
+ # Help you to return a JSON response
137
+ #
138
+ # module Capcode
139
+ # class JsonResponse < Route '/json/([^\/]*)/(.*)'
140
+ # def get( arg1, arg2 )
141
+ # json( { :1 => arg1, :2 => arg2 })
142
+ # end
143
+ # end
144
+ # end
145
+ #
146
+ # <b>DEPRECATED</b>, please use <tt>render( :json => o )</tt>
147
+ def json( d ) ## DELETE THIS IN 1.0.0
148
+ warn( "json is deprecated and will be removed in version 1.0, please use `render( :json => ... )'" )
149
+ render :json => d
150
+ end
151
+
152
+ # Send a redirect response
153
+ #
154
+ # module Capcode
155
+ # class Hello < Route '/hello/(.*)'
156
+ # def get( you )
157
+ # if you.nil?
158
+ # redirect( WhoAreYou )
159
+ # else
160
+ # ...
161
+ # end
162
+ # end
163
+ # end
164
+ # end
165
+ #
166
+ # The first parameter can be a controller class name
167
+ #
168
+ # redirect( MyController )
169
+ #
170
+ # it can be a string path
171
+ #
172
+ # redirect( "/path/to/my/resource" )
173
+ #
174
+ # it can be an http status code (by default <tt>redirect</tt> use the http status code 302)
175
+ #
176
+ # redirect( 304, MyController )
177
+ #
178
+ # For more informations about HTTP status, see http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection
179
+ def redirect( klass, *a )
180
+ httpCode = 302
181
+
182
+ if( klass.class == Fixnum )
183
+ httpCode = klass
184
+ klass = a.shift
185
+ end
186
+
187
+ [httpCode, {'Location' => URL(klass, *a)}, '']
188
+ end
189
+
190
+ # Builds an URL route to a controller or a path
191
+ #
192
+ # if you declare the controller Hello :
193
+ #
194
+ # module Capcode
195
+ # class Hello < Route '/hello/(.*)'
196
+ # ...
197
+ # end
198
+ # end
199
+ #
200
+ # then
201
+ #
202
+ # URL( Capcode::Hello, "you" ) # => /hello/you
203
+ def URL( klass, *a )
204
+ path = nil
205
+ result = {}
206
+
207
+ a = a.delete_if{ |x| x.nil? }
208
+
209
+ if klass.class == Class
210
+ klass.__urls__[0].each do |cpath, regexp|
211
+ data = a.clone
212
+
213
+ n = Regexp.new( regexp ).number_of_captures
214
+ equart = (a.size - n).abs
215
+
216
+ rtable = regexp.dup.gsub( /\\\(/, "" ).gsub( /\\\)/, "" ).split( /\([^\)]*\)/ )
217
+ rtable.each do |r|
218
+ if r == ""
219
+ cpath = cpath + "/#{data.shift}"
220
+ else
221
+ cpath = cpath + "/#{r}"
222
+ end
223
+ end
224
+
225
+ cpath = (cpath + "/" + data.join( "/" )) if data.size > 0
226
+ cpath = cpath.gsub( /(\/){2,}/, "/" )
227
+ result[equart] = cpath
228
+ end
229
+
230
+ path = result[result.keys.min]
231
+ else
232
+ path = klass
233
+ end
234
+
235
+ (ENV['RACK_BASE_URI']||'')+path
236
+ end
237
+
238
+ # Calling content_for stores a block of markup in an identifier.
239
+ #
240
+ # module Capcode
241
+ # class ContentFor < Route '/'
242
+ # def get
243
+ # render( :markaby => :page, :layout => :layout )
244
+ # end
245
+ # end
246
+ # end
247
+ #
248
+ # module Capcode::Views
249
+ # def layout
250
+ # html do
251
+ # head do
252
+ # yield :header
253
+ # end
254
+ # body do
255
+ # yield :content
256
+ # end
257
+ # end
258
+ # end
259
+ #
260
+ # def page
261
+ # content_for :header do
262
+ # title "This is the title!"
263
+ # end
264
+ #
265
+ # content_for :content do
266
+ # p "this is the content!"
267
+ # end
268
+ # end
269
+ # end
270
+ def content_for( x )
271
+ if Capcode::Helpers.args.map{|_| _.to_s }.include?(x.to_s)
272
+ yield
273
+ end
274
+ end
275
+
276
+ # Return information about the static directory
277
+ #
278
+ # * <tt>static[:uri]</tt> give the static URI
279
+ # * <tt>static[:path]</tt> give the path to the static directory on the server
280
+ def static
281
+ {
282
+ :uri => Capcode.static,
283
+ :path => File.expand_path( File.join(Capcode::Configuration.get(:root), Capcode::Configuration.get(:static) ) )
284
+ }
285
+ end
286
+
287
+ # Use the Rack logger
288
+ #
289
+ # log.write( "This is a log !" )
290
+ def log
291
+ Capcode.logger || env['rack.errors']
292
+ end
293
+
294
+ include Authorization
295
+ end
296
+
297
+ include Rack
298
+
299
+ # HTTPError help you to create your own 404, 500 and/or 501 response
300
+ #
301
+ # To create a custom 404 reponse, create a fonction HTTPError.r404 in
302
+ # your application :
303
+ #
304
+ # module Capcode
305
+ # class HTTPError
306
+ # def r404(f)
307
+ # "#{f} not found :("
308
+ # end
309
+ # end
310
+ # end
311
+ #
312
+ # the rXXX method can also receive a second optional parameter corresponding
313
+ # of the header's Hash :
314
+ #
315
+ # module Capcode
316
+ # class HTTPError
317
+ # def r404(f, h)
318
+ # h['Content-Type'] = 'text/plain'
319
+ # "You are here ---> X (#{f} point)"
320
+ # end
321
+ # end
322
+ # end
323
+ #
324
+ # Do the same (r500, r501, r403) to customize 500, 501, 403 errors
325
+ class HTTPError
326
+ def initialize(app) #:nodoc:
327
+ @app = app
328
+ end
329
+
330
+ def call(env) #:nodoc:
331
+ status, headers, body = @app.call(env)
332
+ if self.methods.include? "r#{status}"
333
+ headers.delete('Content-Type') if headers.keys.include?('Content-Type')
334
+ body = begin
335
+ self.send( "r#{status}", env['REQUEST_PATH'], headers )
336
+ rescue
337
+ self.send( "r#{status}", env['REQUEST_PATH'] )
338
+ end
339
+ headers['Content-Length'] = body.length.to_s
340
+ headers['Content-Type'] = "text/html" unless headers.keys.include?('Content-Type')
341
+ end
342
+
343
+ [status, headers, body]
344
+ end
345
+ end
346
+
347
+ class StaticFiles
348
+ def initialize(app)
349
+ @app = app
350
+ end
351
+
352
+ def call(env)
353
+ static = File.expand_path( File.join(Capcode::Configuration.get(:root), Capcode::Configuration.get(:static) ) )
354
+ file = File.join(static, env['REQUEST_PATH'].split("/") )
355
+ file = File.join(file, "index.html" ) if File.directory?(file)
356
+ if File.exist?(file)
357
+ body = [::File.read(file)]
358
+ header = {
359
+ "Last-Modified" => ::File.mtime(file).httpdate,
360
+ "Content-Type" => ::Rack::Mime.mime_type(::File.extname(file), 'text/plain'),
361
+ "Content-Length" => body.first.size.to_s
362
+ }
363
+ return [200, header, body]
364
+ else
365
+ return @app.call(env)
366
+ end
367
+
368
+ return @app.call(env)
369
+ end
370
+ end
371
+
372
+ class << self
373
+ attr :__auth__, true #:nodoc:
374
+
375
+ # Add routes to a controller class
376
+ #
377
+ # module Capcode
378
+ # class Hello < Route '/hello/(.*)', '/hello/([^#]*)#(.*)'
379
+ # def get( arg1, arg2 )
380
+ # ...
381
+ # end
382
+ # end
383
+ # end
384
+ #
385
+ # In the <tt>get</tt> method, you will receive the maximum of parameters declared
386
+ # by the routes. In this example, you will receive 2 parameters. So if you
387
+ # go to <tt>/hello/world#friend</tt> then <tt>arg1</tt> will be set to <tt>world</tt> and <tt>arg2</tt>
388
+ # will be set to <tt>friend</tt>. Now if you go to <tt>/hello/you</tt>, then <tt>arg1</tt> will
389
+ # be set to <tt>you</tt> and <tt>arg2</tt> will be set to <tt>nil</tt>
390
+ #
391
+ # If the regexp in the route does not match, all arguments will be <tt>nil</tt>
392
+ def Route *routes_paths
393
+ create_path = routes_paths[0].nil?
394
+ Class.new {
395
+ meta_def(:__urls__) {
396
+ routes_paths = ['/'+self.to_s.gsub( /^Capcode::/, "" ).underscore] if create_path == true
397
+ # < Route '/hello/world/([^\/]*)/id(\d*)', '/hello/(.*)', :agent => /Songbird (\d\.\d)[\d\/]*?/
398
+ # # => [ {'/hello/world' => '([^\/]*)/id(\d*)', '/hello' => '(.*)'},
399
+ # 2,
400
+ # <Capcode::Klass>,
401
+ # {:agent => /Songbird (\d\.\d)[\d\/]*?/} ]
402
+ hash_of_routes = {}
403
+ max_captures_for_routes = 0
404
+ routes_paths.each do |current_route_path|
405
+ if current_route_path.class == String
406
+ m = /\/([^\/]*\(.*)/.match( current_route_path )
407
+ if m.nil?
408
+ raise Capcode::RouteError, "Route `#{current_route_path}' already defined with regexp `#{hash_of_routes[current_route_path]}' !", caller if hash_of_routes.keys.include?(current_route_path)
409
+ hash_of_routes[current_route_path] = ''
410
+ else
411
+ _pre = m.pre_match
412
+ _pre = "/" if _pre.size == 0
413
+ raise Capcode::RouteError, "Route `#{_pre}' already defined with regexp `#{hash_of_routes[_pre]}' !", caller if hash_of_routes.keys.include?(_pre)
414
+ hash_of_routes[_pre] = m.captures[0]
415
+ max_captures_for_routes = Regexp.new(m.captures[0]).number_of_captures if max_captures_for_routes < Regexp.new(m.captures[0]).number_of_captures
416
+ end
417
+ else
418
+ raise Capcode::ParameterError, "Bad route declaration !", caller
419
+ end
420
+ end
421
+ [hash_of_routes, max_captures_for_routes, self]
422
+ }
423
+
424
+ # Hash containing all the request parameters (GET or POST)
425
+ def params
426
+ @request.params
427
+ end
428
+
429
+ # Hash containing all the environment variables
430
+ def env
431
+ @env
432
+ end
433
+
434
+ # Session hash
435
+ def session
436
+ @env['rack.session']
437
+ end
438
+
439
+ # Return the Rack::Request object
440
+ def request
441
+ @request
442
+ end
443
+
444
+ # Return the Rack::Response object
445
+ def response
446
+ @response
447
+ end
448
+
449
+ def call( e ) #:nodoc:
450
+ @env = e
451
+ @response = Rack::Response.new
452
+ @request = Rack::Request.new(@env)
453
+
454
+ # Check authz
455
+ authz_options = nil
456
+ if Capcode.__auth__ and Capcode.__auth__.size > 0
457
+ authz_options = Capcode.__auth__[@request.path]||nil
458
+ if authz_options.nil?
459
+ route = nil
460
+
461
+ Capcode.__auth__.each do |r, o|
462
+ regexp = "^#{r.gsub(/\/$/, "")}([/]{1}.*)?$"
463
+ if Regexp.new(regexp).match( @request.path )
464
+ if route.nil? or r.size > route.size
465
+ route = r
466
+ authz_options = o
467
+ end
468
+ end
469
+ end
470
+ end
471
+ end
472
+
473
+ r = catch(:halt) {
474
+ unless authz_options.nil?
475
+ http_authentication( :type => authz_options[:type], :realm => authz_options[:realm], :opaque => authz_options[:realm] ) {
476
+ authz_options[:autz]
477
+ }
478
+ end
479
+
480
+ finalPath = nil
481
+ finalArgs = nil
482
+ finalNArgs = nil
483
+
484
+ aPath = @request.path.gsub( /^\//, "" ).split( "/" )
485
+ print "aPath = "; p aPath
486
+ self.class.__urls__[0].each do |p, r|
487
+ puts "p = #{p}, r = #{r}"
488
+ xPath = p.gsub( /^\//, "" ).split( "/" )
489
+ puts "xPath = "; p xPath
490
+ if (xPath - aPath).size == 0
491
+ diffArgs = aPath - xPath
492
+ diffNArgs = diffArgs.size # -1 ##################################### Why ?
493
+ if finalNArgs.nil? or finalNArgs > diffNArgs
494
+ finalPath = p
495
+ finalNArgs = diffNArgs
496
+ finalArgs = diffArgs
497
+ end
498
+ end
499
+ end
500
+
501
+ puts "finalPath = #{finalPath}"
502
+ puts "finalNArgs = #{finalNArgs}"
503
+ require 'pp'
504
+ pp self.class.__urls__
505
+ puts "self.class.__urls__[1] = #{self.class.__urls__[1]}"
506
+ puts "finalArgs = "; pp finalArgs
507
+
508
+ if finalNArgs > self.class.__urls__[1]
509
+ return [404, {'Content-Type' => 'text/plain'}, "Not Found: #{@request.path}"]
510
+ end
511
+
512
+ nargs = self.class.__urls__[1]
513
+ regexp = Regexp.new( self.class.__urls__[0][finalPath] )
514
+ args = regexp.match( Rack::Utils.unescape(@request.path).gsub( Regexp.new( "^#{finalPath}" ), "" ).gsub( /^\//, "" ) )
515
+ if args.nil?
516
+ raise Capcode::ParameterError, "Path info `#{@request.path_info}' does not match route regexp `#{regexp.source}'"
517
+ else
518
+ args = args.captures.map { |x| (x.size == 0)?nil:x }
519
+ end
520
+
521
+ while args.size < nargs
522
+ args << nil
523
+ end
524
+
525
+ filter_output = Capcode::Filter.execute( self )
526
+
527
+ if( filter_output.nil? )
528
+ # case @env["REQUEST_METHOD"]
529
+ # when "GET"
530
+ # get( *args )
531
+ # when "POST"
532
+ # _method = params.delete( "_method" ) { |_| "post" }
533
+ # send( _method.downcase.to_sym, *args )
534
+ # else
535
+ # _method = @env["REQUEST_METHOD"]
536
+ # send( _method.downcase.to_sym, *args )
537
+ # end
538
+ begin
539
+ _method = params.delete( "_method" ) { |_| @env["REQUEST_METHOD"] }
540
+ if self.class.method_defined?( _method.downcase.to_sym )
541
+ send( _method.downcase.to_sym, *args )
542
+ else
543
+ any( *args )
544
+ end
545
+ rescue => e
546
+ raise e.class, e.to_s
547
+ end
548
+ else
549
+ filter_output
550
+ end
551
+ }
552
+
553
+ if r.respond_to?(:to_ary)
554
+ @response.status = r.shift #r[0]
555
+ #r[1].each do |k,v|
556
+ r.shift.each do |k,v|
557
+ @response[k] = v
558
+ end
559
+ @response.write r.shift #r[2]
560
+ else
561
+ @response.write r
562
+ end
563
+
564
+ @response.finish
565
+ end
566
+
567
+ include Capcode::Helpers
568
+ include Capcode::Views
569
+ }
570
+ end
571
+ Capcode::Route = Capcode::Route(nil)
572
+
573
+ # This method help you to map and URL to a Rack or What you want Helper
574
+ #
575
+ # Capcode.map( "/file" ) do
576
+ # Rack::File.new( "." )
577
+ # end
578
+ def map( route, &b )
579
+ Capcode.routes[route] = yield
580
+ end
581
+
582
+ # This method allow you to use a Rack middleware
583
+ #
584
+ # Example :
585
+ #
586
+ # module Capcode
587
+ # ...
588
+ # use Rack::Codehighlighter, :coderay, :element => "pre",
589
+ # :pattern => /\A:::(\w+)\s*\n/, :logging => false
590
+ # ...
591
+ # end
592
+ def use(middleware, *args, &block)
593
+ middlewares << [middleware, args, block]
594
+ end
595
+ def middlewares #:nodoc:
596
+ @middlewares ||= []
597
+ end
598
+
599
+ # Allow you to add and HTTP Authentication (Basic or Digest) to controllers for or specific route
600
+ #
601
+ # Options :
602
+ # * <tt>:type</tt> : Authentication type (<tt>:basic</tt> or <tt>:digest</tt>) - default : <tt>:basic</tt>
603
+ # * <tt>:realm</tt> : realm ;) - default : "Capcode.app"
604
+ # * <tt>:opaque</tt> : Your secret passphrase. You MUST set it if you use Digest Auth - default : "opaque"
605
+ # * <tt>:routes</tt> : Routes - default : "/"
606
+ #
607
+ # The block must return a Hash of username => password like that :
608
+ # {
609
+ # "user1" => "pass1",
610
+ # "user2" => "pass2",
611
+ # # ...
612
+ # }
613
+ def http_authentication( opts = {}, &b )
614
+ options = {
615
+ :type => :basic,
616
+ :realm => "Capcode.app",
617
+ :opaque => "opaque",
618
+ :routes => "/"
619
+ }.merge( opts )
620
+
621
+ options[:autz] = b.call()
622
+
623
+ @__auth__ ||= {}
624
+
625
+ if options[:routes].class == Array
626
+ options[:routes].each do |r|
627
+ @__auth__[r] = options
628
+ end
629
+ else
630
+ @__auth__[options[:routes]] = options
631
+ end
632
+ end
633
+
634
+ # Return the Rack App.
635
+ #
636
+ # Options : see Capcode::Configuration.set
637
+ #
638
+ # Options set here replace the ones set globally
639
+ def application( args = {} )
640
+ Capcode::Configuration.configuration(args)
641
+ Capcode::Configuration.print_debug if Capcode::Configuration.get(:verbose)
642
+
643
+ Capcode.constants.clone.delete_if {|k|
644
+ not( Capcode.const_get(k).to_s =~ /Capcode/ ) or [
645
+ "Filter",
646
+ "Helpers",
647
+ "RouteError",
648
+ "Views",
649
+ "ParameterError",
650
+ "HTTPError",
651
+ "StaticFiles",
652
+ "Configuration",
653
+ "MissingLibrary",
654
+ "Route",
655
+ "RenderError"
656
+ ].include?(k)
657
+ }.each do |k|
658
+ begin
659
+ if eval "Capcode::#{k}.public_methods(true).include?( '__urls__' )"
660
+ hash_of_routes, max_captures_for_routes, klass = eval "Capcode::#{k}.__urls__"
661
+ hash_of_routes.keys.each do |current_route_path|
662
+ #raise Capcode::RouteError, "Route `#{current_route_path}' already define !", caller if @@__ROUTES.keys.include?(current_route_path)
663
+ raise Capcode::RouteError, "Route `#{current_route_path}' already define !", caller if Capcode.routes.keys.include?(current_route_path)
664
+ # Capcode.routes[current_route_path] = klass.new
665
+ Capcode.routes[current_route_path] = klass
666
+ end
667
+ end
668
+ rescue => e
669
+ raise e.message
670
+ end
671
+ end
672
+
673
+ # Set Static directory
674
+ Capcode.static = (Capcode::Configuration.get(:static)[0].chr == "/")?Capcode::Configuration.get(:static):"/"+Capcode::Configuration.get(:static) unless Capcode::Configuration.get(:static).nil?
675
+
676
+ # Initialize Rack App
677
+ puts "** Map routes." if Capcode::Configuration.get(:verbose)
678
+ # app = Rack::URLMap.new(Capcode.routes)
679
+ app = Capcode::Ext::Rack::URLMap.new(Capcode.routes)
680
+ puts "** Initialize static directory (#{Capcode.static}) in #{File.expand_path(Capcode::Configuration.get(:root))}" if Capcode::Configuration.get(:verbose)
681
+ app = Rack::Static.new(
682
+ app,
683
+ #:urls => [@@__STATIC_DIR],
684
+ :urls => [Capcode.static],
685
+ :root => File.expand_path(Capcode::Configuration.get(:root))
686
+ ) unless Capcode::Configuration.get(:static).nil?
687
+ puts "** Initialize session" if Capcode::Configuration.get(:verbose)
688
+ app = Rack::Session::Cookie.new( app, Capcode::Configuration.get(:session) )
689
+ app = Capcode::StaticFiles.new(app)
690
+ app = Capcode::HTTPError.new(app)
691
+ app = Rack::ContentLength.new(app)
692
+ app = Rack::Lint.new(app)
693
+ app = Rack::ShowExceptions.new(app)
694
+ #app = Rack::Reloader.new(app) ## -- NE RELOAD QUE capcode.rb -- So !!!
695
+ app = Rack::CommonLogger.new( app, @cclogger = Logger.new(Capcode::Configuration.get(:log)) )
696
+
697
+ middlewares.each do |mw|
698
+ middleware, args, block = mw
699
+ puts "** Load middleware #{middleware}" if Capcode::Configuration.get(:verbose)
700
+ if block
701
+ app = middleware.new( app, *args, &block )
702
+ else
703
+ app = middleware.new( app, *args )
704
+ end
705
+ end
706
+
707
+ # Start database
708
+ if self.methods.include? "db_connect"
709
+ db_connect( Capcode::Configuration.get(:db_config), Capcode::Configuration.get(:log) )
710
+ end
711
+
712
+ if block_given?
713
+ puts "** Execute block" if Capcode::Configuration.get(:verbose)
714
+ yield( self )
715
+ end
716
+
717
+ return app
718
+ end
719
+
720
+ # Start your application.
721
+ #
722
+ # Options : see Capcode::Configuration.set
723
+ #
724
+ # Options set here replace the ones set globally
725
+ def run( args = {} )
726
+ Capcode::Configuration.configuration(args)
727
+
728
+ # Parse options
729
+ opts = OptionParser.new do |opts|
730
+ opts.banner = "Usage: #{File.basename($0)} [options]"
731
+ opts.separator ""
732
+ opts.separator "Specific options:"
733
+
734
+ opts.on( "-C", "--console", "Run in console mode with IRB (default: false)" ) {
735
+ Capcode::Configuration.set :console, true
736
+ }
737
+ opts.on( "-h", "--host HOSTNAME", "Host for web server to bind to (default: #{Capcode::Configuration.get(:host)})" ) { |h|
738
+ Capcode::Configuration.set :host, h
739
+ }
740
+ opts.on( "-p", "--port NUM", "Port for web server (default: #{Capcode::Configuration.get(:port)})" ) { |p|
741
+ Capcode::Configuration.set :port, p
742
+ }
743
+ opts.on( "-d", "--daemonize [true|false]", "Daemonize (default: #{Capcode::Configuration.get(:daemonize)})" ) { |d|
744
+ Capcode::Configuration.set :daemonize, d
745
+ }
746
+ opts.on( "-r", "--root PATH", "Working directory (default: #{Capcode::Configuration.get(:root)})" ) { |w|
747
+ Capcode::Configuration.set :root, w
748
+ }
749
+ opts.on( "-s", "--static PATH", "Static directory -- relative to the root directory (default: #{Capcode::Configuration.get(:static)})" ) { |r|
750
+ Capcode::Configuration.set :static, r
751
+ }
752
+ opts.on( "-S", "--server SERVER", "Server to use (default: #{Capcode::Configuration.get(:server)})" ) { |r|
753
+ Capcode::Configuration.set :server, r
754
+ }
755
+
756
+ opts.separator ""
757
+ opts.separator "Common options:"
758
+
759
+ opts.on("-?", "--help", "Show this message") do
760
+ puts opts
761
+ exit
762
+ end
763
+ opts.on("-v", "--version", "Show versions") do
764
+ puts "Capcode version #{Capcode::CAPCOD_VERION} (ruby v#{RUBY_VERSION})"
765
+ exit
766
+ end
767
+ opts.on_tail( "-V", "--verbose", "Run in verbose mode" ) do
768
+ Capcode::Configuration.set :verbose, true
769
+ end
770
+ end
771
+
772
+ begin
773
+ opts.parse! ARGV
774
+ rescue OptionParser::ParseError => ex
775
+ puts "!! #{ex.message}"
776
+ puts "** use `#{File.basename($0)} --help` for more details..."
777
+ exit 1
778
+ end
779
+
780
+ # Run in the Working directory
781
+ puts "** Go on root directory (#{File.expand_path(Capcode::Configuration.get(:root))})" if Capcode::Configuration.get(:verbose)
782
+ Dir.chdir( Capcode::Configuration.get(:root) ) do
783
+
784
+ # Check that mongrel exists
785
+ if Capcode::Configuration.get(:server).nil? || Capcode::Configuration.get(:server) == "mongrel"
786
+ begin
787
+ require 'mongrel'
788
+ Capcode::Configuration.set :server, :mongrel
789
+ rescue LoadError
790
+ puts "!! could not load mongrel. Falling back to webrick."
791
+ Capcode::Configuration.set :server, :webrick
792
+ end
793
+ end
794
+
795
+ # From rackup !!!
796
+ if Capcode::Configuration.get(:daemonize)
797
+ if /java/.match(RUBY_PLATFORM).nil?
798
+ if RUBY_VERSION < "1.9"
799
+ exit if fork
800
+ Process.setsid
801
+ exit if fork
802
+ # Dir.chdir "/"
803
+ File.umask 0000
804
+ STDIN.reopen "/dev/null"
805
+ STDOUT.reopen "/dev/null", "a"
806
+ STDERR.reopen "/dev/null", "a"
807
+ else
808
+ Process.daemon
809
+ end
810
+ else
811
+ puts "!! daemonize option unavailable on #{RUBY_PLATFORM} platform."
812
+ end
813
+
814
+ File.open(Capcode::Configuration.get(:pid), 'w'){ |f| f.write("#{Process.pid}") }
815
+ at_exit { File.delete(Capcode::Configuration.get(:pid)) if File.exist?(Capcode::Configuration.get(:pid)) }
816
+ end
817
+
818
+ app = nil
819
+ if block_given?
820
+ app = application(Capcode::Configuration.get) { yield( self ) }
821
+ else
822
+ app = application(Capcode::Configuration.get)
823
+ end
824
+
825
+ if Capcode::Configuration.get(:console)
826
+ puts "Run console..."
827
+ IRB.start
828
+ exit
829
+ end
830
+
831
+ # Start server
832
+ case Capcode::Configuration.get(:server).to_s
833
+ when "mongrel"
834
+ puts "** Starting Mongrel on #{Capcode::Configuration.get(:host)}:#{Capcode::Configuration.get(:port)}"
835
+ Rack::Handler::Mongrel.run( app, {:Port => Capcode::Configuration.get(:port), :Host => Capcode::Configuration.get(:host)} ) { |server|
836
+ trap "SIGINT", proc { server.stop }
837
+ }
838
+ when "webrick"
839
+ puts "** Starting WEBrick on #{Capcode::Configuration.get(:host)}:#{Capcode::Configuration.get(:port)}"
840
+ Rack::Handler::WEBrick.run( app, {:Port => Capcode::Configuration.get(:port), :BindAddress => Capcode::Configuration.get(:host)} ) { |server|
841
+ trap "SIGINT", proc { server.shutdown }
842
+ }
843
+ when "thin"
844
+ puts "** Starting Thin on #{Capcode::Configuration.get(:host)}:#{Capcode::Configuration.get(:port)}"
845
+ Rack::Handler::Thin.run( app, {:Port => Capcode::Configuration.get(:port), :Host => Capcode::Configuration.get(:host)} ) { |server|
846
+ trap "SIGINT", proc { server.stop }
847
+ }
848
+ when "unicorn"
849
+ require 'unicorn/launcher'
850
+ puts "** Starting Unicorn on #{Capcode::Configuration.get(:host)}:#{Capcode::Configuration.get(:port)}"
851
+ Unicorn.run( app, {:listeners => ["#{Capcode::Configuration.get(:host)}:#{Capcode::Configuration.get(:port)}"]} )
852
+ when "rainbows"
853
+ require 'unicorn/launcher'
854
+ require 'rainbows'
855
+ puts "** Starting Rainbow on #{Capcode::Configuration.get(:host)}:#{Capcode::Configuration.get(:port)}"
856
+ Rainbows.run( app, {:listeners => ["#{Capcode::Configuration.get(:host)}:#{Capcode::Configuration.get(:port)}"]} )
857
+ when "control_tower"
858
+ require 'control_tower'
859
+ puts "** Starting ControlTower on #{Capcode::Configuration.get(:host)}:#{Capcode::Configuration.get(:port)}"
860
+ ControlTower::Server.new( app, {:host => Capcode::Configuration.get(:host), :port => Capcode::Configuration.get(:port)} ).start
861
+ end
862
+ end
863
+ end
864
+
865
+ def routes #:nodoc:
866
+ @routes ||= {}
867
+ end
868
+
869
+ def logger
870
+ @cclogger
871
+ end
872
+
873
+ def static #:nodoc:
874
+ @static_dir ||= nil
875
+ end
876
+ def static=(x) #:nodoc:
877
+ @static_dir = x
878
+ end
879
+
880
+ end
881
+ end