draper 0.7.3 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,103 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
+ <head>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6
+ <title>
7
+ Top Level Namespace
8
+
9
+ &mdash; Documentation by YARD 0.7.2
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" media="screen" charset="utf-8" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" media="screen" charset="utf-8" />
16
+
17
+ <script type="text/javascript" charset="utf-8">
18
+ relpath = '';
19
+ if (relpath != '') relpath += '/';
20
+ </script>
21
+
22
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
23
+
24
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
25
+
26
+
27
+ </head>
28
+ <body>
29
+ <script type="text/javascript" charset="utf-8">
30
+ if (window.top.frames.main) document.body.className = 'frames';
31
+ </script>
32
+
33
+ <div id="header">
34
+ <div id="menu">
35
+
36
+ <a href="_index.html">Index</a> &raquo;
37
+
38
+
39
+ <span class="title">Top Level Namespace</span>
40
+
41
+
42
+ <div class="noframes"><span class="title">(</span><a href="." target="_top">no frames</a><span class="title">)</span></div>
43
+ </div>
44
+
45
+ <div id="search">
46
+
47
+ <a id="class_list_link" href="#">Class List</a>
48
+
49
+ <a id="method_list_link" href="#">Method List</a>
50
+
51
+ <a id="file_list_link" href="#">File List</a>
52
+
53
+ </div>
54
+ <div class="clear"></div>
55
+ </div>
56
+
57
+ <iframe id="search_frame"></iframe>
58
+
59
+ <div id="content"><h1>Top Level Namespace
60
+
61
+
62
+
63
+ </h1>
64
+
65
+ <dl class="box">
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+
74
+ </dl>
75
+ <div class="clear"></div>
76
+
77
+ <h2>Defined Under Namespace</h2>
78
+ <p class="children">
79
+
80
+
81
+ <strong class="modules">Modules:</strong> <span class='object_link'><a href="Draper.html" title="Draper (module)">Draper</a></span>
82
+
83
+
84
+
85
+
86
+ </p>
87
+
88
+
89
+
90
+
91
+
92
+
93
+
94
+ </div>
95
+
96
+ <div id="footer">
97
+ Generated on Thu Sep 1 01:13:37 2011 by
98
+ <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
99
+ 0.7.2 (ruby-1.8.7).
100
+ </div>
101
+
102
+ </body>
103
+ </html>
@@ -6,72 +6,157 @@ module Draper
6
6
 
7
7
  DEFAULT_DENIED = Object.new.methods << :method_missing
8
8
  FORCED_PROXY = [:to_param]
9
+ FORCED_PROXY.each do |method|
10
+ define_method method do |*args, &block|
11
+ model.send method, *args, &block
12
+ end
13
+ end
9
14
  self.denied = DEFAULT_DENIED
10
15
 
11
- def initialize(input, context = nil)
16
+ # Initialize a new decorator instance by passing in
17
+ # an instance of the source class. Pass in an optional
18
+ # context is stored for later use.
19
+ #
20
+ # @param [Object] instance to wrap
21
+ # @param [Object] context (optional)
22
+ def initialize(input, context = {})
23
+ input.inspect # forces evaluation of a lazy query from AR
12
24
  input.inspect
13
25
  self.class.model_class = input.class if model_class.nil?
14
26
  @model = input
15
27
  self.context = context
16
- build_methods
17
28
  end
18
29
 
19
- def self.find(input)
20
- self.new(model_class.find(input))
30
+ # Proxies to the class specified by `decorates` to automatically
31
+ # lookup an object in the database and decorate it.
32
+ #
33
+ # @param [Symbol or String] id to lookup
34
+ # @return [Object] instance of this decorator class
35
+ def self.find(input, context = {})
36
+ self.new(model_class.find(input), context)
21
37
  end
22
38
 
39
+ # Typically called within a decorator definition, this method
40
+ # specifies the name of the wrapped object class.
41
+ #
42
+ # For instance, a `ProductDecorator` class might call `decorates :product`
43
+ #
44
+ # But they don't have to match in name, so a `EmployeeDecorator`
45
+ # class could call `decorates :person` to wrap instances of `Person`
46
+ #
47
+ # This is primarilly set so the `.find` method knows which class
48
+ # to query.
49
+ #
50
+ # @param [Symbol] class_name snakecase name of the decorated class, like `:product`
23
51
  def self.decorates(input)
