Ruby 分享一个 attr_accessor 的技巧

huacnlee · 2011年12月08日 · 最后由 jhjguxin 回复于 2012年03月16日 · 6053 次阅读

这个是这周二杭州 Ruby Tuesday 扯出来的 比如这样的场景,你有个 Post ,它有 tags 的属性,里面用 Array 存放多个 tag,但是页面上编辑的时候我们可能会要用户输入以逗号隔开的方式提交多个 tag (比如: ruby, rails, python )然后保存的是将这个数据分割为数组保存。 代码就像这样,只是我以前的写法。

class Post
  include Mongoid::Document
  field :title
  field :body
  filed :tags, :as => Array, :default => []

  attr_accessor :tag_list

  before_save :split_tags
  def split_tags
    if !self.tag_list.blank?
      self.tags = self.tag_list.split(",")
    end
  end
end

而且我还需要在 Controller 里面修改的时候将 tags 转换为逗号分隔的 tag_list

class PostsController < ApplicationController
  def edit
    @post = Post.find(params[:id])
    @post.tag_list = @post.tags.join(",")
  end
end
<% form_form(@post) do %>
  <%= f.input :tag_list %>
<% end %>

但是实际使用的时候却又很多麻烦,因为 before_save 会又很多动作都会经过,而且如果很多类似这种场景的都写 before_save 或者 after_save 里面的话,这里的逻辑会越来越乱,而导致后面看起来很累,而且容易出问题。

于是,我们聊出了新的做法,覆盖 attr_accessor 的 get set 方法来实现分割为数组的动作。

class Post
  ...
  def tag_list=(value)
    self.tags = value.split(",") if !value.blank?
  end

  def tag_list
    return "" if self.tags.blank?
    self.tags.join(",")
  end
end

这样一来, Controller 里面就不用写了,直接调用 tag_list,它的改变将会和 tags 息息相关

共收到 22 条回复

学习啦。

yes. 以前還沒有 nested form 綁 attributes 時。都要用這樣的技巧去實作....

以前還有寫過一個營養食譜網站。裡面有 vitamin_b , vitamin_c , vitamin_d, b_6, b_12 這樣的營養成分。然後輸入時必須要能選 g , mg, ug。但 db 裡是以 mg 為單位。

不用 get,set 做會死人....

另外營養成分有 30 多欄..............

算是印象非常深刻 orz

我的做法是将各种常用attr的get/set处理做成plugin: http://quake.iteye.com/blog/448235

module YourPlugin
  module Mixin
    def xxx_attr(attr)
      attr = attr.to_s
      self.class_eval(%Q{
def #{attr}
  #...
end

def #{attr}=(value)
   #...
end
        }
      )
    end
  end
end

class Post
  include YourPlugin::Mixin
  xxx_attr :tag_list
end

记得Railscast里面有讲过虚拟属性,很实用的技巧

看到!xx.blank?就头晕,xx.present?多好呀~

good idea!

8楼 已删除

#6楼 @hooopo 谢谢分享,present?的使用频率应该高于blank?,我写/看过的codes中,blank?大多跟!配合使用。

#6楼 @hooopo 我还没用过present?,我一般都写unless xx.blank?

#10楼 @chechaoyang 更ruby style的rule是这样的: 能if就不用unless 能用if + return 就不用else 能不用!就不用

#11楼 @hooopo 谢谢分享,有了present?我应该会少写不少unless,我基本都是为了避免!才写的unless。

比较喜欢@quakewang的做法。谢谢分享。

都定义了getter和setter还需要attr_accessor么?

#6楼 @hooopo 居然一直没用过 present? 方法,好东西呀。

要用就要用两个 !! lol....

#17楼 @poshboytl 这个写法挺有趣的,非常醒目

ruby这个属性的概念很容易让人忽视get set,用java的很容易会想到这样做

赞同 @bony 观点,学习了

自从git 出bug过后 才开始关注attr_accessor 很多问题都不是很透彻 使劲抓脑袋吧 写spec也头大 代码不规范 spec example 弄得要疯了 mark一下 慢慢研究

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册