waves-stable 0.7.7
Sign up to get free protection for your applications and to get access to all the features.
- data/app/Rakefile +72 -0
- data/app/bin/waves-console +4 -0
- data/app/bin/waves-server +4 -0
- data/app/configurations/development.rb.erb +31 -0
- data/app/configurations/mapping.rb.erb +14 -0
- data/app/configurations/production.rb.erb +31 -0
- data/app/controllers/.gitignore +0 -0
- data/app/doc/.gitignore +0 -0
- data/app/helpers/.gitignore +0 -0
- data/app/lib/application.rb.erb +5 -0
- data/app/lib/tasks/.gitignore +0 -0
- data/app/log/.gitignore +0 -0
- data/app/models/.gitignore +0 -0
- data/app/public/css/.gitignore +0 -0
- data/app/public/flash/.gitignore +0 -0
- data/app/public/images/.gitignore +0 -0
- data/app/public/javascript/.gitignore +0 -0
- data/app/schema/migrations/.gitignore +0 -0
- data/app/startup.rb +5 -0
- data/app/templates/errors/not_found_404.mab +2 -0
- data/app/templates/errors/server_error_500.mab +2 -0
- data/app/templates/layouts/default.mab +14 -0
- data/app/tmp/sessions/.gitignore +0 -0
- data/app/views/.gitignore +0 -0
- data/bin/waves +84 -0
- data/bin/waves-console +4 -0
- data/bin/waves-server +4 -0
- data/doc/HISTORY +44 -0
- data/doc/LICENSE +22 -0
- data/lib/commands/waves-console.rb +21 -0
- data/lib/commands/waves-server.rb +55 -0
- data/lib/controllers/base.rb +11 -0
- data/lib/controllers/mixin.rb +165 -0
- data/lib/dispatchers/base.rb +67 -0
- data/lib/dispatchers/default.rb +81 -0
- data/lib/foundations/default.rb +27 -0
- data/lib/foundations/simple.rb +30 -0
- data/lib/helpers/asset_helper.rb +67 -0
- data/lib/helpers/common.rb +66 -0
- data/lib/helpers/default.rb +13 -0
- data/lib/helpers/form.rb +40 -0
- data/lib/helpers/formatting.rb +30 -0
- data/lib/helpers/model.rb +33 -0
- data/lib/helpers/number_helper.rb +25 -0
- data/lib/helpers/tag_helper.rb +58 -0
- data/lib/helpers/url_helper.rb +77 -0
- data/lib/helpers/view.rb +24 -0
- data/lib/layers/default_errors.rb +26 -0
- data/lib/layers/inflect/english.rb +24 -0
- data/lib/layers/inflect/english/rules.rb +88 -0
- data/lib/layers/inflect/english/string.rb +24 -0
- data/lib/layers/mvc.rb +54 -0
- data/lib/layers/orm/active_record.rb +92 -0
- data/lib/layers/orm/active_record/migrations/empty.rb.erb +9 -0
- data/lib/layers/orm/active_record/tasks/generate.rb +28 -0
- data/lib/layers/orm/active_record/tasks/schema.rb +22 -0
- data/lib/layers/orm/data_mapper.rb +38 -0
- data/lib/layers/orm/filebase.rb +22 -0
- data/lib/layers/orm/migration.rb +79 -0
- data/lib/layers/orm/sequel.rb +86 -0
- data/lib/layers/orm/sequel/migrations/empty.rb.erb +9 -0
- data/lib/layers/orm/sequel/tasks/generate.rb +28 -0
- data/lib/layers/orm/sequel/tasks/schema.rb +16 -0
- data/lib/layers/simple.rb +32 -0
- data/lib/layers/simple_errors.rb +23 -0
- data/lib/mapping/mapping.rb +289 -0
- data/lib/mapping/pretty_urls.rb +96 -0
- data/lib/renderers/erubis.rb +63 -0
- data/lib/renderers/haml.rb +45 -0
- data/lib/renderers/markaby.rb +33 -0
- data/lib/renderers/mixin.rb +50 -0
- data/lib/runtime/application.rb +69 -0
- data/lib/runtime/blackboard.rb +57 -0
- data/lib/runtime/configuration.rb +185 -0
- data/lib/runtime/console.rb +20 -0
- data/lib/runtime/debugger.rb +9 -0
- data/lib/runtime/logger.rb +59 -0
- data/lib/runtime/mime_types.rb +22 -0
- data/lib/runtime/request.rb +78 -0
- data/lib/runtime/response.rb +40 -0
- data/lib/runtime/response_mixin.rb +38 -0
- data/lib/runtime/response_proxy.rb +30 -0
- data/lib/runtime/server.rb +107 -0
- data/lib/runtime/session.rb +66 -0
- data/lib/tasks/cluster.rb +26 -0
- data/lib/tasks/gem.rb +31 -0
- data/lib/tasks/generate.rb +80 -0
- data/lib/utilities/hash.rb +31 -0
- data/lib/utilities/inflect.rb +110 -0
- data/lib/utilities/integer.rb +24 -0
- data/lib/utilities/module.rb +21 -0
- data/lib/utilities/object.rb +25 -0
- data/lib/utilities/proc.rb +16 -0
- data/lib/utilities/string.rb +49 -0
- data/lib/utilities/symbol.rb +10 -0
- data/lib/utilities/tempfile.rb +9 -0
- data/lib/views/base.rb +9 -0
- data/lib/views/mixin.rb +110 -0
- data/lib/waves.rb +84 -0
- data/samples/blog/Rakefile +14 -0
- data/samples/blog/bin/waves-console +3 -0
- data/samples/blog/bin/waves-server +3 -0
- data/samples/blog/configurations/development.rb +31 -0
- data/samples/blog/configurations/mapping.rb +23 -0
- data/samples/blog/configurations/production.rb +30 -0
- data/samples/blog/doc/EMTPY +0 -0
- data/samples/blog/lib/application.rb +5 -0
- data/samples/blog/models/comment.rb +14 -0
- data/samples/blog/models/entry.rb +14 -0
- data/samples/blog/public/css/site.css +2 -0
- data/samples/blog/public/javascript/site.js +13 -0
- data/samples/blog/schema/migrations/001_initial_schema.rb +17 -0
- data/samples/blog/schema/migrations/002_add_comments.rb +18 -0
- data/samples/blog/schema/migrations/templates/empty.rb.erb +9 -0
- data/samples/blog/startup.rb +6 -0
- data/samples/blog/templates/comment/add.mab +10 -0
- data/samples/blog/templates/comment/list.mab +6 -0
- data/samples/blog/templates/entry/editor.mab +13 -0
- data/samples/blog/templates/entry/list.mab +11 -0
- data/samples/blog/templates/entry/show.mab +9 -0
- data/samples/blog/templates/entry/summary.mab +5 -0
- data/samples/blog/templates/errors/not_found_404.mab +2 -0
- data/samples/blog/templates/errors/server_error_500.mab +2 -0
- data/samples/blog/templates/layouts/default.mab +17 -0
- data/verify/app_generation/helpers.rb +24 -0
- data/verify/app_generation/startup.rb +39 -0
- data/verify/blackboard/blackboard_verify.rb +92 -0
- data/verify/blackboard/helpers.rb +5 -0
- data/verify/configurations/attributes.rb +37 -0
- data/verify/configurations/helpers.rb +1 -0
- data/verify/configurations/rack_integration.rb +29 -0
- data/verify/controllers/base.rb +37 -0
- data/verify/controllers/helpers.rb +13 -0
- data/verify/controllers/interface.rb +51 -0
- data/verify/core/helpers.rb +3 -0
- data/verify/core/utilities.rb +177 -0
- data/verify/foundations/default.rb +86 -0
- data/verify/foundations/default_application/Rakefile +14 -0
- data/verify/foundations/default_application/bin/waves-console +3 -0
- data/verify/foundations/default_application/bin/waves-server +3 -0
- data/verify/foundations/default_application/configurations/development.rb +26 -0
- data/verify/foundations/default_application/configurations/mapping.rb +14 -0
- data/verify/foundations/default_application/configurations/production.rb +30 -0
- data/verify/foundations/default_application/controllers/default.rb +15 -0
- data/verify/foundations/default_application/controllers/loaded.rb +15 -0
- data/verify/foundations/default_application/defaultapplication.db +0 -0
- data/verify/foundations/default_application/helpers/loaded.rb +10 -0
- data/verify/foundations/default_application/lib/application.rb +5 -0
- data/verify/foundations/default_application/models/default.rb +13 -0
- data/verify/foundations/default_application/models/loaded.rb +13 -0
- data/verify/foundations/default_application/schema/migrations/templates/empty.rb.erb +9 -0
- data/verify/foundations/default_application/startup.rb +7 -0
- data/verify/foundations/default_application/templates/errors/not_found_404.mab +2 -0
- data/verify/foundations/default_application/templates/errors/server_error_500.mab +2 -0
- data/verify/foundations/default_application/templates/layouts/default.mab +14 -0
- data/verify/foundations/default_application/views/default.rb +7 -0
- data/verify/foundations/default_application/views/loaded.rb +15 -0
- data/verify/foundations/helpers.rb +1 -0
- data/verify/foundations/simple.rb +25 -0
- data/verify/helpers.rb +76 -0
- data/verify/layers/data_mapper/association_verify.rb +87 -0
- data/verify/layers/default_errors.rb +29 -0
- data/verify/layers/helpers.rb +1 -0
- data/verify/layers/migration.rb +33 -0
- data/verify/layers/sequel/model.rb +41 -0
- data/verify/mapping/always.rb +19 -0
- data/verify/mapping/filters.rb +65 -0
- data/verify/mapping/handle.rb +24 -0
- data/verify/mapping/helpers.rb +7 -0
- data/verify/mapping/matches.rb +27 -0
- data/verify/mapping/named.rb +29 -0
- data/verify/mapping/options.rb +17 -0
- data/verify/mapping/path.rb +40 -0
- data/verify/mapping/response_proxy.rb +50 -0
- data/verify/mapping/threaded.rb +25 -0
- data/verify/requests/helpers.rb +16 -0
- data/verify/requests/request.rb +73 -0
- data/verify/requests/response.rb +59 -0
- data/verify/requests/session.rb +54 -0
- data/verify/views/helpers.rb +1 -0
- data/verify/views/rendering.rb +34 -0
- data/verify/views/templates/foo.erb +0 -0
- data/verify/views/templates/moo.erb +0 -0
- data/verify/views/templates/moo.mab +0 -0
- metadata +439 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
namespace :generate do
|
2
|
+
|
3
|
+
desc 'Generate a Sequel model, with name=<name>'
|
4
|
+
task :model do |task|
|
5
|
+
name = ENV['name']
|
6
|
+
model_name = name.camel_case
|
7
|
+
raise "Cannot generate Default yet" if model_name == 'Default'
|
8
|
+
|
9
|
+
filename = File.expand_path "models/#{name}.rb"
|
10
|
+
if File.exist?(filename)
|
11
|
+
$stderr.puts "#{filename} already exists"
|
12
|
+
exit
|
13
|
+
end
|
14
|
+
|
15
|
+
model = <<TEXT
|
16
|
+
module #{Waves.application.name}
|
17
|
+
module Models
|
18
|
+
class #{model_name} < Default
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
TEXT
|
24
|
+
|
25
|
+
File.write( filename, model )
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/../../migration"
|
2
|
+
|
3
|
+
namespace :schema do
|
4
|
+
|
5
|
+
desc "Create a new Sequel Migration with name=<name>"
|
6
|
+
task :migration do |task|
|
7
|
+
Waves::Layers::ORM.create_migration_for(Sequel)
|
8
|
+
end
|
9
|
+
|
10
|
+
desc "Performs Sequel migrations to version=<version>"
|
11
|
+
task :migrate do |task|
|
12
|
+
version = ENV['version']; version = version.to_i unless version.nil?
|
13
|
+
Sequel::Migrator.apply( Waves.application.database, Waves::Layers::ORM.migration_directory , version )
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Waves
|
2
|
+
|
3
|
+
# Waves uses Layers to provide discrete, stackable, interchangeable bundles of functionality.
|
4
|
+
#
|
5
|
+
# Developers can make use of Layers by including them directly in a Waves application:
|
6
|
+
#
|
7
|
+
# module MyApp
|
8
|
+
# include SomeLayer
|
9
|
+
# end
|
10
|
+
module Layers
|
11
|
+
|
12
|
+
# Creates the Configurations namespace and establishes the standard autoload-or-autocreate
|
13
|
+
# rules.
|
14
|
+
module Simple
|
15
|
+
def self.included( app )
|
16
|
+
|
17
|
+
def app.config ; Waves.config ; end
|
18
|
+
def app.configurations ; self::Configurations ; end
|
19
|
+
|
20
|
+
app.instance_eval { include AutoCode }
|
21
|
+
|
22
|
+
app.auto_create_module( :Configurations ) do
|
23
|
+
include AutoCode
|
24
|
+
auto_create_class true, Waves::Configurations::Default
|
25
|
+
auto_load :Mapping, :directories => [:configurations]
|
26
|
+
auto_load true, :directories => [:configurations]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Waves
|
2
|
+
module Layers
|
3
|
+
# Configures Waves for minimal exception handling.
|
4
|
+
#
|
5
|
+
# For example,
|
6
|
+
# a NotFoundError results in response status of 404, with body text
|
7
|
+
# of "404 Not Found".
|
8
|
+
module SimpleErrors
|
9
|
+
|
10
|
+
def self.included( app )
|
11
|
+
|
12
|
+
app.auto_eval :Configurations do
|
13
|
+
auto_eval :Mapping do
|
14
|
+
handle(Waves::Dispatchers::NotFoundError) do
|
15
|
+
response.status = 404; response.body = "404 Not Found"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,289 @@
|
|
1
|
+
module Waves
|
2
|
+
|
3
|
+
# Mappings in Waves are the interface between the request dispatcher and your
|
4
|
+
# application code. The dispatcher matches each request against the mappings
|
5
|
+
# to determine a primary action and to collect sets of before, after, wrap,
|
6
|
+
# and always actions. The dispatcher also looks for an exception handler
|
7
|
+
# registered in the mappings when attempting a rescue.
|
8
|
+
#
|
9
|
+
# Each mapping associates a block with a set of constraints. Mappings can be
|
10
|
+
# one of several types:
|
11
|
+
#
|
12
|
+
# - action (the actual request processing and response)
|
13
|
+
# - handle (exception handling)
|
14
|
+
# - before
|
15
|
+
# - after
|
16
|
+
# - wrap (registers its block as both a before and after action)
|
17
|
+
# - always (like an "ensure" clause in a rescue)
|
18
|
+
#
|
19
|
+
# Actions are registered using path, url, or map. The other types may be
|
20
|
+
# registered using methods named after the type.
|
21
|
+
#
|
22
|
+
#
|
23
|
+
# The available constraints are:
|
24
|
+
#
|
25
|
+
# - a string or regexp that the path or url must match
|
26
|
+
# - parameters to match against the HTTP request headers and the Rack-specific variables (e.g. 'rack.url_scheme')
|
27
|
+
# - an additional hash reserved for settings not related to the Rack request (e.g. giving Rack handers special instructions for certain requests. See threaded? )
|
28
|
+
#
|
29
|
+
# The dispatcher evaluates mapping blocks in an instance of ResponseProxy,
|
30
|
+
# which provides access to foundational classes of a Waves application (i.e. controllers and views)
|
31
|
+
#
|
32
|
+
# == Examples
|
33
|
+
#
|
34
|
+
# resource = '([\w\-]+)'
|
35
|
+
# name = '([\w\-\_\.\+\@]+)'
|
36
|
+
#
|
37
|
+
# path %r{^/#{resource}/#{name}/?$} do |resource, name|
|
38
|
+
# "Hello from a #{resource} named #{name.capitalize}."
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# In this example, we are using binding regular expressions defined by +resource+
|
42
|
+
# and +name+. The matches are passed into the block as parameters. Thus, this
|
43
|
+
# rule, given the URL '/person/john' will return:
|
44
|
+
#
|
45
|
+
# Hello from a person named John.
|
46
|
+
#
|
47
|
+
# The given block may simple return a string. The content type is inferred from the request
|
48
|
+
# if possible, otherwise it defaults to +text+/+html+.
|
49
|
+
#
|
50
|
+
# path '/critters', :method => :post do
|
51
|
+
# request.content_type
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# /critters # => 'text/html'
|
55
|
+
#
|
56
|
+
# In this example, we match against a string and check to make sure that the request is a
|
57
|
+
# POST. If so, we return the request content_type. The request (and response) objects are
|
58
|
+
# available from within the block implicitly.
|
59
|
+
#
|
60
|
+
# = Invoking Controllers and Views
|
61
|
+
#
|
62
|
+
# You may invoke a controller or view method for the primary application by using the
|
63
|
+
# corresponding methods, preceded by the +use+ directive.
|
64
|
+
#
|
65
|
+
# == Examples
|
66
|
+
#
|
67
|
+
# path %r{^/#{resource}/#{name}/?$} do |resource, name|
|
68
|
+
# resource( resource ) do
|
69
|
+
# controller { find( name ) } | view { | instance | show( resource => instance ) }
|
70
|
+
# end
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# In this example, we take the same rule from above but invoke a controller and view method.
|
74
|
+
# We use the +resource+ directive and the resource parameter to set the MVC instances we're going
|
75
|
+
# to use. This is necessary to use the +controller+ or +view+ methods. Each of these take
|
76
|
+
# a block as arguments which are evaluated in the context of the instance. The +view+ method
|
77
|
+
# can further take an argument which is "piped" from the result of the controller block. This
|
78
|
+
# isn't required, but helps to clarify the request processing. Within a view block, a hash
|
79
|
+
# may also be passed in to the view method, which is converted into instance variables for the
|
80
|
+
# view instance. In this example, the +show+ method is assigned to an instance variable with the
|
81
|
+
# same name as the resource type.
|
82
|
+
#
|
83
|
+
# So given the same URL as above - /person/john - what will happen is the +find+ method for
|
84
|
+
# the +Person+ controller will be invoked and the result passed to the +Person+ view's +show+
|
85
|
+
# method, with +@person+ holding the value returned.
|
86
|
+
#
|
87
|
+
# Crucially, the controller does not need to know what variables the view depends on. This is
|
88
|
+
# the job of the mapping block, to act as the "glue" between the controller and view. The
|
89
|
+
# controller and view can thus be completely decoupled and become easier to reuse separately.
|
90
|
+
#
|
91
|
+
# url 'http://admin.foobar.com:/' do
|
92
|
+
# resource( :admin ) { view { console } }
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# In this example, we are using the +url+ method to map a subdomain of +foobar.com+ to the
|
96
|
+
# console method of the Admin view. In this case, we did not need a controller method, so
|
97
|
+
# we simply didn't call one.
|
98
|
+
#
|
99
|
+
# = Mapping Modules
|
100
|
+
#
|
101
|
+
# You may encapsulate sets of related rules into modules and simply include them into your
|
102
|
+
# mapping module. Some rule sets come packaged with Waves, such as PrettyUrls (rules for
|
103
|
+
# matching resources using names instead of ids). The simplest way to define such modules for
|
104
|
+
# reuse is by defining the +included+ class method for the rules module, and then define
|
105
|
+
# the rules using +module_eval+. See the PrettyUrls module for an example of how to do this.
|
106
|
+
#
|
107
|
+
# *Important:* Using pre-packaged mapping rules does not prevent you from adding to or
|
108
|
+
# overriding these rules. However, order does matter, so you should put your own rules
|
109
|
+
# ahead of those your may be importing. Also, place rules with constraints (for example,
|
110
|
+
# rules that require a POST) ahead of those with no constraints, otherwise the constrainted
|
111
|
+
# rules may never be called.
|
112
|
+
|
113
|
+
module Mapping
|
114
|
+
|
115
|
+
# If the pattern matches and constraints given by the options hash are satisfied, run the
|
116
|
+
# block before running any +path+ or +url+ actions. You can have as many +before+ matches
|
117
|
+
# as you want - they will all run, unless one of them calls redirect, generates an
|
118
|
+
# unhandled exception, etc.
|
119
|
+
def before( path, options = {}, &block )
|
120
|
+
if path.is_a? Hash
|
121
|
+
options = path
|
122
|
+
else
|
123
|
+
options[:path] = path
|
124
|
+
end
|
125
|
+
filters[:before] << [ options, block ]
|
126
|
+
end
|
127
|
+
|
128
|
+
# Similar to before, except it runs its actions after any matching +url+ or +path+ actions.
|
129
|
+
# Note that after methods will run even if an exception is thrown during processing.
|
130
|
+
def after( path, options = {}, &block )
|
131
|
+
if path.is_a? Hash
|
132
|
+
options = path
|
133
|
+
else
|
134
|
+
options[:path] = path
|
135
|
+
end
|
136
|
+
filters[:after] << [ options, block ]
|
137
|
+
end
|
138
|
+
|
139
|
+
# Run the action before and after the matching +url+ or +path+ action.
|
140
|
+
def wrap( path, options = {}, &block )
|
141
|
+
if path.is_a? Hash
|
142
|
+
options = path
|
143
|
+
else
|
144
|
+
options[:path] = path
|
145
|
+
end
|
146
|
+
filters[:before] << [ options, block ]
|
147
|
+
filters[:after] << [ options, block ]
|
148
|
+
end
|
149
|
+
|
150
|
+
# Like after, but will run even when an exception is thrown. Exceptions in
|
151
|
+
# always mappings are simply logged and ignored.
|
152
|
+
def always( path, options = {}, &block )
|
153
|
+
if path.is_a? Hash
|
154
|
+
options = path
|
155
|
+
else
|
156
|
+
options[:path] = path
|
157
|
+
end
|
158
|
+
filters[:always] << [ options, block ]
|
159
|
+
end
|
160
|
+
|
161
|
+
# Maps a request to a block. Don't use this method directly unless you know what
|
162
|
+
# you're doing. Use +path+ or +url+ instead.
|
163
|
+
def map( path, options = {}, params = {}, &block )
|
164
|
+
case path
|
165
|
+
when Hash
|
166
|
+
params = options; options = path
|
167
|
+
when String
|
168
|
+
options[:path] = path
|
169
|
+
end
|
170
|
+
mapping << [ options, params, block ]
|
171
|
+
end
|
172
|
+
|
173
|
+
# Match pattern against the +request.path+, along with satisfying any constraints
|
174
|
+
# specified by the options hash. If the pattern matches and the constraints are satisfied,
|
175
|
+
# run the block. Only one +path+ or +url+ match will be run (the first one).
|
176
|
+
def path( pat, options = {}, params = {}, &block )
|
177
|
+
options[:path] = pat; map( options, params, &block )
|
178
|
+
end
|
179
|
+
|
180
|
+
# Match pattern against the +request.url+, along with satisfying any constraints
|
181
|
+
# specified by the options hash. If the pattern matches and the constraints are satisfied,
|
182
|
+
# run the block. Only one +path+ or +url+ match will be run (the first one).
|
183
|
+
def url( pat, options = {}, params = {}, &block )
|
184
|
+
options[:url] = pat; map( options, params, &block )
|
185
|
+
end
|
186
|
+
|
187
|
+
# Maps the root of the application to a block. If an options hash is specified it must
|
188
|
+
# satisfy those constraints in order to run the block.
|
189
|
+
def root( options = {}, params = {}, &block )
|
190
|
+
path( %r{^/?$}, options, params, &block )
|
191
|
+
end
|
192
|
+
|
193
|
+
# Maps an exception handler to a block.
|
194
|
+
def handle(exception, options = {}, &block )
|
195
|
+
handlers << [exception,options, block]
|
196
|
+
end
|
197
|
+
|
198
|
+
# Maps a request to a block that will be executed within it's
|
199
|
+
# own thread. This is especially useful when you're running
|
200
|
+
# with an event driven server like thin or ebb, and this block
|
201
|
+
# is going to take a relatively long time.
|
202
|
+
def threaded( pat, options = {}, params = {}, &block)
|
203
|
+
params[:threaded] = true
|
204
|
+
map( pat, options, params, &block)
|
205
|
+
end
|
206
|
+
|
207
|
+
# Determines whether the request should be handled in a separate thread. This is used
|
208
|
+
# by event driven servers like thin and ebb, and is most useful for those methods that
|
209
|
+
# take a long time to complete, like for example upload processes. E.g.:
|
210
|
+
#
|
211
|
+
# threaded("/upload", :method => :post) do
|
212
|
+
# handle_upload
|
213
|
+
# end
|
214
|
+
#
|
215
|
+
# You typically wouldn't use this method directly.
|
216
|
+
def threaded?( request )
|
217
|
+
mapping.find do | options, params, function |
|
218
|
+
match = match( request, options, function )
|
219
|
+
return params[:threaded] == true if match
|
220
|
+
end
|
221
|
+
return false
|
222
|
+
end
|
223
|
+
|
224
|
+
# Match the given request against the defined rules. This is typically only called
|
225
|
+
# by a dispatcher object, so you shouldn't typically use it directly.
|
226
|
+
def []( request )
|
227
|
+
|
228
|
+
rx = { :before => [], :after => [], :always => [], :action => nil, :handlers => [] }
|
229
|
+
|
230
|
+
( filters[:before] + filters[:wrap] ).each do | options, function |
|
231
|
+
matches = match( request, options, function )
|
232
|
+
rx[:before] << matches if matches
|
233
|
+
end
|
234
|
+
|
235
|
+
mapping.find do | options, params, function |
|
236
|
+
rx[:action] = match( request, options, function )
|
237
|
+
break if rx[:action]
|
238
|
+
end
|
239
|
+
|
240
|
+
( filters[:after] + filters[:wrap] ).each do | options, function |
|
241
|
+
matches = match( request, options, function )
|
242
|
+
rx[:after] << matches if matches
|
243
|
+
end
|
244
|
+
|
245
|
+
filters[:always].each do | options, function |
|
246
|
+
matches = match( request, options, function )
|
247
|
+
rx[:always] << matches if matches
|
248
|
+
end
|
249
|
+
|
250
|
+
handlers.each do | exception, options, function |
|
251
|
+
matches = match( request, options, function )
|
252
|
+
rx[:handlers] << matches.unshift(exception) if matches
|
253
|
+
end
|
254
|
+
|
255
|
+
return rx
|
256
|
+
end
|
257
|
+
|
258
|
+
# Clear all mapping rules
|
259
|
+
def clear
|
260
|
+
@mapping = @filters = @handlers = nil;
|
261
|
+
end
|
262
|
+
|
263
|
+
private
|
264
|
+
|
265
|
+
def mapping; @mapping ||= []; end
|
266
|
+
|
267
|
+
def filters; @filters ||= { :before => [], :after => [], :wrap => [], :always => [] }; end
|
268
|
+
|
269
|
+
def handlers; @handlers ||= []; end
|
270
|
+
|
271
|
+
def match ( request, options, function )
|
272
|
+
return nil unless satisfy( request, options )
|
273
|
+
return [ function, nil ] if ( options[:path] == true or options[:url] == true )
|
274
|
+
matches = options[:path].match( request.path ) if options[:path]
|
275
|
+
matches = options[:url].match( request.url ) if options[:url]
|
276
|
+
return [ function, matches ? matches[1..-1] : nil ]
|
277
|
+
end
|
278
|
+
|
279
|
+
def satisfy( request, options )
|
280
|
+
options.nil? or options.all? do |name,wanted|
|
281
|
+
return true if wanted == true
|
282
|
+
got = request.send( name ) rescue request.env[ ( name =~ /^rack\./ ) ? name.to_s.downcase : name.to_s.upcase ]
|
283
|
+
( ( wanted.is_a?(Regexp) and wanted.match( got.to_s ) ) or got.to_s == wanted.to_s ) unless ( wanted.nil? or got.nil? )
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
|
289
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Waves
|
2
|
+
module Mapping
|
3
|
+
|
4
|
+
# A set of pre-packed mapping rules for dealing with pretty URLs (that use names instead
|
5
|
+
# of numbers to identify resources). There are two modules.
|
6
|
+
# - GetRules, which defines all the GET methods for dealing with named resources
|
7
|
+
# - RestRules, which defines add, update, and delete rules using a REST style interface
|
8
|
+
#
|
9
|
+
module PrettyUrls
|
10
|
+
|
11
|
+
#
|
12
|
+
# GetRules defines the following URL conventions:
|
13
|
+
#
|
14
|
+
# /resources # => get a list of all instances of resource
|
15
|
+
# /resource/name # => get a specific instance of resource with the given name
|
16
|
+
# /resource/name/editor # => display an edit page for the given resource
|
17
|
+
#
|
18
|
+
module GetRules
|
19
|
+
|
20
|
+
def self.included(target)
|
21
|
+
|
22
|
+
target.module_eval do
|
23
|
+
|
24
|
+
extend Waves::Mapping
|
25
|
+
|
26
|
+
name = '([\w\-\_\.\+\@]+)'; model = '([\w\-]+)'
|
27
|
+
|
28
|
+
# get all resources for the given model
|
29
|
+
path %r{^/#{model}/?$}, :method => :get do | model |
|
30
|
+
resource( model.singular ) { controller { all } | view { |data| list( model => data ) } }
|
31
|
+
end
|
32
|
+
|
33
|
+
# get the given resource for the given model
|
34
|
+
path %r{^/#{model}/#{name}/?$}, :method => :get do | model, name |
|
35
|
+
resource( model ) { controller { find( name ) } | view { |data| show( model => data ) } }
|
36
|
+
end
|
37
|
+
|
38
|
+
# display an editor for the given resource / model
|
39
|
+
path %r{^/#{model}/#{name}/editor/?$}, :method => :get do | model, name |
|
40
|
+
resource( model ) { controller { find( name ) } | view { |data| editor( model => data ) } }
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# RestRules defines the following URL conventions:
|
51
|
+
#
|
52
|
+
# POST /resources # => add a new resource
|
53
|
+
# PUT /resource/name # => update the given resource
|
54
|
+
# DELETE /resource/name # => delete the given resource
|
55
|
+
#
|
56
|
+
module RestRules
|
57
|
+
|
58
|
+
def self.included(target)
|
59
|
+
|
60
|
+
target.module_eval do
|
61
|
+
|
62
|
+
extend Waves::Mapping
|
63
|
+
|
64
|
+
name = '([\w\-\_\.\+\@]+)'; model = '([\w\-]+)'
|
65
|
+
|
66
|
+
# create a new resource for the given model
|
67
|
+
path %r{^/#{model}/?$}, :method => :post do | model |
|
68
|
+
resource( model.singular ) do
|
69
|
+
controller do
|
70
|
+
instance = create
|
71
|
+
redirect( "/#{model_name}/#{instance.name}/editor" )
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# update the given resource for the given model
|
77
|
+
path %r{^/#{model}/#{name}/?$}, :method => :put do | model, name |
|
78
|
+
resource( model ) { controller { update( name ); redirect( url ) } }
|
79
|
+
end
|
80
|
+
|
81
|
+
# delete the given resource for the given model
|
82
|
+
path %r{^/#{model}/#{name}/?$}, :method => :delete do | model, name |
|
83
|
+
resource( model ) { controller { delete( name ) } }
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|