Posts match “ rails ” tag:

如何在model中使用url_helpers

如果想在model中使用类似 url_path 这样的helper方法, 可以这样:

class User < ActiveRecord::Base

  include Rails.application.routes.url_helpers
  .....
  def base_url
     user_path(self)
  end
  
  end

如何在rails中使用hstore模块

首先解释一下什么是 hstore

hstorepostgresql 自带的一个模块,可以在字段内用 key/value 格式存储文档数据。

rails4 开始已经在自带的PostgreSQLAdapter中加入了hstore的支持。使用方式如下。

class CreateProfiles < ActiveRecord::Migration
  #启用hstore模块

  enable_extension 'hstore' unless extension_enabled?('hstore')
  def change
    create_table :profiles do |t|
      t.hstore 'settings'
      t.timestamps
    end
    #可以给hstore字段加索引,, 类型有 gin 和 gist

    add_index :profiles, :settings, using: :gin
  end
end

关于 gingist 索引格式的选择, 请看官方文档。

In choosing which index type to use, GiST or GIN, consider these performance differences:

GIN index lookups are about three times faster than GiST

GIN indexes take about three times longer to build than GiST

GIN indexes are moderately slower to update than GiST indexes, but about 10 times slower if fast-update support was disabled (see Section 54.3.1 for details)

GIN indexes are two-to-three times larger than GiST indexes

As a rule of thumb, GIN indexes are best for static data because lookups are faster. For dynamic data, GiST indexes are faster to update. Specifically, GiST indexes are very good for dynamic data and fast if the number of unique words (lexemes) is under 100,000, while GIN indexes will handle 100,000+ lexemes better but are slower to update.

Note that GIN index build time can often be improved by increasing maintenance_work_mem, while GiST index build time is not sensitive to that parameter.

GINGiST 索引快三倍,但也多花了三倍时间。

postgresql 的其他模块用法可以在官方文档找到

其中注意,保存完 hstore 字段后的数据, 如果要修改时, 需要执行字段名_will_change! 这个方法

## you need to call _will_change! if you are editing the store in place

profile.settings["color"] = "green"
profile.settings_will_change!
profile.save!

如何在HABTM中添加唯一认证

先创建has_many_and_belongs_to_many表结构

create_join_table :products, :categories do |t|
  t.index :product_id
  t.index :category_id
end

在model中添加下列代码

class Product < ActiveRecord::Base
  has_and_belongs_to_many, -> { uniq}
end

设置rails中generator使用的orm类型

在最近的一个项目里, 同时使用了postgresql和mongoid,

结果在rails g model xxxx时, 默认调用的是mongoid,

虽然能通过--orm=actvie_record参数指定orm, 但还是很麻烦.

通过查询手册, 可以用下面的设置方式解决.

#config/application.rb

config.generators do |g|  
   g.orm             :active_record  
end

增加carrierwave对rails_admin的支持

由于rails_admin默认只是支持paperclip, 所以使用carrierwave的话, 需要手动做些修改.

一共3个文件

#app/views/rails_admin/main/_form_carrier_wave_file.html.haml

= label_tag "#{field.abstract_model.to_param}_#{field.name}", field.label
.input
  - if field.bindings[:object].send("#{field.name}_url")
    .row
      = link_to field.bindings[:object].send("#{field.name}_url")
      %br
      = form.check_box "remove_#{field.name}"
      = form.label "remove_#{field.name}", "Remove existing #{field.label.downcase}", :class => "inline"
  .row
    = form.file_field field.name, :class => "fileUploadField #{field.has_errors? ? "errorField" : nil}"
    = form.hidden_field "#{field.name}_cache"
#app/views/rails_admin/main/_form_carrier_wave_image.html.haml


= label_tag "#{field.abstract_model.to_param}_#{field.name}", field.label
.input
  - image = field.bindings[:object].send(field.name)
  - if image.path # the most direct check of an assets existence I could see

    .row
      -# load a default 'version' if it exists. should really be set through rails_admin's DSL:

      - default_version = image.versions[:main]
      = image_tag default_version && default_version.url || image.url
      %br
      = form.check_box "remove_#{field.name}"
      = form.label "remove_#{field.name}", "Remove existing #{field.label.downcase}", :class => "inline"
  .row
    = form.file_field field.name, :class => "fileUploadField #{field.has_errors? ? "errorField" : nil}"
    = form.hidden_field "#{field.name}_cache"
