Rails中的一些辅助技巧

Rails helper作为页面标签的辅助生成器,其中加入了很多奇技淫巧,有时让人觉得像是个黑魔法,像有些浏览器不支持的action,为安全生成的校验token那些都是helper中生成的。很大程度的方便了我们的开发,之前也只是停留在使用的阶段,没有进一步探索过,现在有空总结如下。

input radio type

Html中

  • 如果没有设置value属性,提交到后台时会提交默认的on值过去
  • 有value属性,没有设置值,则会当做空的字符串提交到后台

Rails Helper中

如果用的是 radio_button 那个helper,调用栈如下:

# ~/.rvm/gems/ruby-2.3.1/gems/actionview-4.2.7.1/lib/action_view/helpers/tags/radio_button.rb
def render
  ...
  tag("input", options)
end

# ~/.rvm/gems/ruby-2.3.1/gems/actionview-4.2.7.1/lib/action_view/helpers/tag_helper.rb
def tag(name, options = nil, open = false, escape = true)
  "<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe
end

def tag_options(options, escape = true)
  ...
  options.each_pair do |key, value|
    ...
    elsif !value.nil?    # 如果value为空,则不把那个属性加入元素中
      output << sep
      output << tag_option(key, value, escape)
    end
  end
  output unless output.empty?
end

如果设置一个标签的值是nil的时候,那个属性会被忽略,不会被generate出来,所以如果value设置为nil时提交到后台默认的是on值,这是检索时就可能会出现问题。

checked属性的设置如下:

def render
  ...
  options["checked"] = "checked" if input_checked?(object, options)
  ...
end

# /.rvm/gems/ruby-2.3.1/gems/actionview-4.2.7.1/lib/action_view/helpers/tags/checkable.rb
def input_checked?(object, options)
  if options.has_key?("checked")
    checked = options.delete "checked"
    checked == true || checked == "checked"
  else
    checked?(value(object)) # 用对象调用方法求的值比较当前值,看是否相等来决定是否要设置为checked
  end
end

# ~/.rvm/gems/ruby-2.3.1/gems/actionview-4.2.7.1/lib/action_view/helpers/tags/base.rb
# 对象调用方法求得的值
def value(object)
  object.public_send @method_name if object
end

# ~/.rvm/gems/ruby-2.3.1/gems/actionview-4.2.7.1/lib/action_view/helpers/tags/radio_button.rb
def checked?(value)
  value.to_s == @tag_value.to_s
end

如果标签中设置了checked属性,则看改属性是否设置为true或checked,如果不是,则在解析出来的标签中不设置那个属性,如果没有这个属性,则根据对象调用方法后的值和当前的值进行比较,看是否相等来设置checked的值。

input checkbox type

Html中

  • 使用方式和radio_button差不多,但是有一点就是checkbox是多选的
  • 提交的时候不会提交没有选的checkbox的值,如果有相同的name值,则会全部提交到后台

Rails Helper中

checkbox类型经常用的一种方式是表示boolean值,但是checkbox有种特性,如果checkbox没有提交,则不会提交到后台,这样就会有个问题,如果只为true了,现在要改为false,虽然把checkbox取消了,但是由于没有提交到后台,所以不会置为false。现在rails中的helper是在check_box那个helper里自动生成另外一个hidden input,name值和checkbox的一样,如果checkbox不提交,则只会提交隐藏的input,如果勾选了,则提交checkbox的值了。

# ~/code/mingyou-web/app/views/admin/products/_form.html.erb
def hidden_field_for_checkbox(options)
	@unchecked_value ? tag("input", options.slice("name", "disabled", "form").merge!("type" => "hidden", "value" => @unchecked_value)) : "".html_safe
end

对于保存BOOLEAN值的处理

Html 中

value都是字符串的形式提交到后台

Rails中

false, 0, “0”, “f”, “F”, “false”, “FALSE”, “off”, “OFF” 都是false,其它都是true

# ~/.rvm/gems/ruby-2.5.3/gems/activemodel-5.2.2/lib/active_model/type/boolean.rb
class Boolean < Value
  FALSE_VALUES = [false, 0, "0", "f", "F", "false", "FALSE", "off", "OFF"].to_set

  def type # :nodoc:
    :boolean
  end

  private

  def cast_value(value)
     if value == ""
       nil
     else
       !FALSE_VALUES.include?(value)
     end
  end
end