waves 0.6.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. data/app/Rakefile +4 -0
  2. data/app/configurations/default.rb.erb +8 -0
  3. data/app/configurations/development.rb.erb +22 -0
  4. data/app/configurations/mapping.rb.erb +49 -0
  5. data/app/configurations/production.rb.erb +25 -0
  6. data/app/controllers/default.rb.erb +39 -0
  7. data/app/doc/EMTPY +0 -0
  8. data/app/helpers/default.rb.erb +13 -0
  9. data/app/lib/application.rb.erb +52 -0
  10. data/app/lib/startup.rb.erb +2 -0
  11. data/app/lib/tasks/cluster.rb +26 -0
  12. data/app/lib/tasks/schema.rb +28 -0
  13. data/app/models/default.rb.erb +13 -0
  14. data/app/schema/migration/templates/empty.rb.erb +9 -0
  15. data/app/templates/errors/not_found.mab +2 -0
  16. data/app/templates/errors/server_error.mab +2 -0
  17. data/app/templates/layouts/default.mab +14 -0
  18. data/app/views/default.rb.erb +13 -0
  19. data/bin/waves +32 -0
  20. data/bin/waves-console +8 -0
  21. data/bin/waves-server +45 -0
  22. data/lib/controllers/mixin.rb +62 -0
  23. data/lib/dispatchers/base.rb +41 -0
  24. data/lib/dispatchers/default.rb +33 -0
  25. data/lib/helpers/common.rb +22 -0
  26. data/lib/helpers/form.rb +22 -0
  27. data/lib/helpers/formatting.rb +22 -0
  28. data/lib/helpers/model.rb +15 -0
  29. data/lib/helpers/view.rb +14 -0
  30. data/lib/renderers/erubis.rb +38 -0
  31. data/lib/renderers/markaby.rb +31 -0
  32. data/lib/renderers/mixin.rb +41 -0
  33. data/lib/runtime/application.rb +43 -0
  34. data/lib/runtime/configuration.rb +47 -0
  35. data/lib/runtime/console.rb +21 -0
  36. data/lib/runtime/logger.rb +28 -0
  37. data/lib/runtime/mapping.rb +82 -0
  38. data/lib/runtime/mime_types.rb +18 -0
  39. data/lib/runtime/request.rb +43 -0
  40. data/lib/runtime/response.rb +26 -0
  41. data/lib/runtime/response_mixin.rb +53 -0
  42. data/lib/runtime/response_proxy.rb +29 -0
  43. data/lib/runtime/server.rb +58 -0
  44. data/lib/runtime/session.rb +43 -0
  45. data/lib/utilities/integer.rb +8 -0
  46. data/lib/utilities/kernel.rb +8 -0
  47. data/lib/utilities/module.rb +7 -0
  48. data/lib/utilities/object.rb +13 -0
  49. data/lib/utilities/string.rb +36 -0
  50. data/lib/utilities/symbol.rb +5 -0
  51. data/lib/views/mixin.rb +54 -0
  52. data/lib/waves.rb +50 -0
  53. metadata +189 -0
