-
Notifications
You must be signed in to change notification settings - Fork 333
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
Using Draper with Rabl #286
Comments
This may be bleeding into the internals of Rabl so even though I suggest this as a workaround, it makes sense for us to provide a callback to construct your object. See if this even works: collection @users
extends "memberships/autocomplete_suggestion", object: UserDecorator.decorate(@_object) or leave your autocomplete_suggestions as-is and try this in your extended template: object UserDecorator.decorate(root_object)
attributes :id
# ... |
I used the second option since it looked a bit less hackish and it worked like a charm! Thanks! |
Actually I spoke too soon. None of the above work. |
I can look into this later or... if you setup a stupid quick Rails app and link me to it, it'll save me a few mins and I can look at it sooner. |
Here's a sample app of this: http://db.tt/WzCgeOUZ . Just start the Rails server and go to http://0.0.0.0:3000/users.json I'm aware that I could do something like It seems like Thanks! |
I'll check it out later tonight. |
So basically it does work with partial the first time through when evaluating the block (via Ugh, I think the best way to deal with this permanently will be a more predictable DSL which we still need to get around to. That way, each block itself can just be instance_eval'd such that the results just return up without having to call around between engine and builder. All that above is mostly for @nesquena and I. Here's your solution: # index.json.rabl
collection @users
extends "users/show"
# show.json.rabl
attributes :id, :full_name
# users_controller.rb
class UsersController < ApplicationController
def index
@users = User.all.map {|user| UserDecorator.decorate(user) }
end
end
# user_decorator.rb
class UserDecorator < Draper::Base
decorates :user
def full_name
"#{user.first_name} #{user.last_name}"
end
end To go over the changes. The UserDecorator uses But the biggest change is to have the controller setup the decorator before sending it to the view. This is how I probably would've done it anyway. Think about an ERB. You probably weren't going to call UserDecorator in the form_for helper or anything - it makes sense to have the object already setup before you get into the view. Now it still should've worked from within rabl when passing the object a modified object via partial. So I'll call that a bug that we'll address with the refactor in v1. But the solution above is a proper presentation / decorator setup. Controllers interface with models and setup everything for the dumb views. I like to say that I want my views to control what's seen - not how it's seen. |
@databyte As I said in my previous comment, I actually do instantiate the decorators directly in the view to benefit from caching. When going through many objects, doing so makes the execution much faster at the expense of having a tiny bit of logic in the view. If I can avoid instantiating a 1000 objects, that's a fair price to pay IMO. For now I'll just move the method I need directly in the model but it would be great if there was a hook in RABL to manipulate the data before displaying it. Thanks! |
The biggest call in your controller is going to be to User.all, not really the thin decorators but I see what you mean. You'll still have a problem with the call because of the way caching works in views. If you decorate an object as a parameter to rendering the partial, the object will still be created even if the contents are cached because you need the object first and then grab the cache_key from that object. So what you need is caching layers such that:
So if you look at the rendering process, you can't prevent user being decorated unless you can cache against User.all to know that you don't have to render at all. The trick with view caching at that last level is saving time with any associated models (N+1 queries) and any template fu (concat strings, complex helpers, 100 partials, etc). |
Of course my sample project is just a rough example. In real life, I'd select just the id and updated_at to build the cache_key which is pretty fast. I assume that the user partial is not rendered at all if there's a match for the cache_key? That's how I do it with ERB. I assume Rabl wouldn't execute any code in my partial unless it needs to render that partial? |
Of course it's a sample - I didn't mean to imply otherwise. It's just that if you query the database to grab all users even just the ID column, it results in a lot of GC work. Compared to somehow storing a cache_key elsewhere. For instance, in one system I had a scope that would grab the last_updated value for the entire table as the cache_key (in your case, user sorted desc by last_updated with limit 1). It works well if your table doesn't have a lot of writes obviously and this one was written to a few times a week. The template is initially parsed to read in the object/cache values. We build up the DSL but don't evaluate it if a cache entry exists. |
Closing given the solution in my comment above. We'll be sure to address it though in the API work that needs to be done. |
Any update on this? It is bad to map twice (in the controller decorating and in the rabl) |
I have the following templates:
and
avatar_url
is defined in my Draper decorator, but I cannot access it from inside the node. Is there a way to wrap my @user object in a decorator from inside Rabl?The text was updated successfully, but these errors were encountered: