<body><script type="text/javascript"> function setAttributeOnload(object, attribute, val) { if(window.addEventListener) { window.addEventListener('load', function(){ object[attribute] = val; }, false); } else { window.attachEvent('onload', function(){ object[attribute] = val; }); } } </script> <div id="navbar-iframe-container"></div> <script type="text/javascript" src="https://apis.google.com/js/platform.js"></script> <script type="text/javascript"> gapi.load("gapi.iframes:gapi.iframes.style.bubble", function() { if (gapi.iframes && gapi.iframes.getContext) { gapi.iframes.getContext().openChild({ url: 'https://www.blogger.com/navbar.g?targetBlogID\x3d4684235500622716427\x26blogName\x3dCaiwangqin\x27s+blog\x26publishMode\x3dPUBLISH_MODE_HOSTED\x26navbarType\x3dBLUE\x26layoutType\x3dCLASSIC\x26searchRoot\x3dhttp://blog.caiwangqin.com/search\x26blogLocale\x3dzh_CN\x26v\x3d2\x26homepageUrl\x3dhttp://blog.caiwangqin.com/\x26vt\x3d3393395200455623441', where: document.getElementById("navbar-iframe-container"), id: "navbar-iframe", messageHandlersFilter: gapi.iframes.CROSS_ORIGIN_IFRAMES_FILTER, messageHandlers: { 'blogger-ping': function() {} } }); } }); </script>

Caiwangqin's blog

Focus on Life, Cloud Service, Smart Hardware, Architecture, Technic and beyond…

wealink 确实挂了

2007年3月29日星期四


ideasfactorychina看到有人说wealink进不去,貌似wealink挂了。登录网站看看,wealink(若邻) 此刻确实挂了,现在时间是2007年3月29日23:50.


标签:

posted by Caiwangqin, 15:57 | Permalink | 0 comments |

acts_as_countable

2007年3月28日星期三

我想找到一个类似acts_as_commentable和acts_as_rateable的plugins,给任何一个object统计阅读次数,结果没有找到。于是开始动手写一个acts_as_countable,由于编写时间较短,该插件虽然实现了counter的功能,但效率低下,而且在page_cache时无法使用,以下是公开的源代码,有需要的朋友可以加以改进。


一、Create Table


CREATE TABLE `counts` (
`id` int(11) NOT NULL auto_increment,
`count` int(11) default ‘1′,
`created_at` datetime NOT NULL,
`countable_id` int(11) NOT NULL default ‘0′,
`countable_type` varchar(15) NOT NULL default ‘’,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;



二、Create plugin


ruby script/generate plugin acts_as_countable



三、Open /vendor/plugins/act_as_countable/lib/act_as_countable.rb and put this code inside:


# ActsAsCountable
module Jesse
module Acts #:nodoc:
module Countable #:nodoc:


def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def acts_as_countable
has_many :counts, :as => :countable, :dependent => :destroy, :o rder => ‘created_at DESC’
include Jesse::Acts::Countable::InstanceMethods
extend Jesse::Acts::Countable::SingletonMethods
end
end
module SingletonMethods
def find_counts_for(obj)
countable = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s
Count.find(:first,
:conditions => [”countable_id = ? and countable_type = ?”, obj.id, countable])
end
end
module InstanceMethods
def counter
Count.find(:first,
:conditions => [”countable_id = ? and countable_type = ?”, id, self.type.name])
end
# Helper method that defaults the submitted time.
def add_count(count)
counter = Count.find(:first, :conditions => [”countable_id = ? and countable_type = ?”, count.countable_id, self.type.name ])
if counter.nil?
counts << count
else
counter.update_attribute(:count, counter.count + 1)
end
end
end

end
end
end



四、Create /vendor/plugins/act_as_countable/lib/count.rb and put this code inside:


class Count < ActiveRecord::Base
belongs_to :countable, :polymorphic => true


def self.find_counts_for_countable(countable_str, countable_id)
find(:first,
:conditions => [”countable_type = ? and countable_id = ?”, countable_str, countable_id]
)
end

def self.find_countable(countable_str, countable_id)
countable_str.constantize.find(countable_id)
end
end



五、put this code inside our /vendor/plugins/act_as_countable/init.rb


require ‘acts_as_countable’
ActiveRecord::Base.send(:include, Jesse::Acts::Countable)



六、How to Use


1. put acts_as_countable inside model, like acts_as_commentable

2. in view action:

count = Count.new
count.countable_id = @obj.id
@obj.add_count(count)



Resources:



标签:

posted by Caiwangqin, 07:42 | Permalink | 0 comments |

Web开发与心理学

2007年3月24日星期六



下午参加了安人心理学工作坊第三期讨论:“Web开发与心理学”。讨论了人因(工程心理学)在Web开发领域的应用,受益非浅。我给大家分享了“Ruby On Rails Web开发与用户体验”,感谢阳志平老师的邀请。


标签:

posted by Caiwangqin, 15:42 | Permalink | 0 comments |

Principles of Marketing

2007年3月22日星期四


世界上最前卫的营销书籍,朋友说聪明一点的人写20页就写完了,他扭来扭去整了一千多页。我决定开始读一读,这是一个挑战。最近晚上不再读梁羽生了 :)


