merb 0.0.8 → 0.0.9

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