waves 0.6.7 → 0.6.9

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 (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