draper 0.18.0 → 1.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.markdown +8 -0
- data/Gemfile +4 -0
- data/Rakefile +50 -31
- data/Readme.markdown +42 -48
- data/draper.gemspec +2 -1
- data/lib/draper.rb +42 -7
- data/lib/draper/collection_decorator.rb +88 -0
- data/lib/draper/decoratable.rb +44 -0
- data/lib/draper/decorated_association.rb +55 -0
- data/lib/draper/decorator.rb +208 -0
- data/lib/draper/finders.rb +44 -0
- data/lib/draper/helper_proxy.rb +16 -0
- data/lib/draper/railtie.rb +17 -21
- data/lib/draper/security.rb +48 -0
- data/lib/draper/tasks/tu.rake +5 -0
- data/lib/draper/test/minitest_integration.rb +1 -1
- data/lib/draper/test/test_unit_integration.rb +9 -0
- data/lib/draper/version.rb +1 -1
- data/lib/draper/view_context.rb +6 -13
- data/lib/draper/view_helpers.rb +36 -0
- data/lib/generators/decorator/decorator_generator.rb +1 -1
- data/lib/generators/decorator/templates/decorator.rb +0 -1
- data/spec/draper/collection_decorator_spec.rb +240 -0
- data/spec/draper/decoratable_spec.rb +164 -0
- data/spec/draper/decorated_association_spec.rb +130 -0
- data/spec/draper/decorator_spec.rb +497 -0
- data/spec/draper/finders_spec.rb +156 -0
- data/spec/draper/helper_proxy_spec.rb +12 -0
- data/spec/draper/security_spec.rb +158 -0
- data/spec/draper/view_helpers_spec.rb +41 -0
- data/spec/dummy/.rspec +2 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +4 -0
- data/spec/dummy/app/controllers/localized_urls.rb +5 -0
- data/spec/dummy/app/controllers/posts_controller.rb +17 -0
- data/spec/dummy/app/decorators/post_decorator.rb +25 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/application_mailer.rb +3 -0
- data/spec/dummy/app/mailers/post_mailer.rb +9 -0
- data/spec/dummy/app/models/post.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +11 -0
- data/spec/dummy/app/views/post_mailer/decorated_email.html.erb +1 -0
- data/spec/dummy/app/views/posts/_post.html.erb +19 -0
- data/spec/dummy/app/views/posts/show.html.erb +1 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +64 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +34 -0
- data/spec/dummy/config/environments/production.rb +55 -0
- data/spec/dummy/config/environments/test.rb +32 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +7 -0
- data/spec/dummy/db/migrate/20121019115657_create_posts.rb +8 -0
- data/spec/dummy/db/schema.rb +21 -0
- data/spec/dummy/db/seeds.rb +2 -0
- data/spec/dummy/lib/tasks/spec.rake +5 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/spec/decorators/post_decorator_spec.rb +23 -0
- data/spec/dummy/spec/mailers/post_mailer_spec.rb +29 -0
- data/spec/dummy/spec/spec_helper.rb +9 -0
- data/spec/generators/decorator/decorator_generator_spec.rb +3 -4
- data/spec/integration/integration_spec.rb +33 -0
- data/{performance → spec/performance}/active_record.rb +0 -0
- data/{performance/bechmark.rb → spec/performance/benchmark.rb} +0 -0
- data/{performance → spec/performance}/decorators.rb +2 -4
- data/{performance → spec/performance}/models.rb +0 -0
- data/spec/spec_helper.rb +20 -41
- data/spec/support/action_controller.rb +12 -0
- data/spec/support/active_model.rb +7 -0
- data/spec/support/{samples/active_record.rb → active_record.rb} +5 -0
- data/spec/support/{samples → decorators}/decorator_with_application_helper.rb +1 -1
- data/spec/support/decorators/namespaced_product_decorator.rb +5 -0
- data/spec/support/decorators/non_active_model_product_decorator.rb +2 -0
- data/spec/support/decorators/product_decorator.rb +19 -0
- data/spec/support/{samples → decorators}/products_decorator.rb +1 -1
- data/spec/support/decorators/some_thing_decorator.rb +2 -0
- data/spec/support/{samples → decorators}/specific_product_decorator.rb +0 -2
- data/spec/support/{samples → decorators}/widget_decorator.rb +0 -0
- data/spec/support/dummy_app.rb +84 -0
- data/spec/support/matchers/have_text.rb +50 -0
- data/spec/support/{samples → models}/namespaced_product.rb +1 -3
- data/spec/support/{samples → models}/non_active_model_product.rb +1 -0
- data/spec/support/{samples → models}/product.rb +13 -2
- data/spec/support/models/some_thing.rb +5 -0
- data/spec/support/models/uninferrable_decorator_model.rb +3 -0
- data/spec/support/{samples → models}/widget.rb +0 -0
- metadata +185 -68
- data/lib/draper/active_model_support.rb +0 -27
- data/lib/draper/base.rb +0 -312
- data/lib/draper/decorated_enumerable_proxy.rb +0 -90
- data/lib/draper/model_support.rb +0 -25
- data/lib/draper/rspec_integration.rb +0 -2
- data/lib/draper/system.rb +0 -18
- data/spec/draper/base_spec.rb +0 -873
- data/spec/draper/decorated_enumerable_proxy_spec.rb +0 -45
- data/spec/draper/model_support_spec.rb +0 -48
- data/spec/support/samples/decorator.rb +0 -5
- data/spec/support/samples/decorator_with_allows.rb +0 -3
- data/spec/support/samples/decorator_with_denies.rb +0 -3
- data/spec/support/samples/decorator_with_denies_all.rb +0 -3
- data/spec/support/samples/decorator_with_multiple_allows.rb +0 -4
- data/spec/support/samples/decorator_with_special_methods.rb +0 -13
- data/spec/support/samples/enumerable_proxy.rb +0 -3
- data/spec/support/samples/namespaced_product_decorator.rb +0 -7
- data/spec/support/samples/product_decorator.rb +0 -7
- data/spec/support/samples/sequel_product.rb +0 -13
- data/spec/support/samples/some_thing.rb +0 -2
- data/spec/support/samples/some_thing_decorator.rb +0 -3
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.markdown
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Draper Changelog
|
2
2
|
|
3
|
+
## 0.18.0
|
4
|
+
|
5
|
+
* [Adds the ability to decorate an enumerable proxy](https://github.com/drapergem/draper/commit/67c7125192740a7586a3a635acd735ae01b97837)
|
6
|
+
|
7
|
+
* Many bug fixes.
|
8
|
+
|
9
|
+
* Last version of Draper in the 0.x series.
|
10
|
+
|
3
11
|
## 0.17.0
|
4
12
|
|
5
13
|
* [Fix earlier fix of `view_context` priming](https://github.com/jcasimir/draper/commit/5da44336)
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -1,47 +1,66 @@
|
|
1
|
-
require 'bundler/gem_tasks'
|
2
1
|
require 'rake'
|
2
|
+
require 'bundler/gem_tasks'
|
3
3
|
require 'rspec/core/rake_task'
|
4
4
|
|
5
|
-
|
5
|
+
def run_in_dummy_app(command)
|
6
|
+
success = system("cd spec/dummy && #{command}")
|
7
|
+
raise "#{command} failed" unless success
|
8
|
+
end
|
9
|
+
|
10
|
+
task "default" => "ci"
|
11
|
+
|
12
|
+
desc "Run all tests for CI"
|
13
|
+
task "ci" => "spec"
|
6
14
|
|
7
|
-
|
15
|
+
desc "Run all specs"
|
16
|
+
task "spec" => "spec:all"
|
8
17
|
|
9
|
-
|
10
|
-
|
18
|
+
namespace "spec" do
|
19
|
+
task "all" => ["draper", "generators", "minitest-rails", "integration"]
|
11
20
|
|
12
|
-
|
13
|
-
|
14
|
-
|
21
|
+
def spec_task(name)
|
22
|
+
desc "Run #{name} specs"
|
23
|
+
RSpec::Core::RakeTask.new(name) do |t|
|
24
|
+
t.pattern = "spec/#{name}/**/*_spec.rb"
|
15
25
|
end
|
16
26
|
end
|
17
27
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
28
|
+
spec_task "draper"
|
29
|
+
spec_task "generators"
|
30
|
+
spec_task "minitest-rails"
|
22
31
|
|
23
|
-
|
24
|
-
|
25
|
-
task :cleanup do
|
26
|
-
rm_rf 'coverage.data'
|
27
|
-
rm_rf 'coverage'
|
28
|
-
end
|
32
|
+
desc "Run integration specs"
|
33
|
+
task "integration" => ["db:setup", "integration:all"]
|
29
34
|
|
30
|
-
|
31
|
-
task
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
require 'cover_me'
|
37
|
-
CoverMe.complete!
|
35
|
+
namespace "integration" do
|
36
|
+
task "all" => ["development", "production", "test"]
|
37
|
+
|
38
|
+
["development", "production"].each do |environment|
|
39
|
+
task environment do
|
40
|
+
Rake::Task["spec:integration:run"].execute environment
|
38
41
|
end
|
39
42
|
end
|
40
|
-
end
|
41
43
|
|
42
|
-
|
44
|
+
task "run" do |t, environment|
|
45
|
+
puts "Running integration specs in #{environment}"
|
46
|
+
|
47
|
+
ENV["RAILS_ENV"] = environment
|
48
|
+
success = system("rspec spec/integration")
|
43
49
|
|
44
|
-
|
45
|
-
|
50
|
+
raise "Integration specs failed in #{environment}" unless success
|
51
|
+
end
|
46
52
|
|
47
|
-
task "
|
53
|
+
task "test" do
|
54
|
+
run_in_dummy_app "rake"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
namespace "db" do
|
60
|
+
desc "Set up databases for integration testing"
|
61
|
+
task "setup" do
|
62
|
+
run_in_dummy_app "rm -f db/*.sqlite3"
|
63
|
+
run_in_dummy_app "RAILS_ENV=development rake db:schema:load db:seed db:test:prepare"
|
64
|
+
run_in_dummy_app "RAILS_ENV=production rake db:schema:load db:seed"
|
65
|
+
end
|
66
|
+
end
|
data/Readme.markdown
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Draper: View Models for Rails
|
2
2
|
|
3
|
-
[![TravisCI Build Status](https://secure.travis-ci.org/
|
3
|
+
[![TravisCI Build Status](https://secure.travis-ci.org/drapergem/draper.png?branch=master)](http://travis-ci.org/drapergem/draper)
|
4
|
+
[![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/drapergem/draper)
|
4
5
|
|
5
6
|
## Quick Start
|
6
7
|
|
@@ -11,11 +12,9 @@
|
|
11
12
|
1. `h` to proxy to Rails/application helpers like `h.current_user`
|
12
13
|
2. the name of your decorated model to access the wrapped object like `article.created_at`
|
13
14
|
5. Wrap models in your controller with the decorator using:
|
14
|
-
1. `.
|
15
|
-
ex: `ArticleDecorator.find(1)`
|
16
|
-
2. `.decorate` method with a single object or collection,
|
15
|
+
1. `.decorate` method with a single object or collection,
|
17
16
|
ex: `ArticleDecorator.decorate(Article.all)`
|
18
|
-
|
17
|
+
2. `.new` method with single object
|
19
18
|
ex: `ArticleDecorator.new(Article.first)`
|
20
19
|
6. Call decorator methods from your view templates
|
21
20
|
ex: `<%= @article_decorator.created_at %>`
|
@@ -23,7 +22,7 @@
|
|
23
22
|
If you need common methods in your decorators, create an `app/decorators/application_decorator.rb`:
|
24
23
|
|
25
24
|
``` ruby
|
26
|
-
class ApplicationDecorator < Draper::
|
25
|
+
class ApplicationDecorator < Draper::Decorator
|
27
26
|
# your methods go here
|
28
27
|
end
|
29
28
|
```
|
@@ -45,9 +44,7 @@ Why hate normal helpers? In Ruby/Rails we approach everything from an Object-Ori
|
|
45
44
|
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:
|
46
45
|
|
47
46
|
```ruby
|
48
|
-
class ArticleDecorator < Draper::
|
49
|
-
decorates :article
|
50
|
-
|
47
|
+
class ArticleDecorator < Draper::Decorator
|
51
48
|
def published_at
|
52
49
|
date = h.content_tag(:span, article.published_at.strftime("%A, %B %e").squeeze(" "), :class => 'date')
|
53
50
|
time = h.content_tag(:span, article.published_at.strftime("%l:%M%p"), :class => 'time').delete(" ")
|
@@ -68,8 +65,6 @@ When you use a decorator you have the power of a Ruby object but it's a part of
|
|
68
65
|
|
69
66
|
```ruby
|
70
67
|
class ArticleDecorator < ApplicationDecorator
|
71
|
-
decorates :article
|
72
|
-
|
73
68
|
ADMIN_VISIBLE_ATTRIBUTES = [:title, :body, :author, :status]
|
74
69
|
PUBLIC_VISIBLE_ATTRIBUTES = [:title, :body]
|
75
70
|
|
@@ -90,7 +85,6 @@ The `denies` method takes a blacklist approach. For instance:
|
|
90
85
|
|
91
86
|
```ruby
|
92
87
|
class ArticleDecorator < ApplicationDecorator
|
93
|
-
decorates :article
|
94
88
|
denies :title
|
95
89
|
end
|
96
90
|
```
|
@@ -98,11 +92,19 @@ end
|
|
98
92
|
Then, to test it:
|
99
93
|
|
100
94
|
```irb
|
101
|
-
> ad = ArticleDecorator.find(1)
|
102
|
-
=> #<ArticleDecorator:0x000001020d7728 @model=#<Article id: 1, title: "Hello, World">>
|
95
|
+
> ad = ArticleDecorator.new(Article.find(1))
|
96
|
+
=> #<ArticleDecorator:0x000001020d7728 @model=#<Article id: 1, title: "Hello, World">>
|
103
97
|
> ad.title
|
104
98
|
NoMethodError: undefined method `title' for #<ArticleDecorator:0x000001020d7728>
|
105
|
-
```
|
99
|
+
```
|
100
|
+
|
101
|
+
You may also blacklist all methods by using `denies_all`:
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
class ArticleDecorate < ApplicationDecorator
|
105
|
+
denies_all
|
106
|
+
end
|
107
|
+
```
|
106
108
|
|
107
109
|
#### Using `allows`
|
108
110
|
|
@@ -110,14 +112,13 @@ A better approach is to define a whitelist using `allows`:
|
|
110
112
|
|
111
113
|
```ruby
|
112
114
|
class ArticleDecorator < ApplicationDecorator
|
113
|
-
decorates :article
|
114
115
|
allows :title, :description
|
115
116
|
end
|
116
117
|
```
|
117
118
|
|
118
119
|
```irb
|
119
|
-
> ad = ArticleDecorator.find(1)
|
120
|
-
=> #<ArticleDecorator:0x000001020d7728 @model=#<Article id: 1, title: "Hello, World">>
|
120
|
+
> ad = ArticleDecorator.new(Article.find(1))
|
121
|
+
=> #<ArticleDecorator:0x000001020d7728 @model=#<Article id: 1, title: "Hello, World">>
|
121
122
|
> ad.title
|
122
123
|
=> "Hello, World"
|
123
124
|
> ad.created_at
|
@@ -146,14 +147,12 @@ rails generate decorator article
|
|
146
147
|
|
147
148
|
### Writing Methods
|
148
149
|
|
149
|
-
Open the decorator model (ex: `app/decorators/article_decorator.rb`) and add normal instance methods. To access the wrapped source object, use
|
150
|
+
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:
|
150
151
|
|
151
152
|
```ruby
|
152
|
-
class ArticleDecorator < Draper::
|
153
|
-
decorates :article
|
154
|
-
|
153
|
+
class ArticleDecorator < Draper::Decorator
|
155
154
|
def author_name
|
156
|
-
|
155
|
+
model.author.first_name + " " + model.author.last_name
|
157
156
|
end
|
158
157
|
end
|
159
158
|
```
|
@@ -163,9 +162,7 @@ end
|
|
163
162
|
You probably want to make use of Rails helpers and those defined in your application. Use the `helpers` or `h` method proxy:
|
164
163
|
|
165
164
|
```ruby
|
166
|
-
class ArticleDecorator < Draper::
|
167
|
-
decorates :article
|
168
|
-
|
165
|
+
class ArticleDecorator < Draper::Decorator
|
169
166
|
def published_at
|
170
167
|
date = h.content_tag(:span, article.published_at.strftime("%A, %B %e").squeeze(" "), :class => 'date')
|
171
168
|
time = h.content_tag(:span, article.published_at.strftime("%l:%M%p"), :class => 'time').delete(" ")
|
@@ -179,10 +176,9 @@ end
|
|
179
176
|
Hate seeing that `h.` proxy all over? Willing to mix a bazillion methods into your decorator? Then try lazy helpers:
|
180
177
|
|
181
178
|
```ruby
|
182
|
-
class ArticleDecorator < Draper::
|
183
|
-
decorates :article
|
179
|
+
class ArticleDecorator < Draper::Decorator
|
184
180
|
include Draper::LazyHelpers
|
185
|
-
|
181
|
+
|
186
182
|
def published_at
|
187
183
|
date = content_tag(:span, article.published_at.strftime("%A, %B %e").squeeze(" "), :class => 'date')
|
188
184
|
time = content_tag(:span, article.published_at.strftime("%l:%M%p"), :class => 'time').delete(" ")
|
@@ -207,12 +203,6 @@ ArticleDecorator.new(Article.find(params[:id]))
|
|
207
203
|
ArticleDecorator.decorate(Article.first) # Returns one instance of ArticleDecorator
|
208
204
|
ArticleDecorator.decorate(Article.all) # Returns an enumeration proxy of ArticleDecorator instances
|
209
205
|
```
|
210
|
-
|
211
|
-
* Call `.find` to automatically do a lookup on the `decorates` class:
|
212
|
-
|
213
|
-
```ruby
|
214
|
-
ArticleDecorator.find(1)
|
215
|
-
```
|
216
206
|
|
217
207
|
### In Your Views
|
218
208
|
|
@@ -224,11 +214,11 @@ Use the new methods in your views like any other model method (ex: `@article.pub
|
|
224
214
|
|
225
215
|
### Integration with RSpec
|
226
216
|
|
227
|
-
Using the provided generator, Draper will place specs for your new decorator in `spec/decorators/`.
|
217
|
+
Using the provided generator, Draper will place specs for your new decorator in `spec/decorators/`.
|
228
218
|
|
229
219
|
By default, specs in `spec/decorators` will be tagged as `type => :decorator`. Any spec tagged as `decorator` will make helpers available to the decorator.
|
230
220
|
|
231
|
-
If your decorator specs live somewhere else, which they shouldn't, make sure to tag them with `type => :decorator`. If you don't tag them, Draper's helpers won't be available to your decorator while testing.
|
221
|
+
If your decorator specs live somewhere else, which they shouldn't, make sure to tag them with `type => :decorator`. If you don't tag them, Draper's helpers won't be available to your decorator while testing.
|
232
222
|
|
233
223
|
Note: If you're using Spork, you need to `require 'draper/test/rspec_integration'` in your Spork.prefork block.
|
234
224
|
|
@@ -284,7 +274,7 @@ Then you need to perform the wrapping in your controller. Here's the simplest me
|
|
284
274
|
```ruby
|
285
275
|
class ArticlesController < ApplicationController
|
286
276
|
def show
|
287
|
-
@article = ArticleDecorator.find
|
277
|
+
@article = ArticleDecorator.new(Article.find(params[:id]))
|
288
278
|
end
|
289
279
|
end
|
290
280
|
```
|
@@ -298,9 +288,7 @@ Then within your views you can utilize both the normal data methods and your new
|
|
298
288
|
Ta-da! Object-oriented data formatting for your view layer. Below is the complete decorator with extra comments removed:
|
299
289
|
|
300
290
|
```ruby
|
301
|
-
class ArticleDecorator < Draper::
|
302
|
-
decorates :article
|
303
|
-
|
291
|
+
class ArticleDecorator < Draper::Decorator
|
304
292
|
def published_at
|
305
293
|
date = h.content_tag(:span, article.published_at.strftime("%A, %B %e").squeeze(" "), :class => 'date')
|
306
294
|
time = h.content_tag(:span, article.published_at.strftime("%l:%M%p"), :class => 'time').delete(" ")
|
@@ -314,14 +302,11 @@ end
|
|
314
302
|
Add a `decorates_association :association_name` to gain access to a decorated version of your target association.
|
315
303
|
|
316
304
|
```ruby
|
317
|
-
class ArticleDecorator < Draper::
|
318
|
-
|
319
|
-
decorates_association :author # belongs_to :author association
|
305
|
+
class ArticleDecorator < Draper::Decorator
|
306
|
+
decorates_association :author # belongs_to :author association
|
320
307
|
end
|
321
308
|
|
322
|
-
class AuthorDecorator < Draper::
|
323
|
-
decorates :author
|
324
|
-
|
309
|
+
class AuthorDecorator < Draper::Decorator
|
325
310
|
def fancy_name
|
326
311
|
"#{model.title}. #{model.first_name} #{model.middle_name[0]}. #{model.last_name}"
|
327
312
|
end
|
@@ -346,7 +331,6 @@ end
|
|
346
331
|
|
347
332
|
# app/decorators/user_decorator.rb
|
348
333
|
class UserDecorator < ApplicationDecorator
|
349
|
-
decorates :user
|
350
334
|
end
|
351
335
|
```
|
352
336
|
|
@@ -366,6 +350,16 @@ For automatic decoration, check out [decorates_before_rendering](https://github.
|
|
366
350
|
4. Push to the branch (`git push origin my_awesome_branch`)
|
367
351
|
5. Send pull request
|
368
352
|
|
353
|
+
## Running tests
|
354
|
+
|
355
|
+
If it's the first time you want to run the tests, start by creating the dummy database:
|
356
|
+
|
357
|
+
```
|
358
|
+
$ rake db:migrate
|
359
|
+
```
|
360
|
+
|
361
|
+
You can then run tests by executing `rake`.
|
362
|
+
|
369
363
|
## Contributors
|
370
364
|
|
371
365
|
Draper was conceived by Jeff Casimir and heavily refined by Steve Klabnik and a great community of open source contributors.
|
data/draper.gemspec
CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.version = Draper::VERSION
|
8
8
|
s.authors = ["Jeff Casimir", "Steve Klabnik"]
|
9
9
|
s.email = ["jeff@casimircreative.com", "steve@steveklabnik.com"]
|
10
|
-
s.homepage = "http://github.com/
|
10
|
+
s.homepage = "http://github.com/drapergem/draper"
|
11
11
|
s.summary = "Decorator pattern implementation for Rails."
|
12
12
|
s.description = "Draper implements a decorator or presenter pattern for Rails applications."
|
13
13
|
s.rubyforge_project = "draper"
|
@@ -25,4 +25,5 @@ Gem::Specification.new do |s|
|
|
25
25
|
s.add_development_dependency 'yard'
|
26
26
|
s.add_development_dependency 'minitest-rails', '~> 0.2'
|
27
27
|
s.add_development_dependency 'minitest', '~> 3.0' if RUBY_PLATFORM == "java"
|
28
|
+
s.add_development_dependency 'capybara'
|
28
29
|
end
|
data/lib/draper.rb
CHANGED
@@ -1,16 +1,51 @@
|
|
1
1
|
require 'action_view'
|
2
2
|
|
3
3
|
require 'draper/version'
|
4
|
-
require 'draper/
|
5
|
-
require 'draper/
|
6
|
-
require 'draper/
|
4
|
+
require 'draper/view_helpers'
|
5
|
+
require 'draper/finders'
|
6
|
+
require 'draper/decorator'
|
7
|
+
require 'draper/helper_proxy'
|
7
8
|
require 'draper/lazy_helpers'
|
8
|
-
require 'draper/
|
9
|
+
require 'draper/decoratable'
|
10
|
+
require 'draper/decorated_association'
|
11
|
+
require 'draper/security'
|
9
12
|
require 'draper/helper_support'
|
10
13
|
require 'draper/view_context'
|
11
|
-
require 'draper/
|
14
|
+
require 'draper/collection_decorator'
|
12
15
|
require 'draper/railtie' if defined?(Rails)
|
13
16
|
|
14
17
|
# Test Support
|
15
|
-
require 'draper/test/rspec_integration'
|
16
|
-
require 'draper/test/minitest_integration'
|
18
|
+
require 'draper/test/rspec_integration' if defined?(RSpec) and RSpec.respond_to?(:configure)
|
19
|
+
require 'draper/test/minitest_integration' if defined?(MiniTest::Rails)
|
20
|
+
require 'draper/test/test_unit_integration'
|
21
|
+
|
22
|
+
module Draper
|
23
|
+
def self.setup_action_controller(base)
|
24
|
+
base.class_eval do
|
25
|
+
include Draper::ViewContext
|
26
|
+
extend Draper::HelperSupport
|
27
|
+
before_filter ->(controller) {
|
28
|
+
Draper::ViewContext.current = nil
|
29
|
+
Draper::ViewContext.current_controller = controller
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.setup_action_mailer(base)
|
35
|
+
base.class_eval do
|
36
|
+
include Draper::ViewContext
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.setup_active_record(base)
|
41
|
+
base.class_eval do
|
42
|
+
include Draper::Decoratable
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class UninferrableDecoratorError < NameError
|
47
|
+
def initialize(klass)
|
48
|
+
super("Could not infer a decorator for #{klass}.")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Draper
|
2
|
+
class CollectionDecorator
|
3
|
+
include Enumerable
|
4
|
+
include ViewHelpers
|
5
|
+
|
6
|
+
attr_accessor :source, :options, :decorator_class
|
7
|
+
alias_method :to_source, :source
|
8
|
+
|
9
|
+
delegate :as_json, *(Array.instance_methods - Object.instance_methods), to: :decorated_collection
|
10
|
+
|
11
|
+
# @param source collection to decorate
|
12
|
+
# @param options [Hash] passed to each item's decorator (except
|
13
|
+
# for the keys listed below)
|
14
|
+
# @option options [Class,Symbol] :with the class used to decorate
|
15
|
+
# items, or `:infer` to call each item's `decorate` method instead
|
16
|
+
def initialize(source, options = {})
|
17
|
+
@source = source
|
18
|
+
@decorator_class = options.delete(:with) || self.class.inferred_decorator_class
|
19
|
+
@options = options
|
20
|
+
end
|
21
|
+
|
22
|
+
class << self
|
23
|
+
alias_method :decorate, :new
|
24
|
+
end
|
25
|
+
|
26
|
+
def decorated_collection
|
27
|
+
@decorated_collection ||= source.collect {|item| decorate_item(item) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def find(*args, &block)
|
31
|
+
if block_given?
|
32
|
+
decorated_collection.find(*args, &block)
|
33
|
+
else
|
34
|
+
decorator_class.find(*args)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def method_missing(method, *args, &block)
|
39
|
+
source.send(method, *args, &block)
|
40
|
+
end
|
41
|
+
|
42
|
+
def respond_to?(method, include_private = false)
|
43
|
+
super || source.respond_to?(method, include_private)
|
44
|
+
end
|
45
|
+
|
46
|
+
def kind_of?(klass)
|
47
|
+
super || source.kind_of?(klass)
|
48
|
+
end
|
49
|
+
alias_method :is_a?, :kind_of?
|
50
|
+
|
51
|
+
def ==(other)
|
52
|
+
source == (other.respond_to?(:source) ? other.source : other)
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_s
|
56
|
+
"#<CollectionDecorator of #{decorator_class} for #{source.inspect}>"
|
57
|
+
end
|
58
|
+
|
59
|
+
def options=(options)
|
60
|
+
each {|item| item.options = options }
|
61
|
+
@options = options
|
62
|
+
end
|
63
|
+
|
64
|
+
protected
|
65
|
+
|
66
|
+
def decorate_item(item)
|
67
|
+
if decorator_class == :infer
|
68
|
+
item.decorate(options)
|
69
|
+
else
|
70
|
+
decorator_class.decorate(item, options)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.inferred_decorator_class
|
75
|
+
decorator_name = "#{name.chomp("Decorator").singularize}Decorator"
|
76
|
+
decorator_uninferrable if decorator_name == name
|
77
|
+
|
78
|
+
decorator_name.constantize
|
79
|
+
|
80
|
+
rescue NameError
|
81
|
+
decorator_uninferrable
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.decorator_uninferrable
|
85
|
+
raise Draper::UninferrableDecoratorError.new(self)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|