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.
- 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
|