@@ -0,0 +1,21 @@
1
+ module Waves
2
+
3
+ class Console < Application
4
+
5
+ class << self
6
+
7
+ attr_reader :console
8
+
9
+ def load(mode=:development)
10
+ @console ||= Waves::Console.new(mode)
11
+ Kernel.load( :lib / 'startup.rb' )
12
+ end
13
+
14
+ # allow Waves::Console to act as The Console Instance
15
+ def method_missing(*args); @console.send(*args); end
16
+
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,28 @@
1
+ require 'logger'
2
+ module Waves
3
+
4
+ module Logger
5
+
6
+ # Initializes the logger based on the config, and then adds Waves#log.
7
+
8
+ def self.start
9
+ config = Waves::Server.config
10
+ output = ( config.log[:output] || $stderr )
11
+ level = ::Logger.const_get( config.log[:level].to_s.upcase || 'INFO' )
12
+ @log = if config.log(:rotation)
13
+ ::Logger.new( output, config.log[:rotation].intern );
14
+ else
15
+ ::Logger.new( output );
16
+ end
17
+ @log.level = level
18
+ end
19
+
20
+ # delegate everything to the logger
21
+ def self.method_missing(name,*args,&block)
22
+ @log.send name,*args, &block
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+
@@ -0,0 +1,82 @@
1
+ module Waves
2
+
3
+ module Mapping
4
+
5
+ def before( pattern, options=nil, &block )
6
+ filters[:before] << [ pattern, options, block ]
7
+ end
8
+
9
+ def after( pattern, options=nil, &block )
10
+ filters[:after] << [ pattern, options, block ]
11
+ end
12
+
13
+ def wrap( pattern, options=nil, &block )
14
+ filters[:before] << [ pattern, options, block ]
15
+ filters[:after] << [ pattern, options, block ]
16
+ end
17
+
18
+ def map( options, &block )
19
+ pattern = options[:path] || options[:url]
20
+ mapping << [ pattern, options, block ]
21
+ end
22
+
23
+ def path( pat, options = {}, &block )
24
+ options[:path] = pat; map( options, &block )
25
+ end
26
+
27
+ def url( pat, options = {}, &block )
28
+ options[:url] = pat; map( options, block )
29
+ end
30
+
31
+ def []( request )
32
+
33
+ rx = { :before => [], :after => [], :action => nil }
34
+
35
+ ( filters[:before] + filters[:wrap] ).each do | pattern, options, function |
36
+ matches = pattern.match(request.path)
37
+ rx[:before] << [ function, matches[1..-1] ] if matches &&
38
+ ( ! options || satisfy( request, options ))
39
+ end
40
+
41
+ mapping.find do |pattern, options, function|
42
+ matches = pattern.match(request.path)
43
+ rx[:action] = [ function, matches[1..-1] ] if matches &&
44
+ ( ! options || satisfy( request, options ) )
45
+ end
46
+
47
+ ( filters[:after] + filters[:wrap] ).each do | pattern, options, function |
48
+ matches = pattern.match(request.path)
49
+ rx[:after] << [ function, matches[1..-1] ] if matches &&
50
+ ( ! options || satisfy( request, options ))
51
+ end
52
+
53
+ not_found(request) unless rx[:action]
54
+
55
+ return rx
56
+
57
+ end
58
+
59
+ private
60
+
61
+ def mapping; @mapping ||= []; end
62
+
63
+ def filters; @filters ||= { :before => [], :after => [], :wrap => [] }; end
64
+
65
+ def not_found(request)
66
+ raise Waves::Dispatchers::NotFoundError.new( request.url + ' not found.')
67
+ end
68
+
69
+ def satisfy( request, options )
70
+ options.each do |method, param|
71
+ return false unless self.send( method, param, request )
72
+ end
73
+ return true
74
+ end
75
+
76
+ def method( method, request )
77
+ request.method == method
78
+ end
79
+
80
+ end
81
+
82
+ end
@@ -0,0 +1,18 @@
1
+ module Waves
2
+ module MimeTypes
3
+
4
+ def self.[]( path )
5
+ mapping[ File.extname( path ) ]
6
+ end
7
+
8
+ # TODO: why isn't this working?
9
+ # def self.<<( mapping )
10
+ # mapping.merge!( mapping )
11
+ # end
12
+
13
+ def self.mapping
14
+ @mapping ||= Mongrel::DirHandler::MIME_TYPES
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,43 @@
1
+ module Waves
2
+ class Request
3
+
4
+ class ParseError < Exception ; end
5
+
6
+ attr_reader :response, :session
7
+
8
+ def initialize( env ) # Rack::Request
9
+ @request = Rack::Request.new( env )
10
+ @response = Waves::Response.new( self )
11
+ @session = Waves::Session.new( self )
12
+ end
13
+
14
+ # if we haven't overridden it, give it to Rack
15
+ def method_missing(name,*args)
16
+ @request.send(name,*args)
17
+ end
18
+
19
+ # TODO: should / does this exclude the query_string?
20
+ # Is there a Rack API that is more appropos
21
+ def path
22
+ @request.path_info
23
+ end
24
+
25
+ def domain
26
+ @request.host
27
+ end
28
+
29
+ def method
30
+ @request.request_method.downcase.intern
31
+ end
32
+
33
+ def not_found
34
+ raise Waves::Dispatchers::NotFoundError.new( @request.url + ' not found.')
35
+ end
36
+
37
+ def redirect( path )
38
+ raise Waves::Dispatchers::Redirect.new( path )
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,26 @@
1
+ module Waves
2
+ class Response
3
+
4
+ attr_reader :request
5
+
6
+ def initialize( request )
7
+ @request = request
8
+ @response = Rack::Response.new
9
+ end
10
+
11
+ %w( Content-Type Content-Length Location Expires ).each do |header|
12
+ define_method( header.downcase.gsub('-','_')+ '=' ) do | val |
13
+ @response.headers[header] = val
14
+ end
15
+ end
16
+
17
+ def session ; request.session ; end
18
+
19
+ def finish ; request.session.save ; @response.finish ; end
20
+
21
+ def method_missing(name,*args)
22
+ @response.send(name,*args)
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,53 @@
1
+ module Waves
2
+
3
+ module ResponseMixin
4
+
5
+ # assumes request method
6
+
7
+ def response
8
+ request.response
9
+ end
10
+
11
+ def params
12
+ request.params
13
+ end
14
+
15
+ def session
16
+ request.session
17
+ end
18
+
19
+ def path
20
+ request.path
21
+ end
22
+
23
+ def url
24
+ request.url
25
+ end
26
+
27
+ def domain
28
+ request.domain
29
+ end
30
+
31
+ def redirect(location)
32
+ request.redirect(location)
33
+ end
34
+
35
+ def models
36
+ Waves.application.models
37
+ end
38
+
39
+ def views
40
+ Waves.application.views
41
+ end
42
+
43
+ def controllers
44
+ Waves.application.controllers
45
+ end
46
+
47
+ def not_found
48
+ request.not_found
49
+ end
50
+
51
+ end
52
+
53
+ end
@@ -0,0 +1,29 @@
1
+ module Waves
2
+
3
+ class ResponseProxy
4
+
5
+ attr_reader :request
6
+
7
+ include ResponseMixin
8
+
9
+ def initialize(request); @request = request; end
10
+
11
+ def use( model ) ; @model = model ; self ; end
12
+
13
+ def controller( &block )
14
+ Waves.application.controllers[ @model ].process( @request, &block )
15
+ end
16
+
17
+ def view( &block )
18
+ Waves.application.views[ @model ].process( @request, @value, &block )
19
+ end
20
+
21
+ def |( val ); @value = val; self; end
22
+
23
+ def to_s; @value ; end
24
+
25
+ def redirect(path); @request.redirect(path); end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,58 @@
1
+ module Waves
2
+
3
+ class Server < Application
4
+
5
+ attr_reader :thread
6
+
7
+ def start
8
+ load( :lib / 'startup.rb' )
9
+ Waves::Logger.start
10
+ log.info "** Waves Server Starting ..."
11
+
12
+ t = Benchmark.realtime do
13
+ app = config.application.to_app
14
+ @server = ::Mongrel::HttpServer.new( config.host, config.port )
15
+ @server.register('/', Rack::Handler::Mongrel.new( app ) )
16
+ trap('INT') { puts; stop }
17
+ @thread = @server.run
18
+ end
19
+ log.info "** Waves Server Running on #{config.host}:#{config.port}"
20
+ log.info "Server started in #{(t*1000).round} milliseconds."
21
+ @thread.join
22
+ end
23
+
24
+ def stop
25
+ log.info "** Waves Server Stopping ..."
26
+ @server.stop
27
+ log.info "** Waves Server Stopped"
28
+ end
29
+
30
+ def synchronize( &block )
31
+ ( @mutex ||= Mutex.new ).synchronize( &block )
32
+ end
33
+
34
+ def cache
35
+ #@cache ||= MemCache::new '127.0.0.1'
36
+ end
37
+
38
+ # make this a singleton ... we don't use Ruby's std lib
39
+ # singleton module because it doesn't do quite what we
40
+ # want - need a run method and implicit instance method
41
+ class << self
42
+
43
+ private :new, :dup, :clone
44
+
45
+ def run( mode = :development )
46
+ @server.stop if @server; @server = new( mode ); @server.start
47
+ end
48
+
49
+ # allow Waves::Server to act as The Server Instance
50
+ def method_missing(*args); @server.send(*args); end
51
+
52
+ def synchronize(&block) ; @server.synchronize(&block) ; end
53
+
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,43 @@
1
+ module Waves
2
+ class Session
3
+
4
+ def initialize( request )
5
+ @request = request
6
+ @data ||= ( File.exist?( session_path ) ? load_session : {} )
7
+ end
8
+
9
+ def save
10
+ if @data && @data.length > 0
11
+ File.write( session_path, @data.to_yaml )
12
+ @request.response.set_cookie( 'session_id',
13
+ :value => session_id, :path => '/',
14
+ :expires => Time.now + Waves::Server.config.session[:duration] )
15
+ end
16
+ end
17
+
18
+ def [](key) ; @data[key] ; end
19
+ def []=(key,val) ; @data[key] = val ; end
20
+
21
+ private
22
+
23
+ def session_id
24
+ @request.cookies['session_id'] || generate_session_id
25
+ end
26
+
27
+ def generate_session_id # from Camping ...
28
+ chars = [*'A'..'Z'] + [*'0'..'9'] + [*'a'..'z']
29
+ (0..48).inject(''){|s,x| s+=chars[ rand(chars.length) ] }
30
+ end
31
+
32
+ def session_path
33
+ Waves::Server.config.session[:path] / session_id
34
+ end
35
+
36
+ def load_session
37
+ YAML.load( File.read( session_path ) )
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+
@@ -0,0 +1,8 @@
1
+ class Integer
2
+ def seconds ; self ; end
3
+ def minutes ; self * 60 ; end
4
+ def hours ; self * 60.minutes ; end
5
+ def days ; self * 24.hours ; end
6
+ def weeks ; self * 7.days ; end
7
+ # months and years are not precise
8
+ end
@@ -0,0 +1,8 @@
1
+ module Kernel
2
+
3
+ # inspired by similar function in rails ...
4
+ def returning( object, &block )
5
+ yield object; object
6
+ end
7
+
8
+ end
@@ -0,0 +1,7 @@
1
+ class Module
2
+
3
+ def basename
4
+ self.name.split('::').last || ''
5
+ end
6
+
7
+ end