merb 0.3.7 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +25 -26
- data/Rakefile +48 -36
- data/app_generators/merb/USAGE +5 -0
- data/app_generators/merb/merb_generator.rb +107 -0
- data/app_generators/merb/templates/Rakefile +99 -0
- data/{examples/skeleton/dist → app_generators/merb/templates}/app/controllers/application.rb +1 -1
- data/app_generators/merb/templates/app/controllers/exceptions.rb +13 -0
- data/{examples/skeleton/dist → app_generators/merb/templates}/app/helpers/global_helper.rb +0 -0
- data/{examples/skeleton/dist/app/mailers → app_generators/merb/templates/app/mailers/views}/layout/application.erb +0 -0
- data/app_generators/merb/templates/app/views/exceptions/internal_server_error.html.erb +207 -0
- data/app_generators/merb/templates/app/views/exceptions/not_acceptable.html.erb +38 -0
- data/app_generators/merb/templates/app/views/exceptions/not_found.html.erb +40 -0
- data/app_generators/merb/templates/app/views/layout/application.html.erb +11 -0
- data/app_generators/merb/templates/config/boot.rb +11 -0
- data/app_generators/merb/templates/config/dependencies.rb +41 -0
- data/{examples/skeleton/dist/conf → app_generators/merb/templates/config}/environments/development.rb +0 -0
- data/{examples/skeleton/dist/conf → app_generators/merb/templates/config}/environments/production.rb +0 -0
- data/{examples/skeleton/dist/conf → app_generators/merb/templates/config}/environments/test.rb +0 -0
- data/app_generators/merb/templates/config/merb.yml +64 -0
- data/app_generators/merb/templates/config/merb_init.rb +16 -0
- data/app_generators/merb/templates/config/plugins.yml +1 -0
- data/app_generators/merb/templates/config/router.rb +32 -0
- data/{lib/merb/core_ext/merb_array.rb → app_generators/merb/templates/config/upload.conf} +0 -0
- data/app_generators/merb/templates/public/images/merb.jpg +0 -0
- data/app_generators/merb/templates/public/merb.fcgi +6 -0
- data/app_generators/merb/templates/public/stylesheets/master.css +119 -0
- data/app_generators/merb/templates/script/destroy +28 -0
- data/app_generators/merb/templates/script/generate +28 -0
- data/{examples/skeleton → app_generators/merb/templates}/script/stop_merb +0 -0
- data/app_generators/merb/templates/script/win_script.cmd +1 -0
- data/app_generators/merb/templates/spec/spec.opts +6 -0
- data/app_generators/merb/templates/spec/spec_helper.rb +10 -0
- data/app_generators/merb/templates/test/test_helper.rb +13 -0
- data/app_generators/merb_plugin/USAGE +5 -0
- data/app_generators/merb_plugin/merb_plugin_generator.rb +64 -0
- data/app_generators/merb_plugin/templates/LICENSE +20 -0
- data/app_generators/merb_plugin/templates/README +4 -0
- data/app_generators/merb_plugin/templates/Rakefile +35 -0
- data/app_generators/merb_plugin/templates/TODO +5 -0
- data/app_generators/merb_plugin/templates/merbtasks.rb +6 -0
- data/app_generators/merb_plugin/templates/sampleplugin.rb +10 -0
- data/app_generators/merb_plugin/templates/sampleplugin_spec.rb +7 -0
- data/app_generators/merb_plugin/templates/spec_helper.rb +2 -0
- data/bin/merb +1 -1
- data/lib/autotest/discover.rb +3 -0
- data/lib/autotest/merb_rspec.rb +79 -0
- data/lib/merb.rb +72 -93
- data/lib/merb/{merb_abstract_controller.rb → abstract_controller.rb} +28 -5
- data/lib/merb/caching/action_cache.rb +65 -29
- data/lib/merb/caching/fragment_cache.rb +9 -4
- data/lib/merb/caching/store/file_cache.rb +22 -14
- data/lib/merb/caching/store/memory_cache.rb +26 -8
- data/lib/merb/{merb_constants.rb → constants.rb} +9 -7
- data/lib/merb/controller.rb +178 -0
- data/lib/merb/core_ext.rb +13 -11
- data/lib/merb/core_ext/array.rb +0 -0
- data/lib/merb/core_ext/{merb_class.rb → class.rb} +0 -0
- data/lib/merb/core_ext/{merb_enumerable.rb → enumerable.rb} +0 -0
- data/lib/merb/core_ext/get_args.rb +52 -0
- data/lib/merb/core_ext/{merb_hash.rb → hash.rb} +40 -11
- data/lib/merb/core_ext/{merb_inflections.rb → inflections.rb} +0 -0
- data/lib/merb/core_ext/{merb_inflector.rb → inflector.rb} +1 -1
- data/lib/merb/core_ext/{merb_kernel.rb → kernel.rb} +56 -3
- data/lib/merb/core_ext/mash.rb +88 -0
- data/lib/merb/core_ext/{merb_module.rb → module.rb} +0 -0
- data/lib/merb/core_ext/{merb_numeric.rb → numeric.rb} +0 -0
- data/lib/merb/core_ext/{merb_object.rb → object.rb} +10 -47
- data/lib/merb/core_ext/string.rb +56 -0
- data/lib/merb/core_ext/{merb_symbol.rb → symbol.rb} +0 -0
- data/lib/merb/dispatcher.rb +109 -0
- data/lib/merb/{merb_drb_server.rb → drb_server.rb} +0 -0
- data/lib/merb/erubis_ext.rb +10 -0
- data/lib/merb/exceptions.rb +173 -0
- data/lib/merb/generators/merb_app/merb_app.rb +5 -25
- data/lib/merb/generators/merb_generator_helpers.rb +317 -0
- data/lib/merb/generators/merb_plugin.rb +19 -0
- data/lib/merb/logger.rb +65 -0
- data/lib/merb/{merb_mail_controller.rb → mail_controller.rb} +102 -49
- data/lib/merb/{merb_mailer.rb → mailer.rb} +31 -27
- data/lib/merb/mixins/{basic_authentication_mixin.rb → basic_authentication.rb} +3 -3
- data/lib/merb/mixins/{controller_mixin.rb → controller.rb} +131 -112
- data/lib/merb/mixins/{erubis_capture_mixin.rb → erubis_capture.rb} +12 -21
- data/lib/merb/mixins/{form_control_mixin.rb → form_control.rb} +6 -12
- data/lib/merb/mixins/render.rb +401 -0
- data/lib/merb/mixins/responder.rb +378 -0
- data/lib/merb/mixins/{view_context_mixin.rb → view_context.rb} +65 -10
- data/lib/merb/mixins/web_controller.rb +29 -0
- data/lib/merb/{merb_handler.rb → mongrel_handler.rb} +59 -38
- data/lib/merb/part_controller.rb +19 -0
- data/lib/merb/plugins.rb +16 -0
- data/lib/merb/rack_adapter.rb +37 -0
- data/lib/merb/request.rb +421 -0
- data/lib/merb/router.rb +576 -0
- data/lib/merb/{merb_server.rb → server.rb} +275 -71
- data/lib/merb/session.rb +10 -10
- data/lib/merb/session/cookie_store.rb +125 -0
- data/lib/merb/session/{merb_mem_cache_session.rb → mem_cache_session.rb} +22 -9
- data/lib/merb/session/{merb_memory_session.rb → memory_session.rb} +15 -11
- data/lib/merb/template.rb +35 -8
- data/lib/merb/template/erubis.rb +16 -10
- data/lib/merb/template/haml.rb +33 -20
- data/lib/merb/template/markaby.rb +16 -14
- data/lib/merb/template/xml_builder.rb +8 -4
- data/lib/merb/test/{merb_fake_request.rb → fake_request.rb} +11 -5
- data/lib/merb/test/helper.rb +31 -0
- data/lib/merb/test/hpricot.rb +136 -0
- data/lib/merb/test/{merb_multipart.rb → multipart.rb} +1 -1
- data/lib/merb/test/rspec.rb +93 -0
- data/lib/merb/{merb_upload_handler.rb → upload_handler.rb} +5 -6
- data/lib/merb/{merb_upload_progress.rb → upload_progress.rb} +1 -1
- data/lib/merb/{merb_view_context.rb → view_context.rb} +27 -42
- data/lib/{merb_tasks.rb → tasks.rb} +0 -0
- data/lib/tasks/merb.rake +21 -11
- data/merb_default_generators/model/USAGE +0 -0
- data/merb_default_generators/model/model_generator.rb +16 -0
- data/merb_default_generators/model/templates/new_model_template.erb +5 -0
- data/merb_default_generators/resource_controller/USAGE +0 -0
- data/merb_default_generators/resource_controller/resource_controller_generator.rb +26 -0
- data/merb_default_generators/resource_controller/templates/controller.rb +30 -0
- data/merb_default_generators/resource_controller/templates/edit.html.erb +1 -0
- data/merb_default_generators/resource_controller/templates/helper.rb +5 -0
- data/merb_default_generators/resource_controller/templates/index.html.erb +1 -0
- data/merb_default_generators/resource_controller/templates/new.html.erb +1 -0
- data/merb_default_generators/resource_controller/templates/show.html.erb +1 -0
- data/merb_generators/controller/USAGE +5 -0
- data/merb_generators/controller/controller_generator.rb +16 -0
- data/merb_generators/controller/templates/controller.rb +8 -0
- data/merb_generators/controller/templates/helper.rb +5 -0
- data/merb_generators/controller/templates/index.html.erb +3 -0
- data/merb_generators/resource/USAGE +0 -0
- data/merb_generators/resource/resource_generator.rb +60 -0
- data/rspec_generators/merb_controller_test/merb_controller_test_generator.rb +67 -0
- data/rspec_generators/merb_controller_test/templates/controller_spec.rb +8 -0
- data/rspec_generators/merb_controller_test/templates/edit_spec.rb +12 -0
- data/rspec_generators/merb_controller_test/templates/helper_spec.rb +5 -0
- data/rspec_generators/merb_controller_test/templates/index_spec.rb +12 -0
- data/rspec_generators/merb_controller_test/templates/new_spec.rb +12 -0
- data/rspec_generators/merb_controller_test/templates/show_spec.rb +5 -0
- data/rspec_generators/merb_model_test/merb_model_test_generator.rb +26 -0
- data/rspec_generators/merb_model_test/templates/model_spec_template.erb +7 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/test_unit_generators/merb_controller_test/merb_controller_test_generator.rb +53 -0
- data/test_unit_generators/merb_controller_test/templates/functional_test.rb +17 -0
- data/test_unit_generators/merb_controller_test/templates/helper_test.rb +9 -0
- data/test_unit_generators/merb_model_test/merb_model_test_generator.rb +29 -0
- data/test_unit_generators/merb_model_test/templates/model_test_unit_template.erb +9 -0
- metadata +172 -94
- data/examples/README_EXAMPLES +0 -10
- data/examples/skeleton/Rakefile +0 -68
- data/examples/skeleton/dist/app/views/layout/application.herb +0 -12
- data/examples/skeleton/dist/conf/database.yml +0 -23
- data/examples/skeleton/dist/conf/merb.yml +0 -57
- data/examples/skeleton/dist/conf/merb_init.rb +0 -24
- data/examples/skeleton/dist/conf/router.rb +0 -22
- data/examples/skeleton/dist/conf/upload.conf +0 -5
- data/examples/skeleton/dist/schema/migrations/001_add_sessions_table.rb +0 -14
- data/examples/skeleton/script/new_migration +0 -21
- data/lib/merb/core_ext/merb_string.rb +0 -18
- data/lib/merb/merb_controller.rb +0 -206
- data/lib/merb/merb_dispatcher.rb +0 -87
- data/lib/merb/merb_exceptions.rb +0 -319
- data/lib/merb/merb_part_controller.rb +0 -42
- data/lib/merb/merb_plugins.rb +0 -293
- data/lib/merb/merb_request.rb +0 -165
- data/lib/merb/merb_router.rb +0 -309
- data/lib/merb/merb_yaml_store.rb +0 -31
- data/lib/merb/mixins/render_mixin.rb +0 -283
- data/lib/merb/mixins/responder_mixin.rb +0 -159
- data/lib/merb/session/merb_ar_session.rb +0 -131
- data/lib/merb/vendor/paginator/README.txt +0 -84
- data/lib/merb/vendor/paginator/paginator.rb +0 -124
- 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
|
-
|
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
|
-
|
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.
|
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.
|
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.
|
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.
|
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.
|
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
|
-
|
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
|