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.
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