2

I'm using Draper for general view-layer decorators.

I have some console-related, human-readability functionality I'd like to pull into new decorators.

My first thought was to put them in a module, e.g., Human::XxxDecorator, keeping them isolated from my normal view-layer decorators: they're just for rails c debugging/hacking/testing work.

This works fine at the top level; I can explicitly decorate a model with the namespaced decorator.

Now I need to decorate a collection of STI vehicles. I'm not sure what the best way to create vehicle-type-specific decorators in the same module of decorators, e.g., I have:

  • Human::CarDecorator
  • Human::TruckDecorator
  • Human::MotorcycleDecorator

I'm not sure how to get from, e.g.,

pry » dfleet = Human::FleetDecorator.new(Fleet.find(1))

to its embedded collections of vehicles, each with an appropriate decorator from the Human module. The naive approach using decorates doesn't work; I get:

Draper::UninferrableDecoratorError: Could not infer a decorator for Vehicle

The combination of:

  • Decorators from a specific module, and
  • Appropriate decorators for the STI models

is throwing things off.

Before digging into the Draper decorator inference code (I'm only assuming that's the best place to start), is this a problem that's already been solved and I'm missing something?

Dave Newton
  • 158,873
  • 26
  • 254
  • 302

3 Answers3

4

As I wrote in comments, remove the builtin decoration of your vehicles, and code yours:

def vehicles
  object.vehicles.map do |v|
    # logic to instantiate proper decorator
  end
end 

Hack incoming:

module Human
  class FleetDecorator < Draper::Decorator
    decorates_association :vehicles, with: ::Human::VehicleDecoratorDispatcher
  end

  class VehicleDecoratorDispatcher < Draper::Decorator
    def initialize(*args)
      super
      @object = ... # here you build the proper decorator based on the rules on @object
    end

  end
end

But I doubt this is clearer...

apneadiving
  • 114,565
  • 26
  • 219
  • 213
  • I'm trying to avoid the manual work implied by this, though; I know I could do all the work by hand. – Dave Newton Jun 16 '14 at 19:35
  • good luck then, I doubt there is something straightforward and cleaner though – apneadiving Jun 16 '14 at 19:38
  • Yeah, I suspect you're right after looking further into the code :( That's too bad, though, because context-sensitive decorators would be cool. – Dave Newton Jun 16 '14 at 19:45
  • well, yes and no, they'd be somewhat magical, I'm happy with plain code in this case – apneadiving Jun 16 '14 at 19:46
  • I'm okay with magic in my exploration code, although I have some other uses for this as well. But meh--was just hoping it wouldn't require any effort. – Dave Newton Jun 16 '14 at 19:50
  • well, depending on what extend you want to give to your decorators, you could override the `decorate` method in your classes – apneadiving Jun 16 '14 at 19:53
  • oh, let me suggest some hack – apneadiving Jun 16 '14 at 19:56
  • I'll give this a shot; it's clearer at the decorator level, although I'm still not overly excited for obvious reasons. I think I basically want to have a the initial decorator decide the namespace for the association decorators. I may just end up extending the affected classes on console startup and skip Draper for this, though, from the looks of things. Thanks! – Dave Newton Jun 16 '14 at 20:42
1

You could use constantize:

def dfleet
  dfleet_decorator_class.new(object.dfleet)
end

def dfleet_decorator_class
  "#{object.dfleet.class}Decorator".constantize
end
Dan Draper
  • 1,013
  • 9
  • 15
0

Use decorates: . Here is an example: CLICK

Kerozu
  • 673
  • 5
  • 15