merb 0.3.7 → 0.4.0

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 (173) hide show
  1. data/README +25 -26
  2. data/Rakefile +48 -36
  3. data/app_generators/merb/USAGE +5 -0
  4. data/app_generators/merb/merb_generator.rb +107 -0
  5. data/app_generators/merb/templates/Rakefile +99 -0
  6. data/{examples/skeleton/dist → app_generators/merb/templates}/app/controllers/application.rb +1 -1
  7. data/app_generators/merb/templates/app/controllers/exceptions.rb +13 -0
  8. data/{examples/skeleton/dist → app_generators/merb/templates}/app/helpers/global_helper.rb +0 -0
  9. data/{examples/skeleton/dist/app/mailers → app_generators/merb/templates/app/mailers/views}/layout/application.erb +0 -0
  10. data/app_generators/merb/templates/app/views/exceptions/internal_server_error.html.erb +207 -0
  11. data/app_generators/merb/templates/app/views/exceptions/not_acceptable.html.erb +38 -0
  12. data/app_generators/merb/templates/app/views/exceptions/not_found.html.erb +40 -0
  13. data/app_generators/merb/templates/app/views/layout/application.html.erb +11 -0
  14. data/app_generators/merb/templates/config/boot.rb +11 -0
  15. data/app_generators/merb/templates/config/dependencies.rb +41 -0
  16. data/{examples/skeleton/dist/conf → app_generators/merb/templates/config}/environments/development.rb +0 -0
  17. data/{examples/skeleton/dist/conf → app_generators/merb/templates/config}/environments/production.rb +0 -0
  18. data/{examples/skeleton/dist/conf → app_generators/merb/templates/config}/environments/test.rb +0 -0
  19. data/app_generators/merb/templates/config/merb.yml +64 -0
  20. data/app_generators/merb/templates/config/merb_init.rb +16 -0
  21. data/app_generators/merb/templates/config/plugins.yml +1 -0
  22. data/app_generators/merb/templates/config/router.rb +32 -0
  23. data/{lib/merb/core_ext/merb_array.rb → app_generators/merb/templates/config/upload.conf} +0 -0
  24. data/app_generators/merb/templates/public/images/merb.jpg +0 -0
  25. data/app_generators/merb/templates/public/merb.fcgi +6 -0
  26. data/app_generators/merb/templates/public/stylesheets/master.css +119 -0
  27. data/app_generators/merb/templates/script/destroy +28 -0
  28. data/app_generators/merb/templates/script/generate +28 -0
  29. data/{examples/skeleton → app_generators/merb/templates}/script/stop_merb +0 -0
  30. data/app_generators/merb/templates/script/win_script.cmd +1 -0
  31. data/app_generators/merb/templates/spec/spec.opts +6 -0
  32. data/app_generators/merb/templates/spec/spec_helper.rb +10 -0
  33. data/app_generators/merb/templates/test/test_helper.rb +13 -0
  34. data/app_generators/merb_plugin/USAGE +5 -0
  35. data/app_generators/merb_plugin/merb_plugin_generator.rb +64 -0
  36. data/app_generators/merb_plugin/templates/LICENSE +20 -0
  37. data/app_generators/merb_plugin/templates/README +4 -0
  38. data/app_generators/merb_plugin/templates/Rakefile +35 -0
  39. data/app_generators/merb_plugin/templates/TODO +5 -0
  40. data/app_generators/merb_plugin/templates/merbtasks.rb +6 -0
  41. data/app_generators/merb_plugin/templates/sampleplugin.rb +10 -0
  42. data/app_generators/merb_plugin/templates/sampleplugin_spec.rb +7 -0
  43. data/app_generators/merb_plugin/templates/spec_helper.rb +2 -0
  44. data/bin/merb +1 -1
  45. data/lib/autotest/discover.rb +3 -0
  46. data/lib/autotest/merb_rspec.rb +79 -0
  47. data/lib/merb.rb +72 -93
  48. data/lib/merb/{merb_abstract_controller.rb → abstract_controller.rb} +28 -5
  49. data/lib/merb/caching/action_cache.rb +65 -29
  50. data/lib/merb/caching/fragment_cache.rb +9 -4
  51. data/lib/merb/caching/store/file_cache.rb +22 -14
  52. data/lib/merb/caching/store/memory_cache.rb +26 -8
  53. data/lib/merb/{merb_constants.rb → constants.rb} +9 -7
  54. data/lib/merb/controller.rb +178 -0
  55. data/lib/merb/core_ext.rb +13 -11
  56. data/lib/merb/core_ext/array.rb +0 -0
  57. data/lib/merb/core_ext/{merb_class.rb → class.rb} +0 -0
  58. data/lib/merb/core_ext/{merb_enumerable.rb → enumerable.rb} +0 -0
  59. data/lib/merb/core_ext/get_args.rb +52 -0
  60. data/lib/merb/core_ext/{merb_hash.rb → hash.rb} +40 -11
  61. data/lib/merb/core_ext/{merb_inflections.rb → inflections.rb} +0 -0
  62. data/lib/merb/core_ext/{merb_inflector.rb → inflector.rb} +1 -1
  63. data/lib/merb/core_ext/{merb_kernel.rb → kernel.rb} +56 -3
  64. data/lib/merb/core_ext/mash.rb +88 -0
  65. data/lib/merb/core_ext/{merb_module.rb → module.rb} +0 -0
  66. data/lib/merb/core_ext/{merb_numeric.rb → numeric.rb} +0 -0
  67. data/lib/merb/core_ext/{merb_object.rb → object.rb} +10 -47
  68. data/lib/merb/core_ext/string.rb +56 -0
  69. data/lib/merb/core_ext/{merb_symbol.rb → symbol.rb} +0 -0
  70. data/lib/merb/dispatcher.rb +109 -0
  71. data/lib/merb/{merb_drb_server.rb → drb_server.rb} +0 -0
  72. data/lib/merb/erubis_ext.rb +10 -0
  73. data/lib/merb/exceptions.rb +173 -0
  74. data/lib/merb/generators/merb_app/merb_app.rb +5 -25
  75. data/lib/merb/generators/merb_generator_helpers.rb +317 -0
  76. data/lib/merb/generators/merb_plugin.rb +19 -0
  77. data/lib/merb/logger.rb +65 -0
  78. data/lib/merb/{merb_mail_controller.rb → mail_controller.rb} +102 -49
  79. data/lib/merb/{merb_mailer.rb → mailer.rb} +31 -27
  80. data/lib/merb/mixins/{basic_authentication_mixin.rb → basic_authentication.rb} +3 -3
  81. data/lib/merb/mixins/{controller_mixin.rb → controller.rb} +131 -112
  82. data/lib/merb/mixins/{erubis_capture_mixin.rb → erubis_capture.rb} +12 -21
  83. data/lib/merb/mixins/{form_control_mixin.rb → form_control.rb} +6 -12
  84. data/lib/merb/mixins/render.rb +401 -0
  85. data/lib/merb/mixins/responder.rb +378 -0
  86. data/lib/merb/mixins/{view_context_mixin.rb → view_context.rb} +65 -10
  87. data/lib/merb/mixins/web_controller.rb +29 -0
  88. data/lib/merb/{merb_handler.rb → mongrel_handler.rb} +59 -38
  89. data/lib/merb/part_controller.rb +19 -0
  90. data/lib/merb/plugins.rb +16 -0
  91. data/lib/merb/rack_adapter.rb +37 -0
  92. data/lib/merb/request.rb +421 -0
  93. data/lib/merb/router.rb +576 -0
  94. data/lib/merb/{merb_server.rb → server.rb} +275 -71
  95. data/lib/merb/session.rb +10 -10
  96. data/lib/merb/session/cookie_store.rb +125 -0
  97. data/lib/merb/session/{merb_mem_cache_session.rb → mem_cache_session.rb} +22 -9
  98. data/lib/merb/session/{merb_memory_session.rb → memory_session.rb} +15 -11
  99. data/lib/merb/template.rb +35 -8
  100. data/lib/merb/template/erubis.rb +16 -10
  101. data/lib/merb/template/haml.rb +33 -20
  102. data/lib/merb/template/markaby.rb +16 -14
  103. data/lib/merb/template/xml_builder.rb +8 -4
  104. data/lib/merb/test/{merb_fake_request.rb → fake_request.rb} +11 -5
  105. data/lib/merb/test/helper.rb +31 -0
  106. data/lib/merb/test/hpricot.rb +136 -0
  107. data/lib/merb/test/{merb_multipart.rb → multipart.rb} +1 -1
  108. data/lib/merb/test/rspec.rb +93 -0
  109. data/lib/merb/{merb_upload_handler.rb → upload_handler.rb} +5 -6
  110. data/lib/merb/{merb_upload_progress.rb → upload_progress.rb} +1 -1
  111. data/lib/merb/{merb_view_context.rb → view_context.rb} +27 -42
  112. data/lib/{merb_tasks.rb → tasks.rb} +0 -0
  113. data/lib/tasks/merb.rake +21 -11
  114. data/merb_default_generators/model/USAGE +0 -0
  115. data/merb_default_generators/model/model_generator.rb +16 -0
  116. data/merb_default_generators/model/templates/new_model_template.erb +5 -0
  117. data/merb_default_generators/resource_controller/USAGE +0 -0
  118. data/merb_default_generators/resource_controller/resource_controller_generator.rb +26 -0
  119. data/merb_default_generators/resource_controller/templates/controller.rb +30 -0
  120. data/merb_default_generators/resource_controller/templates/edit.html.erb +1 -0
  121. data/merb_default_generators/resource_controller/templates/helper.rb +5 -0
  122. data/merb_default_generators/resource_controller/templates/index.html.erb +1 -0
  123. data/merb_default_generators/resource_controller/templates/new.html.erb +1 -0
  124. data/merb_default_generators/resource_controller/templates/show.html.erb +1 -0
  125. data/merb_generators/controller/USAGE +5 -0
  126. data/merb_generators/controller/controller_generator.rb +16 -0
  127. data/merb_generators/controller/templates/controller.rb +8 -0
  128. data/merb_generators/controller/templates/helper.rb +5 -0
  129. data/merb_generators/controller/templates/index.html.erb +3 -0
  130. data/merb_generators/resource/USAGE +0 -0
  131. data/merb_generators/resource/resource_generator.rb +60 -0
  132. data/rspec_generators/merb_controller_test/merb_controller_test_generator.rb +67 -0
  133. data/rspec_generators/merb_controller_test/templates/controller_spec.rb +8 -0
  134. data/rspec_generators/merb_controller_test/templates/edit_spec.rb +12 -0
  135. data/rspec_generators/merb_controller_test/templates/helper_spec.rb +5 -0
  136. data/rspec_generators/merb_controller_test/templates/index_spec.rb +12 -0
  137. data/rspec_generators/merb_controller_test/templates/new_spec.rb +12 -0
  138. data/rspec_generators/merb_controller_test/templates/show_spec.rb +5 -0
  139. data/rspec_generators/merb_model_test/merb_model_test_generator.rb +26 -0
  140. data/rspec_generators/merb_model_test/templates/model_spec_template.erb +7 -0
  141. data/script/destroy +14 -0
  142. data/script/generate +14 -0
  143. data/test_unit_generators/merb_controller_test/merb_controller_test_generator.rb +53 -0
  144. data/test_unit_generators/merb_controller_test/templates/functional_test.rb +17 -0
  145. data/test_unit_generators/merb_controller_test/templates/helper_test.rb +9 -0
  146. data/test_unit_generators/merb_model_test/merb_model_test_generator.rb +29 -0
  147. data/test_unit_generators/merb_model_test/templates/model_test_unit_template.erb +9 -0
  148. metadata +172 -94
  149. data/examples/README_EXAMPLES +0 -10
  150. data/examples/skeleton/Rakefile +0 -68
  151. data/examples/skeleton/dist/app/views/layout/application.herb +0 -12
  152. data/examples/skeleton/dist/conf/database.yml +0 -23
  153. data/examples/skeleton/dist/conf/merb.yml +0 -57
  154. data/examples/skeleton/dist/conf/merb_init.rb +0 -24
  155. data/examples/skeleton/dist/conf/router.rb +0 -22
  156. data/examples/skeleton/dist/conf/upload.conf +0 -5
  157. data/examples/skeleton/dist/schema/migrations/001_add_sessions_table.rb +0 -14
  158. data/examples/skeleton/script/new_migration +0 -21
  159. data/lib/merb/core_ext/merb_string.rb +0 -18
  160. data/lib/merb/merb_controller.rb +0 -206
  161. data/lib/merb/merb_dispatcher.rb +0 -87
  162. data/lib/merb/merb_exceptions.rb +0 -319
  163. data/lib/merb/merb_part_controller.rb +0 -42
  164. data/lib/merb/merb_plugins.rb +0 -293
  165. data/lib/merb/merb_request.rb +0 -165
  166. data/lib/merb/merb_router.rb +0 -309
  167. data/lib/merb/merb_yaml_store.rb +0 -31
  168. data/lib/merb/mixins/render_mixin.rb +0 -283
  169. data/lib/merb/mixins/responder_mixin.rb +0 -159
  170. data/lib/merb/session/merb_ar_session.rb +0 -131
  171. data/lib/merb/vendor/paginator/README.txt +0 -84
  172. data/lib/merb/vendor/paginator/paginator.rb +0 -124
  173. data/lib/tasks/db.rake +0 -55
