draper 3.0.0 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6f2c896cc71e7e4e654a5d65907dd0ec4cefed7f
4
- data.tar.gz: ce50051cf161e9416483a40797d38ae2d2da3384
3
+ metadata.gz: cc23ed0f81227aa958510ecb67676453d97b40cc
4
+ data.tar.gz: ed8a159772bafb7df87cc5c767757e8b697f55b3
5
5
  SHA512:
6
- metadata.gz: 527adb1c71ce28f23bb9be4186fe857c543a26edd8d20404f1e2a6db51e819143dd9c7973f7735746457c2a8a392e7a6d5b8a79232d26a1694035e09441a98f4
7
- data.tar.gz: 0ed34d19d3b0300ea733392aab2c4cceaa1b284efc71e84452331fea4c9c11c68c7dc00baa7802a864d60f69bfb42beb8f5cecf959ed5ebf01b8a24c7e676f3f
6
+ metadata.gz: 466c609e53461c1e1f260f9e2bf6a0eb522238e5bdae8a2f37a88c1cd22d22665bf18293ba497f9f64d27faa0e12f0dbb9e3d906bae569f324c6124c02ad6b7e
7
+ data.tar.gz: db229fe0cf5daf0acc2e52834c599a21e9ba85782458a8fd3a0520574e77f1857ec30c57c81dceb75561016170c941f8da3593d3a58fd395137c01ce57322cf0
@@ -1,9 +1,16 @@
1
1
  language: ruby
2
+ sudo: false
3
+ cache: bundler
2
4
 
3
5
  services:
4
6
  - mongodb
5
7
 
6
8
  rvm:
7
- - 2.2.6
8
- - 2.3.3
9
+ - 2.2.7
10
+ - 2.3.4
9
11
  - 2.4.1
12
+ - ruby-head
13
+
14
+ matrix:
15
+ allow_failures:
16
+ - rvm: ruby-head
@@ -1,5 +1,10 @@
1
1
  # Draper Changelog
2
2
 
