merb 0.4.0 → 0.4.1

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 (143) hide show
  1. data/README +23 -160
  2. data/Rakefile +15 -14
  3. data/app_generators/merb/merb_generator.rb +4 -3
  4. data/app_generators/merb/templates/Rakefile +1 -6
  5. data/app_generators/merb/templates/app/mailers/views/layout/{application.erb → application.html.erb} +0 -0
  6. data/app_generators/merb/templates/app/mailers/views/layout/application.text.erb +1 -0
  7. data/app_generators/merb/templates/app/parts/views/layout/application.html.erb +1 -0
  8. data/app_generators/merb/templates/app/views/layout/application.html.erb +2 -2
  9. data/app_generators/merb/templates/config/dependencies.rb +1 -1
  10. data/app_generators/merb/templates/config/router.rb +4 -1
  11. data/app_generators/merb/templates/spec/spec_helper.rb +2 -3
  12. data/lib/autotest/merb_rspec.rb +1 -0
  13. data/lib/merb/abstract_controller.rb +31 -2
  14. data/lib/merb/controller.rb +5 -5
  15. data/lib/merb/core_ext/get_args.rb +5 -1
  16. data/lib/merb/exceptions.rb +17 -0
  17. data/lib/merb/generators/merb_app/merb_app.rb +4 -1
  18. data/lib/merb/generators/merb_plugin.rb +4 -1
  19. data/lib/merb/logger.rb +5 -1
  20. data/lib/merb/mail_controller.rb +1 -1
  21. data/lib/merb/mailer.rb +2 -2
  22. data/lib/merb/mixins/controller.rb +5 -1
  23. data/lib/merb/mixins/render.rb +57 -27
  24. data/lib/merb/part_controller.rb +1 -1
  25. data/lib/merb/request.rb +2 -2
  26. data/lib/merb/server.rb +33 -5
  27. data/lib/merb/template/erubis.rb +1 -1
  28. data/lib/merb.rb +15 -5
  29. data/merb_generators/resource/resource_generator.rb +9 -2
  30. data/spec/fixtures/config/merb.yml +18 -0
  31. data/spec/fixtures/controllers/dispatch_spec_controllers.rb +227 -0
  32. data/spec/fixtures/controllers/render_spec_controllers.rb +115 -0
  33. data/spec/fixtures/foo.rb +3 -0
  34. data/spec/fixtures/mailers/views/layout/application.html.erb +3 -0
  35. data/spec/fixtures/mailers/views/layout/application.text.erb +3 -0
  36. data/spec/fixtures/mailers/views/test_mail_controller/eighth.html.erb +1 -0
  37. data/spec/fixtures/mailers/views/test_mail_controller/eighth.text.erb +1 -0
  38. data/spec/fixtures/mailers/views/test_mail_controller/first.html.erb +1 -0
  39. data/spec/fixtures/mailers/views/test_mail_controller/first.text.erb +1 -0
  40. data/spec/fixtures/mailers/views/test_mail_controller/ninth.html.erb +1 -0
  41. data/spec/fixtures/mailers/views/test_mail_controller/ninth.text.erb +1 -0
  42. data/spec/fixtures/mailers/views/test_mail_controller/second.text.erb +1 -0
  43. data/spec/fixtures/mailers/views/test_mail_controller/third.html.erb +1 -0
  44. data/spec/fixtures/models/router_spec_models.rb +20 -0
  45. data/spec/fixtures/parts/views/layout/todo_part.html.erb +3 -0
  46. data/spec/fixtures/parts/views/layout/todo_part.xml.erb +3 -0
  47. data/spec/fixtures/parts/views/todo_part/formatted_output.html.erb +1 -0
  48. data/spec/fixtures/parts/views/todo_part/formatted_output.js.erb +1 -0
  49. data/spec/fixtures/parts/views/todo_part/formatted_output.xml.erb +1 -0
  50. data/spec/fixtures/parts/views/todo_part/list.html.erb +3 -0
  51. data/spec/fixtures/sample.txt +1 -0
  52. data/spec/fixtures/views/erubis.html.erb +1 -0
  53. data/spec/fixtures/views/examples/_erubis.html.erb +1 -0
  54. data/spec/fixtures/views/examples/_haml.html.haml +1 -0
  55. data/spec/fixtures/views/examples/_markaby.html.mab +1 -0
  56. data/spec/fixtures/views/examples/_throw_content.html.erb +6 -0
  57. data/spec/fixtures/views/examples/hello.xml.builder +1 -0
  58. data/spec/fixtures/views/examples/js.js.erb +1 -0
  59. data/spec/fixtures/views/examples/template_catch_content.html.erb +15 -0
  60. data/spec/fixtures/views/examples/template_catch_content_from_partial.html.erb +6 -0
  61. data/spec/fixtures/views/examples/template_throw_content.html.erb +10 -0
  62. data/spec/fixtures/views/exceptions/admin_access_required.html.erb +1 -0
  63. data/spec/fixtures/views/extension_template_controller/_nested_js.js.erb +1 -0
  64. data/spec/fixtures/views/extension_template_controller/_nested_xml.xml.erb +1 -0
  65. data/spec/fixtures/views/extension_template_controller/_render_partial_multiple_times.html.erb +1 -0
  66. data/spec/fixtures/views/extension_template_controller/erubis_templates.html.erb +1 -0
  67. data/spec/fixtures/views/extension_template_controller/erubis_templates.js.erb +1 -0
  68. data/spec/fixtures/views/extension_template_controller/erubis_templates.rhtml +1 -0
  69. data/spec/fixtures/views/extension_template_controller/erubis_templates.xml.erb +1 -0
  70. data/spec/fixtures/views/extension_template_controller/haml_index.html.haml +0 -0
  71. data/spec/fixtures/views/extension_template_controller/haml_templates.html.haml +1 -0
  72. data/spec/fixtures/views/extension_template_controller/haml_templates.js.haml +1 -0
  73. data/spec/fixtures/views/extension_template_controller/haml_templates.xml.haml +1 -0
  74. data/spec/fixtures/views/extension_template_controller/index.html.erb +0 -0
  75. data/spec/fixtures/views/extension_template_controller/markaby_index.html.mab +0 -0
  76. data/spec/fixtures/views/extension_template_controller/markaby_templates.html.mab +1 -0
  77. data/spec/fixtures/views/extension_template_controller/markaby_templates.js.mab +1 -0
  78. data/spec/fixtures/views/extension_template_controller/markaby_templates.xml.mab +1 -0
  79. data/spec/fixtures/views/extension_template_controller/render_multiple_partials.html.erb +4 -0
  80. data/spec/fixtures/views/extension_template_controller/render_nested_js.js.erb +1 -0
  81. data/spec/fixtures/views/extension_template_controller/render_nested_xml.xml.erb +1 -0
  82. data/spec/fixtures/views/haml.html.haml +1 -0
  83. data/spec/fixtures/views/haml.xml.haml +2 -0
  84. data/spec/fixtures/views/layout/application.html.erb +1 -0
  85. data/spec/fixtures/views/layout/application.xml.erb +1 -0
  86. data/spec/fixtures/views/layout/nested/example.html.erb +1 -0
  87. data/spec/fixtures/views/markaby.html.mab +1 -0
  88. data/spec/fixtures/views/nested/example/test.html.erb +1 -0
  89. data/spec/fixtures/views/partials/_erubis.html.erb +1 -0
  90. data/spec/fixtures/views/partials/_erubis_collection.html.erb +1 -0
  91. data/spec/fixtures/views/partials/_erubis_collection_with_locals.html.erb +1 -0
  92. data/spec/fixtures/views/partials/_erubis_new.html.erb +1 -0
  93. data/spec/fixtures/views/partials/_haml.html.haml +1 -0
  94. data/spec/fixtures/views/partials/_haml_collection.html.haml +1 -0
  95. data/spec/fixtures/views/partials/_haml_collection_with_locals.html.haml +1 -0
  96. data/spec/fixtures/views/partials/_haml_new.html.haml +1 -0
  97. data/spec/fixtures/views/partials/_markaby.html.mab +1 -0
  98. data/spec/fixtures/views/partials/_markaby_collection.html.mab +1 -0
  99. data/spec/fixtures/views/partials/_markaby_collection_with_locals.html.mab +1 -0
  100. data/spec/fixtures/views/partials/_markaby_new.html.mab +1 -0
  101. data/spec/fixtures/views/render_object_controller/render_object_with_template.html.erb +1 -0
  102. data/spec/fixtures/views/render_object_controller/render_object_with_template.js.erb +1 -0
  103. data/spec/fixtures/views/render_object_controller/render_object_with_template.xml.erb +1 -0
  104. data/spec/fixtures/views/template_views/interface__buffer_erubis.html.erb +4 -0
  105. data/spec/fixtures/views/template_views/interface__buffer_haml.html.haml +7 -0
  106. data/spec/fixtures/views/template_views/interface__buffer_markaby.html.mab +7 -0
  107. data/spec/fixtures/views/template_views/interface_capture_erubis.html.erb +15 -0
  108. data/spec/fixtures/views/template_views/interface_capture_haml.html.haml +15 -0
  109. data/spec/fixtures/views/template_views/interface_capture_markaby.html.mab +4 -0
  110. data/spec/fixtures/views/template_views/interface_concat_erubis.html.erb +12 -0
  111. data/spec/fixtures/views/template_views/interface_concat_haml.html.haml +11 -0
  112. data/spec/fixtures/views/template_views/interface_concat_markaby.html.mab +14 -0
  113. data/spec/fixtures/views/test.dir/the_template.html.erb +1 -0
  114. data/spec/merb/abstract_controller_spec.rb +37 -0
  115. data/spec/merb/caching_spec.rb +102 -0
  116. data/spec/merb/config_spec.rb +29 -0
  117. data/spec/merb/controller_filters_spec.rb +188 -0
  118. data/spec/merb/controller_spec.rb +144 -0
  119. data/spec/merb/cookie_store_spec.rb +85 -0
  120. data/spec/merb/core_ext_spec.rb +430 -0
  121. data/spec/merb/dispatch_spec.rb +514 -0
  122. data/spec/merb/fake_request_spec.rb +72 -0
  123. data/spec/merb/form_control_mixin_spec.rb +431 -0
  124. data/spec/merb/generator_spec.rb +121 -0
  125. data/spec/merb/handler_spec.rb +169 -0
  126. data/spec/merb/mail_controller_spec.rb +144 -0
  127. data/spec/merb/mailer_spec.rb +87 -0
  128. data/spec/merb/multipart_spec.rb +49 -0
  129. data/spec/merb/part_controller_spec.rb +92 -0
  130. data/spec/merb/plugins_spec.rb +80 -0
  131. data/spec/merb/render_spec.rb +378 -0
  132. data/spec/merb/request_spec.rb +243 -0
  133. data/spec/merb/responder_spec.rb +561 -0
  134. data/spec/merb/router_spec.rb +726 -0
  135. data/spec/merb/template_spec.rb +41 -0
  136. data/spec/merb/upload_handler_spec.rb +101 -0
  137. data/spec/merb/view_context_spec.rb +148 -0
  138. data/spec/spec_generator_helper.rb +19 -0
  139. data/spec/spec_helper.rb +88 -0
  140. metadata +203 -65
  141. data/lib/merb/caching/store/memcache.rb +0 -20
  142. data/script/destroy +0 -14
  143. data/script/generate +0 -14
