waves 0.6.7 → 0.6.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/app/Rakefile +1 -1
  2. data/app/configurations/default.rb.erb +1 -1
  3. data/app/configurations/mapping.rb.erb +6 -37
  4. data/app/controllers/default.rb.erb +10 -20
  5. data/app/lib/tasks/cluster.rb +8 -10
  6. data/app/lib/tasks/generate.rb +15 -0
  7. data/app/lib/tasks/schema.rb +6 -5
  8. data/app/schema/{migration → migrations}/templates/empty.rb.erb +0 -0
  9. data/app/templates/errors/{not_found.mab → not_found_404.mab} +0 -0
  10. data/app/templates/errors/{server_error.mab → server_error_500.mab} +0 -0
  11. data/app/templates/layouts/default.mab +1 -1
  12. data/bin/waves +1 -1
  13. data/bin/waves-console +21 -4
  14. data/lib/controllers/mixin.rb +85 -16
  15. data/lib/dispatchers/base.rb +26 -15
  16. data/lib/dispatchers/default.rb +25 -4
  17. data/lib/helpers/common.rb +50 -10
  18. data/lib/helpers/form.rb +18 -1
  19. data/lib/helpers/formatting.rb +17 -9
  20. data/lib/helpers/model.rb +20 -2
  21. data/lib/helpers/view.rb +12 -2
  22. data/lib/mapping/mapping.rb +187 -0
  23. data/lib/mapping/pretty_urls.rb +95 -0
  24. data/lib/renderers/erubis.rb +3 -1
  25. data/lib/renderers/markaby.rb +6 -4
  26. data/lib/renderers/mixin.rb +12 -0
  27. data/lib/runtime/application.rb +33 -23
  28. data/lib/runtime/configuration.rb +131 -9
  29. data/lib/runtime/console.rb +2 -2
  30. data/lib/runtime/logger.rb +42 -18
  31. data/lib/runtime/mime_types.rb +8 -4
  32. data/lib/runtime/request.rb +14 -5
  33. data/lib/runtime/response.rb +15 -1
  34. data/lib/runtime/response_mixin.rb +29 -47
  35. data/lib/runtime/server.rb +60 -35
  36. data/lib/runtime/session.rb +14 -1
  37. data/lib/utilities/integer.rb +7 -1
  38. data/lib/utilities/kernel.rb +28 -2
  39. data/lib/utilities/module.rb +3 -0
  40. data/lib/utilities/object.rb +5 -1
  41. data/lib/utilities/string.rb +7 -2
  42. data/lib/utilities/symbol.rb +2 -0
  43. data/lib/views/mixin.rb +55 -1
  44. data/lib/waves.rb +9 -2
  45. metadata +10 -8
  46. data/lib/runtime/mapping.rb +0 -82
@@ -2,6 +2,8 @@ require 'erubis'
2
2
 
3
3
  module Erubis
4
4
 
5
+ # This is added to the Erubis Content class to allow the same helper methods
6
+ # to be used with both Markaby and Erubis.
5
7
  class Context
6
8
  def <<(s) ; s ; end
7
9
  end
@@ -11,7 +13,7 @@ end
11
13
  module Waves
12
14
 
13
15
  module Renderers
14
-
16
+
15
17
  class Erubis
16
18
 
17
19
  include Renderers::Mixin
@@ -11,17 +11,19 @@ module Waves
11
11
  include Renderers::Mixin
12
12
 
13
13
  extension :mab
14
+
14
15
  # capture needed here for content fragments, otherwise
15
16
  # you'll just get the last tag's output ...
16
- def self.capture( template )
17
- "capture { #{template} }"
18
- end
17
+ # def self.capture( template )
18
+ # "capture { #{template} }"
19
+ # end
19
20
 
20
21
  def self.render( path, assigns )
21
22
  builder = ::Markaby::Builder.new( assigns )
22
23
  helper = helper( path )
23
24
  builder.meta_eval { include( helper ) }