标签:

posted by Caiwangqin, 11:49 | Permalink | 0 comments |

垃圾 Windows Live Mail

2007年3月21日星期三


 



 



 


   


我恨透了Windows Live Mail的垃圾,几乎天天被骚扰,我每次都很认真的帮助他们报告垃圾,但从来不能看到任何改进。如果不是因为MSN上的朋友,真想删除了我的Hotmail邮件帐号。在垃圾制造者面前,微软帝国显得如此可怜。


标签:

posted by Caiwangqin, 16:06 | Permalink | 0 comments |

Ruby on Rails Architecture


标签:

posted by Caiwangqin, 01:03 | Permalink | 0 comments |

从中体倍力到呷哺呷哺

2007年3月20日星期二

晚上去了中体倍力,折腾了一个多小时,就浑身酸痛。以前有过很多次健身计划,都没有能持久执行,这次住得离健身俱乐部较近,希望能坚持,一定要坚持。出来后到呷哺呷哺吃了晚餐,这个不错。


朋友说本周日朝阳公园有竞走活动,决定去凑个数,有参加的朋友结伴啊。


标签:

posted by Caiwangqin, 15:19 | Permalink | 0 comments |

我住在哪里?

2007年3月19日星期一

我觉得回复Where i live? 比What do you do? 更好玩,于是积极响应1416教室摄影游戏



 




标签:

posted by Caiwangqin, 13:36 | Permalink | 0 comments |

Ruby 1.8.6 & Rails 1.2.3

Ruby 1.8.6 Release:要升级请三思:


Ruby 1.8.6 Release了,想看详细情形,或是下载 Source 请看这里。根据 fixneo 先生的提醒,我花了时间去找了一下 Bug Report ,发现到这篇文章。里面写到 Time.to_date 跟 MD5.new 会出现问题。不过这些 Bug 会在 Rails 1.2.3 做修正,也就是应该是 Rails 的问题?


隔天早上,Rails Team 也立刻发表了 Rails 1.2.3,所以要升级 Ruby 1.8.6也请顺便升级 Rails 1.2.3。



为啥我得升级 Ruby 1.8.6:


为什么我得升级 Ruby 1.8.6?会跟 Rails 冲突,还得一定要升到 Rails 1.2.3 才能够跑 Rails。那么麻烦的 Ruby 版本,为啥我一定要升级?我给你一个原因


Ruby 1.8.6 已经将 FastThread包进去了

也就是说灌 Mongrel 应该不用加装 FastThread 了。我们可以合理推测 Ruby Native 支持总比 Gem 支持来的好不是吗 :p
还有一点,随着 Ruby 1.8.6 的推出,看来下一版的 Mongrel 除了 fastThread,也不用装 cgi_multipart_eof_fix 了。 Ruby 的整合度越高,狗皮膏药越来越少总是一件好事。



标签:

posted by Caiwangqin, 11:12 | Permalink | 0 comments |

CN域名降价,caiwangqin.cn遭抢注

 


CN域名降价了,1元就可注册。今天上午准备将我的姓名的CN域名给注册了,结果2007-03-14日已经被别人注册了,郁闷。


标签:

posted by Caiwangqin, 06:14 | Permalink | 0 comments |

该是换阅读工具的时候了

2007年3月16日星期五


前天去拜访了一次FeedSky, 看到WebleonHerock都是使用Google Reader阅读,我的Feedburner分析中显示Google Reader已经占了大部分,俨然已经成为了最多人使用的订阅工具。我之前一直使用Great News,不用其他的原因是Great News记录了我上次阅读的位置,换了工具就不知哪些是更新了。现在决定使用Google Reader了,因为这两天体验不错。


欣欣问了几个问题,并且很热情的请我吃了酷烤。味道非常好。


问:你登录FeedSky后台的频率是多少?


答:我一般不登录FeedSky管理后台,除非需要修改我BLOG的RSS输出,这个不经常。最近左岸邀请我加入了Play with Blog的Network,我会上来看看成员中有哪些人。


问:你比较关心自己BLOG的哪项统计数据?


答:我会关心读我BLOG的人自己的BLOG地址,他们的位置,回访的频率。我一般使用Stat CounterGoogle Analytics提供的服务。


问:你会关心和你写相似内容的BLOG统计数据吗?


答:我不关心别人BLOG的访问量,只会关心他们的内容。


FeedSky的话题广告服务很不错,可能在不久后我们就要用来发布一个新的WEB服务。顺便说一下,UUZONE可能不会恢复了,不要等了,如果真的还有一份不能割舍的情感,那就去友派网吧,还是原班子人为优友创建的新家,当然来到这里得换个名字了,叫派友。倒下的不容易,坚持的更不容易,Good lucky, FeedSky! Good lucky, Web 2.0.


