Ruby1.9的中文问题

今天在ruby 1.9.1的环境下试了一下1.8.7下面写的一段代码,结果报错:syntax error, unexpected $end, expecting '}',查看了一下代码,如下

STATUS = {
  "400" => "在线",
  "300" => "离开",
  "600" => "繁忙",
  "0" => "脱机"
}

语法完全没有问题,判断是中文导致的问题,奇怪的是在1.8.7下面运行正常。google了一下,原来只要在文件开头加上coding就可以了

#coding: utf-8

Posted in  ruby


为resque写扩展

resque是基于redis的ruby类库,用于创建后台任务,把这些后台任务放在多个队列中去,之后在处理它们。github就是使用resque来处理它们的后台任务的。

对于需要长时间处理的任务,比如发送email,发tweet,图片resize等等,都是resque的用武之地。默认resque就是将任务加到redis的队列中去,然后定时取出来去处理,实际项目中我们往往需要对其增加额外的扩展,比如你需要增加日志功能,增加处理次数的限制,这个时候就可以给resque写一个plugin,就像rails的plugin一样。

resque定义了非常良好的HOOK,使得为其写扩展变得更加容易。

resque采取的是每隔n秒从队列中获取一个任务,然后fork一个子进程来执行这个任务。resque定义了before_fork, after_fork, before_perform, after_perform, around_perform, on_failure几个hook,执行顺序如下

  1. before_fork

  2. fork

  3. after_fork

  4. before_perform

  5. around_perform

  6. perform

  7. around_perfomr

  8. after_perform

还有就是发生错误的时候,on_failure会被执行。

再给一个我写的resque-restriction插件的实例

def before_perform_restriction(*args)
  settings.each do |period, number|
    key = redis_key(period)
    value = get_restrict(key)

    if value.nil? or value == ""
      set_restrict(key, seconds(period), number)
    elsif value.to_i <= 0
      Resque.push "restriction", :class => to_s, :args => args
      raise Resque::Job::DontPerform
    end
  end
end

def after_perform_restriction(*args)
  settings.each do |period, number|
    key = redis_key(period)
    Resque.redis.decrby(key, 1)
  end
end

before_perform_restriction检查任务在一个时间段执行的次数,如果执行次数超过规定,抛出Resque::Job::DontPerform异常,它将终止该任务继续执行。

after_perform_restriction则在任务执行之后,将计数器减一。

可以看出,resque作为一个后台任务的框架,其api设计非常良好,很容易对其进行扩展,应该多学习学习。

Posted in  rails resque ruby


git for hostmonster

前段时间对网站做了些更新,于是在本地修改了代码,再git push,谁知却得到bash: git-receive-pack: command not found的error,我的git repository是放在hostmonster服务器上面的,之前都是正常的,于是提交ticket给hostmonster的support,得到的答复是他们升级的openssh,通过git+ssh不会再读取.bashrc或.ssh/environment文件,也就是说通过git+ssh没有办法修改PATH了。

没办法了,只能手动将命令的路径补全了,对于git pull/git ps来说,只需要在输入命令的时候增加参数,比如

git clone --upload-pack=/home1/huangzhi/git/bin/git-upload-pack
git push --receive-pack=/home1/huangzhi/git/bin/git-receive-pack

不过每次都输入参数实在麻烦,直接写到配置文件.git/config

[remote "origin"]
uploadpack=/home1/huangzhi/git/bin/git-upload-pack
receivepack=/home1/huangzhi/git/bin/git-receive-pack

然后就可以像以前一样git pull/git ps了。

还有一个问题,那就是capistrano。默认capistrano通过git ls-remote获取最新的commit id,通过git clone来获取最新文件,但是这些命令都没有办法设置upload-pack和receive-pack参数,没办法,只能修改默认的方法定义。

首先是git ls-remote

require 'capistrano/recipes/deploy/scm/base'
::Capistrano::Deploy::SCM::Base.class_eval do
  alias_method :origin_scm, :scm
  def scm(*args)
    if command == "git" and args[0] == "ls-remote"
      args[0] = "ls-remote --upload-pack=/home1/huangzhi/git/bin/git-upload-pack"
    end
    origin_scm(args)
  end
end

当命令为git ls-remote的时候,额外加入参数upload-pack

再就是git checkout

