Skip to content
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

Usage of view_context leads to weird errors… #124

Closed
ream88 opened this issue Feb 6, 2012 · 41 comments
Closed

Usage of view_context leads to weird errors… #124

ream88 opened this issue Feb 6, 2012 · 41 comments
Labels

Comments

@ream88
Copy link
Contributor

ream88 commented Feb 6, 2012

…, for example using content_for both in a Decorator class and directly in an ERB template won't work at all, cause Draper's instance of view_context isn't the same as ERB's. Is there any way to use the same view_context instance as ERB?

@steveklabnik
Copy link
Member

I'm not sure how to re-use ERB's view-context.

@cgriego
Copy link

cgriego commented Apr 2, 2012

This also causes the capture helper to not work, which means you can't write a decorator method that takes a block from your ERB code.

@tessro
Copy link

tessro commented Apr 3, 2012

@steveklabnik I was inspired to glance at this based on your Tweets, and I found this semi-horrible related approach to using content_for in controllers: https://gist.github.com/985457

I'm not sure if this type of approach is at all sane. Probably comes down to how much you trust the safety of the "Override this method in a module to change the default behavior" comment in the Rails source. ;-)

@steveklabnik
Copy link
Member

Ha! Yeah. Hrm. Thanks though.

@rafaelss
Copy link

I'm not using draper, but I'm using a homemade solution that calls view_context as well and I'm having the same issue, the only solution I found til now is this: https://gist.github.com/2562237
I don't like it at all, though.

@rafaelss
Copy link

hmm. it seems an initializer like that:

AbstractController::Rendering.class_eval do
  def view_context
    @view_context ||= view_context_class.new(view_renderer, view_assigns, self)
  end
end

fix the issue too. perhaps rails should be caching the view context instance in the Rendering module.

@steveklabnik
Copy link
Member

Yeah that'd be too hardcore of a monkey patch. I've been talking to @wycats about this a bit, but I really need to dig into what exactly Draper needs before we can determine what the real right thing to do is.

@joevandyk
Copy link
Contributor

@rafaelss Someone asked that a little bit ago, this was the answer: rails/rails#4906

@steveklabnik
Copy link
Member

@joevandyk that is a fantastic find, thank you!

@joevandyk
Copy link
Contributor

For what it's worth, the Cells project is running into this issue as well.

@joevandyk
Copy link
Contributor

If we could reset Draper's view_context at the time the first view is rendered (instead of in the controller), I think that would resolve some of the problems.

@ream88
Copy link
Contributor Author

ream88 commented May 4, 2012

@steveklabnik @rafaelss funny fact, it was actually me who asked the question both here and in the rails repo! 😃

@linuxonrails
Copy link

I have the same problem:

class PedidoMailer < ActionMailer::Base
  helper :application

  default 'init-draper' => Proc.new { set_current_view_context } # <--- when i add this line

I get:

ruby-1.9.2-p290 :020 > PedidoMailer.notificar_pedido_al_comprador(u, nil, nil).deliver
NoMethodError: undefined method `index' for #<#<Class:0x00000005ac43b0>:0x00000005abde98>
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/mail-2.4.4/lib/mail/encodings.rb:117:in `value_decode'
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/mail-2.4.4/lib/mail/encodings.rb:101:in `decode_encode'
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/mail-2.4.4/lib/mail/fields/unstructured_field.rb:64:in `do_decode'
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/mail-2.4.4/lib/mail/fields/unstructured_field.rb:46:in `decoded'
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/mail-2.4.4/lib/mail/fields/unstructured_field.rb:115:in `fold'
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/mail-2.4.4/lib/mail/fields/unstructured_field.rb:96:in `wrapped_value'
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/mail-2.4.4/lib/mail/fields/unstructured_field.rb:60:in `do_encode'
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/mail-2.4.4/lib/mail/fields/unstructured_field.rb:42:in `encoded'
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/mail-2.4.4/lib/mail/field.rb:133:in `method_missing'
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/mail-2.4.4/lib/mail/header.rb:190:in `block in encoded'
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/mail-2.4.4/lib/mail/header.rb:189:in `each'
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/mail-2.4.4/lib/mail/header.rb:189:in `encoded'
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/mail-2.4.4/lib/mail/message.rb:1708:in `encoded'
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/actionmailer-3.2.3/lib/action_mailer/base.rb:433:in `set_payload_for_mail'
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/actionmailer-3.2.3/lib/action_mailer/base.rb:413:in `block in deliver_mail'
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/activesupport-3.2.3/lib/active_support/notifications.rb:123:in `block in instrument'
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/activesupport-3.2.3/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/activesupport-3.2.3/lib/active_support/notifications.rb:123:in `instrument'
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/actionmailer-3.2.3/lib/action_mailer/base.rb:412:in `deliver_mail'
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/mail-2.4.4/lib/mail/message.rb:229:in `deliver'
    from (irb):20
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/railties-3.2.3/lib/rails/commands/console.rb:47:in `start'
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/railties-3.2.3/lib/rails/commands/console.rb:8:in `start'
    from /home/davidm/Documentos/Devel/zoconet/git/mobile/vendor/bundler/ruby/1.9.1/gems/railties-3.2.3/lib/rails/commands.rb:41:in `<top (required)>'

I am using HAML in mailer's views:

$ cat notificar_pedido_al_comprador.html.haml
DEBUG

@linuxonrails
Copy link

The rafaelss's fix doesn't work to me :-(

@ream88
Copy link
Contributor Author

ream88 commented Jun 19, 2012

Any update on this?

@steveklabnik
Copy link
Member

Not really. It's going to require some sort of structural change in Rails to work 100% of the time, I think.

@joevandyk
Copy link
Contributor

Maybe now's the time to do it, before 4.0 comes out?

I wonder if threads are related to the problems people are having with this?

@steveklabnik
Copy link
Member

Yes, I've been having discussions with some rails-core people.

It's not the threads. It's that we get a different copy of the view context than the one the actual renderer uses, so modifications of the context in the view renderer don't get copied over to the one that draper has a copy of.

@rf-
Copy link
Contributor

rf- commented Jul 13, 2012

There's a simple workaround for this:

class ApplicationController
  skip_before_filter :set_current_view_context

  def view_context
    super.tap do |context|
      Draper::ViewContext.current = context
    end
  end
end

Clearly Draper can't patch ApplicationController, and I can see how you wouldn't want to patch ActionController::Base either, so I'm not sure what the best approach would be for Draper itself. Maybe provide a mixin that the user can put in their ApplicationController?

@steveklabnik
Copy link
Member

Right, the ultimate idea is that we just play nice and don't monkey-patch in general. That may not be an immediate option. It also might be better to do a monkeypatch like this now, and then get to a 'real' solution later.

@ream88
Copy link
Contributor Author

ream88 commented Jul 14, 2012

@rf- Really nice solution (for now). Never thought about the other way round!

@rf-
Copy link
Contributor

rf- commented Jul 15, 2012

@steveklabnik The workaround that I pasted isn't monkey-patching, just inheritance. That's what I meant about providing a mixin—if users had to explicitly mix Draper support into their app's base controller, it would be easy to grab a reference to the final view context without patching anything.

That would be kind of a regression in Draper's UI, though, so maybe monkey-patching is a better temporary solution.

@wycats
Copy link

wycats commented Jul 15, 2012

Is Draper::ViewContext.current a thread-local variable?

@rf-
Copy link
Contributor

rf- commented Jul 15, 2012

@steveklabnik
Copy link
Member

I was trying to reproduce this particular issue to attempt @rf-'s patch, but I couldn't actually reproduce it:

https://gist.github.com/84e45c5ca17cd4a75f9c

Are you doing something else, @halhappen?

@rf-
Copy link
Contributor

rf- commented Jul 16, 2012

I'm kind of surprised that example doesn't break for you, but you should at least be able to reproduce it by moving the content_for into a partial and using h.render inside the decorator.

@steveklabnik
Copy link
Member

I was surprised too. Let me try that.

@rf-
Copy link
Contributor

rf- commented Jul 16, 2012

Actually, I was just able to reproduce it using your example. What version of Rails are you using?

@steveklabnik
Copy link
Member

Yep, that makes it bail. Awesome.

@steveklabnik
Copy link
Member

Rails 3.2.2... apparently it's a little outdated. I have a draper_test rails app that I use to try to reproduce issues, so I generated it a bit ago.

@steveklabnik
Copy link
Member

The biggest problem with the monkeypatch is that it breaks all the tests ;) That's our bad, though... I'm working on getting it done now. We were using set_view_context to prime things in the tests.

@joshuaclayton
Copy link

@steveklabnik just bumped into this today, trying this:

class TodoDecorator < Draper::Base
  decorates :todo

  def list_item(&block)
    h.content_tag(:li, h.capture(&block), class: complete_state, id: h.dom_id(self))
  end

  private

  def complete_state
    if completed_at
      'complete'
    else
      ''
    end
  end
end

the alternative implementation using a helper

  def list_item(todo, &block)
    complete_state = todo.completed_at? ? 'complete' : ''
    content_tag(:li, capture(&block), class: complete_state, id: dom_id(todo))
  end

obviously works fine.

Really interested in seeing if this can get fixed, since it'd be really awesome to do this sort of thing.

steveklabnik added a commit that referenced this issue Jul 17, 2012
We need to be able to let draper get a copy of the view_context
no matter what happens. AC::Base doesn't really let us do that. So
we have to monkeypatch.

This was originally suggested in
https://github.com/jcasimir/draper/issues/124#issuecomment-6954291
by @rf-.

It appears to be the cleanest way to take care of this problem, and
shouldn't affect any other things, since we keep the exact same
semantics of view_context.

Theoretically:

Fixes #124
Fixes #109
@steveklabnik
Copy link
Member

Okay. Patch applied. Let's see how this goes. Please check if this works, everyone!

@rf-
Copy link
Contributor

rf- commented Jul 17, 2012

Cool, thanks!

A few small notes:

  • You applied the patch with ApplicationController.send(:include, DraperViewContext). Was this meant to be ActionController::Base.send?
  • It seems like it might make sense to continue applying the patch inside Draper::System.setup, to make sure that any class that has setup called on it is fully Draper-enabled. It doesn't look like ActionMailer is getting patched anymore with the current code.
  • There are still a few references to set_current_view_context, including in the console block of the Railtie.

@steveklabnik
Copy link
Member

It actually doesn't work with ActionController::Base. Or at least, I couldn't get it to. Oh! Oh! I think I know why: in the tests, we're making a mock ActionController::Base. And defining view_context there. Buuuut in real ActionController, it's actually defined in one of the AbstractControllers, IIRC, so... yes. Tests need to be updated to make that happen, but totally.

  1. good catch

  2. argh, I suck at ack.

@steveklabnik
Copy link
Member

Fixed #1 in 19ecb66

@steveklabnik
Copy link
Member

Fixed #3 with 1d6491b

@rf-
Copy link
Contributor

rf- commented Jul 19, 2012

@steveklabnik I'd be happy to come up with a pull request that resolves the other issue and re-enables ActionMailer support, if you don't have anything in mind already. Should I?

@steveklabnik
Copy link
Member

Feel free! I'm at OSCON, so I won't get the chance to do any work until tomorrow. I'll lay off and let you at it!

@rf-
Copy link
Contributor

rf- commented Jul 19, 2012

OK, just opened #241 with a fix for ActionMailer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

10 participants