标签:

posted by Caiwangqin, 13:49 | Permalink | 0 comments |

积木式网站建设


Ruby on Rails让做网站像搭积木一样简单,例如使用acts_as_commentable的方法如下:


* Make you ActiveRecord model act as commentable.
class Model < ActiveRecord::Base
    acts_as_commentable
end
* Add a comment to a model instance
model = Model.new
comment = Comment.new
comment.comment = ‘Some comment’
model.comments << comment
* Each comment reference commentable object
model = Model.find(1)
model.comments.get(0).commtable == model



Plugins:


http://agilewebdevelopment.com/plugins/list


Useful code snippets in Ruby and Ruby on Rails



标签:

posted by Caiwangqin, 11:41 | Permalink | 0 comments |

所有网站的归属都是社区

2007年3月15日星期四

人类在物理世界上是以社区的形式居住的,在网上也将是如此。我现在居住在建外SOHO社区,再大点是北京这个社区,中国,亚洲,地球,太阳系。在网上这个组织有太多的断层,是扁平化的,只能从一个网站,然后直接到相当于太阳系的互联网。


然而,没有哪一个网上社区同时解决了一个人最基础的“吃、穿、住、用、行”的需求。网络一开始就到“精神需求”上去了,而把“物质需求”留给了传统市场。如:天涯小说,猫扑杂烩,西祠胡同,豆瓣小组,百度贴吧。


当一个网上社区同时解决了“精神需求”和“物质需求”,那这个社区就成功了。人们入住的理由就不只是“扎堆、闲聊”,而是真实的生活。


象火车的盈利模式是付费乘座,而不是广告。进入网络社区也是一样,入住就需要付费,可以租房入住,也可以购房,交物业管理费。使网上社区成为一个真实的二维世界。


如此看来,未来做网站,就象地产商开发一个新楼盘一样。比的是定位,配套设施,入住人员的品味,不比垃圾人气。哈哈,有趣。


标签:

posted by Caiwangqin, 07:22 | Permalink | 0 comments |

UUZONE走了

2007年3月14日星期三


很多朋友发来消息,问我原因。 我只能抱歉的回答:“对不起,这不是我所能控制的。”


标签:

posted by Caiwangqin, 02:07 | Permalink | 0 comments |

Thinking in Twitter.com

2007年3月11日星期日


Twitter is a new mobile service where simply, you send a text to a website telling it what you are doing at that specific moment. I have talked with Robert Ness about twitter.com, i havn’t sign up yet, i have no reason for sign up.

Robert: How to you think twitter.com ?

我: i do not like it. i do not know why answer the question on that site.

Robert: hmm….what “answer”

我: what are you doing?

for this question?

just for this question?

Robert: i understand

i think for the same reason young people send text messages.

many people send short message just to know “what are youd doing”

i think it is very interesting for the mobile phone

我: i don’t think so.

Robert: i don’t think it is useful for me, because i dont care what my friends are doing, but i think many other people do care

我: it’s my secret what i am doing.

Robert: yes, privacy is important sometimes, sometimes its not. for example, when i am with my wife at home, i will not say so. but if i am in a public place, then i can say so

secrets are not important all the time.

i thinkg it is great for young girls especially

especially because it works with the mobile

我: when i am in a public place, i have to talk with my friend , or walk, no time for anser the question.

Robert: there is if you do it by short message,

people already do this by short message

… …


标签:

posted by Caiwangqin, 11:07 | Permalink | 0 comments |

像我的明星-病毒推广

2007年3月10日星期六


我向来不擅长追赶潮流,在计算机网络中,把极度容易传播的东西叫做“病毒”。既然是病毒,它就会主动的多方面的侵蚀你,如:“微软的Windows Live Messenger公益活动——i’m making a difference,一个是MyHeritage的小游戏——哪些明星长得像我。”(-keso)  它们在我的朋友圈中多次的重复出现,使我不得不关注。


标签:

posted by Caiwangqin, 13:00 | Permalink | 0 comments |

送张卡片给朋友

2007年3月9日星期五

posted by Caiwangqin, 16:07 | Permalink | 0 comments |

Temporary Post Used For Style Detection (d6c457ea-7480-4168-9ed5-c78aa4ffdd04)

This is a temporary post that was not deleted. Please delete this manually. (4bad76f0-de08-4a34-ae25-d013d0b650c7)


标签:

posted by Caiwangqin, 16:02 | Permalink | 0 comments |

一厘米的高度

2007年3月8日星期四

麦田蚂蚁社区成立了“一厘米社区学习小组”,拿豆瓣网开刀。让豆瓣的真相全面展现:


思践:《豆瓣的吸星大法》


Hilaa:《点评豆瓣》

何田:《豆瓣社区资源的商业转换》

麦田:《豆瓣的真相》



很有意思,遗憾的是都是站在社区经营者的立场去评论的,没有一个用户的声音。以下这段话揭示了目前中文社区的一个方面,有点滑稽,但都是实事:


