merb 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. data/README +64 -80
  2. data/Rakefile +25 -12
  3. data/bin/merb +2 -223
  4. data/examples/README_EXAMPLES +10 -0
  5. data/examples/skeleton.tar +0 -0
  6. data/lib/merb.rb +48 -21
  7. data/lib/merb/core_ext.rb +12 -2
  8. data/lib/merb/core_ext/merb_class.rb +21 -21
  9. data/lib/merb/core_ext/merb_kernel.rb +60 -0
  10. data/lib/merb/core_ext/merb_object.rb +14 -0
  11. data/lib/merb/core_ext/merb_string.rb +3 -13
  12. data/lib/merb/generators/merb_app/merb_app.rb +33 -0
  13. data/lib/merb/merb_constants.rb +7 -0
  14. data/lib/merb/merb_controller.rb +31 -23
  15. data/lib/merb/merb_drb_server.rb +6 -60
  16. data/lib/merb/merb_exceptions.rb +162 -2
  17. data/lib/merb/merb_handler.rb +8 -19
  18. data/lib/merb/merb_mailer.rb +60 -0
  19. data/lib/merb/merb_router.rb +1 -1
  20. data/lib/merb/merb_server.rb +240 -0
  21. data/lib/merb/merb_upload_handler.rb +1 -1
  22. data/lib/merb/merb_view_context.rb +11 -6
  23. data/lib/merb/mixins/basic_authentication_mixin.rb +11 -13
  24. data/lib/merb/mixins/controller_mixin.rb +12 -6
  25. data/lib/merb/mixins/form_control_mixin.rb +94 -0
  26. data/lib/merb/mixins/render_mixin.rb +50 -24
  27. data/lib/merb/mixins/view_context_mixin.rb +122 -0
  28. data/lib/merb/session/merb_ar_session.rb +13 -14
  29. data/lib/merb/session/merb_memory_session.rb +105 -0
  30. metadata +13 -132
  31. data/examples/app_skeleton/Rakefile +0 -82
  32. data/examples/app_skeleton/dist/app/helpers/global_helper.rb +0 -6
  33. data/examples/app_skeleton/dist/conf/merb.yml +0 -11
  34. data/examples/app_skeleton/dist/conf/merb_init.rb +0 -16
  35. data/examples/app_skeleton/dist/conf/mup.conf +0 -5
  36. data/examples/app_skeleton/dist/conf/router.rb +0 -19
  37. data/examples/app_skeleton/scripts/merb_stop +0 -13
  38. data/examples/app_skeleton/scripts/new_migration +0 -21
  39. data/examples/app_skeleton/test/test_helper.rb +0 -1
  40. data/examples/sample_app/Rakefile +0 -82
  41. data/examples/sample_app/dist/app/controllers/files.rb +0 -31
  42. data/examples/sample_app/dist/app/controllers/posts.rb +0 -71
  43. data/examples/sample_app/dist/app/controllers/test.rb +0 -40
  44. data/examples/sample_app/dist/app/helpers/global_helper.rb +0 -7
  45. data/examples/sample_app/dist/app/helpers/posts_helper.rb +0 -4
  46. data/examples/sample_app/dist/app/models/comment.rb +0 -3
  47. data/examples/sample_app/dist/app/models/post.rb +0 -4
  48. data/examples/sample_app/dist/app/views/files/progress.jerb +0 -3
  49. data/examples/sample_app/dist/app/views/files/start.herb +0 -62
  50. data/examples/sample_app/dist/app/views/files/upload.herb +0 -6
  51. data/examples/sample_app/dist/app/views/layout/application.herb +0 -61
  52. data/examples/sample_app/dist/app/views/layout/foo.herb +0 -6
  53. data/examples/sample_app/dist/app/views/posts/_comments.herb +0 -11
  54. data/examples/sample_app/dist/app/views/posts/comment.jerb +0 -1
  55. data/examples/sample_app/dist/app/views/posts/list.herb +0 -5
  56. data/examples/sample_app/dist/app/views/posts/new.herb +0 -37
  57. data/examples/sample_app/dist/app/views/posts/show.herb +0 -37
  58. data/examples/sample_app/dist/app/views/posts/xml_test.xerb +0 -3
  59. data/examples/sample_app/dist/app/views/shared/_test.herb +0 -1
  60. data/examples/sample_app/dist/app/views/test/foo.herb +0 -2
  61. data/examples/sample_app/dist/app/views/test/hello.herb +0 -5
  62. data/examples/sample_app/dist/app/views/test/json.jerb +0 -1
  63. data/examples/sample_app/dist/conf/merb.yml +0 -11
  64. data/examples/sample_app/dist/conf/merb_init.rb +0 -24
  65. data/examples/sample_app/dist/conf/mup.conf +0 -5
  66. data/examples/sample_app/dist/conf/router.rb +0 -19
  67. data/examples/sample_app/dist/public/images/bg.jpg +0 -0
  68. data/examples/sample_app/dist/public/images/book.gif +0 -0
  69. data/examples/sample_app/dist/public/images/booksmall.gif +0 -0
  70. data/examples/sample_app/dist/public/images/greenright.jpg +0 -0
  71. data/examples/sample_app/dist/public/images/louiecon.gif +0 -0
  72. data/examples/sample_app/dist/public/images/menu.gif +0 -0
  73. data/examples/sample_app/dist/public/images/menuleft.gif +0 -0
  74. data/examples/sample_app/dist/public/images/menuright.gif +0 -0
  75. data/examples/sample_app/dist/public/images/mountain.jpg +0 -0
  76. data/examples/sample_app/dist/public/images/n3.jpg +0 -0
  77. data/examples/sample_app/dist/public/images/nautica.jpg +0 -0
  78. data/examples/sample_app/dist/public/javascripts/application.js +0 -0
  79. data/examples/sample_app/dist/public/javascripts/effects.js +0 -975
  80. data/examples/sample_app/dist/public/javascripts/mup.js +0 -113
  81. data/examples/sample_app/dist/public/javascripts/prototype.js +0 -2264
  82. data/examples/sample_app/dist/public/stylesheets/merb.css +0 -277
  83. data/examples/sample_app/dist/public/test.html +0 -5
  84. data/examples/sample_app/dist/schema/migrations/001_add_comments_to_posts.rb +0 -22
  85. data/examples/sample_app/dist/schema/migrations/002_add_sessions_table.rb +0 -14
  86. data/examples/sample_app/dist/schema/schema.rb +0 -28
  87. data/examples/sample_app/foo.txt +0 -0
  88. data/examples/sample_app/log/merb.4000.pid +0 -1
  89. data/examples/sample_app/script/merb_stop +0 -13
  90. data/examples/sample_app/script/new_migration +0 -21
  91. data/examples/sample_app/test/test_helper.rb +0 -1
  92. data/lib/merb/mixins/javascript_mixin.rb +0 -147
  93. data/lib/merb/session/merb_drb_session.rb +0 -65
  94. data/test/test_helper.rb +0 -1
  95. data/test/unit/route_matcher_test.rb +0 -46
