Ruby on Rails Attribute Wrapper Design Pattern
I'm looking for the right way to implement the following in Ruby on Rails 5.1:
I have an ActiveRecord Platform with an attribute of structure_xml
type LONGTEXT. It contains pure XML. I would like to add a wrapper with helper methods to query XML (using Nokogiri) eg. find certain nodes or check it out.
My current solution
Non-ActiveRecord Model The framework implements the required methods:
def Structure
def initialize(xml)
@xml_root = Nokogiri::XML(xml).root
end
def get_node_by_id(node_id)
@xml_root.xpath(".//Node[@id='#{node_id}']").first
end
...
end
The ActiveRecord model initializes this model as needed:
class Platform < ApplicationRecord
attr_accessor :structure
def structure
@structure || (@structure = Structure.new(structure_xml))
end
...
end
It works, but it doesn't seem perfect to me. What would be the correct approach to implement this?
source to share
You seem to be on the right track. I would do the same with a few minor changes:
class Platform < ApplicationRecord
delegate :xml_root, :my_method1, :my_method2, to: :structure
def structure
@structure ||= Structure.new(structure_xml)
end
...
end
delegate
allows you to invoke actions defined on another object without having to navigate through it.
You create modules only when you need a namespace, the same methods in more than one class, and when those methods are independent of the class object.
source to share
I believe the Rails way would be to represent the DSL like this (not tested, but it should work out of the box):
module Structured
def self.extended base
base.send :define_method, :structure do
@structure ||= {}
end
end
def structured(*fields)
fields.each do |field|
define_method "#{field}_structure" do
structure[field] ||= Structure.new(public_send field)
end
end
end
end
somewhere in the initializers:
ApplicationRecord.extend Structured
and in yours Platform
(if it has a field data
containing the original xml):
class Platform < ApplicationRecord
structured :data
def print_it_out
data_structure.get_node_by_id(3)
end
end
source to share
You might want to explore the Presenter or Decorator design pattern.
decorator
The decorator is similar to the inheritance functionality we find in many object-oriented programming languages. This allows you to add non-generic methods to define context objects that already have all the functionality of any generic object. As a Mercedes car brand, hold advanced facilities on top of typical car vehicles.
Presenter
The presenter is a type or a subset of the decorator itself. this is close to the MVC model. Presenters are composite objects, and we pass multiple options to it. This gives the desired result based on a successful state negotiation. The main goal of presenters is to bring logic out of the presentation.
At least I'm sure that's what you need, but the decorator pattern might also be what you're looking for, the links have some basic information about all of them.
source to share