3
+ ## 3.0.1
4
+ * Let `decorator_class` infer anonymous class decorator from superclass [#820](https://github.com/drapergem/draper/pull/820)
5
+ * When inferring decorator fails, show original class instead of `ActiveRecord::Base` [#821](https://github.com/drapergem/draper/pull/821)
6
+ * ActiveJob compatibility and documentation [#817](https://github.com/drapergem/draper/pull/817)
7
+
3
8
  ## 3.0.0 - 2017
4
9
 
5
10
  ### Breaking Changes
data/README.md CHANGED
@@ -618,6 +618,15 @@ is included in `ActiveRecord::Base` and `Mongoid::Document` by default. If
618
618
  you're using another ORM, or want to decorate plain old Ruby objects,
619
619
  you can include this module manually.
620
620
 
621
+ ### Active Job Integration
622
+
623
+ [Active Job](http://edgeguides.rubyonrails.org/active_job_basics.html) allows you to pass ActiveRecord
624
+ objects to background tasks directly and performs the necessary serialization and deserialization. In
625
+ order to do this, arguments to a background job must implement [Global ID](https://github.com/rails/globalid).
626
+ Decorated objects implement Global ID by delegating to the object they are decorating. This means
627
+ you can pass decorated objects to background jobs, however, the object won't be decorated when it is
628
+ deserialized.
629
+
621
630
  ## Contributors
622
631
 
623
632
  Draper was conceived by Jeff Casimir and heavily refined by Steve Klabnik and a
@@ -0,0 +1,22 @@
1
+ module Draper
2
+ module Compatibility
3
+ # [Active Job](http://edgeguides.rubyonrails.org/active_job_basics.html) allows you to pass
4
+ # ActiveRecord objects to background tasks directly and performs the necessary serialization
5
+ # and deserialization. In order to do this, arguments to a background job must implement
6
+ # [Global ID](https://github.com/rails/globalid).
7
+ #
8
+ # This compatibility patch implements Global ID for decorated objects by delegating to the object
9
+ # that is decorated. This means you can pass decorated objects to background jobs, but
10
+ # the object won't be decorated when it is deserialized. This patch is meant as an intermediate
11
+ # fix until we can find a way to deserialize the decorated object correctly.
12
+ module GlobalID
13
+ extend ActiveSupport::Concern
14
+
15
+ included do
16
+ include ::GlobalID::Identification
17
+
18
+ delegate :to_global_id, :to_signed_global_id, to: :object
19
+ end
20
+ end
21
+ end
22
+ end
@@ -69,16 +69,16 @@ module Draper
69
69
  # `Product` maps to `ProductDecorator`).
70
70
  #
71
71
  # @return [Class] the inferred decorator class.
72
- def decorator_class
72
+ def decorator_class(called_on = self)
73
73
  prefix = respond_to?(:model_name) ? model_name : name
74
74
  decorator_name = "#{prefix}Decorator"
75
- decorator_name.constantize
76
- rescue NameError => error
77
- raise unless error.missing_name?(decorator_name)
75
+ decorator_name_constant = decorator_name.safe_constantize
76
+ return decorator_name_constant unless decorator_name_constant.nil?
77
+
78
78
  if superclass.respond_to?(:decorator_class)
79
- superclass.decorator_class
79
+ superclass.decorator_class(called_on)
80
80
  else
81
- raise Draper::UninferrableDecoratorError.new(self)
81
+ raise Draper::UninferrableDecoratorError.new(called_on)
82
82
  end
83
83
  end
84
84
 
@@ -1,6 +1,9 @@
1
+ require 'draper/compatibility/global_id'
2
+
1
3
  module Draper
2
4
  class Decorator
3
5
  include Draper::ViewHelpers
6
+ include Draper::Compatibility::GlobalID if defined?(GlobalID)
4
7
  extend Draper::Delegation
5
8
 
6
9
  include ActiveModel::Serialization
@@ -222,9 +225,9 @@ module Draper
222
225
  # @return [Class] the class created by {decorate_collection}.
223
226
  def self.collection_decorator_class
224
227
  name = collection_decorator_name
225
- name.constantize
226
- rescue NameError
227
- Draper::CollectionDecorator
228
+ name_constant = name && name.safe_constantize
229
+
230
+ name_constant || Draper::CollectionDecorator
228
231
  end
229
232
 
230
233
  private
@@ -239,22 +242,23 @@ module Draper
239
242
  end
240
243
 
241
244
  def self.object_class_name
242
- raise NameError if name.nil? || name.demodulize !~ /.+Decorator$/
245
+ return nil if name.nil? || name.demodulize !~ /.+Decorator$/
243
246
  name.chomp("Decorator")
244
247
  end
245
248
 
246
249
  def self.inferred_object_class
247
250
  name = object_class_name
248
- name.constantize
249
- rescue NameError => error
250
- raise if name && !error.missing_name?(name)
251
+ name_constant = name && name.safe_constantize
252
+ return name_constant unless name_constant.nil?
253
+
251
254
  raise Draper::UninferrableObjectError.new(self)
252
255
  end
253
256
 
254
257
  def self.collection_decorator_name
255
- plural = object_class_name.pluralize
256
- raise NameError if plural == object_class_name
257
- "#{plural}Decorator"
258
+ singular = object_class_name
259
+ plural = singular && singular.pluralize
260
+
261
+ "#{plural}Decorator" unless plural == singular
258
262
  end
259
263
 
260
264
  def handle_multiple_decoration(options)
@@ -1,3 +1,3 @@
1
1
  module Draper
2
- VERSION = '3.0.0'
2
+ VERSION = '3.0.1'
3
3
  end
@@ -1,7 +1,7 @@
1
1
  module Draper
2
2
  module Generators
3
3
  class InstallGenerator < Rails::Generators::Base
4
- source_root File.expand_path('../templates', __FILE__)
4
+ source_root File.expand_path("templates", __dir__)
5
5
 
6
6
  desc 'Creates an ApplicationDecorator, if none exists.'
7
7
 
@@ -4,7 +4,7 @@ module MiniTest
4
4
  module Generators
5
5
  class DecoratorGenerator < Base
6
6
  def self.source_root
7
- File.expand_path('../templates', __FILE__)
7
+ File.expand_path("templates", __dir__)
8
8
  end
9
9
 
10
10
  class_option :spec, type: :boolean, default: false, desc: "Use MiniTest::Spec DSL"
@@ -1,7 +1,7 @@
1
1
  module Rails
2
2
  module Generators
3
3
  class DecoratorGenerator < NamedBase
4
- source_root File.expand_path("../templates", __FILE__)
4
+ source_root File.expand_path("templates", __dir__)
5
5
  check_class_collision suffix: "Decorator"
6
6
 
7
7
  class_option :parent, type: :string, desc: "The parent class for the generated decorator"
@@ -1,9 +1,11 @@
1
1
  module Rspec
2
- class DecoratorGenerator < ::Rails::Generators::NamedBase
3
- source_root File.expand_path('../templates', __FILE__)
2
+ module Generators
3
+ class DecoratorGenerator < ::Rails::Generators::NamedBase
4
+ source_root File.expand_path("templates", __dir__)
4
5
 
5
- def create_spec_file
6
- template 'decorator_spec.rb', File.join('spec/decorators', class_path, "#{singular_name}_decorator_spec.rb")
6
+ def create_spec_file
7
+ template 'decorator_spec.rb', File.join('spec/decorators', class_path, "#{singular_name}_decorator_spec.rb")
8
+ end
7
9
  end
8
10
  end
9
11
  end
@@ -1,9 +1,12 @@
1
1
  module TestUnit
2
- class DecoratorGenerator < ::Rails::Generators::NamedBase
3
- source_root File.expand_path('../templates', __FILE__)
2
+ module Generators
3
+ class DecoratorGenerator < ::Rails::Generators::NamedBase
4
+ source_root File.expand_path("templates", __dir__)
5
+ check_class_collision suffix: "DecoratorTest"
4
6
 
5
- def create_test_file
6
- template 'decorator_test.rb', File.join('test/decorators', class_path, "#{singular_name}_decorator_test.rb")
7
+ def create_test_file
8
+ template 'decorator_test.rb', File.join('test/decorators', class_path, "#{singular_name}_decorator_test.rb")
9
+ end
7
10
  end
8
11
  end
9
12
  end
@@ -73,6 +73,16 @@ module Draper
73
73
  expect(Product).to receive(:decorator_class).and_return(:some_decorator)
74
74
  expect(product.decorator_class).to be :some_decorator
75
75
  end
76
+
77
+ it "specifies the class that #decorator_class was first called on (superclass)" do
78
+ person = Person.new
79
+ expect { person.decorator_class }.to raise_error(Draper::UninferrableDecoratorError, 'Could not infer a decorator for Person.')
80
+ end
81
+
82
+ it "specifies the class that #decorator_class was first called on (subclass)" do
83
+ child = Child.new
84
+ expect { child.decorator_class }.to raise_error(Draper::UninferrableDecoratorError, 'Could not infer a decorator for Child.')
85
+ end
76
86
  end
77
87
 
78
88
  describe "#==" do
@@ -205,10 +215,22 @@ module Draper
205
215
 
206
216
  context "when an unrelated NameError is thrown" do
207
217
  it "re-raises that error" do
208
- allow_any_instance_of(String).to receive(:constantize) { Draper::Base }
218
+ # Not related to safe_constantize behavior, we just want to raise a NameError inside the function
219
+ allow_any_instance_of(String).to receive(:safe_constantize) { Draper::Base }
209
220
  expect{Product.decorator_class}.to raise_error NameError, /Draper::Base/
210
221
  end
211
222
  end
223
+
224
+ context "when an anonymous class is given" do
225
+ it "infers the decorator from a superclass" do
226
+ anonymous_class = Class.new(Product) do
227
+ def self.name
228
+ to_s
229
+ end
230
+ end
231
+ expect(anonymous_class.decorator_class).to be ProductDecorator
232
+ end
233
+ end
212
234
  end
213
235
 
214
236
  end
@@ -202,7 +202,8 @@ module Draper
202
202
 
203
203
  context "when an unrelated NameError is thrown" do
204
204
  it "re-raises that error" do
205
- allow_any_instance_of(String).to receive(:constantize) { SomethingThatDoesntExist }
205
+ # Not related to safe_constantize behavior, we just want to raise a NameError inside the function
206
+ allow_any_instance_of(String).to receive(:safe_constantize) { SomethingThatDoesntExist }
206
207
  expect{ProductDecorator.object_class}.to raise_error NameError, /SomethingThatDoesntExist/
207
208
  end
208
209
  end
@@ -225,7 +226,7 @@ module Draper
225
226
 
226
227
  describe '.collection_decorator_class' do
227
228
  it 'defaults to CollectionDecorator' do
228
- allow_any_instance_of(String).to receive(:constantize) { SomethingThatDoesntExist }
229
+ allow_any_instance_of(String).to receive(:safe_constantize) { nil }
229
230
  expect(ProductDecorator.collection_decorator_class).to be Draper::CollectionDecorator
230
231
  end
231
232
 
@@ -0,0 +1,7 @@
1
+ class PublishPostJob < ActiveJob::Base
2
+ queue_as :default
3
+
4
+ def perform(post)
5
+ post.save!
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ class ApplicationRecord < ActiveRecord::Base
2
+ self.abstract_class = true
3
+ end
@@ -1,3 +1,3 @@
1
- class Post < ActiveRecord::Base
1
+ class Post < ApplicationRecord
2
2
  # attr_accessible :title, :body
3
3
  end
@@ -38,9 +38,6 @@ module Dummy
38
38
  # Configure the default encoding used in templates for Ruby 1.9.
39
39
  config.encoding = "utf-8"
40
40
 
41
- # Configure sensitive parameters which will be filtered from the log file.
42
- config.filter_parameters += [:password]
43
-
44
41
  # Enable escaping HTML in JSON.
45
42
  config.active_support.escape_html_entities_in_json = true
46
43
 
@@ -28,4 +28,6 @@ Dummy::Application.configure do
28
28
  config.active_support.deprecation = :stderr
29
29
 
30
30
  config.eager_load = false
31
+
32
+ config.active_job.queue_adapter = :test
31
33
  end
@@ -0,0 +1,4 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Configure sensitive parameters which will be filtered from the log file.
4
+ Rails.application.config.filter_parameters += [:password]
@@ -1,4 +1,4 @@
1
- class CreatePosts < ActiveRecord::Migration
1
+ class CreatePosts < ActiveRecord::Migration[4.2]
2
2
  def change
3
3
  create_table :posts do |t|
4
4
 
@@ -0,0 +1,9 @@
1
+ RSpec.describe PublishPostJob, type: :job do
2
+ let(:post) { Post.create.decorate }
3
+
4
+ subject(:job) { described_class.perform_later(post) }
5
+
6
+ it 'queues the job' do
7
+ expect { job }.to have_enqueued_job(described_class).with(post.object)
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe ApplicationRecord do
4
+ it { expect(described_class.superclass).to eq ActiveRecord::Base }
5
+
6
+ it { expect(described_class.abstract_class).to be_truthy }
7
+ end
@@ -1,6 +1,15 @@
1
1
  require 'spec_helper'
2
2
  require 'shared_examples/decoratable'
3
3
 
4
- describe Post do
5
- it_behaves_like "a decoratable model"
4
+ RSpec.describe Post do
5
+ it_behaves_like 'a decoratable model'
6
+
7
+ it { should be_a ApplicationRecord }
8
+
9
+ describe '#to_global_id' do
10
+ let(:post) { Post.create }
11
+ subject { post.to_global_id }
12
+
13
+ it { is_expected.to eq post.decorate.to_global_id }
14
+ end
6
15
  end
@@ -16,6 +16,8 @@ class Model; include Draper::Decoratable; end
16
16
  class Product < Model; end
17
17
  class SpecialProduct < Product; end
18
18
  class Other < Model; end
19
+ class Person < Model; end
20
+ class Child < Person; end
19
21
  class ProductDecorator < Draper::Decorator; end
20
22
  class ProductsDecorator < Draper::CollectionDecorator; end
21
23
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: draper
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Casimir
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-05-08 00:00:00.000000000 Z
12
+ date: 2017-10-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -207,6 +207,7 @@ files:
207
207
  - lib/draper/automatic_delegation.rb
208
208
  - lib/draper/collection_decorator.rb
209
209
  - lib/draper/compatibility/api_only.rb
210
+ - lib/draper/compatibility/global_id.rb
210
211
  - lib/draper/configuration.rb
211
212
  - lib/draper/decoratable.rb
212
213
  - lib/draper/decoratable/equality.rb
@@ -267,9 +268,11 @@ files:
267
268
  - spec/dummy/app/decorators/mongoid_post_decorator.rb
268
269
  - spec/dummy/app/decorators/post_decorator.rb
269
270
  - spec/dummy/app/helpers/application_helper.rb
271
+ - spec/dummy/app/jobs/publish_post_job.rb
270
272
  - spec/dummy/app/mailers/application_mailer.rb
271
273
  - spec/dummy/app/mailers/post_mailer.rb
272
274
  - spec/dummy/app/models/admin.rb
275
+ - spec/dummy/app/models/application_record.rb
273
276
  - spec/dummy/app/models/mongoid_post.rb
274
277
  - spec/dummy/app/models/post.rb
275
278
  - spec/dummy/app/models/user.rb
@@ -288,6 +291,7 @@ files:
288
291
  - spec/dummy/config/environments/test.rb
289
292
  - spec/dummy/config/initializers/backtrace_silencers.rb
290
293
  - spec/dummy/config/initializers/draper.rb
294
+ - spec/dummy/config/initializers/filter_parameter_logging.rb
291
295
  - spec/dummy/config/initializers/inflections.rb
292
296
  - spec/dummy/config/initializers/mime_types.rb
293
297
  - spec/dummy/config/initializers/secret_token.rb
@@ -312,7 +316,9 @@ files:
312
316
  - spec/dummy/spec/decorators/post_decorator_spec.rb
313
317
  - spec/dummy/spec/decorators/spec_type_spec.rb
314
318
  - spec/dummy/spec/decorators/view_context_spec.rb
319
+ - spec/dummy/spec/jobs/publish_post_job_spec.rb
315
320
  - spec/dummy/spec/mailers/post_mailer_spec.rb
321
+ - spec/dummy/spec/models/application_spec.rb
316
322
  - spec/dummy/spec/models/mongoid_post_spec.rb
317
323
  - spec/dummy/spec/models/post_spec.rb
318
324
  - spec/dummy/spec/shared_examples/decoratable.rb
@@ -389,9 +395,11 @@ test_files:
389
395
  - spec/dummy/app/decorators/mongoid_post_decorator.rb
390
396
  - spec/dummy/app/decorators/post_decorator.rb
391
397
  - spec/dummy/app/helpers/application_helper.rb
398
+ - spec/dummy/app/jobs/publish_post_job.rb
392
399
  - spec/dummy/app/mailers/application_mailer.rb
393
400
  - spec/dummy/app/mailers/post_mailer.rb
394
401
  - spec/dummy/app/models/admin.rb
402
+ - spec/dummy/app/models/application_record.rb
395
403
  - spec/dummy/app/models/mongoid_post.rb
396
404
  - spec/dummy/app/models/post.rb
397
405
  - spec/dummy/app/models/user.rb
@@ -410,6 +418,7 @@ test_files:
410
418
  - spec/dummy/config/environments/test.rb
411
419
  - spec/dummy/config/initializers/backtrace_silencers.rb
412
420
  - spec/dummy/config/initializers/draper.rb
421
+ - spec/dummy/config/initializers/filter_parameter_logging.rb
413
422
  - spec/dummy/config/initializers/inflections.rb
414
423
  - spec/dummy/config/initializers/mime_types.rb
415
424
  - spec/dummy/config/initializers/secret_token.rb
@@ -434,7 +443,9 @@ test_files:
434
443
  - spec/dummy/spec/decorators/post_decorator_spec.rb
435
444
  - spec/dummy/spec/decorators/spec_type_spec.rb
436
445
  - spec/dummy/spec/decorators/view_context_spec.rb
446
+ - spec/dummy/spec/jobs/publish_post_job_spec.rb
437
447
  - spec/dummy/spec/mailers/post_mailer_spec.rb
448
+ - spec/dummy/spec/models/application_spec.rb
438
449
  - spec/dummy/spec/models/mongoid_post_spec.rb
439
450
  - spec/dummy/spec/models/post_spec.rb
440
451
  - spec/dummy/spec/shared_examples/decoratable.rb