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
@@ -1,31 +0,0 @@
1
- require 'yaml/store'
2
-
3
- module Merb
4
- # thanks to Michael Fellinger from Ramaze
5
- class SimpleModel
6
- attr_accessor :db
7
-
8
- def initialize(filename = "#{DIST_ROOT}/schema/db.yaml")
9
- FileUtils.touch(filename)
10
- @db = YAML::Store.new(filename)
11
- end
12
-
13
- def method_missing(meth, *args, &block)
14
- @db.transaction do
15
- @db.send(meth, *args, &block)
16
- end
17
- end
18
-
19
- def [](key)
20
- @db.transaction do
21
- @db[key]
22
- end
23
- end
24
-
25
- def []=(key, value)
26
- @db.transaction do
27
- @db[key] = value
28
- end
29
- end
30
- end
31
- end
@@ -1,283 +0,0 @@
1
- module Merb
2
-
3
- module RenderMixin
4
-
5
- def self.included(base)
6
- base.class_eval {
7
- class_inheritable_accessor :_template_root,
8
- :_layout
9
-
10
- @@template_extensions = { }
11
- self._layout = :application
12
- self._template_root = File.expand_path(MERB_VIEW_ROOT)
13
- # This method is called by templating-engines to register themselves with
14
- # a list of extensions that will be looked up on #render of an action.
15
- def self.register_engine(engine, *extensions)
16
- [extensions].flatten.uniq.each do |ext|
17
- @@template_extensions[ext] = engine
18
- end
19
- end
20
-
21
- }
22
- end
23
-
24
- # universal render method. Template handlers are registered
25
- # by template extension. So you can use the same render method
26
- # for any kind of template that implements an adapter module.
27
- # out of the box Merb support Erubis, Haml, Markaby and Builder templates
28
- #
29
- # Erubis template ext: .herb .jerb .erb
30
- # Markaby template ext: .mab
31
- # Builder template ext: .rxml .builder .xerb
32
- # Haml template ext: .haml
33
- #
34
- # Examples:
35
- #
36
- # render
37
- #
38
- # Looks for views/controllername/actionname.* and renders
39
- # the template with the proper engine based on its file extension.
40
- #
41
- # render :layout => :none
42
- #
43
- # Renders the current template with no layout. XMl Builder templates
44
- # are exempt from layout by default.
45
- #
46
- # render :action => 'foo'
47
- #
48
- # Renders views/controllername/foo.*
49
- #
50
- # render :nothing => 200
51
- #
52
- # Renders nothing with a status of 200
53
- #
54
- # render :template => 'shared/message'
55
- #
56
- # Renders views/shared/message
57
- #
58
- # render :js => "$('some-div').toggle();"
59
- #
60
- # If the right hand side of :js => is a string then the proper
61
- # javascript headers will be set and the string will be returned
62
- # verbatim as js.
63
- #
64
- # render :js => :spinner
65
- #
66
- # When the rhs of :js => is a Symbol, it will be used as the
67
- # action/template name so: views/controllername/spinner.jerb
68
- # will be rendered as javascript
69
- #
70
- # render :js => true
71
- #
72
- # This will just look for the current controller/action template
73
- # with the .jerb extension and render it as javascript
74
- #
75
- # XML can be rendered with the same options as Javascript.
76
- #
77
- # render :xml => @posts.to_xml
78
- # render :xml => "<foo><bar>Hi!</bar></foo>"
79
- #
80
- # This will set the appropriate xml headers and render the rhs
81
- # of :xml => as a string. SO you can pass any xml string to this
82
- # to be rendered.
83
- #
84
- # render :xml => :hello
85
- #
86
- # Renders the hello.xrb template for the current controller.
87
- #
88
- # render :xml => true
89
- # render :xml => true, :action => "buffalo"
90
- #
91
- # Renders the buffalo.xerb template for the current controller.
92
- #
93
- def render(opts={}, &blk)
94
-
95
- action = if kind_of?(Merb::ControllerExceptions::Base)
96
- self.class.name.snake_case.split('::').last
97
- else
98
- opts[:action] || params[:action]
99
- end
100
-
101
- opts[:layout] ||= _layout
102
-
103
- case
104
- when status = opts[:nothing]
105
- return render_nothing(status)
106
- when partial = opts[:partial]
107
- template = find_partial(partial, opts)
108
- opts[:layout] = :none
109
-
110
- # Add an instance variable that can be used to create the locals in the
111
- # partial
112
- if opts[:locals]
113
- @_merb_partial_locals = opts[:locals]
114
- end
115
- opts[:clean_context] = true
116
- when js = opts[:js]
117
- headers['Content-Type'] = "text/javascript"
118
- opts[:layout] = :none
119
- case js
120
- when String
121
- return js
122
- when Symbol
123
- template = find_template(:action => js, :ext => 'jerb')
124
- else
125
- template = find_template(:action => action, :ext => 'jerb')
126
- end
127
- when xml = opts[:xml]
128
- headers['Content-Type'] = 'application/xml'
129
- headers['Encoding'] = 'UTF-8'
130
- # TODO This is the same as the js logic above. Can it be refactored?
131
- case xml
132
- when String
133
- return xml
134
- when Symbol
135
- template = find_template(:action => xml, :ext => 'rxml,xerb,builder')
136
- else
137
- template = find_template(:action => action, :ext => 'rxml,xerb,builder')
138
- end
139
- when template = opts[:template]
140
- template = find_template(:template => template)
141
- else
142
- template = find_template(:action => action)
143
- end
144
-
145
- engine = engine_for(template)
146
- options = {
147
- :file => template,
148
- :view_context => (opts[:clean_context] ? clean_view_context : cached_view_context),
149
- :opts => opts
150
- }
151
- content = engine.transform(options)
152
- if engine.exempt_from_layout? || opts[:layout] == :none
153
- content
154
- else
155
- wrap_layout(content, opts)
156
- end
157
- end
158
-
159
-
160
-
161
- # does a render with no layout. Also sets the
162
- # content type header to text/javascript. Use
163
- # this when you want to render a template with
164
- # .jerb extension.
165
- def render_js(template=nil)
166
- render :js => true, :action => (template || params[:action])
167
- end
168
-
169
- # renders nothing but sets the status, defaults
170
- # to 200. does send one ' ' space char, this is for
171
- # safari and flash uploaders to work.
172
- def render_nothing(status=200)
173
- @_status = status
174
- return " "
175
- end
176
-
177
- def set_status(status)
178
- @_status = status
179
- end
180
-
181
- def render_no_layout(opts={})
182
- render opts.update({:layout => :none})
183
- end
184
-
185
- # This is merb's partial render method. You name your
186
- # partials _partialname.* , and then call it like
187
- # partial(:partialname). If there is no '/' character
188
- # in the argument passed in it will look for the partial
189
- # in the view directory that corresponds to the current
190
- # controller name. If you pass a string with a path in it
191
- # you can render partials in other view directories. So
192
- # if you create a views/shared directory then you can call
193
- # partials that live there like partial('shared/foo')
194
- def partial(template, locals={})
195
- render :partial => template, :locals => locals
196
- end
197
-
198
- # +catch_content+ catches the thrown content from another template
199
- # So when you throw_content(:foo) {...} you can catch_content :foo
200
- # in another view or the layout.
201
- def catch_content(name)
202
- cached_view_context.instance_variable_get("@_#{name}_content")
203
- end
204
-
205
- private
206
-
207
- # this returns a ViewContext object populated with all
208
- # the instance variables in your controller. This is used
209
- # as the view context object for the Erubis templates.
210
- def cached_view_context
211
- @_view_context_cache ||= ViewContext.new(self)
212
- end
213
-
214
- def clean_view_context
215
- ViewContext.new(self)
216
- end
217
-
218
- def wrap_layout(content, opts={})
219
- if opts[:layout] != :application
220
- layout_choice = find_template(:layout => opts[:layout])
221
- else
222
- if name = find_template(:layout => self.class.name.snake_case.split('::').join('/'))
223
- layout_choice = name
224
- else
225
- layout_choice = find_template(:layout => :application)
226
- end
227
- end
228
-
229
- cached_view_context.instance_variable_set('@_layout_content', content)
230
- engine = engine_for(layout_choice)
231
- options = {
232
- :file => layout_choice,
233
- :view_context => cached_view_context,
234
- :opts => opts
235
- }
236
- engine.transform(options)
237
- end
238
-
239
- # OPTIMIZE : combine find_template and find_partial ?
240
- def find_template(opts={})
241
- if template = opts[:template]
242
- path = _template_root / template
243
- elsif opts[:action] and kind_of?(Merb::ControllerExceptions::Base)
244
- path = _template_root / 'exceptions' / opts[:action]
245
- elsif action = opts[:action]
246
- segment = self.class.name.snake_case.split('::').join('/')
247
- path = _template_root / segment / action
248
- elsif _layout = opts[:layout]
249
- path = _template_root / 'layout' / _layout
250
- else
251
- raise "called find_template without an :action or :layout"
252
- end
253
- extensions = [@@template_extensions.keys].flatten.uniq
254
- glob = "#{path}.{#{opts[:ext] || extensions.join(',')}}"
255
- Dir[glob].first
256
- end
257
-
258
- def find_partial(template, opts={})
259
- if template =~ /\//
260
- t = template.split('/')
261
- template = t.pop
262
- path = _template_root / t.join('/') / "_#{template}"
263
- else
264
- segment = self.class.name.snake_case.split('::').join('/')
265
- path = _template_root / segment / "_#{template}"
266
- end
267
- extensions = [@@template_extensions.keys].flatten.uniq
268
- glob = "#{path}.{#{opts[:ext] || extensions.join(',')}}"
269
- Dir[glob].first
270
- end
271
-
272
- # lookup the template_extensions for the extname of the filename
273
- # you pass. Answers with the engine that matches the extension, Template::Erubis
274
- # is used if none matches.
275
- def engine_for(file)
276
- extension = File.extname(file)[1..-1]
277
- @@template_extensions[extension]
278
- rescue
279
- ::Merb::Template::Erubis
280
- end
281
-
282
- end
283
- end
@@ -1,159 +0,0 @@
1
- require 'enumerator'
2
-
3
- module Merb
4
- # use this in your controllers to switch output based on
5
- # the HTTP_ACCEPT header. like so:
6
- # respond_to do |type|
7
- # type.js { render_js }
8
- # type.html { render }
9
- # type.xml { @foo.to_xml }
10
- # type.yaml { @foo.to_yaml }
11
- # end
12
- module ResponderMixin
13
-
14
- def respond_to(&block)
15
- responder = Rest::Responder.new(request.env['HTTP_ACCEPT'], params)
16
- block.call(responder)
17
- responder.respond(headers)
18
- @_status = responder.status if responder.status
19
- responder.body
20
- end
21
-
22
- module Rest
23
-
24
- TYPES = {
25
- :all => %w[*/*],
26
- :yaml => %w[application/x-yaml text/yaml],
27
- :text => %w[text/plain],
28
- :html => %w[text/html application/xhtml+xml application/html],
29
- :xml => %w[application/xml text/xml application/x-xml],
30
- :js => %w[application/json text/x-json text/javascript application/javascript application/x-javascript]
31
- }
32
-
33
- class Responder
34
-
35
- attr_reader :body, :type, :status
36
-
37
- def initialize(accept_header, params={})
38
- MERB_LOGGER.info accept_header
39
- @accepts = Responder.parse(accept_header)
40
- @params = params
41
- @stack = {}
42
- end
43
-
44
- def method_missing(symbol, &block)
45
- raise "respond_to expects a block" unless block_given?
46
- # the first method we encounter here will be used for the catch all mime-type */*
47
- @stack[:all] = block unless @stack[:all]
48
- @stack[symbol] = block
49
- end
50
-
51
- def respond(headers)
52
- unless @stack.keys.all?{|k| TYPES.has_key?(k) }
53
- raise "unrecognized mime type in respond_to block"
54
- end
55
- mime_type = negotiate_content
56
- if mime_type
57
- headers['Content-Type'] = mime_type.super_range
58
- @body = @stack[mime_type.to_sym].call
59
- else
60
- raise ControllerExceptions::NotAcceptable
61
- end
62
- end
63
-
64
- protected
65
-
66
- def self.parse(accept_header)
67
- # parse the raw accept header into a unique, sorted array of AcceptType objects
68
- returning( accept_header.split(/,/).enum_for(:each_with_index).map do |entry,index|
69
- AcceptType.new(entry,index += 1)
70
- end.sort.uniq ) do |list|
71
- # firefox (and possibly other browsers) send broken default accept headers.
72
- # fix them up by sorting alternate xml forms (namely application/xhtml+xml)
73
- # ahead of pure xml types (application/xml,text/xml).
74
- if app_xml = list.detect{|e| e.super_range == 'application/xml'}
75
- list.select{|e| e.to_s =~ /\+xml/}.each { |acc_type|
76
- list[list.index(acc_type)],list[list.index(app_xml)] =
77
- list[list.index(app_xml)],list[list.index(acc_type)] }
78
- end
79
- end
80
- end
81
-
82
- private
83
-
84
- def negotiate_content
85
- if @params[:format]
86
- negotiate_by_format
87
- elsif (@stack.keys & @accepts.map(&:to_sym)).size > 0
88
- negotiate_by_accept_header
89
- end
90
- end
91
-
92
- def negotiate_by_format
93
- format = @params[:format].to_sym
94
- if @stack[format]
95
- if @accepts.map(&:to_sym).include?(format)
96
- @accepts.detect{|a| a.to_sym == format }
97
- else
98
- AcceptType.new(TYPES[format].first,0)
99
- end
100
- end
101
- end
102
-
103
- def negotiate_by_accept_header
104
- @accepts.each do |accept|
105
- return accept if @stack[accept.to_sym] || accept.to_sym == :all
106
- end
107
- end
108
-
109
- end
110
-
111
- class AcceptType
112
-
113
- attr_reader :media_range, :quality, :index, :type, :sub_type
114
-
115
- def initialize(entry,index)
116
- @index = index
117
- @media_range, quality = entry.split(/;\s*q=/).map(&:strip)
118
- @type, @sub_type = @media_range.split(/\//)
119
- quality ||= 0.0 if @media_range == '*/*'
120
- @quality = ((quality || 1.0).to_f * 100).to_i
121
- end
122
-
123
- def <=>(entry)
124
- returning((entry.quality <=> quality).to_s) do |c|
125
- c.replace((index <=> entry.index).to_s) if c == '0'
126
- end.to_i
127
- end
128
-
129
- def eql?(entry)
130
- synonyms.include?(entry.media_range)
131
- end
132
-
133
- def ==(entry); eql?(entry); end
134
-
135
- def hash; super_range.hash; end
136
-
137
- def synonyms
138
- TYPES.values.select{|e| e.include?(@media_range)}.flatten
139
- end
140
-
141
- def super_range
142
- synonyms.first || @media_range
143
- end
144
-
145
- def to_sym
146
- TYPES.select{|k,v| v == synonyms }.flatten.first
147
- end
148
-
149
- def to_s
150
- @media_range
151
- end
152
-
153
- end
154
-
155
- end
156
-
157
- end
158
-
159
- end