waves-stable 0.7.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. data/app/Rakefile +72 -0
  2. data/app/bin/waves-console +4 -0
  3. data/app/bin/waves-server +4 -0
  4. data/app/configurations/development.rb.erb +31 -0
  5. data/app/configurations/mapping.rb.erb +14 -0
  6. data/app/configurations/production.rb.erb +31 -0
  7. data/app/controllers/.gitignore +0 -0
  8. data/app/doc/.gitignore +0 -0
  9. data/app/helpers/.gitignore +0 -0
  10. data/app/lib/application.rb.erb +5 -0
  11. data/app/lib/tasks/.gitignore +0 -0
  12. data/app/log/.gitignore +0 -0
  13. data/app/models/.gitignore +0 -0
  14. data/app/public/css/.gitignore +0 -0
  15. data/app/public/flash/.gitignore +0 -0
  16. data/app/public/images/.gitignore +0 -0
  17. data/app/public/javascript/.gitignore +0 -0
  18. data/app/schema/migrations/.gitignore +0 -0
  19. data/app/startup.rb +5 -0
  20. data/app/templates/errors/not_found_404.mab +2 -0
  21. data/app/templates/errors/server_error_500.mab +2 -0
  22. data/app/templates/layouts/default.mab +14 -0
  23. data/app/tmp/sessions/.gitignore +0 -0
  24. data/app/views/.gitignore +0 -0
  25. data/bin/waves +84 -0
  26. data/bin/waves-console +4 -0
  27. data/bin/waves-server +4 -0
  28. data/doc/HISTORY +44 -0
  29. data/doc/LICENSE +22 -0
  30. data/lib/commands/waves-console.rb +21 -0
  31. data/lib/commands/waves-server.rb +55 -0
  32. data/lib/controllers/base.rb +11 -0
  33. data/lib/controllers/mixin.rb +165 -0
  34. data/lib/dispatchers/base.rb +67 -0
  35. data/lib/dispatchers/default.rb +81 -0
  36. data/lib/foundations/default.rb +27 -0
  37. data/lib/foundations/simple.rb +30 -0
  38. data/lib/helpers/asset_helper.rb +67 -0
  39. data/lib/helpers/common.rb +66 -0
  40. data/lib/helpers/default.rb +13 -0
  41. data/lib/helpers/form.rb +40 -0
  42. data/lib/helpers/formatting.rb +30 -0
  43. data/lib/helpers/model.rb +33 -0
  44. data/lib/helpers/number_helper.rb +25 -0
  45. data/lib/helpers/tag_helper.rb +58 -0
  46. data/lib/helpers/url_helper.rb +77 -0
  47. data/lib/helpers/view.rb +24 -0
  48. data/lib/layers/default_errors.rb +26 -0
  49. data/lib/layers/inflect/english.rb +24 -0
  50. data/lib/layers/inflect/english/rules.rb +88 -0
  51. data/lib/layers/inflect/english/string.rb +24 -0
  52. data/lib/layers/mvc.rb +54 -0
  53. data/lib/layers/orm/active_record.rb +92 -0
  54. data/lib/layers/orm/active_record/migrations/empty.rb.erb +9 -0
  55. data/lib/layers/orm/active_record/tasks/generate.rb +28 -0
  56. data/lib/layers/orm/active_record/tasks/schema.rb +22 -0
  57. data/lib/layers/orm/data_mapper.rb +38 -0
  58. data/lib/layers/orm/filebase.rb +22 -0
  59. data/lib/layers/orm/migration.rb +79 -0
  60. data/lib/layers/orm/sequel.rb +86 -0
  61. data/lib/layers/orm/sequel/migrations/empty.rb.erb +9 -0
  62. data/lib/layers/orm/sequel/tasks/generate.rb +28 -0
  63. data/lib/layers/orm/sequel/tasks/schema.rb +16 -0
  64. data/lib/layers/simple.rb +32 -0
  65. data/lib/layers/simple_errors.rb +23 -0
  66. data/lib/mapping/mapping.rb +289 -0
  67. data/lib/mapping/pretty_urls.rb +96 -0
  68. data/lib/renderers/erubis.rb +63 -0
  69. data/lib/renderers/haml.rb +45 -0
  70. data/lib/renderers/markaby.rb +33 -0
  71. data/lib/renderers/mixin.rb +50 -0
  72. data/lib/runtime/application.rb +69 -0
  73. data/lib/runtime/blackboard.rb +57 -0
  74. data/lib/runtime/configuration.rb +185 -0
  75. data/lib/runtime/console.rb +20 -0
  76. data/lib/runtime/debugger.rb +9 -0
  77. data/lib/runtime/logger.rb +59 -0
  78. data/lib/runtime/mime_types.rb +22 -0
  79. data/lib/runtime/request.rb +78 -0
  80. data/lib/runtime/response.rb +40 -0
  81. data/lib/runtime/response_mixin.rb +38 -0
  82. data/lib/runtime/response_proxy.rb +30 -0
  83. data/lib/runtime/server.rb +107 -0
  84. data/lib/runtime/session.rb +66 -0
  85. data/lib/tasks/cluster.rb +26 -0
  86. data/lib/tasks/gem.rb +31 -0
  87. data/lib/tasks/generate.rb +80 -0
  88. data/lib/utilities/hash.rb +31 -0
  89. data/lib/utilities/inflect.rb +110 -0
  90. data/lib/utilities/integer.rb +24 -0
  91. data/lib/utilities/module.rb +21 -0
  92. data/lib/utilities/object.rb +25 -0
  93. data/lib/utilities/proc.rb +16 -0
  94. data/lib/utilities/string.rb +49 -0
  95. data/lib/utilities/symbol.rb +10 -0
  96. data/lib/utilities/tempfile.rb +9 -0
  97. data/lib/views/base.rb +9 -0
  98. data/lib/views/mixin.rb +110 -0
  99. data/lib/waves.rb +84 -0
  100. data/samples/blog/Rakefile +14 -0
  101. data/samples/blog/bin/waves-console +3 -0
  102. data/samples/blog/bin/waves-server +3 -0
  103. data/samples/blog/configurations/development.rb +31 -0
  104. data/samples/blog/configurations/mapping.rb +23 -0
  105. data/samples/blog/configurations/production.rb +30 -0
  106. data/samples/blog/doc/EMTPY +0 -0
  107. data/samples/blog/lib/application.rb +5 -0
  108. data/samples/blog/models/comment.rb +14 -0
  109. data/samples/blog/models/entry.rb +14 -0
  110. data/samples/blog/public/css/site.css +2 -0
  111. data/samples/blog/public/javascript/site.js +13 -0
  112. data/samples/blog/schema/migrations/001_initial_schema.rb +17 -0
  113. data/samples/blog/schema/migrations/002_add_comments.rb +18 -0
  114. data/samples/blog/schema/migrations/templates/empty.rb.erb +9 -0
  115. data/samples/blog/startup.rb +6 -0
  116. data/samples/blog/templates/comment/add.mab +10 -0
  117. data/samples/blog/templates/comment/list.mab +6 -0
  118. data/samples/blog/templates/entry/editor.mab +13 -0
  119. data/samples/blog/templates/entry/list.mab +11 -0
  120. data/samples/blog/templates/entry/show.mab +9 -0
  121. data/samples/blog/templates/entry/summary.mab +5 -0
  122. data/samples/blog/templates/errors/not_found_404.mab +2 -0
  123. data/samples/blog/templates/errors/server_error_500.mab +2 -0
  124. data/samples/blog/templates/layouts/default.mab +17 -0
  125. data/verify/app_generation/helpers.rb +24 -0
  126. data/verify/app_generation/startup.rb +39 -0
  127. data/verify/blackboard/blackboard_verify.rb +92 -0
  128. data/verify/blackboard/helpers.rb +5 -0
  129. data/verify/configurations/attributes.rb +37 -0
  130. data/verify/configurations/helpers.rb +1 -0
  131. data/verify/configurations/rack_integration.rb +29 -0
  132. data/verify/controllers/base.rb +37 -0
  133. data/verify/controllers/helpers.rb +13 -0
  134. data/verify/controllers/interface.rb +51 -0
  135. data/verify/core/helpers.rb +3 -0
  136. data/verify/core/utilities.rb +177 -0
  137. data/verify/foundations/default.rb +86 -0
  138. data/verify/foundations/default_application/Rakefile +14 -0
  139. data/verify/foundations/default_application/bin/waves-console +3 -0
  140. data/verify/foundations/default_application/bin/waves-server +3 -0
  141. data/verify/foundations/default_application/configurations/development.rb +26 -0
  142. data/verify/foundations/default_application/configurations/mapping.rb +14 -0
  143. data/verify/foundations/default_application/configurations/production.rb +30 -0
  144. data/verify/foundations/default_application/controllers/default.rb +15 -0
  145. data/verify/foundations/default_application/controllers/loaded.rb +15 -0
  146. data/verify/foundations/default_application/defaultapplication.db +0 -0
  147. data/verify/foundations/default_application/helpers/loaded.rb +10 -0
  148. data/verify/foundations/default_application/lib/application.rb +5 -0
  149. data/verify/foundations/default_application/models/default.rb +13 -0
  150. data/verify/foundations/default_application/models/loaded.rb +13 -0
  151. data/verify/foundations/default_application/schema/migrations/templates/empty.rb.erb +9 -0
  152. data/verify/foundations/default_application/startup.rb +7 -0
  153. data/verify/foundations/default_application/templates/errors/not_found_404.mab +2 -0
  154. data/verify/foundations/default_application/templates/errors/server_error_500.mab +2 -0
  155. data/verify/foundations/default_application/templates/layouts/default.mab +14 -0
  156. data/verify/foundations/default_application/views/default.rb +7 -0
  157. data/verify/foundations/default_application/views/loaded.rb +15 -0
  158. data/verify/foundations/helpers.rb +1 -0
  159. data/verify/foundations/simple.rb +25 -0
  160. data/verify/helpers.rb +76 -0
  161. data/verify/layers/data_mapper/association_verify.rb +87 -0
  162. data/verify/layers/default_errors.rb +29 -0
  163. data/verify/layers/helpers.rb +1 -0
  164. data/verify/layers/migration.rb +33 -0
  165. data/verify/layers/sequel/model.rb +41 -0
  166. data/verify/mapping/always.rb +19 -0
  167. data/verify/mapping/filters.rb +65 -0
  168. data/verify/mapping/handle.rb +24 -0
  169. data/verify/mapping/helpers.rb +7 -0
  170. data/verify/mapping/matches.rb +27 -0
  171. data/verify/mapping/named.rb +29 -0
  172. data/verify/mapping/options.rb +17 -0
  173. data/verify/mapping/path.rb +40 -0
  174. data/verify/mapping/response_proxy.rb +50 -0
  175. data/verify/mapping/threaded.rb +25 -0
  176. data/verify/requests/helpers.rb +16 -0
  177. data/verify/requests/request.rb +73 -0
  178. data/verify/requests/response.rb +59 -0
  179. data/verify/requests/session.rb +54 -0
  180. data/verify/views/helpers.rb +1 -0
  181. data/verify/views/rendering.rb +34 -0
  182. data/verify/views/templates/foo.erb +0 -0
  183. data/verify/views/templates/moo.erb +0 -0
  184. data/verify/views/templates/moo.mab +0 -0
  185. metadata +439 -0
@@ -0,0 +1,9 @@
1
+ class <%= @class_name %> < Sequel::Migration
2
+
3
+ def up
4
+ end
5
+
6
+ def down
7
+ end
8
+
9
+ end
@@ -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