锘??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲午夜国产精品无码老牛影视 ,久久久久亚洲AV成人无码,久久久久亚洲精品无码网址色欲 http://www.tkk7.com/lzj520/category/16808.htmlzh-cnSat, 24 Nov 2007 18:48:59 GMTSat, 24 Nov 2007 18:48:59 GMT60071124 rails閲屼嬌鐢╮estful_authentication鐨刲ogin楠岃瘉渚嬪瓙http://www.tkk7.com/lzj520/archive/2007/11/24/162864.htmllzj520lzj520Sat, 24 Nov 2007 08:20:00 GMThttp://www.tkk7.com/lzj520/archive/2007/11/24/162864.htmlhttp://www.tkk7.com/lzj520/comments/162864.htmlhttp://www.tkk7.com/lzj520/archive/2007/11/24/162864.html#Feedback0http://www.tkk7.com/lzj520/comments/commentRss/162864.htmlhttp://www.tkk7.com/lzj520/services/trackbacks/162864.htmlrails login

ruby script/plugin install http://svn.techno-weenie.net/projects/plugins/restful_authentication

ruby script/generate authenticated login_users login --include-activation
鍏朵腑login_users鏄痬odel
login鏄痗ontroller
–include-activation鍙傛暟鍐沖畾鏄惁鐢熸垚鍚戞柊娉ㄥ唽鐢ㄦ埛鍙戦佹縺媧葷爜鐨勪唬鐮併?/p>

鍦╮outes.rb鍔犲叆
map.resources :login_users
map.resource :login
濡傛灉鏄痳ails 1.2.3錛岄渶瑕佸姞鍏ョ殑control鏄細(xì)
map.resource :login, controller=>'login'

鍦╟onfig/environment.rb鍔犲叆
config.active_record.observers = :login_user_observer

浣犺繕鍙互鍦╮outes.rb涓姞鍏ワ紙鏍規(guī)嵁鑷繁鐨勯渶瑕侊級(jí)
map.signup '/signup', :controller => 'login_users', :action =>'new'
map.login '/login', :controller => 'login', :action =>'new'
map.logout '/logout', :controller => 'login', :action =>'destroy'

閰嶇疆database.yml錛屽緩绔嬫暟鎹簱login_development銆乴ogin_test

rake db:migrate

rake

ruby script/server

璁塊棶錛歨ttp://127.0.0.1:3000/login_users/new/