24
- builder.instance_eval( capture( template( path ) ) ).to_s
25
+ builder.instance_eval( template( path ) )
26
+ builder.to_s
25
27
  end
26
28
 
27
29
  end
@@ -5,8 +5,20 @@ module Waves
5
5
  extend Autoload
6
6
  autoload :renderers
7
7
 
8
+ # The renderers mixin provides a number of methods to simplify writing new renderers.
9
+ # Just include this in your Renderer class and write your render method.
8
10
  module Mixin
9
11
 
12
+ # Adds the following methods to the target class:
13
+ #
14
+ # - extension: allows you to set or get the extension used by this renderer.
15
+ #
16
+ # Renderers::Markaby.extension 'foo' # tell Waves to use .foo as Markaby extension
17
+ #
18
+ # - filename: generate a filename for the template based on a logical path.
19
+ # - template: read the template from the file corresponding to the given logical path.
20
+ # - helper: return a helper module that corresponds to the given logical path.
21
+ #
10
22
  def self.included(target)
11
23
  class << target
12
24
 
@@ -1,43 +1,53 @@
1
+ # See the README for an overview.
1
2
  module Waves
2
3
 
3
4
  class << self
4
5
 
6
+ # Access the principal Waves application.
5
7
  attr_reader :application
6
8
 
7
- # give us the root namespace for the application
9
+ # Register a module as a Waves application.
10
+ # Also, initialize the database connection if necessary.
8
11
  def << ( app )
9
- @application = app if Module === app
12
+ @application = app if Module === app
13
+ app.database if app.respond_to? 'database'
10
14
  end
11
15
 
12
16
  end
13
17
 
18
+ # An application in Waves is anything that provides access to the Waves
19
+ # runtime and the registered Waves applications. This includes both
20
+ # Waves::Server and Waves::Console. Waves::Application is *not* the actual
21
+ # application module(s) registered as Waves applications. To access the
22
+ # main Waves application, you can use +Waves+.+application+.
14
23
  class Application
15
-
16
- def initialize( mode = :development )
17
- @mode = mode
18
- end
19
-
20
- def debug? ; @mode == :development ; end
21
24
 
22
- # now we can get the configuration, based on the mode
23
- # we must specify global scope to avoid picking up Waves::Configurations::*
24
- # and / or Waves::Application::* ... because the configuration may be
25
- # loaded via autoload ....
26
- def config
27
- Waves.application.configurations[ @mode ]
28
- end
25
+ # Accessor for options passed to the application. Valid options include
26
+ attr_reader :options
29
27
 
30
- # and, similarly, the url mapping ...
31
- def mapping
32
- Waves.application.configurations[ :mapping ]
28
+ # Create a new Waves application instance.
29
+ def initialize( options={} )
30
+ @options = options
31
+ Dir.chdir options[:directory] if options[:directory]
33
32
  end
34
33
 
35
- # the config then tells us whether to unload any namespaces
36
- # after each request ...
37
- def reset
38
- config.reloadable.each { |mod| mod.reload }
39
- end
34
+ # The 'mode' of the application determines which configuration it will run under.
35
+ def mode ; @mode ||= @options[:mode]||:development ; end
40
36
 
37
+ # Debug is true if debug is set to true in the current configuration.
38
+ def debug? ; config.debug ; end
39
+
40
+ # Access the current configuration. *Example:* +Waves::Server.config+
41
+ def config ; Waves.application.configurations[ mode ] ; end
42
+
43
+ # Access the mappings for the application.
44
+ def mapping ; Waves.application.configurations[ :mapping ] ; end
45
+
46
+ # Reload the modules specified in the current configuration.
47
+ def reload ; config.reloadable.each { |mod| mod.reload } ; end
48
+
49
+ # Returns the cache set for the current configuration
50
+ def cache ; config.cache ; end
41
51
  end
42
52
 
43
53
  end
@@ -1,15 +1,131 @@
1
1
  module Waves