如同木子美拯救了blogcn和blogchina;如同竹影青瞳拯救了天涯;如同超女拯救了百度贴吧;如同徐静蕾拯救了新浪博客;如同张钰拯救了优酷;如同每一个伟大的网站背后都有一个女人。同样的,蛰伏的豆瓣也在等待它背后的那个女人,而不是在等待――阿北的算法。

无论是否高低学历,年轻的中文网络用户在目标网站上都只做了一件事――扎堆、闲聊。从天涯,到百度,到豆瓣,中国网民在任何一个交互网站上,都做着同一件事――扎堆、闲聊。



标签:

posted by Caiwangqin, 01:36 | Permalink | 0 comments |

Ruby on Rails Command Rerference

2007年3月7日星期三


1.1.Rails
1.1 创建一个Rails应用程序
 $ rails app_name
 可选项:
 -d, database=xxx 指定安装一个数据库(mysql oracle postgresql sqlite2 sqlite3 ), 默认情况下是数据库
 -r, ruby-path= 指定Ruby的安装路径,如果没有指定,scripts使用env去找Ruby
 -f, freeze (冻结)freezes Rails在vendor/rails目录


1.2 API Documentation
 $ gem_server
 启动一个WEBrick服务器。这时候你可以通过Http://localhost:8808/ 打开浏览器去查看rails API文档


1.3 Rake
 rake db:fixtures:load
  # 载入fixtures到当前环境的数据库
  # 载入指定的fixtures使用FIXTURES=x,y
 rake db:migrate
 # 迁移数据库通过在db/migrate目录下的脚本.可以指定版本号通过VERSION=x
 rake db:schema:dump
 # 创建一个db/schema.rb文件,通过AR能过够支持任何数据库去使用
 rake db:schema:load
 # 再入一个schema.rb文件进数据库
 rake db:sessions:clear
 # 清空sessions表
 rake db:sessions:create
 # 用CGI::Session::ActiveRecordStore创建一个sessions表为用户
 rake db:structure:dump
 # 导出数据库结构为一个SQL文件
 rake db:test:clone
 # 重新创建一个测试数据库从当前环境数据库中
 rake db:test:clone_structure
 # 重新创建测试数据库从开发模式数据库
 rake db:test:prepare
 # 准备测试数据库并在入schema
 rake db:test:purge
 # 清空测试数据库
 rake doc:app
 # 创建HTML文件的API Documentation
 rake doc:clobber_app
 # 删除Documentation
 rake doc:clobber_plugins
 # 删除 plugin Documentation
 rake doc:clobber_rails
 # 删除Documentation
 rake doc:plugins
 # 产生Documation为所有安装的plugins
 rake doc:rails
 # 创建HTML文件的API Documentation
 rake doc:reapp
 # 强制重新创建HTML文件的API Documentation
 rake doc:rerails
 # 强制重新创建HTML文件的API Documentation
 rake log:clear
 # 清空目录log/下的所有日志文件
 rake rails:freeze:edge
 # Lock this application to latest Edge Rails. Lock a specific revision with REVISION=X
 rake rails:freeze:gems
 # Lock this application to the current gems (by unpacking them into vendor/rails)
 rake rails:unfreeze
 # Unlock this application from freeze of gems or edge and return to a fluid use of system gems
 rake rails:update
 # Update both scripts and public/javascripts from Rails
 rake rails:update:javascripts
 # Update your javascripts from your current rails install
 rake rails:update:scripts
 # Add new scripts to the application script/ directory
 rake stats
 # Report code statistics (KLOCs, etc) from the application
 rake test
 # Test all units and functionals
 rake test:functionals
  # Run tests for functionalsdb:test:prepare
 rake test:integration
 # Run tests for integrationdb:test:prepare
 rake test:plugins
 # Run tests for pluginsenvironment
 rake test:recent
 # Run tests for recentdb:test:prepare
 rake test:uncommitted
 # Run tests for uncommitteddb:test:prepare
 rake test:units
 # Run tests for unitsdb:test:prepare
 rake tmp:cache:clear
 # 清空tmp/cache目录下的所有文件
 rake tmp:clear
 # 清空session, cache, 和socket文件从tmp/目录
 rake tmp:create
 # 为sessions, cache, and sockets创建tmp/目录
 rake tmp:sessions:clear
 # 清空所有在tmp/sessions目录下的文件
 rake tmp:sockets:clear
 # 清空所有在tmp/sessions 目录下的ruby_sess.* 文件


1.4 Scripts
 script/about
 # 输出当前环境信息
 script/breakpointer
 # 启动断点server
 script/console
 # 启动交换式的Rails控制台
 script/destroy
 # 删除通过generators创建的文件
 script/generate
 # -> generators
 script/plugin
 # -> Plugins
 script/runner
 # 执行一个任务在rails上下文中
 script/server
 # 启动开发模式服务器http://localhost:3000
 //以下几个不知道怎么去使用
 script/performance/profiler
 script/performance/benchmarker
 script/process/reaper
 script/process/spawner