require 'capistrano/recipes/deploy/scm/git'
::Capistrano::Deploy::SCM::Git.class_eval do
  def checkout(revision, destination)
    git    = "/home1/huangzhi/git/bin/git"
    remote = origin

    args = []
    args  "-o #{remote}" unless remote == 'origin'
    if depth = configuration[:git_shallow_clone]
      args  "--depth #{depth}"
    end

    execute = []
    if args.empty?
      execute  "#{git} clone --upload-pack=/home1/huangzhi/git/bin/git-upload-pack #{verbose} #{configuration[:repository]} #{destination}"
    else
      execute  "#{git} clone --upload-pack=/home1/huangzhi/git/bin/git-upload-pack #{verbose} #{args.join(' ')} #{configuration[:repository]} #{destination}"
    end

    # checkout into a local branch rather than a detached HEAD
    execute  "cd #{destination}  #{git} checkout #{verbose} -b deploy #{revision}"

    if configuration[:git_enable_submodules]
      execute  "#{git} submodule #{verbose} init"
      execute  "#{git} submodule #{verbose} sync"
      execute  "#{git} submodule #{verbose} update"
    end

    execute.join("  ")
  end
end

这个我没有找个比较优雅的方式,只能直接覆盖原来的方法定义,并在git clone的命令中加入upload-pack。

还有就是当capistrano执行远程命令的时候,同样没有合适的environments,比如执行rake db:migrate的时候,所以需要修改默认的rake命令

set :rake, "source /home1/huangzhi/.bashrc; rake"

这样当执行rake命令执行,首先读取.bashrc,设置合适的environments,然后再执行rake命令。

到此为止,一切又恢复了正常。

Posted in  git capistrano hostmonster


为select和input type=file标签添加样式

select和input type=file两个都是html标签,但是它们在不同的浏览器上显示是完全不同,对于那些对UI要求非常高的网站来说,这是不可接受的。由于这两个标签的样式是由浏览器实现的,所以要想完全通过css来统一样式几乎是不可能的,所以我们这里需要借助javascript的帮助。

看上去这个应该由两个标签组成,左边是一个text field,右边是一个上传的按钮,要想在所有的浏览器上都把input type=file做成这个样子好像没这个可能,可以想到的办法就是设置两个层,下面的层由一个text field和一个按钮组成,上面是一个透明的input type=file的层,高度和宽度正好覆盖下面的层就可以了。

我是借助 javascript来生成下面的那个层

$.each($('form input[type=file]'), function(i, elem) {
  $(elem).parent().append($("<div class='fakefile'><input type='text'><div class='browser_button'></div></div>"));
  $(elem).change(function() {
    $(this).parent().find('input[type=text]').val($(this).val());
  });
});

这里fakefile就是下面的那个层,<div class="browser_button"></div>是通过css_sprite得到的按钮图片(最近使用 css_sprite到了偏执的状态)。然后就是通过css来区分上下两个层

.file {
  width: 209px;
  height: 28px;
  position: relative; }
.file input[type=file] {
  z-index: 2;
  width: 278px;
  opacity: 0;
  filter: alpha(opacity: 0);
  -moz-opacity: 0;
  cursor: pointer; }
.file div.fakefile {
  z-index: 1;
  position: absolute;
  top: 0;
  left: 0; }
.file div.fakefile input {
  width: 207px;
  height: 28px;
  border: 1px solid #c1c1c1;
  -moz-border-radius: 3px;
  -webkit-border-radius: 3px; }
.file div.fakefile .browser_button {
  position: absolute;
  top: 0;
  left: 207px; }

input type=file的z-index为2,.fakefile的z-index为1,表示fakefile为下面的层,input type=file为上面的层。input type=file的opacity设置为0,表示input type=file为透明的,通过position: absolute可以将两个层完全重叠。这样做出来的input type=file就和之前的图片一模一样了。

对于select标签的样式修改也可以用相同的办法进行处理。

Posted in  css javascript html


浙大校友趣味运动会

下午参加了浙大校友趣味运动会,在华师大。

上半场是组队活动,有跳长绳、拔河和四人五足活动。下半场是羽毛球比赛。我主要是参加了羽毛球比赛,一开始进行25分的淘汰赛,连赢了两盘,不过体力已经成为问题。进入冠军循环赛,才用11分3局2胜,真是累得一塌糊涂,第一个对手非常擅长打高吊球,基本被压在后场,僵持几个回合就比较累了,一急就想劈杀,虽然有几个扣死了,不过大多都是被救回,或者下网,输得比较郁闷,没什么办法。第二个对手旗鼓相当吧,不过还是输了,反应和移动都下降不少,体力成了制约发挥的最大障碍。

回来的路上洗了个澡,已经感觉右边大腿比较酸,右手胳膊也有点太不起来了,估计明天得单手敲代码了。

听说浙大校友每周都会在华师大打球的,以后可以经常去玩玩。

Posted in  life


Fork me on GitHub