RailsCasts Pro episodes are now free!

Learn more or hide this

Recent Comments

Avatar

That is great news Ryan. Keep up the good work.
Best regards,
Bharat

Avatar

I think it isn't long enough :)

Avatar

Thanks for another great episode Ryan!

What would your thoughts on using decorators to show/hide certain information based on the current user's permissions (eg. with CanCan) - by doing something like:

ruby
if can? :read, model
  h.link_to model.email, "mailto:#{model.email}"
else
  content_tag :p, "Not listed", :class => "none"
end

within the decorator? A use case might be a member directory where non-members can only see members name but logged-in members can see each others email addresses.

Maybe it's a bit of an edge case? Do you think it would appropriate to put authorisation logic in a decorator?

Avatar

Wouldn't it be better that these comments would only be shown to the subscribers?

Avatar

You set this up on SOLR side by switching to the EdgeNGram filter

diff --git a/solr/conf/schema.xml b/solr/conf/schema.xml
index 6a83419..b4b62dd 100644
--- a/solr/conf/schema.xml
+++ b/solr/conf/schema.xml
@@ -59,13 +59,17 @@
     <!-- *** This fieldType is used by Sunspot! *** -->
     <fieldType name="rand" class="solr.RandomSortField" omitNorms="true"/>
     <!-- *** This fieldType is used by Sunspot! *** -->
-    <fieldType name="text" class="solr.TextField" omitNorms="false">
-      <analyzer>
-        <tokenizer class="solr.StandardTokenizerFactory"/>
-        <filter class="solr.StandardFilterFactory"/>
+    <fieldtype name="text" class="solr.TextField" omitNorms="false">
+      <analyzer type="index">
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
         <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.EdgeNGramFilterFactory" minGramSize="1" maxGramSize="50" side="front"/>
       </analyzer>
-    </fieldType>
+      <analyzer type="query">
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+      </analyzer>
+    </fieldtype>
Avatar

If anybody wants to use prawnto, it's become very out of date. I have tried to merge all the fragmented branches and update the code for rails 3.1. You can find the newest version with instructions and a wiki at https://github.com/forrest/prawnto.

Hope it helps!

Avatar

Are you going to include specs (and tests) in all the updates to old screencasts? That would be a huge incentive to me to subscribe.

(And thanks for not using only PayPal. Limiting payment to PayPal would be a big *dis*incentive to me.)

Avatar

Great cast! Can spork be modified to be used for rails console as well? It seems like preloading the environment would be useful in a variety of contexts...

Avatar

You also need to worry about HTML escaping when putting HTML directly in a string. The content_tag method handles that for us, unless we're doing something more with that string. "<span class='none'>None given</span>".html_safe just feels ugly.

Avatar

If you have more ERB tags than HTML tags, use a decorator. ;)

I also like to think about a partial being at an object scope (a _post partial) but a decorator method being at the attribute scope (a post.title method). It's finer bits of logic.

Avatar

This is one reason I prefer keeping presenters in the view layer as I discuss in episode 287. It helps for cases like pagination.

In this specific case you could create a UserDecorator#posts method which loops through the posts, fetches the decorator for each one, and then renders a post partial with the decorator.

Avatar

Cells feels a bit heavy for this case being like mini controllers and views. I just want an object that I can place some view logic into.

Avatar

Good point, there is some logic I can see going in the model here. Primarily the full_name vs username portion. Those are both attributes on the model and feels ugly in the presenter.

The only other methods which don't have an HTML tag in them are avatar_name and member_since. In these cases, I ask myself, is there a case I may want to embed markup into the logic? I can see treating the default avatar image special, perhaps giving it a CSS class to make it look different, not linking it, etc. Same with the member_since method. We may want to make the year italic for example.

That said, this may be a bit of a pre-refactoring and doesn't fallow YAGNI since that markup requirement doesn't exist. I can see that point.

As for having similar logic in xml/json responses. Those are also views and can have their own presenters with their own set of logic. If there is duplication in logic then it's a sign it should go in the model or maybe a shared module.

Avatar

I prefer the presenter be a bit verbose in the view so that it is obvious we are doing something special here beyond calling model attributes. Otherwise one might think calling user.twitter would just return the twitter username and not handle the link, etc.

That said, you could do something like smart partials where the object passed to the partial is a presenter instead of a simple object. But unless Rails adopts presenters as part of their stack, I prefer they be explicitly declared whenever used.

Avatar

Even though the topics will be more advanced, they will not necessarily be more detailed. I am unable to produce a half hour episode each week unfortunately, but I will consider revisiting specific areas of presenters in future pro episodes to address the scenarios you mentioned. Thanks for the feedback.

Avatar