24
52
  self.model_class = input.to_s.camelize.constantize
25
53
  model_class.send :include, Draper::ModelSupport
26
54
  end
27
55
 
56
+ # Specifies a black list of methods which may *not* be proxied to
57
+ # to the wrapped object.
58
+ #
59
+ # Do not use both `.allows` and `.denies` together, either write
60
+ # a whitelist with `.allows` or a blacklist with `.denies`
61
+ #
62
+ # @param [Symbols*] methods to deny like `:find, :find_by_name`
28
63
  def self.denies(*input_denied)
29
64
  raise ArgumentError, "Specify at least one method (as a symbol) to exclude when using denies" if input_denied.empty?
30
65
  raise ArgumentError, "Use either 'allows' or 'denies', but not both." if self.allowed?
31
66
  self.denied += input_denied
32
67
  end
33
68
 
69
+ # Specifies a white list of methods which *may* be proxied to
70
+ # to the wrapped object. When `allows` is used, only the listed
71
+ # methods and methods defined in the decorator itself will be
72
+ # available.
73
+ #
74
+ # Do not use both `.allows` and `.denies` together, either write
75
+ # a whitelist with `.allows` or a blacklist with `.denies`
76
+ #
77
+ # @param [Symbols*] methods to allow like `:find, :find_by_name`
34
78
  def self.allows(*input_allows)
35
79
  raise ArgumentError, "Specify at least one method (as a symbol) to allow when using allows" if input_allows.empty?
36
80
  raise ArgumentError, "Use either 'allows' or 'denies', but not both." unless (self.denied == DEFAULT_DENIED)
37
81
  self.allowed = input_allows
38
82
  end
39
83
 
40
- def self.decorate(input, context = nil)
84
+ # Initialize a new decorator instance by passing in
85
+ # an instance of the source class. Pass in an optional
86
+ # context is stored for later use.
87
+ #
88
+ # When passing in a single object, using `.decorate` is
89
+ # identical to calling `.new`. However, `.decorate` can
90
+ # also accept a collection and return a collection of
91
+ # individually decorated objects.
92
+ #
93
+ # @param [Object] instance(s) to wrap
94
+ # @param [Object] context (optional)
95
+ def self.decorate(input, context = {})
41
96
  input.respond_to?(:each) ? input.map{|i| new(i, context)} : new(input, context)
42
97
  end
43
98
 
99
+ # Access the helpers proxy to call built-in and user-defined
100
+ # Rails helpers. Aliased to `.h` for convinience.
101
+ #
102
+ # @return [Object] proxy
44
103
  def helpers
45
104
  @helpers ||= ApplicationController::all_helpers
46
105
  end
47
106
  alias :h :helpers
48
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.
49
114
  def self.lazy_helpers
50
115
  self.send(:include, Draper::LazyHelpers)
51
116
  end
52
117
 
118
+ # Use primarily by `form_for`, this returns an instance of
119
+ # `ActiveModel::Name` set to the wrapped model's class name
120
+ #
121
+ # @return [ActiveModel::Name] model_name
53
122
  def self.model_name
54
123
  ActiveModel::Name.new(model_class)
55
124
  end
56
125
 
126
+ # Fetch the original wrapped model.
127
+ #
128
+ # @return [Object] original_model
57
129
  def to_model
58
130
  @model
59
131
  end
60
132
 
133
+ # Delegates == to the decorated models
134
+ #
135
+ # @return [Boolean] true if other's model == self's model
136
+ def ==(other)
137
+ @model == other.model
138
+ end
139
+
140
+ def respond_to?(method)
141
+ if select_methods.include?(method)
142
+ model.respond_to?(method)
143
+ else
144
+ super
145
+ end
146
+ end
147
+
148
+ def method_missing(method, *args, &block)
149
+ if select_methods.include?(method)
150
+ model.send(method, *args, &block)
151
+ else
152
+ super
153
+ end
154
+ end
155
+
61
156
  private
62
157
  def select_methods
63
158
  specified = self.allowed || (model.public_methods.map{|s| s.to_sym} - denied.map{|s| s.to_sym})
