-
Notifications
You must be signed in to change notification settings - Fork 42
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
RailsConf: Presenters #11
base: rc-decorators
Are you sure you want to change the base?
Conversation
* Values are memoized so that queries are only done when value is needed for display. This helps avoid unnecessary running of queries. * Try out application running as rails s -e production * Observe queries first time and second time. * Notice that before abstracting out the presenter, the count query in the controller ran even if the view was cached.
* Created FollowPresenter, and FollowedUsersPresenter
Regarding the coding style to set at most one instance variable in a controller action, what about having using controller filters ( I got that comment in response to whether or my Presenters pattern (#11) is justified. Unless one uses such |
</section> | ||
<section> | ||
<%= render 'shared/stats' %> | ||
<% if @users.any? %> |
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.
tell, don't ask. you can get rid of both of these predicates on .any? by moving the logic into a helper or partial.
if the only point in these predicates are to prevent @users.each and @presenter.users.each from chunking on you, then you can do a @presenter.users.to_a.... and @users.to_a...
Then they wont blow up if there isnt anything in them as they become empty arrays!
Why do you make separate stereotypes for draper decorators and presenters ? Why not just use draper decorators as presenters and have multiple possible decorators for a given resource - a resource being an ActiveRecord, or an ActiveModel, or any kind of object which need to be manipulated by the controller. If you have multiple draper decorators sharing behaviour, you could just use modules or concerns. |
<span><%= link_to "view my profile", @user %></span> | ||
<span><b>Microposts:</b> <%= @user.microposts.count %></span> | ||
<%= gravatar_for @presenter.user %> | ||
<h1><%= @presenter.user.name %></h1> |
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.
Does this here violate the "Law" of Demeter? The View here needs to know about the User model. It might be better to have a method named "user_name" in the presenter. This way the presenter is the only one that needs to know about the User model. The same could be said for "@presenter.user.microposts.count", although perhaps the User model (or some other object) should have a "microposts_count" method so that the presenter doesn't know about the Microposts model either.
This way, the view is no longer coupled to any other objects but the presenter. It's much easier to test, because you can easily stub out these methods, and it would be much easier to change implementation without breaking things.
Just a thought. :)
By the way, I really enjoyed your talk from RailsConf on this subject. It has given me lots to consider and try for myself. Great work!
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.
Hi @dsibiski, Thanks! I don't really see the value of adding a user_name
method on the presenter, as it would be just a pass through. However, if there was some sort of modification to the @presenter.user.name
, such as needing to to modify the value if it were too long, then that would be a fine example to justify a new method.
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.
@Systho The reason I separate the concept of presenters and decorators is that I'm using the concept of a presenter
as very specifically for a single view or controller action, and the decorator
as something more closely related to the model that could be used on many views and controller actions. Thus, I might start with the code in the presenter
and then if that logic was needed elsewhere in the program, I'd be tempted to break out the method into one on the decorator
.
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.
@justin808 You're right when you say that the method would just be a pass through, but unfortunately, without it, you are introducing some unnecessary coupling to 3rd party objects. In my opinion, that is one of the purposes of having a Presenter object. To eliminate the coupling between the View layer and the Model/data layer.
There is a portion of an article on "The Pragmatic Bookshelf" that addresses this issue exactly...under the subhead "Law of Demeter" in the article "Tell, Don't Ask" it says:
The disadvantage, of course, is that you end up writing many small wrapper methods that do very little but delegate container traversal and such. The cost tradeoff is between that inefficiency and higher class coupling.
The higher the degree of coupling between classes, the higher the odds that any change you make will break something somewhere else. This tends to create fragile, brittle code.
Depending on your application, the development and maintenance costs of high class coupling may easily swamp run-time inefficiencies in most cases.
Reference: https://pragprog.com/articles/tell-dont-ask
There is also a really great talk by Ben Orenstein that touches on these points and why they're beneficial. Check it out: https://www.youtube.com/watch?v=C0H-LyZy9Ko
You also mentioned in your previous comment:
However, if there was some sort of modification to the @presenter.user.name, such as needing to to modify the value if it were too long, then that would be a fine example to justify a new method.
I think the problem here is that when you change the model, you have to remember that you need to change things not only in the presenter, but also in the view (and perhaps in other classes - real apps are always more complex). If the presenter was the only one coupled to the Model, then this would be far less of an issue.
Of course, one could go crazy with this stuff and it might not be practical to follow this religiously for every circumstance, however, it has surely helped me to keep coupling low and save me tons of headaches especially when more features and complexity are introduced into the system.
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.
You can delegate specific methods to the user object. E.g. user_name
would delegate the name
method to the user. The key thing would be decouple the view with knowing how the what to go through to get the user name. Allows the presenter could override this and provide implementation of user_name
if necessary.
RailsConf 2014 Talk, Tuesday, 2:30, Ballroom 4, Concerns, Decorators, Presenters, Service Objects, Helpers, Help Me Decide!
Presenters
Code: Github Fat Code Refactoring Techniques Pull Request Presenters
Example: Added
FollowersPresenter
andFollowedPresenter
to encapsulatemultiple instance variables in the show_follow view.
WARNING: This code change is an over-simplified example of a good case of when
to encapsulate the state passed to a view. As you look at this example, consider
a case where there is far more logic in the view and the controller setup of the
instance variables for the view.
A Name for a PORO
a view.
thumbs up on the term "Presenter".
"Presenter"
constructed in the controller method or maybe in the view.
Why?
controller action to one.
being spread inside the ERB or HAML code.
Presenter Applicable Situations
associated with the action just to set up the context for the view.
different response types for the same controller method.
can move to a decorator or concern. A module is another option for DRY'ing up
duplication.
Presenter Solution
app/presenters
and add this code toapplication.rb
app/presenters/users
User::FollowPresenter
User::FollowPresenter
that is used by the view.are available:
Presenter Advantages
view needs.
highly beneficial if fragment caching is used, as compared to instantiating
instance variables in the controller..
helper.
Presenter Disadvantages
as putting some logic in the Draper decorator or a view helper. A few lines of
inline ruby code may work better than moving this code to a separate class.
into several classes and using controller concerns. However, those may not
tackle the issue of passing too many instance variables into the view.