draper 0.18.0 → 1.0.0.beta1
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.
- 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
|
-
[](http://travis-ci.org/drapergem/draper)
|
4
|
+
[](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
|