merb 0.3.4 → 0.3.7

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 (62) hide show
  1. data/README +206 -197
  2. data/Rakefile +12 -21
  3. data/bin/merb +1 -1
  4. data/examples/skeleton/Rakefile +6 -20
  5. data/examples/skeleton/dist/app/mailers/layout/application.erb +1 -0
  6. data/examples/skeleton/dist/conf/database.yml +23 -0
  7. data/examples/skeleton/dist/conf/environments/development.rb +1 -0
  8. data/examples/skeleton/dist/conf/environments/production.rb +1 -0
  9. data/examples/skeleton/dist/conf/environments/test.rb +1 -0
  10. data/examples/skeleton/dist/conf/merb.yml +32 -28
  11. data/examples/skeleton/dist/conf/merb_init.rb +16 -13
  12. data/examples/skeleton/dist/conf/router.rb +9 -9
  13. data/examples/skeleton/dist/schema/migrations/001_add_sessions_table.rb +2 -2
  14. data/lib/merb.rb +23 -18
  15. data/lib/merb/caching/fragment_cache.rb +3 -7
  16. data/lib/merb/caching/store/memcache.rb +20 -0
  17. data/lib/merb/core_ext/merb_array.rb +0 -0
  18. data/lib/merb/core_ext/merb_class.rb +44 -4
  19. data/lib/merb/core_ext/merb_enumerable.rb +43 -1
  20. data/lib/merb/core_ext/merb_hash.rb +200 -122
  21. data/lib/merb/core_ext/merb_kernel.rb +2 -0
  22. data/lib/merb/core_ext/merb_module.rb +41 -0
  23. data/lib/merb/core_ext/merb_numeric.rb +57 -5
  24. data/lib/merb/core_ext/merb_object.rb +172 -6
  25. data/lib/merb/generators/merb_app/merb_app.rb +15 -9
  26. data/lib/merb/merb_abstract_controller.rb +193 -0
  27. data/lib/merb/merb_constants.rb +26 -1
  28. data/lib/merb/merb_controller.rb +143 -234
  29. data/lib/merb/merb_dispatcher.rb +28 -20
  30. data/lib/merb/merb_drb_server.rb +2 -3
  31. data/lib/merb/merb_exceptions.rb +194 -49
  32. data/lib/merb/merb_handler.rb +34 -26
  33. data/lib/merb/merb_mail_controller.rb +200 -0
  34. data/lib/merb/merb_mailer.rb +33 -13
  35. data/lib/merb/merb_part_controller.rb +42 -0
  36. data/lib/merb/merb_plugins.rb +293 -0
  37. data/lib/merb/merb_request.rb +6 -4
  38. data/lib/merb/merb_router.rb +99 -65
  39. data/lib/merb/merb_server.rb +65 -21
  40. data/lib/merb/merb_upload_handler.rb +2 -1
  41. data/lib/merb/merb_view_context.rb +36 -15
  42. data/lib/merb/mixins/basic_authentication_mixin.rb +5 -5
  43. data/lib/merb/mixins/controller_mixin.rb +67 -28
  44. data/lib/merb/mixins/erubis_capture_mixin.rb +1 -8
  45. data/lib/merb/mixins/form_control_mixin.rb +280 -42
  46. data/lib/merb/mixins/render_mixin.rb +127 -45
  47. data/lib/merb/mixins/responder_mixin.rb +5 -7
  48. data/lib/merb/mixins/view_context_mixin.rb +260 -94
  49. data/lib/merb/session.rb +23 -0
  50. data/lib/merb/session/merb_ar_session.rb +28 -16
  51. data/lib/merb/session/merb_mem_cache_session.rb +108 -0
  52. data/lib/merb/session/merb_memory_session.rb +65 -20
  53. data/lib/merb/template/erubis.rb +22 -13
  54. data/lib/merb/template/haml.rb +5 -16
  55. data/lib/merb/template/markaby.rb +5 -3
  56. data/lib/merb/template/xml_builder.rb +17 -5
  57. data/lib/merb/test/merb_fake_request.rb +63 -0
  58. data/lib/merb/test/merb_multipart.rb +58 -0
  59. data/lib/tasks/db.rake +2 -0
  60. data/lib/tasks/merb.rake +20 -8
  61. metadata +24 -25
  62. data/examples/skeleton.tar +0 -0
@@ -1,57 +1,103 @@
1
1
  module Merb
2
2
 
