-
Notifications
You must be signed in to change notification settings - Fork 356
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add class decorator support #237
Conversation
@hayesr, @martinpovolny you were active in the original discussion about class decorators => ping :) |
Aand @skateman :) |
So..
Putting this in So.. I guess we can either keep it here, or remove draper and reimplement the decorate method here as well. EDIT: actually, putting it in (ui-classic) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is good idea, but I think we should do some changes as to the implementation.
lib/miq_decorator.rb
Outdated
def method_missing(*args) | ||
@_decorated_instance.send(*args) | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would strongly suggest using an actual delegator rather than crossing your fingers with method_missing.
Are you familiar with some of Ruby's core Delegator classes? I would use SimpleDelegator
here.
class MiqDecorator < SimpleDelegator
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 on using the SimpleDelegator
def decorate | ||
self.class.decorator_for(self.class).new(self) | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Follow this after addressing comment here)
Patching something that doesn't belong to you is a bad idea, especially re-opening a class in our lovely project 😄
Instead, you can do away with this entirely and create these methods in MiqDecorator
itself. MiqDecorator
can take in instances and klasses and return the proper decorator (which it owns) instance or class for you.
class MiqDecorator < SimpleDelegator
self << class
def decorator_for(klass)
"#{klass.name}Decorator".safe_constantize
end
def decorate(instance)
decorator = decorator_for(instance.class)
decorator.new(instance)
end
end
end
Disclaimer: I just wrote this pseudo code up quick, a few details are plainly missing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Yay for naming conventions in decorator_for
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Patching something that doesn't belong to you is a bad idea, especially re-opening a class in our lovely project 😄
I would take this a bit further and state that as you move to the REST API, you can't patch platform classes, so doing so here might make it harder to move forward. However, using the decoration pattern above, it is clear which methods are owned by the UI and not the platform, and those can be converted to code that eventually decorates the result of the REST API.
@@ -959,7 +959,7 @@ def listicon_item(view, id = nil) | |||
# Return the icon classname for the list view icon of a db,id pair | |||
# this always supersedes listicon_image if not nil | |||
def listicon_icon(item) | |||
item.decorate.try(:fonticon) if item.decorator_class? | |||
item.decorate.try(:fonticon) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With my other comments addressed, this now becomes a non-patched call, entirely dependent on the UI's own code, with MiqDecorator.decorate(item)
. You of course can feel free to create a top level controller helper like decorate_model
that does the same thing without MiqDecorator
explicitly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good. just 2 style ideas that can be ignored.
I like chris's idea of using a delegator
spec/helpers/quadicon_helper_spec.rb
Outdated
@@ -963,7 +963,7 @@ | |||
|
|||
context "when item is not CIM or decorated" do | |||
before(:each) do | |||
allow(item).to receive(:decorator_class?) { false } | |||
allow(item).to receive(:decorate) { nil } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: could you use and_return
?
allow(item).to receive(:decorate).and_return(nil)
class ApplicationRecord < ActiveRecord::Base | ||
class << self | ||
def decorate | ||
@_decorator || (@_decorator = decorator_for(self)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
another nit. I think this is more clear with ||=
:
@_decorator ||= decorator_for(self)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(But you won't need this code given proper delegation, anyway)
Your comment about initializers here should be addressed/fixed given my feedback. |
I think it might be possible to extend ApplicationRecord with an initializer block in |
It sounds like you want to remove Draper, so in that case, I would say remove draper. |
Indeed, Draper should be replaced given where we are now. Let me be clear that your implementation is understandable given that you were basically implementing how Draper does it. But Draper, with the UI split having been done, now doesn't make any sense for us at all because of how it patches things in much the same way. The UI doesn't really own it's models that it's trying to decorate anymore. |
This pull request is not mergeable. Please rebase and repush. |
Looking forward that's true. But for now it's not that much imporant. We need to be able to decorate to be able to do cleanups and move forward. As @Fryguy wrote, we might be decorating stuff returned from the API in the future. But for now we need the decorators for both instances and classes of the models. |
This pull request is not mergeable. Please rebase and repush. |
This makes it possible to run `decorate` not only on model instances, but also directly on the classes. Differences from Drapper: * there is only that `decorate` method, no attempt at implementing `decorator_class?` or any other Draper methods was made * `delegate_all` works out of the box (meaning `Host.decorate.foo` calls `Host.foo` when the decorator does not implement that method, and does have the `delegate_all` call) * `decorate` returns nil when no decorator is found, instead of throwing Usage: `VmServer.decorate` will return `VmOrTemplateDecorator` adding ``` def self.fonticon "foobar" end ``` to that decorator (note the `self`) will cause `VmServer.decorate.fonticon` to return `"foobar"`.
pretty much just expects an instance passed to the constructor, so it can delegate everything to it
…qDecorator.for so that MiqDecorator.for(Klass) returns KlassDecorator, if it exists
now that decorate doesn't throw but returns nil instead
So... I've rebased and addressed most of the concerns, including moving the class resolution logic to But... do we really want to write |
because sometimes Foo::Bar means class Foo, class Foo::Bar and not module Foo > class Bar
…r::Klass and just call ApplicationRecord.send(extend/include...)
Checked commits https://github.com/himdel/manageiq-ui-classic/compare/de3561070a93f4dd69f140bcc29a8e75805728bc~...5c1fec047e408741352f64d5fbdb0f164bbaacce with ruby 2.2.6, rubocop 0.47.1, and haml-lint 0.20.0 app/decorators/miq_event_definition_decorator.rb
spec/lib/miq_decorator_spec.rb
|
Tested this does not break the API, so we do not depend on ManageIQ/manageiq#14040. |
... doing a resume of a gitter discussion - ☝️ February 24, 2017 3:18 PM We will use |
👍 Please note for anyone having an issue with the |
This makes it possible to run
decorate
not only on model instances, but also directly on the classes.It also replaces Draper with a simpler solution.
Differences from Drapper:
decorate
method, no attempt at implementingdecorator_class?
or any other Draper methods was madedecorate
method is also available on classes nowdelegate_all
is done automatically, but only for decorated instances, not much sense in using it for the decorated classdecorate
returnsnil
when no decorator is found, instead of throwingUsage:
VmServer.decorate
will returnVmOrTemplateDecorator
vm_instance.decorate
will return an instance ofVmOrTemplateDecorator
which delegates to the originalvm_instance
objectadding
to that decorator (note the
self
) will causeVmServer.decorate.fonticon
to return"foobar"
.Closes ManageIQ/manageiq#10313
Done in preparation for ManageIQ/manageiq#13199, cc @epwinchell
TODO::.decorate
use from manageiq - API - VMs collection - replace decorators with SupportsFeatureMixin manageiq#14040 (and VmOrTemplateDecorator - remove supports_console and supports_cockpit #458)Note that this does monkeypatch
ApplicationRecord
- adding a class and instancedecorate
method (and a@_decorator
field to cache it once resolved).To be able to decorate non-AR models, we'll need to
extend
andinclude
the relevant mixin manually.