﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-tech.cap-文章分类-rails</title><link>http://www.blogjava.net/cap/category/7192.html</link><description>我在一望无际的路上</description><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 10:31:59 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 10:31:59 GMT</pubDate><ttl>60</ttl><item><title>rails笔记 ajax</title><link>http://www.blogjava.net/cap/articles/rails_ajax.html</link><dc:creator>tech.cap</dc:creator><author>tech.cap</author><pubDate>Tue, 24 Jan 2006 08:56:00 GMT</pubDate><guid>http://www.blogjava.net/cap/articles/rails_ajax.html</guid><wfw:comment>http://www.blogjava.net/cap/comments/29123.html</wfw:comment><comments>http://www.blogjava.net/cap/articles/rails_ajax.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/cap/comments/commentRss/29123.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cap/services/trackbacks/29123.html</trackback:ping><description><![CDATA[<H3>ajax web2.0</H3>
<P>要使用ajax,先要在页面中包含 &lt;%=javascript_include_tag "prototype"%&gt;</P>
<P>ajax的指令如下</P>
<H4>link_to_remote</H4>
<P>例子如下</P><PRE><CODE>&lt;%= link_to_remote("Do the Ajax thing",
        :update =&gt; 'mydiv',
        :url =&gt; { :action =&gt; :say_hello }) %&gt;
&lt;div id="mydiv"&gt;This text will be changed&lt;/div&gt;
</CODE></PRE>
<P>第一个参数为link的文本第二个参数为更新的元素的的id(div,span font等都可以) 第三个参数为远程的url(url_for语法),注意这个url要把layout关闭</P>
<H4>form_remote_tag</H4>
<P>例子如下 &lt;%= form_remote_tag(:update =&gt; "update_div", :url =&gt; { :action =&gt; :guess } ) %&gt; &lt;%= text_field_tag :guess %&gt; &lt;%= submit_tag "Post form with AJAX" %&gt; &lt;%= end_form_tag %&gt;</P>
<P>参数有:update更新的元素id :url同上 更多参数查api ??? 为什么api上的参数不一样呢</P>
<H4>observers</H4>
<P>例子如下</P><PRE><CODE>&lt;%= text_field_tag :search %&gt;

