安装Rails 4.0 beta

2012年12月16日 21:56

首先当然要按照先前的《Fedora / Debian 用 RVM 安装 Ruby on Rails 最新版本》先安装RVM和Ruby 1.9.3,Rails 3.2.9(Ruby至少依赖到1.9.3哦,至于安装Rails 3.2.9,是因为似乎Rails 4.0的部分组件依然在依赖3.2.9的Rails组件,挺奇怪的),然后

git clone https://github.com/rails/rails.git

下载rails源码,接着,运行一次

bundle install

安装所有依赖的gem(务必保证所有gem都要安装成功),接着运行

ruby install.rb 4.0.0.beta

额,理论上这样安装应该是成功的,但是实际上当我运行install.rb的时候,却出现了如下错误:

Installing activesupport...
Installing activemodel...
Installing activerecord...
ERROR:  While executing gem ... (Gem::DependencyError)
    Unable to resolve dependencies: activerecord requires activerecord-deprecated_finders (= 0.0.1)
Installing actionpack...
ERROR:  While executing gem ... (Gem::DependencyError)
    Unable to resolve dependencies: actionpack requires journey (~> 2.0.0)
Installing actionmailer...
ERROR:  While executing gem ... (Gem::DependencyError)
    Unable to resolve dependencies: actionmailer requires actionpack (= 4.0.0.beta)
Installing railties...
ERROR:  While executing gem ... (Gem::DependencyError)
    Unable to resolve dependencies: railties requires actionpack (= 4.0.0.beta)
Installing Rails...
ERROR:  While executing gem ... (Gem::DependencyError)
    Unable to resolve dependencies: activemodel requires activesupport (= 3.2.9); rails requires actionpack (= 4.0.0.beta), activerecord (= 4.0.0.beta), actionmailer (= 4.0.0.beta), railties (= 4.0.0.beta); sprockets-rails requires actionpack (>= 3.0)

很奇怪,虽然bundle install完全安装成功了,但是这些明明出现在Gemfile中的gem却没有安装成功,我的解决方案是,额,手动再安装一遍:

cd `rvm gemdir`/bundler/gems
for f in `ls`; do cd `pwd`/$f; gem build *.gemspec; gem install *.gem; done

这样就可以了

最后运行下

rails -v
# Rails 4.0.0.beta

看到4.0.0 beta就算OK了

不过这样似乎还不足以创建一个新的Rails 4.0 App,你必须再安装好新的coffee-rails 4.0.0.beta和sass-rails 4.0.0.beta,这两个项目你依然需要通过git clone下两个项目的源码,bundle install(在运行这句命令前最好把Gemfile中的几个github项目勾掉,每次都下载一遍实在太慢了),然后gem build *.gemspec,gem install *.gem后才能安装成功。

快尝试创建一个Rails 4的项目吧:

然后在玩玩Live Stream这个Rails 4的新特性,看着SSE数据流连续不断的送到浏览器,帅爆了呢~

这篇文章只是随手写写玩玩的,我对Ruby的Block和Lambda其实理解并不怎么深刻,知识现在够用就差不多了吧。

第一种方法最传统了:

def f1(a)
  yield a
end

f1(1){|a| a.to_s} # "1"
f1(1, &:to_s) # "1" 

这种方法要求传入一个block,但是不传入也可以,可以用block_given?方法判断是否被传入了block。在第二个示例中虽然看上去要求是一个参数,但是再传入一个block并不会出错,&:to_s是{|a| a.to_s}这个block的简写

下面是第二种方法:

def f2(a, &b)
  b[a]
end

f2(1){|a| a.to_s} # "1"
f2(1, &:to_s) # "1"

这种方法和前一种其实效果一致,虽然看上去要求两个参数,但是block依旧是可选的,如果不传也不会出错,一样可以用block_given?方法判断是否被传入了block。唯一的区别就是这种方法可以为传入的block赋个值,以便于在再传给另外一个方法作为参数。

下面是第三种方法:

def f3(a, b)
  b[a]
end

f3(1, lambda{|a| a.to_s}) # "1"
f3(1, :to_s.to_proc) # "1"

