2008-04-10
ruby instance_eval的理解和应用
最近做的项目有这样一个需求:需要对页面的title,desc,keywods做SEO.根据页面的不同,其内容各、规则和格式都不尽相同,大多数情况下是根据view中的部分实例变量的某些字段按照特定规则态生成的,并且在不同时期,SEO的格式和规则也会有所不同(比如:会在title中加入最近热门的搜索关键字,页面不同搜索关键)。但项目中,所有的页面都共用一个layout,这样就遇到一个问题:怎么取到当前view的实例变量,得到实例变量后需要知道当前view的SEO生成规则,然后生成的SEO插入到当前View中。
脑海里的第一个方案是在ApplicationHelper里加一个方法,并在每个action里生命一个@seo_obj变量,根据@seo的类型来决定怎样生成SEO内容。用这个方案实现的代码丑陋繁杂,判断分支又多,难以维护。代码类似下面:
在同事的启发下,又经过自己的一番探索,决定采用instance_eval去处理。instance_eval可以根据当前对象上下文得到此对象的实例变量以及可以动态地为当前对象生成实例变量的特性刚好可以解决这个需求的问题.
解决方案:
第一步:分为title,keywords,desc创建个第一个hash,key由controller_name.action_name组成,value是SEO的内容,其中动态部分用'?'占位符。例如:
第二步:对那些规则和格式相对简单的,比如知只有一个占位符或者不需要占位符的写一个通用的默认方法,而对那些比较复杂的有多个占位符的写专门的方法,方法名按照controller_name_action_name的方式i。比如像index.merchants'的格式就比较简单,像sodeal.merchant_deals'的格式就稍微发杂一点。根据这个原则就得到如下方法:
第三步:现在该解决怎么调用的问题。第二步定义的SEO方法有两种,特定的和通用的;并且特定方法的方法名是按照controller_name_action_name+'seo'后缀定义的。那么就可以根据respond_to?("#{params[:controller_name]}_#{params[:action]}_seo".to_sym)(注:此modul会mixin到helper里)来判断是否有特定的方法,如果没有就调用方法default_seo,由此思路,便有了下面的方法:
全部代码如下:
在AppliactionHepler里include Seo.然后在layout里调用方法seo.代码如下:
总结:在实际项目中,可以利用ruby的自省和modul_eval,class_eval,evainstance_eval等动态方法,实现业务的横切。把一些和业务没有直接关系的模块从业务代码里剥离出来。
脑海里的第一个方案是在ApplicationHelper里加一个方法,并在每个action里生命一个@seo_obj变量,根据@seo的类型来决定怎样生成SEO内容。用这个方案实现的代码丑陋繁杂,判断分支又多,难以维护。代码类似下面:
def seo_title
case @seo_obj
when "latest"
return "latest coupon codes - #{SITE_NAME}"
when "featured"
return "featured coupon codes - #{SITE_NAME}"
when "stores"
return "coupon code of thousands of stores - #{SITE_NAME}"
when "tags"
return "tags coupons - #{SITE_NAME}"
else
if @seo_obj.kind_of?(Merchant)
....
if @seo_obj.html_title && !(@seo_obj.html_title.blank?)
....
elsif @seo_obj.printable == YES
#printable
...
else
....
end
elsif @seo_obj.kind_of?(Tag)
else
...
end
end
end
def seo_keywords
case @seo_obj
...
end
...
end
在同事的启发下,又经过自己的一番探索,决定采用instance_eval去处理。instance_eval可以根据当前对象上下文得到此对象的实例变量以及可以动态地为当前对象生成实例变量的特性刚好可以解决这个需求的问题.
解决方案:
第一步:分为title,keywords,desc创建个第一个hash,key由controller_name.action_name组成,value是SEO的内容,其中动态部分用'?'占位符。例如:
Seo_Title = {'sodeal.index' => "hot deal 50% of",
'sodeal.merchant_deals' => "IBM Deal,HP Deal,merchant ?1, coupon ?2",
'index.merchants' => 'hot deals and coupons for many online stores'
}
Seo_Desc ={...}
Seo_Keywords = {...}
第二步:对那些规则和格式相对简单的,比如知只有一个占位符或者不需要占位符的写一个通用的默认方法,而对那些比较复杂的有多个占位符的写专门的方法,方法名按照controller_name_action_name的方式i。比如像index.merchants'的格式就比较简单,像sodeal.merchant_deals'的格式就稍微发杂一点。根据这个原则就得到如下方法:
def index_merchants_seo
merchant_name = instance_eval {@merchant.merchant_name}
coupon_name = instance_eval{@coupon.name}
seo_title = Seo_Title["#{params[:controller]}.#{params[:action]}"]
seo_title.gsub('?1',merchant_name).gsub('?2',coupon_name)
set_seo_title seo_title
end
def default_seo
set_seo_title Seo_Title["#{params[:controller]}.#{params[:action]}"]
set_seo_desc Seo_Description["#{params[:controller]}.#{params[:action]}"]
set_seo_keywords Seo_Keywords["#{params[:controller]}.#{params[:action]}"]
end
private
def set_seo_title(title)
instance_eval { @seo_title = title}
end
def set_seo_keywords(keywords)
instance_eval { @seo_keywords = keywords}
end
def set_seo_desc(desc)
instance_eval { @seo_desc = desc}
end
第三步:现在该解决怎么调用的问题。第二步定义的SEO方法有两种,特定的和通用的;并且特定方法的方法名是按照controller_name_action_name+'seo'后缀定义的。那么就可以根据respond_to?("#{params[:controller_name]}_#{params[:action]}_seo".to_sym)(注:此modul会mixin到helper里)来判断是否有特定的方法,如果没有就调用方法default_seo,由此思路,便有了下面的方法:
def seo
if respond_to?("#{params[:controller_name]}_#{params[:action]}_seo".to_sym)
send("#{params[:action]}_seo")
else
default_seo
end
end
全部代码如下:
module Seo
def self.included(helper)
end
Seo_Title = {'sodeal.index' => "hot deal 50% of",
'sodeal.merchant_deals' => "IBM Deal,HP Deal,merchant ?1, coupon ?2",
'index.merchants' => 'hot deals and coupons for many online stores'
}
Seo_Description = {'sodeal.index' => 'index desc'}
Seo_Keywords = {'sodeal.index' => 'index keywords'}
protected
#later can add contoller_name here
def seo
if respond_to?("#{params[:controller_name]}_#{params[:action]}_seo".to_sym)
send("#{params[:action]}_seo")
else
default_seo
end
end
def index_merchants_seo
merchant_name = instance_eval {@merchant.merchant_name}
coupon_name = instance_eval{@coupon.name}
seo_title = "24*7 hot deals and coupons for ?1".gsub('?1',merchant_name)
seo_title = Seo_Title["#{params[:controller]}.#{params[:action]}"].gsub('?1',merchant_name).gsub('?2',coupon_name)
set_seo_title seo_title
end
def default_seo
set_seo_title Seo_Title["#{params[:controller]}.#{params[:action]}"]
set_seo_desc Seo_Description["#{params[:controller]}.#{params[:action]}"]
set_seo_keywords Seo_Keywords["#{params[:controller]}.#{params[:action]}"]
end
private
def set_seo_title(title)
instance_eval { @seo_title = title}
end
def set_seo_keywords(keywords)
instance_eval { @seo_keywords = keywords}
end
def set_seo_desc(desc)
instance_eval { @seo_desc = desc}
end
end
在AppliactionHepler里include Seo.然后在layout里调用方法seo.代码如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/2001/REC-xhtml11-20010531/DTD/xhtml11-flat.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <% seo %> <title><%= @seo_title %></title> <meta http-equiv="Content-Language" content="en-gb" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <META content="<%= @seo_desc %>" name="description"> <META content="<%= @seo_keywords %>" name="keywords">
总结:在实际项目中,可以利用ruby的自省和modul_eval,class_eval,evainstance_eval等动态方法,实现业务的横切。把一些和业务没有直接关系的模块从业务代码里剥离出来。
发表评论
- 浏览: 838 次
- 性别:

- 来自: 北京

- 详细资料
搜索本博客
最近加入圈子
最新评论
-
Binding趣用
eval是在当前定上下文中去执行代码,由此可知,eval("lvar = 'ne ...
-- by shaquan6776 -
Binding趣用
eval("lvar = 'new value'", the_binding) ...
-- by flyinglife -
Binding趣用
执行下不就知道了。
-- by shaquan6776 -
Binding趣用
解释哈 趣味何在??
-- by yangtao309 -
正则和eval的趣用
借用了你的正则表达式,我的方法是 hash=Hash.new ...
-- by ek2






评论排行榜