在Ruby-on-Rails 6.0中,我想创建一个名为资源的服务,包括模型、视图和控制器。
每个服务都有一个 ID、一个名称、一个类型和一个图像索引
但是我希望在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 数据库,如果数据不遵守严格的模式。
第一步是将导入移动到模型中并使其减少吸吮:
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小时后,您将构建一些基本上可以执行DB功能的东西,但在各个方面都更糟,因为它根本不可扩展。
这就是为什么我想找到/构建一种像 活动记录替换,我可以在其中列出属性和 我的资源关系。
再次 - 完全浪费时间。您可以使用ActiveModel::Model
创建一些类似于关联在 ActiveRecord 中执行的操作的内容。但是为什么?