#config/initializers/rails_admin.rb


# Register a custom field factory and field type for CarrierWave if its defined

if defined?(::CarrierWave)
  module RailsAdmin::Config::Fields::Types
    # Field type that supports CarrierWave file uploads

    class CarrierWaveFile < RailsAdmin::Config::Fields::Types::FileUpload
      register_instance_option(:partial) do
        :form_carrier_wave_file
      end
    end
    
    # Field type that supports CarrierWave file uploads with image preview

    class CarrierWaveImage < CarrierWaveFile
      register_instance_option(:partial) do
        :form_carrier_wave_image
      end
    end
    
    # Register field type to the types registry

    register(:carrier_wave_file, CarrierWaveFile)
    register(:carrier_wave_image, CarrierWaveImage)
  end
  RailsAdmin::Config::Fields.register_factory do |parent, properties, fields|
    model = parent.abstract_model.model
    if model.kind_of?(CarrierWave::Mount) && model.uploaders.include?(properties[:name])
      type = properties[:name] =~ /image|picture|thumb/ ? :carrier_wave_image : :carrier_wave_file
      fields << RailsAdmin::Config::Fields::Types.load(type).new(parent, properties[:name], properties)
      true
    else
      false
    end
  end
end

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'

rails3rc1在安装rspec-rails时碰到报错

今天在rails3rc1下安装rspec-rails, 提示了以下错误

Invalid gemspec in [/Users/ZoOL/.rvm/gems/ruby-1.8.7-p334/specifications/rspec-core-2.6.2.gemspec]: invalid date format in specification: "2011-05-21 00:00:00.000000000Z"

解决方法

找到specifications/rspec-core-2.6.2.gemspec, 将下面这行注释

s.date = %q{2011-05-21 00:00:00.000000000Z}

另外在执行rake命令时, 遇到了下面的报错信息

undefined method `prerequisites' for nil:NilClass

google后发现是rspec-rails版本的问题, 使用2.6.1.beta1版本以上就好了, 编辑Gemfile

  gem 'rspec-rails', '~> 2.6.1.beta'

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中设置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中处理大批量数据的方法

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

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的定时任务插件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对此做了更多的扩展, 后面会另外写一篇文章来说明

rails中获取url信息的一些方法

如本机我测试域名为test.blog.zool.it:3000

打开的uri为 /post/Hello-World

fullurl为 http://test.blog.zool.it:3000/post/Hello-world

则rails的路由生成一下几个方法

domain(tld_length = 1)

取得域名

request.domain #=>  zool.it

request.domain(2) #=> blog.zool.it

subdomain(tld_length = 1)

subdomains(tld_length = 1)

取得子域名

request.subdomain #=>  "test.blog"

request.subdomain(2) #=> "test"

request.subdomain #=>  ["test", "blog"]

request.subdomain(2) #=> ["test"]

host()

取得主机名

request.host #=> "test.blog.zool.it"

host_with_port()

取得带端口的主机名

request.host_with_port #=> "test.blog.zool.it:3000"

raw_host_with_port()

代理服务器的主机名和端口

request.raw_host_with_port #=> "test.blog.zool.it:3000"

port()

取得由raw_host_with_port()获得的端口数值

request.port #=> 3000

port_string()

取得raw_host_with_port()获得的端口文本字符串

request.port_string #=> ":3000"

protocol()

取得当前使用网络协议

request.protocol #=> "http://"

scheme()

取得网络协议

request.scheme #=> "http"

request_uri()

request请求的uri地址

request.request_uri #=> "/posts/Hello-World"

server_port

server_port()

取得由env['SERVER_PORT']返回的端口值

``ruby
request.server_port #=> "3000"

ssl?()

当前是否在是用https加密协议


ruby
request.ssl?() #=> false

standard_port()

返回网络协议标准端口(http为80, https为443)


ruby
request.standard_port #=> 80

standard_port?()

判断当前协议是否是标准端口


ruby
request.standard_port? #=> false

url()

取得当前requset完整url


ruby
request.url #=> "http://test.blog.zool.it:3000/posts/Hello-World"
```

