1

我正在编写一个充当定制度量表单的应用程序。

客户模型有很多以毫米为单位的整数形式存储在数据库中的属性。由于此应用程序将在欧洲和美国使用,因此我将使用虚拟属性向用户显示英寸和厘米版本的数据。

例如对于客户身高,我的模型中有这个:

  def height_in_cm
    height / 10
  end

  def height_in_cm=(height)
    self.height = height.to_f  * 10
  end

  def height_in_in
    height * 0.039370
  end

  def height_in_in=(height)
    self.height = height.to_f / 0.039370
  end

这在我的 _form 视图中:

  <% if @customer.measure_unit.eql? "imperial" %>
    <%= f.input :height_in_in %></br>
  <% else %>      
    <% if @customer.measure_unit.eql? "metric" %>
      <%= f.input :height_in_cm %></br>
    <% end %>     
  <% end %>

正如我所说,我有很多属性,我的客户模型文件变得非常长并且非常容易出错。

有没有干燥的方法来缩短它?

4

2 回答 2

1

封装这样的逻辑:

def display_height
  case measure_unit
  when 'imperial' then height_in_in
  when 'metric'   then height_in_cm
  else height_in_in
  end
end

那么你的视图可以是这样的:

<%= @customer.display_height %>

如果您在许多模型中使用这些相同的转换方法,请将其提取到一个模块中,如下所示:

module HeightConversions

  def height_in_cm
    height / 10
  end

  def height_in_in
    height * 0.039370
  end

  def display_height
    case measure_unit
    when 'imperial' then height_in_in
    when 'metric'   then height_in_cm
    else height_in_in
    end
  end

end

并将其包含在需要的模型中:

class Customer
  include HeightConversions
end

编辑:

好的,也许您需要更多类似的东西:

  %w(neck waist arm).each do |name|
    self.class_eval do

      define_method :"#{name}_in_cm" do
        self.send(name) / 10
      end

      define_method :"#{name}_in_cm=" do |n|
        self.send("#{name}=", (n.to_f * 10))
      end

      define_method :"#{name}_in_in" do
        self.send(name) * 0.03
      end

      define_method :"#{name}_in_in=" do |n|
        self.send("#{name}=", (n.to_f / 0.039370))
      end

    end
  end

这通常称为元编程。这是一篇关于在 Rails 中使用不同方法的简洁文章: http ://www.trottercashion.com/2011/02/08/rubys-define_method-method_missing-and-instance_eval.html

于 2012-12-12T15:31:46.593 回答
0

我会让 gem 或 javascript 进行转换。像ruby ​​-units 这样的 gem可能有点矫枉过正,但它为您可能想要的任何转换提供了持久的解决方案。

为了让 javascript 进行转换,您可以在 application.js 中添加一些内容。您还可以使用“基线”概念在 Rails 中干燥您的方法。

function convert_units(input, input_unit, output_unit) {
// Used to convert all units to similar intermediary unit. I used meters.
var inches_per_meter = 39.3701;
var sixteenths_per_meter = 629.9216;
var cm_per_meter = 100;
var mm_per_meter = 1000;

var intermediary = 0; //in meters

switch(input_unit)
{
  case: "mm": intermediary = input * mm_per_meter;
    break;
  case: "cm": intermediary = input * cm_per_meter;
    break;
  case: "meter": intermediary = input;
    break;
  case: "inches": intermediary = input * inches_per_meter;
    break;
  case: "sixteenths": intermediary = input * sixteenths_per_meter;
    break;
  default: return "Input unit not correctly accomodated";
}

switch(output_unit)
{
  case: "mm": return intermediary / mm_per_meter;
    break;
  case: "meter": return intermediary;
    break;
  case: "inches": return intermediary / inches_per_meter;
    break;
  case: "sixteenths": return intermediary / sixteenths_per_meter;
    break;
  case: "cm": return intermediary / cm_per_meter;
    break;
  default: return "Output unit not correctly accomodated";
}
}

那么在你看来

convert_units(@customer.height, 'inches', 'cm');
于 2012-12-12T16:24:22.150 回答