It has important differences such as making presenters in the view instead of the controller, and passing the template object through. Overall I think the implementation is simpler and more flexible, but it's up to you to decide which you prefer.

Avatar

I should have the first revised episode up later this week. I also hope to be more active in the comments as RailsCasts becomes my full time job. Almost there.

Avatar

I would have to see the stack trace to find out where this to_presenter method is coming from. Perhaps there is another present method already defined that has different functionality.

Avatar

Sometimes the pro episode linking takes a few minutes after subscribing. If it doesn't start working let me know.

Avatar

Both types are the default search option. If you select a type you can cancel that by clicking the "x" in the Filter section about the episodes list.

Avatar

The setup method is only for Test Unit. If you're using RSpec you can do before(:each) instead.

Avatar

I would probably make my own generator for this. See episode 218 for details. This generator can wrap the scaffold generator if you want.

Avatar

Moving the sass-rails gem out of the :assets group as @autodidakto says below lets me run the db migrations, but I'm still having the rake assets:precompile issues on heroku cedar.

There is an open issue for that

Avatar

Caution: When using decorators, all your routes are accessibles when prefixed with the h method.

PressReleaseDecotator.rb
h.press_release_path(model)

But if you set default url options in your ApplicationController, they will not be apply. In my case, it's the locale setting...

ApplicationController.rb
def default_url_options(options={})
{:locale => I18n.locale}
end

Avatar

I'm curious: why the preference for h.content_tag, instead of a little snippet of HTML ?? Is there a reason, aside from aesthetics?

Avatar

+1 kudos for also showing Test::Unit and not only RSpec!

Avatar

Hey Ryan,

how about extracting this into its own gem! ;)

Avatar

There's quite a bit of discussion of sass in the GitHub issues.

I'm not yet running 3.1, but I got around a similar sass problem in 3.0 by generating the assets locally, then putting this in an initializer:
Sass::Plugin.options[:never_update] = true

Avatar

Nifty stuff! I agree that the controller shouldn't need to know about what is basically a super-helper for the view. I've been trying to think if there's any way to pull the "present" helper method out so that it's seamless to the view as well that it's using a presenter.

Possibly a super smart layout? Any ideas?

Avatar

Ryan, I am a big fan of your Railscasts and I congratulate on Railscasts Pro. However, for a "pro" edition, I found the cast very short and basic. In the cast you assumed that the view only gets one value set (presents). What if the view needs more information, e.g. the controller did that:

def index
@customers = Customers.all
@prices = Prices.all
...
end

How would you do that?

And the second thing is that you just scratched the testing issue. I believe that Presenters shine most in testing.

Maybe I am setting the expectations too high, but I expected a much more detailed (paid) cast.

My 2 cents, good luck with Railscast Pro.

Avatar

ActionView::TestCase::Behavior does not exists in Rails 2.3.x. Does anyone know how to test the presenter within a Rails 2.3.14 app and RSpec 1.3.2?

Avatar

It's a new syntax introduced in Ruby 1.9 for hashes that use symbols as keys.

{class: "none"} is equivalent to {:class => "none"}

Avatar

So this screencast is just creating draper library...? :)

Avatar

Did someone get this work under rails 3.1?

Avatar

Ryan,

Great episode as always. I had a question about this line:

h.content_tag :span, "None given", class: "none"

What is the difference between :span (which I know is a symbol) and class:? Why is the colon after class?

Avatar

I think it's doesn't matter - in 1.9.2 you can write both {:a => 1} and {a: 1}

Avatar

This is the best of both worlds. Free RailsCasts to get you going and Pro to push you further. I am waiting for your previous screen-casts to be brought up to date. There is a wealth of knowledge there already.

Keep them coming and congratulations on expanding on what you are already very good at. Can we expect you to respond more actively on the "Pro" screen-cast questions though? Since these are more in-depth, naturally there are more questions.
Bharat

Avatar

By the way, there's a small typo in the user_presenter.rb file:

delegate :username, to: :user

should be

delegate :username, :to => :user
Avatar

Great tutorial, as always! Quick question however. How can we use this technique for local objects inside partials?

We have a partial with a local variable called user (not @user). when trying to apply the same logic to it, i get back the following error 500:

ruby
ActionView::Template::Error (undefined method `to_presenter' for #<User:0x10b80f0a0>):
    1: - present user do |user_presenter|
    2:   #contact_information
    3:     %dl
    4:       %dt Name
Avatar

Am I the only one who can't access video?

Avatar

The requested URL /assets/subscriptions/qgznl20zqplReMjco1UASA/videos/287-presenters-from-scratch.mp4 was not found on this server

Avatar

Thanks Ryan (and Jeff!)

Just a small question: when refactoring a view, how to choose between using a partial, or using a decorator?

Avatar

Is it me or is there any issues with the audio?