lzj520 2007-11-24 16:20 鍙戣〃璇勮
]]>
071719 rspec鍦╪etbeans IED 6 beta涓婅繍琛屾椂鐨勪竴浜涢棶棰?/title><link>http://www.tkk7.com/lzj520/archive/2007/11/19/161635.html</link><dc:creator>lzj520</dc:creator><author>lzj520</author><pubDate>Mon, 19 Nov 2007 07:45:00 GMT</pubDate><guid>http://www.tkk7.com/lzj520/archive/2007/11/19/161635.html</guid><wfw:comment>http://www.tkk7.com/lzj520/comments/161635.html</wfw:comment><comments>http://www.tkk7.com/lzj520/archive/2007/11/19/161635.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/lzj520/comments/commentRss/161635.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/lzj520/services/trackbacks/161635.html</trackback:ping><description><![CDATA[涓嶇煡鏄垜寮勯敊浜?jiǎn)浠涔堝湴鏂癸紝榪樻槸鏈潵灝辨槸榪欐牱鐨勶紝鍦╪etbeans IED 6 beta榪愯rspec鏃訛紝鍋囧鍏堟槸鍏堣繍琛宻caffold_resource錛岀劧鍚庢寜鐓spec瀹樻柟鐨勬枃妗g殑鏂規(guī)硶鏉ュ畨瑁卹spec銆乺spec_on_rails錛屽茍榪愯rspec銆乺spec_model錛岀劧鑰屾鏃訛紝嫻嬭瘯榛樿鐢熸垚鐨刴odel錛屽氨浼?xì)寰楀埌绫讳计D繖鏍風(fēng)殑閿欒鎻愮ず錛?br /> 1.0.1/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:34:in `require': no such file to load -- E:/NetBeansProjects/RailsApplication2/spec/models/../../vendor/generators/rspec/lib/rspec_on_rails (MissingSourceFile)<br /> 姝ゆ椂鎵撳紑spec_helper.rb錛屽彂鐜皉spec_on_rails鐨勮繖涓璺緞榛樿鏄笉瀵圭殑錛岄渶瑕佹墜宸ユ妸瀹冭緗負(fù)姝g‘鐨勮礬寰勩傛垨鑰呮垜璇曡繃鎶婅繖鍙ヨ礬寰勭殑寮曠敤鐩存帴鍒犻櫎鎺夛紝濂藉儚涔熷彲浠ャ?br /> <br /> 澶勭悊瀹岃繖涓姝ヤ箣鍚庯紝浼?xì)閬囧埌鐨勮窡鐫鐨勮繖鏍蜂竴涓敊璇彁紺猴細(xì)<br /> spec/models/../spec_helper.rb:9: undefined local variable or method `use_transactional_fixtures=' for Spec::Runner::Context:Class (NameError)<br /> 榪欓渶瑕佷綘鍘籺est_helper.rb閲屽姞鍏ヤ竴鍙equire File.expand_path(File.dirname(__FILE__) + "/test2spec_help")錛屾妸use_transactional_fixtures鍔犲叆榪涙潵銆傜劧鍚庡湪spec_helper.rb閲屽姞鍏equire File.expand_path(File.dirname(__FILE__) + "/../test/test_helper")<br /> <br /> 鐒跺悗鎵嶈兘姝e紡鐪嬪埌1 example, 1 failure榪欐牱姣旇緝姝e父鐨勬彁紺恒備笉鐭ユ槸鍚︽垜鎿嶄綔閿欒錛岃繕鏄粈涔堥棶棰橈紝鎬諱箣灝辨槸紕板埌浜?jiǎn)杩欐牱涓榪炰覆鐨勯棶棰? 鑰岃繖涓В鍐沖姙娉曚篃涓嶇煡鏄惁姝g‘ <img src ="http://www.tkk7.com/lzj520/aggbug/161635.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/lzj520/" target="_blank">lzj520</a> 2007-11-19 15:45 <a href="http://www.tkk7.com/lzj520/archive/2007/11/19/161635.html#Feedback" target="_blank" style="text-decoration:none;">鍙戣〃璇勮</a></div>]]></description></item><item><title>071014 windows涓婮Ruby 1.0.2 涓巖ails1.2.5鍑洪敊鐨勫鐞?/title><link>http://www.tkk7.com/lzj520/archive/2007/11/14/160598.html</link><dc:creator>lzj520</dc:creator><author>lzj520</author><pubDate>Wed, 14 Nov 2007 09:19:00 GMT</pubDate><guid>http://www.tkk7.com/lzj520/archive/2007/11/14/160598.html</guid><wfw:comment>http://www.tkk7.com/lzj520/comments/160598.html</wfw:comment><comments>http://www.tkk7.com/lzj520/archive/2007/11/14/160598.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/lzj520/comments/commentRss/160598.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/lzj520/services/trackbacks/160598.html</trackback:ping><description><![CDATA[鍦╪etbeans 6 ruby ide beta閲岄潰錛屽鏋滀嬌鐢╦ruby 1.0.2 騫朵笖rails鍗囩駭鍒?.2.5錛屽氨浼?xì)鍑洪敊濡傚Q?br /> C:/jruby/jruby-1.0.1/lib/ruby/1.8/pathname.rb:420:in `realpath_rec': No such file or directory - C:/railstest/C: (Errno::ENOENT)<br /> ....<br /> <br /> 閬垮厤鐨勫姙娉曞彲浠ラ噸鏂拌netbeans 6 ruby ide騫朵嬌鐢╮ails1.2.3<br /> <br /> 鎴栬呮槸鍦ㄦ澶勪笅杞戒竴涓猵atch錛?br /> http://jira.codehaus.org/browse/JRUBY-1401?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel<br /> <br /> patch鐨勪嬌鐢ㄦ柟娉曞氨鏄寜鐓atch閲岄潰鐨勮鏄庢潵瀵筽athname.rb鍘熸枃浠朵慨鏀癸細(xì)<br /> <p>Index: pathname.rb</p> <p>===================================================================</p> <p>--- pathname.rb (revision 4471)</p> <p>+++ pathname.rb (working copy)</p> <p>@@ -285,6 +285,8 @@</p> <p>   def prepend_prefix(prefix, relpath)<br />      if relpath.empty?<br />        File.dirname(prefix)<br /> +    elsif prefix =~ /^[a-zA-Z]:$/<br /> +      File.join(prefix,relpath)<br />      elsif /#{SEPARATOR_PAT}/ =~ prefix<br />        prefix = File.dirname(prefix)<br />        prefix = File.join(prefix, "") if File.basename(prefix + 'a') != 'a'<br /> @@ -408,6 +410,8 @@</p> <p>         next<br />        elsif n == '..'<br />          resolved.pop<br /> +      elsif n =~ /^[a-zA-Z]:$/<br /> +        resolved << n<br />        else<br />          path = prepend_prefix(prefix, File.join(*(resolved + [n])))<br />          if h.include? path<br /> @@ -446,7 +450,9 @@</p> <p>   def realpath<br />      path = @path<br />      prefix, names = split_names(path)<br /> -    if prefix == ''<br /> +    if prefix == '' && names[0] =~ /^[a-zA-Z]:$/<br /> +      prefix = names.shift<br /> +    elsif prefix == ''<br />        prefix, names2 = split_names(Dir.pwd)<br />        names = names2 + names<br />      end</p> 寰堢洿瑙傦紝+灝辮〃紺哄姞澶氱殑鍐呭錛?灝辨槸琛ㄧず鍑忓皯鐨勫唴瀹? <img src ="http://www.tkk7.com/lzj520/aggbug/160598.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/lzj520/" target="_blank">lzj520</a> 2007-11-14 17:19 <a href="http://www.tkk7.com/lzj520/archive/2007/11/14/160598.html#Feedback" target="_blank" style="text-decoration:none;">鍙戣〃璇勮</a></div>]]></description></item><item><title>浣跨敤 RSpec 榪涜琛屼負(fù)椹卞姩嫻嬭瘯錛堣漿錛?/title><link>http://www.tkk7.com/lzj520/archive/2007/10/23/155374.html</link><dc:creator>lzj520</dc:creator><author>lzj520</author><pubDate>Tue, 23 Oct 2007 11:01:00 GMT</pubDate><guid>http://www.tkk7.com/lzj520/archive/2007/10/23/155374.html</guid><wfw:comment>http://www.tkk7.com/lzj520/comments/155374.html</wfw:comment><comments>http://www.tkk7.com/lzj520/archive/2007/10/23/155374.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/lzj520/comments/commentRss/155374.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/lzj520/services/trackbacks/155374.html</trackback:ping><description><![CDATA[<blockquote>嫻嬭瘯鐑疆鐜板湪浼犳挱鍒頒簡(jiǎn) Ruby 緙栫▼紺懼尯錛屽茍涓旀剤婕旀剤鐑傚湪榪囧幓涓騫撮噷錛屾祴璇曢鍩熶腑鏈涓虹灘鐩殑鍒涙柊搴斿睘 RSpec 鐨勫紩鍏ュ拰蹇熷彂灞曪紝榪欐槸涓縐嶈涓洪┍鍔ㄦ祴璇曞伐鍏楓傞氳繃鏈枃浜?jiǎn)瑙?RSpec 濡備綍鏀瑰彉浜轟滑鎬濊冩祴璇曠殑鏂瑰紡銆?</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES--> <p>鍦ㄨ繃鍘誨崄騫翠腑錛岃蔣浠跺紑鍙戜漢鍛樺嫻嬭瘯鐨勭儹鎯呮棩娓愪綆榪楓傚悓涓鏃舵湡鍑虹幇鐨勫姩鎬佽璦騫舵病鏈夋彁渚涚紪璇戠▼搴忔潵鎹曟崏鏈鍩烘湰鐨勯敊璇紝榪欎嬌寰楁祴璇曞彉寰楁洿鍔犻噸瑕併傞殢鐫嫻嬭瘯紺懼尯鐨勬垚闀匡紝寮鍙戜漢鍛樺紑濮嬫敞鎰忓埌錛岄櫎浜?jiǎn)鎹曡?bug 絳夋渶鍩烘湰鐨勪紭鐐瑰錛屾祴璇曡繕鍏鋒湁浠ヤ笅浼樺娍錛?/p> <ul> <li>嫻嬭瘯鑳藉鏀硅繘鎮(zhèn)ㄧ殑璁捐銆傝繘琛屾祴璇曠殑姣忎釜鐩爣瀵硅薄蹇呴』鍏峰鑷沖皯涓や釜瀹㈡埛鏈猴細(xì)鐢熶駭浠g爜鍜屾祴璇曠敤渚嬨傝繖浜涘鎴鋒満寮哄埗鎮(zhèn)ㄥ浠g爜榪涜瑙h︺傛祴璇曡繕榧撳姳寮鍙戜漢鍛樹嬌鐢ㄦ洿灝忋佹洿綆鍗曠殑鏂規(guī)硶銆? <li>嫻嬭瘯鍑忓皯浜?jiǎn)涓嶅繀瑕佺殑浠g爜銆傚湪緙栧啓嫻嬭瘯鐢ㄤ緥鏃訛紝鎮(zhèn)ㄥ吇鎴愪簡(jiǎn)寰堝ソ鐨勬祴璇曚範(fàn)鎯紝鍗沖彧緙栧啓榪愯嫻嬭瘯鐢ㄤ緥鎵闇鐨勬渶灝戜唬鐮併傛?zhèn)ㄦ姷鍒朵綇浜?jiǎn)瀵瑰姛鑳借繘琛岀紪鐮佺殑璇辨儜錛屽洜涓烘?zhèn)ㄧ洰鍓崢q樹笉闇瑕佸畠銆? <li>鎺ㄥ姩浜?jiǎn)娴嬭瘯浼樺厛寮鍙戙傛?zhèn)ň~栧啓鐨勬瘡涓祴璇曠敤渚嬩細(xì)紜畾涓涓皬闂銆備嬌鐢ㄤ唬鐮佽В鍐寵繖涓棶棰橀潪甯告湁鐢ㄥ茍涓斿彲浠ユ帹鍔ㄥ紑鍙戙傚綋鎴戣繘琛屾祴璇曢┍鍔ㄥ紑鍙戞椂錛屾椂闂磋繃寰楅蹇? <li>嫻嬭瘯鎻愪緵浜?jiǎn)鏇村鐨勮嚜涓绘潈銆傚湪浣跨敤嫻嬭瘯鐢ㄤ緥鎹曡幏鍙兘鐨勯敊璇椂錛屾?zhèn)ㄤ細(xì)鍙戠幇鑷繁闈炲父鎰挎剰瀵逛唬鐮仒q涜鏀硅繘銆?</li> </ul> <p><a name="N10062"><span id="ztvkjfz" class="atitle">嫻嬭瘯椹卞姩鐨勫紑鍙戝拰 RSpec</span></a></p> <p>鏈夊叧嫻嬭瘯鐨勪紭鐐規(guī)棤闇璧樿堪錛屾垜灝嗗悜鎮(zhèn)ㄤ粙緇嶄竴涓畝鍗曠殑浣跨敤 RSpec 鐨勬祴璇曢┍鍔ㄥ紑鍙戠ず渚嬨俁Spec 宸ュ叿鏄竴涓?Ruby 杞歡鍖咃紝鍙互鐢ㄥ畠鏋勫緩鏈夊叧鎮(zhèn)ㄧ殑杞歡鐨勮鑼冦傝瑙勮寖瀹為檯涓婃槸涓涓弿榪扮郴緇熻涓虹殑嫻嬭瘯銆備嬌鐢?RSpec 鐨勫紑鍙戞祦紼嬪涓嬶細(xì)</p> <ul> <li>緙栧啓涓涓祴璇曘傝嫻嬭瘯鎻忚堪緋葷粺涓煇涓緝?yōu)畯鍏冪礌鐨勮湄?fù)銆? <li>榪愯嫻嬭瘯銆傜敱浜庡皻娌℃湁涓虹郴緇熶腑鐨勭浉搴旈儴鍒嗘瀯寤轟唬鐮侊紝嫻嬭瘯澶辮觸銆傝繖涓閲嶈姝ラ灝嗘祴璇曟?zhèn)ㄧ殑娴嬭瘯鐢ㄤ緥锛寢個(gè)楠屾祴璇曠敤渚嬫槸鍚﹀湪搴斿綋澶辮觸鐨勬椂鍊欏け璐ャ? <li>緙栧啓瓚沖鐨勪唬鐮侊紝浣挎祴璇曢氳繃銆? <li>榪愯嫻嬭瘯錛屾楠屾祴璇曟槸鍚︽垚鍔熴?</li> </ul> <p>瀹炶川涓婏紝RSpec 寮鍙戜漢鍛樻墍鍋氱殑宸ヤ綔灝辨槸灝嗗け璐ョ殑嫻嬭瘯鐢ㄤ緥璋冭瘯涓烘垚鍔熺殑嫻嬭瘯鐢ㄤ緥銆傝繖鏄竴涓富鍔ㄧ殑榪囩▼銆傛湰鏂囦腑錛屾垜灝嗕粙緇?RSpec 鐨勫熀鏈敤娉曘?</p> <p>棣栧厛錛屽亣璁炬?zhèn)ㄥ凡瀹夎浜?jiǎn) Ruby 鍜?gems銆傛?zhèn)q橀渶瑕佸畨瑁?RSpec銆傝緭鍏ヤ笅闈㈢殑鍐呭錛?</p> <p><code>gem install rspec</code> </p> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td> </tr> </tbody> </table> <br /> <br /> <p><a name="N10086"><span id="dxypffl" class="atitle">浣跨敤紺轟緥</span></a></p> <p>鎺ヤ笅鏉ワ紝鎴戝皢閫愭鏋勫緩涓涓姸鎬佹満銆傛垜灝嗛伒寰?TDD 瑙勫垯銆傞鍏堢紪鍐欒嚜宸辯殑嫻嬭瘯鐢ㄤ緥錛屽茍涓旂洿鍒版祴璇曠敤渚嬮渶瑕佹椂鎵嶇紪鍐欎唬鐮併俁ake 鐨勫垱寤鴻?Jim Weirich 璁や負(fù)榪欐湁鍔╀簬瑙掕壊鎵紨銆傚湪緙栧啓瀹為檯鐨勭敓浜т唬鐮佹椂錛屾?zhèn)ㄥ笇鏈涘厖褰撲竴鍥?jerk 寮鍙戜漢鍛樼殑瑙掕壊錛屽彧瀹屾垚鏈灝戦噺鐨勫伐浣滄潵浣挎祴璇曢氳繃銆傚湪緙栧啓嫻嬭瘯鏃訛紝鎮(zhèn)ㄥ垯鎵紨嫻嬭瘯浜哄憳鐨勮鑹詫紝璇曞浘涓哄紑鍙戜漢鍛樻彁渚涗竴浜涙湁鐩婄殑甯姪銆?/p> <p>浠ヤ笅鐨勭ず渚嬪睍紺轟簡(jiǎn)濡備綍鏋勫緩涓涓姸鎬佹満銆傚鏋滄?zhèn)ㄤ互鍓嶄粠鏈帴瑙q囩姸鎬佹満錛岃鏌ラ槄 <a >鍙傝冭祫鏂?/a>銆傜姸鎬佹満鍏鋒湁澶氱鐘舵併傛瘡縐嶇姸鎬佹敮鎸佸彲浠ヨ漿鎹㈢姸鎬佹満鐘舵佺殑浜嬩歡銆傛祴璇曢┍鍔ㄥ紑鍙戝叆闂ㄧ殑鍏抽敭灝辨槸浠庨浂鍏ユ墜錛屽敖閲忓皯鍦頒嬌鐢ㄥ亣璁炬潯浠躲傞拡瀵規(guī)祴璇曡繘琛岀▼搴忚璁°?/p> <p>浣跨敤娓呭崟 1 鐨勫唴瀹瑰垱寤哄悕涓?machine_spec.rb 鐨勬枃浠躲傝鏂囦歡灝辨槸鎮(zhèn)ㄧ殑瑙勮寖銆傛?zhèn)q樹笉浜?jiǎn)瑙?machine.rb 鏂囦歡鐨勪綔鐢紝鐩墠鍏堝垱寤轟竴涓┖鏂囦歡銆?/p> <br /> <a name="listing1"><strong>娓呭崟 1. 鏈鍒濈殑 machine_spec.rb 鏂囦歡 </strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode"> require 'machine' </pre> </td> </tr> </tbody> </table> <br /> <p>鎺ヤ笅鏉ワ紝闇瑕佽繍琛屾祴璇曘傚緇堥氳繃杈撳叆 <code>spec machine_spec.rb</code> 榪愯嫻嬭瘯銆傛竻鍗?2 灞曠ず浜?jiǎn)棰勬枡涔嬩腑鐨剫箣璇曞まp觸錛?/p> <br /> <a name="listing2"><strong>娓呭崟 2. 榪愯絀虹殑瑙勮寖</strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">~/rspec batate$ spec machine_spec.rb /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require': no such file to load -- machine (LoadError) from /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require' from ./state_machine_spec.rb:1 from ... </pre> </td> </tr> </tbody> </table> <br /> <p>鍦ㄦ祴璇曢┍鍔ㄥ紑鍙戜腑錛屾?zhèn)ㄩ渶瑕佽繘琛屽閲忓紑鍙戯紝鍥犳鍦ㄨ繘琛屼笅涓嬈″紑鍙戝墠錛岄渶瑕佸厛瑙e喅姝ゆ嫻嬭瘯鍑虹幇鐨勯棶棰樸傜幇鍦紝鎴戝皢鎵紨 jerk 寮鍙戜漢鍛樼殑瑙掕壊錛屽嵆鍙畬鎴愭弧瓚沖簲鐢ㄧ▼搴忚繍琛屾墍闇鐨勬渶灝戝伐浣滈噺銆傛垜灝嗗垱寤轟竴涓悕涓?machine.rb 鐨勭┖鏂囦歡錛屼嬌嫻嬭瘯閫氳繃銆傛垜鐜板湪鍙互浠ラ稿緟鍔籌紝嫻嬭瘯閫氳繃鑰屾垜鍑犱箮娌″仛浠諱綍浜嬫儏銆?/p> <p>緇х畫瑙掕壊鎵紨銆傛垜鐜板湪鎵紨涓涓儲(chǔ)韜佺殑嫻嬭瘯浜哄憳錛屼績(jī)浣?jerk 寮鍙戜漢鍛樺仛浜涘疄闄呯殑宸ヤ綔銆傛垜灝嗙紪鐮佷互涓嬭鑼冿紝闇瑕佷嬌鐢?<code>Machine</code> 綾伙紝濡傛竻鍗?3 鎵紺猴細(xì)</p> <br /> <a name="listing3"><strong>娓呭崟 3. 鍒濆瑙勮寖</strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">require 'machine' describe Machine do before :each do @machine = Machine end end </pre> </td> </tr> </tbody> </table> <br /> <p>璇ヨ鑼冩弿榪頒簡(jiǎn)鐩墠灝氫笉瀛樺湪鐨?<code>Machine</code> 綾匯?code>describe</code> 鏂規(guī)硶鎻愪緵浜?RSpec 鎻忚堪錛屾?zhèn)畣浼犲叆娴嬭瘯绫荤殑鍚嵖U板拰鍖呭惈瀹為檯瑙勮寖鐨勪唬鐮佸潡銆傞氬父錛屾祴璇曠敤渚嬮渶瑕佹墽琛屼竴瀹氭暟閲忕殑璁劇疆宸ヤ綔銆傚湪 RSpec 涓紝灝嗙敱 <code>before</code> 鏂規(guī)硶瀹屾垚榪欎簺璁劇疆宸ヤ綔銆傛?zhèn)ㄥ?<code>before</code> 鏂規(guī)硶浼犻掍竴涓彲閫夌殑鏍囧織鍜屼竴涓唬鐮佸潡銆備唬鐮佸潡涓寘鍚緗伐浣溿傛爣蹇楃‘瀹?RSpec 鎵ц浠g爜鍧楃殑棰戠巼銆傞粯璁ょ殑鏍囧織涓?<code>:each</code>錛岃〃紺?RSpec 灝嗗湪姣忔嫻嬭瘯涔嬪墠璋冪敤 set up 浠g爜鍧椼傛?zhèn)ㄤ篃鍙互鎸囧?<code>:all</code>錛岃〃紺?RSpec 鍦ㄦ墽琛屾墍鏈夋祴璇曚箣鍓嶅彧璋冪敤涓嬈?<code>before</code> 浠g爜鍧椼傛?zhèn)ㄥ簲璇ュ缁堜娇鐢?<code>:each</code>錛屼嬌鍚勪釜嫻嬭瘯褰兼鐙珛銆?</p> <p>杈撳叆 <code>spec</code> 榪愯嫻嬭瘯錛屽娓呭崟 4 鎵紺猴細(xì) </p> <br /> <a name="listing4"><strong>娓呭崟 4. 瀛樺湪鎬ф祴璇曞け璐?/strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">~/rspec batate$ spec machine_spec.rb ./machine_spec.rb:3: uninitialized constant Machine (NameError) </pre> </td> </tr> </tbody> </table> <br /> <p>鐜板湪錛岀儲(chǔ)韜佺殑嫻嬭瘯浜哄憳瑕佷績(jī)浣?jerk 寮鍙戜漢鍛樺仛鐐逛粈涔堜簡(jiǎn) 鈥?jerk 寮鍙戜漢鍛樼幇鍦ㄩ渶瑕佸垱寤烘煇涓被銆傚鎴戞潵璇達(dá)紝灝辨槸淇嫻嬭瘯鍑虹幇鐨勯敊璇傚湪 <code>machine.rb</code> 涓紝鎴戣緭鍏ユ渶灝戦噺鐨勪唬鐮侊紝濡傛竻鍗?5 鎵紺猴細(xì)</p> <br /> <a name="listing5"><strong>娓呭崟 5. 鍒涘緩鍒濆 Machine 綾?/strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">class Machine end</pre> </td> </tr> </tbody> </table> <br /> <p>淇濆瓨鏂囦歡錛岀劧鍚庤繍琛屾祴璇曘傛鏃犵枒闂紝娓呭崟 6 鏄劇ず鐨勬祴璇曟姤鍛婃病鏈夊嚭鐜伴敊璇細(xì)</p> <br /> <a name="listing6"><strong>娓呭崟 6. 嫻嬭瘯 Machine 鏄惁瀛樺湪</strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">~/rspec batate$ spec machine_spec.rb Finished in 5.0e-06 seconds 0 examples, 0 failures </pre> </td> </tr> </tbody> </table> <br /> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td> </tr> </tbody> </table> <br /> <br /> <p><a name="N10116"><span id="zpnmops" class="atitle">緙栧啓琛屼負(fù)</span></a></p> <p>鐜板湪錛屾垜鍙互寮濮嬪疄鐜版洿澶氱殑琛屼負(fù)銆傛垜鐭ラ亾錛屾墍鏈夌姸鎬佹満蹇呴』鍦ㄦ煇浜涘垵濮嬬姸鎬佷笅鍚姩銆傜洰鍓嶆垜榪樹笉鏄緢娓呮濡備綍璁捐榪欎釜琛屼負(fù)錛屽洜姝ゆ垜鍏堢紪鍐欎竴涓潪甯哥畝鍗曠殑嫻嬭瘯錛岄鍏堝亣璁?<code>state</code> 鏂規(guī)硶浼?xì)杩斿?<code>:initial</code> 鏍囧織銆傛垜瀵?<code>machine_spec.rb</code> 榪涜淇敼騫惰繍琛屾祴璇曪紝濡傛竻鍗?7 鎵紺猴細(xì)</p> <br /> <a name="listing7"><strong>娓呭崟 7. 瀹炵幇鍒濆鐘舵佸茍榪愯嫻嬭瘯</strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">require 'machine' describe Machine do before :each do @machine = Machine.new end it "should initially have a state of :initial" do @machine.state.should == :initial end end ~/rspec batate$ spec machine_spec.rb F 1) NoMethodError in 'Machine should initially have a state of :initial' undefined method `state' for #<Machine:0x10c7f8c> ./machine_spec.rb:9: Finished in 0.005577 seconds 1 example, 1 failure </pre> </td> </tr> </tbody> </table> <br /> <p>娉ㄦ剰榪欐潯瑙勫垯錛?code> it "should initially have a state of :initial" do @machine.state.should == :initial end</code>銆傞鍏堟敞鎰忓埌榪欐潯瑙勫垯璇昏搗鏉ュ儚鏄竴涓嫳鏂囧彞瀛愩傚垹闄ゆ爣鐐癸紝灝嗗緱鍒?<code>it should initially have a state of initial</code>銆傜劧鍚庝細(xì)娉ㄦ剰鍒拌繖鏉¤鍒欏茍涓嶅儚鏄吀鍨嬬殑闈㈠悜瀵硅薄浠g爜銆傚畠紜疄涓嶆槸銆傛?zhèn)ㄧ幇鍦ㄦ湁涓涓柟娉曪紝縐頒負(fù) <code>it</code>銆傝鏂規(guī)硶鍏鋒湁涓涓嬌鐢ㄥ紩鍙鋒嫭璧鋒潵鐨勫瓧絎︿覆鍙傛暟鍜屼竴涓唬鐮佸潡銆傚瓧絎︿覆搴旇鎻忚堪嫻嬭瘯闇姹傘傛渶鍚庯紝<code>do</code> 鍜?<code>end</code> 涔嬮棿鐨勪唬鐮佸潡鍖呭惈嫻嬭瘯鐢ㄤ緥鐨勪唬鐮併?</p> <p>鍙互鐪嬪埌錛屾祴璇曡繘搴﹀垝鍒嗗緱寰堢粏銆傝繖浜涘井灝忕殑姝ラ浜х敓鐨勬敹鐩婂嵈寰堝ぇ銆傚畠浠嬌鎴戣兘澶熸敼榪涙祴璇曞瘑搴︼紝鎻愪緵鏃墮棿渚涙垜鎬濊冩湡鏈涚殑琛屼負(fù)浠ュ強(qiáng)瀹炵幇琛屼負(fù)鎵闇鐨?API銆傝繖浜涙楠よ繕鑳戒嬌鎴戝湪寮鍙戞湡闂磋窡韙唬鐮佽鐩栨儏鍐碉紝浠庤屾瀯寤烘洿鍔犱赴瀵岀殑瑙勮寖銆?/p> <p>榪欑椋庢牸鐨勬祴璇曞叿鏈夊弻閲嶄綔鐢細(xì)嫻嬭瘯瀹炵幇騫跺湪嫻嬭瘯鐨勫悓鏃舵瀯寤洪渶姹傝璁℃枃妗c傜◢鍚庯紝鎴戝皢閫氳繃嫻嬭瘯鐢ㄤ緥鏋勫緩涓涓渶姹傚垪琛ㄣ?/p> <p>鎴戜嬌鐢ㄦ渶綆鍗曠殑鏂瑰紡淇浜?jiǎn)娴嬭瘯锛寴q斿洖 <code>:initial</code>錛屽娓呭崟 8 鎵紺猴細(xì)</p> <br /> <a name="listing8"><strong>娓呭崟 8. 鎸囧畾鍒濆鐘舵?/strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">class Machine def state :initial end end </pre> </td> </tr> </tbody> </table> <br /> <p>褰撴煡鐪嬪疄鐜版椂錛屾?zhèn)ㄥ彲鑳戒細(xì)鏀惧0澶ЫW戞垨鎰熻鍙楀埌浜?jiǎn)鎰氬紕銆傚浜庢祴璇曢┍鍔ㄥ紑鍙戯紝鎮(zhèn)ㄥ繀欏葷◢寰敼鍙樹竴涓嬫濊冩柟寮忋傛?zhèn)ㄧ殑鐩爣迤堜笉鏄~栧啓鏈緇堢殑鐢熶駭浠g爜錛岃嚦灝戠幇鍦ㄤ笉鏄傛?zhèn)ㄧ殑鐩爣鏄娇娴嬭瘯閫氳繃銆傚綋鎺屾彙浠ヨ繖縐嶆柟寮忓伐浣滄椂錛屾?zhèn)ㄥ彲鑳戒細(xì)鍙戠幇鏂扮殑瀹炵帋图岒q朵笖緙栧啓鐨勪唬鐮佽榪滆繙灝戜簬閲囩敤 TDD 鏃剁紪鍐欑殑浠g爜銆?/p> <p>涓嬩竴姝ユ槸榪愯浠g爜錛屾煡鐪嬪畠鏄惁閫氳繃嫻嬭瘯錛?/p> <br /> <a name="listing9"><strong>娓呭崟 9. 榪愯鍒濆鐘舵佹祴璇?/strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">~/rspec batate$ spec machine_spec.rb . Finished in 0.005364 seconds 1 example, 0 failures</pre> </td> </tr> </tbody> </table> <br /> <p>鑺變簺鏃墮棿鎬濊冧竴涓嬭繖涓氳繃嫻嬭瘯鐨勮凱浠c傚鏋滄煡鐪嬩唬鐮佺殑璇濓紝鎮(zhèn)ㄥ彲鑳戒細(xì)瑙夊緱姘旈銆傚洜涓哄茍娌℃湁鍙栧緱浠涔堣繘灞曘傚鏋滄煡鐪嬫暣涓凱浠o紝灝嗙湅鍒版洿澶氬唴瀹癸細(xì)鎮(zhèn)ㄦ崟鑾蜂簡(jiǎn)涓涓噸瑕侀渶姹傚茍緙栧啓嫻嬭瘯鐢ㄤ緥瀹炵幇闇姹傘備綔涓轟竴鍚嶇▼搴忓憳錛屾垜鐨勭涓涓涓烘祴璇曞府鍔╂垜鏄庣‘浜?jiǎn)寮鍙戣繃紼嬨傚洜涓哄疄鐜扮粏鑺傞殢鐫嫻嬭瘯鐨勮繘琛岃秺鏉ヨ秺娓呮櫚銆?/p> <p>鐜板湪錛屾垜鍙互瀹炵幇涓涓洿鍋ュ.鐨勭姸鎬佸疄鐜般傚叿浣撴潵璁詫紝鎴戦渶瑕佸鐞嗙姸鎬佹満鐨勫涓姸鎬併傛垜闇瑕佸垱寤轟竴涓柊鐨勮鍒欒幏鍙栨湁鏁堢姸鎬佸垪琛ㄣ傚儚浠ュ墠涓鏍鳳紝鎴戝皢榪愯嫻嬭瘯騫舵煡鐪嬫槸鍚﹂氳繃銆?/p> <br /> <a name="listing10"><strong>娓呭崟 10. 瀹炵幇鏈夋晥鐘舵佽鑼?/strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode"> it "should remember a list of valid states" do @machine.states = [:shopping, :checking_out] @machine.states.should = [:shopping, :checking_out] end run test(note: failing first verifies test) ~/rspec batate$ spec machine_spec.rb .F 1) NoMethodError in 'Machine should remember a list of valid states' undefined method `states=' for #<Machine:0x10c7154> ./machine_spec.rb:13: Finished in 0.005923 seconds 2 examples, 1 failure</pre> </td> </tr> </tbody> </table> <br /> <p>鍦ㄦ竻鍗?10 涓紝鍑虹幇浜?jiǎn)涓涓?RSpec 褰㈠紡鐨勬柇璦銆傝鏂█浠?<code>should</code> 鏂規(guī)硶寮濮嬶紝鐒跺悗娣誨姞浜?jiǎn)涓浜涙瘮杈冨叧緋匯?code>should</code> 鏂規(guī)硶瀵瑰簲鐢ㄧ▼搴忚繘琛屾煇縐嶈瀵熴傚伐浣滀腑鐨勫簲鐢ㄧ▼搴忓簲璇ヤ互鏌愮鏂瑰紡榪愯銆?code>should</code> 鏂規(guī)硶寰堝ソ鍦版崟鑾蜂簡(jiǎn)榪欑闇姹傘傚湪鏈緥涓紝鎴戠殑鐘舵佹満搴旇璁板繂涓ょ涓嶅悓鐨勭姸鎬併?/p> <p>鐜板湪錛屽簲璇ユ坊鍔犱竴涓疄渚嬪彉閲忔潵瀹為檯璁板繂鐘舵併傚儚浠ュ線涓鏍鳳紝鎴戝湪淇敼浠g爜鍚庤繍琛屾祴璇曠敤渚嬶紝騫惰瀵熸祴璇曟槸鍚︽垚鍔熴?/p> <br /> <a name="listing11"><strong>娓呭崟 11. 鍒涘緩涓涓睘鎬т互璁板繂鐘舵?/strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">class Machine attr_accessor :states def state :initial end end ~/rspec batate$ spec machine_spec.rb .. Finished in 0.00606 seconds 2 examples, 0 failures</pre> </td> </tr> </tbody> </table> <br /> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td> </tr> </tbody> </table> <br /> <br /> <p><a name="N10199"><span id="smvzfes" class="atitle">椹卞姩閲嶆瀯</span></a></p> <p>姝ゆ椂錛屾垜騫朵笉鎯沖喅瀹氬皢 <code>:initial</code> 鐘舵佺О涓虹姸鎬佹満鐨勭涓涓姸鎬併傜浉鍙嶏紝鎴戞洿甯屾湜絎竴涓姸鎬佹槸鐘舵佹暟緇勪腑鐨勭涓涓厓绱犮傛垜瀵圭姸鎬佹満鐨勭悊瑙e湪涓嶆柇婕斿彉銆傝繖縐嶇幇璞″茍涓嶅皯瑙併傛祴璇曢┍鍔ㄥ紑鍙戠粡甯歌揩浣挎垜閲嶆柊鑰冭檻涔嬪墠鐨勫亣璁俱傜敱浜庢垜宸茬粡閫氳繃嫻嬭瘯鐢ㄤ緥鎹曡幏浜?jiǎn)鏃╂湡闇姹傦紝鎴戝彲浠ヨ交鏉懼湴瀵逛唬鐮佽繘琛岄噸鏋勩傚湪鏈緥涓紝閲嶆瀯灝辨槸瀵逛唬鐮佽繘琛岃皟鏁達(dá)紝浣垮叾鏇村ソ鍦板伐浣溿?/p> <p>淇敼絎竴涓祴璇曪紝浣垮叾濡傛竻鍗?12 鎵紺猴紝騫惰繍琛屾祴璇曪細(xì)</p> <br /> <a name="listing12"><strong>娓呭崟 12. 鍒濆鐘舵佸簲璇ヤ負(fù)鎸囧畾鐨勭涓涓姸鎬?/strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">it "should initially have a state of the first state" do @machine.states = [:shopping, :checking_out] @machine.state.should == :shopping end ~/rspec batate$ spec machine_spec.rb F. 1) 'Machine should initially have a state of the first state' FAILED expected :shopping, got :initial (using ==) ./machine_spec.rb:10: Finished in 0.005846 seconds 2 examples, 1 failure</pre> </td> </tr> </tbody> </table> <br /> <p>鍙互榪欐牱璇達(dá)紝嫻嬭瘯鐢ㄤ緥璧峰埌浣滅敤浜?jiǎn)锛屽洜湄?fù)瀹冭繍琛屽け璐ワ紝鍥犳鎴戠幇鍦ㄩ渶瑕佷慨鏀逛唬鐮佷互浣垮叾宸ヤ綔銆傛樉鑰屾槗瑙侊紝鎴戠殑浠誨姟灝辨槸浣挎祴璇曢氳繃銆傛垜鍠滄榪欑嫻嬭瘯鐩殑錛屽洜涓烘垜鐨勬祴璇曠敤渚嬫鍦ㄩ┍鍔ㄦ垜榪涜璁捐銆傛垜灝嗘妸鍒濆鐘舵佷紶閫掔粰 <code>new</code> 鏂規(guī)硶銆傛垜灝嗗瀹炵幇紼嶄綔淇敼錛屼互絎﹀悎淇敼鍚庣殑瑙勮寖錛屽娓呭崟 13 鎵紺恒?/p> <br /> <a name="listing13"><strong>娓呭崟 13. 鎸囧畾鍒濆鐘舵?/strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">start to fix it class Machine attr_accessor :states attr_reader :state def initialize(states) @states = states @state = @states[0] end end ~/rspec batate$ spec machine_spec.rb 1) ArgumentError in 'Machine should initially have a state of the first state' wrong number of arguments (0 for 1) ./machine_spec.rb:5:in `initialize' ./machine_spec.rb:5:in `new' ./machine_spec.rb:5: 2) ArgumentError in 'Machine should remember a list of valid states' wrong number of arguments (0 for 1) ./machine_spec.rb:5:in `initialize' ./machine_spec.rb:5:in `new' ./machine_spec.rb:5: Finished in 0.006391 seconds 2 examples, 2 failures</pre> </td> </tr> </tbody> </table> <br /> <p>鐜板湪錛屾祴璇曞嚭鐜頒簡(jiǎn)涓浜涢敊璇傛垜鎵懼埌浜?jiǎn)瀹炵庮C腑鐨勪竴浜?bug銆傛祴璇曠敤渚嬩笉鍐嶄嬌鐢ㄦ紜殑鎺ュ彛錛屽洜涓烘垜娌℃湁鎶婂垵濮嬬姸鎬佷紶閫掔粰鐘舵佹満銆傚彲浠ョ湅鍒幫紝嫻嬭瘯鐢ㄤ緥宸茬粡璧峰埌浜?jiǎn)淇濇姢浣滅敤銆傛垜榪涜浜?jiǎn)杈冨ぇ鐨勬洿鏀瑰Q屾祴璇曞氨鍙戠幇浜?bug銆傛垜浠渶瑕佸嫻嬭瘯榪涜閲嶆瀯浠ュ尮閰嶆柊鐨勬帴鍙o紝灝嗗垵濮嬬姸鎬佸垪琛ㄤ紶閫掔粰 <code>new</code> 鏂規(guī)硶銆傚湪榪欓噷鎴戝茍娌℃湁閲嶅鍒濆鍖栦唬鐮侊紝鑰屾槸灝嗗叾鏀劇疆鍦?<code>before</code> 鏂規(guī)硶涓紝濡傛竻鍗?14 鎵紺猴細(xì)</p> <br /> <a name="listing14"><strong>娓呭崟 14. 鍦?“before” 涓垵濮嬪寲鐘舵佹満 </strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">require 'machine' describe Machine do before :each do @machine = Machine.new([:shopping, :checking_out]) end it "should initially have a state of the first state" do @machine.state.should == :shopping end it "should remember a list of valid states" do @machine.states.should == [:shopping, :checking_out] end end ~/rspec batate$ spec machine_spec.rb .. Finished in 0.005542 seconds 2 examples, 0 failures</pre> </td> </tr> </tbody> </table> <br /> <p>鐘舵佹満寮濮嬮愭笎鎴愬瀷銆備唬鐮佷粛鐒舵湁涓浜涢棶棰橈紝浣嗘槸姝e湪鍚戣壇濂界殑鏂瑰悜婕斿寲銆傛垜灝嗗紑濮嬪鐘舵佹満榪涜涓浜涜漿鎹€傝繖浜涜漿鎹㈠皢淇?jī)鋴射唬鐮佸疄闄呰蹇嗗綋鍓嶇姸鎬併?/p> <p>嫻嬭瘯鐢ㄤ緥淇?jī)鋴涉垜鍏ㄩ潰鍦版濊?API 鐨勮璁°傛垜闇瑕佺煡閬撳浣曡〃紺轟簨浠跺拰杞崲銆傞鍏堬紝鎴戝皢浣跨敤涓涓暎鍒楄〃琛ㄧず杞崲錛岃屾病鏈変嬌鐢ㄦ垚鐔熺殑闈㈠悜瀵硅薄瀹炵幇銆傞殢鍚庯紝嫻嬭瘯闇姹傚彲鑳戒細(xì)瑕佹眰鎴戜慨鏀瑰亣璁炬潯浠訛紝浣嗘槸鐩墠錛屾垜浠嶇劧淇濇寔榪欑綆鍗曟с傛竻鍗?15 鏄劇ず浜?jiǎn)淇敼鍚庣殑浠g爜锛?xì)</p> <br /> <a name="listing15"><strong>娓呭崟 15. 娣誨姞浜嬩歡鍜岃漿鎹?/strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">remember events... change before conditions require 'machine' describe Machine do before :each do @machine = Machine.new([:shopping, :checking_out]) @machine.events = {:checkout => {:from => :shopping, :to => :checking_out}} end it "should initially have a state of the first state" do @machine.state.should == :shopping end it "should remember a list of valid states" do @machine.states.should == [:shopping, :checking_out] end it "should remember a list of events with transitions" do @machine.events.should == {:checkout => {:from => :shopping, :to => :checking_out}} end end ~/rspec batate$ spec machine_spec.rb FFF 1) NoMethodError in 'Machine should initially have a state of the first state' undefined method `events=' for #<Machine:0x10c6f38> ./machine_spec.rb:6: 2) NoMethodError in 'Machine should remember a list of valid states' undefined method `events=' for #z7lt;Machine:0x10c5afc> ./machine_spec.rb:6: 3) NoMethodError in 'Machine should remember a list of events with transitions' undefined method `events=' for #<Machine:0x10c4a58> ./machine_spec.rb:6: Finished in 0.006597 seconds 3 examples, 3 failures</pre> </td> </tr> </tbody> </table> <br /> <p>鐢變簬鏂扮殑嫻嬭瘯浠g爜浣嶄簬 <code>before</code> 涓紝灝嗘垜鐨勪笁涓祴璇曞垎瑙e紑鏉ャ傚敖綆″姝わ紝娓呭崟 16 涓睍紺虹殑嫻嬭瘯闈炲父瀹規(guī)槗淇銆傛垜灝嗘坊鍔犲彟涓涓闂▼搴忥細(xì)</p> <br /> <a name="listing16"><strong>娓呭崟 16. 璁板繂浜嬩歡</strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">class Machine attr_accessor :states, :events attr_reader :state def initialize(states) @states = states @state = @states[0] end end ~/rspec batate$ spec machine_spec.rb ... Finished in 0.00652 seconds 3 examples, 0 failures test</pre> </td> </tr> </tbody> </table> <br /> <p>嫻嬭瘯鍏ㄩ儴閫氳繃銆傛垜寰楀埌浜?jiǎn)涓涓兘姝e父榪愯鐨勭姸鎬佹満銆傛帴涓嬫潵鐨勫嚑涓祴璇曞皢浣垮畠鏇村姞瀹屽杽銆?/p> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td> </tr> </tbody> </table> <br /> <br /> <p><a name="N101FD"><span id="xotylzy" class="atitle">鎺ヨ繎鐪熷疄鐨勫簲鐢ㄧ▼搴?/span></a></p> <p>鐩墠涓烘錛屾垜鎵鍋氱殑涓嶈繃鏄Е鍙戜簡(jiǎn)涓嬈$姸鎬佽漿鎹紝浣嗘槸鎴戝凡緇忓仛濂戒簡(jiǎn)鎵鏈夊熀紜宸ヤ綔銆傛垜寰楀埌浜?jiǎn)涓緇勯渶姹傘傛垜榪樻瀯寤轟簡(jiǎn)涓緇勬祴璇曘傛垜鐨勪唬鐮佸彲浠ヤ負(fù)鐘舵佹満鎻愪緵浣跨敤鐨勬暟鎹傛鏃訛紝綆$悊鍗曚釜鐘舵佹満杞崲浠呰〃紺轟竴嬈$畝鍗曠殑杞崲錛屽洜姝ゆ垜灝嗘坊鍔犲娓呭崟 17 鎵紺虹殑嫻嬭瘯錛?/p> <br /> <a name="listing17"><strong>娓呭崟 17. 鏋勫緩鐘舵佹満鐨勭姸鎬佽漿鎹?/strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">it "should transition to :checking_out upon #trigger(:checkout) event " do @machine.trigger(:checkout) @machine.state.should == :checking_out end ~/rspec batate$ spec machine_spec.rb ...F 1) NoMethodError in 'Machine should transition to :checking_out upon #trigger(:checkout) event ' undefined method `trigger' for #<Machine:0x10c4d00> ./machine_spec.rb:24: Finished in 0.006153 seconds 4 examples, 1 failure</pre> </td> </tr> </tbody> </table> <br /> <p>鎴戦渶瑕佹姷鍒跺揩閫熸瀯寤哄ぇ閲忓姛鑳界殑璇辨儜銆傛垜搴旇鍙紪鍐欏皯閲忎唬鐮侊紝鍙浣挎祴璇曢氳繃鍗沖彲銆傛竻鍗?18 灞曠ず鐨勮凱浠e皢琛ㄧず API 鍜岄渶姹傘傝繖灝辮凍澶熶簡(jiǎn)錛?/p> <br /> <a name="listing18"><strong>娓呭崟 18. 瀹氫箟 trigger 鏂規(guī)硶</strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">def trigger(event) @state = :checking_out end ~/rspec batate$ spec machine_spec.rb .... Finished in 0.005959 seconds 4 examples, 0 failures</pre> </td> </tr> </tbody> </table> <br /> <p>榪欓噷鍑虹幇浜?jiǎn)涓涓湁瓚g殑杈規(guī)敞銆傚湪緙栧啓浠g爜鏃訛紝鎴戜袱嬈¢兘寮勯敊浜?jiǎn)杩欎釜绠鍗曠殑鏂規(guī)硶銆傜涓嬈℃垜榪斿洖浜?<code>:checkout</code>錛涚浜屾鎴戝皢鐘舵佽緗負(fù) <code>:checkout</code> 鑰屼笉鏄?<code>:checking_out</code>銆傚湪嫻嬭瘯涓嬌鐢ㄨ緝?yōu)畯鐨勬楠ゅ彲浠ヤ负鎴戣妭鐪佸ぇ閲忔棄櫁村Q屽洜涓烘祴璇曠敤渚嬩負(fù)鎴戞崟鑾風(fēng)殑榪欎簺閿欒鍦ㄥ皢鏉ョ殑寮鍙戜腑寰堥毦鎹曡幏鍒般傛湰鏂囩殑鏈鍚庝竴涓楠ゆ槸瀹為檯鎵ц涓嬈$姸鎬佹満杞崲銆傚湪絎竴涓ず渚嬩腑錛屾垜騫朵笉鍏沖績(jī)瀹為檯鐨勬満鍣ㄧ姸鎬佹槸浠涔堟牱瀛愮殑銆傛垜浠呬粎鏄牴鎹簨浠惰繘琛岀洸鐩漿鎹紝鑰屼笉鑰冭檻鐘舵併?/p> <p>涓よ妭鐐圭殑鐘舵佹満鏃犳硶鎵ц榪欎釜鎿嶄綔錛屾垜闇瑕佸湪絎笁涓妭鐐逛腑鏋勫緩銆傛垜娌℃湁浣跨敤宸叉湁鐨?<code>before</code> 鏂規(guī)硶錛屽彧鏄湪鏂扮姸鎬佷腑娣誨姞鍙﹀鐨勭姸鎬併傛垜灝嗗湪嫻嬭瘯鐢ㄤ緥涓繘琛屼袱嬈¤漿鎹紝浠ョ‘淇濈姸鎬佹満鑳藉姝g‘鍦版墽琛岃漿鎹紝濡傛竻鍗?19 鎵紺猴細(xì)</p> <br /> <a name="listing19"><strong>娓呭崟 19. 瀹炵幇絎竴嬈¤漿鎹?/strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">it "should transition to :success upon #trigger(:accept_card)" do @machine.events = { :checkout => {:from => :shopping, :to => :checking_out}, :accept_card => {:from => :checking_out, :to => :success} } @machine.trigger(:checkout) @machine.state.should == :checking_out @machine.trigger(:accept_card) @machine.state.should == :success end ~/rspec batate$ spec machine_spec.rb ....F 1) 'Machine should transition to :success upon #trigger(:accept_card)' FAILED expected :success, got :checking_out (using ==) ./machine_spec.rb:37: Finished in 0.007564 seconds 5 examples, 1 failure</pre> </td> </tr> </tbody> </table> <br /> <p>榪欎釜嫻嬭瘯灝嗕嬌鐢?<code>:checkout</code> 鍜?<code>:accept_card</code> 浜嬩歡寤虹珛鏂扮殑鐘舵佹満銆傚湪澶勭悊絳懼嚭鏃訛紝鎴戦夋嫨浣跨敤涓や釜浜嬩歡鑰屼笉鏄竴涓紝榪欐牱鍙互闃叉鍙戠敓鍙屽懡浠ゃ傜鍑轟唬鐮佸彲浠ョ‘淇濈姸鎬佹満鍦ㄧ鍑轟箣鍓嶅浜?<code>shopping</code> 鐘舵併傜涓嬈$鍑洪鍏堝皢鐘舵佹満浠?<code>shopping</code> 杞崲涓?<code>checking_out</code>銆傛祴璇曠敤渚嬮氳繃瑙﹀彂 <code>checkout</code> 鍜?<code>accept_card</code> 浜嬩歡瀹炵幇涓や釜杞崲錛屽茍鍦ㄨ皟鐢ㄤ簨浠朵箣鍚庢楠屼簨浠剁姸鎬佹槸鍚︽紜備笌棰勬湡涓鏍鳳紝嫻嬭瘯鐢ㄤ緥澶辮觸 鈥?鎴戝茍娌℃湁緙栧啓澶勭悊澶氫釜杞崲鐨勮Е鍙戝櫒鏂規(guī)硶銆備唬鐮佷慨姝e寘鍚竴琛岄潪甯擱噸瑕佺殑浠g爜銆傛竻鍗?20 灞曠ず浜?jiǎn)鐘舵佹満鐨勬牳蹇?jī)锛?xì) </p> <br /> <a name="listing20"><strong>娓呭崟 20. 鐘舵佹満鐨勬牳蹇?/strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">def trigger(event) @state = events[event][:to] end ~/rspec batate$ spec machine_spec.rb ..... Finished in 0.006511 seconds 5 examples, 0 failures</pre> </td> </tr> </tbody> </table> <br /> <p>嫻嬭瘯鍙互榪愯銆傝繖浜涚矖緋欑殑浠g爜絎竴嬈℃紨鍙樹負(fù)鐪熸鍙互縐頒箣涓虹姸鎬佹満鐨勪笢瑗褲備絾鏄繖榪樿繙榪滀笉澶熴傜洰鍓嶏紝鐘舵佹満緙轟箯涓ュ瘑鎬с備笉綆$姸鎬佸浣曪紝鐘舵佹満閮戒細(xì)瑙﹀彂浜嬩歡銆備緥濡傦紝褰撳浜?<code>shopping</code> 鐘舵佹椂錛岃Е鍙?<code>:accept_card</code> 騫朵笉浼?xì)铦{鎹負(fù) <code>:success</code> 鐘舵併傛?zhèn)ㄥ彧鑳藉浠?<code>:checking_out</code> 鐘舵佽Е鍙?<code>:accept_card</code>銆傚湪緙栫▼鏈涓紝<code>trigger</code> 鏂規(guī)硶鐨勮寖鍥村簲閽堝浜嬩歡銆傛垜灝嗙紪鍐欎竴涓祴璇曟潵瑙e喅闂錛岀劧鍚庝慨澶?bug銆傛垜灝嗙紪鍐欎竴涓礋嫻嬭瘯錛坣egative test錛夛紝鍗蟲柇璦涓涓笉搴旇鍑虹幇鐨勮涓猴紝濡傛竻鍗?21 鎵紺猴細(xì)</p> <br /> <a name="listing21"><strong>娓呭崟 21: 璐熸祴璇?/strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">it "should not transition from :shopping to :success upon :accept_card" do @machine.events = { :checkout => {:from => :shopping, :to => :checking_out}, :accept_card => {:from => :checking_out, :to => :success} } @machine.trigger(:accept_card) @machine.state.should_not == :success end rspec batate$ spec machine_spec.rb .....F 1) 'Machine should not transition from :shopping to :success upon :accept_card' FAILED expected not == :success, got :success ./machine_spec.rb:47: Finished in 0.006582 seconds 6 examples, 1 failure</pre> </td> </tr> </tbody> </table> <br /> <p>鐜板湪鍙互鍐嶆榪愯嫻嬭瘯錛屽叾涓竴涓祴璇曞棰勬湡涓鏍瘋繍琛屽け璐ャ備慨澶嶄唬鐮佸悓鏍峰彧鏈変竴琛岋紝濡傛竻鍗?22 鎵紺猴細(xì)</p> <br /> <a name="listing22"><strong>娓呭崟 22. 淇 trigger 涓殑鑼冨洿闂 </strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">def trigger(event) @state = events[event][:to] if state == events[event][:from] end rspec batate$ spec machine_spec.rb ...... Finished in 0.006873 seconds 6 examples, 0 failures</pre> </td> </tr> </tbody> </table> <br /> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td> </tr> </tbody> </table> <br /> <br /> <p><a name="N1029C"><span id="nnpkftw" class="atitle">緇勫悎浠g爜</span></a></p> <p>鐜板湪錛屾垜鍏鋒湁涓涓彲綆鍗曡繍琛岀殑鐘舵佹満銆傛棤璁轟粠鍝柟闈㈡潵璇達(dá)紝瀹冮兘涓嶆槸涓涓畬緹庣殑紼嬪簭銆傚畠榪樺叿鏈変笅闈㈣繖浜涢棶棰橈細(xì)</p> <ul> <li>鐘舵佹暎鍒楀疄闄呬笂涓嶅叿澶囦換浣曞姛鑳姐傛垜搴旇鏍規(guī)嵁鐘舵佸浜嬩歡鍙?qiáng)鍏惰浆鎹q涜楠岃瘉錛屾垨鑰呭皢鎵鏈夌姸鎬侀泦涓搗鏉ャ傚悗緇渶姹傚緢鍙兘浼?xì)瑕佹眰杩欐牱鍋氥? <li>鏌愪釜鏃㈠畾浜嬩歡鍙兘瀛樺湪浜庝竴涓姸鎬佷腑銆傝繖縐嶉檺鍒跺茍涓嶅悎鐞嗐備緥濡傦紝<code>submit</code> 鍜?<code>cancel</code> 浜嬩歡鍙兘闇瑕佸浜庡涓姸鎬併? <li>浠g爜騫朵笉鍏峰鏄庢樉鐨勯潰鍚戝璞$壒寰併備負(fù)浣塊厤緗繚鎸佺畝鍗曪紝鎴戝皢澶ч噺鏁版嵁緗叆鏁e垪涓傚悗緇殑榪唬浼?xì)杩涗竴姝ラ┍鍔ㄨ璁★紝浣垮叾鏈濋潰鍚戝璞¤璁℃柟鍚戝彂灞曘?</li> </ul> <p>浣嗘槸錛屾?zhèn)q樺彲浠ョ湅鍒幫紝榪欎釜鐘舵佹満宸茬粡鑳藉婊¤凍涓浜涢渶姹備簡(jiǎn)銆傛垜榪樺叿澶囦竴涓弿榪扮郴緇熻涓虹殑鏂囨。錛岃繖鏄繘琛屼竴緋誨垪嫻嬭瘯鐨勫ソ璧風(fēng)偣銆傛瘡涓祴璇曠敤渚嬮兘鏀寔緋葷粺鐨勪竴涓熀鏈渶姹傘備簨瀹炰笂錛岄氳繃榪愯 <code>spec machine_spec.rb --format specdoc</code>錛屾?zhèn)ㄥ彲浠ユ煡鐪嬬斵q郴緇熻鑼冪粍鎴愮殑鍩烘湰鎶ュ憡錛屽娓呭崟 23 鎵紺猴細(xì)</p> <br /> <a name="listing23"><strong>娓呭崟 23. 鏌ョ湅瑙勮寖</strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="code-outline"> <pre class="displaycode">spec machine_spec.rb --format specdoc Machine - should initially have a state of the first state - should remember a list of valid states - should remember a list of events with transitions - should transition to :checking_out upon #trigger(:checkout) event - should transition to :success upon #trigger(:accept_card) - should not transition from :shopping to :success upon :accept_card Finished in 0.006868 seconds</pre> </td> </tr> </tbody> </table> <br /> <p>嫻嬭瘯椹卞姩鏂規(guī)硶騫朵笉閫傚悎鎵鏈変漢錛屼絾鏄秺鏉ヨ秺澶氱殑浜哄紑濮嬩嬌鐢ㄨ繖縐嶆妧鏈紝浣跨敤瀹冩瀯寤哄叿鏈夌伒媧繪у拰閫傚簲鎬х殑楂樿川閲忎唬鐮侊紝騫朵笖鏍規(guī)嵁嫻嬭瘯浠庡ご鏋勫緩浠g爜銆傚綋鐒?dòng)灱屾?zhèn)ㄤ篃鍙互閫氳繃鍏朵粬妗嗘灦錛堝 test_unit錛夎幏寰楃浉鍚岀殑浼樼偣銆俁Spec 榪樻彁渚涗簡(jiǎn)浼樼鐨勫疄鐜版柟娉曘傝繖縐嶆柊嫻嬭瘯妗嗘灦鐨勪竴澶т寒鐐瑰氨鏄唬鐮佺殑琛ㄧず銆傛柊鎵嬪挨鍏跺彲浠ヤ粠榪欑琛屼負(fù)椹卞姩鐨勬祴璇曟柟娉曚腑鍙楃泭銆傝灝濊瘯浣跨敤璇ユ鏋跺茍鍛婅瘔鎴戞?zhèn)ㄧ殑鎰熷彈銆?/p> <img src ="http://www.tkk7.com/lzj520/aggbug/155374.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/lzj520/" target="_blank">lzj520</a> 2007-10-23 19:01 <a href="http://www.tkk7.com/lzj520/archive/2007/10/23/155374.html#Feedback" target="_blank" style="text-decoration:none;">鍙戣〃璇勮</a></div>]]></description></item><item><title>錛堣漿錛塕olling with Ruby on Rails, Part 2 (onlamp.com)http://www.tkk7.com/lzj520/archive/2006/11/04/79074.htmllzj520lzj520Sat, 04 Nov 2006 08:36:00 GMThttp://www.tkk7.com/lzj520/archive/2006/11/04/79074.htmlhttp://www.tkk7.com/lzj520/comments/79074.htmlhttp://www.tkk7.com/lzj520/archive/2006/11/04/79074.html#Feedback0http://www.tkk7.com/lzj520/comments/commentRss/79074.htmlhttp://www.tkk7.com/lzj520/services/trackbacks/79074.htmlAre you ready? Let's begin!

