动手写rails(二)Rails_Ruby_ERB使用_模板_定制
动手写rails(二)Rails_Ruby_ERB使用_模板_定制
?
一。基础:基本知识
?
先看例子代码:
例子1:
require "erb"#例子1:erb = ERB.new("I am <%= 'Fantaxy' %>")puts "例子1:"puts erb.resultputs erb.src
?
输出:
例子1:
I am Fantaxy
_erbout = ''; _erbout.concat "I am "; _erbout.concat(( 'Fantaxy' ).to_s); _erbout
?
例子2:
#例子2:a = 123erb = ERB.new("it is <%=a %>")puts "例子2:"puts erb.result(binding)puts erb.src
输出:?
例子2:
it is 123
_erbout = ''; _erbout.concat "it is "; _erbout.concat((a ).to_s); _erbout
?
?
例子3:
erb_str = %q* <% x = "_aaa_" 1.upto(5) do |i| x << "_#{i}_" end %> <%= x %>*erb = ERB.new(erb_str)puts erb.result(binding)puts erb.src
输出:
?
??
? _aaa__1__2__3__4__5_
_erbout = ''; _erbout.concat "\n ?"
;?
? x = "_aaa_"
? 1.upto(5) do |i|
? ? x << "_#{i}_"
? end
? ; _erbout.concat "\n ?"
; _erbout.concat(( x ).to_s); _erbout.concat "\n"
; _erbout
?
?
小结:
#1 如果不是用绑定的变量,则不许要binding参数,否则需要把环境传进来
#2?ERB中可以使用<%= 1111 %> 和 <% 控制语句 %>, 这一点和jsp是如如出一辙的
#3 ERB的src命令可以输出可以run的ruby代码
#4 在rvm中运行输出的src代码 等价于 调用result方法 等价于在rvm中运行 eval(erb.src)
_erbout = ''; _erbout.concat "\n "; x = "_aaa_" 1.upto(5) do |i| x << "_#{i}_" end ; _erbout.concat "\n "; _erbout.concat(( x ).to_s); _erbout.concat "\n"; _erbout
输出:
?
??
? _aaa__1__2__3__4__5_
puts eval(erb.src)?
输出同上。
?
二。进阶:输出新起一行设置,安全设置,返回值设置,及灵活使用
?
ERB.new(str, safe_level=nil, trim_mode=nil, eoutvar='_erbout')
#1 安全级别
#2?新起一行设置
ERB默认会把%>结尾的行单独成行;
If?trim_mode?is passed a String containing one or more of the following modifiers,?ERB?will adjust its code generation as listed:
% enables Ruby code processing for lines beginning with %<> omit newline for lines starting with <% and ending in %>> omit newline for lines ending in %>
上面的输出部分,会看见前面有空行,这个就是输出的准确内容,空行原因就是这里说的。
?
#3 返回值设置
eoutvar?can be used to set the name of the variable?ERB?will build up its output in. This is useful when you need to run multiple?ERB?templates through the same binding and/or when you want to control where output ends up. Pass the name of the variable to be used inside a String.
例子:
require "erb"a = "Fantaxy"@output_buffer = "I am "erb = ERB.new("<% @output_buffer ||= '';%> Yes! <%= @output_buffer << a %>", 1, "@output_buffer")puts erb.result(binding)a = "025025"puts erb.result(binding)puts @output_buffer
输出(请选中查看):
?
?Yes! ?I am Fantaxy
?Yes! ?I am Fantaxy025025
I am Fantaxy025025
?
这里有个问题,@output_buffer在模板中共用了。
因为变量共用导致了模板使用没有了独立性,一般不会使用共用变量的方式。
但如果不共用一个变量,erb每次都会重新对输出结果保存的变量进行赋值。
看这个例子:
require "erb"user_name = "Fantaxy"@output_buffer = ""puts "@output_buffer = #{@output_buffer}"erb = ERB.new("I am <%= user_name %>", 1, "<>", "@output_buffer")puts "erb_result = #{erb.result(binding)}"puts "@output_buffer = #{@output_buffer}"user_name = "June"puts "erb_result = #{erb.result(binding)}"puts "@output_buffer = #{@output_buffer}"
输出:
@output_buffer =?
erb_result = I am Fantaxy
@output_buffer = I am Fantaxy
erb_result = I am June
@output_buffer = I am June
?
从结果可以验证,@output_buffer的被重新赋值了。
为什么?
从本质上来说,erb的执行就是生成src后用rvm执行的。
每次调用src方法,都会重新使用输出变量并赋值为空字符串(''),请看前面几个例子中src生成的代码:
_erbout = '';
?
怎么解决这个问题:
折中的办法是保持erb模板文件的独立性,用逻辑来达到输出结果的关联。
看例子的改进版本:
require "erb"user_name = "Fantaxy"@output_buffer = ""puts "@output_buffer = #{@output_buffer}"erb = ERB.new("I am <%= user_name %>", 1, "<>", "@output_buffer")puts "erb_result = #{erb.result(binding)}"puts "@output_buffer = #{@output_buffer}"old_output_buffer = @output_buffer #先保存下来user_name = "June"puts "erb_result = #{erb.result(binding)}" #@output_buffer被覆盖了puts "@output_buffer = #{@output_buffer}"@output_buffer = "#{old_output_buffer} AND #{@output_buffer}"puts "@output_buffer = #{@output_buffer}"
输出结果:
@output_buffer =?
erb_result = I am Fantaxy
@output_buffer = I am Fantaxy
erb_result = I am June
@output_buffer = I am June
@output_buffer = I am Fantaxy AND I am June
?
这样就使得view的erb模板成为了独立,想怎么使用(比如内容前置,后置等),何时使用,交给了控制端灵活使用。
?
上面的做法可以解决view模板内容前置,后置的问题,但是如果想把一个view模板的内容插入另一个erb模板的中间,该怎么办呢?
需求简写条件:
erbA的内容是:
This is head
body content xxx
body(将会有内容插入此处)
body content yyy
This is foot
erbB的内容是:
I am Fantaxy
需求简写问题:
如何可以用erb渲染后,把erbB的内容插入erbA中间bodyXXX的位置(别用replace哈)
?
解决方法:yield
?
require "erb"#mocking@output_buffer = ""def test_erb_out_mocking_layout erbA_str = <<-ERB <%="This is head"%> <%="body content xxx"%> <% yield %> <%="body content yyy"%> <%="This is foot"%> ERB erbA = ERB.new(erbA_str, 1, '<>', "@output_buffer") erbA.result(binding)end#runtest_erb_out_mocking_layout do @old_output_buffer = @output_buffer erbB = ERB.new("I am Fantaxy!", 1, '<>', "@output_buffer") erbB.result(binding) @output_buffer = "#{@old_output_buffer}#{@output_buffer}"end#testingputs @output_buffer?
输出:
?
? ? This is head
? ? body content xxx
? ? I am Fantaxy!
? ? body content yyy
? ? This is foot
这样rails的layout问题如何写的核心问题也就解决了。
再强调一下核心内容(对么?):
erb模板run(result)的过程本质就是转化成ruby语句后在rvm中运行。
上面的yield如何使用,只需要把erb的src输出来看看就知道了,这也是为什么这个例子包装入了一个方法内,其他的例子可以直接写代码了。(自己试试看吧,不贴上来了)
?
三。高级:erb的其他内容
?
1)erb中的ruby代码空白换行问题
erb中的ruby代码嵌入了<% ruby代码 %>
但是默认会认为其前后的内容也是输出result的结果。但很多时候不许要这个,比如html代码中的这些ruby逻辑控制,仅仅想让他成为控制,而不影响生成的html格式。
方法:上面已经有了,可以设置new中的3ed参数,以及解释:
% enables Ruby code processing for lines beginning with %<> omit newline for lines starting with <% and ending in %>> omit newline for lines ending in %>
?
但是rails中的 -%>怎么会其作用呢?
原来有个hack没有在文档中写出来:3ed参数传递'-'则打开了 <%- ?和 ?-%>标签
?
2)erb中可以定义方法和类等,作用如同freemarker中的macro或者c中的宏吧
A:定义Class:
<%# file: example.html.erb %><%= "arg1 = #{@arg1}" %><%= "arg2 = #{@arg2}" %>
class MyClass_1 def initialize(arg1, arg2) @arg1 = arg1; @arg2 = arg2 endendfilename = 'example.html.erb' # @arg1 and @arg2 are used in example.html.erberb = ERB.new(File.read(filename))erb.filename = filenameMyClass_1 = erb.def_class(MyClass_1, 'render()')print MyClass_1.new('foo', 123).render()
?输出:
?
(注:空行)
arg1 = foo
arg2 = 123
?
?
B:定义方法
<%# example.rhtml %><%= "arg1 = #{arg1}" %><%= "arg2 = #{arg2}" %>
class MyClass; endfilename = 'example.rhtml' # 'arg1' and 'arg2' are used in example.rhtmlerb = ERB.new(File.read(filename))erb.def_method(MyClass, 'render(arg1, arg2)', filename)print MyClass.new.render('foo', 123)
输出:同上
?
C:定义Module
?
filename = 'example.rhtml' # 'arg1' and 'arg2' are used in example.rhtmlerb = ERB.new(File.read(filename))erb.filename = filenameMyModule = erb.def_module('render(arg1, arg2)')class MyClass include MyModule def test_render(arg1, arg2) puts render(arg1, arg2) endendMyClass.new.test_render("Lee", "June")
输出:
?
?
arg1 = Lee
arg2 = June
?
结束:
rails中使用的主要模板解析用的是ERB,避免不了很多的地方都出现了ERB的使用方法和技巧。
可以说,ERB的环境和使用方法,影响了整个MVC框架的VC层的设计。
?
?
参考:
官方api介绍比较详细的解释,erb
http://ruby-doc.org/stdlib-1.9.3/
换行问题和ERB标签的识别和扩展:
http://cheat.errtheblog.com/s/erb/
http://stackoverflow.com/questions/9208728/embedded-ruby-erb-tags
http://stackoverflow.com/questions/3311589/how-to-extend-ruby-erb-for-handling-tags-as-well
ERB的一个tutorial step by step:
http://www.stuartellis.eu/articles/erb/
讨论erb和rails以及mvc的几个帖子:
http://www.iteye.com/topic/72525#268222
http://www.iteye.com/topic/84116
?
?
? ? ? ? ? ? ||
? ? ? ? ? ?| ?|
? ? ? ? ? | ? ?|
====结束====
=== ? ? ? ? ? ===
== ? ? ? ? ? ? ? ?==
= ? ? ? ? ? ? ? ? ? ? =
| ? ? ? ? ? ? ? ? ? ? ? |
?
?
?
?