这种方法虽然和第二种方法只有一个字符之差,但其实天差地远,b不能被传入一个block,无论是{|a| a.to_s}还是&:to_s都将被视为错误,它必须被传入一个lambda。因此在示例中我用两种方法创建了lambda,注意第二个示例其实是第一个示例的简写,在Ruby中&:to_s只能创建block,而:to_s.to_proc则可以创建proc对象(这里有件更加神奇的事,可以通过hack to_proc修改这个方法的返回值,但是依然要求必须是Proc对象,并且无法通过hack这个Proc对象的call方法或是[]方法修改它的执行行为,并且写在这个Proc中的return,next,break的语意也只按proc的语意处理,下面会提及)。至于proc,lamdba和block三者的差异?lamdba和proc其实差别很小,主要差异在binding和参数传递上,不过lambda {|a| a.to_s}其实也返回一个Proc对象,而lambda和block的区别主要是返回方法不一致,这个在很多Ruby书中都有详细介绍。但是它们其实可以相互转换,看下列代码:

def f4(a, &b)
  f3(a, b)
end

f4(1, &:to_s) # "1"

def f5(a, b)
  f1(a, &b)
end

f5(1, :to_s.to_proc) # "1"

可以看到在第一个示例中,一个方法调用时传入的block参数在方法内部会被当作成proc对象,因而f3也可以接受。而在第二个示例中,一个proc对象前面加上&符号跟在方法后面就被当作block,很有意思吧。更有意思的是,似乎无论怎么转换,代码块中return,next,break语句的语意似乎并不改变,几次试验下来都是如此,这点我至今还是没有想通。例如:

def f6(&b)
  puts "class of b is: #{b.class.inspect}"
  3.times {b.call}
end

def f7(b)
  puts "class of b is: #{b.class.inspect}"
  3.times {b.call}
end

f6 {puts 1; break}
# output:
# class of b is: Proc
# 1

f7 lambda {puts 1; break}
# class of b is: Proc
# 1
# 1
# 1

可以看到虽然都是在方法中调用代码块,并且在方法中b都是proc对象,代码内容也完全一致,但是由于f6传入的block形式而f7中传入的lambda形式,因此最后的结果存在差异。证明了break的语意并没有因为都转换成了proc对象而发生转变。

由于我所维护的Rails项目规模很大,所以被分割成了多个项目,而其中仅Rails项目就有不下6个,而我在开发我所属的子项目的时候,通常至少同时运行4个项目才能把流程完整的运行起来。而这四个项目中就有三个在生产环境上会监听默认端口(80/443),因此,这些项目的开发者都希望当其他项目与自己的项目产生交互的时候,会采用默认的端口。也许他们在自己开发的时候就是这么做的并且没有发生什么问题,但是作为另一个项目的开发者,遇到这种情况就非常麻烦了。虽然我们有时都会留下一个配置文件供开发者配置,但是由于这个配置文件本身也被Git托管(这个做法其实很不正规),所以我一旦修改配置文件,这个文件就至始至终保持被修改的状态,非常麻烦,经常干扰到我原本华丽而流畅的Git操作(瀑布汗。。)。

所以就希望能寻找到一种办法,能够使服务器不再根据端口,而是根据域名判断请求应该送达的服务器。这个功能其实Apache,Nginx等网页服务器都具备,但是开发环境下的Rails服务器不比真正的网页服务器,没有这种功能。

当然,不能直接简单地用个网页服务器了事,因为传统网页服务器不能实现对Ruby代码的Debug(何况最近还特别迷恋pry神器),而这是我需要的基本功能。除此以外,我还需要它的路由层次必须高于/etc/hosts文件,因为我们还要时常切换到QA环境或是生产环境,而我们切换的方法就是修改/etc/hosts。所以在防火墙甚至于更底层实现都是我不能接受的。