Updating Ruby on Rails

When I wrote Part 1, the current version of Rails was 0.9.3. At the time of this writing, Rails is up to version 0.10.0 and has some useful new features. I will use Rails 0.10.0 for this article. If you installed Rails after February 24, 2005, you already have 0.10.0 installed.

Figure 1 shows how to see what RubyGems you have installed (and their version numbers). As with Part 1, I am working on a Windows system, so you will need to translate if you use a different platform.

listing installed RubyGems
Figure 1. Listing installed RubyGems

Open a command window and run the command:

				
gem list --local

Tip: the command gem list --remote will show all the available RubyGems on the remote gem server on rubyforge.org.

If you don't have Rails 0.10.0 (or later) installed, then you will need to rerun the command:

				
gem install rails

MySQL security update

In Part 1, I recommended that you leave the MySQL root password blank because (at the time of writing) Rails did not support MySQL's new password protocol. Many of you were not happy with this state of affairs, and to make matters worse, there is now a virus that exploits password vulnerabilities in MySQL on Windows.

Happily, starting with version 0.9.4, Rails now supports the new password protocol.

New scaffold feature

Rails has a new scaffold feature, which I won't explore here, but it's cool enough that I want to make sure you know about it. This is best illustrated by an example.

Part 1 showed how to create a recipe model and controller with the commands:

				
ruby script\generate model Recipe
ruby script\generate controller Recipe