&lt;%= observe_field(:search,
:frequency =&gt; 0.5,
:update =&gt; :results,
:url =&gt; { :action =&gt; :search }) %&gt;
</CODE></PRE>
<P>:frequency是指定更新的频率,当发现输入字段有改变时候会自动提交修改到对应的action,把返回值搞下来更新 </P>
<P>??? <STRONG>注意</STRONG> 这里在action端使用了使用了request.raw_post || request.query_string 似乎还不是很明白这个用法</P>
<H4>periodic update</H4>
<P>例子如下 &lt;%= periodically_call_remote(:update =&gt; 'process-list', :url =&gt; { :action =&gt; :ps }, :frequency =&gt; 2 )%&gt;</P>
<P>参数同上</P>
<H3>Dom接口 !!!看看javascript的文档</H3>
<P>rails单独提供了一套javascript的api来操作Dom接口</P>
<H4>$(id)</H4>
<P>Pass the $( ) method a string, and it returns the DOM element with the given id. Otherwise it returns its argument. (This behavior means you can pass in either an element’s id= attribute or the element itself and get an element returned.)</P><PRE><CODE>$('mydiv').style.border = "1px solid red"; /* sets border on mydiv */
</CODE></PRE>
<H4>Element.toggle(element, ...)</H4>
<P>Element.toggle( ) toggles whether the given element (or elements) are shown. Internally, it switches the value of the CSS display attribute between ’inline’ and ’none’.</P><PRE><CODE>Element.toggle('mydiv'); /* toggles mydiv */
Element.toggle('div1', 'div2', 'div3'); /* toggles div1-div3 */
</CODE></PRE>
<H4>Element.show(element, ...)</H4>
<P>Element.show( ) ensures all elements it receives as parameters will be shown.</P><PRE><CODE>Element.show('warning'); /* shows the element with id 'warning' */
</CODE></PRE>
<H4>Element.hide(element, ...)</H4>
<P>Opposite of Element.show( ).</P>
<H4>Element.remove(element)</H4>
<P>Element.remove( ) completely removes an element from the DOM. </P><PRE><CODE>Element.remove('mydiv'); /* completely erase mydiv */
</CODE></PRE>
<H4>Insertion methods</H4>
<P>The various insertion methods make it easy to add HTML fragments to existing elements. They are discussed in Section 18.4, Replacement Techniques, on page 389.</P>
<H3>javascript效果</H3>
<P>javascript效果需要额外include "effects"包,所以一共include 两个包</P><PRE><CODE>&lt;%= javascript_include_tag "prototype", "effects" %&gt;
</CODE></PRE>
<P>调用效果一般通过Effect.xxx(element) 可以传入一个代表id的string,或者使用this表明当前对象</P>
<H3>...下面的内容很需要熟悉javascript, 本人暂时决定不深入研究</H3><img src ="http://www.blogjava.net/cap/aggbug/29123.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cap/" target="_blank">tech.cap</a> 2006-01-24 16:56 <a href="http://www.blogjava.net/cap/articles/rails_ajax.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>rails笔记 active controller</title><link>http://www.blogjava.net/cap/articles/rails_controller.html</link><dc:creator>tech.cap</dc:creator><author>tech.cap</author><pubDate>Tue, 24 Jan 2006 08:55:00 GMT</pubDate><guid>http://www.blogjava.net/cap/articles/rails_controller.html</guid><wfw:comment>http://www.blogjava.net/cap/comments/29121.html</wfw:comment><comments>http://www.blogjava.net/cap/articles/rails_controller.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/cap/comments/commentRss/29121.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cap/services/trackbacks/29121.html</trackback:ping><description><![CDATA[<H3>active controller</H3>
<P>model指令 提前load model对象 model :Product</P>
<H4>基本对应关系</H4>
<P>http://xxx.com/admin/hello/list 对应为app/controllers/admin/hello_controller.rb中的list方法如下 module Admin class Hello def list end end end</P>
<P>ctroller是每次都新建</P>
<H4>url自动映射 map.connect</H4>
<P>在config/routes.rb中可以定义最底层的url和action对应的关系,这使得rails的url映射可以异常灵活,</P>
<P>默认的映射如下 map.connect ':controller/:action/:id' 他会自动匹配有三段的url请求, 详细的规则如下</P>
<UL>
<LI>url按照前置的"/"分段为components</LI>
<LI>":name"会创建以name为名的参数,同时把对应url段赋值给它</LI>
<LI>"<EM>name" 匹配所有剩下的url段,会创建以name为名的数组,"</EM>name"只能出现在最后</LI>
<LI>其他以文本出现字符的均严格匹配</LI></UL>
<P>map.connect接受的参数如下 </P>
<UL>
<LI>:defaults=&gt;{:name=&gt;'value',...} 设置对应name的url段默认值,比如:action=&gt;'index'</LI>
<LI>:requirements=&gt;{:name=&gt;/regexp/,...} 要求对应name的url段必须匹配的正则表达式</LI>
<LI>:name=&gt;value 也是设置默认值, 不过这个对应的name可以自己取, 不一定必须要在url匹配中出现,相当于追加的参数</LI>
<LI>:name=&gt;/regexp/ 和:requirements一样的功能, 设置正则</LI></UL>
<H4>url自动产生 url_for</H4>
<P>和url自动映射对应, rails可以利用map.connect中存储的信息来自动构造url,实现友好的url功能如默认设置中, 通过url_for :controller=&gt;"xx",:action=&gt;"fda",:id=&gt;"fdas"可以构造这三个特殊的url段</P>
<P>url_for会根据传入的url来填写默认值, 比如url_for(:action=&gt;"fda")会默认:controller为当前的,假设url段为目录那样的结构,url_for默认值工作如下: <STRONG>子目录变化,他的父级目录都会填入默认值,但是他的子级目录都会舍弃</STRONG>, 这里的变化是值的变化,显式赋相同的值是没有变化的</P>
<P>如果要阻止url_for智能默认值,可以显式的赋最高级目录为nil,这样从最高到下的默认值都舍弃了</P>
<P>如果改变了高级,仍然要低级不舍弃, 可以通过url_for(:overwrite_params=&gt;{:xxx=&gt;yyy})来完成, 这样rails会认为没有改变, 所有低级目录段都会填充进来</P>
<P>url_for的扩展参数</P>
<UL>
<LI>:anchor 指定anchor(页内联接),rails会自动添加<CODE>#</CODE></LI>
<LI>:host 指定主机(可以带端口)</LI>
<LI>:only_path 只产生path, host, port,protocol都省略</LI>
<LI>:protocol 协议名 比如"https://"</LI>
<LI>:trailing_slash 在末尾添加"/",<STRONG>注意</STRONG>有报告说末尾有"/"的时候,页面cache系统有混乱</LI></UL>
<H5>to_param</H5>
<P>实现了to_param的对象, 在传入url_for的时候, 会自动抽取to_param的值,否则会抽取to_s, rails的model的to_param会自动获取id</P>
<H3>命名routes map.xxx</H3>
<P>使用map.xxx 来代替map.connect 可以创建一个xxx的route,这个route还对应了一个xxx_url的方法,使用和url_for一样,只是规则只针对当前route</P>
<H3>action方法</H3>
<P>rails会自动把url请求转发到对应的controller类的方法中, 这个方法就叫action方法, action方法如果没有找到,会调用method_missing,传入action名和空参数列表, 如果没有action方法调用,rails直接跳转到action对应的template中实现纯view ,如果没有找到action, 就报错Unknown Action</P>
<P>隐藏方法: private或者<CODE>hide_action</CODE>都可以 <STRONG>注意</STRONG> 如果使用<CODE>hide_action</CODE>的目的是想公开private的方法给其他类, 可以考虑使用helpers类来完成, <CODE>hide_action</CODE>为下策也</P>
<H3>controller环境对象</H3>
<UL>
<LI>
<P>request</P>
<UL>
<LI>domain() 主机名</LI>
<LI>remote_ip() 远程ip</LI>
<LI>env 访问浏览器传过来的参数</LI>
<LI>method http请求方法 :delete, :get,:head,:post,:put</LI>
<LI>delete?,get?,head?,post?,put? 返回true or false</LI></UL></LI>
<LI>
<P>params 相当于jsp.parametermap,类似hash.使用params[:xxx]和params['xxx']等价, 推荐前者</P></LI>
<LI>cookies 和params类似</LI>
<LI>response 一般不用到它, 在filter中可能用到</LI>
<LI>session</LI>
<LI>headers 输出给浏览器的HEADER</LI>
<LI>还有一个logger对象</LI></UL>
<H3>response处理</H3>
<P>三种处理 1.使用view模板输出 2.直接render文本 3 输出其他数据(pdf,文件)</P>
<P>response只能被render一次, 再次render会产生DoubleRenderError <STRONG>注意</STRONG> 使用erase_render_results可以清除以前的render,但是这个方法无任何保证( undocumented method)</P>
<P>rails检查render,如果controller执行完毕以后还没有render,就会去调对应的view,如果render了, 就不掉view了</P>
<H4>使用view输出</H4>
<P>模板有<EM>.rhtml和</EM>.rxml(builder)两种, 默认在app/views/control/* 中, 默认位置app/views/可以通过ActionController::Base.templdate_root=xxx来更新设置</P>
<H4>直接render文本</H4>
<UL>
<LI>render(:text=&gt;string) 直接render出文本</LI>
<LI>render(:inline=&gt;string,[:type=&gt;"rhtml"|"rxml"]) 把传入的string当成模板处理, 相当于rhtml的内容</LI>
<LI>render(:action=&gt;action_name) 直接调用某个action的模板,相当于forward到一个view</LI>
<LI>render(:file=&gt;path;[:use_full_path=&gt;true|false]) 使用某个模板文件render, 当use_full_path打开时可以传入相对路径</LI>
<LI>render(:template=&gt;name) 使用模板名render ,例子如下 render(:template =&gt; "blog/short_list") # 自动使用/app/views/blog/short_list.rhtml(rxml)</LI>
<LI>render(:partial=&gt;name) ???</LI>
<LI>render(:nothing=&gt;true) 什么也不输出,包括layout</LI>
<LI>render() 默认的的render, 相当于render(:action=&gt;self)</LI></UL>
<P>所有的render都接收:status 和:layout两个额外参数 :layout为false的时候表示不使用layout, nil和true都表示使用layout :layout为string的时候, 使用string表示的名字的layout</P>
<P>使用render_to_string(用法和render一样)可以得到输出的string而不是直接输出到浏览器</P>
<H4>输出其他数据</H4>
<P>send_data(data,options...) 支持的options有</P>
<UL>
<LI>:filename 给浏览器建议的文件名</LI>
<LI>:type 文件类型,默认为"application/octet-stream"</LI>
<LI>:disposition 给浏览器的处理建议,"inline"表示直接显示,"attachment"表示下载另存</LI></UL>
<P>send_file 和send_data一样, 多了一个参数:streaming,当stream为false的时候,整个文件被读入内存传输,反之则使用:buffer_size传输流</P>
<H3>redirect</H3>
<UL>
<LI>redirect_to(:action=&gt;'xxx') 使用语法和url_for一样(底层用url_for)</LI>
<LI>redirect_to("/localpath")</LI>
<LI>redirect_to("http://url")</LI></UL>
<P>默认的redirect都是tempoary</P>
<H3>cookies</H3>
<P>cookies必须使用string值 ,如果使用其他value ,可能会有"private method ‘gsub’ called" 这样的错误</P>
<P>cookies的扩展参数 例子如下</P>
<P>cookies[:marsupial] = { :value =&gt; "wombat",:expires =&gt; 30.days.from_now,:path =&gt; "/store" }</P>
<UL>
<LI>:value 直接的value</LI>
<LI>:domain 存储和访问的地址</LI>
<LI>:expires 过期时间</LI>
<LI>:path 和domain共同决定存储和访问地址</LI>
<LI>:secure 是否只在https//中使用</LI></UL>
<H3>session</H3>
<P>session默认存为文件的, 可以通过删除它们实现更新class结构冲突(旧对象和新对象不一样)</P>
<P>关于session具体配置可以在config/environments中配置,(session过期时间除外)</P>
<P>例子如下</P><PRE><CODE>ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:session_key] = 'my_app'
</CODE></PRE>
<UL>
<LI>:database_manager Controls how the session data is stored on the server. We’ll have more to say about this shortly.</LI>
<LI>:session_domain The domain of the cookie used to store the session id on the browser. Defaults to the application’s host name.</LI>
<LI>:session_id Overrides the default session id. If not set, new sessions automatically have a 32-character key created for them. This key is used in subsequent requests.</LI>
<LI>:session_key The name of the cookie used to store the session id. You’ll want to override this in your application, as shown previously.</LI>
<LI>:session_path The request path to which this session applies (it’s actually the path of the cookie). The default is /, so it applies to all applications in this domain.</LI>
<LI>:session_secure If true, sessions will be enabled only over https://. The default is false.</LI>
<LI>:new_session Directly maps to the underlying cookie’s new_session option. However, this option is unlikely to work the way you need it to under Rails,and we’ll discuss an alternative in Section 16.8, Time-Based Expiry of Cached Pages, on page 323.</LI>
<LI>:session_expires The absolute time of the expiry of this session. Like :new_session, this option should probably not be used under Rails.</LI></UL>
<P>更多设置查阅CGI::Session的文档</P>
<P>DEFAULT_SESSION_OPTIONS还有一个database_manager能配置session的存储方式(:database_manager=&gt;xxxx),列表如下</P>
<UL>
<LI>CGI::Session::PStore 默认的存储方式,使用marshal</LI>
<LI>CGI::Session::ActiveRecordStore 使用ActiveRecord存储到表中 create table sessions ( id int(11) not null auto_increment, sessid varchar(255), data text, updated_at datetime default NULL, primary key(id), index session_index (sessid) );</LI>
<LI>CGI::Session::DRbStore 使用drbserver访问,自带有一个drb_server.rb</LI>
<LI>CGI::Session::MemCacheStore 使用缓存系统 ???</LI>
<LI>CGI::Session::MemoryStore 直接内存存储,不推荐</LI>
<LI>CGI::Session::FileStore 直接文件存储,不推荐,只能支持string存储</LI></UL>
<P>设置 ::ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS = false可以禁用所有session以及依赖session的功能(flash)</P>
<H4>session存储方式比较</H4>
<P>查看这里 <A href="http://media.pragprog.com/ror/sessions/">http://media.pragprog.com/ror/sessions/</A>, 一句话 先用最简单直接的,等到速度变慢时候认证测量, 然后再决策,不要overkill</P>
<H4>session清除</H4>
<P>rails目前比较原始, 居然要自己去清除, 写crontab吧(清文件, 清table) ???</P>
<H3>flash</H3>
<P>flash的生命期是两次request,底层使用session保存对象 flash.now创建临时的flash(一个request内) flash.now[:xxx] flash.keep延迟一个request的生命期 flash.keep(:xxx) 或者flash.keep(全部)</P>
<H3>filter</H3>
<P>before_filter after_filter around_filter</P>
<P>设置filter的参数可以为一个方法symbol :method_a ,一个block 或者一个类, 该类的静态方法self.filter()会被调用 默认filter作用于当前类的所有action方法和子类的action方法, :only 和 :except用来在controller中包含或者排除action</P>
<P>before_filter 和after_filter默认是添加filter到filter chains最后, 如果要添加到最前使用prepend_before_filter() 和 prepend_after_filter()</P>
<P>before_filter 主要用来作验证 after_filter 主要用来控制内容(压缩,替换) around_filter 可以用来记录时间 设置参数必须为一个类, 该类有before(controller)和after(controller)两个方法 <CODE>around_filter XXXFilter.new</CODE></P>
<P>around_filter的添加是嵌套的, around_filter A.new,B.new的结果如下 A.before B.before action..... B.after A.after 注意顺序和前面两个filter不一样</P>
<H4>filter 在继承中的关系</H4>
<P>子controller会执行所有父controller的filter,但是父不会执行子的filter</P>
<H3>verification</H3>
<P>verify指令可以看做一个专门抽出来的filter功能,当verify失败, 当前action就不会执行了 class BlogController &lt; ApplicationController verify :only =&gt; :post_comment, :session =&gt; :user_id, :add_flash =&gt; { :note =&gt; "You must log in to comment"}, :redirect_to =&gt; :index #.... end 支持的参数如下启用条件 :only=&gt;:name or [:name,...] :except=&gt;name or [:name,...] 测试条件 :flash=&gt;:key or [:key,...] flash中必须包含某些key :method=&gt;:symbol or [:symbol,...] 请求必须是某些http 方法 :session=&gt;:key or [:key,...] session中必须包含某些key 执行操作 :add_flash=&gt;hash 把传入hash的值对写入flash中 :redirect_to=&gt;params 跳转页面</P><img src ="http://www.blogjava.net/cap/aggbug/29121.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cap/" target="_blank">tech.cap</a> 2006-01-24 16:55 <a href="http://www.blogjava.net/cap/articles/rails_controller.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>rails笔记 cache系统 </title><link>http://www.blogjava.net/cap/articles/rails_cache.html</link><dc:creator>tech.cap</dc:creator><author>tech.cap</author><pubDate>Tue, 24 Jan 2006 08:55:00 GMT</pubDate><guid>http://www.blogjava.net/cap/articles/rails_cache.html</guid><wfw:comment>http://www.blogjava.net/cap/comments/29122.html</wfw:comment><comments>http://www.blogjava.net/cap/articles/rails_cache.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/cap/comments/commentRss/29122.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cap/services/trackbacks/29122.html</trackback:ping><description><![CDATA[<h3>cache系统 1</h3>

<p>cache系统默认只在production下面生效, 要手动生效
修改环境(config/environments)
ActionController::Base.perform_caching=true|false</p>

<p>手动cache</p>

<ul>
<li>caches_page :xxxaction 直接cache整个页面,相当于html</li>
<li>caches_action :xxxxaction 只cache action,也就是要执行filter</li>
</ul>

<p>cache只适合静态内容, 动态变化的内容不适合cache</p>

<h3>cache的清除</h3>

<p>手动清除,他们接受的参数都和url_for一样,直接按照url来过期</p>

<ul>
<li>expire_page</li>
<li>expire_action</li>
</ul>

<p>自动清除</p>

<p>通过定一个sweeper类  &lt; ActionController::Caching::Sweeper实现after_create
after_update,after_destory三个方法, 传入的参数就是model对象</p>

<p>在controller中生效的方式是cache_sweeper指令</p>

<p>批量清除
pagecache会放在app/public/下面,清除对应的html即可,比如写一个crontab,cache目录和文件名的控制如下
ActionController::Base.page_cache_directory = "dir/name"
ActionController::Base.page_cache_extension = ".html"</p>

<h3>rails的忠告, 为了避免新出现的google web accelarator这样的工具破坏web, 所有风险大的操作都应该隐藏在POST协议中, 不要直接用get,不然一旦被客户端(和真实用户在一个session中)抓住, url就不保险了</h3>

<h3>cache系统 2</h3>

<p>通过 
    &lt;%cache do%>
      ...
    &lt;%end%></p>

<p>来实现部分cache</p>

<p>通过expire_fragment(:action=>'xxx')来清除对应cache</p>

<p>如果一个页面里面有多个part
可以使用
cache(:part=>"yyy") 在清除的时候加入:part=>"yyy"就可以了</p>

<p>cache(:controller=>..:action=>...)参数和url_for类似</p>

<p>cache的存储方式有4种可选</p>

<ul>
<li>ActionController::Caching::Fragments::MemoryStore.new  直接内存(not a good idea when data is large)</li>
<li>ActionController::Caching::Fragments::FileStore.new(path)  直接文件</li>
<li>ActionController::Caching::Fragments::DRbStore.new(url)  drbserver</li>
<li>ActionController::Caching::Fragments::MemCachedStore.new(host) 使用memcached server</li>
</ul><img src ="http://www.blogjava.net/cap/aggbug/29122.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cap/" target="_blank">tech.cap</a> 2006-01-24 16:55 <a href="http://www.blogjava.net/cap/articles/rails_cache.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>rails笔记 actionmailer</title><link>http://www.blogjava.net/cap/articles/rails_mail.html</link><dc:creator>tech.cap</dc:creator><author>tech.cap</author><pubDate>Tue, 24 Jan 2006 08:54:00 GMT</pubDate><guid>http://www.blogjava.net/cap/articles/rails_mail.html</guid><wfw:comment>http://www.blogjava.net/cap/comments/29120.html</wfw:comment><comments>http://www.blogjava.net/cap/articles/rails_mail.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/cap/comments/commentRss/29120.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cap/services/trackbacks/29120.html</trackback:ping><description><![CDATA[<h3>actionmailer</h3>

<p>rails 内置提供了发送邮件的方法即actionmailer , 在内部使用TMail的api,同时提供了对测试友好的功能,默认在test状态下面,rails不会真正发送mail,只会把mail附加到ActionMailer::Base.deliveries中,这样可以通过测试方法访问了</p>

<p>配置actionmailer</p>

<p>ActionMailer::Base.delivery_method = :smtp | :sendmail | :test</p>

<ul>
<li><p>:smtp 是直接使用smtp发送,具体配置如下</p>

<p>ActionMailer::Base.server_settings = {
    :address => "domain.of.smtp.host.net",
    :port => 25,
    :domain => "domain.of.sender.net",
    :authentication => :login,
    :user_name => "dave",
    :password => "secret",
}</p>

<p>其中:authentication可以为:login,:plain,:cram_md5(目前不可用SSL),如果不需要登陆就省略这个参数 :domain为HELO使用的域名</p></li>
<li><p>:sendmail 使用/usr/sbin下的sendmail程序(linux/unix)</p></li>
<li><p>:test 就是附加到deliveries中</p></li>
</ul>

<p>此外还有</p>

<p>ActionMailer::Base.perform_deliveries = true | false 配置是否真正发送mail
ActionMailer::Base.raise_delivery_errors = true | false 发送失败是否抛出exception
ActionMailer::Base.default_charset = "utf-8"  发送mail的默认编码</p>

<p><strong>注意</strong> 现在的rails似乎都用config开头了, 
config.action_mailer.xxxx</p>

<p>使用mailer的流程如下</p>

<h4>使用generator</h4>

<p>ruby script/generate mailer XXXXMailer methoda methodb....</p>

<p>Mailer类会创建在app/models中,不同的method和controller类似,代表不同的邮件,也有不同的模板(和controller类比下来就是),还有不同的fixture和不同的test_方法</p>

<p>Mailer实例变量如下</p>

<ul>
<li>@bcc @cc @from @recipients 数组和string都可以 支持"Cap He <a href="m&#x61;&#105;&#108;&#116;&#111;:&#104;y&#122;&#x40;&#x6A;&#101;&#x74;&#x69;&#099;.&#x6F;&#114;&#103;">&#104;y&#122;&#x40;&#x6A;&#101;&#x74;&#x69;&#099;.&#x6F;&#114;&#103;</a>"这样的语法</li>
<li>@charset header中的content-type表明的编码, 默认为default_charset的设置</li>
<li>@send_on = time 设置header中的发送时间, 默认为当前时间</li>
<li>@subject  邮件标题</li>
</ul>

<p>@body 是一个hash, @body["xxx"]=...可以创建 rhtml中的 @xxx变量</p>

<h4>rhtml模板</h4>

<p>mailer的模板在app/views/xxx_mailer/下, 是rhtml文件(没有说不能用builder,但是email不需要builder xml) 用法和普通rhtml一样,不过在render(::partial=>"./line_item")这样类似的操作中需要用到"./"因为rails无法通过controller来推测模板位置</p>

<h4>发送方法</h4>

<p>两个静态方法<code>deliver_..</code>和<code>create_..</code></p>

<p>使用XXXMailer.deliver_methoda(...) 直接发送
XXXMailer.create_methodb(..) 创建Tmail::Mail对象 通过它的encoded()得到email对象的内容,还可以再用XXXMailer.deliver(email)发送</p>

<p>默认是发送纯文本的邮件, 发送html的mail很简单 <code>email.set_content_type("text/html")</code> 即可</p>

<p>中文测试没有问题, 就是发送smtp要多调试, smtp不需要验证的时候千万不要使用:authentication :user_name :password 否则会出错</p>

<h3>接收email</h3>

<p>rails内置没有接受email的功能, 目前只能通过使用linux系统的shell来调用runner来完成</p>

<p>比如编写.procmailrc文件</p>

<h3>测试mail</h3>

<p>系统会默认生成好测试的类框架, </p>

<p>修改测试方法, 然后测试得到的mail对象即可</p>

<p>unit测试例子</p>

<pre><code>class OrderMailerTest &lt; Test::Unit::TestCase
    def setup
        @order = Order.new(:name =&gt;"Dave Thomas", :email =&gt; "dave@pragprog.com")
    end
    def test_confirm
        response = OrderMailer.create_confirm(@order)
        assert_equal("Pragmatic Store Order Confirmation", response.subject)
        assert_equal("dave@pragprog.com", response.to[0])
        assert_match(/Dear Dave Thomas/, response.body)
    end
end
</code></pre>

<p>置于测试邮件是否发送到指定位子, 可以在controller的functional test中测试,因为mail类本身是仿造model的, 所以也有自己的fixture</p><img src ="http://www.blogjava.net/cap/aggbug/29120.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cap/" target="_blank">tech.cap</a> 2006-01-24 16:54 <a href="http://www.blogjava.net/cap/articles/rails_mail.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>rails笔记: webservice</title><link>http://www.blogjava.net/cap/articles/rails_webservice.html</link><dc:creator>tech.cap</dc:creator><author>tech.cap</author><pubDate>Tue, 24 Jan 2006 02:49:00 GMT</pubDate><guid>http://www.blogjava.net/cap/articles/rails_webservice.html</guid><wfw:comment>http://www.blogjava.net/cap/comments/29080.html</wfw:comment><comments>http://www.blogjava.net/cap/articles/rails_webservice.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/cap/comments/commentRss/29080.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cap/services/trackbacks/29080.html</trackback:ping><description><![CDATA[<H3>web service</H3>
<H3>创建</H3>
<P>先用generator 来创建service 参数为</P>
<P>generator web_service ServiceOne method_a method_b</P>
<P>rails会创建一个service_one_api.rb在/app/apis下,同时有service_one_controller在/app/controllers/下</P>
<P><STRONG>注意</STRONG> 因为ruby完全动态,所以无法根据方法接口来自动生成webservice, 所以搞了一个xxx_api.rb来干这个事情, <STRONG>注意</STRONG> rails对于对象之间的关系不会暴露到webservice外面去,只是简单的把字段id写出去</P>
<H3>方法申明</H3>
<P>在xxxx_api.rb中负责定义方法的参数, 定义一个web方法如下</P>
<P>api_method :xxxmethod_name, :expects=&gt;... ,:returns =&gt;</P>
<P>:expects 如果忽略表示不能传递参数 :returns 忽略表示返回为空</P>
<P>它们接收的参数只能是如下情况之一</P>
<UL>
<LI>symbol或者string 的基本类型 </LI>
<LI>Class类型只支持<CODE>ActionWebService::Struct</CODE>或者ActiveRecord::Base子类</LI>
<LI>一个包含前面2个类型参数的数组</LI>
<LI>一个包含前面3个类型参数的hash 用来表明webservice参数名 (webservice友好)</LI></UL>
<P>比如 [[:string]] [Person] [{:lastname=&gt;:string}] [:int,:int]</P>
<P>基本类型为 :int :string :base64(会自动转为binary来传送文件） :bool :float :time :datetime(Ruby的DateTime) :date(Ruby的Date) 除此以为均非法</P>
<P>在Contoller中有如下指令</P>
<UL>
<LI>wsdl_service_name 'SomeName' 设定当前service的名字(说不是必须的)</LI>
<LI>wsdl_namespace 'http://xxx' 设定namespace 默扔为'urn:ActionWebService'</LI>
<LI>web_service_api XXXApi 关联controller和他的api类,如果是符合命名规范,可以省略此指令</LI>
<LI>web_service_scaffold :somemethod 生成一个action能提供一个直接体验webservice的web界面,方便调试</LI>
<LI>web_service_dispatching_mode :layered | :delegated 设置dispatch mode, 省略表示 direct</LI></UL>
<H3>ActionWebService::Struct　使用</H3>
<P>这个类是用来帮助组织webservice可以识别的数据对象(DTO) 通过member方法来定义域和类型, 例子如下</P><PRE><CODE>class Person &lt; ActionWebService::Struct
    member :id, :int
    member :name, :string
end
</CODE></PRE>
<H3>webservice 分派方式</H3>
<P>通过web_service_dispatching_mode来申明分派方式, 注意不要写错了,写错了rails不报错,而且能给出无用但是合付xml文法的wsdl(一个没有任何方法的服务)</P>
<UL>
<LI>直接分配, 实现写在生成的controller中</LI>
<LI>
<P>layered dispatching 单独实现ActionWebService::Base的子类(放在apis目录下),定义public方法即可 </P>
<P>class ProductService &lt; ActionWebService::Base<BR>web_service_api ProductApi<BR>def find_all_products Product.find(:all).map{ |product| product.id end }<BR>def find_product_by_id(id) Product.find(id) end end </P></LI>
<LI>
<P>delegated dispatching</P></LI></UL>
<P>申明web_service_dispatching_mode以后,使用web_service :my_serv_name ,XXXXService.new(相对静态生成) 或者web_service :my_name {XXXService.new} 作延迟加载(可以在block中访问controller的变量了,同时也可以对my_name这个新添加的service_action作filter了)</P>
<P>???具体的三种方法我还没有感觉出有什么特别用处,目前对webservice认识还不足</P>
<H3>对webservice作 AOP 拦截</H3>
<P>支持 before_invocation和after_invocation (:only 和 :except 语法), 如果before_invocation返回false或者抛出异常或者直接return[false,"reason"], 调用都会中止</P>
<P>拦截方法接收两个参数, 一个method_name, 一个method参数数组) 和 其他拦截类似, 还可以传入block(|sourceobj,m_name,m_params|) 和实现拦截类(只要实现interceptor(m_name,m_params)来拦截</P>
<H3>webservice的测试</H3>
<P>默认已经生成好functional test</P>
<P>使用invoke来调用直接的servcie, 类似还有</P>
<P>invoke_layered(service_name, method_name, *args) invoke_delegated(service_name, method_name, *args)</P>
<H3>url对应关系</H3>
<P>SOAP</P>
<P>默认controller有一个wsdl的action可以得到wsdl描述文件,通过service.wsdl也一样可以得到(routes.rb创建的),通过这个wsdl就可以得到所有的url信息了</P>
<P>XML-PRC (没有wsdl的情况) (其实在wsdl下方可以看到这些url,一样是对XML-PRC有效的</P>
<P>layered dispatching</P>
<P>http://host/PATH/TO/CONTROLLER/api</P>
<P>delegated dispatching</P>
<P>http://host/PATH/TO/CONTROLLER/SERVICE_NAME</P>
<P>这里的SERVICE_NAME就是web_service()方法的第一个参数</P>
<H3>调用外部webservice</H3>
<P>在rails controller内部,通过 web_client_api :product,:soap, 'http://url' 就可以创建一个product方法代理服务,使用product.xxx即可</P>
<P>还可以使用ActionWebService::Client::Soap或者ActionWebService::Client::XmlRpc来基于Api定义的类直接创建对象 shop =ActionWebService::Client::Soap.new(ProductApi,"http://my.app.com/backend/api") product = shop.find_product_by_id(5)</P>
<P>如果服务和rails关联不紧密, 就使用ruby的webservice包,不必用rails的了</P><img src ="http://www.blogjava.net/cap/aggbug/29080.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cap/" target="_blank">tech.cap</a> 2006-01-24 10:49 <a href="http://www.blogjava.net/cap/articles/rails_webservice.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>rails笔记: 工具</title><link>http://www.blogjava.net/cap/articles/rails_util.html</link><dc:creator>tech.cap</dc:creator><author>tech.cap</author><pubDate>Tue, 24 Jan 2006 02:44:00 GMT</pubDate><guid>http://www.blogjava.net/cap/articles/rails_util.html</guid><wfw:comment>http://www.blogjava.net/cap/comments/29078.html</wfw:comment><comments>http://www.blogjava.net/cap/articles/rails_util.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/cap/comments/commentRss/29078.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cap/services/trackbacks/29078.html</trackback:ping><description><![CDATA[<H3>工具</H3>
<H3>script</H3>
<P>script/server -e development | test | production 切换运行环境 script/generate 支持 Admin::Book 这样的语法, 方便创建子空间 script/console 提供一个包含了当前工程环境的irb(注意运行环境,dev,test,prod)</P>
<H3>(以下两个目前在win32下面有bug,不能运行)</H3>
<P>script/profiler "some invokcation" # 分析某操作的性能,自动导入rails环境, 然后执行语句, 打印分析结果 script/benchmark 10 "invoke1" "invoke2" 可以通过执行多次,比较两个操作的性能, </P>
<H3>调试</H3>
<P>有一个debug方法可以在rhtml中打印对象(html方式)</P>
<P>断点支持 : 在需要断点的地方调用breakpoint()方法,然后使用 ruby script/breakpointer 连接, 当遇到breakpoint以后会自动连接然后出现irb的环境, 退出irb以后程序会自动运行</P>
<H3>配置</H3>
<P>database.yml支持_alias_的比如</P><PRE><CODE>development: development_sqlite
development_mysql:
adapter: mysql
database: depot_development
host: localhost
username:
password:
development_sqlite:
adapter: sqlite
dbfile: my_db
</CODE></PRE>
<P>还比如这样, _typo用到的语法_</P><PRE><CODE>login: &login
  adapter: mysql
  host: localhost
  username: root
  password: 

development:
  database: typo_dev
  <<: *login

test:
  database: typo_tests
  <<: *login

production:
  database: typo
  <<: *login
</CODE></PRE>
<H3>rake</H3>
<P>rake clone_structure_to_test #copy dev的数据库结构到test</P>
<P>rake test_units #所有的unit</P>
<P>rake test_functional #所有的functional</P>
<P>rake #所有的测试</P>
<P>ruby xxxxx.rb -n name # 测试某方法</P>
<P>ruby xxxxx.rb -n /regex/ # 正则匹配测试方法</P>
<P>持续集成,因为不熟悉, 暂时不写</P>
<P>rake stats # 统计测试</P>
<P>ruby -rcoverage test/functional/xxx_controller_test.rb #统计覆盖率</P>
<H4>convention</H4>
<P>表对应关系: Person -> people table -> person_id join table: tablenamea_tablenameb 为table名(按照字母顺序排列)</P>
<P>字段 xxx_on xxx_at 自动维护date 和 datetime</P><img src ="http://www.blogjava.net/cap/aggbug/29078.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cap/" target="_blank">tech.cap</a> 2006-01-24 10:44 <a href="http://www.blogjava.net/cap/articles/rails_util.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>rails笔记: view</title><link>http://www.blogjava.net/cap/articles/rails_view.html</link><dc:creator>tech.cap</dc:creator><author>tech.cap</author><pubDate>Tue, 24 Jan 2006 02:44:00 GMT</pubDate><guid>http://www.blogjava.net/cap/articles/rails_view.html</guid><wfw:comment>http://www.blogjava.net/cap/comments/29079.html</wfw:comment><comments>http://www.blogjava.net/cap/articles/rails_view.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/cap/comments/commentRss/29079.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cap/services/trackbacks/29079.html</trackback:ping><description><![CDATA[<H4>view</H4>
<P>view分为 builder模式和rhtml模式</P>
<P>builder模式例子如下,rails自带</P><PRE><CODE>xml.div(:class =&gt; "productlist") do
    xml.timestamp(Time.now)
        @products.each do |product|
            xml.product do
            xml.productname(product.title)
            xml.price(product.price, :currency =&gt; "USD")
        end
    end
end
</CODE></PRE>
<P>如果标记和内置方法重复,可以使用something!()来产生标记</P>
<P>rhtml类似于jsp, &lt;%=...%&gt;输出 &lt;%...%&gt;纯标记 &lt;%...-%&gt;自动删除自己的空行</P>
<P>h方法提供了html encoding</P>
<H3>Helper</H3>
<P>XXXController都会对应一个 XXXHelper(xxx_helper.rb)来写帮助方法, 这些方法在rhtml中都可见</P>
<P>要实现跨controller调用,可以</P>
<UL>
<LI>在application_helper.rb中写方法</LI>
<LI>在controller中通过helper :some_helper.rb导入</LI>
<LI>在controller中通过helper SomeHelper导入(类已经栽入的情况下)</LI></UL>
<P>通过helper_method 还可以把controller中的方法变为helper,但是这样作不推荐,破坏封装</P>
<P>rails内置了很多helper可用,比如各种单位的换算helper(对view特别有意义),详情查阅rdoc 特别的helper有</P>
<UL>
<LI>debug 把对象以html可见的方式打印在页面,方便调试</LI>
<LI>simeple_format 简单的格式化string,添加
<P>段落和<BR></P></LI>
<LI>excerpt 给字符两边添加... "...Some words fo..."</LI>
<LI>highlight(string,highword) 添加<STRONG class=highlight>...</STRONG></LI>
<LI>truncate(sring,maxlen) 给字符右边添加... "hello this is my..."</LI>
<LI>pluralize(num,word) 自动添加复数形式 1 person -&gt; 2 people</LI>
<LI>markdown 文章filter</LI>
<LI>texttile 文章filter</LI></UL>
<H3>处理链接</H3>
<P>link_to "xxxx",{hash},{hash}</P>
<P>第一个是链接文字,第二个hash和url_for的一样,第三个hash是用户自己在<CODE>&lt;a&gt;</CODE>中添加的标签,比如</P><PRE><CODE>&lt;%= link_to "Delete", { :controller =&gt; "admin",
                        :action =&gt; "delete",
                        :id =&gt; @product
                        },
                        { :class =&gt; "redlink",
                        :confirm =&gt; "Are you sure?"
                        }
%&gt;
</CODE></PRE>
<P>button_to 的参数和link_to一样, 产生一个button来跳转(安全的POST协议),但是button_to要单独在一个form里面,html不允许嵌套form</P>
<P>link_to_unless_current 自动识别当前链接, 如果当前url和链接一样, 就只产生文本</P>
<P>image_tag嵌套进入link_to</P>
<P>mail_to　有一个:encode=&gt;"javascript" 可以实现把email地址编码防spider</P>
<P>stylesheet_link_tag 添加css</P>
<P>auto_descovery_link_tag 自动申明rss</P>
<P>默认所有的image在/images css在/stylesheets,除非显示使用/xxx来指定根路径另外可以修改 ActionController::Base.asset_host = "http://media.my.url/assets" #共同的prefix 这样可以实现静态内容专门在一一起</P>
<H3>分页</H3>
<P>分页涉及到controller和view两个部分在controller中 Paginator对象, 专门负责分页 ,默认分页为10个一页</P>
<P>@user_pages, @users=paginate(:users,:order_by =&gt;'name')</P>
<P>在页面里面使用&lt;%=pagination_links(@user_pages)%&gt; 会自动产生页面链接</P>
<P>详情查阅RDoc</P>
<H3>表单处理</H3>
<P>本质上rails是简单的把上行的参数设置为层次分明的hash(aaa.bbb.ccc 映射为 aaa[bbb][ccc]), 这样来和activerecord自动交换数据,但是也可以直接读取之</P>
<P>param[:xx][:yy] rails推荐以symbol来读取(string也可以)</P>
<P>大多数输入helper前两个参数分部是:variable和:attribute ,最后一个是增加的html options 例子如下 &lt;%=xxx_field :variable,:attribute,options_hash%&gt;</P>
<P><STRONG>注意</STRONG> update_attribute会自动调用save方法</P>
<H3>表单处理 FormHelper中的方法(和Model绑定)</H3>
<H4>普通输入框</H4>
<UL>
<LI>text_field</LI>
<LI>hidden_field </LI>
<LI>password_field </LI>
<LI>
<P>text_area</P></LI>
<LI>
<P>radio_button 第三个参数是tag_value,当他和value相等的时候显示为selected</P></LI>
<LI>check_box value必须为true/false或者转换为int的值(非0为true) 倒数两个参数为on_value和off_value 为打开/关闭时提交的值, 默认为"1","0"</LI></UL>
<H4>列表</H4>
<P>select(:variable, :attribute,choices,options,html_options)</P>
<P>choice参数为enumerable,当enumerable的元素为[a,b]这样的长度为2数组的时候,数组第一个元素为value,第二个为key</P>
<P>collection_select(:variable,:attribute,choices,:inner_attr_for_key,:inner_attr_for_value)专门用来列表集合</P>
<H4>分组列表</H4>
<P>暂时不看 @@@</P>
<H4>日期输入框</H4>
<P>date_select(:variable,:attribute,options) datetime_select(..)是处理和model相关的日期</P>
<P>select_xxx 是处理普通日期(直接通过params访问)</P>
<P>没有怎么看明白有什么用处</P>
<H3>上传文件</H3>
<P>form中需要添加:multipart=&gt;true,本质上然后通过对应的参数获得数据(一个StringIO) 通过read即可读取全面内容到string中然后就相当于拿到文件内容处理了</P>
<H3>错误处理</H3>
<P>上门的helper参数都会自动读取对于field的错误信息(errors.on(field)),发现错误以后会自动产生一个class=fieldWithErrors的div,通过css可以控制显示. 如果需要直接读取某个field的错误信息, 可以使用<CODE>&lt;%= error_message_on(:bean,:field)%&gt;</CODE>,读取所有错误使用<CODE>&lt;%= error_messages_for(:bean)%&gt;</CODE></P>
<H3>FormTagHelper 和Model无关的方法</H3>
<P>FormTagHelper中的方法都比Form中多一个_tag结尾</P>
<UL>
<LI>start_form_tag和end_form_tag form开始结束</LI>
<LI>submit_tag 提交按钮</LI></UL>
<P>其他FormHelper中的帮助方法都有一个对应的_tag版本,参数接受 :name,value,html_options</P>
<P>例子如下</P>
<P>&lt;%=text_field_tag(:arg1,@params[:args],:size=&gt;3)%&gt;</P>
<H3>layout技术 (sitemesh式的模板)</H3>
<P>layout模板中可以访问所有对应页面中能访问的对象,还多有@content_for_layout参数来指向整个页面输出的内容</P>
<P>自动查找: 一个xxx_controller对应了一个在layout下同名的xxx.rhtml/rxml作为他的layout, </P>
<P>显式申明:</P>
<OL>
<LI>在controller中通过layout申明一个string, 支持:except和:only参数(过滤:action名)</LI>
<LI>声明为nil表示关闭layout</LI>
<LI>申明为一个:symbol表示通过当前类symbol方法获取string名</LI></OL>
<P>string对应了 string.rhtml或者string.rxml</P>
<P>直接render: 直接调用render(:layout=&gt;"layouts/xxxx")可以使用layout,或者使用:layout=&gt;false关闭layout</P>
<H3>partial page template 页面片断</H3>
<P>页面片断的页面必须以_开头命名,放在和controller相同路径下,使用render(:partial=&gt;"xxx",:object=&gt;@newinst)访问</P>
<P>文件名必须ruby变量规范和文件名普通规范,通过:object注入的参数通过文件名访问,比如_myname.rhtml就得到了myname这个参数,要追加其他参数,使用:locals={...}</P>
<P>如果忽略:object参数表示把@xxx 自动传入 _xxx.rhtml中的xxx变量, 也就是当@instance和partial中的变量名相同时候, 可以省略:object</P>
<H3>使用集合的partial</H3>
<P>通过 render(:partial=&gt;"xxx" ,:collection=&gt;xxx) 可以指定一个集合, 集合中的元素会传给partial content作为主元素,同时还会添加一个xxx_counter来作为index, 此时使用:spacer_template=&gt;"yyy"还可以指定两个元素之间使用的空白template</P>
<P>如果:partial=&gt;"xxx/yyy" 指定的名字含有/ ,那么rails默认为这个路径从/app/views开始,通过这个方法可以做到多个controller使用一个partial conetent</P>
<H3>components 组件</H3>
<P>在页面中通过render_component使用 render_component(:controller=&gt;'xxx',:action=&gt;'fdas')</P>
<P>需要注意的是要注意避免循环引用,所以一般用来作component的action最好使用render(:layout=&gt;false...)或者在class中申明 layout "xxx","except=&gt;:xxxx</P>
<H3>独立的component组件</H3>
<UL>
<LI>必须方在components/xxx下面</LI>
<LI>使用XXX::MyController申明 必须在model xxx中</LI>
<LI>类开头申明uses_component_template_root</LI>
<LI>使用方式为&lt;%%=render_component):controller=&gt;'xxx/my',:action=&gt;'yyy') %&gt;</LI></UL><img src ="http://www.blogjava.net/cap/aggbug/29079.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cap/" target="_blank">tech.cap</a> 2006-01-24 10:44 <a href="http://www.blogjava.net/cap/articles/rails_view.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>rails笔记: 测试</title><link>http://www.blogjava.net/cap/articles/rails_test.html</link><dc:creator>tech.cap</dc:creator><author>tech.cap</author><pubDate>Tue, 24 Jan 2006 02:39:00 GMT</pubDate><guid>http://www.blogjava.net/cap/articles/rails_test.html</guid><wfw:comment>http://www.blogjava.net/cap/comments/29077.html</wfw:comment><comments>http://www.blogjava.net/cap/articles/rails_test.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/cap/comments/commentRss/29077.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cap/services/trackbacks/29077.html</trackback:ping><description><![CDATA[<H3>rails 测试</H3>
<UL>
<LI>model的测试 => unit test</LI>
<LI>controller的测试 => functional</LI></UL>
<H3>unit test</H3>
<P>默认所有的Model都产生在 test/unit/xxxx_test.rb</P>
<P>rake clone_structure_to_test #可以把dev中的数据结果自动同步到test的数据中</P>
<H4>fixture</H4>
<P>数据库不好测试,因为数据是长时间有效的,很难重现,所有得采用类似csv导入的技术(我目前用的),而fixture 就是类似csv文件的东西,但是和rails内部结合更紧密,在<STRONG>Conveniences</STRONG>的作用下能自动控制数据初始状态,这也是rails的一次成功尝试(在java中未看到这样的设施, 倒是有蹩脚的dbunit...xml啊xml)</P>
<P>fixture的结构是一个yaml格式的文件</P><PRE><CODE>version_control_book:
  id: 1
  title: Pragmatic Version Control
  description: How to use version control
  image_url: http://.../sk_svn_small.jpg
  price: 29.95
  date_available: 2005-01-26 00:00:00
entity_name:
  id: 2
  title: Pragmatic Project Automation
  description: How to automate your project
  image_url: http://.../sk_auto_small.jpg
  price: 29.95
  date_available: <%=some_method%>
</CODE></PRE>
<P>抬头的标记version_control_book标明了该记录的名字, 缩进的部分一共组成了一个hash做为一条记录, fixture支持使用erb指令<%%>来进行一些动态生成</P>
<P>在所有的的testcase中 都可以直接使用fixture :<STRONG>fixnames</STRONG>指令来导入数据到对象中, 十分方便,访问方式为<STRONG>fixname</STRONG>(:entityname),</P>
<P>这里的对应关系是Product.rb => products(db teable) => products.yml=>fixture :products</P>
<H4>test_helper.rb</H4>
<P>test_helper负责test环境中的初始化, 在这里进行的修改所有test都可以看到, 比如添加新的assert方法</P>
<HR>

<H3>rails (升级的问题)</H3>
<P>1 fixture升级</P>
<UL>
<LI>老rails中 fixture中的记录会自动创建@var 实例变量到对象中,可以直接用@version_control_book访问</LI>
<LI>新的采用products(:ersion_control_book),不自动创建实例变量</LI></UL>
<P>2 事务管理升级</P>
<UL>
<LI>老rails默认没有事务, 每个testcase都会重新生成数据,速度较慢</LI>
<LI>新的采用事务,每个testcase从新开始事务,结束以后ROLLBACK,避免删除数据以及重新导入(对于mysql需要使用支持事务的表类型,如innodb)</LI></UL>
<P>至于如何让新rails采用旧方式测试, 目前还没有这个的需求,觉得随着rails1.0流行,可能今后也不会用到</P>
<HR>

<H3>functional test (controller)</H3>
<P>controller的测试能力</P>
<UL>
<LI>检查rails内部对象: session, flash, cookies等</LI>
<LI>模仿浏览器作操作: get post put delete head</LI>
<LI>检查服务端响应: assert_redirected_to assert_response</LI>
<LI>检查view: assert_template</LI>
<LI>解析view: assert_tag </LI></UL>
<P>功能虽然强大,但是使用的时候应采取KISS原则写测试,有必要的才测</P>
<H3>Mock Object</H3>
<P>在ruby下,mock很简单, 如下步骤即可</P><PRE><CODE>require 'models/some_model'
class SomeModel
   def mockedmethod(arg)
    ....
   end
end
</CODE></PRE>
<P>这样自动覆盖了原来的方法, 起到mock的效果</P>
<H3>测试驱动</H3>
<UL>
<LI>一定要先写测试</LI>
<LI>有bug的时候,先写测试,能测试出bug以后再修改</LI>
<LI>如果一个类难测试,那么说明设计有问题, 测试驱动设计</LI></UL>
<H3>rake的工具支持</H3>
<P>..见rails工具</P>
<H3>性能测试</H3>
<P>首先通过 fixture 的动态功能批量生成数据</P><PRE><CODE><% for i in 1..100 %>
order_<%= i %>:
  id: <%= i %>
  name: Fred
  email: fred@flintstones.com
  address: 123 Rockpile Circle
  pay_type: check
<% end %>
</CODE></PRE>
<P>在test方法中,使用 Benchmark::realltime do xxx end来跑测试即可</P><img src ="http://www.blogjava.net/cap/aggbug/29077.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cap/" target="_blank">tech.cap</a> 2006-01-24 10:39 <a href="http://www.blogjava.net/cap/articles/rails_test.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>rails笔记 安全和其他</title><link>http://www.blogjava.net/cap/articles/rails_maintain.html</link><dc:creator>tech.cap</dc:creator><author>tech.cap</author><pubDate>Tue, 24 Jan 2006 02:37:00 GMT</pubDate><guid>http://www.blogjava.net/cap/articles/rails_maintain.html</guid><wfw:comment>http://www.blogjava.net/cap/comments/29076.html</wfw:comment><comments>http://www.blogjava.net/cap/articles/rails_maintain.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/cap/comments/commentRss/29076.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cap/services/trackbacks/29076.html</trackback:ping><description><![CDATA[<h3>SQL injection</h3>

<p>默认的rails的 find(xxx) 是过滤了sql 字符串的
但是自己构造condition limit count sql等需要转义,务必使用? 或者:name来传参数,不要直接构造sql</p>

<h3>性能配置 fastcgi</h3>

<p>timout注意: fastcgi的timeout是强制回收的, 如果一个请求超过timeout会被kill掉 返回500,所以长时间的任务要设置大一点</p>

<p>设置environment
WEBrick
./script/server --environment=production
Apache/CGI
SetEnv RAILS_ENV production</p>

<p>Apache/FastCGI
-initial-env RAILS_ENV product</p>

<p>lighttpd/FastCGI
"bin-environment" =>("RAILS_ENV"=>"production")</p>

<p>使用link -s 来更换版本的主意真是太COOL了, rails提供的不干扰用户的杀进程法
killall -USR1 dispatch.fcgi</p>

<h3>维护</h3>

<h4>session清理</h4>

<p>太多session可能导致严重的文件系统问题和数据库lock</p>

<ul>
<li><p>清除12小时内没有访问的session
find /tmp/ -name 'ruby_sess*' -ctime +12h -delete</p></li>
<li><p>从数据库中清理session
RAILS_ENV=production ./script/runner 'ActiveRecord::Base.connection.delete("DELETE FROM sessions WHERE updated_at < now() - 12*3600")'</p></li>
</ul>

<h3>测试性能</h3>

<ul>
<li>ab工具 </li>
<li>curl/wget</li>
<li>siege http://www.joedog.org/siege/</li>
</ul><img src ="http://www.blogjava.net/cap/aggbug/29076.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cap/" target="_blank">tech.cap</a> 2006-01-24 10:37 <a href="http://www.blogjava.net/cap/articles/rails_maintain.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>rails笔记 activerecord 关系</title><link>http://www.blogjava.net/cap/articles/rails_relation.html</link><dc:creator>tech.cap</dc:creator><author>tech.cap</author><pubDate>Tue, 24 Jan 2006 02:35:00 GMT</pubDate><guid>http://www.blogjava.net/cap/articles/rails_relation.html</guid><wfw:comment>http://www.blogjava.net/cap/comments/29074.html</wfw:comment><comments>http://www.blogjava.net/cap/articles/rails_relation.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/cap/comments/commentRss/29074.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cap/services/trackbacks/29074.html</trackback:ping><description><![CDATA[<H3>actuverecord realation</H3>
<H4>convention 约定</H4>
<P>对应关系: </P>
<UL>
<LI>普通对象: Person -> people table -> person_id</LI>
<LI>join_table: tablenamea_tablenameb 为table名(按照字母顺序排列)</LI></UL>
<H3>关系</H3>
<P>注意belongs_to对应的表<STRONG>必须有外键</STRONG>, rails认为一个表belongs_to他外键引用的表</P>
<UL>
<LI>
<P>one-to-one</P><PRE><CODE>class Order < ActiveRecord::Base
    has_one :invoices
    . . .
class Invoice < ActiveRecord::Base
    belongs_to :order
    . . .
</CODE></PRE></LI>
<LI>
<P>one-to-many: belongs_to</P><PRE><CODE>class Order < ActiveRecord::Base
    has_many :line_items
    . . .
class LineItem < ActiveRecord::Base
    belongs_to :order 
    . . .
</CODE></PRE></LI>
<LI>
<P>many-to-many </P><PRE><CODE>class Product < ActiveRecord::Base
    has_and_belongs_to_many :categories
    . . .
class Category < ActiveRecord::Base
    has_and_belongs_to_many :products
    . . .
</CODE></PRE></LI></UL>
<H3>one-to-one</H3>
<P>下面两个操作在逻辑在等价</P>
<UL>
<LI>order.invoce=invo</LI>
<LI>auto invo.save</LI></UL>
<P><STRONG>注意1</STRONG></P>
<P>当把子对象赋给父的时候(order.invoce=invo),子对象如果未存储会自动存储(auto invo.save),但是把父赋给子的时候(invo.order=ord)不会自动存储, 因为此时存储order没有意义, 对应关系是保存在invoces表中的,不过在稍后的invo.save中, 如果父没有创建,还是会自动创建的</P>
<P><STRONG>注意2</STRONG> 当使用上门的自动存储的时候,由于调用的save方法,所以即使出错也不会报告,所以最好还是自己来invo.save!(比如invo有内部验证的时候),然后在建立连接关系</P>
<P>belongs_to扩展参数，如下例子</P><PRE><CODE>class LineItem < ActiveRecord::Base
    belongs_to :paid_order,
        :class_name => "Order",
        :foreign_key => "order_id",
        :conditions => "paid_on is not null"
end
</CODE></PRE>
<UL>
<LI>:class_name :对应的类</LI>
<LI>:foreign_key :指定外键名</LI>
<LI>:conditions : 用来筛选外键表中的记录(此时foreign_key不能唯一确定记录,或者有不需要的记录)</LI></UL>
<P>has_one 也有类似的功能, 多了两个</P>
<UL>
<LI>
<P>:denpendent </P>
<P>删除父亲的时候自动删除孤儿(参见hibernate) ???实现失败,加上不加上都不会删除,数据库报外键约束错误</P></LI>
<LI>
<P>:order </P>
<P>排序,虽然是has_one, 但是这个one可以通过排序动态得到(比如最高分的选手)</P></LI></UL>
<H4>hash_one and belongs_to 增加的方法</H4>
<P>belongs_to和has_one都会给对象添加下面的方法，假设对应的是product对象</P>
<UL>
<LI>
<P>product(force_reload=false)</P>
<P>Return the associated product (or nil if no associated product exists).The result is cached, and the database will not be queried again if this order had previously been fetched unless true is passed as a parameter.</P></LI>
<LI>
<P>product=(obj)</P>
<P>Associate this line item with the given product, setting the foreign key in this line item to the product’s primary key. If the product has not been saved, it will be when the line item is saved, and the keys will be linked at that time.</P></LI>
<LI>
<P>build_product(attributes={})</P>
<P>Construct a new product object, initialized using the given attributes.This line item will be linked to it. The product will not yet have been saved.</P></LI>
<LI>
<P>create_product(attributes={})</P>
<P>Build a new product object, link this line item to it, and save the product.</P></LI></UL>
<H3>one-to-many</H3>
<H4>has_many 扩展声明(未列出的与has_one相同)</H4>
<UL>
<LI>
<P>:dependent</P>
<P>会把关联他的对象一个一个load出来调用它们的destory</P></LI>
<LI>
<P>:exclusively_dependent</P>
<P>一个sql删除:denpent需要删除的对象,但是需要保证这些对象真是孤儿而且没有callback</P></LI>
<LI>
<P>:finder_sql</P>
<P>构建动态的子对象群</P></LI>
<LI>
<P>:counter_sql </P>
<P>finder_sql调用前call他得到数量, 如果不给出,会自动通过finder_sql添加"count(*)"来计算</P></LI>
<LI>
<P>:order </P>
<P>排序</P></LI></UL>
<H4>has_many 增加的方法</H4>
<UL>
<LI>
<P>orders(force_reload=false)</P>
<P>Returns an array of orders associated with this customer (which may be empty if there are none). The result is cached, and the database will not be queried again if orders had previously been fetched unless true is passed as a parameter.</P></LI>
<LI>
<P>orders <<ORDER< p> 
<P>Adds order to the list of orders associated with this customer.orders.push(order1, ...) Adds one or more order objects to the list of orders associated with this customer. concat( ) is an alias for this method.</P></LI>
<LI>
<P>orders.delete(order1, ...)</P>
<P>Deletes one or more order objects from the list of orders associated with this customer. This does not delete the order objects from the database—it simply sets their customer_id foreign keys to null, breaking their association.</P></LI>
<LI>
<P>orders.clear</P>
<P>Disassociates all orders from this customer. Like delete( ), this breaks the association but deletes the orders from the database only if they were marked as :dependent.</P></LI>
<LI>
<P>orders.find(options...)</P>
<P>Issues a regular find( ) call, but the results are constrained only to return orders associated with this customer. Works with the id, the :all, and the :first forms.</P></LI>
<LI>
<P>orders.build(attributes={})</P>
<P>Constructs a new order object, initialized using the given attributes and linked to the customer. It is not saved.</P></LI>
<LI>
<P>orders.create(attributes={})</P>
<P>Constructs and save a new order object, initialized using the given attributes and linked to the customer.</P></LI></UL>
<H4>many-to-many</H4>
<P>在many-to-many中,必须有一个jiontable, 用来保存对应关系,表名的规则是按照字母顺序连接,如 table categories_products join table不能有id ,一方面不需要, 另外一方面会在自动include中覆盖父对象的id</P>
<P>join table额外保存的属性,按道理不应该额外保存属性,额外的属性越明显,就越说明此处的many-to-many关系设计得有问题,适当时候要抽象出一个新model来分别belongs_to两个父亲</P>
<H3>relation 中的延迟读取</H3>
<P>默认relation关系中都是延迟读取的,如果需要一次性遍历多个记录操作, 可以采用:include指令,下面例子中,include读取三个表只需要一个sql语句(采用left join,需要数据库支持) for post in Post.find(:all, :include => [:author, :comments]) puts "Post: #{post.title}" puts "Written by: #{post.author.name}" puts "Last comment on: #{post.comments.first.created_on}" end</P>
<P>同时要注意,有:include和:condition一起的时候,字段名要<STRONG>全名</STRONG>,如下 ost.find(:all, :conditions => "posts.title like '%ruby%'", :include => [:author, :comments]) # ...</P>
<H4>counter_cache</H4>
<P>belongs_to :xxx,:counter_cache=>true ,同时父对象表中需要如下的新字段 line_items_count int default 0,注意默认值必须为0(或者其他机制保证初始化为0),否则每次都是null 可以避免每次count都去数据库count(*),这里的cache会在数据增删的时候自动维护</P>
<P><STRONG>注意</STRONG> 以上的自动更新时, 直接给儿子们更新父亲时是不起作用的, 必须使用father.children(:refresh)才能更新但是使用father.children<< child就没有问题 ???</P><img src ="http://www.blogjava.net/cap/aggbug/29074.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cap/" target="_blank">tech.cap</a> 2006-01-24 10:35 <a href="http://www.blogjava.net/cap/articles/rails_relation.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>rails笔记 activerecord 2</title><link>http://www.blogjava.net/cap/articles/rails_activerecord_2.html</link><dc:creator>tech.cap</dc:creator><author>tech.cap</author><pubDate>Tue, 24 Jan 2006 02:32:00 GMT</pubDate><guid>http://www.blogjava.net/cap/articles/rails_activerecord_2.html</guid><wfw:comment>http://www.blogjava.net/cap/comments/29073.html</wfw:comment><comments>http://www.blogjava.net/cap/articles/rails_activerecord_2.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/cap/comments/commentRss/29073.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cap/services/trackbacks/29073.html</trackback:ping><description><![CDATA[<H2>扩展的activerecord</H2>
<H3>act_as_list</H3>
<P>act_as_list : one-to-many对象关系是通过list(默认是set)来完成, 既然有list, 就有了排序,首先对应表中必须有position(自动默认)字段,用来标示排序,如果不默认就得显示通过:order指定</P><PRE><CODE>class Parent < ActiveRecord::Base
    has_many :children, :order => :position
end
class Child < ActiveRecord::Base
    belongs_to :parent
    acts_as_list :scope => :parent_id
end
</CODE></PRE>
<P>这里的:scope=> :parent_id 说明了list是相对于单个parent_id的, 否则就是所有的product共用一个排序</P>
<P>对应关系建立以后, child对象会增加如下move_lower,move_higher, move_to_top,move_to_bottom,还会有first?和last?方法</P>
<P><STRONG>注意</STRONG> 对对象作了排序操作以后, 只是修改数据库中的记录, parent对象还不会在list中调整, 必须调用parent.reload</P>
<H3>act_as_tree</H3>
<P>和act_as_list类似,例子如下</P><PRE><CODE>class Category < ActiveRecord::Base
    acts_as_tree :order=> "name"
end
</CODE></PRE>
<P>实际上这个代码等同于===></P><PRE><CODE>class Category < ActiveRecord::Base
    belongs_to :parent,
        :class_name => "Category"
    has_many :children,
        :class_name => "Category",
        :foreign_key => "parent_id",
        :order => "name",
        :dependent => true
end
</CODE></PRE>
<P>通过children操作, 如有必要,可以通过:counter_cache=> true 并添加children_count来优化子对象数量获取</P>
<H3>aggregation hibernate的 component</H3>
<P>简单的说就是把几个字段映射为一个字段,方法为新建立一个对象 class Name attr_reader :first,:last end 然后在主对象中使用composed_of </P><PRE><CODE>class XX < ActiveRecord::Base
    composed_of :name, :class_name=>Name,:mapping=>
    [
    [:first_name,:first]
    [:last_name,:last]
    ]
end
</CODE></PRE>
<P>其中:class_name和mapping可省略 ???</P>
<P>aggregation还可以用来聚合单一字段,下面的类就聚合了一个字段(以,号分隔)</P><PRE><CODE>class XxYy
    attr_reader :list
    def initialize(db_str)
      @list=db.str.split(/,/)
    end
    def xx_yy
      @list.join(',')
    end
end
</CODE></PRE>
<P>然后主类需要用composed_of 标明映射的字段xx_yy</P><PRE><CODE>class Main
    composed_of :xx_yy
end
</CODE></PRE>
<P><STRONG>注意</STRONG> aggregation对象都是value object, 你不能修改他里面的值,即使修改了,rails也不会回写它们到数据库中,唯一修改的办法是新建另外一个component然后赋给主对象</P>
<H3>单表继承</H3>
<P>只适用于大部分值都在父类的对象结构, 对于abstact方法的对象结构(子类直接差别非常大),可能不适合</P>
<UL>
<LI>表中必须有所有子对象的属性, 不能有重合,冲突,所有 父类可能有很多允许NULL的字段</LI>
<LI>表中有个type字段标示类型 ,根对象的type是"",</LI>
<LI>子对象的类型实际上父对象也有,所有这里的对象不严格了, rail说是权宜之计</LI>
<LI>由于type对象和ruby内置的type属性冲突,所有要用model[:type]来访问 </LI></UL>
<H3>验证 validation模块</H3>
<H4>底层有三个方法</H4>
<UL>
<LI>validate 任何保存操作都调用</LI>
<LI>validate_on_create 创建对象的时候调用 通过new_record?区分</LI>
<LI>validate_on_update 更新对象的时候调用 </LI></UL>
<P>实现这三个方法就可以自动使用validate, 手动调用的方法是valid?()</P>
<P>validate就是简单的判断, 然后再errors.add(:xxxx,"readon")就可以,例子如下</P><PRE><CODE>class User < ActiveRecord::Base
    def validate
        unless name && name =~ /^\w+$/
            errors.add(:name, "is missing or invalid")
        end
    end
    def validate_on_create
        if self.find_by_name(name)
            errors.add(:name, "is already being used")
        end
    end
end
</CODE></PRE>
<P><STRONG>提示</STRONG> rails增加了一个blank?方法 可以判断string为nil或""</P>
<H4>高层的帮助指令(很多)</H4>
<P>大部分指令都支持:on和:message参数, </P>
<UL>
<LI>:on标示何时进行验证</LI>
<LI>:message标示验证失败的帮助信息(可格式化)</LI></UL>
<P>失败以后,controller会自动重新显示form,然后page可以通过调用error_messages_for()来显示错误消息</P>
<P>指令列表如下</P>
<UL>
<LI>validates_acceptance_of 用于检查checkbox, 检查对应参数的值是否为"1"</LI>
<LI>validates_asscociated 调用指派model自己的validate, 此方法指派的属性必须也是一个ActiveRecord的Model, 这个方法需要注意不要形成循环验证</LI>
<LI>validates_confirmation_of 用来检查如同password的带confirm的输入,同时检查xxx字段和xxx_confirmation是否一样(约定)</LI>
<LI>validates_each {|model,attr,value| ...} 一次检查多个字段,需要传入一个block, :allow_nil参数如果为true(默认false), nil就不会被传入进来</LI>
<LI>validates_exclusion_of attr....,:in=> enum[] 验证对应的参数不在给的列表内(支持include?的都行)</LI>
<LI>validates_inclusion_of 和上门相反</LI>
<LI>validates_format_of :with=>/..../ 正则匹配</LI>
<LI>validates_length_of 验证长度 有:maximum :minimum :in可用, :message还有三个扩展:too_long :too_short :wrong_length对应细化的错误信息(格式化支持%d标识要求的数值)</LI>
<LI>validates_numericality_of 验证数字，　支持:only_integer（支持负数）</LI>
<LI>validates_presence_of 验证非空</LI>
<LI>validates_uniqueness_of 验证唯一 :scope=>"xx" 指定的字段可以作为范围(在同一个xx中唯一)</LI></UL>
<H3>Callbacks</H3>
<P>一共16个callback, 其中14个如下图调用 (可以看出没有严格嵌套)</P>
<P><IMG alt=callback调用图 src="/cap/admin/callback.gif"></P>
<P>另外两个特殊的callback是after_find after_initialize </P>
<P>callback的注册可以通过</P>
<UL>
<LI>直接在类中写方法,如def before_create end</LI>
<LI>通过类指令: before_cate :some_method</LI>
<LI>通过提供一个block {|model|...}</LI>
<LI>提供一个callback对象的实例,单独的Callback对象相当于专门把callback方法独立出来放入一个类中,使得callback可以在类之间复用(默认在/app/model下)</LI></UL>
<P>要统一注册callback ,可以修改ActiveRecord::Base根类</P>
<P><STRONG>注意</STRONG> 字段created_at created_on updated_at updated_on会自动被rails更新</P>
<H3>Observers 类似aop</H3>
<P>默认在app/model下一个典型的例子, 如果不使用observe类指令, 默认是根据类名推导出来为Audit类</P>
<P>class AuditObserver < ActiveRecord::Observer observe Order, Payment, Refund def after_save(model) model.logger.info("#{model.class.name} #{model.id} created") end end</P>
<P>最后要调用一下 XXXObserver.instance方法才能生效, 在rails中可以在controller中使用observe方法,不用单独instance了</P>
<H3>属性扩展 Advanced Attributes</H3>
<P>model.attributes可以取得属性hash,可以访问特殊名的字段</P>
<P>特殊的组合sql可能无法得到字段类型(mysql5.0好像没有这个问题了),导致rails以文本方式保存数据,这时我们可以通过调用read_attribute("xxx")和write_attribute("xxx")可以直接在底层操作数据,作一些转换来构造一个facade column来满足需求,例子如下</P><PRE><CODE>class ProductData < ActiveRecord::Base
    CUBITS_TO_INCHES = 18
    def length
        read_attribute("length") * CUBITS_TO_INCHES
    end
    def length=(inches)
        write_attribute("length", Float(inches) / CUBITS_TO_INCHES)
    end
end
</CODE></PRE>
<H3>注意事项</H3>
<UL>
<LI>为万无一失, 使用find_by_sql最好都把id给select出来, 不然不能保存</LI>
<LI>rails复写了ruby的id和hash功能，id是数据库ID(原来是object_id)，如果id相等则认为对象相等,所有没有保存的对象(没有id)会都相等,用来hash不安全</LI>
<LI>使用原始的connection: Order.connection.select_all(..) ,详情查阅文档</LI></UL>
<H3>magic column name</H3>
<UL>
<LI>created_at, created_on, updated_at, updated_on Automatically updated with the timestamp (_at form) or date (_on form) of a row’s creation or last update (page 267).</LI>
<LI>lock_version Rails will track row version numbers and perform optimistic locking if a table contains lock_version (page 213).</LI>
<LI>type Used by single table inheritance to track the type of a row (page 253).</LI>
<LI>id Default name of a table’s primary key column (page 197).</LI>
<LI>xxx_id Default name of a foreign key reference to table named with the singlua form of xxx (page 216).</LI>
<LI>xxx_count Maintains a counter cache for the child table xxx (page 235).</LI>
<LI>position The position of this row in a list if acts_as_list is used (page 243).</LI>
<LI>parent_id A reference to the id of this row’s parent if acts_as_tree is used (page</LI></UL><img src ="http://www.blogjava.net/cap/aggbug/29073.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cap/" target="_blank">tech.cap</a> 2006-01-24 10:32 <a href="http://www.blogjava.net/cap/articles/rails_activerecord_2.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>rails笔记 activerecord 1</title><link>http://www.blogjava.net/cap/articles/rails_activerecord_1.html</link><dc:creator>tech.cap</dc:creator><author>tech.cap</author><pubDate>Tue, 24 Jan 2006 02:19:00 GMT</pubDate><guid>http://www.blogjava.net/cap/articles/rails_activerecord_1.html</guid><wfw:comment>http://www.blogjava.net/cap/comments/29071.html</wfw:comment><comments>http://www.blogjava.net/cap/articles/rails_activerecord_1.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/cap/comments/commentRss/29071.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cap/services/trackbacks/29071.html</trackback:ping><description><![CDATA[<H2>rails笔记 activerecord</H2>
<P><STRONG>修改model的属性(如果是性质变化)以后最好重启动,避免奇怪的错误</STRONG></P>
<H3>自动类型转换:</H3>
<UL>
<LI>int, integer =&gt; Fixnum 
<LI>decimal, numeric =&gt; Float 
<LI>clob, blob, text =&gt; String 
<LI>interval, date =&gt; Date 
<LI>float, double =&gt; FLoat 
<LI>char, varchar, string =&gt; String 
<LI>datetime, time =&gt; Time 
<LI>boolean =&gt; 字面值</LI></UL>
<P><STRONG>注意</STRONG></P>
<P>1 decimal到ruby的float中运算以后精度可能_无法保证_(使用放大以后的integer代替) 2 数据表示boolean的特殊之处: Boolean处理 modelobj.name1? 来访问rails的boolean, (否则是ruby内置的低能boolean) 对比</P>
<UL>
<LI>rails: 0 ,"0","f","false","" ,nil, false <STRONG>都是</STRONG>false,其他为true 
<LI>ruby: nil,false是false, <STRONG>其他为true</STRONG>_ 若要定制boolean可以自己写一个name1?的方法</LI></UL>
<H3>record的mapping</H3>
<P>ModelClass.columns.map{|col|col.name} 查看column ModelClass.columns_hash 返回列信息hash</P>
<H3>访问数据</H3>
<P>使用object.property访问, 也可以使用object[:property]的方式访问(当和ruby保留字冲突时) 后者还可以用来覆盖默认行为,比如</P><PRE><CODE>class Account &lt; ActiveRecord::Base
    def balance=(value)
        raise BalanceTooLow if value &lt; MINIMUM_LEVEL
        self[:balance] = value
    end
end
</CODE></PRE>
<P>添加modelobj1.name1_before_type_cast可以得到原始的数据对象(未转换的)</P>
<P>serialize :last_five 可以使用YAML序列化任何对象到TEXT字段,但是这样只有识别YAML的程序可以读取</P>
<P>set_primary_key 'another_column' 可以让rails按照another_column来当id, 但是在对象级别访问还是使用id属性(此时映射到another_column)</P>
<P>使用establish_connection方法可以连接到不同于配置的数据库,方法中忽略的参数还是从配置读取默认值</P>
<H2>对象操作</H2>
<H3>创建</H3>
<P>new是内存中, save以后才到数据苦 Order.new do |o| ... o.save end</P>
<P>create一步搞定,可以接受一个hash和hash的数组来创建多个, 返回创建的数组,比如order=Order.create(params)(直接从http上拿参数创建)</P>
<H3>find 查找</H3>
<P>find支持的参数</P>
<UL>
<LI>:all or :first 
<LI>
<P>:conditinos <STRONG>conditions支持的参赛</STRONG></P>
<UL>
<LI>直接使用sql conditions=&gt;"..." 
<LI>使用?参数conditons=&gt;["..?..",a] 
<LI>使用conditons=&gt;["..:name", hash]</LI></UL>
<LI>:order 
<LI>:limit 
<LI>:offset 
<LI>:include</LI></UL>
<H4>join指令, 可能用得不多 ???</H4>
<P>LineItem.find(:all, :conditions =&gt; "pr.title = 'Programming Ruby'", :joins =&gt; "as li inner join products as pr on li.product_id = pr.id")</P>
<P>Order.count(支持的参数和:condition类似) 可以获取记录数</P>
<P>默认按照id来find会找不到报异常, 但是自己的复杂find只会返回空记录(nil or []) ObjectA.find_by_sql 可以生成自由填充的ObjectA, 但是如果要修改记得要load id</P>
<P>find_(all)_by_A_and_B(a,b) (只支 持and) 可以自动转换为find(:first(:all) ,:conditions=&gt;'A=? and B=?',a,b)</P>
<P>reload重新读取</P>
<UL>
<LI>save 返回true false 
<LI>
<P>save!返回nil 或者报exception ,如果表中有lock_version int default 0 字段,会自动使用乐观锁定</P>
<LI>
<P>delete 和delete_all 会直接删除, </P>
<LI>destory和destory_all会把对象都读入内存然后调用所有的callback然后再删除(没有前面快)</LI></UL>
<HR>

<H3>事务</H3>
<P>rails里面实现trasaction, 通过trasaction(obj1,objec2) 调用会自动rollback内存里面的对象</P>
<P>rails内置的 save,delete方法(可能导致多个sql)默认是事务(原子)的,不用额外加事务</P>
<P>rails不支持跨数据库事务(至少目前),下面有个简单模拟方案</P><PRE><CODE>User.transaction(user) do
    Account.transaction(account) do
        account.calculate_fees
        user.date_fees_last_calculated = Time.now
        user.save
        account.save
    end
end
</CODE></PRE>
<P>但是这样只是模拟,如果是执行期出错可以回退, 但是user commit的时候如果出错, 此时内部的account已经commit了,不能回退了</P><img src ="http://www.blogjava.net/cap/aggbug/29071.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cap/" target="_blank">tech.cap</a> 2006-01-24 10:19 <a href="http://www.blogjava.net/cap/articles/rails_activerecord_1.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>