draper 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/Guardfile CHANGED
@@ -1,5 +1,5 @@
1
1
  guard 'rspec', :version => 2, :notification => false do
2
2
  watch(%r{^spec/.+_spec\.rb$})
3
- watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
4
  watch('spec/spec_helper.rb') { "spec" }
5
5
  end
data/Readme.markdown CHANGED
@@ -198,7 +198,7 @@ Hate seeing that `h.` proxy all over? Willing to mix a bazillion methods into yo
198
198
  ```ruby
199
199
  class ArticleDecorator < ApplicationDecorator
200
200
  decorates :article
201
- lazy_helpers
201
+ include Draper::LazyHelpers
202
202
 
203
203
  def published_at
204
204
  date = content_tag(:span, model.published_at.strftime("%A, %B %e").squeeze(" "), :class => 'date')
data/lib/draper/base.rb CHANGED
@@ -5,7 +5,7 @@ module Draper
5
5
  attr_accessor :context, :model
6
6
 
7
7
  DEFAULT_DENIED = Object.new.methods << :method_missing
8
- FORCED_PROXY = [:to_param]
8
+ FORCED_PROXY = [:to_param, :id]
9
9
  FORCED_PROXY.each do |method|
10
10
  define_method method do |*args, &block|
11
11
  model.send method, *args, &block
@@ -93,7 +93,7 @@ module Draper
93
93
  # @param [Object] instance(s) to wrap
94
94
  # @param [Object] context (optional)
95
95
  def self.decorate(input, context = {})
96
- input.respond_to?(:each) ? input.map{|i| new(i, context)} : new(input, context)
96
+ input.respond_to?(:each) ? DecoratedEnumerableProxy.new(input, self, context) : new(input, context)
97
97
  end
98
98
 
99
99
  # Access the helpers proxy to call built-in and user-defined
@@ -105,16 +105,6 @@ module Draper
105
105
  end
106
106
  alias :h :helpers
107
107
 
108
- # Calling `lazy_helpers` will make the built-in and
109
- # user-defined Rails helpers accessible as class methods
110
- # in the decorator without using the `h.` or `helpers.` proxy.
111
- #
112
- # The drawback is that you dump many methods into your decorator's
113
- # namespace and collisions could create unexpected results.
114
- def self.lazy_helpers
115
- self.send(:include, Draper::LazyHelpers)
116
- end
117
-
118
108
  # Fetch the original wrapped model.
119
109
  #
120
110
  # @return [Object] original_model
@@ -130,16 +120,16 @@ module Draper
130
120
  end
131
121
 
132
122
  def respond_to?(method, include_private = false)
133
- if select_methods.include?(method)
134
- model.respond_to?(method)
135
- else
136
- super
137
- end
123
+ super || (allow?(method) && model.respond_to?(method))
138
124
  end
139
125
 
140
126
  def method_missing(method, *args, &block)
141
- if select_methods.include?(method)
142
- model.send(method, *args, &block)
127
+ if allow?(method)
128
+ begin
129
+ model.send(method, *args, &block)
130
+ rescue NoMethodError
131
+ super
132
+ end
143
133
  else
144
134
  super
145
135
  end
@@ -154,9 +144,35 @@ module Draper
154
144
  end
155
145
 
156
146
  private
157
- def select_methods
158
- specified = self.allowed || (model.public_methods.map{|s| s.to_sym} - denied.map{|s| s.to_sym})
159
- (specified - self.public_methods.map{|s| s.to_sym}) + FORCED_PROXY
147
+ def allow?(method)
148
+ (!allowed? || allowed.include?(method) || FORCED_PROXY.include?(method)) && !denied.include?(method)
160
149
  end
150
+
151
+ class DecoratedEnumerableProxy
152
+ include Enumerable
153
+
154
+ def initialize(collection, klass, context)
155
+ @wrapped_collection, @klass, @context = collection, klass, context
156
+ end
157
+
158
+ # Implementation of Enumerable#each that proxyes to the wrapped collection
159
+ def each(&block)
160
+ @wrapped_collection.each { |member| block.call(@klass.new(member, @context)) }
161
+ end
162
+
163
+ # Implement to_arry so that render @decorated_collection is happy
164
+ def to_ary
165
+ @wrapped_collection.to_ary
166
+ end
167
+
168
+ def method_missing (meth, *args, &block)
169
+ @wrapped_collection.send(meth, *args, &block)
170
+ end
171
+
172
+ def to_s
173
+ "#<DecoratedEnumerableProxy of #{@klass} for #{@wrapped_collection.inspect}>"
174
+ end
175
+ end
176
+
161
177
  end
162
178
  end
@@ -1,3 +1,3 @@
1
1
  module Draper
2
- VERSION = "0.8.1"
2
+ VERSION = "0.9.0"
3
3
  end
@@ -1,7 +1,7 @@
1
1
  module Draper
2
2
  module ViewContext
3
3
  def set_current_view_context
4
- Thread.current[:current_view_context] ||= self.view_context
4
+ Thread.current[:current_view_context] = self.view_context
5
5
  end
6
6
 
7
7
  def self.included(source)
@@ -13,5 +13,7 @@ module Draper
13
13
  end
14
14
  template 'decorator.rb', "#{DECORATORS_ROOT}#{singular_name}_decorator.rb"
15
15
  end
16
+
17
+ hook_for :test_framework
16
18
  end
17
- end
19
+ end
@@ -0,0 +1,17 @@
1
+ module Rspec
2
+ class DecoratorGenerator < ::Rails::Generators::NamedBase
3
+ source_root File.expand_path('../templates', __FILE__)
4
+
5
+ SPEC_ROOT = 'spec/decorators/'
6
+ APPLICATION_DECORATOR_SPEC = 'application_decorator_spec.rb'
7
+ APPLICATION_DECORATOR_SPEC_PATH = SPEC_ROOT + APPLICATION_DECORATOR_SPEC
8
+
9
+ def build_model_and_application_decorator_specs
10
+ empty_directory SPEC_ROOT
11
+ unless File.exists?(APPLICATION_DECORATOR_SPEC_PATH)
12
+ template APPLICATION_DECORATOR_SPEC, APPLICATION_DECORATOR_SPEC_PATH
13
+ end
14
+ template 'decorator_spec.rb', "#{SPEC_ROOT}#{singular_name}_decorator_spec.rb"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe ApplicationDecorator do
4
+ before { ApplicationController.new.set_current_view_context }
5
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe <%= singular_name.camelize %>Decorator do
4
+ before { ApplicationController.new.set_current_view_context }
5
+ end
data/spec/base_spec.rb CHANGED
@@ -26,13 +26,6 @@ describe Draper::Base do
26
26
  end
27
27
  end
28
28
 
29
- context(".lazy_helpers") do
30
- it "makes Rails helpers available without using the h. proxy" do
31
- Draper::Base.lazy_helpers
32
- subject.send(:pluralize, 5, "cat").should == "5 cats"
33
- end
34
- end
35
-
36
29
  context(".decorates") do
37
30
  it "sets the model class for the decorator" do
38
31
  ProductDecorator.new(source).model_class.should == Product
@@ -74,6 +67,11 @@ describe Draper::Base do
74
67
  Draper::Base.new(source).to_param.should == 1
75
68
  end
76
69
 
70
+ it "should always proxy id" do
71
+ source.send :class_eval, "def id; 123456789; end"
72
+ Draper::Base.new(source).id.should == 123456789
73
+ end
74
+
77
75
  it "should not copy the .class, .inspect, or other existing methods" do
78
76
  source.class.should_not == subject.class
79
77
  source.inspect.should_not == subject.inspect
@@ -150,6 +148,7 @@ describe Draper::Base do
150
148
  its(:context) { should eq(context) }
151
149
  end
152
150
  end
151
+
153
152
  end
154
153
 
155
154
  context('.==') do
@@ -159,6 +158,32 @@ describe Draper::Base do
159
158
  end
160
159
  end
161
160
 
161
+ describe "collection decoration" do
162
+
163
+ # Implementation of #decorate that returns an array
164
+ # of decorated objects is insufficient to deal with
165
+ # situations where the original collection has been
166
+ # expanded with the use of modules (as often the case
167
+ # with paginator gems) or is just more complex then
168
+ # an array.
169
+ module Paginator; def page_number; "magic_value"; end; end
170
+ Array.send(:include, Paginator)
171
+ let(:paged_array) { [Product.new, Product.new] }
172
+ subject { ProductDecorator.decorate(paged_array) }
173
+
174
+ it "should proxy all calls to decorated collection" do
175
+ paged_array.page_number.should == "magic_value"
176
+ subject.page_number.should == "magic_value"
177
+ end
178
+
179
+ it "should support Rails partial lookup for a collection" do
180
+ # to support Rails render @collection the returned collection
181
+ # (or its proxy) should implement #to_ary.
182
+ subject.respond_to?(:to_ary).should be true
183
+ subject.to_a.first.should == ProductDecorator.decorate(paged_array.first)
184
+ end
185
+ end
186
+
162
187
  describe "a sample usage with denies" do
163
188
  let(:subject_with_denies){ DecoratorWithDenies.new(source) }
164
189
 
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ # Generators are not automatically loaded by Rails
4
+ require 'generators/rspec/decorator_generator'
5
+
6
+ describe Rspec::DecoratorGenerator do
7
+ # Tell the generator where to put its output (what it thinks of as Rails.root)
8
+ destination File.expand_path("../../../../tmp", __FILE__)
9
+
10
+ before { prepare_destination }
11
+
12
+ describe 'no arguments' do
13
+ before { run_generator %w(products) }
14
+
15
+ describe 'spec/decorators/application_decorator_spec.rb' do
16
+ subject { file('spec/decorators/application_decorator_spec.rb') }
17
+ it { should exist }
18
+ it { should contain "describe ApplicationDecorator do" }
19
+ end
20
+
21
+ describe 'spec/decorators/products_decorator_spec.rb' do
22
+ subject { file('spec/decorators/products_decorator_spec.rb') }
23
+ it { should exist }
24
+ it { should contain "describe ProductsDecorator" }
25
+ end
26
+
27
+ end
28
+ end
@@ -22,4 +22,14 @@ describe Draper::ViewContext do
22
22
  Thread.current[:current_view_context] = nil
23
23
  expect {app_controller.current_view_context}.should raise_exception(Exception)
24
24
  end
25
+
26
+ it "sets view_context every time" do
27
+ app_controller_instance.stub(:view_context) { 'first' }
28
+ app_controller_instance.set_current_view_context
29
+ Thread.current[:current_view_context].should == 'first'
30
+
31
+ app_controller_instance.stub(:view_context) { 'second' }
32
+ app_controller_instance.set_current_view_context
33
+ Thread.current[:current_view_context].should == 'second'
34
+ end
25
35
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: draper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.9.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-09 00:00:00.000000000Z
12
+ date: 2011-10-20 00:00:00.000000000Z
13
13
  dependencies: []
14
14
  description: Draper reimagines the role of helpers in the view layer of a Rails application,
15
15
  allowing an object-oriented approach rather than procedural.
@@ -60,9 +60,13 @@ files:
60
60
  - lib/generators/draper/decorator/templates/application_decorator.rb
61
61
  - lib/generators/draper/decorator/templates/decorator.rb
62
62
  - lib/generators/rails/decorator_generator.rb
63
+ - lib/generators/rspec/decorator_generator.rb
64
+ - lib/generators/rspec/templates/application_decorator_spec.rb
65
+ - lib/generators/rspec/templates/decorator_spec.rb
63
66
  - spec/base_spec.rb
64
67
  - spec/draper/model_support_spec.rb
65
68
  - spec/generators/draper/decorator/decorator_generator_spec.rb
69
+ - spec/generators/rspec/decorator_generator_spec.rb
66
70
  - spec/samples/active_record.rb
67
71
  - spec/samples/application_controller.rb
68
72
  - spec/samples/application_helper.rb
@@ -102,6 +106,7 @@ test_files:
102
106
  - spec/base_spec.rb
103
107
  - spec/draper/model_support_spec.rb
104
108
  - spec/generators/draper/decorator/decorator_generator_spec.rb
109
+ - spec/generators/rspec/decorator_generator_spec.rb
105
110
  - spec/samples/active_record.rb
106
111
  - spec/samples/application_controller.rb
107
112
  - spec/samples/application_helper.rb