I then instantiated the scaffolding by inserting scaffold :recipe into the RecipeController class. The resulting CRUD controllers and view templates were created on the fly and are not visible for inspection.

The technique described above still works, but you now have another option. Run the command:

				
ruby script\generate scaffold Recipe

This generates both the model and the controller, plus it creates scaffold code and view templates for all CRUD operations. This allows you to see the scaffold code and modify it to meet your needs. Be careful using this if you've already created models, controllers, or view templates, as it will overwrite any existing files as it creates the scaffold code.

Completing the Recipe Application

It's time to round out the recipe application a bit. After that I'll present some other features of Rails that I'm sure you'll want to know about.

Remember that I created my cookbook application in the directory c:\rails\cookbook; all paths used in this article assume this base directory. If you chose a different location, please be sure to make the proper adjustments to the application paths you see in this article.

You can also download my cookbook source code for this tutorial in one single zip file. This works with Rails 0.13 and later, so if you're still using an older version, I suggest that you follow the upgrade instructions.

For those of you who are cheating (you know who you are) and plan to just download my source code without going through Part 1, you will also need to create a database named cookbook in MySQL and populate it using cookbook.sql.

Creating a new recipe with a category

Because the code still relies on the scaffolding to create new recipes, there is no way to assign a category to a recipe. This wouldn't be so bad--except that the page created to list all recipes assumes that every recipe will have a category, and it generates an error if this is not true. That means that in the way I left things in Part 1, if you add a new recipe, you'll receive errors while trying to list them.

