merb 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|