3
3
  module RenderMixin
4
-
4
+
5
+ def self.included(base)
6
+ base.class_eval {
7
+ class_inheritable_accessor :_template_root,
8
+ :_layout
9
+
10
+ @@template_extensions = { }
11
+ self._layout = :application
12
+ self._template_root = File.expand_path(MERB_VIEW_ROOT)
13
+ # This method is called by templating-engines to register themselves with
14
+ # a list of extensions that will be looked up on #render of an action.
15
+ def self.register_engine(engine, *extensions)
16
+ [extensions].flatten.uniq.each do |ext|
17
+ @@template_extensions[ext] = engine
18
+ end
19
+ end
20
+
21
+ }
22
+ end
23
+
5
24
  # universal render method. Template handlers are registered
6
25
  # by template extension. So you can use the same render method
7
26
  # for any kind of template that implements an adapter module.
8
- # out of the box Merb support Erubis, Markaby and Builder templates
27
+ # out of the box Merb support Erubis, Haml, Markaby and Builder templates
9
28
  #
10
29
  # Erubis template ext: .herb .jerb .erb
11
30
  # Markaby template ext: .mab
12
31
  # Builder template ext: .rxml .builder .xerb
32
+ # Haml template ext: .haml
13
33
  #
14
34
  # Examples:
15
35
  #
16
- # render
17
- # looks for views/controllername/actionname.* and renders
36
+ # render
37
+ #
38
+ # Looks for views/controllername/actionname.* and renders
18
39
  # the template with the proper engine based on its file extension.
19
40
  #
20
- # render :layout => :none
21
- # renders the current template with no layout. XMl Builder templates
41
+ # render :layout => :none
42
+ #
43
+ # Renders the current template with no layout. XMl Builder templates
22
44
  # are exempt from layout by default.
23
45
  #
24
- # render :action => 'foo'
25
- # renders views/controllername/foo.*
46
+ # render :action => 'foo'
47
+ #
48
+ # Renders views/controllername/foo.*
49
+ #
50
+ # render :nothing => 200
26
51
  #
27
- # render :nothing => 200
28
- # renders nothing with a status of 200
52
+ # Renders nothing with a status of 200
29
53
  #
30
- # render :template => 'shared/message'
31
- # renders views/shared/message
54
+ # render :template => 'shared/message'
32
55
  #
33
- # render :js => "$('some-div').toggle();"
34
- # if the right hand side of :js => is a string then the proper
56
+ # Renders views/shared/message
57
+ #
58
+ # render :js => "$('some-div').toggle();"
59
+ #
60
+ # If the right hand side of :js => is a string then the proper
35
61
  # javascript headers will be set and the string will be returned
36
62
  # verbatim as js.
37
63
  #
38
- # render :js => :spinner
39
- # when the rhs of :js => is a Symbol, it will be used as the
64
+ # render :js => :spinner
65
+ #
66
+ # When the rhs of :js => is a Symbol, it will be used as the
40
67
  # action/template name so: views/controllername/spinner.jerb
41
68
  # will be rendered as javascript
42
69
  #
43
- # render :js => true
44
- # this will just look for the current controller/action tenmplate
70
+ # render :js => true
71
+ #
72
+ # This will just look for the current controller/action template
45
73
  # with the .jerb extension and render it as javascript
46
74
  #
47
- # render :xml => @posts.to_xml
48
- # render :xml => "<foo><bar>Hi!</bar></foo>"
49
- # this will set the appropriate xml headers and render the rhs
75
+ # XML can be rendered with the same options as Javascript.
76
+ #
77
+ # render :xml => @posts.to_xml
78
+ # render :xml => "<foo><bar>Hi!</bar></foo>"
79
+ #
80
+ # This will set the appropriate xml headers and render the rhs
50
81
  # of :xml => as a string. SO you can pass any xml string to this
51
82
  # to be rendered.
52
83
  #
84
+ # render :xml => :hello
85
+ #
86
+ # Renders the hello.xrb template for the current controller.
87
+ #
88
+ # render :xml => true
89
+ # render :xml => true, :action => "buffalo"
90
+ #
91
+ # Renders the buffalo.xerb template for the current controller.
92
+ #
53
93
  def render(opts={}, &blk)
54
- action = opts[:action] || params[:action]
94
+
95
+ action = if kind_of?(Merb::ControllerExceptions::Base)
96
+ self.class.name.snake_case.split('::').last
97
+ else
98
+ opts[:action] || params[:action]
99
+ end
100
+
55
101
  opts[:layout] ||= _layout
