Boris
Boris

Reputation: 348

Design pattern for attribute wrapper in Ruby on Rails

I am looking for the right way to implement the following in Ruby on Rails 5.1:

I have an ActiveRecord Platform with the attribute structure_xml of type LONGTEXT. It contains pure XML. I would like to add a wrapper with helper methods to query the XML (using Nokogiri), e.g. to find certain nodes or to validate it.

My current solution

A non-ActiveRecord model Structure 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 initialises this model if needed:

class Platform < ApplicationRecord
  attr_accessor :structure

  def structure
    @structure || (@structure = Structure.new(structure_xml))
  end
  ...
end

It works, but it does not seem ideal to me. What would be the right approach to implement this?

Upvotes: 3

Views: 1624

Answers (3)

Surya
Surya

Reputation: 16002

You seem to be on right path. I would do probably the same with slight 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 call the actions defined in another object without having to navigate through it.

You create modules only when you require namespace, same methods in more than one class, and when those methods are independent of an object of a class.

Upvotes: 1

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

I believe, Rails way would be to introduce a DSL like that (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 initializers:

ApplicationRecord.extend Structured

and in your Platform (assuming it has a field data containing the raw xml):

class Platform < ApplicationRecord
  structured :data

  def print_it_out
    data_structure.get_node_by_id(3)
  end
end

Upvotes: 1

Thermatix
Thermatix

Reputation: 2929

You might want to look into the Presenter or Decorator design pattern.

Decorator

A decorator resembles functionality of inheritance that we find in many object oriented programming languages. It allows adding of non-generic methods to define contextual objects which already possess all features of any common entity. Like Mercedes brand’s motorcars hold advance facilities on top of typical automobile machines.

Presenter

A presenter is a type or subset of decorator itself. It is close to view model of MVC architecture. Presenters are composite objects and we feed multiple options to it. It renders desired outcome based on successful matching of condition. The main objective of presenters is to put logic out of the view.

At least I'm pretty sure that's what you need but the decorator pattern might also be what you're looking for, the links has some basic info on both of them.

Upvotes: 0

Related Questions