2
+
3
+ # Waves configurations are simply Ruby code, meaning you can use an Ruby expression as
4
+ # a value for a configuration parameter, extend and inherit your configurations, and
5
+ # add your own configuration attributes. You can even use it as a configuration repository
6
+ # for your applications.
7
+ #
8
+ # The form for configuration parameters to use the parameter name as a method name. Passing
9
+ # in a parameter sets the value.
10
+ #
11
+ # == Example
12
+ #
13
+ # module Blog
14
+ # module Configurations
15
+ # class Development < Default
16
+ # host '127.0.0.1'
17
+ # port 2000
18
+ # reloadable [ Blog ]
19
+ # log :level => :debug
20
+ # application do
21
+ # use Rack::ShowExceptions
22
+ # run Waves::Dispatchers::Default.new
23
+ # end
24
+ # end
25
+ # end
26
+ # end
27
+ #
28
+ # There are three forms for accessing parameters:
29
+ #
30
+ # Waves.config.port # generic form - gets current config
31
+ # Blog.configurations[:development] # gets a value for a specific config
32
+ # Blog::Configurations::Development.port # Access config constant directly
33
+ #
34
+ # You can inherit configurations, as is shown in the example above. Typically, you
35
+ # can use the application's "default" configuration to set shared configurations,
36
+ # and then inherit from it for specific variations.
37
+ #
38
+ # To define your own attributes, and still make them inheritable, you should use
39
+ # the +attribute+ class method, like this:
40
+ #
41
+ # class Default < Waves::Configurations::Default
42
+ # attribute 'theme' # define a theme attribute
43
+ # theme 'ultra' # give it a default
44
+ # end
45
+ #
46
+ # There are a number of reserved or built-in attributes. These are:
47
+ #
48
+ # - application: configure the application for use with Rack
49
+ # - database: takes a hash of parameters used to initalize the database; see below
50
+ # - reloadable: an array of module names to reload; see below for more
51
+ # - log: takes a hash of parameters; see below for more
52
+ # - host: the host to bind the server to (string)
53
+ # - port: the port for the server to listen on (number)
54
+ # - ports: used by the cluster:start task for clustering servers (array of numbers)
55
+ # - debug: true if running in "debug" mode, which automatically reloads code
56
+ #
57
+ # == Configuring The Rack Application
58
+ #
59
+ # One of the really nice features of Rack is the ability to install "middleware"
60
+ # components to optimize the way you handle requests. Waves exposes this ability
61
+ # directly to the application developer via the +application+ configuration parameter.
62
+ #
63
+ # *Example*
64
+ #
65
+ # # Typical debugging configuration
66
+ # application do
67
+ # use Rack::ShowExceptions
68
+ # run Waves::Dispatchers::Default.new
69
+ # end
70
+ #
71
+ # == Configuring Database Access
72
+ #
73
+ # The database parameter takes a hash with the following elements:
74
+ #
75
+ # - host: which host the database is running on
76
+ # - adapter: which adapter is being used to access the database (mysql, postgres, etc.)
77
+ # - database: the name of the database the application is connecting to
78
+ # - user: the user for authentication
79
+ # - password: password for authentication
80
+ #
81
+ # *Example*
82
+ #
83
+ # database :host => host, :adapter => 'mysql', :database => 'blog',
84
+ # :user => 'root', :password => 'guess'
85
+ #
86
+ #
87
+ # == Configuring Code Reloading
88
+ #
89
+ # You can specify a list of modules to reload on each request using the +reloadable+
90
+ # configuration parameter. The Waves server will call +reload+ on each module to trigger
91
+ # the reloading. Typically, your modules will use the Autocode gem to set parameters for
92
+ # reloading. This is done for you when you generate an application using the +waves+
93
+ # command, but you can change the default settings. See the documentation for Autocode
94
+ # for more information. Typically, you will set this parameter to just include your
95
+ # main application:
96
+ #
97
+ # reloadable [ Blog ]
98
+ #
99
+ # although you could do this with several modules just as easily (say, your primary
100
+ # application and several helper applications).
101
+ #
102
+ # == Configuring Logging
103
+ #
104
+ # The +log+ configuration parameter takes the following options (as a hash):
105
+ # - level: The level to filter logging at. Uses Ruby's built in Logger class.
106
+ # - output: A filename or IO object. Should be a filename if running as a daemon.
107
+ #
108
+ # *Examples*
109
+ #
110
+ # log :level => :info, :output => $stderr
111
+ # log :level => :error, :output => 'log/blog.log'
112
+ #
2
113
 