56
102
 
57
103
  case
@@ -70,23 +116,32 @@ module Merb
70
116
  when js = opts[:js]
71
117
  headers['Content-Type'] = "text/javascript"
72
118
  opts[:layout] = :none
73
- if String === js
119
+ case js
120
+ when String
74
121
  return js
75
- elsif Symbol === js
122
+ when Symbol
76
123
  template = find_template(:action => js, :ext => 'jerb')
77
- else
124
+ else
78
125
  template = find_template(:action => action, :ext => 'jerb')
79
126
  end
80
127
  when xml = opts[:xml]
81
128
  headers['Content-Type'] = 'application/xml'
82
129
  headers['Encoding'] = 'UTF-8'
83
- return xml
130
+ # TODO This is the same as the js logic above. Can it be refactored?
131
+ case xml
132
+ when String
133
+ return xml
134
+ when Symbol
135
+ template = find_template(:action => xml, :ext => 'rxml,xerb,builder')
136
+ else
137
+ template = find_template(:action => action, :ext => 'rxml,xerb,builder')
138
+ end
84
139
  when template = opts[:template]
85
140
  template = find_template(:template => template)
86
141
  else
87
142
  template = find_template(:action => action)
88
143
  end
89
-
144
+
90
145
  engine = engine_for(template)
