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.
- data/README +64 -80
- data/Rakefile +25 -12
- data/bin/merb +2 -223
- data/examples/README_EXAMPLES +10 -0
- data/examples/skeleton.tar +0 -0
- data/lib/merb.rb +48 -21
- data/lib/merb/core_ext.rb +12 -2
- data/lib/merb/core_ext/merb_class.rb +21 -21
- data/lib/merb/core_ext/merb_kernel.rb +60 -0
- data/lib/merb/core_ext/merb_object.rb +14 -0
- data/lib/merb/core_ext/merb_string.rb +3 -13
- data/lib/merb/generators/merb_app/merb_app.rb +33 -0
- data/lib/merb/merb_constants.rb +7 -0
- data/lib/merb/merb_controller.rb +31 -23
- data/lib/merb/merb_drb_server.rb +6 -60
- data/lib/merb/merb_exceptions.rb +162 -2
- data/lib/merb/merb_handler.rb +8 -19
- data/lib/merb/merb_mailer.rb +60 -0
- data/lib/merb/merb_router.rb +1 -1
- data/lib/merb/merb_server.rb +240 -0
- data/lib/merb/merb_upload_handler.rb +1 -1
- data/lib/merb/merb_view_context.rb +11 -6
- data/lib/merb/mixins/basic_authentication_mixin.rb +11 -13
- data/lib/merb/mixins/controller_mixin.rb +12 -6
- data/lib/merb/mixins/form_control_mixin.rb +94 -0
- data/lib/merb/mixins/render_mixin.rb +50 -24
- data/lib/merb/mixins/view_context_mixin.rb +122 -0
- data/lib/merb/session/merb_ar_session.rb +13 -14
- data/lib/merb/session/merb_memory_session.rb +105 -0
- metadata +13 -132
- data/examples/app_skeleton/Rakefile +0 -82
- data/examples/app_skeleton/dist/app/helpers/global_helper.rb +0 -6
- data/examples/app_skeleton/dist/conf/merb.yml +0 -11
- data/examples/app_skeleton/dist/conf/merb_init.rb +0 -16
- data/examples/app_skeleton/dist/conf/mup.conf +0 -5
- data/examples/app_skeleton/dist/conf/router.rb +0 -19
- data/examples/app_skeleton/scripts/merb_stop +0 -13
- data/examples/app_skeleton/scripts/new_migration +0 -21
- data/examples/app_skeleton/test/test_helper.rb +0 -1
- data/examples/sample_app/Rakefile +0 -82
- data/examples/sample_app/dist/app/controllers/files.rb +0 -31
- data/examples/sample_app/dist/app/controllers/posts.rb +0 -71
- data/examples/sample_app/dist/app/controllers/test.rb +0 -40
- data/examples/sample_app/dist/app/helpers/global_helper.rb +0 -7
- data/examples/sample_app/dist/app/helpers/posts_helper.rb +0 -4
- data/examples/sample_app/dist/app/models/comment.rb +0 -3
- data/examples/sample_app/dist/app/models/post.rb +0 -4
- data/examples/sample_app/dist/app/views/files/progress.jerb +0 -3
- data/examples/sample_app/dist/app/views/files/start.herb +0 -62
- data/examples/sample_app/dist/app/views/files/upload.herb +0 -6
- data/examples/sample_app/dist/app/views/layout/application.herb +0 -61
- data/examples/sample_app/dist/app/views/layout/foo.herb +0 -6
- data/examples/sample_app/dist/app/views/posts/_comments.herb +0 -11
- data/examples/sample_app/dist/app/views/posts/comment.jerb +0 -1
- data/examples/sample_app/dist/app/views/posts/list.herb +0 -5
- data/examples/sample_app/dist/app/views/posts/new.herb +0 -37
- data/examples/sample_app/dist/app/views/posts/show.herb +0 -37
- data/examples/sample_app/dist/app/views/posts/xml_test.xerb +0 -3
- data/examples/sample_app/dist/app/views/shared/_test.herb +0 -1
- data/examples/sample_app/dist/app/views/test/foo.herb +0 -2
- data/examples/sample_app/dist/app/views/test/hello.herb +0 -5
- data/examples/sample_app/dist/app/views/test/json.jerb +0 -1
- data/examples/sample_app/dist/conf/merb.yml +0 -11
- data/examples/sample_app/dist/conf/merb_init.rb +0 -24
- data/examples/sample_app/dist/conf/mup.conf +0 -5
- data/examples/sample_app/dist/conf/router.rb +0 -19
- data/examples/sample_app/dist/public/images/bg.jpg +0 -0
- data/examples/sample_app/dist/public/images/book.gif +0 -0
- data/examples/sample_app/dist/public/images/booksmall.gif +0 -0
- data/examples/sample_app/dist/public/images/greenright.jpg +0 -0
- data/examples/sample_app/dist/public/images/louiecon.gif +0 -0
- data/examples/sample_app/dist/public/images/menu.gif +0 -0
- data/examples/sample_app/dist/public/images/menuleft.gif +0 -0
- data/examples/sample_app/dist/public/images/menuright.gif +0 -0
- data/examples/sample_app/dist/public/images/mountain.jpg +0 -0
- data/examples/sample_app/dist/public/images/n3.jpg +0 -0
- data/examples/sample_app/dist/public/images/nautica.jpg +0 -0
- data/examples/sample_app/dist/public/javascripts/application.js +0 -0
- data/examples/sample_app/dist/public/javascripts/effects.js +0 -975
- data/examples/sample_app/dist/public/javascripts/mup.js +0 -113
- data/examples/sample_app/dist/public/javascripts/prototype.js +0 -2264
- data/examples/sample_app/dist/public/stylesheets/merb.css +0 -277
- data/examples/sample_app/dist/public/test.html +0 -5
- data/examples/sample_app/dist/schema/migrations/001_add_comments_to_posts.rb +0 -22
- data/examples/sample_app/dist/schema/migrations/002_add_sessions_table.rb +0 -14
- data/examples/sample_app/dist/schema/schema.rb +0 -28
- data/examples/sample_app/foo.txt +0 -0
- data/examples/sample_app/log/merb.4000.pid +0 -1
- data/examples/sample_app/script/merb_stop +0 -13
- data/examples/sample_app/script/new_migration +0 -21
- data/examples/sample_app/test/test_helper.rb +0 -1
- data/lib/merb/mixins/javascript_mixin.rb +0 -147
- data/lib/merb/session/merb_drb_session.rb +0 -65
- data/test/test_helper.rb +0 -1
- 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.
|
|
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/
|
|
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::
|
|
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 -
|
|
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.
|
|
16
|
+
username == Merb::Server.basic_auth[:username] and password == Merb::Server.basic_auth[:password]
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
def
|
|
19
|
+
def basic_authentication
|
|
20
20
|
if !authenticated?
|
|
21
21
|
throw :halt, :access_denied
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
def
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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(
|
|
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.
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|