Unable to Decorate a Particular Model - Stack Level Too Deep

For some reason, I cannot decorate a particular model, not being able to even access that model’s attributes via the decorator in the console.

I continuously get Stack Too Deep errors inconsistently; inconsistently outside of the fact I’m unable to access the original model’s attributes at all before getting a Stack Too Deep error.

The behavior is really bizzarre because the test_method works when attempted first, but I can’t actually access the same attributes of the original model; I’ve created tons of decorators and never had this problem.

The decorator is rather simple:


class PersonaDecorator
  attr_reader :persona 

  include ActionView::Helpers


  def initialize(persona)
    @persona = persona 
  end

  def self.decorate_personas(personas)
    personas.map { |persona| new(persona) }
  end

  class << self 
    alias_method :build_collection, :decorate_personas
  end

  def respond_to_missing?(method, include_private=false)
    persona.respond_to?(method) || super 
  end

  def method_missing(method, *args, &blocks)
    persona.send(method, *args, &block)
  end

  def age 
    "#{persona.age} years old"
  end


  def description
    "#{persona.description}".html_safe
  end

  def test_method
    content_tag(:div, "#{persona.description}")
  end

  def test_method2
    content_tag(:div, "#{persona.creative_commons_attribution}")
  end

  def class_box
    content_tag :div, class: 'large-12.columns' do 
        cc_class = case @persona.creative_commons_attribution_license
          when 'Attribution-ShareAlike'
            'cc_sa'
          when 'Attribution-NoDerivs'
            'cc_nd'
          when 'Attribution-NonCommercial'
            'cc_nonc'
          when 'Attribution-NonCommercial-NoDerivs'
            'cc_nonc_nd'
          when 'Attribution-NonCommercial-ShareAlike'
            'cc_nonc_sa'
          end

          content_tag(:figure, class: "large-3-columns creative_commmons_attribution_badge #{cc_class}")
          content_tag :a, class: 'creative_commons_explanation' do 
            "#{persona.creative_commons_attribution}"
          end
    end
  end

end


The model is also rather simple:


class Persona < ActiveRecord::Base

  #virtual attributes 
  attr_accessor :approve_persona, :unapprove_persona, :full_name

  #special configuration, properties, and actions 
  CREATIVE_COMMONS_ATTRIBUTION_LICENSES = %w(None Attribution-ShareAlike Attribution-NoDerivs Attribution-NonCommercial Attribution-NonCommercial-ShareAlike Attribution-NonCommercial-NoDerivs)
  has_attached_file :avatar  
  has_attached_file :background_image
  extend FriendlyId 
  friendly_id :slug_candidates, use: [:slugged, :history]

  # call backs 
  before_save :perform_state_change
  include NameConcern

  # validations 
  validates :first_name, :last_name, presence: true 
  validates_attachment :avatar, content_type: { content_type: ["image/jpeg", "image/webp", "image/png"] }, size: { less_than: 5.megabyte }
  validates :age, numericality: { greater_than: 0, integer: true }
  validates :description, length: { minimum: 50 }
  validates :byline, length: { maximum: 140 }
  #validates :project, presence: true 
  validates_with CreativeCommonsValidator

  #associations
  belongs_to :project
  has_many :influencers 
  has_many :interests 
  has_many :goals, as: :goalable

  # state_machine 
  state_machine :state, initial: :pending do 
    state :approved 
    state :coming_soon
  end

  def full_name 
    "#{first_name} #{last_name}"
  end

  def slug_candidates 
    [
      :full_name, 
      [:full_name, :age],
      [:full_name, :occupation, :age]
    ]
  end


  def perform_state_change 
    self.state = 'approved' if approve_persona == '1' 
    self.state = 'pending' if unapprove_persona == '1'
  end


end


In case the custom validator may be the cause, it’s a basic level one (though one that couldn’t be one-lined via a Proc


class CreativeCommonsValidator < ActiveModel::Validator 
  def validate(record)
    if record.creative_commons_license != "None" || !record.creative_commons_license.blank? 
      if record.creative_commons_attribution.blank? || record.creative_commons_attribution_link.blank? 
        record.errors[:base] << "name and link to original owner of copyright work attached for this record incorrectly done."
      end
    end
  end
end 


Hey Kevin,

For questions like this (“I’m getting a weird error from this particular code sample”), you’re likely to get a better response on Stack Overflow. They have much more traffic, so you’re more likely to find someone who wants to debug your code for you. :smile:

I think this forum is better suited for things like “what’s a good general approach for decorating objects?”, or similar high-level questions. Things like that are likely to attract the attention of more experienced folks who like chiming in.

-Ben

Sorry @benorenstein; my question does read too much like that; apologize the time to help others you can never get back due to my lack of thinking more carefully about my question being asked in a more meaningful way to have more value to people on this forum.

In terms of a higher level question , I am confused in general how to avoid stack level too deep levels when it comes to the use of decorators; what common view actions to be careful of “compartionizing” inside a decorator and instead keep it in the view?

I don’t think I’ve ever gotten “stack level too deep” errors when trying to decorate an object. If you’re seeing that, I think it’s probably due to a bug in your code.

I don’t think there’s a reason that you should end up with an infinite delegation loop.

Understood, it’s something I’ll begin debugging another way that’s not decorator-related

##Solution: Use Draper
@benorenstein If it helps, the problems go away once I give in and install and inherit things from the Draper gem and its classes; I’ve found out the hard way render and other ActionView capabilities are out of reach of ActionView::Helpers; since you can’t inherit that class or include it (not being a module), it’s a lot of nasty code to do that’s already done by Klabnik and Casimir.

Seems I’ve reached the limitations of custom decorators with my knowledge of Rails/Rack middleware

Similarly, I had to do @project.project to handle representations of my models in paths and controllers; doing things the long way (i.e. having to use respond_to |format| block approach instead of respond_to :formatrespond_with approach to use serializers appropriately with limited mileage advice regarding including modules of ActiveModelSerializer to have some (but limited) mileage.