draper 0.5.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -1
- data/Rakefile +39 -11
- data/Readme.markdown +75 -27
- data/draper.gemspec +8 -2
- data/lib/draper.rb +1 -0
- data/lib/draper/all_helpers.rb +2 -1
- data/lib/draper/base.rb +23 -21
- data/lib/draper/model_support.rb +5 -0
- data/lib/draper/version.rb +1 -1
- data/lib/generators/draper/decorator/USAGE +7 -0
- data/lib/generators/draper/decorator/decorator_generator.rb +17 -0
- data/lib/generators/draper/decorator/templates/application_decorator.rb +28 -0
- data/lib/generators/draper/decorator/templates/decorator.rb +32 -0
- data/spec/base_spec.rb +84 -51
- data/spec/draper/model_support_spec.rb +10 -0
- data/spec/spec_helper.rb +7 -1
- metadata +94 -98
- data/lib/generators/draper/model/USAGE +0 -7
- data/lib/generators/draper/model/model_generator.rb +0 -10
- data/lib/generators/draper/model/templates/model.rb +0 -29
data/.gitignore
CHANGED
data/Rakefile
CHANGED
@@ -1,19 +1,47 @@
|
|
1
1
|
require 'bundler/gem_tasks'
|
2
|
+
require 'rake'
|
3
|
+
require 'rspec/core/rake_task'
|
2
4
|
|
3
|
-
|
5
|
+
RCOV = RUBY_VERSION.to_f == 1.8
|
6
|
+
|
7
|
+
namespace :spec do
|
8
|
+
|
9
|
+
RSpec::Core::RakeTask.new(:coverage) do |t|
|
10
|
+
t.pattern = 'spec/**/*_spec.rb'
|
11
|
+
|
12
|
+
if RCOV
|
13
|
+
t.rcov = true
|
14
|
+
t.rcov_opts = '--exclude osx\/objc,spec,gems\/'
|
15
|
+
end
|
16
|
+
end
|
4
17
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
18
|
+
RSpec::Core::RakeTask.new(:normal) do |t|
|
19
|
+
t.pattern ='spec/**/*_spec.rb'
|
20
|
+
t.rcov = false
|
21
|
+
end
|
22
|
+
|
23
|
+
namespace :coverage do
|
24
|
+
desc "Cleanup coverage data"
|
25
|
+
task :cleanup do
|
26
|
+
rm_rf 'coverage.data'
|
27
|
+
rm_rf 'coverage'
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "Browse the code coverage report."
|
31
|
+
task :report => ["spec:coverage:cleanup", "spec:coverage"] do
|
32
|
+
if RCOV
|
33
|
+
require "launchy"
|
34
|
+
Launchy.open("coverage/index.html")
|
35
|
+
else
|
36
|
+
require 'cover_me'
|
37
|
+
CoverMe.complete!
|
38
|
+
end
|
39
|
+
end
|
9
40
|
end
|
10
41
|
|
11
42
|
end
|
12
43
|
|
13
|
-
|
14
|
-
|
15
|
-
end
|
44
|
+
desc "RSpec tests"
|
45
|
+
task "spec" => "spec:normal"
|
16
46
|
|
17
|
-
task
|
18
|
-
Rake::Task['cover_me:report'].invoke
|
19
|
-
end
|
47
|
+
task "default" => "spec"
|
data/Readme.markdown
CHANGED
@@ -3,25 +3,29 @@
|
|
3
3
|
## Quick Start
|
4
4
|
|
5
5
|
1. Add `gem 'draper'` to your `Gemfile` and `bundle`
|
6
|
-
2. Run `rails g draper:
|
7
|
-
3. Edit `app/decorators/
|
6
|
+
2. Run `rails g draper:decorator YourModel`
|
7
|
+
3. Edit `app/decorators/[your_model]_decorator.rb` using:
|
8
8
|
1. `h` to proxy to Rails/application helpers like `h.current_user`
|
9
9
|
2. `model` to access the wrapped object like `model.created_at`
|
10
|
-
4.
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
4. Put common decorations in `app/decorators/application.rb`
|
11
|
+
5. Wrap models in your controller with the decorator using:
|
12
|
+
1. `.find` automatic lookup & wrap
|
13
|
+
ex: `ArticleDecorator.find(1)`
|
14
|
+
2. `.decorate` method with single object or collection,
|
15
|
+
ex: `ArticleDecorator.decorate(Article.all)`
|
16
|
+
3. `.new` method with single object
|
17
|
+
ex: `ArticleDecorator.new(Article.first)`
|
18
|
+
6. Output the instance methods in your view templates
|
19
|
+
ex: `@article_decorator.created_at`
|
15
20
|
|
16
21
|
## Goals
|
17
22
|
|
18
|
-
This gem makes it easy to apply the decorator pattern to
|
23
|
+
This gem makes it easy to apply the decorator pattern to domain models in a Rails application. This pattern gives you three wins:
|
19
24
|
|
20
25
|
1. Replace most helpers with an object-oriented approach
|
21
26
|
2. Filter data at the presentation level
|
22
27
|
3. Enforce an interface between your controllers and view templates.
|
23
28
|
|
24
|
-
|
25
29
|
### 1. Object Oriented Helpers
|
26
30
|
|
27
31
|
Why hate helpers? In Ruby/Rails we approach everything from an Object-Oriented perspective, then with helpers we get procedural.The job of a helper is to take in data and output a presentation-ready string. We can do that with a decorator.
|
@@ -29,7 +33,7 @@ Why hate helpers? In Ruby/Rails we approach everything from an Object-Oriented p
|
|
29
33
|
A decorator wraps an object with presentation-related accessor methods. For instance, if you had an `Article` object, then the decorator could override `.published_at` to use formatted output like this:
|
30
34
|
|
31
35
|
```ruby
|
32
|
-
class ArticleDecorator <
|
36
|
+
class ArticleDecorator < ApplicationDecorator
|
33
37
|
decorates :article
|
34
38
|
def published_at
|
35
39
|
date = h.content_tag(:span, model.published_at.strftime("%A, %B %e").squeeze(" "), :class => 'date')
|
@@ -50,7 +54,7 @@ How would you handle this in the model layer? You'd probably pass the `current_u
|
|
50
54
|
When you use a decorator you have the power of a Ruby object but it's a part of the view layer. This is where your `to_xml` belongs. You can access your `current_user` helper method using the `h` proxy available in the decorator:
|
51
55
|
|
52
56
|
```ruby
|
53
|
-
class ArticleDecorator <
|
57
|
+
class ArticleDecorator < ApplicationDecorator
|
54
58
|
decorates :article
|
55
59
|
ADMIN_VISIBLE_ATTRIBUTES = [:title, :body, :author, :status]
|
56
60
|
PUBLIC_VISIBLE_ATTRIBUTES = [:title, :body]
|
@@ -71,7 +75,7 @@ Want to strictly control what methods are proxied to the original object? Use `d
|
|
71
75
|
The `denies` method takes a blacklist approach. For instance:
|
72
76
|
|
73
77
|
```ruby
|
74
|
-
class ArticleDecorator <
|
78
|
+
class ArticleDecorator < ApplicationDecorator
|
75
79
|
decorates :article
|
76
80
|
denies :title
|
77
81
|
end
|
@@ -91,7 +95,7 @@ NoMethodError: undefined method `title' for #<ArticleDecorator:0x000001020d7728>
|
|
91
95
|
A better approach is to define a whitelist using `allows`:
|
92
96
|
|
93
97
|
```ruby
|
94
|
-
class ArticleDecorator <
|
98
|
+
class ArticleDecorator < ApplicationDecorator
|
95
99
|
decorates :article
|
96
100
|
allows :title, :description
|
97
101
|
end
|
@@ -106,7 +110,7 @@ ruby-1.9.2-p290 :003 > ad.created_at
|
|
106
110
|
NoMethodError: undefined method `created_at' for #<ArticleDecorator:0x000001020d7728>
|
107
111
|
```
|
108
112
|
|
109
|
-
## Up
|
113
|
+
## Up and Running
|
110
114
|
|
111
115
|
### Setup
|
112
116
|
|
@@ -122,12 +126,24 @@ Run bundle:
|
|
122
126
|
bundle
|
123
127
|
```
|
124
128
|
|
129
|
+
#### Disable Rails Helper Generation (Optional)
|
130
|
+
|
131
|
+
When you generate a scaffold, Rails will create a matching helper file. If you're using decorators, this is probably unnecessary. You can disable the helper file creation by adding this to your `config/application.rb`
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
config.generators do |g|
|
135
|
+
g.helper false
|
136
|
+
end
|
137
|
+
```
|
138
|
+
|
139
|
+
If you want a helper, you can still call `rails generate helper` directly.
|
140
|
+
|
125
141
|
### Generate the Decorator
|
126
142
|
|
127
143
|
To decorate a model named `Article`:
|
128
144
|
|
129
145
|
```
|
130
|
-
rails generate draper:
|
146
|
+
rails generate draper:decorator Article
|
131
147
|
```
|
132
148
|
|
133
149
|
### Writing Methods
|
@@ -135,7 +151,7 @@ rails generate draper:model Article
|
|
135
151
|
Open the decorator model (ex: `app/decorators/article_decorator.rb`) and add normal instance methods. To access the wrapped source object, use the `model` method:
|
136
152
|
|
137
153
|
```ruby
|
138
|
-
class
|
154
|
+
class ArticleDecorator < ApplicationDecorator
|
139
155
|
decorates :article
|
140
156
|
|
141
157
|
def author_name
|
@@ -144,13 +160,12 @@ class Article < Draper::Base
|
|
144
160
|
end
|
145
161
|
```
|
146
162
|
|
147
|
-
|
148
163
|
### Using Existing Helpers
|
149
164
|
|
150
165
|
You probably want to make use of Rails helpers and those defined in your application. Use the `helpers` or `h` method proxy:
|
151
166
|
|
152
167
|
```ruby
|
153
|
-
class
|
168
|
+
class ArticleDecorator < ApplicationDecorator
|
154
169
|
decorates :article
|
155
170
|
|
156
171
|
def published_at
|
@@ -161,6 +176,23 @@ class Article < Draper::Base
|
|
161
176
|
end
|
162
177
|
```
|
163
178
|
|
179
|
+
#### Lazy Helpers
|
180
|
+
|
181
|
+
Hate seeing that `h.` proxy all over? Willing to mix a bazillion methods into your decorator? Then try lazy helpers:
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
class ArticleDecorator < ApplicationDecorator
|
185
|
+
decorates :article
|
186
|
+
lazy_helpers
|
187
|
+
|
188
|
+
def published_at
|
189
|
+
date = content_tag(:span, model.published_at.strftime("%A, %B %e").squeeze(" "), :class => 'date')
|
190
|
+
time = content_tag(:span, model.published_at.strftime("%l:%M%p"), :class => 'time').delete(" ")
|
191
|
+
content_tag :span, date + time, :class => 'created_at'
|
192
|
+
end
|
193
|
+
end
|
194
|
+
```
|
195
|
+
|
164
196
|
### In the Controller
|
165
197
|
|
166
198
|
When writing your controller actions, you have three options:
|
@@ -218,7 +250,7 @@ First, follow the steps above to add the dependency and update your bundle.
|
|
218
250
|
Since we're talking about the `Article` model we'll create an `ArticleDecorator` class. You could do it by hand, but use the provided generator:
|
219
251
|
|
220
252
|
```
|
221
|
-
rails generate draper:
|
253
|
+
rails generate draper:decorator Article
|
222
254
|
```
|
223
255
|
|
224
256
|
Now open up the created `app/decorators/article_decorator.rb` and you'll find an `ArticleDecorator` class. Add this method:
|
@@ -250,22 +282,38 @@ Then within your views you can utilize both the normal data methods and your new
|
|
250
282
|
Ta-da! Object-oriented data formatting for your view layer. Below is the complete decorator with extra comments removed:
|
251
283
|
|
252
284
|
```ruby
|
253
|
-
class ArticleDecorator <
|
285
|
+
class ArticleDecorator < ApplicationDecorator
|
254
286
|
decorates :article
|
255
287
|
|
256
288
|
def published_at
|
257
|
-
date = h.content_tag(:span, published_at.strftime("%A, %B %e").squeeze(" "), :class => 'date')
|
258
|
-
time = h.content_tag(:span, published_at.strftime("%l:%M%p"), :class => 'time').delete(" ")
|
259
|
-
h.content_tag :span, date + time, :class => '
|
289
|
+
date = h.content_tag(:span, model.published_at.strftime("%A, %B %e").squeeze(" "), :class => 'date')
|
290
|
+
time = h.content_tag(:span, model.published_at.strftime("%l:%M%p"), :class => 'time').delete(" ")
|
291
|
+
h.content_tag :span, date + time, :class => 'published_at'
|
260
292
|
end
|
261
293
|
end
|
262
294
|
```
|
263
295
|
|
264
296
|
## Issues / Pending
|
265
297
|
|
266
|
-
*
|
267
|
-
* Keep revising Readme for better organization/clarity
|
268
|
-
*
|
298
|
+
* Documentation
|
299
|
+
* Keep revising Readme for better organization/clarity
|
300
|
+
* Add more information about using "context"
|
301
|
+
* Add information about the `.decorator` method
|
302
|
+
* Make clear the pattern of overriding accessor methods of the wrapped model
|
303
|
+
* Build sample Rails application(s)
|
304
|
+
* Add a short screencast
|
305
|
+
* Add YARD documentation to source
|
306
|
+
* Add a section about contributing
|
307
|
+
* Generators
|
308
|
+
* Test coverage for generators (help!)
|
309
|
+
* Implement hook so generating a controller/scaffold generates a decorator
|
310
|
+
* Add generators for...
|
311
|
+
* `draper:model`: Model + Decorator
|
312
|
+
* `draper:controller`: Controller setup with decoration calls
|
313
|
+
* `draper:scaffold`: Controller, Model, Decorator, Views, Tests
|
314
|
+
* Other
|
315
|
+
* Implement a HATEOAS helper, maybe as a separate gem
|
316
|
+
* Build a fly website like http://fabricationgem.com
|
269
317
|
|
270
318
|
## License
|
271
319
|
|
@@ -277,4 +325,4 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
277
325
|
|
278
326
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
279
327
|
|
280
|
-
THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
328
|
+
THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/draper.gemspec
CHANGED
@@ -22,9 +22,15 @@ Gem::Specification.new do |s|
|
|
22
22
|
s.add_development_dependency "rspec", "~> 2.0.1"
|
23
23
|
s.add_development_dependency "activesupport", "~> 3.0.9"
|
24
24
|
s.add_development_dependency "actionpack", "~> 3.0.9"
|
25
|
-
s.add_development_dependency "ruby-debug19"
|
26
25
|
s.add_development_dependency "guard"
|
27
26
|
s.add_development_dependency "guard-rspec"
|
28
27
|
s.add_development_dependency "rb-fsevent"
|
29
|
-
|
28
|
+
if RUBY_VERSION.to_f == 1.8
|
29
|
+
s.add_development_dependency "ruby-debug"
|
30
|
+
s.add_development_dependency "rcov"
|
31
|
+
s.add_development_dependency "launchy"
|
32
|
+
else
|
33
|
+
s.add_development_dependency "ruby-debug19"
|
34
|
+
s.add_development_dependency 'cover_me', '>= 1.0.0.rc6'
|
35
|
+
end
|
30
36
|
end
|
data/lib/draper.rb
CHANGED
data/lib/draper/all_helpers.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module Draper
|
2
2
|
module AllHelpers
|
3
|
+
# Most of the black magic here thanks to Xavier Shay (@xshay)
|
3
4
|
# Provide access to helper methods from outside controllers and views,
|
4
5
|
# such as in Presenter objects. Rails provides ActionController::Base.helpers,
|
5
6
|
# but this does not include any of our application helpers.
|
@@ -30,7 +31,7 @@ module Draper
|
|
30
31
|
def url_for(*args)
|
31
32
|
if args.last.is_a?(Hash) && !args.last[:only_path]
|
32
33
|
args = args.dup
|
33
|
-
args << args.pop.merge(host
|
34
|
+
args << args.pop.merge('host' => ActionMailer::Base.default_url_options[:host])
|
34
35
|
end
|
35
36
|
super(*args)
|
36
37
|
end
|
data/lib/draper/base.rb
CHANGED
@@ -1,44 +1,46 @@
|
|
1
1
|
module Draper
|
2
|
-
class Base
|
2
|
+
class Base
|
3
3
|
require 'active_support/core_ext/class/attribute'
|
4
4
|
class_attribute :denied, :allowed, :model_class
|
5
|
-
attr_accessor :model
|
6
|
-
|
5
|
+
attr_accessor :context, :model
|
6
|
+
|
7
7
|
DEFAULT_DENIED = Object.new.methods << :method_missing
|
8
8
|
FORCED_PROXY = [:to_param]
|
9
9
|
self.denied = DEFAULT_DENIED
|
10
10
|
|
11
|
-
def initialize(input)
|
11
|
+
def initialize(input, context = nil)
|
12
12
|
input.inspect
|
13
13
|
self.class.model_class = input.class if model_class.nil?
|
14
|
-
@model = input
|
14
|
+
@model = input
|
15
|
+
self.context = context
|
15
16
|
build_methods
|
16
17
|
end
|
17
|
-
|
18
|
+
|
18
19
|
def self.find(input)
|
19
20
|
self.new(model_class.find(input))
|
20
21
|
end
|
21
|
-
|
22
|
+
|
22
23
|
def self.decorates(input)
|
23
24
|
self.model_class = input.to_s.classify.constantize
|
25
|
+
model_class.send :include, Draper::ModelSupport
|
24
26
|
end
|
25
|
-
|
27
|
+
|
26
28
|
def self.denies(*input_denied)
|
27
29
|
raise ArgumentError, "Specify at least one method (as a symbol) to exclude when using denies" if input_denied.empty?
|
28
30
|
raise ArgumentError, "Use either 'allows' or 'denies', but not both." if self.allowed?
|
29
31
|
self.denied += input_denied
|
30
32
|
end
|
31
|
-
|
33
|
+
|
32
34
|
def self.allows(*input_allows)
|
33
35
|
raise ArgumentError, "Specify at least one method (as a symbol) to allow when using allows" if input_allows.empty?
|
34
36
|
raise ArgumentError, "Use either 'allows' or 'denies', but not both." unless (self.denied == DEFAULT_DENIED)
|
35
37
|
self.allowed = input_allows
|
36
38
|
end
|
37
39
|
|
38
|
-
def self.decorate(input)
|
39
|
-
input.respond_to?(:each) ? input.map{|i| new(i)} : new(input)
|
40
|
+
def self.decorate(input, context = nil)
|
41
|
+
input.respond_to?(:each) ? input.map{|i| new(i, context)} : new(input, context)
|
40
42
|
end
|
41
|
-
|
43
|
+
|
42
44
|
def helpers
|
43
45
|
@helpers ||= ApplicationController::all_helpers
|
44
46
|
end
|
@@ -47,19 +49,19 @@ module Draper
|
|
47
49
|
def self.lazy_helpers
|
48
50
|
self.send(:include, Draper::LazyHelpers)
|
49
51
|
end
|
50
|
-
|
52
|
+
|
51
53
|
def self.model_name
|
52
54
|
ActiveModel::Name.new(model_class)
|
53
55
|
end
|
54
|
-
|
56
|
+
|
55
57
|
def to_model
|
56
58
|
@model
|
57
59
|
end
|
58
|
-
|
59
|
-
private
|
60
|
+
|
61
|
+
private
|
60
62
|
def select_methods
|
61
|
-
specified = self.allowed || (model.public_methods - denied)
|
62
|
-
(specified - self.public_methods) + FORCED_PROXY
|
63
|
+
specified = self.allowed || (model.public_methods.map{|s| s.to_sym} - denied.map{|s| s.to_sym})
|
64
|
+
(specified - self.public_methods.map{|s| s.to_sym}) + FORCED_PROXY
|
63
65
|
end
|
64
66
|
|
65
67
|
def build_methods
|
@@ -69,7 +71,7 @@ module Draper
|
|
69
71
|
model.send method, *args, &block
|
70
72
|
end
|
71
73
|
end
|
72
|
-
end
|
73
|
-
end
|
74
|
+
end
|
75
|
+
end
|
74
76
|
end
|
75
|
-
end
|
77
|
+
end
|
data/lib/draper/version.rb
CHANGED
@@ -0,0 +1,17 @@
|
|
1
|
+
module Draper
|
2
|
+
class DecoratorGenerator < Rails::Generators::NamedBase
|
3
|
+
source_root File.expand_path('../templates', __FILE__)
|
4
|
+
|
5
|
+
DECORATORS_ROOT = 'app/decorators/'
|
6
|
+
APPLICATION_DECORATOR = 'application_decorator.rb'
|
7
|
+
APPLICATION_DECORATOR_PATH = DECORATORS_ROOT + APPLICATION_DECORATOR
|
8
|
+
|
9
|
+
def build_model_and_application_decorators
|
10
|
+
empty_directory "app/decorators"
|
11
|
+
unless File.exists?(APPLICATION_DECORATOR_PATH)
|
12
|
+
template APPLICATION_DECORATOR, APPLICATION_DECORATOR_PATH
|
13
|
+
end
|
14
|
+
template 'decorator.rb', "#{DECORATORS_ROOT}#{singular_name}_decorator.rb"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class ApplicationDecorator < Draper::Base
|
2
|
+
# Lazy Helpers
|
3
|
+
# PRO: Call Rails helpers without the h. proxy
|
4
|
+
# ex: number_to_currency(model.price)
|
5
|
+
# CON: Add a bazillion methods into your decorator's namespace
|
6
|
+
# and probably sacrifice performance/memory
|
7
|
+
#
|
8
|
+
# Enable them by uncommenting this line:
|
9
|
+
# lazy_helpers
|
10
|
+
|
11
|
+
# Shared Decorations
|
12
|
+
# Consider defining shared methods common to all your models.
|
13
|
+
#
|
14
|
+
# Example: standardize the formatting of timestamps
|
15
|
+
#
|
16
|
+
# def formatted_timestamp(time)
|
17
|
+
# h.content_tag :span, time.strftime("%a %m/%d/%y"),
|
18
|
+
# :class => 'timestamp'
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# def created_at
|
22
|
+
# formatted_timestamp(model.created_at)
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# def updated_at
|
26
|
+
# formatted_timestamp(model.updated_at)
|
27
|
+
# end
|
28
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class <%= singular_name.camelize %>Decorator < ApplicationDecorator
|
2
|
+
decorates :<%= singular_name.to_sym %>
|
3
|
+
|
4
|
+
# Accessing Helpers
|
5
|
+
# You can access any helper via a proxy
|
6
|
+
#
|
7
|
+
# Normal Usage: helpers.number_to_currency(2)
|
8
|
+
# Abbreviated : h.number_to_currency(2)
|
9
|
+
#
|
10
|
+
# Or, optionally enable "lazy helpers" by calling this method:
|
11
|
+
# lazy_helpers
|
12
|
+
# Then use the helpers with no proxy:
|
13
|
+
# number_to_currency(2)
|
14
|
+
|
15
|
+
# Defining an Interface
|
16
|
+
# Control access to the wrapped subject's methods using one of the following:
|
17
|
+
#
|
18
|
+
# To allow only the listed methods (whitelist):
|
19
|
+
# allows :method1, :method2
|
20
|
+
#
|
21
|
+
# To allow everything except the listed methods (blacklist):
|
22
|
+
# denies :method1, :method2
|
23
|
+
|
24
|
+
# Presentation Methods
|
25
|
+
# Define your own instance methods, even overriding accessors
|
26
|
+
# generated by ActiveRecord:
|
27
|
+
#
|
28
|
+
# def created_at
|
29
|
+
# h.content_tag :span, time.strftime("%a %m/%d/%y"),
|
30
|
+
# :class => 'timestamp'
|
31
|
+
# end
|
32
|
+
end
|
data/spec/base_spec.rb
CHANGED
@@ -3,7 +3,7 @@ require 'draper'
|
|
3
3
|
|
4
4
|
describe Draper::Base do
|
5
5
|
subject{ Draper::Base.new(source) }
|
6
|
-
let(:source){ Product.new }
|
6
|
+
let(:source){ Product.new }
|
7
7
|
|
8
8
|
context(".lazy_helpers") do
|
9
9
|
it "makes Rails helpers available without using the h. proxy" do
|
@@ -11,7 +11,7 @@ describe Draper::Base do
|
|
11
11
|
subject.send(:pluralize, 5, "cat").should == "5 cats"
|
12
12
|
end
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
context(".model_name") do
|
16
16
|
it "should return an ActiveModel::Name instance" do
|
17
17
|
Draper::Base.model_name.should be_instance_of(ActiveModel::Name)
|
@@ -23,14 +23,14 @@ describe Draper::Base do
|
|
23
23
|
ProductDecorator.new(source).model_class == Product
|
24
24
|
end
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
context(".model / .to_model") do
|
28
28
|
it "should return the wrapped object" do
|
29
29
|
subject.to_model.should == source
|
30
30
|
subject.model.should == source
|
31
31
|
end
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
context("selecting methods") do
|
35
35
|
it "echos the methods of the wrapped class except default exclusions" do
|
36
36
|
source.methods.each do |method|
|
@@ -39,149 +39,182 @@ describe Draper::Base do
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
it "should not override a defined method with a source method" do
|
44
44
|
DecoratorWithApplicationHelper.new(source).length.should == "overridden"
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
47
|
it "should always proxy to_param" do
|
48
48
|
source.send :class_eval, "def to_param; 1; end"
|
49
49
|
Draper::Base.new(source).to_param.should == 1
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
it "should not copy the .class, .inspect, or other existing methods" do
|
53
53
|
source.class.should_not == subject.class
|
54
54
|
source.inspect.should_not == subject.inspect
|
55
55
|
source.to_s.should_not == subject.to_s
|
56
56
|
end
|
57
|
-
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'the decorated model' do
|
60
|
+
it 'receives the mixin' do
|
61
|
+
source.class.ancestors.include?(Draper::ModelSupport)
|
62
|
+
end
|
63
|
+
end
|
58
64
|
|
59
65
|
it "should wrap source methods so they still accept blocks" do
|
60
66
|
subject.block{"marker"}.should == "marker"
|
61
67
|
end
|
62
|
-
|
68
|
+
|
63
69
|
context ".find" do
|
64
70
|
it "should lookup the associated model when passed an integer" do
|
65
71
|
pd = ProductDecorator.find(1)
|
66
72
|
pd.should be_instance_of(ProductDecorator)
|
67
73
|
pd.model.should be_instance_of(Product)
|
68
74
|
end
|
69
|
-
|
75
|
+
|
70
76
|
it "should lookup the associated model when passed a string" do
|
71
77
|
pd = ProductDecorator.find("1")
|
72
78
|
pd.should be_instance_of(ProductDecorator)
|
73
79
|
pd.model.should be_instance_of(Product)
|
74
80
|
end
|
75
81
|
end
|
76
|
-
|
82
|
+
|
77
83
|
context ".decorate" do
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
84
|
+
context "without any context" do
|
85
|
+
subject { Draper::Base.decorate(source) }
|
86
|
+
|
87
|
+
context "when given a collection of source objects" do
|
88
|
+
let(:source) { [Product.new, Product.new] }
|
89
|
+
|
90
|
+
its(:size) { should == source.size }
|
91
|
+
|
92
|
+
it "returns a collection of wrapped objects" do
|
93
|
+
subject.each{ |decorated| decorated.should be_instance_of(Draper::Base) }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context "when given a single source object" do
|
98
|
+
let(:source) { Product.new }
|
99
|
+
|
100
|
+
it { should be_instance_of(Draper::Base) }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context "with a context" do
|
105
|
+
let(:context) {{ :some => 'data' }}
|
106
|
+
|
107
|
+
subject { Draper::Base.decorate(source, context) }
|
108
|
+
|
109
|
+
context "when given a collection of source objects" do
|
110
|
+
let(:source) { [Product.new, Product.new] }
|
111
|
+
|
112
|
+
it "returns a collection of wrapped objects with the context" do
|
113
|
+
subject.each{ |decorated| decorated.context.should eq(context) }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context "when given a single source object" do
|
118
|
+
let(:source) { Product.new }
|
119
|
+
|
120
|
+
its(:context) { should eq(context) }
|
121
|
+
end
|
89
122
|
end
|
90
123
|
end
|
91
|
-
|
92
|
-
describe "a sample usage with denies" do
|
124
|
+
|
125
|
+
describe "a sample usage with denies" do
|
93
126
|
let(:subject_with_denies){ DecoratorWithDenies.new(source) }
|
94
|
-
|
127
|
+
|
95
128
|
it "should proxy methods not listed in denies" do
|
96
129
|
subject_with_denies.should respond_to(:hello_world)
|
97
130
|
end
|
98
|
-
|
131
|
+
|
99
132
|
it "should not echo methods specified with denies" do
|
100
133
|
subject_with_denies.should_not respond_to(:goodnight_moon)
|
101
134
|
end
|
102
135
|
|
103
136
|
it "should not clobber other decorators' methods" do
|
104
137
|
subject.should respond_to(:hello_world)
|
105
|
-
end
|
106
|
-
|
138
|
+
end
|
139
|
+
|
107
140
|
it "should not allow method_missing to circumvent a deny" do
|
108
141
|
expect{subject_with_denies.title}.to raise_error(NoMethodError)
|
109
|
-
end
|
142
|
+
end
|
110
143
|
end
|
111
|
-
|
144
|
+
|
112
145
|
describe "a sample usage with allows" do
|
113
146
|
let(:subject_with_allows){ DecoratorWithAllows.new(source) }
|
114
|
-
|
147
|
+
|
115
148
|
it "should echo the allowed method" do
|
116
149
|
subject_with_allows.should respond_to(:upcase)
|
117
150
|
end
|
118
|
-
|
151
|
+
|
119
152
|
it "should echo _only_ the allowed method" do
|
120
153
|
subject_with_allows.should_not respond_to(:downcase)
|
121
154
|
end
|
122
155
|
end
|
123
|
-
|
156
|
+
|
124
157
|
describe "invalid usages of allows and denies" do
|
125
158
|
let(:blank_allows){
|
126
|
-
class DecoratorWithInvalidAllows < Draper::Base
|
159
|
+
class DecoratorWithInvalidAllows < Draper::Base
|
127
160
|
allows
|
128
161
|
end
|
129
162
|
}
|
130
|
-
|
163
|
+
|
131
164
|
let(:blank_denies){
|
132
|
-
class DecoratorWithInvalidDenies < Draper::Base
|
165
|
+
class DecoratorWithInvalidDenies < Draper::Base
|
133
166
|
denies
|
134
167
|
end
|
135
168
|
}
|
136
|
-
|
169
|
+
|
137
170
|
let(:using_allows_then_denies){
|
138
|
-
class DecoratorWithAllowsAndDenies < Draper::Base
|
171
|
+
class DecoratorWithAllowsAndDenies < Draper::Base
|
139
172
|
allows :hello_world
|
140
173
|
denies :goodnight_moon
|
141
174
|
end
|
142
|
-
}
|
143
|
-
|
175
|
+
}
|
176
|
+
|
144
177
|
let(:using_denies_then_allows){
|
145
|
-
class DecoratorWithDeniesAndAllows < Draper::Base
|
178
|
+
class DecoratorWithDeniesAndAllows < Draper::Base
|
146
179
|
denies :goodnight_moon
|
147
|
-
allows :hello_world
|
180
|
+
allows :hello_world
|
148
181
|
end
|
149
182
|
}
|
150
183
|
|
151
184
|
it "should raise an exception for a blank allows" do
|
152
185
|
expect {blank_allows}.should raise_error(ArgumentError)
|
153
186
|
end
|
154
|
-
|
187
|
+
|
155
188
|
it "should raise an exception for a blank denies" do
|
156
189
|
expect {blank_denies}.should raise_error(ArgumentError)
|
157
190
|
end
|
158
|
-
|
191
|
+
|
159
192
|
it "should raise an exception for calling allows then denies" do
|
160
193
|
expect {using_allows_then_denies}.should raise_error(ArgumentError)
|
161
194
|
end
|
162
|
-
|
195
|
+
|
163
196
|
it "should raise an exception for calling denies then allows" do
|
164
197
|
expect {using_denies_then_allows}.should raise_error(ArgumentError)
|
165
198
|
end
|
166
199
|
end
|
167
|
-
|
200
|
+
|
168
201
|
context "in a Rails application" do
|
169
202
|
let(:decorator){ DecoratorWithApplicationHelper.decorate(Object.new) }
|
170
|
-
|
203
|
+
|
171
204
|
it "should have access to ApplicationHelper helpers" do
|
172
205
|
decorator.uses_hello_world == "Hello, World!"
|
173
206
|
end
|
174
|
-
|
207
|
+
|
175
208
|
it "should be able to use the content_tag helper" do
|
176
209
|
decorator.sample_content.to_s.should == "<span>Hello, World!</span>"
|
177
210
|
end
|
178
|
-
|
211
|
+
|
179
212
|
it "should be able to use the link_to helper" do
|
180
213
|
decorator.sample_link.should == "<a href=\"/World\">Hello</a>"
|
181
214
|
end
|
182
|
-
|
215
|
+
|
183
216
|
it "should be able to use the pluralize helper" do
|
184
217
|
decorator.sample_truncate.should == "Once..."
|
185
218
|
end
|
186
219
|
end
|
187
|
-
end
|
220
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'bundler'
|
3
|
-
|
3
|
+
|
4
|
+
require 'rspec'
|
5
|
+
begin
|
6
|
+
require 'cover_me'
|
7
|
+
rescue LoadError
|
8
|
+
# Silently fail
|
9
|
+
end
|
4
10
|
require './spec/samples/application_helper.rb'
|
5
11
|
Bundler.require
|
6
12
|
Dir.glob('./spec/samples/*') {|file| require file}
|
metadata
CHANGED
@@ -1,127 +1,123 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: draper
|
3
|
-
version: !ruby/object:Gem::Version
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.7.0
|
4
5
|
prerelease:
|
5
|
-
version: 0.5.0
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
8
|
- Jeff Casimir
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
dependencies:
|
16
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2011-08-17 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
17
15
|
name: rake
|
18
|
-
|
19
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: &70309492944640 !ruby/object:Gem::Requirement
|
20
17
|
none: false
|
21
|
-
requirements:
|
22
|
-
- -
|
23
|
-
- !ruby/object:Gem::Version
|
18
|
+
requirements:
|
19
|
+
- - =
|
20
|
+
- !ruby/object:Gem::Version
|
24
21
|
version: 0.8.7
|
25
22
|
type: :development
|
26
|
-
version_requirements: *id001
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rspec
|
29
23
|
prerelease: false
|
30
|
-
|
24
|
+
version_requirements: *70309492944640
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &70309492944140 !ruby/object:Gem::Requirement
|
31
28
|
none: false
|
32
|
-
requirements:
|
29
|
+
requirements:
|
33
30
|
- - ~>
|
34
|
-
- !ruby/object:Gem::Version
|
31
|
+
- !ruby/object:Gem::Version
|
35
32
|
version: 2.0.1
|
36
33
|
type: :development
|
37
|
-
version_requirements: *id002
|
38
|
-
- !ruby/object:Gem::Dependency
|
39
|
-
name: activesupport
|
40
34
|
prerelease: false
|
41
|
-
|
35
|
+
version_requirements: *70309492944140
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: activesupport
|
38
|
+
requirement: &70309492943680 !ruby/object:Gem::Requirement
|
42
39
|
none: false
|
43
|
-
requirements:
|
40
|
+
requirements:
|
44
41
|
- - ~>
|
45
|
-
- !ruby/object:Gem::Version
|
42
|
+
- !ruby/object:Gem::Version
|
46
43
|
version: 3.0.9
|
47
44
|
type: :development
|
48
|
-
version_requirements: *id003
|
49
|
-
- !ruby/object:Gem::Dependency
|
50
|
-
name: actionpack
|
51
45
|
prerelease: false
|
52
|
-
|
46
|
+
version_requirements: *70309492943680
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: actionpack
|
49
|
+
requirement: &70309492943220 !ruby/object:Gem::Requirement
|
53
50
|
none: false
|
54
|
-
requirements:
|
51
|
+
requirements:
|
55
52
|
- - ~>
|
56
|
-
- !ruby/object:Gem::Version
|
53
|
+
- !ruby/object:Gem::Version
|
57
54
|
version: 3.0.9
|
58
55
|
type: :development
|
59
|
-
version_requirements: *id004
|
60
|
-
- !ruby/object:Gem::Dependency
|
61
|
-
name: ruby-debug19
|
62
56
|
prerelease: false
|
63
|
-
|
57
|
+
version_requirements: *70309492943220
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: guard
|
60
|
+
requirement: &70309492942840 !ruby/object:Gem::Requirement
|
64
61
|
none: false
|
65
|
-
requirements:
|
66
|
-
- -
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version:
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
69
66
|
type: :development
|
70
|
-
version_requirements: *id005
|
71
|
-
- !ruby/object:Gem::Dependency
|
72
|
-
name: guard
|
73
67
|
prerelease: false
|
74
|
-
|
68
|
+
version_requirements: *70309492942840
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: guard-rspec
|
71
|
+
requirement: &70309492942380 !ruby/object:Gem::Requirement
|
75
72
|
none: false
|
76
|
-
requirements:
|
77
|
-
- -
|
78
|
-
- !ruby/object:Gem::Version
|
79
|
-
version:
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
80
77
|
type: :development
|
81
|
-
version_requirements: *id006
|
82
|
-
- !ruby/object:Gem::Dependency
|
83
|
-
name: guard-rspec
|
84
78
|
prerelease: false
|
85
|
-
|
79
|
+
version_requirements: *70309492942380
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: rb-fsevent
|
82
|
+
requirement: &70309492941960 !ruby/object:Gem::Requirement
|
86
83
|
none: false
|
87
|
-
requirements:
|
88
|
-
- -
|
89
|
-
- !ruby/object:Gem::Version
|
90
|
-
version:
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
91
88
|
type: :development
|
92
|
-
version_requirements: *id007
|
93
|
-
- !ruby/object:Gem::Dependency
|
94
|
-
name: rb-fsevent
|
95
89
|
prerelease: false
|
96
|
-
|
90
|
+
version_requirements: *70309492941960
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: ruby-debug19
|
93
|
+
requirement: &70309492941520 !ruby/object:Gem::Requirement
|
97
94
|
none: false
|
98
|
-
requirements:
|
99
|
-
- -
|
100
|
-
- !ruby/object:Gem::Version
|
101
|
-
version:
|
95
|
+
requirements:
|
96
|
+
- - ! '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
102
99
|
type: :development
|
103
|
-
version_requirements: *id008
|
104
|
-
- !ruby/object:Gem::Dependency
|
105
|
-
name: cover_me
|
106
100
|
prerelease: false
|
107
|
-
|
101
|
+
version_requirements: *70309492941520
|
102
|
+
- !ruby/object:Gem::Dependency
|
103
|
+
name: cover_me
|
104
|
+
requirement: &70309492941020 !ruby/object:Gem::Requirement
|
108
105
|
none: false
|
109
|
-
requirements:
|
110
|
-
- -
|
111
|
-
- !ruby/object:Gem::Version
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
112
109
|
version: 1.0.0.rc6
|
113
110
|
type: :development
|
114
|
-
|
115
|
-
|
116
|
-
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: *70309492941020
|
113
|
+
description: Draper reimagines the role of helpers in the view layer of a Rails application,
|
114
|
+
allowing an object-oriented approach rather than procedural.
|
115
|
+
email:
|
117
116
|
- jeff@casimircreative.com
|
118
117
|
executables: []
|
119
|
-
|
120
118
|
extensions: []
|
121
|
-
|
122
119
|
extra_rdoc_files: []
|
123
|
-
|
124
|
-
files:
|
120
|
+
files:
|
125
121
|
- .gitignore
|
126
122
|
- Gemfile
|
127
123
|
- Guardfile
|
@@ -132,12 +128,15 @@ files:
|
|
132
128
|
- lib/draper/all_helpers.rb
|
133
129
|
- lib/draper/base.rb
|
134
130
|
- lib/draper/lazy_helpers.rb
|
131
|
+
- lib/draper/model_support.rb
|
135
132
|
- lib/draper/system.rb
|
136
133
|
- lib/draper/version.rb
|
137
|
-
- lib/generators/draper/
|
138
|
-
- lib/generators/draper/
|
139
|
-
- lib/generators/draper/
|
134
|
+
- lib/generators/draper/decorator/USAGE
|
135
|
+
- lib/generators/draper/decorator/decorator_generator.rb
|
136
|
+
- lib/generators/draper/decorator/templates/application_decorator.rb
|
137
|
+
- lib/generators/draper/decorator/templates/decorator.rb
|
140
138
|
- spec/base_spec.rb
|
139
|
+
- spec/draper/model_support_spec.rb
|
141
140
|
- spec/samples/active_record.rb
|
142
141
|
- spec/samples/application_controller.rb
|
143
142
|
- spec/samples/application_helper.rb
|
@@ -147,36 +146,33 @@ files:
|
|
147
146
|
- spec/samples/product.rb
|
148
147
|
- spec/samples/product_decorator.rb
|
149
148
|
- spec/spec_helper.rb
|
150
|
-
has_rdoc: true
|
151
149
|
homepage: http://github.com/jcasimir/draper
|
152
150
|
licenses: []
|
153
|
-
|
154
151
|
post_install_message:
|
155
152
|
rdoc_options: []
|
156
|
-
|
157
|
-
require_paths:
|
153
|
+
require_paths:
|
158
154
|
- lib
|
159
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
155
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
160
156
|
none: false
|
161
|
-
requirements:
|
162
|
-
- -
|
163
|
-
- !ruby/object:Gem::Version
|
164
|
-
version:
|
165
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - ! '>='
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
166
162
|
none: false
|
167
|
-
requirements:
|
168
|
-
- -
|
169
|
-
- !ruby/object:Gem::Version
|
170
|
-
version:
|
163
|
+
requirements:
|
164
|
+
- - ! '>='
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
171
167
|
requirements: []
|
172
|
-
|
173
168
|
rubyforge_project: draper
|
174
|
-
rubygems_version: 1.6
|
169
|
+
rubygems_version: 1.8.6
|
175
170
|
signing_key:
|
176
171
|
specification_version: 3
|
177
172
|
summary: Decorator pattern implmentation for Rails.
|
178
|
-
test_files:
|
173
|
+
test_files:
|
179
174
|
- spec/base_spec.rb
|
175
|
+
- spec/draper/model_support_spec.rb
|
180
176
|
- spec/samples/active_record.rb
|
181
177
|
- spec/samples/application_controller.rb
|
182
178
|
- spec/samples/application_helper.rb
|
@@ -1,10 +0,0 @@
|
|
1
|
-
module Draper
|
2
|
-
class ModelGenerator < Rails::Generators::NamedBase
|
3
|
-
source_root File.expand_path('../templates', __FILE__)
|
4
|
-
|
5
|
-
def build_model
|
6
|
-
empty_directory "app/decorators"
|
7
|
-
template 'model.rb', "app/decorators/#{singular_name}_decorator.rb"
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
class <%= singular_name.camelize %>Decorator < Draper::Base
|
2
|
-
decorates :<%= singular_name.to_sym %>
|
3
|
-
|
4
|
-
# Helpers from Rails an Your Application
|
5
|
-
# You can access any helper via a proxy to ApplicationController
|
6
|
-
#
|
7
|
-
# Normal Usage: helpers.number_to_currency(2)
|
8
|
-
# Abbreviated : h.number_to_currency(2)
|
9
|
-
#
|
10
|
-
# You can optionally enable "lazy helpers" by including this module:
|
11
|
-
# include Draper::LazyHelpers
|
12
|
-
# Then use the helpers with no prefix:
|
13
|
-
# number_to_currency(2)
|
14
|
-
|
15
|
-
# Wrapper Methods
|
16
|
-
# Control access to the wrapped subject's methods using one of the following:
|
17
|
-
#
|
18
|
-
# To allow only the listed methods (whitelist):
|
19
|
-
# allows :method1, :method2
|
20
|
-
#
|
21
|
-
# To allow everything except the listed methods (blacklist):
|
22
|
-
# denies :method1, :method2
|
23
|
-
|
24
|
-
# Presentation Methods
|
25
|
-
# Define your own instance methods. Ex:
|
26
|
-
# def formatted_created_at
|
27
|
-
# content_tag :span, created_at.strftime("%A")
|
28
|
-
# end
|
29
|
-
end
|