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.
Files changed (123) hide show
  1. data/.gitignore +3 -0
  2. data/.travis.yml +5 -0
  3. data/CHANGELOG.markdown +8 -0
  4. data/Gemfile +4 -0
  5. data/Rakefile +50 -31
  6. data/Readme.markdown +42 -48
  7. data/draper.gemspec +2 -1
  8. data/lib/draper.rb +42 -7
  9. data/lib/draper/collection_decorator.rb +88 -0
  10. data/lib/draper/decoratable.rb +44 -0
  11. data/lib/draper/decorated_association.rb +55 -0
  12. data/lib/draper/decorator.rb +208 -0
  13. data/lib/draper/finders.rb +44 -0
  14. data/lib/draper/helper_proxy.rb +16 -0
  15. data/lib/draper/railtie.rb +17 -21
  16. data/lib/draper/security.rb +48 -0
  17. data/lib/draper/tasks/tu.rake +5 -0
  18. data/lib/draper/test/minitest_integration.rb +1 -1
  19. data/lib/draper/test/test_unit_integration.rb +9 -0
  20. data/lib/draper/version.rb +1 -1
  21. data/lib/draper/view_context.rb +6 -13
  22. data/lib/draper/view_helpers.rb +36 -0
  23. data/lib/generators/decorator/decorator_generator.rb +1 -1
  24. data/lib/generators/decorator/templates/decorator.rb +0 -1
  25. data/spec/draper/collection_decorator_spec.rb +240 -0
  26. data/spec/draper/decoratable_spec.rb +164 -0
  27. data/spec/draper/decorated_association_spec.rb +130 -0
  28. data/spec/draper/decorator_spec.rb +497 -0
  29. data/spec/draper/finders_spec.rb +156 -0
  30. data/spec/draper/helper_proxy_spec.rb +12 -0
  31. data/spec/draper/security_spec.rb +158 -0
  32. data/spec/draper/view_helpers_spec.rb +41 -0
  33. data/spec/dummy/.rspec +2 -0
  34. data/spec/dummy/README.rdoc +261 -0
  35. data/spec/dummy/Rakefile +7 -0
  36. data/spec/dummy/app/controllers/application_controller.rb +4 -0
  37. data/spec/dummy/app/controllers/localized_urls.rb +5 -0
  38. data/spec/dummy/app/controllers/posts_controller.rb +17 -0
  39. data/spec/dummy/app/decorators/post_decorator.rb +25 -0
  40. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  41. data/spec/dummy/app/mailers/application_mailer.rb +3 -0
  42. data/spec/dummy/app/mailers/post_mailer.rb +9 -0
  43. data/spec/dummy/app/models/post.rb +3 -0
  44. data/spec/dummy/app/views/layouts/application.html.erb +11 -0
  45. data/spec/dummy/app/views/post_mailer/decorated_email.html.erb +1 -0
  46. data/spec/dummy/app/views/posts/_post.html.erb +19 -0
  47. data/spec/dummy/app/views/posts/show.html.erb +1 -0
  48. data/spec/dummy/config.ru +4 -0
  49. data/spec/dummy/config/application.rb +64 -0
  50. data/spec/dummy/config/boot.rb +10 -0
  51. data/spec/dummy/config/database.yml +25 -0
  52. data/spec/dummy/config/environment.rb +5 -0
  53. data/spec/dummy/config/environments/development.rb +34 -0
  54. data/spec/dummy/config/environments/production.rb +55 -0
  55. data/spec/dummy/config/environments/test.rb +32 -0
  56. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  57. data/spec/dummy/config/initializers/inflections.rb +15 -0
  58. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  59. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  60. data/spec/dummy/config/initializers/session_store.rb +8 -0
  61. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  62. data/spec/dummy/config/locales/en.yml +5 -0
  63. data/spec/dummy/config/routes.rb +7 -0
  64. data/spec/dummy/db/migrate/20121019115657_create_posts.rb +8 -0
  65. data/spec/dummy/db/schema.rb +21 -0
  66. data/spec/dummy/db/seeds.rb +2 -0
  67. data/spec/dummy/lib/tasks/spec.rake +5 -0
  68. data/spec/dummy/public/404.html +26 -0
  69. data/spec/dummy/public/422.html +26 -0
  70. data/spec/dummy/public/500.html +25 -0
  71. data/spec/dummy/public/favicon.ico +0 -0
  72. data/spec/dummy/script/rails +6 -0
  73. data/spec/dummy/spec/decorators/post_decorator_spec.rb +23 -0
  74. data/spec/dummy/spec/mailers/post_mailer_spec.rb +29 -0
  75. data/spec/dummy/spec/spec_helper.rb +9 -0
  76. data/spec/generators/decorator/decorator_generator_spec.rb +3 -4
  77. data/spec/integration/integration_spec.rb +33 -0
  78. data/{performance → spec/performance}/active_record.rb +0 -0
  79. data/{performance/bechmark.rb → spec/performance/benchmark.rb} +0 -0
  80. data/{performance → spec/performance}/decorators.rb +2 -4
  81. data/{performance → spec/performance}/models.rb +0 -0
  82. data/spec/spec_helper.rb +20 -41
  83. data/spec/support/action_controller.rb +12 -0
  84. data/spec/support/active_model.rb +7 -0
  85. data/spec/support/{samples/active_record.rb → active_record.rb} +5 -0
  86. data/spec/support/{samples → decorators}/decorator_with_application_helper.rb +1 -1
  87. data/spec/support/decorators/namespaced_product_decorator.rb +5 -0
  88. data/spec/support/decorators/non_active_model_product_decorator.rb +2 -0
  89. data/spec/support/decorators/product_decorator.rb +19 -0
  90. data/spec/support/{samples → decorators}/products_decorator.rb +1 -1
  91. data/spec/support/decorators/some_thing_decorator.rb +2 -0
  92. data/spec/support/{samples → decorators}/specific_product_decorator.rb +0 -2
  93. data/spec/support/{samples → decorators}/widget_decorator.rb +0 -0
  94. data/spec/support/dummy_app.rb +84 -0
  95. data/spec/support/matchers/have_text.rb +50 -0
  96. data/spec/support/{samples → models}/namespaced_product.rb +1 -3
  97. data/spec/support/{samples → models}/non_active_model_product.rb +1 -0
  98. data/spec/support/{samples → models}/product.rb +13 -2
  99. data/spec/support/models/some_thing.rb +5 -0
  100. data/spec/support/models/uninferrable_decorator_model.rb +3 -0
  101. data/spec/support/{samples → models}/widget.rb +0 -0
  102. metadata +185 -68
  103. data/lib/draper/active_model_support.rb +0 -27
  104. data/lib/draper/base.rb +0 -312
  105. data/lib/draper/decorated_enumerable_proxy.rb +0 -90
  106. data/lib/draper/model_support.rb +0 -25
  107. data/lib/draper/rspec_integration.rb +0 -2
  108. data/lib/draper/system.rb +0 -18
  109. data/spec/draper/base_spec.rb +0 -873
  110. data/spec/draper/decorated_enumerable_proxy_spec.rb +0 -45
  111. data/spec/draper/model_support_spec.rb +0 -48
  112. data/spec/support/samples/decorator.rb +0 -5
  113. data/spec/support/samples/decorator_with_allows.rb +0 -3
  114. data/spec/support/samples/decorator_with_denies.rb +0 -3
  115. data/spec/support/samples/decorator_with_denies_all.rb +0 -3
  116. data/spec/support/samples/decorator_with_multiple_allows.rb +0 -4
  117. data/spec/support/samples/decorator_with_special_methods.rb +0 -13
  118. data/spec/support/samples/enumerable_proxy.rb +0 -3
  119. data/spec/support/samples/namespaced_product_decorator.rb +0 -7
  120. data/spec/support/samples/product_decorator.rb +0 -7
  121. data/spec/support/samples/sequel_product.rb +0 -13
  122. data/spec/support/samples/some_thing.rb +0 -2
  123. data/spec/support/samples/some_thing_decorator.rb +0 -3
