动手写rails(二)Rails_Ruby_ERB使用_模板_定制
一。基础:基本知识
先看例子代码:
例子1:
require "erb" #例子1: erb = ERB.new("I am <%= 'Fantaxy' %>") puts "例子1:" puts erb.result puts erb.src
输出:
例子1:
I am Fantaxy
_erbout = ''; _erbout.concat "I am "; _erbout.concat(( 'Fantaxy' ).to_s); _erbout
例子2:
#例子2: a = 123 erb = 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
#run
test_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
#testing
puts @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 end end filename = 'example.html.erb' # @arg1 and @arg2 are used in example.html.erb erb = ERB.new(File.read(filename)) erb.filename = filename MyClass_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; end filename = 'example.rhtml' # 'arg1' and 'arg2' are used in example.rhtml erb = 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.rhtml erb = ERB.new(File.read(filename)) erb.filename = filename MyModule = erb.def_module('render(arg1, arg2)') class MyClass include MyModule def test_render(arg1, arg2) puts render(arg1, arg2) end end MyClass.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
||
| |
| |
====结束====
=== ===
== ==
= =
| |