64
159
  (specified - self.public_methods.map{|s| s.to_sym}) + FORCED_PROXY
65
160
  end
66
-
67
- def build_methods
68
- select_methods.each do |method|
69
- (class << self; self; end).class_eval do
70
- define_method method do |*args, &block|
71
- model.send method, *args, &block
72
- end
73
- end
74
- end
75
- end
76
161
  end
77
162
  end
@@ -1,5 +1,6 @@
1
1
  module Draper::ModelSupport
2
2
  def decorator
3
3
  @decorator ||= "#{self.class.name}Decorator".constantize.decorate(self)
4
+ block_given? ? yield(@decorator) : @decorator
4
5
  end
5
6
  end
@@ -1,3 +1,3 @@
1
1
  module Draper
2
- VERSION = "0.7.3"
2
+ VERSION = "0.7.4"
3
3
  end
@@ -0,0 +1,15 @@
1
+ require File.expand_path('../../draper/decorator/decorator_generator.rb', __FILE__)
2
+ class Rails::DecoratorGenerator < Draper::DecoratorGenerator
3
+
4
+ source_root File.expand_path('../../draper/decorator/templates', __FILE__)
5
+
6
+ class_option :invoke_after_finished, :type => :string, :description => "Generator to invoke when finished"
7
+
8
+ def build_model_and_application_decorators
9
+ super
10
+ if self.options[:invoke_after_finished]
11
+ Rails::Generators.invoke(self.options[:invoke_after_finished], [@name, @_initializer.first[1..-1]])
12
+ end
13
+ end
14
+
15
+ end
@@ -43,8 +43,9 @@ describe Draper::Base do
43
43
 
44
44
  context("selecting methods") do
45
45
  it "echos the methods of the wrapped class except default exclusions" do
46
+ pending "Fine on 1.9 but fails on 1.8 due to differences in implementation of respond_to and method_missing. Help?"
46
47
  source.methods.each do |method|
47
- unless Draper::Base::DEFAULT_DENIED.include?(method)
48
+ unless Draper::Base::DEFAULT_DENIED.include?(method)
48
49
  subject.should respond_to(method)
49
50
  end
50
51
  end
@@ -88,6 +89,11 @@ describe Draper::Base do
88
89
  pd.should be_instance_of(ProductDecorator)
89
90
  pd.model.should be_instance_of(Product)
90
91
  end
92
+
93
+ it "should accept and store a context" do
94
+ pd = ProductDecorator.find(1, :admin)
95
+ pd.context.should == :admin
96
+ end
91
97
  end
92
98
 
93
99
  context ".decorate" do
@@ -132,6 +138,13 @@ describe Draper::Base do
132
138
  end
133
139
  end
134
140
 
141
+ context('.==') do
142
+ it "should compare the decorated models" do
143
+ other = Draper::Base.new(source)
144
+ subject.should == other
145
+ end
146
+ end
147
+
135
148
  describe "a sample usage with denies" do
136
149
  let(:subject_with_denies){ DecoratorWithDenies.new(source) }
137
150
 
@@ -156,11 +169,11 @@ describe Draper::Base do
156
169
  let(:subject_with_allows){ DecoratorWithAllows.new(source) }
157
170
 
158
171
  it "should echo the allowed method" do
159
- subject_with_allows.should respond_to(:upcase)
172
+ subject_with_allows.should respond_to(:goodnight_moon)
160
173
  end
161
174
 
162
175
  it "should echo _only_ the allowed method" do
163
- subject_with_allows.should_not respond_to(:downcase)
176
+ subject_with_allows.should_not respond_to(:hello_world)
164
177
  end
165
178
  end
166
179
 
@@ -6,5 +6,10 @@ describe Draper::ModelSupport do
6
6
  describe '#decorator' do
7
7
  its(:decorator) { should be_kind_of(ProductDecorator) }
8
8
  its(:decorator) { should be(subject.decorator) }
9
+
10
+ it 'should have abillity to pass block' do
11
+ a = Product.new.decorator { |d| d.awesome_title }
12
+ a.should eql "Awesome Title"
13
+ end
9
14
  end
10
15
  end
@@ -1,7 +1,4 @@
1
1
  module ActiveRecord
2
2
  class Base
3
- def method_missing(name, *args)
4
- name
5
- end
6
3
  end