data/.gitignore CHANGED
@@ -6,8 +6,11 @@ pkg/*
6
6
  coverage.data
7
7
  coverage/*
8
8
  .yardoc
9
+ doc/*
9
10
  tmp
10
11
  vendor/bundle
11
12
  *.swp
12
13
  *.swo
13
14
  *.DS_Store
15
+ spec/dummy/log/*
16
+ spec/dummy/db/*.sqlite3
data/.travis.yml CHANGED
@@ -1,4 +1,9 @@
1
+ language: ruby
1
2
  rvm:
2
3
  - 1.9.3
3
4
  - rbx-19mode
4
5
  - jruby-19mode
6
+ - ruby-head
7
+ matrix:
8
+ allow_failures:
9
+ - rvm: ruby-head
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
@@ -1,3 +1,7 @@
1
1
  source :rubygems
2
2
 
3
3
  gemspec
4
+
5
+ gem "sqlite3", :platforms => :ruby
6
+
7
+ gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
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
- RCOV = RUBY_VERSION.to_f == 1.8
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
- namespace :spec do
15
+ desc "Run all specs"
16
+ task "spec" => "spec:all"
8
17
 
9
- RSpec::Core::RakeTask.new(:coverage) do |t|
10
- t.pattern = 'spec/**/*_spec.rb'
18
+ namespace "spec" do
19
+ task "all" => ["draper", "generators", "minitest-rails", "integration"]
11
20
 
12
- if RCOV
13
- t.rcov = true
14
- t.rcov_opts = '--exclude osx\/objc,spec,gems\/'
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
- RSpec::Core::RakeTask.new(:normal) do |t|
19
- t.pattern ='spec/**/*_spec.rb'
20
- t.rcov = false
21
- end
28
+ spec_task "draper"
29
+ spec_task "generators"
30
+ spec_task "minitest-rails"
22
31
 
23
- namespace :coverage do
24
- desc "Cleanup coverage data"
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
- 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!
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
- end
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
- desc "RSpec tests"
45
- task "spec" => "spec:normal"
50
+ raise "Integration specs failed in #{environment}" unless success
51
+ end
46
52
 
47
- task "default" => "spec"
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/jcasimir/draper.png)](http://travis-ci.org/jcasimir/draper)
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. `.find` automatic lookup & wrap
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
- 3. `.new` method with single object
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::Base
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::Base
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 a method named after the `decorates` argument:
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::Base
153
- decorates :article
154
-
153
+ class ArticleDecorator < Draper::Decorator
155
154
  def author_name
156
- article.author.first_name + " " + article.author.last_name
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::Base
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::Base
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 params[:id]
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::Base
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::Base
318
- decorates :article
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::Base
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/jcasimir/draper"
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/system'
5
- require 'draper/active_model_support'
6
- require 'draper/base'
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/model_support'
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/decorated_enumerable_proxy'
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' if defined?(RSpec) and RSpec.respond_to?(:configure)
16
- require 'draper/test/minitest_integration' if defined?(MiniTest::Rails)
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