在Ruby-on-Rails 6.0中,我想使用模型,视图和控制器创建一个名为services的资源。
每个服务都有一个ID,一个名称,一个类型和一个ImageIndex
但是我希望模型映射到XML(请参见下文)而不是ActiveRecord。我希望这些动作(索引,显示,新建,编辑和销毁)修改XML本身。
我的第一个尝试是将代码添加到控制器中以解析XML,如下所示:
def index
@services = []
f = File.open("xml/eidos/LeftTree/Services.xml")
doc = Nokogiri::XML(f)
f.close
root = doc.root
tags = root.xpath("Tag")
tags.each do |tag|
@service = Service.new
@service.node_name = tag.xpath("NodeName").text
@service.text = tag.xpath("Text").text
@service.image_index = tag.xpath("ImageIndex").text
@services << @service
end
@services
end
此操作成功,并向我返回了完整的实例列表。我对def Show
做相同的操作(请注意,我不需要编辑或删除XML)
现在的中间问题是:
我如何管理主要Tag元素的子代?
我应该创建至少两个其他资源:request
和choice
(还有更多,基于type
元素的内容,而我的方法看起来并不干净。
最后一个问题是:
这就是为什么我想找到/构建一种类似于ActiveRecord的方法,在其中可以列出资源的属性和关系的原因。
朝这个方向有什么建议吗?
示例XML如下所示:
<Services>
<Tag>
<ID>X401ED7DA027C</ID>
<Name>Accertamento Ici Con Adesione</Name>
<Type>Service</Type>
<ImageIndex>0</ImageIndex>
</Tag>
<Tag>
<ID>X3F9BEB550223</ID>
<Name>Cambio di abitazione</Name>
<Type>Service</Type>
<ImageIndex>0</ImageIndex>
<Tag>
<ID>X3F9BEB550223/X400E4CBA032E</ID>
<Name>Richiesta Di Cambio Di Abitazione</Name>
<Type>Request</Type>
<ImageIndex>1</ImageIndex>
<Tag>
<ID>X3F9BEB550223/X400E4CBA032E/X40091B660349_X3FFF3E980118</ID>
<Name>Allegato</Name>
<Type>Choice</Type>
<Multiplicity>
<MinOccurrence>0</MinOccurrence>
<MaxOccurrence>*</MaxOccurrence>
</Multiplicity>
<Required>False</Required>
<ImageIndex>5</ImageIndex>
<IDRole>X40091B660349</IDRole>
<NameSpace>OggettiCondivisi</NameSpace>
</Tag>
</Tag>
</Tag>
</Services>
ActiveRecord没有对平面文件数据库的内置支持。如果要使用ActiveRecord,建议您使用一种受支持的数据库,并从XML导入/导出。
这个答案实际上只是在给猪上妆。因为建立自己的基于XML的对象持久性根本没有任何好处(这是没有人这样做的原因),您将需要花费一些时间来更好地利用它。如果数据不遵循严格的架构,则只需使用Mongoid或其他NoSQL db。
第一步是将输入移动到模型中,并减少它的吸引力:
class Service
include ActiveModel::Model
include ActiveModel::Attributes
attribute :node_name, String
attribute :text, String
attribute :image_index, String
def self.import(fn = "xml/eidos/LeftTree/Services.xml")
doc = Nokogiri::XML(File.read(fn))
tags = doc.root.xpath("Tag")
tags.map do |tag|
Service.new(
node_name: tag.xpath("NodeName").text,
text: tag.xpath("Text").text,
image_index: tag.xpath("ImageIndex").text
)
end
end
end
这将使您将控制器的动作细化为:
def index
@services = Service.import
end
#each
仅在仅关心循环的副作用而不是返回值的情况下才应使用。例如:
@users.each do |u|
u.send_welcome_email!
end
对于其他,请使用#map
或可枚举的其他方法,例如#collect
,#reject
...
您还将为循环的每次迭代重新分配实例变量@service
。了解实例变量和局部变量之间的区别。而且要分配的局部变量的数量要少一些。您不需要将所有内容都分配给变量-特别是如果您只使用过一次则不需要。
您想要在模型(甚至是单独的服务对象)中进行导入的原因是,控制器很难测试。它们具有大量的依赖关系,真正测试它们的唯一好方法是通过HTTP请求间接进行。控制器是在Rails中将所有内容捆绑在一起的粘合代码-请勿肿它们。
另一方面,模型很容易测试,因为您只需调用Service.import
并编写关于结果的断言。
而且我希望操作(索引,显示,新建,编辑和销毁)修改> XML本身。
玩得开心。您将需要使用Nokogiri来加载和修改文档树。在[[n小时后,您将建立一些基本上可以完成数据库功能的事情,但是由于它根本无法扩展,因此在所有方面都变得更糟。
这就是为什么我想找到/建立像ActiveRecord替换,我可以在其中列出属性和我的资源之间的关系。
再次-浪费时间。您可以只使用ActiveModel::Model
创建某些东西,就像在ActiveRecord中进行关联一样。但是为什么呢?