The fix is to take over the new action from the scaffolding just as I showed already with the edit action. Edit c:\rails\cookbook\app\controllers\recipe_controller.rb and add a new method like in Figure 2.

the Recipe controller's new method
Figure 2. The Recipe controller's new method

The code @recipe = Recipe.new creates a new, empty recipe object and assigns it to the instance variable @recipe. Remember, an instance of the Recipe class represents a row in the recipes database table. When creating a new recipe object, the Recipe class can assign default values for each field that the view template can use.

The Recipe model class doesn't currently set any such default values, but the view template I'll show off momentarily will use whatever is in the @recipe object to initialize the display form. Later, you could add default values in the Recipe class that will show up when you create a new recipe.

As with the edit action, this also retrieves a collection of all categories so that it can display a drop-down list of categories from which the user can choose. The @categories instance variable holds this list of categories.

In the directory c:\rails\cookbook\app\views\recipe, create a file named new.rhtml that contains the HTML template shown below. It's mostly standard HTML, with some extra code to create the <select> and <option> tags for the drop-down list of categories:

				
<html>
<head>
<title>New Recipe</title>
</head>
<body>
<h1>New Recipe</h1>
<form action="/recipe/create" method="post">
<p>
<b>Title</b><br/>
<input id="recipe_title" name="recipe[title]" size="30" type="text" value=""/>
</p>
<p>
<b>Description</b><br/>
<input id="recipe_description" name="recipe[description]"
size="30" type="text" value=""/>
</p>
<p>
<b>Category:</b><br/>
<select name="recipe[category_id]">
<% @categories.each do |category| %>
<option value="<%= category.id %>">
<%= category.name %>
</option>
<% end %>
</select>
</p>
<p>
<b>Instructions</b><br/>
<textarea cols="40" id="recipe_instructions" name="recipe[instructions]"
rows="20" wrap="virtual">
</textarea>
</p>
<input type="submit" value="Create"/>
</form>
<a href="/recipe/list">Back</a>
</body>
</html>