1.5 Generators
 ruby script/generate model ModelName
 ruby script/generate controller ListController show edit
 ruby script/generate scaffold ModelName ControllerName
 ruby script/generate migration AddNewTable
 ruby script/generate plugin PluginName
 ruby script/generate mailer Notification lost_password signup
 ruby script/generate web_service ServiceName api_one api_two
 ruby script/generate integration_test TestName
 ruby script/generate session_migration
 可选项:
 -p, –pretend Run but do not make any changes.
 -f, –force Overwrite files that already exist.
 -s, –skip Skip files that already exist.
 -q, –quiet Suppress normal output.
 -t, –backtrace Debugging: show backtrace on errors.
 -h, –help Show this help message.
 -c, –svn Modify files with subversion. (Note: svn must be in path) 


1.6 Plugins
 script/plugin discover
 # discover plugin repositories
 script/plugin list
 # list all available plugins
 script/plugin install where
 # install the a€wherea€? plugin
 script/plugin install -x where
 # install where plugin as SVN external
 script/plugin install http://invisible.ch/projects/plugins/where
 script/plugin update
 # update installed plugins
 script/plugin source
 # add a source repository
 script/plugin unsource
 # removes a source repository
 script/plugin sources
 # lists source repositories


 


Source: http://www.javaeye.com/article/43500


标签:

posted by Caiwangqin, 05:25 | Permalink | 0 comments |

BLOG恢复访问

2007年3月6日星期二

经过了两天的努力,我的BLOG终于又可以访问了。原因是朋友提供的dreamhost到期了,我于是又搬家了,希望可以得到一段长时间的稳定。


看到了freeman的留言:三月文章收藏。都是关于UUZONE的,谢谢。


此刻我在北京,从北京的高楼里看窗外的烟花,看中国的互联网,看 剩者为王。昨天中午会见龙源期刊网的周永德时,也提到有一个规律是后进电梯里的人先出来。我想有一个途径可能可以避开这个规律,那就是进不同的电梯。


标签:

posted by Caiwangqin, 03:55 | Permalink | 0 comments |

Ruby on Rails Caching Tutorial

2007年3月2日星期五


This useful article from Rails Envy, I’ve used Caching on my application, but not write any article about it, now you can get it here.


 


It’s not the size of the app that matters, it’s how you code it


When your Ruby On Rails Website gets famous you’re going to wish you implemented proper caching. Are you worried? Maybe just a little?


This tutorial is going to show everything you need to know to use Caching in your Rails applications, so when you get digg’d or slashdot’d you won’t be left begging your hosting provider for more bandwidth.


Shakespeare Says


Since there are so many different types of caching, I’m going to split this up into several blog entries. Each one will build on the previous, talking about more complex types of caching and how to implement them. We’ll even discuss some advanced caching plugins people have written for customized caching.


Today we’re going to dive into the FASTEST rails caching mechanism, page caching!


When your Ruby On Rails Website gets famous you’re going to wish you implemented proper caching. Are you worried? Maybe just a little?


This tutorial is going to show everything you need to know to use Caching in your Rails applications, so when you get digg’d or slashdot’d you won’t be left begging your hosting provider for more CPU processing power.


Shakespeare Says


Since there are so many different types of caching, I’m going to split this up into several blog entries. Each one will build on the previous, talking about more complex types of caching and how to implement them. We’ll even discuss some advanced caching plugins people have written for customized caching.


Today we’re going to dive into the FASTEST rails caching mechanism, page caching!


Table of Contents



  1. Why for art thou caching?
  2. Configuration
  3. Page Caching
  4. Page caching with pagination
  5. Cleaning up your cache
  6. Sweeping up your mess
  7. Playing with Apache/Lighttpd
  8. Moving your cache
  9. Clearing out your whole/partial cache
  10. Advanced page caching techniques
  11. Testing your page caching
  12. Conclusion


 

Why for art thou caching?


(Feel free to skip this if you’re a l33t hax0r)

Ruby is what we call an “Interpreted Programming Language” (as you probably already know). What this means is that your code does not get translated into machine code (the language your computer talks) until someone actually runs it.


If you’re a PHP developer, you’re probably saying “No Duh!” about now. PHP is also an “Interpreted Language”. However, Java code on the other hand needs to be compiled before it can be executed.


Unfortunately this means that every time someone surfs onto your Ruby on Rails website, your code gets read and processed that instant. As you can probably imagine, handling more than 100 requests a second can take great deal of processor power. So how can we speed things up?

Caching!


Caching, in the web application world, is the art of taking a processed web page (or part of a webpage), and storing it in a temporary location. If another user requests this same webpage, then we can serve up the cached version.


Loading up a cached webpage can not only save us from having to do ANY database queries, it can even allow us to serve up websites without touching our Ruby on Rails Server. Sounds kinda magical doesn’t it? Keep on reading for the good stuff.