rails中文配置

rails默认为英文,像一些model的验证提示都是英文。
按下面方法将其汉化

首先,在https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale下载相应的语言包

中文选择 zh-CN.yml ,将其下载到config/locale下面

然后在config/application.rb里加入下面的配置

config.i18n.default_locale = :'zh-CN' 

这句话的意思是将rails的语言包默认为简体中文

然后重启服务器即可

效果

ruby-1.8.7-p302 > I18n.l Time.now
 => "2010年12月30日 星期四 10:11:21 CST" 

生成rails项目的实体关系图

实体关系图 == Entity-Relationship Diagrams

插件官网

首先安装Graphviz

% brew install cairo pango graphviz    # Homebrew on Mac OS X

% sudo port install graphviz           # Macports on Mac OS X

% sudo aptitude install graphviz       # Debian and Ubuntu

然后在开发环境中使用

group :development do
  gem "rails-erd"
end

安装

% bundle install

生成PDF

% rake erd

我的autotest配置

放在 ~/.autotest里, 起到统一配置

其中我没有用mac,所以注释掉了growl的相关配置

#require "autotest/growl"

#require 'redgreen/autotest'

require "autotest/restart"
require 'autotest/timestamp'
require 'autotest_notification'
SPEAKING = false
DOOM_EDITION = false
BUUF = false
PENDING = false
STICKY = false
SUCCESS_SOUND = ''
FAILURE_SOUND = ''


Autotest.add_hook :initialize do |autotest|
  %w{.git .svn .hg .DS_Store ._* vendor tmp log doc}.each do |exception|
    autotest.add_exception(exception)
  end
end
#

#module Autotest::Growl

#

#  def self.growl title, msg, img, pri=0, stick="" 

#    system "growlnotify -n autotest --image #{img} -p #{pri} -m #{ msg.inspect} #{title} #{stick}" 

#  end

#  #

#  Autotest.add_hook :ran_command do |autotest|

#    results = [autotest.results].flatten.join("\n")

#    output = results.slice(/(\d+)\s+examples?,\s*(\d+)\s+failures?(,\s*(\d+)\s+pending)?/) 

#    if output  =~ /[1-9]\sfailures?/

#      growl "FAIL:", "#{output}", "~/Library/autotest/fail.png", 2, "-s" 

#    elsif output  =~ /[1-9]\spending?/

#      growl "PENDING:", "#{output}", "~/Library/autotest/pending.png", 2, "-s" 

#    else

#      growl "PASS:", "#{output}", "~/Library/autotest/pass.png" 

#    end

#  end

#

#end

在has many through里实现计数

由于has many through 这种方式不支持counter_cache。

所以可以用下面的代码实现计数

class Categorization < ActiveRecord::Base
  belongs_to :post
  belongs_to :category

  after_create  :increment_counter_cache
  after_destroy :decrement_counter_cache

  private
  def decrement_counter_cache
    Category.decrement_counter("posts_count", category_id)
  end

  def increment_counter_cache
    Category.increment_counter("posts_count", category_id)
  end

end

使用sitemap_generator来为rails网站生成Sitemap

github地址

sitemap描述了网站的页面架构,请看 维基百科

  • 安装

编辑Gemfile

gem 'sitemap_generator'

bundle install安装完成之后, 执行

rake sitemap:install

会在config目录下生成sitemap.rb

*配置

将SitemapGenerator::Sitemap.default_host = “http://www.example.comurl改为自己的网站url”里的

通过下列方法,可以将所有文章生成出去

Post.all.each do |a|
    sitemap.add post_path(a), :lastmod => a.updated_at
  end

*生成

在crontab定时器里运行下列命令

rake sitemap:refresh

就会生成sitemap并且向搜索引擎进行提交