This is not much different from the edit template from Part 1. I left out the recipe's date because I'll set it to the current date when a user posts the form back to the web app. This ensures that the recipe's date will always be its creation date.

If you look at the form tag, you will see that this form will post to a create action in the recipe controller. Edit c:\rails\cookbook\app\controllers\recipe_controller.rb and add this create method:

				
def create
@recipe = Recipe.new(@params['recipe'])
@recipe.date = Date.today
if @recipe.save
redirect_to :action => 'list'
else
render_action 'new'
end
end

This method first creates a new recipe object and initializes it from the parameters posted by the form in new.rhtml. Then it sets the recipe's date to today's date, and tells the recipe object to save itself to the database. If the save is successful, it redirects to the list action that displays all recipes. If the save fails, it redirects back to the new action so the user can try again.

Give it a try. Start the web server by opening a command window, navigating to c:\rails\cookbook, and running the command ruby script\server. Then browse to http://127.0.0.1:3000/recipe/new and add a new recipe like the one shown in Figure 3.

adding a new recipe with a category
Figure 3. Adding a new recipe with a category

After you create the new recipe, you should see something like Figure 4.

list of all recipes
Figure 4. List of all recipes

Deleting a recipe

If you remember from Part 1, once I took over the list action from the scaffolding I no longer had a way to delete a recipe. The list action must implement this. I'm going to add a small delete link after the name of each recipe on the main list page that will delete its associated recipe when clicked. This is easy.