7
- end
4
+ end
@@ -1,3 +1,3 @@
1
1
  class DecoratorWithAllows < Draper::Base
2
- allows :upcase
2
+ allows :goodnight_moon
3
3
  end
@@ -1,3 +1,7 @@
1
1
  class ProductDecorator < Draper::Base
2
2
  decorates :product
3
+
4
+ def awesome_title
5
+ "Awesome Title"
6
+ end
3
7
  end
metadata CHANGED
@@ -1,39 +1,52 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: draper
3
- version: !ruby/object:Gem::Version
4
- hash: 5
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.4
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 7
9
- - 3
10
- version: 0.7.3
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Jeff Casimir
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2011-08-29 00:00:00 Z
12
+ date: 2011-10-03 00:00:00.000000000Z
19
13
  dependencies: []
20
-
21
- description: Draper reimagines the role of helpers in the view layer of a Rails application, allowing an object-oriented approach rather than procedural.
22
- email:
14
+ description: Draper reimagines the role of helpers in the view layer of a Rails application,
15
+ allowing an object-oriented approach rather than procedural.
16
+ email:
23
17
  - jeff@casimircreative.com
24
18
  executables: []
25
-
26
19
  extensions: []
27
-
28
20
  extra_rdoc_files: []
29
-
30
- files:
21
+ files:
31
22
  - .gitignore
32
23
  - .travis.yml
24
+ - .yardopts
33
25
  - Gemfile
34
26
  - Guardfile
35
27
  - Rakefile
36
28
  - Readme.markdown
29
+ - doc/ApplicationDecorator.html
30
+ - doc/Draper.html
31
+ - doc/Draper/AllHelpers.html
32
+ - doc/Draper/Base.html
33
+ - doc/Draper/DecoratorGenerator.html
34
+ - doc/Draper/LazyHelpers.html
35
+ - doc/Draper/ModelSupport.html
36
+ - doc/Draper/System.html
37
+ - doc/_index.html
38
+ - doc/class_list.html
39
+ - doc/css/common.css
40
+ - doc/css/full_list.css
41
+ - doc/css/style.css
42
+ - doc/file_list.html
43
+ - doc/frames.html
44
+ - doc/index.html
45
+ - doc/js/app.js
46
+ - doc/js/full_list.js
47
+ - doc/js/jquery.js
48
+ - doc/method_list.html
49
+ - doc/top-level-namespace.html
37
50
  - draper.gemspec
38
51
  - lib/draper.rb
39
52
  - lib/draper/all_helpers.rb
@@ -46,6 +59,7 @@ files:
46
59
  - lib/generators/draper/decorator/decorator_generator.rb
47
60
  - lib/generators/draper/decorator/templates/application_decorator.rb
48
61
  - lib/generators/draper/decorator/templates/decorator.rb
62
+ - lib/generators/rails/decorator_generator.rb
49
63
  - spec/base_spec.rb
50
64
  - spec/draper/model_support_spec.rb
51
65
  - spec/samples/active_record.rb
@@ -59,38 +73,29 @@ files:
59
73
  - spec/spec_helper.rb
60
74
  homepage: http://github.com/jcasimir/draper
61
75
  licenses: []
62
-
63
76
  post_install_message:
64
77
  rdoc_options: []
65
-
66
- require_paths:
78
+ require_paths:
67
79
  - lib
68
- required_ruby_version: !ruby/object:Gem::Requirement
80
+ required_ruby_version: !ruby/object:Gem::Requirement
69
81
  none: false
70
- requirements:
71
- - - ">="
72
- - !ruby/object:Gem::Version
73
- hash: 3
74
- segments:
75
- - 0
76
- version: "0"
77
- required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
87
  none: false
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- hash: 3
83
- segments:
84
- - 0
85
- version: "0"
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
86
92
  requirements: []
87
-
88
93
  rubyforge_project: draper
89
- rubygems_version: 1.8.6
94
+ rubygems_version: 1.8.10
90
95
  signing_key:
91
96
  specification_version: 3
92
97
  summary: Decorator pattern implmentation for Rails.
93
- test_files:
98
+ test_files:
94
99
  - spec/base_spec.rb
95
100
  - spec/draper/model_support_spec.rb
96
101
  - spec/samples/active_record.rb