rails3.1rc1在服务器上运行报Could not find a JavaScript

rail3.1rc1在服务器上运行时报以下的错误信息:

  Could not find a JavaScript runtime. See https://github.com/sstephenson/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)

解决方式是在gemfile中加入

  gem 'therubyracer-heroku', '0.8.1.pre3'

修复安装gems时Errno::ETIMEDOUT: Operation timed out - connect(2)的错误

这两天更新gems出现下面的错误

➜  ~  gem install rails      
ERROR:  Could not find a valid gem 'rails' (>= 0) in any repository
ERROR:  While executing gem ... (Gem::RemoteFetcher::FetchError)
    Errno::ETIMEDOUT: Connection timed out - connect(2) (http://rubygems.org/latest_specs.4.8.gz)

经检查和GFW无关,是rubygems的DNS
调整问题

问题解决的最好方法方法

使用google的DNS
8.8.8.8 / 8.8.4.4

另一种解决方式

修改rubygems的source源

#删除原有gem source

gem source -r http://rubygems.org/
gem source -r http://production.s3.rubygems.org/ 

#增加新source源

gem source -a  http://production.s3.rubygems.org.s3.amazonaws.com/

ubutnu配置rvm,nginx和passenger的生产环境

下述内容参考了A Guide to a Nginx, Passenger and RVM Server

首先, 使用 root 帐号登录,

安装git和curl

apt-get install curl git-core

使用脚本安装rvm

bash < <(curl -L http://bit.ly/rvm-install-system-wide)

脚本会自动创建一个rvm组,并将root用户加入

编辑 /root/.bashrc和/etc/skel/.bashrc

[ -z "$PS!"]  && return 

替换为

if [[ -n "$PS1" ]]; then

在文件最后加入

fi
if groups | grep -q rvm ; then
  source "/usr/local/lib/rvm"
fi

配置用户

#增加zool用户

adduser zool
#将zool加入rvm组

adduser zool rvm

登录zool用户并测试

type rvm | head -n1

如果显示 rvm is a function 则表示安装正确

安装ree依赖组件

aptitude install build-essential bison openssl libreadline5 libreadline-dev \

curl git-core zlib1g zlib1g-dev libssl-dev vim libsqlite3-0 libsqlite3-dev \

sqlite3 libreadline-dev libxml2-dev git-core subversion autoconf

安装ree

rvm install ree

设为默认环境

rvm use ree --default

安装passenger和nginx

gem install passenger
rvmsudo passenger-install-nginx-module

配置nginx里的ruby环境

passenger_root /usr/local/rvm/gems/ree-1.8.7-2010.02/gems/passenger-3.0.0;
passenger_ruby /usr/local/rvm/wrappers/ree-1.8.7-2010.02/ruby;

设置nginx开机脚本

curl -L http://bit.ly/nginx-ubuntu-init-file > /etc/init.d/nginx
chmod +x /etc/init.d/nginx
update-rc.d nginx defaults
/etc/init.d/nginx start

原文中后面的step6,7关于Capistrano的环境配置以后再另文详述

建立sproutcore应用

sproutcore是通过rubygems来安装的

gem install sproutcore

建立应用

sc-init HelloWorld

启动应用

cd HelloWorld
sc-server

便会通过启动thin在locahost:4020提供应用服务

rails路由设置网站首页

做一个应用必做的一步

首先删除 /public/index.html 这个文件

然后编辑 config/route.rb ,设置路由

root :to => "home#index"

其中 "home#index" 是rails路由的简写, 指的是 homescontroller的index方法

设置首页后, 会自动生成两个helper方法 root_path和 root_url, 其中path是相对路径,url是绝对路径

rails3新建项目模板

rails 新建项目时可以使用-m 参数来使用模板生成新项目

安装需求

*nix系统

git

sqlite3/mysql等环境已经安装完毕

rails 3.0.4(之前版本不能使用https的模板地址)

样例

  rails new my_app -T -J -m https://gist.github.com/777670.txt

就会自动生成新项目,安装一些gem,如
devise
haml
jquery
rspec
等等
同时自动迁移,删除index.html等等不必要文件。

https://gist.github.com/777670 是我自己配置文件,里面的配置可能不适合每个人, 可以自行fork修改。

rails中处理大批量数据的方法

在实际运用中,我们要对每个用户发一封信的话。 一般的写法是

User.all.each {|user|
  NewsLetter.weekly_deliver(user)
}

当数据条目超过1000时, 这样的命令会占用大量的内存。

rails里使用find_each方法来处理这样的情况

User.all.each {|user|
  NewsLetter.weekly_deliver(user)
}

默认是1000条一批
可以用下面的参数指定一批的条数, 还可以指定开始的主键ID

User.find_each(:batch_size => 5000, :start => 2000) {|user|
  NewsLetter.weekly_deliver(user)
}

find_each 是逐条处理对象, 有另外一个find_in_batches是按数组来处理

Person.where("age > 21").find_in_batches do |group|
    sleep(50) # Make sure it doesn't get too crowded in there!

    group.each { |person| person.party_all_night! }
  end

看源码, find_each是对find_in_batches的包装

# File activerecord/lib/active_record/relation/batches.rb, line 19

    def find_each(options = {})
      find_in_batches(options) do |records|
        records.each { |record| yield record }
      end

      self
    end

rails中设置p3p头

由于ie6,7的bug,无法跨iframe保存cookies,所以需要设置p3p头

class ApplicationController < ActionController::Base
  ...
 
  before_filter  :set_p3p

  def set_p3p
    response.headers["P3P"]='CP="CAO PSA OUR"'
  end

rails的定时任务插件whenever

在做web应用时,有时会需要定时做一些操作,如发邮件,统计信息等。
这些都是需要放在后台来执行, whenever就是这样的一个插件,使用ruby强大的DSL, 高效的配置生成定时任务。
注意,whenever使用的是crontab定时器,所以这个gem在windows上无效。

github地址

安装

gem 'whenever', :require => false

开始配置

cd /path/to/myapp/
wheneverize .
#会在config目录下生成一个schedule.rb文件

配置文件说明

每个配置都是在一个叫every的block里面配置
运行频率 .minutes, .hours, .days, .months
可以运行任务 runner rake command 三种

例子

#每隔10分钟运行一次

every 10.minutes do
  #等同于 rails runner MyModel.some_process

  runner "MyModel.some_process"
  #等同于 rake my:rake:task

  rake "my:rake:task"  
  #等同于在终端执行 /usr/bin/my_great_command

  command "/usr/bin/my_great_command" 
end

whenever默认使用production环境,可以在配置文件里另外定义

  set :environment, :autotest
  #或单独指定

  runner "MyModel.some_process", :environment => :autotest

高级配置

使用at参数来指定分钟

#每隔两个小时23分钟

every 2.hours, :at => 23 do
#每隔两天在上午4:30

every 2.days, :at => '4:30am' do
#每周五晚从05:00到23:45每隔15分钟

every :friday, :at => ('05'..'23').to_a.collect {|x| ["#{x}:00","#{x}:15","#{x}:30","#{x}:45"]}.flatten do

与Capistrano结合

编辑Capistrano的配置文件config/deploy.rb, 加入

require "whenever/capistrano"
...
after "deploy:symlink", "deploy:update_crontab"
  namespace :deploy do
    desc "Update the crontab file"
    task :update_crontab, :roles => :db do
      run "cd #{release_path} && whenever --update-crontab #{application}"
    end
  end

rails中对时间的操作方法

做web应用,和时间打交道是不可免的。rails对ruby的时间模块做了扩展。
本文作于2011年1月29日, ruby版本为1.8.7, rails版本为3.0.3

基本的时间转换

>> now=Time.now
=> Sat Jan 29 21:47:07 0800 2011
#utc秒数互相转换

>> now.to_i
=> 1296308827
>> Time.at(1296308827)
=> Sat Jan 29 21:47:07 0800 2011
#当前时间的一些变量

>> now.sec
=> 7
>> now.min
=> 47
>> now.hour
=> 21
>> now.month
=> 1
>> now.year
=> 2011
#现在是星期几(注意!!!周日是返回 0 )

>> now.wday
=> 6
#现在是本月第几天

>> now.day
=> 29
#现在是今年第几天

>> now.yday
=> 29
#时间参数的数组

>> now.to_a
=> [7, 47, 21, 29, 1, 2011, 6, 29, false, "CST"]

时间化输出

>> now.strftime("%Y-%m-%d %H:%M:%S")
=> "2011-01-29 21:47:07"

参数解释如下

  %a - 星期几的英文简写 (``Sun'')
  %A - 星期几的英文全称 (``Sunday'')
  %b - 月份的英文简写 (``Jan'')
  %B - 月份的英文全称 (``January'')
  %c - 默认的首选本地时间输出格式
  %d - 本月第几天 (01..31)
  %H - 24小时制的小时 (00..23)
  %I - 12小时制的小时 (01..12)
  %j - 今年的第几天 (001..366)
  %m - 月份 (01..12)
  %M - 分钟 (00..59)
  %p - 上午还是下午 (``AM''  or  ``PM'')
  %S - 秒数 (00..60)
  %U - 从星期天算一周开始的本年第几周 (00..53)
  %W - 从星期一算一周开始的本年第几周 (00..53)
  %w - 现在是星期几 (周日是0 , 0..6)
  %x - 默认的日期输出格式 ("01/29/11")
  %X - 默认的时间输出格式 ("21:47:07")
  %y - 年份的后两位 (00..99)
  %Y - 年份
  %Z - 时区名
  %% - 输出%字符

以上是ruby的基本方法,rails对其做了更多的扩展

#重写了to_s方法,能够接受参数

>> now.to_s
=> "Sat Jan 29 21:47:07 +0800 2011"
>> now.to_s(:db)
=> "2011-01-29 21:47:07"
>> now.to_s(:number)
=> "20110129214707"
>> now.to_s(:time)
=> "21:47"
>> now.to_s(:short)
=> "29 Jan 21:47"
>> now.to_s(:long)
=> "January 29, 2011 21:47"
>> now.to_s(:long_ordinal)
=> "January 29th, 2011 21:47"
>> now.to_s(:rfc822)
=> "Sat, 29 Jan 2011 21:47:07 +0800"

如果要自己设计时间输出格式,按下面方法来,新建一个配置文件

  # config/initializers/time_formats.rb

  Time::DATE_FORMATS[:month_and_year] = "%B %Y"
  Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") }

rails对日期的一些扩展

#指定时间

>> now.change(:year=>2012, :month=>12, :day => 21, :hour => 0, :min => 0, :sec => 0, :usec => 0)
=> Fri Dec 21 00:00:00 0800 2012

#begginning家族

>> now.beginning_of_day
=> Sat Jan 29 00:00:00 0800 2011
>> now.midnight
=> Sat Jan 29 00:00:00 0800 2011
>> now.beginning_of_week
=> Mon Jan 24 00:00:00 0800 2011
>> now.beginning_of_month
=> Sat Jan 01 00:00:00 0800 2011
>> now.beginning_of_quarter
=> Sat Jan 01 00:00:00 0800 2011
>> now.beginning_of_year
=> Sat Jan 01 00:00:00 0800 2011
#end家族

>> now.end_of_day
=> Sat Jan 29 23:59:59 0800 2011
>> now.end_of_week
=> Sun Jan 30 23:59:59 0800 2011
>> now.end_of_month
=> Mon Jan 31 23:59:59 0800 2011
>> now.end_of_quarter
=> Thu Mar 31 23:59:59 0800 2011
>> now.end_of_year
=> Sat Dec 31 23:59:59 0800 2011
#时间的魔术方法

>> now.yesterday
=> Fri Jan 28 21:47:07 0800 2011
>> now.tomorrow
=> Sun Jan 30 21:47:07 0800 2011
>> now.next_week
=> Mon Jan 31 00:00:00 0800 2011
>> now.next_month
=> Mon Feb 28 21:47:07 0800 2011
>> now.next_year
#注意没有prev_week

>> now.prev_month
=> Wed Dec 29 21:47:07 0800 2010
>> now.prev_year
=> Fri Jan 29 21:47:07 0800 2010
#今日已过秒数

>> now.seconds_since_midnight
=> 78427.615017
#日期输出

>> now.to_date
=> Sat, 29 Jan 2011
>> now.to_datetime
=> Sat, 29 Jan 2011 21:47:07 0800
#按秒数计算

>> now.ago(3600)
=> Sat Jan 29 20:47:07 0800 2011
>> now.since(3600)
=> Sat Jan 29 22:47:07 0800 2011

实际上还有很多方法没有列出,具体使用还是参考rails的api手册为准。

另外有一个rails插件bystar对此做了更多的扩展, 后面会另外写一篇文章来说明