First, edit c:\rails\cookbook\app\views\recipe\list.rhtml and add the delete link by making it look like this:

						
<html>
<head>
<title>All Recipes</title>
</head>
<body>
<h1>Online Cookbook - All Recipes</h1>
<table border="1">
<tr>
<td width="40%"><p align="center"><i><b>Recipe</b></i></td>
<td width="20%"><p align="center"><i><b>Category</b></i></td>
<td width="20%"><p align="center"><i><b>Date</b></i></td>
</tr>
<% @recipes.each do |recipe| %>
<tr>
<td>
<%= link_to recipe.title,
:action => "show",
:id => recipe.id %>
<font size=-1>
   
<%= link_to "(delete)",
{:action => "delete", :id => recipe.id},
:confirm => "Really delete #{recipe.title}?" %>
</font>
</td>
<td><%= recipe.category.name %></td>
<td><%= recipe.date %></td>
</tr>
<% end %>
</table>
<p><%= link_to "Create new recipe", :action => "new" %></p>
</body>
</html>

The main change here is the addition of this link:

						
<%= link_to "(delete)", {:action => "delete", :id
=> recipe.id},
:confirm => "Really delete #{recipe.title}?" %>

This is different from the previous ones. It uses an option that generates a JavaScript confirmation dialog. If the user clicks on OK in this dialog, it follows the link. It takes no action if the user clicks on Cancel.