@@ -10,7 +10,7 @@ class MerbUploadHandler < Mongrel::HttpHandler
10
10
  if options[:drb]
11
11
  require 'drb'
12
12
  DRb.start_service
13
- Mongrel.const_set :Uploads, DRbObject.new(nil, "druby://#{Merb::Server.config[:host]}:#{Merb::Server.config[:session]}").upload_progress
13
+ Mongrel.const_set :Uploads, DRbObject.new(nil, "druby://#{Merb::Server.host}:#{Merb::Server.drb_server_port}").upload_progress
14
14
  else
15
15
  require File.dirname(__FILE__)+'/merb_upload_progress'
16
16
  Mongrel.const_set :Uploads, Merb::UploadProgress.new
@@ -1,8 +1,13 @@
1
1
  require File.dirname(__FILE__)+'/mixins/erubis_capture_mixin'
2
- require File.dirname(__FILE__)+'/mixins/javascript_mixin'
2
+ require File.dirname(__FILE__)+'/mixins/view_context_mixin'
3
+ require File.dirname(__FILE__)+'/mixins/form_control_mixin'
3
4
 
4
5
  module Merb
5
-
6
+ PROTECTED_IVARS = %w[@cookies @session @headers @params
7
+ @env @in @status @root @method @request @fingerprint_before
8
+ @_new_cookie @tmpl_ext_cache @body]
9
+ module GlobalHelper
10
+ end
6
11
  # the ViewContext is really