3
114
  module Configurations
4
115
 
5
116
  class Base
6
117
 
118
+ # Set the given attribute with the given value. Typically, you wouldn't
119
+ # use this directly.
7
120
  def self.[]=( name, val )
8
121
  meta_def("_#{name}") { val }
9
122
  end
10
123
 
124
+ # Get the value of the given attribute. Typically, you wouldn't
125
+ # use this directly.
11
126
  def self.[]( name ) ; send "_#{name}" ; end
12
127
 
128
+ # Define a new attribute. After calling this, you can get and set the value.
13
129
  def self.attribute( name )
14
130
  meta_def(name) do |*args|
15
131
  raise ArgumentError.new('Too many arguments.') if args.length > 1
@@ -17,18 +133,23 @@ module Waves
17
133
  end
18
134
  self[ name ] = nil
19
135
  end
20
-
21
- def self.mime_types
22
- Waves::MimeTypes
23
- end
24
136
 
25
137
  end
26
138
 
139
+ # The Default configuration provides a good starting point for your applications,
140
+ # defining a number of attributes that are required by Waves.
27
141
  class Default < Base
28
142
 
29
- %w( host port ports log reloadable server database session ).
143
+ %w( host port ports log reloadable database session debug ).
30
144
  each { |name| attribute(name) }
31
145
 
146
+ # Provide access to the Waves::MimeTypes class via the configuration. You
147
+ # could potentially point this to your own MIME types repository class.
148
+ def self.mime_types
149
+ Waves::MimeTypes
150
+ end
151
+
152
+ # Defines the application for use with Rack.
32
153
  def self.application( &block )
33
154
  if block_given?
34
155
  self['application'] = Rack::Builder.new( &block )
@@ -36,10 +157,11 @@ module Waves
36
157
  self['application']
37
158
  end
38
159
  end
39
-
40
- session :duration => 30.minutes,
41
- :path => :tmp / :sessions
42
-
160
+
161
+ debug true
162
+ session :duration => 30.minutes, :path => '/tmp/sessions'
163
+ log :level => :info, :output => $stderr
164
+
43
165
  end
44
166
  end
45
167
  end
@@ -6,8 +6,8 @@ module Waves
6
6
 
7
7
  attr_reader :console
8
8
 
9
- def load(mode=:development)
10
- @console ||= Waves::Console.new(mode)
9
+ def load( options={} )
10
+ @console ||= Waves::Console.new( options )
11
11
  Kernel.load( :lib / 'startup.rb' )
12
12
  end
13
13
 
@@ -1,26 +1,50 @@
1
1
  require 'logger'
2
2
  module Waves
3
3
 
4
+ # Waves::Logger is based on Ruby's built-in Logger. It uses the same filtering approach
5
+ # (debug, info, warn, error, fatal), although the interface is slightly different.
6
+ # You won't typically instantiate this class directly; instead, you will specify the
7
+ # logging configuration you want in your configuration files. See Waves::Configurations
8
+ # for more information on this.
9
+ #
10
+ # To use the logger for output, you can usually just call +log+, since the Waves::ResponseHelper
11
+ # mixin defines it (meaning it is available in the mapping file, controllers, views, and
12
+ # templates). Or, you can access Waves::Logger directly. Either way, the logger provides five
13
+ # methods for output corresponding to the log levels.
14
+ #
15
+ # *Examples*
16
+ # # log the value of foo
17
+ # log.info "Value of foo: #{foo}"
18
+ #
19
+ # # fatal error!
20
+ # Waves::Logger.fatal "She can't hold up any longer, cap'n!"
21
+ #
4
22
  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 );
