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

cannot access ApplicationHelper #523

Closed
abacha opened this issue Apr 15, 2013 · 19 comments
Closed

cannot access ApplicationHelper #523

abacha opened this issue Apr 15, 2013 · 19 comments
Labels

Comments

@abacha
Copy link

abacha commented Apr 15, 2013

I've recently updated Draper to 1.2.0 but now I can't access ApplicationHelper methods

rails 3.2.11
draper 1.2.0

module ApplicationHelper
  def initials(text)
     [...]
  end
end
class AccountDecorator < ApplicationDecorator
    def officers_initials
        h.initials(officer.name)
    end
end
NoMethodError: undefined method `initials' for #<#<Class:0x000000081107f8>:0x00000009404620>
from /home/duke/.rvm/gems/ruby-1.9.3-p392@bigbang/gems/draper-1.2.0/lib/draper/helper_proxy.rb:29:in `block in define_proxy'
@carloslopes
Copy link
Contributor

@abacha, i tried to reproduce your case but i didn't have success. Here are all the codes that i used:

Gemfile:

gem 'rails', '3.2.11'
gem 'draper', '1.2.0'

Resource:

class User < ActiveRecord::Base
  attr_accessible :age, :name
end

Application helper:

module ApplicationHelper
  def foo(bar)
    bar
  end
end

Decorator:

class UserDecorator < Draper::Decorator
  def test
    h.foo('hello')
  end
end

Console test:

irb(main):001:0> u = User.new.decorate
=> #<UserDecorator:0x007fad4860e5d0 @source=#<User id: nil, name: nil, age: nil, created_at: nil, updated_at: nil>, @context={}>
irb(main):002:0> u.test
=> "hello"

Maybe you are doing something else in your code that is affecting this behavior, could you show more information about your method or configs?

@abacha
Copy link
Author

abacha commented May 18, 2013

I don't know what it was, I cleaned up my changes and reapplied it and now it works
thanks anyway

@abacha abacha closed this as completed May 18, 2013
@carloslopes
Copy link
Contributor

nice! 😉

@gaizka
Copy link
Contributor

gaizka commented May 28, 2013

Hi there!

This is happening to me now and then at our jenkins build, and on production, too.

Now I am able to reproduce it always (by replaying our specs with the same --seed), so I'll try to get more information.

Basically, in the sometimes failing spec:

def some_method
  h.my_object_path(object)  # works
  h.my_method_defined_in_application_helpers # does not work

I am using version 1.2.1 of Draper, with default :full strategy.

It's really weird,

$ rspec spec/decorators/

always works, but

$ rspec spec/

fails sometimes at decorators' specs!!

I'll get in touch again when i have more info...

@steveklabnik
Copy link
Member

Strange. Yes, please let me know; if you get a way to reproduce, i'll re-open.

@gaizka
Copy link
Contributor

gaizka commented May 28, 2013

Ok, I think I've found something:

Here at helper_proxy.rb

def method_missing(method, *args, &block)
  self.class.define_proxy method
  send(method, *args, &block)
end

When it fails, view_context is something like:

#<#<Class:0x0000000d0ab1c8>:0x0000000f6b6368 @_config={},
bla bla bla
@_controller=#<AdminMailer:0x0000000f596000 

When it works, it's something like:

 #<#<Class:0x000000046859a8>:0x00000002d43030 @_config={},
 bla bla bla
 @_controller=#<ApplicationController:0x00000002383ae0

So it looks that somehow draper swaps the correct controller and uses my AdminMailer there.

Since AdminMailer has access to my_object_path|url methods, but since it does not include ApplicationHelper, it works as long as the decorator don't try to access any ApplicationHelper methods...

I'll now try to find out why this is happening...

@gaizka
Copy link
Contributor

gaizka commented May 28, 2013

Ok, I think I know what's happening:

I see that it's normal sometimes that view_context has as a controller a Mailer instead of one ApplicationController.

The thing is, Draper::ViewContext is not being reset sometimes.

In rspec_integration.rb we have:

RSpec.configure do |config|
  config.include DecoratorExampleGroup, example_group: {file_path: %r{spec/decorators}}, type: :decorator

  [:decorator, :controller, :mailer].each do |type|
    config.after(:each, type: type) { Draper::ViewContext.clear! }
  end
end

Now, in some of my models specs, an email is sent. Then, Draper::ViewContext.current is set to my mailer's view_context.

After this model spec, one decorator spec comes and Draper::ViewContext.current is pointing to that mailer's view_context because it has not been reset.

This fixes it:

  [:model, :decorator, :controller, :mailer].each do |type|
    config.after(:each, type: type) { Draper::ViewContext.clear! }
  end

but I don't know if this is OK, or something else should be tried.

Probably that is what's happening also to us in production. But there this would not do anything, and i don't know that the right fix should be.

Any thoughts?

Btw, thanks a lot for Draper, it's great to be able to get rid of untested/hard to use helpers :)

@steveklabnik
Copy link
Member

@carloslopes
Copy link
Contributor

Maybe this way would be better:

[:decorator, :controller, :mailer].each do |type|
  config.before(:each, type: type) { Draper::ViewContext.clear! }