91
146
  options = {
92
147
  :file => template,
@@ -101,16 +156,7 @@ module Merb
101
156
  end
102
157
  end
103
158
 
104
- # this returns a ViewContext object populated with all
105
- # the instance variables in your controller. This is used
106
- # as the view context object for the Erubis templates.
107
- def cached_view_context
108
- @_view_context_cache ||= ViewContext.new(self)
109
- end
110
-
111
- def clean_view_context
112
- ViewContext.new(self)
113
- end
159
+
114
160
 
115
161
  # does a render with no layout. Also sets the
116
162
  # content type header to text/javascript. Use
@@ -124,10 +170,14 @@ module Merb
124
170
  # to 200. does send one ' ' space char, this is for
125
171
  # safari and flash uploaders to work.
126
172
  def render_nothing(status=200)
127
- @status = status
173
+ @_status = status
128
174
  return " "
129
175
  end
130
176
 
177
+ def set_status(status)
178
+ @_status = status
179
+ end
180
+
131
181
  def render_no_layout(opts={})
132
182
  render opts.update({:layout => :none})
133
183
  end
@@ -144,14 +194,32 @@ module Merb
144
194
  def partial(template, locals={})
145
195
  render :partial => template, :locals => locals
146
196
  end
197
+
198
+ # +catch_content+ catches the thrown content from another template
199
+ # So when you throw_content(:foo) {...} you can catch_content :foo
200
+ # in another view or the layout.
201
+ def catch_content(name)
202
+ cached_view_context.instance_variable_get("@_#{name}_content")
203
+ end
147
204
 
148
205
  private
149
206
 
207
+ # this returns a ViewContext object populated with all
208
+ # the instance variables in your controller. This is used
209
+ # as the view context object for the Erubis templates.
210
+ def cached_view_context
211
+ @_view_context_cache ||= ViewContext.new(self)
212
+ end
213
+
214
+ def clean_view_context
215
+ ViewContext.new(self)
216
+ end
217
+
150
218
  def wrap_layout(content, opts={})
151
219
  if opts[:layout] != :application
152
220
  layout_choice = find_template(:layout => opts[:layout])
153
221
  else
154
- if name = find_template(:layout => self.class.name.snake_case)
222
+ if name = find_template(:layout => self.class.name.snake_case.split('::').join('/'))
155
223
  layout_choice = name
156
224
  else
157
225
  layout_choice = find_template(:layout => :application)
@@ -172,14 +240,17 @@ module Merb
172
240
  def find_template(opts={})
173
241
  if template = opts[:template]
174
242
  path = _template_root / template
243
+ elsif opts[:action] and kind_of?(Merb::ControllerExceptions::Base)
244
+ path = _template_root / 'exceptions' / opts[:action]
175
245
  elsif action = opts[:action]
176
- path = _template_root / self.class.name.snake_case / action
246
+ segment = self.class.name.snake_case.split('::').join('/')
247
+ path = _template_root / segment / action
177
248
  elsif _layout = opts[:layout]
178
- path = _layout_root / _layout
249
+ path = _template_root / 'layout' / _layout
179
250
  else
180
251
  raise "called find_template without an :action or :layout"
181
- end
182
- extensions = [_template_extensions.keys].flatten.uniq
252
+ end
253
+ extensions = [@@template_extensions.keys].flatten.uniq
183
254
  glob = "#{path}.{#{opts[:ext] || extensions.join(',')}}"
184
255
  Dir[glob].first
185
256
  end
@@ -190,12 +261,23 @@ module Merb
190
261
  template = t.pop
191
262
  path = _template_root / t.join('/') / "_#{template}"
192
263
  else
193
- path = _template_root / self.class.name.snake_case / "_#{template}"
264
+ segment = self.class.name.snake_case.split('::').join('/')
265
+ path = _template_root / segment / "_#{template}"
194
266
  end
195
- extensions = [_template_extensions.keys].flatten.uniq
267
+ extensions = [@@template_extensions.keys].flatten.uniq
196
268
  glob = "#{path}.{#{opts[:ext] || extensions.join(',')}}"
197
269
  Dir[glob].first
198
270
  end
199
271
 
272
+ # lookup the template_extensions for the extname of the filename
273
+ # you pass. Answers with the engine that matches the extension, Template::Erubis
274
+ # is used if none matches.
275
+ def engine_for(file)
276
+ extension = File.extname(file)[1..-1]
277
+ @@template_extensions[extension]
278
+ rescue
279
+ ::Merb::Template::Erubis
280
+ end
281
+
200
282
  end
201
283
  end
@@ -12,10 +12,10 @@ module Merb
12
12
  module ResponderMixin
13
13
 
14
14
  def respond_to(&block)
15
- responder = Rest::Responder.new(@env['HTTP_ACCEPT'], params)
15
+ responder = Rest::Responder.new(request.env['HTTP_ACCEPT'], params)
16
16
  block.call(responder)
17
17
  responder.respond(headers)
18
- @status = responder.status if responder.status
18
+ @_status = responder.status if responder.status
19
19
  responder.body
20
20
  end
21
21
 
@@ -57,9 +57,7 @@ module Merb
57
57
  headers['Content-Type'] = mime_type.super_range
58
58
  @body = @stack[mime_type.to_sym].call
59
59
  else
60
- headers['Content-Type'] = nil
61
- @status = 406
62
- @body = nil
60
+ raise ControllerExceptions::NotAcceptable
63
61
  end
64
62
  end
65
63
 
@@ -84,7 +82,7 @@ module Merb
84
82
  private
85
83
 
86
84
  def negotiate_content
87
- if @params['format']
85
+ if @params[:format]
88
86
  negotiate_by_format
89
87
  elsif (@stack.keys & @accepts.map(&:to_sym)).size > 0
90
88
  negotiate_by_accept_header
@@ -92,7 +90,7 @@ module Merb
92
90
  end
93
91
 
94
92
  def negotiate_by_format
95
- format = @params['format'].to_sym
93
+ format = @params[:format].to_sym
96
94
  if @stack[format]
97
95
  if @accepts.map(&:to_sym).include?(format)
98
96
  @accepts.detect{|a| a.to_sym == format }
@@ -1,39 +1,96 @@
1
1
  module Merb
2
+ # The ViewContextMixin module provides a number of helper methods to views for
3
+ # linking to assets and other pages, dealing with JavaScript, and caching.
2
4
  module ViewContextMixin
3
-
5
+ # :section: Accessing Assets
6
+ # Merb provides views with convenience methods for links images and other assets.
7
+
8
+ # Creates a link for the URL given in +url+ with the text in +name+; HTML options are given in the +opts+
9
+ # hash.
10
+ #
11
+ # ==== Options
12
+ # The +opts+ hash is used to set HTML attributes on the tag.
13
+ #
14
+ # ==== Examples
15
+ # link_to("The Merb home page", "http://www.merbivore.com/")
16
+ # # => <a href="http://www.merbivore.com/">The Merb home page</a>
17
+ #
18
+ # link_to("The Ruby home page", "http://www.ruby-lang.org", {'class' => 'special', 'target' => 'blank'})
19
+ # # => <a href="http://www.ruby-lang.org" class="special" target="blank">The Ruby home page</a>
20
+ #
21
+ # link_to p.title, "/blog/show/#{p.id}"
22
+ # # => <a href="blog/show/13">The Entry Title</a>
23
+ #
4
24
  def link_to(name, url='', opts={})
5
25
  %{<a href="#{url}" #{opts.map{|k,v| "#{k}=\"#{v}\""}.join(' ')}>#{name}</a>}
6
26
  end
7
27
 
8
- # escape text for javascript.
28
+ # Creates an image tag with the +src+ attribute set to the +img+ argument. The path
29
+ # prefix defaults to <tt>/images/</tt>. The path prefix can be overriden by setting a +:path+
30
+ # parameter in the +opts+ hash. The rest of the +opts+ hash sets HTML attributes.
31
+ #
32
+ # ==== Options
33
+ # path:: Sets the path prefix for the image (defaults to +/images/+)
34
+ #
35
+ # All other options in +opts+ set HTML attributes on the tag.
36
+ #
37
+ # ==== Examples
38
+ # image_tag('foo.gif')
39
+ # # => <img src='/images/foo.gif' />
40
+ #
41
+ # image_tag('foo.gif', :class => 'bar')
42
+ # # => <img src='/images/foo.gif' class='bar' />
43
+ #
44
+ # image_tag('foo.gif', :path => '/files/')
45
+ # # => <img src='/files/foo.gif' />
46
+ #
47
+ def image_tag(img, opts={})
48
+ opts[:path] ||= '/images/'
49
+ %{<img src="#{opts.delete(:path) + img}" #{opts.map{|k,v| "#{k}=\"#{v}\""}.join(' ')} />}
50
+ end
51
+
52
+ # :section: JavaScript related functions
53
+ #
54
+
55
+ # Escapes text for use in JavaScript, replacing unsafe strings with their
56
+ # escaped equivalent.
57
+ #
58
+ # ==== Examples
59
+ # escape_js("'Lorem ipsum!' -- Some guy")
60
+ # # => "\\'Lorem ipsum!\\' -- Some guy"
61
+ #
62
+ # escape_js("Please keep text\nlines as skinny\nas possible.")
63
+ # # => "Please keep text\\nlines as skinny\\nas possible."
9
64
  def escape_js(javascript)
10
65
  (javascript || '').gsub('\\','\0\0').gsub(/\r\n|\n|\r/, "\\n").gsub(/["']/) { |m| "\\#{m}" }
11
66
  end
12
67
 
13
- # creates an <a> tag with with an onclick containing
14
- # a js function
15
- # link_to_function('click me', "alert('hi!')")
68
+ # Creates a link tag with the text in +name+ and the <tt>onClick</tt> handler set to a JavaScript
69
+ # string in +function+.
70
+ #
71
+ # ==== Examples
72
+ # link_to_function('Click me', "alert('hi!')")
73
+ # # => <a href="#" onclick="alert('hi!'); return false;">Click me</a>
74
+ #
75
+ # link_to_function('Add to cart', "item_total += 1; alert('Item added!');")
76
+ # # => <a href="#" onclick="item_total += 1; alert('Item added!'); return false;">Add to cart</a>
77
+ #
16
78
  def link_to_function(name, function)
17
79
  %{<a href="#" onclick="#{function}; return false;">#{name}</a>}
18
80
  end
19
81
 
20
- # creates an <img> tag
21
- # defaults to a src path prefix of /images/
22
- #
23
- # image_tag('foo.gif') => <img src='/images/foo.gif' />
24
- # image_tag('foo.gif', :class => 'bar') => <img src='/images/foo.gif' class='bar' />
82
+ # The js method simply calls +to_json+ on an object in +data+; if the object
83
+ # does not implement a +to_json+ method, then it calls +to_json+ on
84
+ # +data.inspect+.
25
85
  #
26
- # you can override the default path by sending a :path parameter in the opts
86
+ # ==== Examples
87
+ # js({'user' => 'Lewis', 'page' => 'home'})
88
+ # # => "{\"user\":\"Lewis\",\"page\":\"home\"}"
27
89
  #
28
- # image_tag('foo.gif', :path => '/files/') => <img src='/files/foo.gif' />
90
+ # my_array = [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
91
+ # js(my_array)
92
+ # # => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
29
93
  #
30
- def image_tag(img, opts={})
31
- opts[:path] ||= '/images/'
32
- %{<img src="#{opts.delete(:path) + img}" #{opts.map{|k,v| "#{k}=\"#{v}\""}.join(' ')} />}
33
- end
34
-
35
- # calls .to_json on data.
36
- # so it can be faster than escape_js
37
94
  def js(data)
38
95
  if data.respond_to? :to_json
39
96
  data.to_json
@@ -41,117 +98,226 @@ module Merb
41
98
  data.inspect.to_json
42
99
  end
43
100
  end
44
-
45
-
46
- # Helper method to cache a fragment.
47
- # <h1>Article list</h1>
101
+
102
+ # :section: External JavaScript and Stylesheets
48
103
  #
49
- # <% cache(:article_list) do %>
50
- # <ul>
51
- # <% @articles.each do |a| %>
52
- # <li><%= a.title %></li>
53
- # <% end %>
54
- # </ul>
55
- # <% end %>
56
-
57
- def cache(name, &block)
58
- return block.call unless caching_enabled?
59
- buffer = eval("_buf", block.binding)
60
- if fragment = ::Merb::Caching::Fragment.get(name)
61
- buffer.concat(fragment)
62
- else
63
- pos = buffer.length
64
- block.call
65
- ::Merb::Caching::Fragment.put(name, buffer[pos..-1])
66
- end
67
- end
68
-
69
- # Requiring javascripts and stylesheets:
70
- # you can use require_js(:prototype) or require_css(:shinystyles)
71
- # from any view or layout and the scripts will only be included once
72
- # in the head of the final page. In the head of your layout you will
73
- # need to add these two tags:
74
- #
75
- # <%= include_required_js %>
76
- # <%= include_required_css %>
77
- #
78
- # --app/views/layouts/application.rhtml
79
- #
80
- # <html>
81
- # <head>
82
- # <%= include_required_js %>
83
- # <%= include_required_css %>
84
- # </head>
85
- # <body>
86
- # <%= catch_content :layout %>
87
- # </body>
88
- # </html>
89
- #
90
- # --app/views/whatever/index.rhtml
104
+ # You can use require_js(:prototype) or require_css(:shinystyles)
105
+ # from any view or layout, and the scripts will only be included once
106
+ # in the head of the final page. To get this effect, the head of your layout you will
107
+ # need to include a call to include_required_js and include_required_css.
108
+ #
109
+ # ==== Examples
110
+ # # File: app/views/layouts/application.herb
91
111
  #
92
- # <%= partial(:part1) %>
93
- # <%= partial(:part2) %>
112
+ # <html>
113
+ # <head>
114
+ # <%= include_required_js %>
115
+ # <%= include_required_css %>
116
+ # </head>
117
+ # <body>
118
+ # <%= catch_content :layout %>
119
+ # </body>
120
+ # </html>
94
121
  #
95
- # --app/views/whatever/_part1.rhtml
122
+ # # File: app/views/whatever/_part1.herb
96
123
  #
97
- # <% require_js 'this' -%>
98
- # <% require_css 'that', 'another_one' -%>
124
+ # <% require_js 'this' -%>
125
+ # <% require_css 'that', 'another_one' -%>
99
126
  #
100
- # --app/views/whatever/_part2.rhtml
127
+ # # File: app/views/whatever/_part2.herb
101
128
  #
102
- # <% require_js 'this', 'something_else' -%>
103
- # <% require_css 'that' -%>
104
-
129
+ # <% require_js 'this', 'something_else' -%>
130
+ # <% require_css 'that' -%>
131
+ #
132
+ # # File: app/views/whatever/index.herb
133
+ #
134
+ # <%= partial(:part1) %>
135
+ # <%= partial(:part2) %>
136
+ #
137
+ # # Will generate the following in the final page...
138
+ # <html>
139
+ # <head>
140
+ # <script src="/javascripts/this.js" type="text/javascript"></script>
141
+ # <script src="/javascripts/something_else.js" type="text/javascript"></script>
142
+ # <link href="/stylesheets/that.css" media="all" rel="Stylesheet" type="text/css"/>
143
+ # <link href="/stylesheets/another_one.css" media="all" rel="Stylesheet" type="text/css"/>
144
+ # </head>
145
+ # .
146
+ # .
147
+ # .
148
+ # </html>
149
+ #
150
+ # See each method's documentation for more information.
105
151
 
106
- # require_js(:myjs) can be used to require any javascript
107
- # file anywhere in your templates. It will only include the
108
- # javascript tag once in the header
152
+ # The require_js method can be used to require any JavaScript
153
+ # file anywhere in your templates. Regardless of how many times
154
+ # a single script is included with require_js, Merb will only include
155
+ # it once in the header.
156
+ #
157
+ # ==== Examples
158
+ # <% require_js 'jquery' %>
159
+ # # A subsequent call to include_required_js will render...
160
+ # # => <script src="/javascripts/jquery.js" type="text/javascript"></script>
161
+ #
162
+ # <% require_js 'jquery', 'effects' %>
163
+ # # A subsequent call to include_required_js will render...
164
+ # # => <script src="/javascripts/jquery.js" type="text/javascript"></script>
165
+ # # <script src="/javascripts/effects.js" type="text/javascript"></script>
166
+ #
109
167
  def require_js(*js)
110
168
  @required_js ||= []
111
169
  @required_js |= js
112
170
  end
113
171
 
114
- # require_css(:mystyles) can be used to require any javascript
115
- # file anywhere in your templates. It will only include the
116
- # javascript tag once in the header
172
+ # The require_ccs method can be used to require any CSS
173
+ # file anywhere in your templates. Regardless of how many times
174
+ # a single stylesheet is included with require_css, Merb will only include
175
+ # it once in the header.
176
+ #
177
+ # ==== Examples
178
+ # <% require_css('style') %>
179
+ # # A subsequent call to include_required_css will render...
180
+ # # => <link href="/stylesheets/style.css" media="all" rel="Stylesheet" type="text/css"/>
181
+ #
182
+ # <% require_css('style', 'ie-specific') %>
183
+ # # A subsequent call to include_required_css will render...
184
+ # # => <link href="/stylesheets/style.css" media="all" rel="Stylesheet" type="text/css"/>
185
+ # # <link href="/stylesheets/ie-specific.css" media="all" rel="Stylesheet" type="text/css"/>
186
+ #
117
187
  def require_css(*css)
118
188
  @required_css ||= []
119
189
  @required_css |= css
120
190
  end
121
191
 
122
- # this goes in the head of your layout if you will be using
123
- # require_js
192
+ # A method used in the layout of an application to create +<script>+ tags to include JavaScripts required in
193
+ # in templates and subtemplates using require_js.
194
+ #
195
+ # ==== Examples
196
+ # # my_action.herb has a call to require_js 'jquery'
197
+ # # File: layout/application.herb
198
+ # include_required_js
199
+ # # => <script src="/javascripts/jquery.js" type="text/javascript"></script>
200
+ #
201
+ # # my_action.herb has a call to require_js 'jquery', 'effects', 'validation'
202
+ # # File: layout/application.herb
203
+ # include_required_js
204
+ # # => <script src="/javascripts/jquery.js" type="text/javascript"></script>
205
+ # # <script src="/javascripts/effects.js" type="text/javascript"></script>
206
+ # # <script src="/javascripts/validation.js" type="text/javascript"></script>
207
+ #
124
208
  def include_required_js
125
209
  js_include_tag(*@required_js)
126
210
  end
127
211
 
128
- # this goes in the head of your layout if you will be using
129
- # require_css
212
+ # A method used in the layout of an application to create +<link>+ tags for CSS stylesheets required in
213
+ # in templates and subtemplates using require_css.
214
+ #
215
+ # ==== Examples
216
+ # # my_action.herb has a call to require_css 'style'
217
+ # # File: layout/application.herb
218
+ # include_required_css
219
+ # # => <link href="/stylesheets/style.css" media="all" rel="Stylesheet" type="text/css"/>
220
+ #
221
+ # # my_action.herb has a call to require_js 'style', 'ie-specific'
222
+ # # File: layout/application.herb
223
+ # include_required_css
224
+ # # => <link href="/stylesheets/style.css" media="all" rel="Stylesheet" type="text/css"/>
225
+ # # <link href="/stylesheets/ie-specific.css" media="all" rel="Stylesheet" type="text/css"/>
226
+ #
130
227
  def include_required_css
131
- css_link_tag(*@required_js)
228
+ css_link_tag(*@required_css)
132
229
  end
133
230
 
134
- # js_include_tag(:foo, :bar, :baz) will create a javascript
135
- # include tag for each script in the arguments. It will append
231
+ # The js_include_tag method will create a JavaScript
232
+ # +<include>+ tag for each script named in the arguments, appending
136
233
  # '.js' if it is left out of the call.
234
+ #
235
+ # ==== Examples
236
+ # js_include_tag 'jquery'
237
+ # # => <script src="/javascripts/jquery.js" type="text/javascript"></script>
238
+ #
239
+ # js_include_tag 'moofx.js', 'upload'
240
+ # # => <script src="/javascripts/moofx.js" type="text/javascript"></script>
241
+ # # <script src="/javascripts/upload.js" type="text/javascript"></script>
242
+ #
243
+ # js_include_tag :effects
244
+ # # => <script src="/javascripts/effects.js" type="text/javascript"></script>
245
+ #
246
+ # js_include_tag :jquery, :validation
247
+ # # => <script src="/javascripts/jquery.js" type="text/javascript"></script>
248
+ # # <script src="/javascripts/validation.js" type="text/javascript"></script>
249
+ #
137
250
  def js_include_tag(*scripts)
138
251
  return nil if scripts.empty?
139
- scripts.inject('') do |memo,script|
252
+ include_tag = ""
253
+ scripts.each do |script|
140
254
  script = script.to_s
141
- memo << %Q|<script src="/javascripts/#{script=~/\.js$/ ? script : script+'.js' }" type="text/javascript">//</script>\n|
255
+ include_tag << %Q|<script src="/javascripts/#{script=~/\.js$/ ? script : script+'.js' }" type="text/javascript">//</script>\n|
142
256
  end
257
+ include_tag
143
258
  end
144
259
 
145
- # css_include_tag(:foo, :bar, :baz) will create a stylesheet
146
- # link tag for each stylesheet in the arguments. It will append
260
+ # The css_include_tag method will create a CSS stylesheet
261
+ # +<link>+ tag for each stylesheet named in the arguments, appending
147
262
  # '.css' if it is left out of the call.
263
+ #
264
+ # ==== Examples
265
+ # css_include_tag 'style'
266
+ # # => <link href="/stylesheets/style.css" media="all" rel="Stylesheet" type="text/css"/>
267
+ #
268
+ # css_include_tag 'style.css', 'layout'
269
+ # # => <link href="/stylesheets/style.css" media="all" rel="Stylesheet" type="text/css"/>
270
+ # # <link href="/stylesheets/layout.css" media="all" rel="Stylesheet" type="text/css"/>
271
+ #
272
+ # css_include_tag :menu
273
+ # # => <link href="/stylesheets/menu.css" media="all" rel="Stylesheet" type="text/css"/>
274
+ #
275
+ # css_include_tag :style, :screen
276
+ # # => <link href="/stylesheets/style.css" media="all" rel="Stylesheet" type="text/css"/>
277
+ # # <link href="/stylesheets/screen.css" media="all" rel="Stylesheet" type="text/css"/>
278
+ #
148
279
  def css_include_tag(*scripts)
149
280
  return nil if scripts.empty?
150
- scripts.inject('') do |memo,script|
281
+ include_tag = ""
282
+ scripts.each do |script|
151
283
  script = script.to_s
152
- memo << %Q|<link href="/stylesheets/#{script=~/\.css$/ ? script : script+'.css' }" media="all" rel="Stylesheet" type="text/css"/>\n|
284
+ include_tag << %Q|<link href="/stylesheets/#{script=~/\.css$/ ? script : script+'.css' }" media="all" rel="Stylesheet" type="text/css"/>\n|
153
285
  end
286
+ include_tag
154
287
  end
155
288
 
289
+ # :section: Caching
290
+ # ViewContextMixin provides views with fragment caching facilities.
291
+
292
+ # The cache method is a simple helper method
293
+ # for caching template fragments. The value of the supplied
294
+ # block is stored in the cache and identified by the string
295
+ # in the +name+ argument.
296
+ #
297
+ # ==== Example
298
+ # <h1>Article list</h1>
299
+ #
300
+ # <% cache(:article_list) do %>
301
+ # <ul>
302
+ # <% @articles.each do |a| %>
303
+ # <li><%= a.title %></li>
304
+ # <% end %>
305
+ # </ul>
306
+ # <% end %>
307
+ #
308
+ # See the documentation for Merb::Caching::Fragment for more
309
+ # information.
310
+ #
311
+ def cache(name, &block)
312
+ return block.call unless caching_enabled?
313
+ buffer = eval("_buf", block.binding)
314
+ if fragment = ::Merb::Caching::Fragment.get(name)
315
+ buffer.concat(fragment)
316
+ else
317
+ pos = buffer.length
318
+ block.call
319
+ ::Merb::Caching::Fragment.put(name, buffer[pos..-1])
320
+ end
321
+ end
156
322
  end
157
- end
323
+ end