7
12
  # just an empty container for us to fill with instance
8
13
  # variables from the controller, include helpers into
@@ -10,13 +15,13 @@ module Merb
10
15
  # when evaluating the templates.
11
16
  class ViewContext
12
17
  include Merb::ErubisCaptureMixin
13
- include Merb::JavascriptMixin
18
+ include Merb::ViewContextMixin
19
+ include Merb::FormControls
20
+ include Merb::GlobalHelper
14
21
 
15
22
  def initialize(controller)
16
23
  @controller = controller
17
- (@controller.instance_variables - %w[@cookies @session @headers @params
18
- @env @in @status @root @method @request @fingerprint_before
19
- @controller @k]).each do |ivar|
24
+ (@controller.instance_variables - PROTECTED_IVARS).each do |ivar|
20
25
  self.instance_variable_set(ivar, @controller.instance_variable_get(ivar))
21
26
  end
22
27
  begin
@@ -4,7 +4,7 @@ module Merb
4
4
  require 'base64'
5
5
 
6
6
  def credentials
7
- if d = %w{REDIRECT_X_HTTP_AUTHORIZATION
7
+ if d = %w{REDIRECT_X_HTTP_AUTHORIZATION X_HTTP_AUTHORIZATION
8
8
  X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION}.
9
9
  inject([]) { |d,h| @env.has_key?(h) ? @env[h].to_s.split : d }
10
10
  return Base64.decode64(d[1]).split(':')[0..1] if d[0] == 'Basic'
@@ -13,25 +13,23 @@ module Merb
13
13
 
14
14
  def authenticated?
15
15
  username, password = *credentials
16
- username == Merb::Server.config[:basic_auth][:username] and password == Merb::Server.config[:basic_auth][:password]
16
+ username == Merb::Server.basic_auth[:username] and password == Merb::Server.basic_auth[:password]
17
17
  end
18
18
 
19
- def authenticate
19
+ def basic_authentication
20
20
  if !authenticated?
21
21
  throw :halt, :access_denied
22
22
  end
23
23
  end
24
24
 
25
- def self.included(base)
26
- base.class_eval do
27
- def access_denied
28
- @status = 401
29
- @headers['Content-type'] = 'text/plain'
30
- @headers['Status'] = 'Unauthorized'
31
- @headers['WWW-Authenticate'] = "Basic realm=\"#{Merb::Server.config[:basic_auth][:domain]}\""
32
- return 'Unauthorized'
33
- end
34
- end
25
+ def access_denied
26
+ @status = 401
27
+ @headers['Content-type'] = 'text/plain'
28
+ @headers['Status'] = 'Unauthorized'
29
+ @headers['WWW-Authenticate'] = "Basic realm=\"#{Merb::Server.basic_auth[:domain]}\""
30
+ return 'Unauthorized'
31
+ end
32
+
35
33
  end
36
34
 
37
35
  end
@@ -16,14 +16,8 @@ module Merb
16
16
  # serve the static file directly.
17
17
  def send_file(file, opts={})
18
18
  opts.update(Merb::Const::DEFAULT_SEND_FILE_OPTIONS.merge(opts))
19
- [:type, :disposition].each do |arg|
20
- raise ArgumentError, ":#{arg} option required" if opts[arg].nil?
21
- end
22
-
23
19
  disposition = opts[:disposition].dup || 'attachment'
24
-
25
20
  disposition << %(; filename="#{opts[:filename] ? opts[:filename] : File.basename(file)}")
26
-
27
21
  headers.update(
28
22
  'Content-Type' => opts[:type].strip, # fixes a problem with extra '\r' with some browsers
29
23
  'Content-Disposition' => disposition,
@@ -41,6 +35,18 @@ module Merb
41
35
  return
42
36
  end
43
37
 
38
+ # Sets a cookie to be included in the response.
39
+ def set_cookie(name, value, expires)
40
+ (headers['Set-Cookie'] ||='') <<
41
+ (Merb::Const::SET_COOKIE % [name.to_s, escape(value.to_s), expires.rfc2822])
42
+ end
43
+
44
+ # Marks a cookie as deleted. The cookie is given an expires stamp in
45
+ # the past.
46
+ def delete_cookie(name)
47
+ set_cookie(name, nil, Merb::Const::COOKIE_EXPIRED_TIME)
48
+ end
49
+
44
50
  # parses a query string or the payload of a POST
45
51
  # request into the params hash. So for example:
46
52
  # /foo?bar=nik&post[title]=heya&post[body]=whatever
@@ -0,0 +1,94 @@
1
+ require 'date'
2
+ require 'ostruct'
3
+
4
+ class OpenStruct
5
+ def temp hash
6
+ OpenStruct.new(@table.merge(hash))
7
+ end
8
+ end
9
+
10
+ module Merb
11
+ module FormControls
12
+ #
13
+ # this is the main merb form control helper. It can build
14
+ # either textfield, textarea, date selects, or time selects.
15
+ #
16
+ # <%= control_for @post, :title, :text, :id => 'foo', :size => 53 %>
17
+ # <%= control_for @post, :intro, :textarea, :class => 'post_intro',
18
+ # :rows => 10, :cols => 50 %>
19
+ # <%= control_for @post, :created_at, :time %>
20
+ # <%= control_for @post, :published_at, :date %>
21
+ #
22
+ def control_for(obj, meth, type, opts={})
23
+ instance = obj
24
+ obj = obj.class
25
+ o = OpenStruct.new(:name => "#{obj.to_s.downcase}[#{meth}]",
26
+ :value => (instance.send(meth) rescue nil),
27
+ :title => (opts.has_key?(:title) ? opts.delete(:title) : nil),
28
+ :html => opts)
29
+ Control.send(type, o)
30
+ end
31
+
32
+ module Control
33
+
34
+ # This is ripped wholesale from Ramaze with some modifications to work
35
+ # with AR objects instead of Og. Thanks again to Michael Fellinger
36
+ class << self
37
+
38
+ def number(o)
39
+ o.value ||= 0
40
+ text(o)
41
+ end
42
+
43
+ def text(o)
44
+ o.value ||= ""
45
+ tag = ''
46
+ tag << "#{o.title}: " if o.title
47
+ tag << %{<input type="text" name="#{o.name}" value="#{o.value}" #{o.html ? o.html.map{|k,v| "#{k}=\"#{v}\""}.join(' ') : nil}/>}
48
+ end
49
+
50
+ def textarea(o)
51
+ o.value ||= ""
52
+ %{<textarea name="#{o.name}" #{o.html ? o.html.map{|k,v| "#{k}=\"#{v}\""}.join(' ') : nil}>#{o.value}</textarea>}
53
+ end
54
+
55
+ def date(o)
56
+ o.value ||= Date.today
57
+ selects = []
58
+ selects << date_day(o.temp(:value => o.value.day))
59
+ selects << date_month(o.temp(:value => o.value.month))
60
+ selects << date_year(o.temp(:value => o.value.year))
61
+ selects.join("\n")
62
+ end
63
+
64
+ def time(o)
65
+ o.value ||= Time.now
66
+ selects = []
67
+ selects << date_day(o.temp(:value => o.value.day))
68
+ selects << date_month(o.temp(:value => o.value.month))
69
+ selects << date_year(o.temp(:value => o.value.year))
70
+ selects << time_hour(o.temp(:value => o.value.hour))
71
+ selects << time_minute(o.temp(:value => o.value.min))
72
+ selects << time_second(o.temp(:value => o.value.sec))
73
+ selects.join("\n")
74
+ end
75
+
76
+ def time_second(o) select(o.name+'[second]', (0...60),o.value) end
77
+ def time_minute(o) select(o.name+'[minute]', (0...60),o.value) end
78
+ def time_hour(o) select(o.name+'[hour]', (0...24),o.value) end
79
+ def date_day(o) select(o.name+'[day]', (1..31),o.value) end
80
+ def date_month(o) select(o.name+'[month]', (1..12),o.value) end
81
+ def date_year(o) select(o.name+'[year]', (1950..2050),o.value) end
82
+
83
+ def select(name, range, default)
84
+ out = %{<select name="#{name}">\n}
85
+ range.each do |i|
86
+ out << %{ <option value="#{i}"#{' selected="selected"' if default == i}>#{i}</option>\n}
87
+ end
88
+ out << "</select>\n"
89
+ end
90
+ end
91
+
92
+ end # Control
93
+ end # FormHelper
94
+ end # Merb
@@ -1,10 +1,16 @@
1
1
  module Merb
2
2
 
3
3
  module RenderMixin
4
+ @@erbs = {}
5
+ @@mtimes = {}
6
+
7
+ def self.erbs
8
+ @@erbs
9
+ end
4
10
 
5
11
  # shortcut to a template path based on name.
6
12
  def template_dir(loc)
7
- File.expand_path(Merb::Server.config[:merb_root] / "/dist/app/views/#{loc}")
13
+ File.expand_path(MERB_ROOT / "/dist/app/views/#{loc}")
8
14
  end
9
15
 
10
16
  # returns the current method name. Used for
@@ -18,14 +24,14 @@ module Merb
18
24
  # extension from the :template_ext map froom your app's
19
25
  # configuration. defaults to .herb, .jerb & .xerb
20
26
  def template_extension_for(ext)
21
- Merb::Server.config[:template_ext][ext]
27
+ (@tmpl_ext_cache ||= Merb::Server.template_ext)[ext]
22
28
  end
23
29
 
24
30
  # this returns a ViewContext object populated with all
25
31
  # the instance variables in your controller. This is used
26
32
  # as the view context object for the Erubis templates.
27
- def create_view_context
28
- ViewContext.new(self)
33
+ def _view_context
34
+ @_view_context_cache ||= ViewContext.new(self)
29
35
  end
30
36
 
31
37
  # does a render with no layout. Also sets the
@@ -35,11 +41,12 @@ module Merb
35
41
  def render_js(template=current_method_name(1))
36
42
  headers['Content-Type'] = "text/javascript"
37
43
  template = new_eruby_obj(template_dir(self.class.name.snake_case) / "/#{template}.#{template_extension_for(:js)}")
38
- template.evaluate(create_view_context)
44
+ template.evaluate(_view_context)
39
45
  end
40
46
 
41
47
  # renders nothing but sets the status, defaults
42
- # to 200
48
+ # to 200. does send one \n newline char, tyhis is for
49
+ # safari and flash uploaders to work.
43
50
  def render_nothing(status=200)
44
51
  @status = status
45
52
  return "\n"
@@ -52,7 +59,7 @@ module Merb
52
59
  # extension
53
60
  def render_no_layout(template=current_method_name(1))
54
61
  template = new_eruby_obj(template_dir(self.class.name.snake_case) / "/#{template}.#{template_extension_for(:html)}")
55
- template.evaluate(create_view_context)
62
+ template.evaluate(_view_context)
56
63
  end
57
64
 
58
65
  # This is merb's partial render method. You name your
@@ -72,7 +79,7 @@ module Merb
72
79
  else
73
80
  tmpl = new_eruby_obj(template_dir(self.class.name.snake_case) / "/_#{template}.#{template_extension_for(:html)}")
74
81
  end
75
- tmpl.evaluate(create_view_context)
82
+ tmpl.evaluate(_view_context)
76
83
  end
77
84
 
78
85
  # This creates and returns a new Erubis object populated
@@ -80,11 +87,28 @@ module Merb
80
87
  # template then we rescue the Errno::ENOENT exception
81
88
  # and raise a no template found message
82
89
  def new_eruby_obj(path)
83
- begin
84
- Erubis::MEruby.new(IO.read(path))
85
- rescue Errno::ENOENT
86
- raise "No template found at path: #{path}"
87
- end
90
+ if @@erbs[path] && !cache_template?(path)
91
+ return @@erbs[path]
92
+ else
93
+ begin
94
+ returning Erubis::MEruby.new(IO.read(path)) do |eruby|
95
+ eruby.init_evaluator :filename => path
96
+ if cache_template?(path)
97
+ @@erbs[path] = eruby
98
+ @@mtimes[path] = Time.now
99
+ end
100
+ end
101
+ rescue Errno::ENOENT
102
+ raise "No template found at path: #{path}"
103
+ end
104
+ end
105
+ end
106
+
107
+ def cache_template?(path)
108
+ return false unless Merb::Server.config[:cache_templates]
109
+ return true unless @@erbs[path]
110
+ @@mtimes[path] < File.mtime(path) ||
111
+ (File.symlink?(path) && (@@mtimes[path] < File.lstat(path).mtime))
88
112
  end
89
113
 
90
114
  # this is the xml builder render method. This method
@@ -96,12 +120,15 @@ module Merb
96
120
  # xml.bar "baz"
97
121
  # }
98
122
  def render_xml(template=current_method_name(1))
99
- xml = Builder::XmlMarkup.new :indent => 2
100
- xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
101
- eval IO.read( template_dir(self.class.name.snake_case) + "/#{template}.#{template_extension_for(:xml)}" )
102
- @headers['Content-Type'] = 'application/xml'
103
- @headers['Encoding'] = 'UTF-8'
104
- xml.target!
123
+ _xml_body = IO.read( template_dir(self.class.name.snake_case) + "/#{template}.#{template_extension_for(:xml)}" )
124
+ headers['Content-Type'] = 'application/xml'
125
+ headers['Encoding'] = 'UTF-8'
126
+ _view_context.instance_eval %{
127
+ xml = Builder::XmlMarkup.new :indent => 2
128
+ xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
129
+ #{_xml_body}
130
+ return xml.target!
131
+ }
105
132
  end
106
133
 
107
134
  # This is the main render method that handles layouts.
@@ -137,13 +164,12 @@ module Merb
137
164
  # render :layout => :none
138
165
  # end
139
166
  def render(opts={})
140
- template = opts[:action] || params[:action]
167
+ template = opts.is_a?(Symbol) ? opts : (opts[:action] || params[:action])
141
168
  tmpl_ext = template_extension_for(:html)
142
169
  MERB_LOGGER.info("Rendering template: #{template}.#{tmpl_ext}")
143
170
  name = self.class.name.snake_case
144
171
  template = new_eruby_obj(template_dir(name) / "/#{template}.#{tmpl_ext}")
145
- view_context = create_view_context
146
- layout_content = template.evaluate(view_context)
172
+ layout_content = template.evaluate(_view_context)
147
173
  self.layout = opts[:layout].to_sym if opts.has_key?(:layout)
148
174
  return layout_content if (layout == :none)
149
175
  if layout != :application
@@ -156,9 +182,9 @@ module Merb
156
182
  end
157
183
  end
158
184
  MERB_LOGGER.info("With Layout: #{layout_choice}.#{tmpl_ext}")
159
- view_context.instance_eval { throw_content(:layout) { layout_content } }
185
+ _view_context.instance_variable_set('@_layout_content', layout_content)
160
186
  layout_tmpl = new_eruby_obj("#{template_dir('layout')}/#{layout_choice}.#{tmpl_ext}")
161
- layout_tmpl.evaluate(view_context)
187
+ layout_tmpl.evaluate(_view_context)
162
188
  end
163
189
 
164
190
  end
@@ -0,0 +1,122 @@
1
+ module Merb
2
+ module ViewContextMixin
3
+
4
+ def link_to(name, url='', opts={})
5
+ %{<a href="#{url}" #{opts.map{|k,v| "#{k}=\"#{v}\""}.join(' ')}>#{name}</a>}
6
+ end
7
+
8
+ # escape text for javascript.
9
+ def escape_js(javascript)
10
+ (javascript || '').gsub('\\','\0\0').gsub(/\r\n|\n|\r/, "\\n").gsub(/["']/) { |m| "\\#{m}" }
11
+ end
12
+
13
+ # creates an <a> tag with with an onclick containing
14
+ # a js function
15
+ # link_to_function('click me', "alert('hi!')")
16
+ def link_to_function(name, function)
17
+ %{<a href="#" onclick="#{function}; return false;">#{name}</a>}
18
+ end
19
+
20
+ def image_tag(img, path='/images/', opts={})
21
+ %{<img src="#{path+img}" #{opts.map{|k,v| "#{k}=\"#{v}\""}.join(' ')} />}
22
+ end
23
+
24
+ # calls .to_json on data. This will use fjson if installed
25
+ # so it can be faster than escape_js
26
+ def js(data)
27
+ if data.respond_to? :to_json
28
+ data.to_json
29
+ else
30
+ data.inspect.to_json
31
+ end
32
+ end
33
+
34
+ # Requiring javascripts and stylesheets:
35
+ # you can use require_js(:prototype) or require_css(:shinystyles)
36
+ # from any view or layout and the scripts will only be included once
37
+ # in the head of the final page. In the head of your layout you will
38
+ # need to add these two tags:
39
+ #
40
+ # <%= include_required_js %>
41
+ # <%= include_required_css %>
42
+ #
43
+ # --app/views/layouts/application.rhtml
44
+ #
45
+ # <html>
46
+ # <head>
47
+ # <%= include_required_js %>
48
+ # <%= include_required_css %>
49
+ # </head>
50
+ # <body>
51
+ # <%= catch_content :layout %>
52
+ # </body>
53
+ # </html>
54
+ #
55
+ # --app/views/whatever/index.rhtml
56
+ #
57
+ # <%= partial(:part1) %>
58
+ # <%= partial(:part2) %>
59
+ #
60
+ # --app/views/whatever/_part1.rhtml
61
+ #
62
+ # <% require_js 'this' -%>
63
+ # <% require_css 'that', 'another_one' -%>
64
+ #
65
+ # --app/views/whatever/_part2.rhtml
66
+ #
67
+ # <% require_js 'this', 'something_else' -%>
68
+ # <% require_css 'that' -%>
69
+
70
+
71
+ # require_js(:myjs) can be used to require any javascript
72
+ # file anywhere in your templates. It will only include the
73
+ # javascript tag once in the header
74
+ def require_js(*js)
75
+ @required_js ||= []
76
+ @required_js |= js
77
+ end
78
+
79
+ # require_css(:mystyles) can be used to require any javascript
80
+ # file anywhere in your templates. It will only include the
81
+ # javascript tag once in the header
82
+ def require_css(*css)
83
+ @required_css ||= []
84
+ @required_css |= css
85
+ end
86
+
87
+ # this goes in the head of your layout if you will be using
88
+ # require_js
89
+ def include_required_js
90
+ js_include_tag(*@required_js)
91
+ end
92
+
93
+ # this goes in the head of your layout if you will be using
94
+ # require_css
95
+ def include_required_css
96
+ css_link_tag(*@required_js)
97
+ end
98
+
99
+ # js_include_tag(:foo, :bar, :baz) will create a javascript
100
+ # include tag for each script in the arguments. It will append
101
+ # '.js' if it is left out of the call.
102
+ def js_include_tag(*scripts)
103
+ return nil if scripts.empty?
104
+ scripts.inject('') do |memo,script|
105
+ script = script.to_s
106
+ memo << %Q|<script src="/javascripts/#{script=~/\.js$/ ? script : script+'.js' }" type="text/javascript">//</script>\n|
107
+ end
108
+ end
109
+
110
+ # css_include_tag(:foo, :bar, :baz) will create a stylesheet
111
+ # link tag for each stylesheet in the arguments. It will append
112
+ # '.css' if it is left out of the call.
113
+ def css_include_tag(*scripts)
114
+ return nil if scripts.empty?
115
+ scripts.inject('') do |memo,script|
116
+ script = script.to_s
117
+ memo << %Q|<link href="/stylesheets/#{script=~/\.css$/ ? script : script+'.css' }" media="all" rel="Stylesheet" type="text/css"/>\n|
118
+ end
119
+ end
120
+
121
+ end
122
+ end