@@ -0,0 +1,561 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ module ResponderSpecModule
4
+ def new_mime(entry,index)
5
+ Merb::ResponderMixin::Rest::AcceptType.new(entry,index)
6
+ end
7
+ end
8
+
9
+ describe "The Merb Module" do
10
+
11
+ after do
12
+ Merb.reset_default_mime_types!
13
+ end
14
+
15
+ it "should respond to add_mime_type" do
16
+ Merb.should respond_to(:add_mime_type)
17
+ end
18
+ it "should respond to remove_mime_type" do
19
+ Merb.should respond_to(:remove_mime_type)
20
+ end
21
+ it "should respond to available_mime_types" do
22
+ Merb.should respond_to(:available_mime_types)
23
+ end
24
+ it "should respond to outgoing_headers" do
25
+ Merb.should respond_to(:response_headers)
26
+ end
27
+ it "should respond to add_outgoing_headers!" do
28
+ Merb.should respond_to(:add_response_headers!)
29
+ end
30
+ it "should respond to remove_outgoing_headers!" do
31
+ Merb.should respond_to(:remove_response_headers!)
32
+ end
33
+
34
+ it "should give access to the available_mime_types" do
35
+ Merb.available_mime_types.should equal(Merb::ResponderMixin::Rest::TYPES)
36
+ end
37
+
38
+ it "should add a mime type to the TYPES array" do
39
+ Merb::ResponderMixin::Rest::TYPES.has_key?(:png).should be_false
40
+ Merb.add_mime_type(:png, :to_png, %w[image/png])
41
+ Merb::ResponderMixin::Rest::TYPES.has_key?(:png).should be_true
42
+ end
43
+ it "should only accept Symbols for add_mime_type's key argument" do
44
+ lambda{
45
+ Merb.add_mime_type('silly string', %w[string/silly])
46
+ }.should raise_error(ArgumentError)
47
+ end
48
+ it "should only accept an Array for add_mime_type's values argument" do
49
+ lambda{Merb.add_mime_type(:key, :to_key, 'Congos')}.should raise_error(ArgumentError)
50
+ end
51
+ it "should remove a mime type from the TYPES array" do
52
+ Merb.add_mime_type(:png, :to_png, %w[image/png])
53
+ Merb::ResponderMixin::Rest::TYPES.has_key?(:png).should be_true
54
+ Merb.remove_mime_type(:png)
55
+ Merb::ResponderMixin::Rest::TYPES.has_key?(:png).should be_false
56
+ end
57
+ it "should not allow removal of the special :all mime-type" do
58
+ Merb.remove_mime_type(:all).should be_false
59
+ Merb::ResponderMixin::Rest::TYPES.has_key?(:all).should be_true
60
+ end
61
+
62
+ it "should add a mime type with outgoing headers defined" do
63
+ Merb.available_mime_types.should_not have_key(:pdf)
64
+ Merb.response_headers.should_not have_key?(:pdf)
65
+ Merb.mime_transform_method(:pdf).should be_nil
66
+ Merb.add_mime_type(:pdf, :to_pdf, %w[application/pdf],{"Content-Encoding" => "gzip"})
67
+ Merb.available_mime_types.should have_key(:pdf)
68
+ Merb.available_mime_types[:pdf].should == %w[application/pdf]
69
+ Merb.response_headers.should have_key(:pdf)
70
+ Merb.response_headers[:pdf].should == { "Content-Encoding" => "gzip" }
71
+ Merb.mime_transform_method(:pdf).should == :to_pdf
72
+ end
73
+
74
+ it "should add an outgoing header for an existing mime type" do
75
+ Merb.response_headers[:html].should be_empty
76
+ Merb.add_response_headers!(:html,{:header => "content"})
77
+ Merb.response_headers[:html].should == {:header => "content"}
78
+ end
79
+
80
+ it "should set the transform for the default html to nil" do
81
+ Merb.available_mime_types.should have_key(:html)
82
+ Merb.mime_transform_method(:html).should be_nil
83
+
84
+ end
85
+
86
+ it "should set xml to default to :Encoding => 'UTF-8'" do
87
+ Merb.response_headers[:xml].should == {:Encoding => "UTF-8"}
88
+ end
89
+
90
+ it "should set the xml transform method to :to_xml" do
91
+ Merb.mime_transform_method(:xml).should == :to_xml
92
+ end
93
+
94
+ it "should set the :js transform method to :to_json" do
95
+ Merb.mime_transform_method(:js).should == :to_json
96
+ end
97
+
98
+ it "should replace any and all existing headers on an existing mime type" do
99
+ header = {:header => "content"}
100
+ Merb.response_headers[:xml].should_not be_empty
101
+ Merb.response_headers[:xml].should_not == header
102
+ Merb.add_response_headers!(:xml, header)
103
+ Merb.response_headers[:xml].should == header
104
+ end
105
+
106
+ it "the specs should not alter the outgoing headers between specs" do
107
+ Merb.response_headers[:xml].should == {:Encoding => "UTF-8"}
108
+ end
109
+
110
+ it "should remove all existing headers for a given mime type" do
111
+ Merb.response_headers[:xml].should_not be_empty
112
+ Merb.remove_response_headers!(:xml)
113
+ Merb.response_headers[:xml].should be_empty
114
+ end
115
+ end
116
+
117
+ describe "A Merb Responder's AcceptType" do
118
+ include ResponderSpecModule
119
+
120
+ before :each do
121
+ @app_xhtml = new_mime('application/xhtml+xml',1)
122
+ @text_html = new_mime('text/html',5)
123
+ @app_html = new_mime('application/html;q=0.9',9)
124
+ end
125
+
126
+ it "should initialize properly from mime description and index" do
127
+ acc_entry = new_mime(' application/html ; q=0.9 ',1)
128
+ acc_entry.media_range.should == 'application/html'
129
+ acc_entry.quality.should == 90
130
+ acc_entry.index.should == 1
131
+ acc_entry.synonyms.should ==
132
+ %w[text/html application/xhtml+xml application/html]
133
+ acc_entry.super_range.should == 'text/html'
134
+ acc_entry.to_s.should == acc_entry.media_range
135
+ end
136
+
137
+ it "should assign lowest quality to */* unless otherwise specified" do
138
+ new_mime('*/*',1).quality.should == 0
139
+ new_mime('*/*;q=1.0',1).quality.should == 100
140
+ new_mime('*/*;q=0.2',1).quality.should == 20
141
+ end
142
+
143
+ it "should be equal to another AcceptType in the same synonym group" do
144
+ @text_html.should == @app_html
145
+ @text_html.should eql(@app_html)
146
+ @text_html.hash.should == @app_html.hash
147
+ end
148
+
149
+ it "should share a super range with an AcceptType in the same synonym group" do
150
+ @text_html.synonyms.should == @app_html.synonyms
151
+ @text_html.super_range.should == @app_html.super_range
152
+ end
153
+
154
+ it "should parse type and subtype" do
155
+ @app_xhtml.type.should == 'application'
156
+ @app_xhtml.sub_type.should == 'xhtml+xml'
157
+ @text_html.type.should == 'text'
158
+ @text_html.sub_type.should == 'html'
159
+ @app_html.type.should == 'application'
160
+ @app_html.sub_type.should == 'html'
161
+ end
162
+
163
+ end
164
+
165
+ describe "A Merb Responder's parsing of an Accept header" do
166
+
167
+ setup do
168
+ @acc_hdr = "text/xml,application/xml,application/xhtml+xml," \
169
+ "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
170
+ end
171
+
172
+ it "should parse accept header string to Array of AcceptType instances" do
173
+ acc_hdr = Merb::ResponderMixin::Rest::Responder.parse(@acc_hdr)
174
+ acc_hdr.should be_kind_of(Array)
175
+ acc_hdr.all?{|e| e.kind_of?(Merb::ResponderMixin::Rest::AcceptType) }.should be_true
176
+ end
177
+
178
+ it "should parse single entry accept headers" do
179
+ acc_hdr = Merb::ResponderMixin::Rest::Responder.parse('application/xml')
180
+ acc_hdr.should be_kind_of(Array)
181
+ acc_hdr.all?{|e| e.kind_of?(Merb::ResponderMixin::Rest::AcceptType) }.should be_true
182
+ end
183
+
184
+ it "should parse accept header into proper number of AcceptType instances" do
185
+ acc_hdr = 'foo/bar,baz/quuz,chimi/changa,cobra/khai'
186
+ acc_hdr = Merb::ResponderMixin::Rest::Responder.parse(acc_hdr)
187
+ acc_hdr.size.should == 4
188
+ end
189
+
190
+ it "should only return unique AcceptType instances" do
191
+ acc_hdr = 'text/html,application/xhtml+xml,application/html,text/xml,' \
192
+ 'application/xml,application/x-xml'
193
+ acc_hdr = Merb::ResponderMixin::Rest::Responder.parse(acc_hdr)
194
+ acc_hdr.size.should == 2
195
+ end
196
+
197
+ it "should sort AcceptType instances by quality" do
198
+ acc_hdr = 'foo/bar;q=0.1,donny/darko;q=0.9,tango/cash,water/melon;q=0.5'
199
+ acc_hdr = Merb::ResponderMixin::Rest::Responder.parse(acc_hdr)
200
+ acc_hdr.map!{|hdr| hdr.super_range }.should ==
201
+ %w[tango/cash donny/darko water/melon foo/bar]
202
+ end
203
+
204
+ it "should sort AcceptType instances by order" do
205
+ acc_hdr = 'foo/bar,baz/quuz,chimi/changa,cobra/khai'
206
+ acc_hdr = Merb::ResponderMixin::Rest::Responder.parse(acc_hdr)
207
+ acc_hdr.map!{|hdr| hdr.super_range }.should ==
208
+ %w[foo/bar baz/quuz chimi/changa cobra/khai]
209
+ end
210
+
211
+ it "should prefer alternate xml forms (foo+xml) over application/xml" do
212
+ acc_hdr = Merb::ResponderMixin::Rest::Responder.parse(@acc_hdr)
213
+ acc_hdr.first.super_range.should == 'text/html'
214
+ end
215
+
216
+ it "should sort AcceptType instances by quality and order" do
217
+ acc_hdr = Merb::ResponderMixin::Rest::Responder.parse(@acc_hdr)
218
+ acc_hdr.map!{|hdr| hdr.super_range }.should ==
219
+ %w[text/html application/xml image/png text/plain */*]
220
+ end
221
+
222
+ end
223
+
224
+
225
+ class ResponderSpecController < Merb::Controller
226
+ def index
227
+ only_provides :html, :xml, :yaml
228
+ content_type.to_s
229
+ end
230
+
231
+ def create
232
+ only_provides :xml
233
+ render :nothing => 201
234
+ end
235
+ end
236
+
237
+ class CrazyResponderSpecController < Merb::Controller
238
+ def index
239
+ only_provides :donkey
240
+ content_type
241
+ "donkey"
242
+ end
243
+ end
244
+
245
+ Merb::Server.load_action_arguments
246
+ Merb::Server.load_controller_template_path_cache
247
+
248
+ describe "A Merb Responder's Content Negotiation" do
249
+
250
+ it "should set Content-Type by :format for supported type: xml" do
251
+ c = new_responder_spec_controller(:format => 'xml')
252
+ c.dispatch(:index)
253
+ c.status.should == 200
254
+ c.headers['Content-Type'].should == 'application/xml'
255
+ end
256
+
257
+ it "should set Content-Type by :format for supported type: yaml" do
258
+ c = new_responder_spec_controller(:format => 'yaml')
259
+ c.dispatch(:index)
260
+ c.status.should == 200
261
+ c.headers['Content-Type'].should == 'application/x-yaml'
262
+ end
263
+
264
+ it "should set Content-Type by :format for supported type: html" do
265
+ c = new_responder_spec_controller(:format => 'html')
266
+ c.dispatch(:index)
267
+ c.status.should == 200
268
+ c.headers['Content-Type'].should == 'text/html'
269
+ end
270
+
271
+ it "should set Content-Type by accept header for supported type: xml" do
272
+ c = new_responder_spec_controller(:http_accept => 'text/xml')
273
+ c.dispatch(:index)
274
+ c.status.should == 200
275
+ c.headers['Content-Type'].should == 'application/xml'
276
+ end
277
+
278
+ it "should set Content-Type by accept header for supported type: yaml" do
279
+ c = new_responder_spec_controller(:http_accept => 'text/yaml')
280
+ c.dispatch(:index)
281
+ c.status.should == 200
282
+ c.headers['Content-Type'].should == 'application/x-yaml'
283
+ end
284
+
285
+ it "should set Content-Type by accept header for supported type: html" do
286
+ c = new_responder_spec_controller(:http_accept => 'text/html')
287
+ c.dispatch(:index)
288
+ c.status.should == 200
289
+ c.headers['Content-Type'].should == 'text/html'
290
+ end
291
+
292
+ it "should set Content-Type by :format in preference to accept headers when both are of a supported response type" do
293
+ c = new_responder_spec_controller(:http_accept => 'text/plain', :format => 'yaml')
294
+ c.dispatch(:index)
295
+ c.status.should == 200
296
+ c.headers['Content-Type'].should == 'application/x-yaml'
297
+ end
298
+
299
+ it "should set status 406 when format is of an unsupported response type" do
300
+ c = new_responder_spec_controller(:format => 'fromage')
301
+ lambda{c.dispatch(:index)}.should raise_error(Merb::ControllerExceptions::NotAcceptable)
302
+ end
303
+
304
+ it "should set status 406 when no accept header is of a unsupported response type" do
305
+ c = new_responder_spec_controller(:http_accept => 'stale/crackers;q=0.7,camel/milk;q=1.0')
306
+ lambda{c.dispatch(:index)}.should raise_error(Merb::ControllerExceptions::NotAcceptable)
307
+ end
308
+
309
+ it "should raise 406 when negotiated type is not in TYPES" do
310
+ r = Merb::Test::FakeRequest.new
311
+ c = CrazyResponderSpecController.build(r, r.body)
312
+ lambda{c.dispatch(:index)}.should raise_error(Merb::ControllerExceptions::NotAcceptable)
313
+ end
314
+
315
+ it "should call the block for the supported response type yaml" do
316
+ c = new_responder_spec_controller(:http_accept => 'text/yaml')
317
+ c.dispatch(:index)
318
+ c.body.should == "yaml"
319
+ end
320
+
321
+ it "should call the block for the supported response type xml" do
322
+ c = new_responder_spec_controller(:http_accept => 'text/xml')
323
+ c.dispatch(:index)
324
+ c.body.should == "xml"
325
+ end
326
+
327
+ it "should call the block for the supported response type html" do
328
+ c = new_responder_spec_controller(:http_accept => 'text/html')
329
+ c.dispatch(:index)
330
+ c.body.should == "html"
331
+ end
332
+
333
+ it "should utilise format in preference to accept header" do
334
+ c = new_responder_spec_controller(:http_accept => 'text/html', :format => 'xml')
335
+ c.dispatch(:index)
336
+ c.status.should == 200
337
+ c.headers['Content-Type'].should == 'application/xml'
338
+ end
339
+
340
+ it "should respond to the */* catchall accept header" do
341
+ c = new_responder_spec_controller(:http_accept => '*/*')
342
+ c.dispatch(:index)
343
+ c.status.should == 200
344
+ c.headers['Content-Type'].should == 'text/html'
345
+ end
346
+
347
+ it "should honor a return :nothing => status specified in the respond_to block" do
348
+ c = new_responder_spec_controller(:http_accept => 'application/xml')
349
+ c.dispatch(:create)
350
+ c.status.should == 201
351
+ end
352
+
353
+ def new_responder_spec_controller(options={})
354
+ params = {:controller => 'ResponderSpecController', :action => 'index'}
355
+ params.update(:format => options.delete(:format)) if options[:format]
356
+
357
+ @request = Merb::Test::FakeRequest.new(options)
358
+ @request.params.merge!(params)
359
+ ResponderSpecController.build(@request, @request.body)
360
+ end
361
+ end
362
+
363
+ class SimpleResponder
364
+ include Merb::ResponderMixin
365
+ end
366
+
367
+ describe "Merb::ResponderMixin", "negotiating content_type" do
368
+ before(:each) do
369
+ @responder = SimpleResponder.new
370
+ @responder.stub!(:provided_formats).and_return([:html, :xml])
371
+ @responder.stub!(:params).and_return({:format => "html"})
372
+ @request = mock("request")
373
+ @request.stub!(:accept).and_return("text/html")
374
+ @responder.stub!(:request).and_return(@request)
375
+ end
376
+
377
+ it "should have a perform_content_negotiation" do
378
+ @responder.should respond_to(:perform_content_negotiation)
379
+ end
380
+
381
+ it "should use provided_formats when params[:format] is nil" do
382
+ @responder.stub!(:params).and_return({})
383
+ @responder.should_receive(:provided_formats).
384
+ at_least(:once).and_return([:html])
385
+ @responder.perform_content_negotiation
386
+ end
387
+
388
+ it "should use provided_formats when params[:format] is not nil" do
389
+ @responder.should_receive(:provided_formats).
390
+ at_least(:once).and_return([:html])
391
+ @responder.perform_content_negotiation
392
+ end
393
+
394
+ it "should use params[:format] when determining content_type" do
395
+ @responder.should_receive(:params).at_least(:once).
396
+ and_return({:format => "html"})
397
+ @responder.perform_content_negotiation
398
+ end
399
+
400
+ it "should use request.accept when params[:format] is nil" do
401
+ @responder.stub!(:params).and_return({})
402
+ @responder.should_receive(:request).once.and_return(@request)
403
+ @request.should_receive(:accept).once.and_return("text/html")
404
+ @responder.perform_content_negotiation
405
+ end
406
+
407
+ it "should not use request.accept when params[:format] is not nil" do
408
+ @responder.should_receive(:request).exactly(0).times
409
+ @responder.perform_content_negotiation
410
+ end
411
+
412
+ it "should return :html when params[:format] = :html" do
413
+ @responder.perform_content_negotiation.should == :html
414
+ end
415
+
416
+ it "should raise NotAcceptable when params[:format] = :html but :html is not provided" do
417
+ @responder.should_receive(:provided_formats).at_least(:once).
418
+ and_return([:xml])
419
+ lambda {@responder.perform_content_negotiation}.should raise_error(Merb::ControllerExceptions::NotAcceptable)
420
+ end
421
+
422
+ it "should return :xml when it is the first provided and accepts is */*" do
423
+ @responder.stub!(:provided_formats).and_return([:xml, :html])
424
+ @responder.stub!(:params).and_return({})
425
+ @request.should_receive(:accept).once.and_return("*/*")
426
+ @responder.perform_content_negotiation.should == :xml
427
+ end
428
+
429
+ it "should return :xml when it is the first requested" do
430
+ @responder.stub!(:provided_formats).and_return([:html, :xml, :text])
431
+ @responder.stub!(:params).and_return({})
432
+ @request.should_receive(:accept).once.and_return("text/xml, text/html")
433
+ @responder.perform_content_negotiation.should == :xml
434
+ end
435
+
436
+ it "should raise NotAcceptable when accepts and provides do not intersect" do
437
+ @responder.stub!(:provided_formats).and_return([:html, :text])
438
+ @responder.stub!(:params).and_return({})
439
+ @request.should_receive(:accept).once.and_return("text/xml, text/json")
440
+ lambda {@responder.perform_content_negotiation}.should raise_error(Merb::ControllerExceptions::NotAcceptable)
441
+ end
442
+
443
+ it "should short circuit and raise NotAcceptable when provided_formats is empty" do
444
+ @responder.stub!(:provided_formats).and_return([])
445
+ @responder.should_receive(:params).exactly(0).times
446
+ @responder.should_receive(:request).exactly(0).times
447
+ lambda {@responder.perform_content_negotiation}.should raise_error(Merb::ControllerExceptions::NotAcceptable)
448
+ end
449
+ end
450
+
451
+ class DoubleProvides < Merb::Controller
452
+ def double_provides ; provides :xml; @_content_type = :html ; provides :txt ; end
453
+ end
454
+
455
+ describe "Merb::ResponderMixin", "dealing with content_type variable" do
456
+ it "should raise an error if provides is called after content_type has been determined" do
457
+ c = new_controller("double_provides",DoubleProvides)
458
+ lambda { c.double_provides }.should raise_error
459
+ end
460
+
461
+ it "should return false for content_type_set? when @_content_type is nil" do
462
+ c = new_controller("double_provides",DoubleProvides)
463
+ c.should_not be_content_type_set
464
+ end
465
+
466
+ it "should return true for content_type_set? when @_content_type is not nil" do
467
+ c = new_controller("double_provides",DoubleProvides)
468
+ c.instance_variable_set("@_content_type",:html)
469
+ c.should be_content_type_set
470
+ end
471
+
472
+ it "should return @_content_type when content_type is called" do
473
+ c = new_controller("double_provides",DoubleProvides)
474
+ c.instance_variable_set("@_content_type",:html)
475
+ c.content_type.should == :html
476
+ end
477
+
478
+ it "should allow content_type to be set directly" do
479
+ c = new_controller()
480
+ c.content_type=:txt
481
+ c.content_type.should == :txt
482
+ end
483
+ end
484
+
485
+ class FormattedBasic < Merb::Controller ; end
486
+ class WithXml < Merb::Controller ; provides :xml ; end
487
+ class XmlOnly < Merb::Controller ; only_provides :xml ; end
488
+ class ProvidesNothing < Merb::Controller ; does_not_provide :html ; end
489
+ class HtmlAgain < Merb::Controller ; provides :html ; end
490
+
491
+ class ManyProvides < Merb::Controller
492
+ provides :html, :xml, :txt
493
+ def noextra ; end
494
+ def extra ; provides :json, :yaml ; end
495
+ end
496
+
497
+ describe "Responder", "managing provided formats (class)" do
498
+ it "should have the default class_provided_formats of [:html]" do
499
+ c = new_controller("index",FormattedBasic)
500
+ c.class_provided_formats.should == [:html]
501
+ end
502
+
503
+ it "should add :xml to class_provided_formats when called with provides :xml" do
504
+ c = new_controller("index",WithXml)
505
+ c.class_provided_formats.should == [:html, :xml]
506
+ end
507
+
508
+ it "should only have :xml in class_provided_formats when called with only_provides :xml" do
509
+ c = new_controller("index",XmlOnly)
510
+ c.class_provided_formats.should == [:xml]
511
+ end
512
+
513
+ it "should have an empty class_provided_formats when called with does_not_provide :html" do
514
+ c = new_controller("index",ProvidesNothing)
515
+ c.class_provided_formats.should == []
516
+ end
517
+
518
+ it "should only have :html in class_provided_formats when called with provides :html (after other controllers have set it)" do
519
+ c = new_controller("index",HtmlAgain)
520
+ c.class_provided_formats.should == [:html]
521
+ end
522
+
523
+ it "should handle multiple provides added via class method" do
524
+ c = new_controller("noextra",ManyProvides)
525
+ c.provided_formats.should == [:html, :xml, :txt]
526
+ end
527
+ end
528
+
529
+ class ActionProvides < Merb::Controller
530
+ def basic ; end
531
+ def with_xml ; provides :xml ; end
532
+ def xml_only ; only_provides :xml ; end
533
+ def nothing ; does_not_provide :html ; end
534
+ end
535
+
536
+ describe "Responder", "managing provided formats (action)" do
537
+ it "should have the default provided_formats" do
538
+ c = new_controller("basic",ActionProvides)
539
+ c.basic
540
+ c.provided_formats.should == [:html]
541
+ end
542
+
543
+ it "should add :xml to provided_formats when called with provides :xml" do
544
+ c = new_controller("with_xml",ActionProvides)
545
+ c.with_xml
546
+ c.provided_formats.should == [:html, :xml]
547
+ end
548
+
549
+ it "should only have :xml in provided_formats when called with only_provides :xml" do
550
+ c = new_controller("xml_only",ActionProvides)
551
+ c.xml_only
552
+ c.provided_formats.should == [:xml]
553
+ end
554
+
555
+ it "should have an empty provided_formats when called with does_not_provide :html" do
556
+ c = new_controller("nothing",ActionProvides)
557
+ c.nothing
558
+ c.provided_formats.should be_empty
559
+ end
560
+ end
561
+