Before we get our feet wet, there’s one small configuration step you need to take..


 

Configuration



There’s only one thing you’ll need to do to start playing with caching, and this is only needed if you’re in development mode. Look for the following line and change it to true in your /config/environments/development.rb:












config.action_controller.perform_caching = true


Normally you probably don’t want to bother with caching in development mode, but we want try it out already!


 


Page Caching


Page caching is the FASTEST Rails caching mechanism, so you should do it if at all possible. Where should you use page caching?



  • If your page is the same for all users.
  • If your page is available to the public, with no authentication needed.


If your app contains pages that meet these requirements, keep on reading. If it doesn’t, you probably should know how to use it anyways, so keep reading!


Say we have a blog page (Imagine that!) that doesn’t change very often. The controller code for our front page might look like this:










1
2
3
4
5


class BlogController < ApplicationController
def list
Post.find(:all, :order => created_on desc, :limit => 10)
end


As you can see, our List action queries the latest 10 blog posts, which we can then display on our webpage. If we wanted to use page caching to speed things up, we could go into our blog controller and do:










1
2
3
4
5
6
7


class BlogController < ApplicationController
caches_page :list

def list
Post.find(:all, :order => created_on desc, :limit => 10)
end


The “caches_page” directive tells our application that next time the “list” action is requested, take the resulting html, and store it in a cached file.


If you ran this code using mongrel, the first time the page is viewed your /logs/development.log would look like this:










1
2
3
4
5
6


