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

Extract helper proxy #310

Merged
merged 1 commit into from Oct 15, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/draper.rb
Expand Up @@ -4,6 +4,7 @@
require 'draper/system'
require 'draper/active_model_support'
require 'draper/decorator'
require 'draper/helper_proxy'
require 'draper/lazy_helpers'
require 'draper/decoratable'
require 'draper/security'
Expand Down
34 changes: 8 additions & 26 deletions lib/draper/decorator.rb
Expand Up @@ -162,48 +162,30 @@ def self.last(options = {})
decorate(model_class.last, options)
end

# Some helpers are private, for example html_escape... as a workaround
# we are wrapping the helpers in a delegator that passes the methods
# along through a send, which will ignore private/public distinctions
class HelpersWrapper
def initialize(helpers)
@helpers = helpers
end

def method_missing(method, *args, &block)
@helpers.send(method, *args, &block)
end

#needed for tests
def ==(other)
other.instance_variable_get(:@helpers) == @helpers
end
end

# Access the helpers proxy to call built-in and user-defined
# Rails helpers. Aliased to `.h` for convenience.
# Rails helpers. Aliased to `h` for convenience.
#
# @return [Object] proxy
# @return [HelperProxy] the helpers proxy
def helpers
@helpers ||= HelpersWrapper.new self.class.helpers
self.class.helpers
end
alias :h :helpers

# Localize is something that's used quite often. Even though
# it's available through helpers, that's annoying. Aliased
# to `.l` for convenience.
def localize(object, options = {})
self.class.helpers.localize(object, options)
# to `l` for convenience.
def localize(*args)
helpers.localize(*args)
end
alias :l :localize

# Access the helpers proxy to call built-in and user-defined
# Rails helpers from a class context.
#
# @return [Object] proxy
# @return [HelperProxy] the helpers proxy
class << self
def helpers
Draper::ViewContext.current
@helpers ||= HelperProxy.new
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure that helpers method should be memoized. @steveklabnik, there was an issue with it, wasn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, as of e2a757d we are memoizing the view context. This, on the other hand, only memoizes the HelperProxy object, and HelperProxy#view_context delegates to Draper::ViewContext.current, so I think this is safer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. I'm really just not sure what all this effects. We really need some real-world integration tests :/

end
alias :h :helpers
end
Expand Down
16 changes: 16 additions & 0 deletions lib/draper/helper_proxy.rb
@@ -0,0 +1,16 @@
module Draper
class HelperProxy
# Some helpers are private, for example html_escape... as a workaround
# we are wrapping the helpers in a delegator that passes the methods
# along through a send, which will ignore private/public distinctions
def method_missing(method, *args, &block)
view_context.send(method, *args, &block)
end

private

def view_context
Draper::ViewContext.current
end
end
end
39 changes: 22 additions & 17 deletions spec/draper/decorator_spec.rb
Expand Up @@ -20,13 +20,13 @@
end
end

context(".helpers") do
it "have a valid view_context" do
subject.helpers.should be
describe "#helpers" do
it "returns a HelperProxy" do
subject.helpers.should be_a Draper::HelperProxy
end

it "is aliased to .h" do
subject.h.should == subject.helpers
it "is aliased to #h" do
subject.h.should be subject.helpers
end

it "initializes the wrapper only once" do
Expand All @@ -37,13 +37,25 @@
end
end

context("#helpers") do
it "have a valid view_context" do
Decorator.helpers.should be
describe "#localize" do
before { subject.helpers.should_receive(:localize).with(:an_object, {some: "options"}) }

it "delegates to helpers" do
subject.localize(:an_object, some: "options")
end

it "is aliased to #h" do
Decorator.h.should == Decorator.helpers
it "is aliased to #l" do
subject.l(:an_object, some: "options")
end
end

describe ".helpers" do
it "returns a HelperProxy" do
Decorator.helpers.should be_a Draper::HelperProxy
end

it "is aliased to .h" do
Decorator.h.should be Decorator.helpers
end
end

Expand Down Expand Up @@ -673,13 +685,6 @@ module Paginator; def page_number; "magic_value"; end; end
decorator.sample_truncate.should == "Once..."
end

it "is able to use l rather than helpers.l" do
now = Time.now
helper_proxy = decorator.helpers.instance_variable_get(:@helpers)
helper_proxy.should_receive(:localize).with(now, :format => :long)
decorator.l now, :format => :long
end

it "is able to access html_escape, a private method" do
decorator.sample_html_escaped_text.should == '&lt;script&gt;danger&lt;/script&gt;'
end
Expand Down
12 changes: 12 additions & 0 deletions spec/draper/helper_proxy_spec.rb
@@ -0,0 +1,12 @@
require 'spec_helper'

describe Draper::HelperProxy do
subject(:helper_proxy) { Draper::HelperProxy.new }
let(:view_context) { Object.new }
before { helper_proxy.stub(:view_context).and_return(view_context) }

it "proxies methods to the view context" do
view_context.should_receive(:foo).with("bar")
helper_proxy.foo("bar")
end
end