于是我在Twitter和Ruby China发起了提问(http://ruby-china.org/topics/6102),收到了不少响应,比如RVM+Pow的手法,确实不曾听说过,感觉不错的样子。还有Passenger + Apache的办法,来自于Railscasts介绍的办法:http://railscasts.com/episodes/122-passenger-in-development。不过,我更希望的是一种更加简单的办法,不需要复杂的GUI控制,完全透明,而且最好是平台无关的,毕竟我比起Mac OS X还是更希望用Linux开发,因此不希望采用Mac Only的办法。

在反复思虑后,选择了@RainFlying提出的用Apache/Nginx做反向代理的办法,将发送到Apache/Nginx的请求通过代理转发到真正的Rails服务器上,以此实现了让多个Rails App时同时监听80端口的假象。

于是,我就在目前正在使用的Ubuntu 12.04上安装了nginx-light包,这个包比起nginx-full来说仅仅包含了最核心的nginx,不带过多的Modules,保持环境的轻量。然后在/etc/nginx/sites-available里创建了一个新文件,内容如下:

server {
        server_name test1.com;
        location / {
                proxy_pass http://localhost:3000/;
        }
}

server {
        server_name test2.com;
        location / {
                proxy_pass http://localhost:3001/;
        }
}

由于每个站点的配置仅有简单的几行,因此我把所有配置都包含在一个文件里,看上去简洁明了。

然后修改/etc/hosts文件,将域名指向本地服务器。这个步骤也暗示了我随时随地可以通过修改/etc/hosts切换环境,而不需要任何其他操作,完全和以前一样。

最后运行

sudo service nginx restart

重启Nginx。虽然网上有说用

sudo service nginx reload

也可以,但是我在尝试后发现似乎并没能成功更新配置信息,因此还是使用restart命令。

到这里,其实反向代理因此可以工作了,但是由于Nginx服务器毕竟不比为开发设计的Rails服务器,所有的连接都有超时时间。如果在连接时进入了Debug模式,我希望连接能够一直保持等待直到Debug结束,在查阅了Nginx的文档后,发现可以用proxy_read_timeout选项设置Nginx等待被代理服务器的响应时间,默认为60秒,我将这个时间设置到了36000秒,即10个小时,差不多足够了吧。

明天就把这个方法在团队中推广下,大家以后不用再抢端口了吧。

RubyGem Introduction

2012年10月15日 00:53

这是我下次做关于RubyGem Presentation的Slides,这次花了较多的时间来准备,因为一直感觉做不到太多的可以讲的素材,如果只是按照Team Leader的要求讲如何写一个RubyGem,我五分钟就可以讲完了,这样就不是很有意思了。后来由于掌握了一些阅读Rails源码的技巧,发现RubyGem和GemBundler的运作原理绝对是个不错的演讲题材,因此在制作这个Slides的过程中,我一方面自己翻阅资料,阅读源码,另一方面放了些小提示进去,启发大家思考,鼓励大家也通过阅读源码来彻底的了解RubyGem这个东西。

不过按照Team Leader对Slides简单但蕴涵深度的要求,我不会把更多信息放在Slides里,而是准备在演示时带着大家找到问题的答案。

通过做Slides,我自己也学习了很多,了解了很多,还发现了Ruby Plugin这样一个未知领域,呵呵,说不定我下一个目标就是它了!

Cucumber Introduction 2

2012年9月04日 15:13

上次那个Slide我的Team Leader说讲的太深了,让我先做个入本级的Cucumber介绍,推销给Team成员。于是我又连夜做了个更入门级的,Share给大家!

Cucumber Introduction

2012年8月30日 10:44

自从正式入职以来还没有空写博客呢,不过最近在为项目编写Cucumber test cases,同时做了个Cucumber入门的Slides,Share给大家吧!

Rails 3.2 新特性简介

2012年4月08日 00:12

好久没有写Blog了,我依稀记得当初面试的时候面试官翻阅我Blog的场景,后来我也只写过一篇Blog而已。而今天,我已经成功的进入了这家企业,成为了真正的Ruby on Rails开发者。从去年暑假刚开始学习Rails,到今天,只有半年有余。不过Rails已经从最初学习时的3.0.8升级到了3.2.3。我依然记得第一次用3.1.0的时候还在疑惑怎么一些功能与书上已经开始不一致了,那会还不知道3.1.0加了很多新特性。不过那时幸亏有GitCafe团队的成员Rainux(@RainuxLuo)带领稀里糊涂的我用上了Rails 3.1,否则我可能至今都不了解Rails的版本计划和3.1的新特性。

本文简单介绍Rails 3.2的新特性,主要参考 Ruby on Rails Guide 3.2 Release Notes。不过作为新手,这Release notes我有很多是看不懂的,幸亏后来又看了Railscasts Upgrading to Rails 3.2视频,终于理解了主要更新,至于次要更新,如果我能理解的就会提及(不能保证理解完全正确,如果有错误请务必指出),如果不能理解的就不说了,一般也不会很重要(如果读者能在评论中略微指导下,我将不胜感激)。

1. Development环境下性能改进,由于集成了Active Reload插件,Rails在Development环境下只会重新加载哪些确实被改过的类文件,在大型项目中,这个新特性将明显改进效率。

2. 使用新的Journey引擎,路由识别的性能提升。

3. ActiveRecord::Relation增加一个explain方法,用以分析SQL包括索引在内的优化信息。目前只支持SQLITE3,MySQL,PostgreSQL三种Adapter。在Development环境下,config/environments/development.rb增加了个新选项config.active_record.auto_explain_threshold_in_seconds,默认值0.5,意思是当一条SQL语句运行时间超过0.5秒时将自动explain并且记入Log,这个新特性将帮助开发者留意那些效率极低的SQL语句。但是如果存在些不可避免的超过0.5秒的SQL语句,你不希望再看到Rails将它记入日志,将这句语句包含在

ActiveRecord::Base.silence_auto_explain do
  # no automatic EXPLAIN here
end

中,Rails将不会自动explain这其中的SQL。

4. Tagged Logging可以方便在多用户多IP访问应用的情况下观看Log。可以在config/environments/development.rb中增加config.log_tags选项,例如

config.log_tags = [:uuid, :remote_ip]

Log将记录例如

Started GET "/users/1" for 10.10.10.10 at 2012-04-07 22:15:35 +0800
[952ab51671f8d31f14069f6a372bb1f5] [10.10.10.10] Processing by UsersController#show as HTML
[952ab51671f8d31f14069f6a372bb1f5] [10.10.10.10]   Parameters: {"id"=>"1"}
[952ab51671f8d31f14069f6a372bb1f5] [10.10.10.10]   User Load (0.7ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", "1"]]
[952ab51671f8d31f14069f6a372bb1f5] [10.10.10.10]   Rendered users/show.html.erb within layouts/application (13.1ms)
[952ab51671f8d31f14069f6a372bb1f5] [10.10.10.10] Completed 200 OK in 288ms (Views: 139.8ms | ActiveRecord: 7.6ms)
[d1a1f1ebb6c56d0fc3f09d592ad4c36c] [10.10.10.10]

这样的信息,可以看到,每行之前都有一个UUID和请求的IP地址。其中UUID是Rails根据HTTP Request生成的独一无二的ID。通过这个功能,我们能够把Log中相同的HTTP Request和相同的请求IP地址取分开来。

5. ~/.railsrc现在可以直接写入rails new命令的默认参数。如果你像我一样,习惯创建没有Test Case,不自动运行bundle install命令的Rails项目,你可以运行

echo -T --skip-bundle > ~/.railsrc

这样以后-T --skip-bundle将作为rails new命令的默认参数。

6. rails命令现在接受d命令,等同于destroy命令,方便像我这样的newbie理解。之前我也确实困惑过,为什么generate可以缩写为g,而destroy命令不能缩写为d呢?现在这样就好多了。

7. rails generate的 model / migration / scaffold 命令生成Model属性的时候可以这样写

rails g scaffold Post title:string:index subtitle author:uniq 'price:decimal{7,2}'

可以看到,原来每个属性只能写两列,而现在可以只写一列或是三列。一列表示默认类型是String,三列中第三列可以使用的modifier有index,表示本列添加index;uniq,表示本列添加unique index。如果类型是数值型,还可以加上{x,y}这样的格式表示数据库中数值的精度和小数的精度(留意price:decimal{7,2}左右的引号,shell中{}有特殊含义,因此可以用引号来保持原意)。

8. rails generate plugin命令被移除,请使用rails plugin new替代。

9. 移除config.paths.app.controller,请使用config.paths['app/controller']替代。

10. Rails::Plugin类过时,并将在Rails 4.0中被彻底移除。

11. 如果你在ApplicationController中指定了layout,同时使用了:only或:expect过滤。那么如果过滤条件失败,Rails现在将使用默认的layout。

12. ActionController::TestCase现在支持用cookies直接修改或清除cookie。而在此之前则不得不使用HTTP_COOKIE或CookieJar这样的方法。

13. send_file方法现在可以猜测MIME类型,如果:type没有提供的话。

14. MIME添加了包括PDF和ZIP在内的几种类型

15. 当Controller的父类显式指定过layout的情况下,子类不再按照约定的方法查询layout。

class ApplicationController
  layout "application"
end
 
class PostsController < ApplicationController
end

比如上述例子,PostsController将不会试图去寻找一个posts的layout。如果你想恢复原来的功能,删除ApplicationController中的layout语句或是显式指定PostsController的layout为nil。

16. ActionController::UnknownAction过时,请用AbstractController::ActionNotFound替代。

17. ActionController::DoubleRenderError过时,请用AbstractController::DoubleRenderError替代。

18. ActionController#rescue_action,ActionController#initialize_template_class和ActionController#assign_shortcuts过时。

19. ActionView::Helpers::FormBuilder支持button_tag方法,默认行为等同于submit_tag

<%= form_for @post do |f| %>
  <%= f.button %>
<% end %>

20. Date helpers支持:use_two_digit_numbers选项,设置为true表示日期的月和日都有两个数字构成,如果小于零则之前补零。

21. form_for方法支持:namespace选项,作为form的id的前缀,确保form的id的唯一性。

22. 限制select_year方法生成的year条目的最大数量为1000,可以通过:max_years_allowed选项设置上限。

23. content_tag_for和div_for方法支持直接传入ActiveRecord对象的集合。例如

@items.each do |item|
  content_tag_for(:li, item) do
     Title: <%= item.title %>
  end
end

现在可以被写为

content_tag_for(:li, @items) do |item|
  Title: <%= item.title %>
end

24. 由timestamps创建的created_at和updated_at字段现在默认为not null。

25. 可以使用ActiveRecord::Base.store功能添加ActiveRecord的Key-Value存储器,例如

class User < ActiveRecord::Base
  store :settings, accessors: [ :color, :homepage ]
end
 
u = User.create(color: 'black', homepage: '37signals.com')
u.color                          # Accessor stored attribute
u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor

如果要让上述代码能够成功运行,必须确保users表有settings字段,类型为text。运行

u = User.create(color: 'black', homepage: '37signals.com')

时,将color和homepage的属性以json的形式存储在users表的settings字段中。注意,如果User自身也有color或homepage字段,这些字段将不被使用。我们可以通过u.color或是u.settings[:color]的方法访问被存储的color字段。试图给settings添加没有被accessors指定的属性也是可以的,唯一的区别就是,将不会在ActiveRecord的实例下添加这个属性的getter和setter。所以上述代码中的u.settings[:country] = 'Denmark'可以正常运行,u.settings[:country]也确实可以取到被存储的值,并且这个值确实在数据库中被持久化。但是不能使用u.country来访问这个值。

26. ActiveRecord::Relation添加pluck,传入这个ActiveRecord的一个字段,将返回包含所有Relation中存储的Record的这个字段的值的数组。

> User.where('id <= 3').pluck(:name)
=> ["bachue", "deltamaster", "anne"]

27. ActiveRecord::Relation添加uniq方法,请看示例

> Client.select('DISTINCT name')
=> [#<User name: "bachue">, #<User name: "bachue">, #<User name: "bachue">]
> User.select(:name).uniq
=> [#<User name: "bachue">]
> User.select(:name).uniq.uniq(false)
=> [#<User name: "bachue">, #<User name: "bachue">, #<User name: "bachue">]

28. :class_name选项现在可以传入Symbol,之前传入Symbol会出错,只能传入类名的字符串。//也曾让我这个newbie confusing了好一阵。

29. 在Development环境下,db:drop命令现在也会drop掉test数据库,和db:create会创建test数据库相对应。

30. 大小写不敏感的uniqueness检查时,如果MySQL的column中已经使用了大小写不敏感的校验,那么Adapter将不会再调用MySQL的LOWER函数来进行这个检查。

31. ActiveRecord::Relation新增first_or_create,first_or_create!和first_or_initialize方法,当Relation没有找到搜索结果时,将创建同时包含Relation的参数和first_or_create的参数的记录,例如

> User.create name: 'bachue', age: 22
=> #<User id: 1, name: "bachue", age: 22>
> User.where(name: 'bachue').first_or_create!(age: 16)
=> #<User id: 1, name: "bachue", age: 22>
# No new record is created
> User.count
=> 1
> User.where(name: 'anne').first_or_create!(age: 16)
=> #<User id: 2, name: "anne", age: 16>
# New record is created
> User.count
=> 2

32. 在Development环境下,config/environments/development.rb增加了新语句

config.active_record.mass_assignment_sanitizer = :strict

功能是当违反mass assignment protection的时候,Rails将抛出ActiveModel::MassAssignmentSecurity::Error异常来阻止这一赋值,而此前仅仅是给出一个warning而并不阻止。

//感觉可以考虑把这句话移到全局的配置文件,阻止Production环境上有人试图用mass assignment攻击系统。

33.  现在将不会自动关闭Thread的数据库连接,例如

Thread.new { Post.find(1) }.join

你现在要在Thread闭包结束时显式关闭连接,例如

Thread.new {
  Post.find(1)
  Post.connection.close
}.join

如果之前你在项目中使用线程的话现在要注意了 //不知道什么情况!Release notes中就这么写的,为啥要修改成这样啊!此前几乎从没有关注过Rails的数据库连接机制好不好!

34. set_table_name,set_inheritance_column,set_sequence_name,set_primary_key,set_locking_column方法过时了,使用setter替代。例如以前写的

class Project < ActiveRecord::Base
  set_table_name "project"
end

现在将被写成

class Project < ActiveRecord::Base
  self.table_name = "project"
end

这种方法的好处在于,你可以使self.table_name变成方法,例如

class Post < ActiveRecord::Base
  def self.table_name
    "special_" + super
  end
end
 
Post.table_name # => "special_posts"

//大爱ruby的local variable和method可以相互替换的特点。也由此可见在ruby中写set_xxxx是没有前途的。

35. ActiveModel::AttributeMethods的define_attr_method方法过时,这方法本来就是用于支持类似于set_table_name方法的,但现在它们都过时了。

36. 新增ActiveSupport:TaggedLogging类,用来包装任何标准的Logger类以提供tagging功能,请看示例

Logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
 
Logger.tagged("BCX") { Logger.info "Stuff" }
# Logs "[BCX] Stuff"
 
Logger.tagged("BCX", "Jason") { Logger.info "Stuff" }
# Logs "[BCX] [Jason] Stuff"
 
Logger.tagged("BCX") { Logger.tagged("Jason") { Logger.info "Stuff" } }
# Logs "[BCX] [Jason] Stuff"

37. 在Date,Time和DateTime类中的beginning_of_week方法现在可以接受一个参数,代表一周的哪一天属于一周的第一天,请看示例

> Date.today.beginning_of_week :monday
=> Mon, 02 Apr 2012 
> Date.today.beginning_of_week :sunday
=> Sun, 08 Apr 2012 
> Date.today.beginning_of_week :saturday
=> Sat, 07 Apr 2012 
> Date.today.beginning_of_week :wednesday
=> Wed, 04 Apr 2012

38.  String类添加safe_constantize方法,用以将字符串转换成同名的Class对象。和constantize方法的区别在于如果无法找到这个Class对象,将返回nil而不是抛掷异常。例如

> 'User'.safe_constantize
=> User(id: integer, name: string, age: integer, created_at: datetime, updated_at: datetime) 
> 'User1'.safe_constantize
=> nil 
> 'User'.constantize
=> User(id: integer, name: string, age: integer, created_at: datetime, updated_at: datetime) 
> 'User1'.constantize
NameError: uninitialized constant User1

39.  增加Array#prepend方法,alias Array#unshift。增加Array#append方法,alias Array#<<。//之前想找给Array push元素的方法,除了<<就找不到了,现在好了。

40.  Time新增all_day,all_week,all_quarter,all_year方法可以生成相应的Range对象,例如:

> Time.now.all_week
=> 2012-04-02 00:00:00 +0800..2012-04-08 23:59:59 +0800

//但是前面才刚提到关于week添加一个选项设定一周的那一天是这周的第一天,这里却又看不到了。

41. 新增ActiveSupport::Cache::NullStore用于Development环境和Testing环境。

42. 移除ActiveSupport::SecureRandom,请使用标准库中的SecureRandom替代。

43. ActiveSupport::Base64过时,请使用::Base64替代。

44. ActiveSupport::Memoizable过时,请使用Ruby的memoization pattern替代。

45. Module#synchronize过时,没有替代方案,请使用ruby标准库的monitor。

46. ActiveSupport::MessageEncryptor#encrypt和ActiveSupport::MessageEncryptor#decrypt方法过时。

47. ActiveSupport::BufferedLogger#silence,如果你不想记录当前语句块的Log,请修改它的Log Level。

48. ActiveSupport::BufferedLogger给你的Log自动创建目录的行为过时,请自行创建。

49. ActiveSupport::BufferedLogger#auto_flushing过时,请像这样设定日志文件的sync level。或是优化你的文件系统。FS cache现在控制着flushing。

f = File.open('foo.log', 'w')
f.sync = true
ActiveSupport::BufferedLogger.new f

50. ActiveSupport::BufferedLogger#flush过时,请设定文件句柄的sync,或是优化你的文件系统。

在KDE下用GVim,一直有个很怪的问题,就是最大化几乎是无效的,最大化之后可以看到右边和下边都各有一条细缝。有人说可能是GVim规定要整数的行和列,也有人说是GVim和KWin都想管理窗口,然后冲突了。

有一种比较简单的解决方法,右击GVim标题,选择Advanced,选择Fullscreen即可。

设置后效果如图所示:

这种方法效果总体较好,但是却有个很严重的缺陷。ALT+TAB看不到窗口切换的效果,这在窗口切换时是很不方便的。

这是Gvim官方的Wiki中的教程:http://vim.wikia.com/wiki/Automatically_maximizing_gvim_in_KDE

设置界面:

按照这个做法的话,也有个很大的问题,就是如果用GVim打开一个其他窗口,比如查找和替换,那个就会变成这样:

显然对GVim主窗口的设置影响到了对话框。

经过多次研究,后来发现一种办法,可以完美解决这个问题而没有这样那样的副作用。首先,和Wiki中一样,进入KWin设置界面。

选择Detach Window Properties按钮,在GVim主窗口任意位置点击一下。

看到如下界面,选择Use whole window class(specific window)。

保持Window Extra中只有Normal Window一项被选中。

这里还是和Wiki中一样的设置方法,在我电脑上,GVim大小为1280x775。

设置后效果如下,可以看到,已经最大化了。

搜索对话框还是和原来一样大小。

下面说下如何修改和删除已经设置过的KWin的规则,之前以为KWin的规则都保存在~/.kde/share/config/kwinrulesrc中,只要编辑这个文件就可以修改KWin的规则,其实是不对的,因为设置完之后再次启动GVim的话,~/.kde/share/config/kwinrulesrc会回滚到原来的设置,不知道这是什么情况。如果要修改或删除已经设置过的KWin的规则,可以启动Window Rules,启动方法如图:

在这个框口中编辑即可,编辑完后不要忘记点击Apply按钮,否则编辑还是无效的。

我们夏季学期的作业,JavaEE的项目正在进行。在我的反复诱导下,Deltamaster 同学终于动用了AOP,不过很快就出了问题。他用AOP对所有Action类的execute方法进行增强(“advice”),但问题是,增强之后,View中的错误信息就无法输出了。

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<s:property value="errorMessage" />

由于AOP是我怂恿的,所以问题还是要由我来解决。Deltamaster如果有什么问题,那我肯定不会先去Google,因为我用Google就是他教的。所以首先,我仔细对比了启用AOP与不启用AOP两种情况下Action的表现,发现完全一致,确定Action没有问题。

由于AOP中我们用的是级别最高的around,有拦截并修改函数返回值的能力。我多次测试了around函数中proceed()函数的返回值在函数中各个点的位置,也没有发现问题。也就是说,around本身的代码也没有错误,但不排除是SpringFramework AOP的Bug使得Controller无法获取到action的返回值。不过,我暂时不会往Bug这块地方想。

如果Controller确实没有获取到action的返回值,也就是,jsp文件没有被调用。我随后在jsp后面加了一句输出语句,然后运行。输出成功了!这就表示该jsp文件确实被调用了,同时也证明了Controller确实获取到了action的返回值,前面AOP有Bug的推论不成立。总之从action到view,中间所有过程均正常。

那么就是errorMessage没有被获得了,在jsp中写errorMessage,实质将调用action类的getErrorMessage方法,我在这个方法中加了句输出语句。测试时这句话并没有输出,也就是说,getErrorMessage没有被调用。但为什么不被调用呢?我在action类中自己写了点get方法,测试发现都没有输出。(此处省略1000+字,关于我是如何验证是否是AOP的Bug造成getErrorMessage失效,以及是否是传回对象的类型无法匹配等等各种怀疑)最后,索性直接写

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<s:property value="class" />

输出了,而且发现是个proxy类,我随即将class改成了class.superclass.name,也就是输出本类的父类的名字,结果是,java.lang.reflect.Proxy,原因终于被发现了,作为一个Java的动态代理类,怎么可能有getErrorMessage方法。我们知道,SpringFramework AOP实现的核心方法是Java动态代理(基于接口的代理)或CGLIB(基于类的代理),这里似乎用的是前者。Proxy类对象代替Action类调用了View,结果当View要求调用者的getErrorMessage方法时,Proxy类没有这样的方法,所以导致出错!

随后我仔细查看了Proxy类的文档,全是类方法,没有任何能获得被代理对象的实例方法。无奈下,直接Google了,很快就找到了解答,很幸运,是中文解答:http://jeooo-li.iteye.com/blog/436931。“只要在Spring的配置文件applicationContext中的<aop:aspectj-autoproxy/>改为<aop:aspectj-autoproxy proxy-target-class="true"/>就可以了。”,经过测试,这个解答是正确的,问题解决。

经过初步测试,发现proxy-target-class="true" 一旦加上后,SpringFramework AOP将使用CGLIB方法而不是动态代理,生成Action类的子类,这样即可以满足AOP增强处理的要求,又完美继承了Action类的所有方法,因此才能解决问题。

最近一直折腾Debian啊,KDE已经玩的很熟练了,LXDE碰到点麻烦,startlxde命令执行后什么事情都没发生啊,不知道如何解决。只能盘算着先用用Debian的Live版本。

今天想开启VMware Player的,没想到VMware Player开不出来了,从错误信息中,得知编译出现错误。我的Fedora的内核不知道什么时候升级到2.6.40了。2.6.40其实就是3.0改个名字而已,怀疑大约是代码发生改动,使得VMware Player编译出错了。因此迅速Google,在 http://osicarg.wordpress.com/2011/07/15/here-is-patch-for-vmware-player-3-1-4-build-385536-on-linux-2-6-39-2-amd64-kernel/ 中找到了解决方案,不过这个解决方案写得并不是很清楚。因此我额外说明下。

下载 这个 tar.bz2 包

解压,然后将其中的五个tar文件(vmblock.tar vmci.tar vmmon.tar vmnet.tar vsock.tar)放入 /usr/lib/vmware/modules/source,覆盖原文件,即可。

然后再用root用户开启VMware Player,即可编译成功。

之所以写这么简单的文章,只是因为希望中文文章至少能有一个Google搜索结果而已,虽然英文搜索结果相当多,但希望帮助到英文不好的Fedora/Linux用户,同时也帮助提升简体中文文章的质量。