23
+
24
+ class << self
25
+
26
+ # Returns the object being used for output by the logger.
27
+ def output
28
+ @output ||= ( config[:output] ? File.expand_path( config[:output] ) : $stderr )
29
+ end
30
+ # Returns the active configuration for the logger.
31
+ def config ; @config ||= Waves::Server.config.log ; end
32
+ # Returns the logging level used to filter logging events.
33
+ def level ; @level ||= ::Logger.const_get( config[:level].to_s.upcase || 'INFO' ) ; end
34
+ # Starts the logger, using the active configuration to initialize it.
35
+ def start
36
+ @log = config[:rotation] ?
37
+ ::Logger.new( output, config[:rotation].intern ) :
38
+ ::Logger.new( output )
39
+ @log.level = level
40
+ self
16
41
  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
42
+ # Forwards logging methods to the logger.
43
+ def method_missing(name,*args,&block)
44
+ @log.send name,*args, &block
45
+ end
46
+
47
+ end
24
48
 
25
49
  end
26
50
 
@@ -1,14 +1,18 @@
1
1
  module Waves
2
+
3
+ # Waves::MimeTypes defines an interface for adding MIME types used in mapping requests
4
+ # to content types. Mongrel's MIME_TYPES hash is used as the baseline MIME map.
5
+
2
6
  module MimeTypes
3
7
 
4
8
  def self.[]( path )
5
9
  mapping[ File.extname( path ) ]
6
10
  end
7
11
 
8
- # TODO: why isn't this working?
9
- # def self.<<( mapping )
10
- # mapping.merge!( mapping )
11
- # end
12
+ # TODO: This does not seem to be working.
13
+ def self.<<( mapping )
14
+ mapping.merge!( mapping )
15
+ end
12
16
 
13
17
  def self.mapping
14
18
  @mapping ||= Mongrel::DirHandler::MIME_TYPES
@@ -1,39 +1,48 @@
1
1
  module Waves
2
+ # Waves::Request represents an HTTP request and has methods for accessing anything
3
+ # relating to the request. See Rack::Request for more information, since many methods
4
+ # are actually delegated to Rack::Request.
2
5
  class Request
3
6
 
4
7
  class ParseError < Exception ; end
5
8
 
6
9
  attr_reader :response, :session
7
10
 
8
- def initialize( env ) # Rack::Request
11
+ # Create a new request. Takes a env parameter representing the request passed in from Rack.
12
+ # You shouldn't need to call this directly.
13
+ def initialize( env )
9
14
  @request = Rack::Request.new( env )
10
15
  @response = Waves::Response.new( self )
11
16
  @session = Waves::Session.new( self )
12
17
  end
13
18
 
14
- # if we haven't overridden it, give it to Rack
19
+ # Accessor not explicitly defined by Waves::Request are delegated to Rack::Request.
20
+ # Check the Rack documentation for more information.
15
21
  def method_missing(name,*args)
16
22
  @request.send(name,*args)
17
23
  end
18
24
 
19
- # TODO: should / does this exclude the query_string?
20
- # Is there a Rack API that is more appropos
25
+ # The request path (PATH_INFO). Ex: +/entry/2008-01-17+
21
26
  def path
22
27
  @request.path_info
23
28
  end
24
-
29
+
30
+ # The request domain. Ex: +www.fubar.com+
25
31
  def domain
26
32
  @request.host
27
33
  end
28
34
 
35
+ # The request method: GET, PUT, POST, or DELETE.
29
36
  def method
30
37
  @request.request_method.downcase.intern
31
38
  end
32
39
 
40
+ # Raise a not found exception.
33
41
  def not_found
34
42
  raise Waves::Dispatchers::NotFoundError.new( @request.url + ' not found.')
35
43
  end
36
44
 
45
+ # Issue a redirect for the given path.
37
46
  def redirect( path )
38
47
  raise Waves::Dispatchers::Redirect.new( path )
39
48
  end