Ruby on rails 是否有可能在RubyonRails中编写一个适用于不同模型的分部代码?

Ruby on rails 是否有可能在RubyonRails中编写一个适用于不同模型的分部代码?,ruby-on-rails,Ruby On Rails,是否可以创建用于语义相似但属性不同的表单内部的分部 一个例子来解释这个想法 假设我们有以下模型: class UnitedStatesTemperature < ApplicationRecord # higher_fahrenheit # lower_fahrenheit end 如果我们将此部分用于brazil\u temperature表单,它运行良好 <%= form_with(model: brazil_temperature, local: true) do |

是否可以创建用于语义相似但属性不同的表单内部的分部

一个例子来解释这个想法

假设我们有以下模型:

class UnitedStatesTemperature < ApplicationRecord
  # higher_fahrenheit
  # lower_fahrenheit
end
如果我们将此部分用于
brazil\u temperature
表单,它运行良好

<%= form_with(model: brazil_temperature, local: true) do |form| %>

  <%= render 'shared/temperatures', form: form %>

  <%= form.submit %>
<% end %>

如果我们在
united\u states\u temperature
中使用相同的部分,则它不起作用(因为属性不同)

我的问题是:

是否可以以通用方式重写部分(
shared/\u temperatures.html.erb
),使其可以以两种形式使用(
brazil\u temperature
united\u states\u temperature
)?

答案 如果将属性名传递给表单,则可以使其更通用

<% underscored_klass = form.object.class.name.underscore %>
<% form_id = ->(attr) { "#{underscored_klass}_#{attr}".to_sym } %>

<% # use the two lines below only if you want certain default attributes %>
<% lower_temp_attr = lower_temp_attr || :lower_celsius %>
<% higher_temp_attr = higher_temp_attr || :higher_celsius %>

<%= form.label higher_temp_attr, 'Higher' %>
<%= form.text_field higher_temp_attr, id: form_id[higher_temp_attr] %>

<%= form.label lower_temp_attr, 'Lower' %>
<%= form.text_field lower_temp_attr, id: form_id[lower_temp_attr] %>
然后在您的partial中调用
lower\u temp\u attr(form.object)

使用模型 最后但并非最不重要的一点是,您可以省略helper,并确保每个温度模型都响应返回属性名称的特定接口方法

app/models/generic_temperature.rb

class通用温度
而不是让你的温度模型继承一般温度

class UnitedStatesTemperature
并在您的部分应用程序中调用
form.object.lower\u temp\u attr

解决这个问题的方法还有很多。例如,您还可以选择始终在
genericttemperature
中引发
NotImplementedError
异常,并为每个模型实施该方法。在这种情况下,如果未在继承的模型中定义方法(例如,
UnitedStatesTemperature
),则从
GenericTemperature
继承除了确保引发
NotImplementedError
异常外,什么也不做

如果您由于某种原因无法从父模型继承(可能您将其用于其他用途),则始终可以将代码放在模块中并包含它。

Answer 如果将属性名传递给表单,则可以使其更通用

<% underscored_klass = form.object.class.name.underscore %>
<% form_id = ->(attr) { "#{underscored_klass}_#{attr}".to_sym } %>

<% # use the two lines below only if you want certain default attributes %>
<% lower_temp_attr = lower_temp_attr || :lower_celsius %>
<% higher_temp_attr = higher_temp_attr || :higher_celsius %>

<%= form.label higher_temp_attr, 'Higher' %>
<%= form.text_field higher_temp_attr, id: form_id[higher_temp_attr] %>

<%= form.label lower_temp_attr, 'Lower' %>
<%= form.text_field lower_temp_attr, id: form_id[lower_temp_attr] %>
然后在您的partial中调用
lower\u temp\u attr(form.object)

使用模型 最后但并非最不重要的一点是,您可以省略helper,并确保每个温度模型都响应返回属性名称的特定接口方法

app/models/generic_temperature.rb

class通用温度
而不是让你的温度模型继承一般温度

class UnitedStatesTemperature
并在您的部分应用程序中调用
form.object.lower\u temp\u attr

解决这个问题的方法还有很多。例如,您还可以选择始终在
genericttemperature
中引发
NotImplementedError
异常,并为每个模型实施该方法。在这种情况下,如果未在继承的模型中定义方法(例如,
UnitedStatesTemperature
),则从
GenericTemperature
继承除了确保引发
NotImplementedError
异常外,什么也不做


如果您由于某种原因无法从父模型继承(可能您将其用于其他用途),则始终可以将代码放置在模块中并包含它。

首先定义一个类方法,该类方法返回温度的比例

class UnitedStatesTemperature < ApplicationRecord
  # higher_fahrenheit
  # lower_fahrenheit

  def self.scale
    :fahrenheit
  end
end


class BrazilTemperature < ApplicationRecord
  # higher_celsius
  # lower_celsius

  def self.scale
    :celsius
  end
end

首先定义一个类方法,它返回温度的刻度

class UnitedStatesTemperature < ApplicationRecord
  # higher_fahrenheit
  # lower_fahrenheit

  def self.scale
    :fahrenheit
  end
end


class BrazilTemperature < ApplicationRecord
  # higher_celsius
  # lower_celsius

  def self.scale
    :celsius
  end
end
module TemperatureHelper

  def lower_temp_attr(temp) # variation #1
    attribute_names = temp.attribute_names

    return :lower_celsius if attribute_names.include?('lower_celsius')
    return :lower_fahrenheit if attribute_names.include?('lower_fahrenheit')

    raise ArgumentError,
          'Passed object should have either "lower_celsius" or ' +
          '"lower_fahrenheit" as attribute.'
  end

  def higher_temp_attr(temp) # variation #2
    attribute_names = temp.attribute_names
    options = %w[higher_celsius higher_fahrenheit]

    attr_name = attribute_names.find { |attr_name| options.include?(attr_name) }

    return attr_name.to_sym if attr_name

    raise ArgumentError,
          'Passed object should have either "lower_celsius" or ' +
          '"lower_fahrenheit" as attribute.'
  end

end
class GenericTemperature < ApplicationRecord
  self.abstract_class = true

  def lower_temp_attr # variation #1
    return :lower_celsius if attribute_names.include?('lower_celsius')
    return :lower_fahrenheit if attribute_names.include?('lower_fahrenheit')
    raise NotImplementedError
  end

  def higher_temp_attr # variation #2
    options = %w[higher_celsius higher_fahrenheit]
    attr_name = attribute_names.find { |attr_name| options.include?(attr_name) }
    attr_name&.to_sym or raise NotImplementedError
  end
end
class UnitedStatesTemperature < GenericTemperature
end
class UnitedStatesTemperature < ApplicationRecord
  # higher_fahrenheit
  # lower_fahrenheit

  def self.scale
    :fahrenheit
  end
end


class BrazilTemperature < ApplicationRecord
  # higher_celsius
  # lower_celsius

  def self.scale
    :celsius
  end
end
<%
  higher_scale = "higher_#{form.object.class.scale}".to_sym
  lower_scale = "lower_#{form.object.class.scale}".to_sym
%>

<%= form.label higher_scale, 'Higher' %>
<%= form.text_field higher_scale, id: 
"#{form.object.class.table_name}_#{higher_scale}" %>

<%# same for lower --%>