@@ -0,0 +1,378 @@
1
+ require 'enumerator'
2
+
3
+ module Merb
4
+ class << self
5
+ # Provides the currently implemented mime types as a hash
6
+ def available_mime_types
7
+ ResponderMixin::Rest::TYPES
8
+ end
9
+
10
+ # Any specific outgoing headers should be included here. These are not
11
+ # the content-type header but anything in addition to it.
12
+ # +tranform_method+ should be set to a symbol of the method used to
13
+ # transform a resource into this mime type.
14
+ # For example for the :xml mime type an object might be transformed by
15
+ # calling :to_xml, or for the :js mime type, :to_json.
16
+ # If there is no transform method, use nil.
17
+ def add_mime_type(key,transform_method, values,new_response_headers = {})
18
+ raise ArgumentError unless key.is_a?(Symbol) && values.is_a?(Array)
19
+ ResponderMixin::Rest::TYPES.update(key => values)
20
+ add_response_headers!(key, new_response_headers)
21
+ ResponderMixin::Rest::TRANSFORM_METHODS.merge!(key => transform_method)
22
+ end
23
+
24
+ def remove_mime_type(key)
25
+ key == :all ? false : ResponderMixin::Rest::TYPES.delete(key)
26
+ end
27
+
28
+ def mime_transform_method(key)
29
+ ResponderMixin::Rest::TRANSFORM_METHODS[key]
30
+ end
31
+
32
+
33
+ # Adds outgoing headers to a mime type. This can be done with the Merb.add_mime_type method
34
+ # or directly here.
35
+ # ===Example
36
+ # {{[
37
+ # Merb.outgoing_headers!(:xml => { :Encoding => "UTF-8" })
38
+ # ]}}
39
+ #
40
+ # This method is destructive on any already defined outgoing headers
41
+ def add_response_headers!(key, values = {})
42
+ raise ArgumentError unless key.is_a?(Symbol) && values.is_a?(Hash)
43
+ response_headers[key] = values
44
+ end
45
+
46
+ def response_headers
47
+ ResponderMixin::Rest::RESPONSE_HEADERS
48
+ end
49
+
50
+ # Completely removes any headers set that are additional to the content-type header.
51
+ def remove_response_headers!(key)
52
+ raise ArgumentError unless key.is_a?(Symbol)
53
+ response_headers[key] = {}
54
+ end
55
+
56
+ # Sets the mime types and outgoing headers to their original states
57
+ def reset_default_mime_types!
58
+ available_mime_types.clear
59
+ response_headers.clear
60
+ Merb.add_mime_type(:all,nil,%w[*/*])
61
+ Merb.add_mime_type(:yaml,:to_yaml,%w[application/x-yaml text/yaml])
62
+ Merb.add_mime_type(:text,:to_text,%w[text/plain])
63
+ Merb.add_mime_type(:html,nil,%w[text/html application/xhtml+xml application/html])
64
+ Merb.add_mime_type(:xml,:to_xml,%w[application/xml text/xml application/x-xml], :Encoding => "UTF-8")
65
+ Merb.add_mime_type(:js,:to_json,%w[ text/javascript application/javascript application/x-javascript])
66
+ Merb.add_mime_type(:json,:to_json,%w[application/json text/x-json ])
67
+ end
68
+
69
+ end
70
+
71
+ # The ResponderMixin adds methods that help you manage what
72
+ # formats your controllers have available, determine what format(s)
73
+ # the client requested and is capable of handling, and perform
74
+ # content negotiation to pick the proper content format to
75
+ # deliver.
76
+ #
77
+ # If you hear someone say "Use provides" they're talking about the
78
+ # Responder. If you hear someone ask "What happened to respond_to?"
79
+ # it was replaced by provides and the other Responder methods.
80
+ #
81
+ # == A simple example
82
+ #
83
+ # The best way to understand how all of these pieces fit together is
84
+ # with an example. Here's a simple web-service ready resource that
85
+ # provides a list of all the widgets we know about. The widget list is
86
+ # available in 3 formats: :html (the default), plus :xml and :text.
87
+ #
88
+ # class Widgets < Application
89
+ # provides :html # This is the default, but you can
90
+ # # be explicit if you like.
91
+ # provides :xml, :text
92
+ #
93
+ # def index
94
+ # @widgets = Widget.fetch
95
+ # render @widgets
96
+ # end
97
+ # end
98
+ #
99
+ # Let's look at some example requests for this list of widgets. We'll
100
+ # assume they're all GET requests, but that's only to make the examples
101
+ # easier; this works for the full set of RESTful methods.
102
+ #
103
+ # 1. The simplest case, /widgets.html
104
+ # Since the request includes a specific format (.html) we know
105
+ # what format to return. Since :html is in our list of provided
106
+ # formats, that's what we'll return. +render+ will look
107
+ # for an index.html.erb (or another template format
108
+ # like index.html.mab; see the documentation on Template engines)
109
+ #
110
+ # 2. Almost as simple, /widgets.xml
111
+ # This is very similar. They want :xml, we have :xml, so
112
+ # that's what they get. If +render+ doesn't find an
113
+ # index.xml.builder or similar template, it will call +to_xml+
114
+ # on @widgets. This may or may not do something useful, but you can
115
+ # see how it works.
116
+ #
117
+ # 3. A browser request for /widgets
118
+ # This time the URL doesn't say what format is being requested, so
119
+ # we'll look to the HTTP Accept: header. If it's '*/*' (anything),
120
+ # we'll use the first format on our list, :html by default.
121
+ #
122
+ # If it parses to a list of accepted formats, we'll look through
123
+ # them, in order, until we find one we have available. If we find
124
+ # one, we'll use that. Otherwise, we can't fulfill the request:
125
+ # they asked for a format we don't have. So we raise
126
+ # 406: Not Acceptable.
127
+ #
128
+ # == A more complex example
129
+ #
130
+ # Sometimes you don't have the same code to handle each available
131
+ # format. Sometimes you need to load different data to serve
132
+ # /widgets.xml versus /widgets.txt. In that case, you can use
133
+ # +content_type+ to determine what format will be delivered.
134
+ #
135
+ # class Widgets < Application
136
+ # def action1
137
+ # if content_type == :text
138
+ # Widget.load_text_formatted(params[:id])
139
+ # else
140
+ # render
141
+ # end
142
+ # end
143
+ #
144
+ # def action2
145
+ # case content_type
146
+ # when :html
147
+ # handle_html()
148
+ # when :xml
149
+ # handle_xml()
150
+ # when :text
151
+ # handle_text()
152
+ # else
153
+ # render
154
+ # end
155
+ # end
156
+ # end
157
+ #
158
+ # You can do any standard Ruby flow control using +content_type+. If
159
+ # you don't call it yourself, it will be called (triggering content
160
+ # negotiation) by +render+.
161
+ #
162
+ # Once +content_type+ has been called, the output format is frozen,
163
+ # and none of the provides methods can be used.
164
+ module ResponderMixin
165
+
166
+ def self.included(base) # :nodoc:
167
+ base.extend(ClassMethods)
168
+ base.class_eval { class_inheritable_accessor :class_provided_formats }
169
+ base.class_provided_formats = [:html]
170
+ end
171
+
172
+ module ClassMethods
173
+
174
+ # Adds symbols representing formats to the controller's
175
+ # default list of provided_formats. These will apply to
176
+ # every action in the controller, unless modified in the action.
177
+ def provides(*formats)
178
+ formats.each {|fmt|
179
+ self.class_provided_formats << fmt unless class_provided_formats.include?(fmt)
180
+ }
181
+ end
182
+
183
+ # Overwrites the controller's list of provided_formats. These
184
+ # will apply to every action in the controller, unless modified
185
+ # in the action.
186
+ def only_provides(*formats)
187
+ self.class_provided_formats = formats
188
+ end
189
+
190
+ # Removes formats from the controller's
191
+ # default list of provided_formats. These will apply to
192
+ # every action in the controller, unless modified in the action.
193
+ def does_not_provide(*formats)
194
+ self.class_provided_formats -= formats
195
+ end
196
+ end
197
+
198
+ # Returns the current list of formats provided for this instance
199
+ # of the controller. It starts with what has been set in the controller
200
+ # (or :html by default) but can be modifed on a per-action basis.
201
+ def provided_formats
202
+ @_provided_formats ||= class_provided_formats
203
+ end
204
+
205
+ # Sets the provided formats for this action. Usually, you would
206
+ # use a combination of +provides+, +only_provides+ and +does_not_provide+
207
+ # to manage this, but you can set it directly.
208
+ def provided_formats=(*formats)
209
+ raise "Cannot modify provided_formats because content_type has already been set" if content_type_set?
210
+ @_provided_formats = formats.flatten
211
+ end
212
+
213
+ # Adds formats to the list of provided formats for this particular
214
+ # request. Usually used to add formats to a single action. See also
215
+ # the controller-level provides that affects all actions in a controller.
216
+ def provides(*formats)
217
+ formats.each {|fmt|
218
+ self.provided_formats += [fmt] unless provided_formats.include?(fmt)
219
+ }
220
+ end
221
+
222
+ # Sets list of provided formats for this particular
223
+ # request. Usually used to limit formats to a single action. See also
224
+ # the controller-level provides that affects all actions in a controller.
225
+ def only_provides(*formats)
226
+ self.provided_formats = formats.flatten
227
+ end
228
+
229
+ # Removes formats from the list of provided formats for this particular
230
+ # request. Usually used to remove formats from a single action. See
231
+ # also the controller-level provides that affects all actions in a
232
+ # controller.
233
+ def does_not_provide(*formats)
234
+ self.provided_formats -= formats.flatten
235
+ end
236
+
237
+ # Do the content negotiation:
238
+ # 1. if params[:format] is there, and provided, use it
239
+ # 2. Parse the Accept header
240
+ # 3. If it's */*, use the first provided format
241
+ # 4. Look for one that is provided, in order of request
242
+ # 5. Raise 406 if none found
243
+ def perform_content_negotiation # :nodoc:
244
+ raise Merb::ControllerExceptions::NotAcceptable if provided_formats.empty?
245
+ if fmt = params[:format]
246
+ if provided_formats.include?(fmt.to_sym)
247
+ fmt.to_sym
248
+ else
249
+ raise Merb::ControllerExceptions::NotAcceptable
250
+ end
251
+ else
252
+ accepts = Rest::Responder.parse(request.accept).
253
+ collect {|t| t.to_sym}
254
+ if accepts.include?(:all)
255
+ provided_formats.first
256
+ else
257
+ accepts.each do |type|
258
+ return type if provided_formats.include?(type)
259
+ end
260
+ raise Merb::ControllerExceptions::NotAcceptable
261
+ end
262
+ end
263
+ end
264
+
265
+ # Checks to see if content negotiation has already been performed.
266
+ # If it has, you can no longer modify the list of provided formats.
267
+ def content_type_set?
268
+ !@_content_type.nil?
269
+ end
270
+
271
+ # Returns the output format for this request, based on the
272
+ # provided formats, <tt>params[:format]</tt> and the client's HTTP
273
+ # Accept header.
274
+ #
275
+ # The first time this is called, it triggers content negotiation
276
+ # and caches the value. Once you call +content_type+ you can
277
+ # not set or change the list of provided formats.
278
+ #
279
+ # Called automatically by +render+, so you should only call it if
280
+ # you need the value, not to trigger content negotiation.
281
+ def content_type
282
+ unless content_type_set?
283
+ @_content_type = perform_content_negotiation
284
+ raise Merb::ControllerExceptions::NotAcceptable.new("Unknown content_type for response: #{@_content_type}") unless
285
+ Merb.available_mime_types.has_key?(@_content_type)
286
+ headers['Content-Type'] = Merb.available_mime_types[@_content_type].first
287
+ end
288
+ @_content_type
289
+ end
290
+
291
+ # Sets the output content_type for this request. Normally you
292
+ # should use +provides+, +does_not_provide+ and +only_provides+
293
+ # and then let the content negotiation process determine the proper
294
+ # content_type. However, in some circumstances you may want to
295
+ # set it directly, or override what content negotiation picks.
296
+ def content_type=(new_type)
297
+ @_content_type = new_type
298
+ end
299
+
300
+ module Rest
301
+
302
+ TYPES = {}
303
+ RESPONSE_HEADERS = {}
304
+ TRANSFORM_METHODS = {}
305
+
306
+ class Responder
307
+
308
+ protected
309
+
310
+ def self.parse(accept_header)
311
+ # parse the raw accept header into a unique, sorted array of AcceptType objects
312
+ list = accept_header.to_s.split(/,/).enum_for(:each_with_index).map do |entry,index|
313
+ AcceptType.new(entry,index += 1)
314
+ end.sort.uniq
315
+ # firefox (and possibly other browsers) send broken default accept headers.
316
+ # fix them up by sorting alternate xml forms (namely application/xhtml+xml)
317
+ # ahead of pure xml types (application/xml,text/xml).
318
+ if app_xml = list.detect{|e| e.super_range == 'application/xml'}
319
+ list.select{|e| e.to_s =~ /\+xml/}.each { |acc_type|
320
+ list[list.index(acc_type)],list[list.index(app_xml)] =
321
+ list[list.index(app_xml)],list[list.index(acc_type)] }
322
+ end
323
+ list
324
+ end
325
+
326
+ end
327
+
328
+ class AcceptType
329
+
330
+ attr_reader :media_range, :quality, :index, :type, :sub_type
331
+
332
+ def initialize(entry,index)
333
+ @index = index
334
+ @media_range, quality = entry.split(/;\s*q=/).map{|a| a.strip }
335
+ @type, @sub_type = @media_range.split(/\//)
336
+ quality ||= 0.0 if @media_range == '*/*'
337
+ @quality = ((quality || 1.0).to_f * 100).to_i
338
+ end
339
+
340
+ def <=>(entry)
341
+ c = entry.quality <=> quality
342
+ c = index <=> entry.index if c == 0
343
+ c
344
+ end
345
+
346
+ def eql?(entry)
347
+ synonyms.include?(entry.media_range)
348
+ end
349
+
350
+ def ==(entry); eql?(entry); end
351
+
352
+ def hash; super_range.hash; end
353
+
354
+ def synonyms
355
+ @syns ||= TYPES.values.select{|e| e.include?(@media_range)}.flatten
356
+ end
357
+
358
+ def super_range
359
+ synonyms.first || @media_range
360
+ end
361
+
362
+ def to_sym
363
+ TYPES.select{|k,v|
364
+ v == synonyms || v[0] == synonyms[0]}.flatten.first
365
+ end
366
+
367
+ def to_s
368
+ @media_range
369
+ end
370
+
371
+ end
372
+
373
+ end
374
+
375
+ end
376
+ reset_default_mime_types!
377
+
378
+ end
@@ -22,7 +22,8 @@ module Merb
22
22
  # # => <a href="blog/show/13">The Entry Title</a>
23
23
  #
24
24
  def link_to(name, url='', opts={})
25
- %{<a href="#{url}" #{opts.map{|k,v| "#{k}=\"#{v}\""}.join(' ')}>#{name}</a>}
25
+ opts[:href] ||= url
26
+ %{<a #{ opts.to_xml_attributes }>#{name}</a>}
26
27
  end
27
28
 
28
29
  # Creates an image tag with the +src+ attribute set to the +img+ argument. The path
@@ -44,11 +45,14 @@ module Merb
44
45
  # image_tag('foo.gif', :path => '/files/')
45
46
  # # => <img src='/files/foo.gif' />
46
47
  #
48
+ # image_tag('http://test.com/foo.gif')
49
+ # # => <img src="http://test.com/foo.gif">
47
50
  def image_tag(img, opts={})
48
- opts[:path] ||= '/images/'
49
- %{<img src="#{opts.delete(:path) + img}" #{opts.map{|k,v| "#{k}=\"#{v}\""}.join(' ')} />}
51
+ opts[:path] ||= (img =~ %r{^https?://}) ? '' : '/images/'
52
+ opts[:src] ||= opts.delete(:path) + img
53
+ %{<img #{ opts.to_xml_attributes } />}
50
54
  end
51
-
55
+
52
56
  # :section: JavaScript related functions
53
57
  #
54
58
 
@@ -107,7 +111,7 @@ module Merb
107
111
  # need to include a call to include_required_js and include_required_css.
108
112
  #
109
113
  # ==== Examples
110
- # # File: app/views/layouts/application.herb
114
+ # # File: app/views/layouts/application.html.erb
111
115
  #
112
116
  # <html>
113
117
  # <head>
@@ -194,18 +198,19 @@ module Merb
194
198
  #
195
199
  # ==== Examples
196
200
  # # my_action.herb has a call to require_js 'jquery'
197
- # # File: layout/application.herb
201
+ # # File: layout/application.html.erb
198
202
  # include_required_js
199
203
  # # => <script src="/javascripts/jquery.js" type="text/javascript"></script>
200
204
  #
201
205
  # # my_action.herb has a call to require_js 'jquery', 'effects', 'validation'
202
- # # File: layout/application.herb
206
+ # # File: layout/application.html.erb
203
207
  # include_required_js
204
208
  # # => <script src="/javascripts/jquery.js" type="text/javascript"></script>
205
209
  # # <script src="/javascripts/effects.js" type="text/javascript"></script>
206
210
  # # <script src="/javascripts/validation.js" type="text/javascript"></script>
207
211
  #
208
212
  def include_required_js
213
+ return '' if @required_js.nil?
209
214
  js_include_tag(*@required_js)
210
215
  end
211
216
 
@@ -214,18 +219,19 @@ module Merb
214
219
  #
215
220
  # ==== Examples
216
221
  # # my_action.herb has a call to require_css 'style'
217
- # # File: layout/application.herb
222
+ # # File: layout/application.html.erb
218
223
  # include_required_css
219
224
  # # => <link href="/stylesheets/style.css" media="all" rel="Stylesheet" type="text/css"/>
220
225
  #
221
226
  # # my_action.herb has a call to require_js 'style', 'ie-specific'
222
- # # File: layout/application.herb
227
+ # # File: layout/application.html.erb
223
228
  # include_required_css
224
229
  # # => <link href="/stylesheets/style.css" media="all" rel="Stylesheet" type="text/css"/>
225
230
  # # <link href="/stylesheets/ie-specific.css" media="all" rel="Stylesheet" type="text/css"/>
226
231
  #
227
232
  def include_required_css
228
- css_link_tag(*@required_css)
233
+ return '' if @required_css.nil?
234
+ css_include_tag(*@required_css)
229
235
  end
230
236
 
231
237
  # The js_include_tag method will create a JavaScript
@@ -319,5 +325,54 @@ module Merb
319
325
  ::Merb::Caching::Fragment.put(name, buffer[pos..-1])
320
326
  end
321
327
  end
328
+
329
+ # Calling throw_content stores the block of markup for later use.
330
+ # Subsequently, you can make calls to it by name with <tt>catch_content</tt>
331
+ # in another template or in the layout.
332
+ #
333
+ # Example:
334
+ #
335
+ # <% throw_content :header do %>
336
+ # alert('hello world')
337
+ # <% end %>
338
+ #
339
+ # You can use catch_content :header anywhere in your templates.
340
+ #
341
+ # <%= catch_content :header %>
342
+ def throw_content(name, content = nil, &block)
343
+ controller.thrown_content[name] << ( content || "" ) << capture( &block )
344
+ end
345
+
346
+ # Concat will concatenate text directly to the buffer of the template.
347
+ # The binding must be supplied in order to obtian the buffer. This can be called directly in the
348
+ # template as
349
+ # concat( "text", binding )
350
+ #
351
+ # or from a helper method that accepts a block as
352
+ # concat( "text", block.binding )
353
+ def concat( string, binding )
354
+ _buffer( binding ) << string
355
+ end
356
+
357
+ # Creates the opening tag with attributes for the provided +name+
358
+ # attrs is a hash where all members will be mapped to key="value"
359
+ #
360
+ # Note: This tag will need to be closed
361
+ def open_tag(name, attrs = nil)
362
+ "<#{name}#{(' ' + attrs.to_html_attributes) if attrs && !attrs.empty?}>"
363
+ end
364
+
365
+ # Creates a closing tag
366
+ def close_tag(name)
367
+ "</#{name}>"
368
+ end
369
+
370
+ # Creates a self closing tag. Like <br/> or <img src="..."/>
371
+ #
372
+ # +name+ : the name of the tag to create
373
+ # +attrs+ : a hash where all members will be mapped to key="value"
374
+ def self_closing_tag(name, attrs = nil)
375
+ "<#{name}#{' ' + attrs.to_html_attributes if attrs}/>"
376
+ end
322
377
  end
323
378
  end