Processing BlogController#list (for 127.0.0.1 at 2007-02-23 00:58:56) [GET]
Parameters: {action=>list, controller=>blog}
SELECT * FROM posts ORDER BY created_on LIMIT 10
Rendering blog/list
Cached page: /blog/list.html (0.00000)
Completed in 0.18700 (5 reqs/sec) | Rendering: 0.10900 (58%) | DB: 0.00000 (0%) | 200 OK [http://localhost/blog/list]


See the line where it says “Cached page: /blog/list.html”. This is telling you that the page was loaded, and the resulting html was stored in a file located at /public/blog/list.html. If you looked in this file you’d find plain html with no ruby code at all.


Subsequent requests to the same url will now hit this html file rather then reloading the page. As you can imagine, loading a static html page is much faster than loading and processing a interpreted programming language. Like 100 times faster!


However, it is very important to note that Loading Page Cached .html files does not invoke Rails at all! What this means is that if there is any content that is dynamic from user to user on the page, or the page is secure in some fashion, then you can’t use page caching. Rather you’d probably want to use action or fragment caching, which I will cover in part 2 of this tutorial.


What if we then say in our model:













caches_page :show


Where do you think the cached page would get stored when we visited “/blog/show/5″ to show a specific blog post?


The answer is /public/blog/show/5.html


Here are a few more examples of where page caches are stored.:










1
2
3
4
5


http://localhost:3000/blog/list => /public/blog/list.html
http://localhost:3000/blog/edit/5 => /public/edit/5.html
http://localhost:3000/blog => /public/blog.html
http://localhost:3000/ => /public/index.html
http://localhost:3000/blog/list?page=2 => /public/blog/list.html


Hey, wait a minute, notice how above the first item is the same as the last item. Yup, page caching is going to ignore all additional parameters on your url.


 


But what if I want to cache my pagination pages?


Very interesting question, and a more interesting answer. In order to cache your different pages, you just have to create a differently formed url. So instead of linking “/blog/list?page=2″, which wouldn’t work because caching ignores additional parameters, we would want to link using “/blog/list/2″, but instead of 2 being stored in params[:id], we want that 2 on the end to be params[:page].


We can make this configuration change in our /config/routes.rb










1
2
3
4
5


map.connect blog/list/:page,
:controller => blog,
:action => list,
:requirements => { :page => /d+/},
:page => nil


With this new route defined, we can now do:













<%= link_to Next Page, :controller => blog, :action => list, :page => 2 %>


the resulting url will be “/blog/list/2″. When we click this link two great things will happen:



  1. Rather than storing the 2 in params[:id], which is the default, the application will store the 2 as params[:page],
  2. The page will be cached as /public/blog/list/2.html


The moral of the story is; If you’re going to use page caching, make sure all the parameters you require are part of the URL, not after the question mark! Many thanks to Charlie Bowman for inspiration.


 


Cleaning up the cache



You must be wondering, “What happens if I add another blog post and then refresh /blog/list at this point?”


Absolutely NOTHING!!!


Well, not quite nothing. We would see the /blog/list.html cached file which was generated a minute ago, but it won’t contain our newest blog entry.


To remove this cached file so a new one can be generated we’ll need to expire the page. To expire the two pages we listed above, we would simply run:










1
2
3
4
5


# This will remove /blog/list.html
expire_page(:controller => blog, :action => list)

# This will remove /blog/show/5.html
expire_page(:controller => blog, :action => show, :id => 5)


We could obviously go and add this to every place where we add/edit/remove a post, and paste in a bunch of expires, but there is a better way!  


Sweepers


Sweepers are pieces of code that automatically delete old caches when the data on the cached page gets old. To do this, sweepers observe of one or more of your models. When a model is added/updated/removed the sweeper gets notified, and then runs those expire lines I listed above.


Sweepers can be created in your controllers directory, but I think they should be separated, which you can do by adding this line to your /config/environment.rb.










1
2
3
4
5


Rails::Initializer.run do |config|
# …
config.load_paths += %W( #{RAILS_ROOT}/app/sweepers )
# …
end


(don’t forget to restart your server after you do this)


With this code, we can create an /app/sweepers directory and start creating sweepers. So, lets jump right into it. /app/sweepers/blog_sweeper.rb might look like this:










1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27


class BlogSweeper < ActionController::Caching::Sweeper
observe Post # This sweeper is going to keep an eye on the Post model

# If our sweeper detects that a Post was created call this
def after_create(post)
expire_cache_for(post)
end

# If our sweeper detects that a Post was updated call this
def after_update(post)
expire_cache_for(post)
end

# If our sweeper detects that a Post was deleted call this
def after_destroy(post)
expire_cache_for(post)
end

private
def expire_cache_for(record)
# Expire the list page now that we posted a new blog entry
expire_page(:controller => blog, :action => list)

# Also expire the show page, incase we just edited a blog entry
expire_page(:controller => blog, :action => show, :id => record.id)
end
end


NOTE: We can call “after_save”, instead of “after_create” and “after_update” above, to dry out our code.


We then need to tell our controller when to invoke this sweeper, so in /app/controllers/BlogController.rb:










1
2
3
4


class BlogController < ApplicationController
caches_page :list, :show
cache_sweeper :blog_sweeper, :only => [:create, :update, :destroy]


If we then try creating a new post we would see the following in our logs/development.log:










1
2


Expired page: /blog/list.html (0.00000)
Expired page: /blog/show/3.html (0.00000)


That’s our sweeper at work!


 


Playing nice with Apache/Lighttpd



When deploying to production, many rails applications still use Apache as a front-end, and dynamic Ruby on Rails requests get forwarded to a Rails Server (Mongrel or Lighttpd). However, since we are actually pushing out pure html code when we do caching, we can tell Apache to check to see if the page being requested exists in static .html form. If it does, we can load the requested page without even touching our Ruby on Rails server!


Our httpd.conf might look like this:










1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19


<VirtualHost *:80>

# Configure mongrel_cluster
<Proxy balancer://blog_cluster>
BalancerMember http://127.0.0.1:8030
</Proxy>

RewriteEngine On
# Rewrite index to check for static
RewriteRule ^
/
$ /index.html [QSA]

# Rewrite to check for Rails cached page
RewriteRule ^([^.]+)$ $1.html [QSA]

# Redirect all non-static requests to cluster
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule ^/(.*)$ balancer://blog_cluster%{REQUEST_URI} [P,QSA,L]

</VirtualHost>


In lighttpd you might have:










1
2
3


server.modules = ( mod_rewrite, … )
url.rewrite += ( ^/$ => /index.html )
url.rewrite += ( ^([^.]+)$ => $1.html )


The proxy servers will then look for cached files in your /public directory. However, you may want to change the caching directory to keep things more separated. You’ll see why shortly.


 


Moving your Page Cache


First you’d want to add the following to your /config/environment.rb:













config.action_controller.page_cache_directory = RAILS_ROOT + /public/cache/


This tells Rails to publish all your cached files in the /public/cache directory. You would then want to change your Rewrite rules in your httpd.conf to be:










1
2
3
4
5


  # Rewrite index to check for static
RewriteRule ^/$ cache/index.html [QSA]

# Rewrite to check for Rails cached page
RewriteRule ^([^.]+)$ cache/$1.html [QSA]


 


Clearing out a partial/whole cache


When you start implementing page caching, you may find that when you add/edit/remove one model, almost all of your cached pages need to be expired. This could be the case if, for instance, all of your website pages had a list which showed the 10 most recent blog posts.


One alternative would be to just delete all your cached files. In order to do this you’ll first need to move your cache directory (as shown above). Then you might create a sweeper like this:










1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19


class BlogSweeper < ActionController::Caching::Sweeper
observe Post

def after_save(record)
self.class::sweep
end

def after_destroy(record)
self.class::sweep
end

def self.sweep
cache_dir = ActionController::Base.page_cache_directory
unless cache_dir == RAILS_ROOT+/public
FileUtils.rm_r(Dir.glob(cache_dir+/*)) rescue Errno::ENOENT
RAILS_DEFAULT_LOGGER.info(Cache directory ‘#{cache_dir}‘ fully sweeped.)
end
end
end


That FileUtils.rm_r simply deletes all the files in the cache, which is really all the expire_cache line does anyways. You could also do a partial cache purge by only deleting a cache subdirectory. If I just wanted to remove all the caches under /public/blog I could do:










1
2


        cache_dir = ActionController::Base.page_cache_directory
FileUtils.rm_r(Dir.glob(cache_dir+/blog/*)) rescue Errno::ENOENT


If calling these File Utilities feels too hackerish for you, Charlie Bowman wrote up the broomstick plugin which allows you to “expire_each_page” of a controller or action, with one simple call.  


Needing something more advanced?



Page caching can get very complex with large websites. Here are a few notable advanced solutions:


Rick Olson (aka Technoweenie) wrote up a Referenced Page Caching Plugin which uses a database table to keep track of cached pages. Check out the Readme for examples.


Max Dunn wrote a great article on Advanced Page Caching where he shows you how he dealt with wiki pages using cookies to dynamically change cached pages based on user roles.


Lastly, there doesn’t seem to be any good way to page cache xml files, as far as I’ve seen. Mike Zornek wrote about his problems and figured out one way to do it. Manoel Lemos figured out a way to do it using action caching. We’ll cover action caching in the next tutorial.


 


How do I test my page caching?


There is no built in way to do this in rails. Luckily Damien Merenne created a swank plugin for page cache testing. Check it out!


 


Conclusions


Page caching should be used if at all possible in your project, because of the awesome speeds it can provide. However, if you have a website with a member system where authentication is needed throughout, then you might not be able to do much with it outside of a login and new member form.


Let me know if you think I missed anything substantial. Next up in part 2 of the caching tutorial will be Action and Content-Only caching.


Stay oh so very tuned.




Credits:
Thanks to Paul Davis and Jason Seifer for help with proofing this article.
Shakespeare Comic design by Erika Greco
Shakespeare original image - guuskrahe - Flickr - http://www.flickr.com/photos/krahe/
All the rest - Open Clip Art Gallery - Public Domain (which means I really don’t need to be writing this, so I’m going to bed, I hope you learned a lot today, goodnight.)



Link to Ruby on Rails Caching Tutorial


标签:

posted by Caiwangqin, 02:31 | Permalink | 0 comments |

蛙爪上线了

Ya, Yee看到蛙爪上线了,一个月前蛙爪网络媒体在广州招务精英。感觉是MyBlogLog的克隆,使用上还有些问题,但还是有机会成为最好的中国的博客社交站点,他们行动好快。


“Any successful Web2.0 startup will be copied by Chinese in short time”. -by Yee


标签:

posted by Caiwangqin, 02:02 | Permalink | 0 comments |

洪波(KESO)与刘韧创办五季咨询公司



新浪科技摘要:(图片来源新浪科技)


著名博客洪波已决定与Donews制作人刘韧联手创业,成立名为“五季咨询”的互联网咨询公司,短期内通过出售已有的经验、人脉,解决离开千橡后的收入来源问题,同时寻求建立Web2.0时代的互联网咨询公司品牌。


据悉,该公司将采用“合伙人”的方式进行运作,建立松散的协作模式,独立的咨询师个人将从具体客户处获得绝大多数收益。目前该公司除了洪波与刘韧两名创办者外,还将由另一位Donews博客老白担任咨询师。


在新成立的五季咨询里,将形成以洪波为主导,刘韧与老白为辅助的局面。新的公司短期内的发展目标,旨在解决洪波离职千橡后的收入来源问题。


“每个月有1万来块钱,我的生活就能过得很轻松了。”洪波表示,先期将主要是通过发展几位客户养活自己,而长期则希望打造Web2.0时代的咨询品牌。


据悉,目前王微的土豆网、王怀南的宝宝树以及红鼎投资已成为洪波签约客户,今后主要通过已有的人脉关系不断拓展新客户。



相关阅读:独家专访洪波:五季咨询可能是最终归宿


Blogsir的评论:“五季咨询”不难让人想到与Donews原有的5G评论之间的密切关系。05年开始,每到周末,DoNews都在5G(办公室门牌号)举行聚会,进行无主题的讨论。参与人员大多是与Donews关系良好的IT、互联网业内人士或知名Blogger。虽然陌生人加入比较困难,但通过整理出的文字与录音,网友还是可以了解相关内容。通过Donews的平台尤其是KESO、老白等人的影响,越来越多的Blogger参与到5G评论中,甚至开始在各地(上海、广州、深圳和武汉)也开始吸收成员加入。(5G评论录音


由于技术及资源门槛很低,导致如今Web2.0公司多如牛毛,可真正象样的却不多见。许多创业者缺少的正是如何获取人脉及相关经验。KESO此次利用已有的人脉、经验及影响力,为Web2.0公司提供帮助并换取价值体现,是件对网络公司、KESO和网友都有利的事。这其实也是必然的结果,只不过催化剂是千橡不在容忍他的特立独行。与刘韧的二次合作创业,主角是KESO,但刘的大力支持也多为自己铺了一条后路。 



Link to 洪波(KESO)与刘韧创办五季咨询公司


标签:

posted by Caiwangqin, 01:39 | Permalink | 0 comments |