1

I have some objects in my DB that I like to render with a dedicated Hyperstack view Component. Lets say my objects have a unique name property from A to J. Now I would like to loop through them with each and render a ComponentA, ComponentB, ComponentC, ... depending on the name of my object, and pass my object as a param to the component.

What I do now is:

DB_Objects.each do |object|
  if    object.name == 'A'
    ComponentA(data: object)
  elsif object.name == 'B'
    ComponentB(data: object)
  elsif object.name == 'C'
    ComponentC(data: object)
  elsif ...

What I want to do is something like:

DB_Objects.each do |object|
  ('Component' + object.name).constantize(data: object)
end

This is pseudo code as you can't give variables to constantize. But it shows what I would like to do.

How can I prevent a manual mapping of a object to its view.

2 Answers2

2

Never used HyperStack, but this should give you the right idea.

class Thing < ApplicationRecord
  OBJECT_TYPES = { 'A': 'CommponentA' }.freeze

  def to_component
    @to_component ||= OBJECT_TYPES[self.name].constantize.new(self)
  end 
end 

You could just "Component#{self.name}".constantize.new(self) but I've found that to be rather confusing to other developers when they jump in on a project.

Keep in mind String#constantize will return the actual constant, and not just a string that's constantized (camelcased and what have you) so you can call #new or #call or whatever you want on it.

Josh Brody
  • 5,153
  • 1
  • 14
  • 25
  • This would almost work. The problem is `new` only creates a component but does not actually render it. You would have to use the `insert_element` method instead of new. Otherwise pretty good answer for somebody who never used Hyperstack :-) – Mitch VanDuyn Aug 03 '19 at 23:31
1

Every component class defines a method of the same name as the class. When you say for example

ComponentA(data: object)

you are calling a method named ComponentA that is tied the the ComponentA class.

So to dynamically reference the component you would use the send method:

send('Component' + object.name, data: object)

Alternatively each component class has a class method insert_element which will render an instance of the component class, and insert it into the rendering buffer. For example:

('Component' + object.name).constantize.insert_element(data: object)

I bring this up because while it is longer, you could use it to generalize this idea of yours (which I think is pretty cool)

class ApplicationRecord < ActiveRecord::Base
  def render(opts = {})
    ('Component' + object.name).constantize.insert_element(opts, data: object)
  end
end

Now you can say

some_record.render

Of course to generalize it like this you might want to use something other than name. Anyway you could really have fun with this !

Mitch VanDuyn
  • 2,838
  • 1
  • 22
  • 29