Blow up Decoratable
This commit is contained in:
parent
57a514133b
commit
f4b21284f4
|
@ -16,7 +16,6 @@ require 'draper/finders'
|
||||||
require 'draper/decorator'
|
require 'draper/decorator'
|
||||||
require 'draper/helper_proxy'
|
require 'draper/helper_proxy'
|
||||||
require 'draper/lazy_helpers'
|
require 'draper/lazy_helpers'
|
||||||
require 'draper/decoratable'
|
|
||||||
require 'draper/factory'
|
require 'draper/factory'
|
||||||
require 'draper/decorated_association'
|
require 'draper/decorated_association'
|
||||||
require 'draper/helper_support'
|
require 'draper/helper_support'
|
||||||
|
|
|
@ -1,96 +0,0 @@
|
||||||
require 'draper/decoratable/equality'
|
|
||||||
|
|
||||||
module Draper
|
|
||||||
# Provides shortcuts to decorate objects directly, so you can do
|
|
||||||
# `@product.decorate` instead of `ProductDecorator.new(@product)`.
|
|
||||||
#
|
|
||||||
# This module is included by default into `ActiveRecord::Base` and
|
|
||||||
# `Mongoid::Document`, but you're using another ORM, or want to decorate
|
|
||||||
# plain old Ruby objects, you can include it manually.
|
|
||||||
module Decoratable
|
|
||||||
extend ActiveSupport::Concern
|
|
||||||
include Draper::Decoratable::Equality
|
|
||||||
|
|
||||||
# Decorates the object using the inferred {#decorator_class}.
|
|
||||||
# @param [Hash] options
|
|
||||||
# see {Decorator#initialize}
|
|
||||||
def decorate(options = {})
|
|
||||||
decorator_class.decorate(self, options)
|
|
||||||
end
|
|
||||||
|
|
||||||
# (see ClassMethods#decorator_class)
|
|
||||||
def decorator_class
|
|
||||||
self.class.decorator_class
|
|
||||||
end
|
|
||||||
|
|
||||||
def decorator_class?
|
|
||||||
self.class.decorator_class?
|
|
||||||
end
|
|
||||||
|
|
||||||
# The list of decorators that have been applied to the object.
|
|
||||||
#
|
|
||||||
# @return [Array<Class>] `[]`
|
|
||||||
def applied_decorators
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
|
|
||||||
# (see Decorator#decorated_with?)
|
|
||||||
# @return [false]
|
|
||||||
def decorated_with?(decorator_class)
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
# Checks if this object is decorated.
|
|
||||||
#
|
|
||||||
# @return [false]
|
|
||||||
def decorated?
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
module ClassMethods
|
|
||||||
|
|
||||||
# Decorates a collection of objects. Used at the end of a scope chain.
|
|
||||||
#
|
|
||||||
# @example
|
|
||||||
# Product.popular.decorate
|
|
||||||
# @param [Hash] options
|
|
||||||
# see {Decorator.decorate_collection}.
|
|
||||||
def decorate(options = {})
|
|
||||||
collection = Rails::VERSION::MAJOR >= 4 ? all : scoped
|
|
||||||
decorator_class.decorate_collection(collection, options.reverse_merge(with: nil))
|
|
||||||
end
|
|
||||||
|
|
||||||
def decorator_class?
|
|
||||||
decorator_class
|
|
||||||
rescue Draper::UninferrableDecoratorError
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
# Infers the decorator class to be used by {Decoratable#decorate} (e.g.
|
|
||||||
# `Product` maps to `ProductDecorator`).
|
|
||||||
#
|
|
||||||
# @return [Class] the inferred decorator class.
|
|
||||||
def decorator_class
|
|
||||||
prefix = respond_to?(:model_name) ? model_name : name
|
|
||||||
decorator_name = "#{prefix}Decorator"
|
|
||||||
decorator_name.constantize
|
|
||||||
rescue NameError => error
|
|
||||||
if superclass.respond_to?(:decorator_class)
|
|
||||||
superclass.decorator_class
|
|
||||||
else
|
|
||||||
raise unless error.missing_name?(decorator_name)
|
|
||||||
raise Draper::UninferrableDecoratorError.new(self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Compares with possibly-decorated objects.
|
|
||||||
#
|
|
||||||
# @return [Boolean]
|
|
||||||
def ===(other)
|
|
||||||
super || (other.respond_to?(:object) && super(other.object))
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,10 +0,0 @@
|
||||||
require 'spec_helper'
|
|
||||||
require 'support/shared_examples/decoratable_equality'
|
|
||||||
|
|
||||||
module Draper
|
|
||||||
describe Decoratable::Equality do
|
|
||||||
describe "#==" do
|
|
||||||
it_behaves_like "decoration-aware #==", Object.new.extend(Decoratable::Equality)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,202 +0,0 @@
|
||||||
require 'spec_helper'
|
|
||||||
require 'support/shared_examples/decoratable_equality'
|
|
||||||
|
|
||||||
module Draper
|
|
||||||
describe Decoratable do
|
|
||||||
|
|
||||||
describe "#decorate" do
|
|
||||||
it "returns a decorator for self" do
|
|
||||||
product = Product.new
|
|
||||||
decorator = product.decorate
|
|
||||||
|
|
||||||
expect(decorator).to be_a ProductDecorator
|
|
||||||
expect(decorator.object).to be product
|
|
||||||
end
|
|
||||||
|
|
||||||
it "accepts context" do
|
|
||||||
context = {some: "context"}
|
|
||||||
decorator = Product.new.decorate(context: context)
|
|
||||||
|
|
||||||
expect(decorator.context).to be context
|
|
||||||
end
|
|
||||||
|
|
||||||
it "uses the #decorator_class" do
|
|
||||||
product = Product.new
|
|
||||||
product.stub decorator_class: OtherDecorator
|
|
||||||
|
|
||||||
expect(product.decorate).to be_an_instance_of OtherDecorator
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#applied_decorators" do
|
|
||||||
it "returns an empty list" do
|
|
||||||
expect(Product.new.applied_decorators).to eq []
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#decorated_with?" do
|
|
||||||
it "returns false" do
|
|
||||||
expect(Product.new).not_to be_decorated_with Decorator
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#decorated?" do
|
|
||||||
it "returns false" do
|
|
||||||
expect(Product.new).not_to be_decorated
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#decorator_class?" do
|
|
||||||
it "returns true for decoratable model" do
|
|
||||||
expect(Product.new.decorator_class?).to be_truthy
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns false for non-decoratable model" do
|
|
||||||
expect(Model.new.decorator_class?).to be_falsey
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".decorator_class?" do
|
|
||||||
it "returns true for decoratable model" do
|
|
||||||
expect(Product.decorator_class?).to be_truthy
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns false for non-decoratable model" do
|
|
||||||
expect(Model.decorator_class?).to be_falsey
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#decorator_class" do
|
|
||||||
it "delegates to .decorator_class" do
|
|
||||||
product = Product.new
|
|
||||||
|
|
||||||
Product.should_receive(:decorator_class).and_return(:some_decorator)
|
|
||||||
expect(product.decorator_class).to be :some_decorator
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#==" do
|
|
||||||
it_behaves_like "decoration-aware #==", Product.new
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#===" do
|
|
||||||
it "is true when #== is true" do
|
|
||||||
product = Product.new
|
|
||||||
|
|
||||||
product.should_receive(:==).and_return(true)
|
|
||||||
expect(product === :anything).to be_truthy
|
|
||||||
end
|
|
||||||
|
|
||||||
it "is false when #== is false" do
|
|
||||||
product = Product.new
|
|
||||||
|
|
||||||
product.should_receive(:==).and_return(false)
|
|
||||||
expect(product === :anything).to be_falsey
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".====" do
|
|
||||||
it "is true for an instance" do
|
|
||||||
expect(Product === Product.new).to be_truthy
|
|
||||||
end
|
|
||||||
|
|
||||||
it "is true for a derived instance" do
|
|
||||||
expect(Product === Class.new(Product).new).to be_truthy
|
|
||||||
end
|
|
||||||
|
|
||||||
it "is false for an unrelated instance" do
|
|
||||||
expect(Product === Model.new).to be_falsey
|
|
||||||
end
|
|
||||||
|
|
||||||
it "is true for a decorated instance" do
|
|
||||||
decorator = double(object: Product.new)
|
|
||||||
|
|
||||||
expect(Product === decorator).to be_truthy
|
|
||||||
end
|
|
||||||
|
|
||||||
it "is true for a decorated derived instance" do
|
|
||||||
decorator = double(object: Class.new(Product).new)
|
|
||||||
|
|
||||||
expect(Product === decorator).to be_truthy
|
|
||||||
end
|
|
||||||
|
|
||||||
it "is false for a decorated unrelated instance" do
|
|
||||||
decorator = double(object: Model.new)
|
|
||||||
|
|
||||||
expect(Product === decorator).to be_falsey
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".decorate" do
|
|
||||||
let(:scoping_method) { Rails::VERSION::MAJOR >= 4 ? :all : :scoped }
|
|
||||||
|
|
||||||
it "calls #decorate_collection on .decorator_class" do
|
|
||||||
scoped = [Product.new]
|
|
||||||
Product.stub scoping_method => scoped
|
|
||||||
|
|
||||||
Product.decorator_class.should_receive(:decorate_collection).with(scoped, with: nil).and_return(:decorated_collection)
|
|
||||||
expect(Product.decorate).to be :decorated_collection
|
|
||||||
end
|
|
||||||
|
|
||||||
it "accepts options" do
|
|
||||||
options = {with: ProductDecorator, context: {some: "context"}}
|
|
||||||
Product.stub scoping_method => []
|
|
||||||
|
|
||||||
Product.decorator_class.should_receive(:decorate_collection).with([], options)
|
|
||||||
Product.decorate(options)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ".decorator_class" do
|
|
||||||
context "for classes" do
|
|
||||||
it "infers the decorator from the class" do
|
|
||||||
expect(Product.decorator_class).to be ProductDecorator
|
|
||||||
end
|
|
||||||
|
|
||||||
context "without a decorator on its own" do
|
|
||||||
it "infers the decorator from a superclass" do
|
|
||||||
expect(SpecialProduct.decorator_class).to be ProductDecorator
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "for ActiveModel classes" do
|
|
||||||
it "infers the decorator from the model name" do
|
|
||||||
allow(Product).to receive(:model_name){"Other"}
|
|
||||||
|
|
||||||
expect(Product.decorator_class).to be OtherDecorator
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "in a namespace" do
|
|
||||||
context "for classes" do
|
|
||||||
it "infers the decorator from the class" do
|
|
||||||
expect(Namespaced::Product.decorator_class).to be Namespaced::ProductDecorator
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "for ActiveModel classes" do
|
|
||||||
it "infers the decorator from the model name" do
|
|
||||||
Namespaced::Product.stub(:model_name).and_return("Namespaced::Other")
|
|
||||||
|
|
||||||
expect(Namespaced::Product.decorator_class).to be Namespaced::OtherDecorator
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when the decorator can't be inferred" do
|
|
||||||
it "throws an UninferrableDecoratorError" do
|
|
||||||
expect{Model.decorator_class}.to raise_error UninferrableDecoratorError
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when an unrelated NameError is thrown" do
|
|
||||||
it "re-raises that error" do
|
|
||||||
String.any_instance.stub(:constantize) { Draper::Base }
|
|
||||||
expect{Product.decorator_class}.to raise_error NameError, /Draper::Base/
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -12,7 +12,7 @@ RSpec.configure do |config|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Model; include Draper::Decoratable; end
|
class Model; end
|
||||||
|
|
||||||
class Product < Model; end
|
class Product < Model; end
|
||||||
class SpecialProduct < Product; end
|
class SpecialProduct < Product; end
|
||||||
|
|
Loading…
Reference in New Issue