end

@steveklabnik
Copy link
Member

How is that different? My eyes can't see the diff.

Actually, how about this: submit it as a PR. :) That way we can make sure it's working for you.

@carloslopes
Copy link
Contributor

Is different because it will not call Draper::ViewContext.clear! after each model spec, only before the specs that really need the context to be clear (the decorator, controller and mailer)

@gaizka
Copy link
Contributor

gaizka commented May 28, 2013

Done!

#547

It passes the tests.

stevenharman pushed a commit to stevenharman/draper that referenced this issue Jan 8, 2014
By doing it before, we get sure that another type of spec (model,
acceptance) does not leave ViewContext in a wrong context...

drapergem#523
@davekapp
Copy link

davekapp commented Jul 9, 2014

This is randomly and mysteriously happening to me on a project now as well. I'm also getting the ViewContext to be set to a mailer somehow (although running rspec spec/decorators/blah and rspec spec does not cause any difference). Calling Draper::ViewContext.clear! from within pry right before the problematic line makes it work. Adding a before(:each) do / Draper::ViewContext.clear! / end does not fix the problem though.

Any suggestions? I'm pretty lost at this point. :(

@leonelgalan
Copy link

I have this issue on a recent project (draper 1.4.0, rspec 3.1.0).

class Post < ActiveRecord::Base
  belongs_to :user
  # ...
end
class PostDecorator < Draper::Decorator
  delegate_all

  def post_test
    helpers.application_test
  end
end
module ApplicationHelper
  def application_test
    1
  end
end
FactoryGirl.define do
  factory :post do
    association :user
    # ...
  end
end
# ...
RSpec.describe PostDecorator, type: :decorator do
  subject(:post) { build(:post).decorate }
  it 'calls application helper' do
    expect(post.post_test).to eq 1
  end
  # ...
end

User is set with a after_create that will send an email. I didn't needed a user for my PostDecorator, so stubbing the user, preventing the emails from happening, fixes the issue. Note: I'm using FactoryGirl, so even when i'm building :post, associations are created.

  subject(:post) { build(:post, user: build_stubbed(:user)).decorate }

Make the tests pass.

EDIT: Running this single test also failed, so I never suspected about the config.before(:each, type: type) { Draper::ViewContext.clear! }

@carloslopes
Copy link
Contributor

@leonelgalan probably the email is causing some issue that prevents the post to be created correctly.

Can you give us more details about the failed test? How is the state of the post and the decorator object before the expectation happens?

@leonelgalan
Copy link

Hello Carlos, the post is created successfuly, valid with no errors and the error is the following:

undefined method 'application_test' for #<#<Class:0x007f8e553b1380>:0x007f8e53f2d9e0>

Adding

Draper::ViewContext.current.class_eval do
  include ApplicationHelper # workaround for draper test bug
end

to the ApplicationHelper method makes the test pass.

@kreintjes
Copy link

I had the same problem. In a project I work on, we are using a decorator within a model method (don't ask me why). The problem occurs when certain mailer specs (which do not use ApplicationHelper) are run before this model's spec. Adding Draper::ViewContext.clear! to the failing model spec fixes the problem.

@gaizka solution in #547 does not fix this problem, since the view context is set to the mailers view context without application helper, while it should be reset before the model spec (or after the mailer spec). Maybe we should run Draper::ViewContext.clear! both before and after each test? Or simply run Draper::ViewContext.clear! before each type of test?

@zinkkrysty
Copy link

zinkkrysty commented Jul 11, 2017

It is still not clear how to deal with this problem. We are facing the issue that once the mailer specs run, the controller specs are failing precisely because of this. Draper should be automatically configured so that it clears any cached variables after each spec. If someone wants to optimize the speed of the specs by enabling that, they should have the option, but at their own risk!

I am trying to make it work by adding this to my spec_helper.rb:

  [:decorator, :controller, :mailer].each do |type|
    config.before(:each, type: type) { Draper::ViewContext.clear! }
  end

But it still does not work

EDIT adding the after hook made it work now:

  [:decorator, :controller, :mailer].each do |type|
    config.before(:each, type: type) { Draper::ViewContext.clear! }
    config.after(:each, type: type) { Draper::ViewContext.clear! }
  end

@stopanko
Copy link

stopanko commented Jan 15, 2019

It is still not clear how to deal with this problem. We are facing the issue that once the mailer specs run, the controller specs are failing precisely because of this. Draper should be automatically configured so that it clears any cached variables after each spec. If someone wants to optimize the speed of the specs by enabling that, they should have the option, but at their own risk!

I am trying to make it work by adding this to my spec_helper.rb:

  [:decorator, :controller, :mailer].each do |type|
    config.before(:each, type: type) { Draper::ViewContext.clear! }
  end

But it still does not work

EDIT adding the after hook made it work now:

  [:decorator, :controller, :mailer].each do |type|
    config.before(:each, type: type) { Draper::ViewContext.clear! }
    config.after(:each, type: type) { Draper::ViewContext.clear! }
  end

I just added Draper::ViewContext.clear! to the :feature test settings
config.before type: :feature do ...
and
config.after type: :feature do ...

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