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 --%>