Try it out by browsing to http://127.0.0.1:3000/recipe/list. Try to delete the Ice Water recipe, but click on Cancel when the dialog pops up. You should see something like Figure 5.

confirm deleting the ice water recipe
Figure 5. Confirm deleting the Ice Water recipe

Now try it again, but this time click on OK. Did you see the results shown in Figure 6?

error deleting the ice water recipe
Figure 6. Error deleting the Ice Water recipe

Alright, I admit it; I did this on purpose to remind you that it's OK to make mistakes. I added a link to a delete action in the view template, but never created a delete action in the recipe controller.

Edit c:\rails\cookbook\app\controllers\recipe_controller.rb and add this delete method:

						
def delete
Recipe.find(@params['id']).destroy
redirect_to :action => 'list'
end

The first line of this method finds the recipe with the ID from the link, then calls the destroy method on that recipe. The second line merely redirects back to the list action.

Try it again. Browse to http://127.0.0.1:3000/recipe/list and try to delete the Ice Water recipe. Now it should look like Figure 7, and the Ice Water recipe should be gone.

ice water recipe is gone
Figure 7. Ice Water recipe is gone

Using layouts

Part 1 used Rails' scaffolding to provide the full range of CRUD operations for categories, but I didn't have to create any links from our main recipe list page. Instead of just throwing in a link on the recipe list page, I want to do something more generally useful: create a set of useful links that will appear at the bottom of every page. Rails has a feature called layouts, which is designed just for things like this.

Most web sites that have common headers and footers across all of the pages do so by having each page "include" special header and footer text. Rails layouts reverse this pattern by having the layout file "include" the page content. This is easier to see than to describe.

Edit c:\rails\cookbook\app\controllers\recipe_controller.rb and add the layout line immediately after the class definition, as shown in Figure 8.

adding a layout to the recipe controller
Figure 8. Adding a layout to the recipe controller

This tells the recipe controller to use the file standard-layout.rhtml as the layout for all pages rendered by the recipe controller. Rails will look for this file using the path c:\rails\cookbook\app\views\layouts\standard-layout.rhtml, but you will have to create the layouts directory because it doesn't yet exist. Create this layout file with the following contents:

						
<html>
<head>
<title>Online Cookbook</title>
</head>
<body>
<h1>Online Cookbook</h1>
<%= @content_for_layout %>
<p>
<%= link_to "Create new recipe",
:controller => "recipe",
:action => "new" %>
  
<%= link_to "Show all recipes",
:controller => "recipe",
:action => "list" %>
  
<%= link_to "Show all categories",
:controller => "category",
:action => "list" %>
</p>
</body>
</html>

Only one thing makes this different from any of the other view templates created so far--the line:

						
<%= @content_for_layout %>

This is the location at which to insert the content rendered by each recipe action into the layout template. Also, notice that I have used links that specify both the controller and the action. (Before, the controller defaulted to the currently executing controller.) This was necessary for the link to the category list page, but I could have used the short form on the other two links.

Before you try this out, you must perform one more step. The previous recipe view templates contain some HTML tags that are now in the layout, so edit c:\rails\cookbook\app\views\recipe\list.rhtml and delete the extraneous lines at the beginning and end to make it look like this:

						
<table border="1">
<tr>
<td width="40%"><p align="center"><i><b>Recipe</b></i></td>
<td width="20%"><p align="center"><i><b>Category</b></i></td>
<td width="20%"><p align="center"><i><b>Date</b></i></td>
</tr>
<% @recipes.each do |recipe| %>
<tr>
<td>
<%= link_to recipe.title,
:action => "show",
:id => recipe.id %>
<font size=-1>
   
<%= link_to "(delete)",
{:action => "delete", :id => recipe.id},
:confirm => "Really delete #{recipe.title}?" %>
</font>
</td>
<td><%= recipe.category.name %></td>
<td><%= recipe.date %></td>
</tr>
<% end %>
</table>

Similarly, edit both c:\rails\cookbook\app\views\recipe\edit.rhtml and c:\rails\cookbook\app\views\recipe\new.rhtml to delete the same extraneous lines. Only the form tags and everything in between should remain.

Browse to http://127.0.0.1:3000/recipe/list, and it should look like Figure 9.

using a layout with common links
Figure 9. Using a layout with common links

The three links at the bottom of the page should now appear on every page displayed by the recipe controller. Go ahead and try it out!

If you clicked on the "Show all categories" link, you probably noticed that these nice new links did not appear. That is because the category pages display through the category controller, and only the recipe controller knows to use the new layout.

To fix that, edit c:\rails\cookbook\app\controllers\category_controller.rb and add the layout line as shown in Figure 10.

adding a layout to the recipe controller
Figure 10. Adding a layout to the category controller

Now you should see the common links at the bottom of all pages of the recipe web application.

Showing recipes in a category

The final task is to add the ability to display only those recipes in a particular category. I'll take the category displayed with each recipe on the main page and turn it into a link that will display only the recipes in that category.

To do this, I'll change the recipe list view template to accept a URL parameter that specifies what category to display, or all categories if the user has omitted the parameter. First, I need to change the list action method to retrieve this parameter for use by the view template.

Edit c:\rails\cookbook\app\controllers\recipe_controller.rb and modify the list method to look like this:

						
def list
@category = @params['category']
@recipes = Recipe.find_all
end

Then edit c:\rails\cookbook\app\views\recipe\list.rhtml to look like this:

						
<table border="1">
<tr>
<td width="40%"><p align="center"><i><b>Recipe</b></i></td>
<td width="20%"><p align="center"><i><b>Category</b></i></td>
<td width="20%"><p align="center"><i><b>Date</b></i></td>
</tr>
<% @recipes.each do |recipe| %>
<% if (@category == nil) || (@category == recipe.category.name)%>
<tr>
<td>
<%= link_to recipe.title,
:action => "show",
:id => recipe.id %>
<font size=-1>
   
<%= link_to "(delete)",
{:action => "delete", :id => recipe.id},
:confirm => "Really delete #{recipe.title}?" %>
</font>
</td>
<td>
<%= link_to recipe.category.name,
:action => "list",
:category => "#{recipe.category.name}" %>
</td>
<td><%= recipe.date %></td>
</tr>
<% end %>
<% end %>
</table>

There are two changes in here that do all the work. First, this line:

						
<% if (@category == nil) || (@category == recipe.category.name)%>

decides whether to display the current recipe in the loop. If the category is nil (there was no category parameter on the URL), or if the category from the URL parameter matches the current recipe's category, it displays that recipe.

Second, this line:

						
<%= link_to recipe.category.name,
:action => "list",
:category => "#{recipe.category.name}" %>

creates a link back to the list action that includes the proper category parameter.

Browse to http://127.0.0.1:3000/recipe/list and click on one of the Snacks links. It should look like Figure 11.

showing only snacks
Figure 11. Showing only snacks

What is it? How long did it take?

That's it! This is a reasonably functional online cookbook application developed in record time. It's a functional skeleton just begging for polish.

Wading through all of the words and screenshots in this article may have obscured (at least somewhat) exactly what this code can do and in what amount of developer time. Let me present some statistics to try to put it all into perspective.

Fortunately, Rails has some built-in facilities to help answer these questions. Open up a command window in the cookbook directory (c:\rails\cookbook) and run the command:

						
rake stats

Your results should be similar to Figure 12. Note that LOC means "lines of code."

viewing development statistics
Figure 12. Viewing development statistics

I won't give a detailed description of each number produced, but the last line has the main figure I want to point out:

						
Code LOC: 47

This says that the actual number of lines of code in this application (not counting comments or test code) is 47. It took me about 30 minutes to create this application! I could not have come even close to this level of productivity in any other web app development framework that I have used.

Maybe you're thinking that this is an isolated experience using an admittedly trivial example. Maybe you're thinking that this might be OK for small stuff, but it could never scale. If you harbor any such doubts, the next section should lay those to rest.

Ruby on Rails Success Stories

Rails is a relatively young framework. As of this writing, it's been barely six months since the first public release. Yet it debuted with such a stunning feature set and solid stability that a vibrant developer community quickly sprang up around it. Within this time frame, several production web applications have been deployed that were built with Ruby on Rails.



lzj520 2006-11-04 16:36 鍙戣〃璇勮
]]>
主站蜘蛛池模板: 亚洲色偷精品一区二区三区| 成人a免费α片在线视频网站| 国产精品亚洲综合| 亚洲欧洲综合在线| 亚洲Av综合色区无码专区桃色| 亚洲精品成人在线| 在线免费观看a级片| 99久久免费中文字幕精品| 好猛好深好爽好硬免费视频| 亚洲精品宾馆在线精品酒店| 91亚洲自偷在线观看国产馆| 亚洲AV无码久久精品成人| 国产av无码专区亚洲国产精品| 日本一区免费电影| 免费精品国产日韩热久久| 久久久久久毛片免费播放| 国产啪精品视频网站免费尤物| 国产免费区在线观看十分钟| xxxxx做受大片在线观看免费| 爱情岛亚洲论坛在线观看 | 中文字幕亚洲免费无线观看日本 | 香蕉视频在线观看亚洲| 亚洲香蕉成人AV网站在线观看| 四虎永久成人免费| 国产一区二区三区免费视频| 暖暖免费高清日本中文| 久久电影网午夜鲁丝片免费| 91在线视频免费播放| 4hu四虎最新免费地址| 亚洲天堂免费在线| 国产桃色在线成免费视频| 99热在线精品免费全部my| 猫咪社区免费资源在线观看 | 亚洲av成人一区二区三区| 亚洲美女一区二区三区| 亚洲精品视频观看| 91亚洲自偷在线观看国产馆| 亚洲AV无码一区二区三区在线| 亚洲成人午夜电影| 亚洲天堂免费在线| 国产成人亚洲综合在线|