draper_new 3.0.0
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.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rspec +2 -0
- data/.travis.yml +15 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +230 -0
- data/CONTRIBUTING.md +20 -0
- data/Gemfile +16 -0
- data/Guardfile +26 -0
- data/LICENSE +7 -0
- data/README.md +587 -0
- data/Rakefile +69 -0
- data/draper_new.gemspec +31 -0
- data/gemfiles/4.0.gemfile +3 -0
- data/gemfiles/4.1.gemfile +3 -0
- data/gemfiles/4.2.6.gemfile +3 -0
- data/gemfiles/4.2.gemfile +3 -0
- data/lib/draper.rb +63 -0
- data/lib/draper/automatic_delegation.rb +56 -0
- data/lib/draper/collection_decorator.rb +100 -0
- data/lib/draper/decoratable.rb +96 -0
- data/lib/draper/decoratable/equality.rb +26 -0
- data/lib/draper/decorated_association.rb +35 -0
- data/lib/draper/decorates_assigned.rb +44 -0
- data/lib/draper/decorator.rb +293 -0
- data/lib/draper/delegation.rb +13 -0
- data/lib/draper/factory.rb +91 -0
- data/lib/draper/finders.rb +37 -0
- data/lib/draper/helper_proxy.rb +44 -0
- data/lib/draper/helper_support.rb +5 -0
- data/lib/draper/lazy_helpers.rb +15 -0
- data/lib/draper/railtie.rb +70 -0
- data/lib/draper/tasks/test.rake +22 -0
- data/lib/draper/test/devise_helper.rb +30 -0
- data/lib/draper/test/minitest_integration.rb +6 -0
- data/lib/draper/test/rspec_integration.rb +20 -0
- data/lib/draper/test_case.rb +42 -0
- data/lib/draper/undecorate.rb +9 -0
- data/lib/draper/version.rb +3 -0
- data/lib/draper/view_context.rb +104 -0
- data/lib/draper/view_context/build_strategy.rb +48 -0
- data/lib/draper/view_helpers.rb +37 -0
- data/lib/generators/controller_override.rb +17 -0
- data/lib/generators/mini_test/decorator_generator.rb +20 -0
- data/lib/generators/mini_test/templates/decorator_spec.rb +4 -0
- data/lib/generators/mini_test/templates/decorator_test.rb +4 -0
- data/lib/generators/rails/decorator_generator.rb +36 -0
- data/lib/generators/rails/templates/decorator.rb +19 -0
- data/lib/generators/rspec/decorator_generator.rb +9 -0
- data/lib/generators/rspec/templates/decorator_spec.rb +4 -0
- data/lib/generators/test_unit/decorator_generator.rb +9 -0
- data/lib/generators/test_unit/templates/decorator_test.rb +4 -0
- data/spec/draper/collection_decorator_spec.rb +307 -0
- data/spec/draper/decoratable/equality_spec.rb +10 -0
- data/spec/draper/decoratable_spec.rb +202 -0
- data/spec/draper/decorated_association_spec.rb +84 -0
- data/spec/draper/decorates_assigned_spec.rb +71 -0
- data/spec/draper/decorator_spec.rb +816 -0
- data/spec/draper/factory_spec.rb +251 -0
- data/spec/draper/finders_spec.rb +166 -0
- data/spec/draper/helper_proxy_spec.rb +61 -0
- data/spec/draper/lazy_helpers_spec.rb +21 -0
- data/spec/draper/undecorate_spec.rb +19 -0
- data/spec/draper/view_context/build_strategy_spec.rb +116 -0
- data/spec/draper/view_context_spec.rb +154 -0
- data/spec/draper/view_helpers_spec.rb +8 -0
- data/spec/dummy/.rspec +2 -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 +20 -0
- data/spec/dummy/app/decorators/mongoid_post_decorator.rb +4 -0
- data/spec/dummy/app/decorators/post_decorator.rb +60 -0
- data/spec/dummy/app/helpers/application_helper.rb +5 -0
- data/spec/dummy/app/mailers/application_mailer.rb +3 -0
- data/spec/dummy/app/mailers/post_mailer.rb +19 -0
- data/spec/dummy/app/models/admin.rb +5 -0
- data/spec/dummy/app/models/mongoid_post.rb +5 -0
- data/spec/dummy/app/models/post.rb +3 -0
- data/spec/dummy/app/models/user.rb +5 -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 +40 -0
- data/spec/dummy/app/views/posts/show.html.erb +1 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +71 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +33 -0
- data/spec/dummy/config/environments/production.rb +57 -0
- data/spec/dummy/config/environments/test.rb +31 -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 +8 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/mongoid.yml +79 -0
- data/spec/dummy/config/routes.rb +9 -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/fast_spec/post_decorator_spec.rb +37 -0
- data/spec/dummy/lib/tasks/test.rake +16 -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/active_model_serializers_spec.rb +16 -0
- data/spec/dummy/spec/decorators/devise_spec.rb +64 -0
- data/spec/dummy/spec/decorators/helpers_spec.rb +21 -0
- data/spec/dummy/spec/decorators/post_decorator_spec.rb +66 -0
- data/spec/dummy/spec/decorators/spec_type_spec.rb +7 -0
- data/spec/dummy/spec/decorators/view_context_spec.rb +22 -0
- data/spec/dummy/spec/mailers/post_mailer_spec.rb +33 -0
- data/spec/dummy/spec/models/mongoid_post_spec.rb +8 -0
- data/spec/dummy/spec/models/post_spec.rb +6 -0
- data/spec/dummy/spec/shared_examples/decoratable.rb +24 -0
- data/spec/dummy/spec/spec_helper.rb +8 -0
- data/spec/dummy/test/decorators/minitest/devise_test.rb +64 -0
- data/spec/dummy/test/decorators/minitest/helpers_test.rb +21 -0
- data/spec/dummy/test/decorators/minitest/spec_type_test.rb +52 -0
- data/spec/dummy/test/decorators/minitest/view_context_test.rb +24 -0
- data/spec/dummy/test/decorators/test_unit/devise_test.rb +64 -0
- data/spec/dummy/test/decorators/test_unit/helpers_test.rb +21 -0
- data/spec/dummy/test/decorators/test_unit/view_context_test.rb +24 -0
- data/spec/dummy/test/minitest_helper.rb +2 -0
- data/spec/dummy/test/test_helper.rb +3 -0
- data/spec/generators/controller/controller_generator_spec.rb +22 -0
- data/spec/generators/decorator/decorator_generator_spec.rb +92 -0
- data/spec/integration/integration_spec.rb +66 -0
- data/spec/performance/active_record.rb +4 -0
- data/spec/performance/benchmark.rb +55 -0
- data/spec/performance/decorators.rb +45 -0
- data/spec/performance/models.rb +20 -0
- data/spec/spec_helper.rb +41 -0
- data/spec/support/dummy_app.rb +85 -0
- data/spec/support/matchers/have_text.rb +50 -0
- data/spec/support/shared_examples/decoratable_equality.rb +40 -0
- data/spec/support/shared_examples/view_helpers.rb +39 -0
- metadata +420 -0
data/Rakefile
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require 'rake'
|
|
2
|
+
require 'bundler/gem_tasks'
|
|
3
|
+
require 'rspec/core/rake_task'
|
|
4
|
+
|
|
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"
|
|
14
|
+
|
|
15
|
+
desc "Run all specs"
|
|
16
|
+
task "spec" => "spec:all"
|
|
17
|
+
|
|
18
|
+
namespace "spec" do
|
|
19
|
+
task "all" => ["draper", "generators", "integration"]
|
|
20
|
+
|
|
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"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
spec_task "draper"
|
|
29
|
+
spec_task "generators"
|
|
30
|
+
|
|
31
|
+
desc "Run integration specs"
|
|
32
|
+
task "integration" => ["db:setup", "integration:all"]
|
|
33
|
+
|
|
34
|
+
namespace "integration" do
|
|
35
|
+
task "all" => ["development", "production", "test"]
|
|
36
|
+
|
|
37
|
+
["development", "production"].each do |environment|
|
|
38
|
+
task environment do
|
|
39
|
+
Rake::Task["spec:integration:run"].execute environment
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
task "run" do |t, environment|
|
|
44
|
+
puts "Running integration specs in #{environment}"
|
|
45
|
+
|
|
46
|
+
ENV["RAILS_ENV"] = environment
|
|
47
|
+
success = system("rspec spec/integration")
|
|
48
|
+
|
|
49
|
+
raise "Integration specs failed in #{environment}" unless success
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
task "test" do
|
|
53
|
+
puts "Running rake in dummy app"
|
|
54
|
+
ENV["RAILS_ENV"] = "test"
|
|
55
|
+
run_in_dummy_app "rake"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
namespace "db" do
|
|
61
|
+
desc "Set up databases for integration testing"
|
|
62
|
+
task "setup" do
|
|
63
|
+
puts "Setting up databases"
|
|
64
|
+
run_in_dummy_app "rm -f db/*.sqlite3"
|
|
65
|
+
run_in_dummy_app "RAILS_ENV=development rake db:schema:load db:seed"
|
|
66
|
+
run_in_dummy_app "RAILS_ENV=production rake db:schema:load db:seed"
|
|
67
|
+
run_in_dummy_app "RAILS_ENV=test rake db:schema:load"
|
|
68
|
+
end
|
|
69
|
+
end
|
data/draper_new.gemspec
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
3
|
+
require "draper/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = "draper_new"
|
|
7
|
+
s.version = Draper::VERSION
|
|
8
|
+
s.authors = ["Chashmeet Singh"]
|
|
9
|
+
s.email = ["chashmeetsingh@gmail.com"]
|
|
10
|
+
s.homepage = "http://github.com/chashmeetsingh/draper"
|
|
11
|
+
s.summary = "View Models for Rails"
|
|
12
|
+
s.description = "Draper adds an object-oriented layer of presentation logic to your Rails apps."
|
|
13
|
+
s.license = "MIT"
|
|
14
|
+
|
|
15
|
+
s.files = `git ls-files`.split("\n")
|
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
18
|
+
s.require_paths = ["lib"]
|
|
19
|
+
|
|
20
|
+
s.add_dependency 'activesupport'
|
|
21
|
+
s.add_dependency 'actionpack'
|
|
22
|
+
s.add_dependency 'request_store'
|
|
23
|
+
s.add_dependency 'activemodel'
|
|
24
|
+
|
|
25
|
+
s.add_development_dependency 'ammeter'
|
|
26
|
+
s.add_development_dependency 'rake'
|
|
27
|
+
s.add_development_dependency 'rspec-rails'
|
|
28
|
+
s.add_development_dependency 'minitest-rails'
|
|
29
|
+
s.add_development_dependency 'capybara'
|
|
30
|
+
s.add_development_dependency 'active_model_serializers'
|
|
31
|
+
end
|
data/lib/draper.rb
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'action_view'
|
|
2
|
+
require 'active_model/naming'
|
|
3
|
+
require 'active_model/serialization'
|
|
4
|
+
require 'active_model/serializers/json'
|
|
5
|
+
require 'active_model/serializers/xml'
|
|
6
|
+
require 'active_support/inflector'
|
|
7
|
+
require 'active_support/core_ext/hash/keys'
|
|
8
|
+
require 'active_support/core_ext/hash/reverse_merge'
|
|
9
|
+
require 'active_support/core_ext/name_error'
|
|
10
|
+
|
|
11
|
+
require 'draper/version'
|
|
12
|
+
require 'draper/view_helpers'
|
|
13
|
+
require 'draper/delegation'
|
|
14
|
+
require 'draper/automatic_delegation'
|
|
15
|
+
require 'draper/finders'
|
|
16
|
+
require 'draper/decorator'
|
|
17
|
+
require 'draper/helper_proxy'
|
|
18
|
+
require 'draper/lazy_helpers'
|
|
19
|
+
require 'draper/decoratable'
|
|
20
|
+
require 'draper/factory'
|
|
21
|
+
require 'draper/decorated_association'
|
|
22
|
+
require 'draper/helper_support'
|
|
23
|
+
require 'draper/view_context'
|
|
24
|
+
require 'draper/collection_decorator'
|
|
25
|
+
require 'draper/undecorate'
|
|
26
|
+
require 'draper/decorates_assigned'
|
|
27
|
+
require 'draper/railtie' if defined?(Rails)
|
|
28
|
+
|
|
29
|
+
module Draper
|
|
30
|
+
def self.setup_action_controller(base)
|
|
31
|
+
base.class_eval do
|
|
32
|
+
include Draper::ViewContext
|
|
33
|
+
extend Draper::HelperSupport
|
|
34
|
+
extend Draper::DecoratesAssigned
|
|
35
|
+
|
|
36
|
+
before_filter :activate_draper
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.setup_action_mailer(base)
|
|
41
|
+
base.class_eval do
|
|
42
|
+
include Draper::ViewContext
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.setup_orm(base)
|
|
47
|
+
base.class_eval do
|
|
48
|
+
include Draper::Decoratable
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
class UninferrableDecoratorError < NameError
|
|
53
|
+
def initialize(klass)
|
|
54
|
+
super("Could not infer a decorator for #{klass}.")
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class UninferrableSourceError < NameError
|
|
59
|
+
def initialize(klass)
|
|
60
|
+
super("Could not infer a source for #{klass}.")
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module Draper
|
|
2
|
+
module AutomaticDelegation
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
# Delegates missing instance methods to the source object.
|
|
6
|
+
def method_missing(method, *args, &block)
|
|
7
|
+
return super unless delegatable?(method)
|
|
8
|
+
|
|
9
|
+
self.class.delegate method
|
|
10
|
+
send(method, *args, &block)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Checks if the decorator responds to an instance method, or is able to
|
|
14
|
+
# proxy it to the source object.
|
|
15
|
+
def respond_to_missing?(method, include_private = false)
|
|
16
|
+
super || delegatable?(method)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @private
|
|
20
|
+
def delegatable?(method)
|
|
21
|
+
object.respond_to?(method)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
module ClassMethods
|
|
25
|
+
# Proxies missing class methods to the source class.
|
|
26
|
+
def method_missing(method, *args, &block)
|
|
27
|
+
return super unless delegatable?(method)
|
|
28
|
+
|
|
29
|
+
object_class.send(method, *args, &block)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Checks if the decorator responds to a class method, or is able to proxy
|
|
33
|
+
# it to the source class.
|
|
34
|
+
def respond_to_missing?(method, include_private = false)
|
|
35
|
+
super || delegatable?(method)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @private
|
|
39
|
+
def delegatable?(method)
|
|
40
|
+
object_class? && object_class.respond_to?(method)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @private
|
|
44
|
+
# Avoids reloading the model class when ActiveSupport clears autoloaded
|
|
45
|
+
# dependencies in development mode.
|
|
46
|
+
def before_remove_const
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
included do
|
|
51
|
+
private :delegatable?
|
|
52
|
+
private_class_method :delegatable?
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
module Draper
|
|
2
|
+
class CollectionDecorator
|
|
3
|
+
include Enumerable
|
|
4
|
+
include Draper::ViewHelpers
|
|
5
|
+
extend Draper::Delegation
|
|
6
|
+
|
|
7
|
+
# @return the collection being decorated.
|
|
8
|
+
attr_reader :object
|
|
9
|
+
|
|
10
|
+
# @return [Class] the decorator class used to decorate each item, as set by
|
|
11
|
+
# {#initialize}.
|
|
12
|
+
attr_reader :decorator_class
|
|
13
|
+
|
|
14
|
+
# @return [Hash] extra data to be used in user-defined methods, and passed
|
|
15
|
+
# to each item's decorator.
|
|
16
|
+
attr_accessor :context
|
|
17
|
+
|
|
18
|
+
array_methods = Array.instance_methods - Object.instance_methods
|
|
19
|
+
delegate :==, :as_json, *array_methods, to: :decorated_collection
|
|
20
|
+
|
|
21
|
+
# @param [Enumerable] object
|
|
22
|
+
# collection to decorate.
|
|
23
|
+
# @option options [Class, nil] :with (nil)
|
|
24
|
+
# the decorator class used to decorate each item. When `nil`, each item's
|
|
25
|
+
# {Decoratable#decorate decorate} method will be used.
|
|
26
|
+
# @option options [Hash] :context ({})
|
|
27
|
+
# extra data to be stored in the collection decorator and used in
|
|
28
|
+
# user-defined methods, and passed to each item's decorator.
|
|
29
|
+
def initialize(object, options = {})
|
|
30
|
+
options.assert_valid_keys(:with, :context)
|
|
31
|
+
@object = object
|
|
32
|
+
@decorator_class = options[:with]
|
|
33
|
+
@context = options.fetch(:context, {})
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class << self
|
|
37
|
+
alias_method :decorate, :new
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @return [Array] the decorated items.
|
|
41
|
+
def decorated_collection
|
|
42
|
+
@decorated_collection ||= object.map{|item| decorate_item(item)}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Delegated to the decorated collection when using the block form
|
|
46
|
+
# (`Enumerable#find`) or to the decorator class if not
|
|
47
|
+
# (`ActiveRecord::FinderMethods#find`)
|
|
48
|
+
def find(*args, &block)
|
|
49
|
+
if block_given?
|
|
50
|
+
decorated_collection.find(*args, &block)
|
|
51
|
+
else
|
|
52
|
+
ActiveSupport::Deprecation.warn("Using ActiveRecord's `find` on a CollectionDecorator is deprecated. Call `find` on a model, and then decorate the result", caller)
|
|
53
|
+
decorate_item(object.find(*args))
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def to_s
|
|
58
|
+
"#<#{self.class.name} of #{decorator_class || "inferred decorators"} for #{object.inspect}>"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def context=(value)
|
|
62
|
+
@context = value
|
|
63
|
+
each {|item| item.context = value } if @decorated_collection
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# @return [true]
|
|
67
|
+
def decorated?
|
|
68
|
+
true
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
alias_method :decorated_with?, :instance_of?
|
|
72
|
+
|
|
73
|
+
def kind_of?(klass)
|
|
74
|
+
decorated_collection.kind_of?(klass) || super
|
|
75
|
+
end
|
|
76
|
+
alias_method :is_a?, :kind_of?
|
|
77
|
+
|
|
78
|
+
def replace(other)
|
|
79
|
+
decorated_collection.replace(other)
|
|
80
|
+
self
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
protected
|
|
84
|
+
|
|
85
|
+
# Decorates the given item.
|
|
86
|
+
def decorate_item(item)
|
|
87
|
+
item_decorator.call(item, context: context)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def item_decorator
|
|
93
|
+
if decorator_class
|
|
94
|
+
decorator_class.method(:decorate)
|
|
95
|
+
else
|
|
96
|
+
->(item, options) { item.decorate(options) }
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
require 'draper/decoratable/equality'
|
|
2
|
+
|
|
3
|
+
module Draper
|
|
4
|
+
# Provides shortcuts to decorate objects directly, so you can do
|
|
5
|
+
# `@product.decorate` instead of `ProductDecorator.new(@product)`.
|
|
6
|
+
#
|
|
7
|
+
# This module is included by default into `ActiveRecord::Base` and
|
|
8
|
+
# `Mongoid::Document`, but you're using another ORM, or want to decorate
|
|
9
|
+
# plain old Ruby objects, you can include it manually.
|
|
10
|
+
module Decoratable
|
|
11
|
+
extend ActiveSupport::Concern
|
|
12
|
+
include Draper::Decoratable::Equality
|
|
13
|
+
|
|
14
|
+
# Decorates the object using the inferred {#decorator_class}.
|
|
15
|
+
# @param [Hash] options
|
|
16
|
+
# see {Decorator#initialize}
|
|
17
|
+
def decorate(options = {})
|
|
18
|
+
decorator_class.decorate(self, options)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# (see ClassMethods#decorator_class)
|
|
22
|
+
def decorator_class
|
|
23
|
+
self.class.decorator_class
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def decorator_class?
|
|
27
|
+
self.class.decorator_class?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# The list of decorators that have been applied to the object.
|
|
31
|
+
#
|
|
32
|
+
# @return [Array<Class>] `[]`
|
|
33
|
+
def applied_decorators
|
|
34
|
+
[]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# (see Decorator#decorated_with?)
|
|
38
|
+
# @return [false]
|
|
39
|
+
def decorated_with?(decorator_class)
|
|
40
|
+
false
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Checks if this object is decorated.
|
|
44
|
+
#
|
|
45
|
+
# @return [false]
|
|
46
|
+
def decorated?
|
|
47
|
+
false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
module ClassMethods
|
|
51
|
+
|
|
52
|
+
# Decorates a collection of objects. Used at the end of a scope chain.
|
|
53
|
+
#
|
|
54
|
+
# @example
|
|
55
|
+
# Product.popular.decorate
|
|
56
|
+
# @param [Hash] options
|
|
57
|
+
# see {Decorator.decorate_collection}.
|
|
58
|
+
def decorate(options = {})
|
|
59
|
+
collection = Rails::VERSION::MAJOR >= 4 ? all : scoped
|
|
60
|
+
decorator_class.decorate_collection(collection, options.reverse_merge(with: nil))
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def decorator_class?
|
|
64
|
+
decorator_class
|
|
65
|
+
rescue Draper::UninferrableDecoratorError
|
|
66
|
+
false
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Infers the decorator class to be used by {Decoratable#decorate} (e.g.
|
|
70
|
+
# `Product` maps to `ProductDecorator`).
|
|
71
|
+
#
|
|
72
|
+
# @return [Class] the inferred decorator class.
|
|
73
|
+
def decorator_class
|
|
74
|
+
prefix = respond_to?(:model_name) ? model_name : name
|
|
75
|
+
decorator_name = "#{prefix}Decorator"
|
|
76
|
+
decorator_name.constantize
|
|
77
|
+
rescue NameError => error
|
|
78
|
+
if superclass.respond_to?(:decorator_class)
|
|
79
|
+
superclass.decorator_class
|
|
80
|
+
else
|
|
81
|
+
raise unless error.missing_name?(decorator_name)
|
|
82
|
+
raise Draper::UninferrableDecoratorError.new(self)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Compares with possibly-decorated objects.
|
|
87
|
+
#
|
|
88
|
+
# @return [Boolean]
|
|
89
|
+
def ===(other)
|
|
90
|
+
super || (other.respond_to?(:object) && super(other.object))
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Draper
|
|
2
|
+
module Decoratable
|
|
3
|
+
module Equality
|
|
4
|
+
# Compares self with a possibly-decorated object.
|
|
5
|
+
#
|
|
6
|
+
# @return [Boolean]
|
|
7
|
+
def ==(other)
|
|
8
|
+
super || Equality.test_for_decorator(self, other)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Compares an object to a possibly-decorated object.
|
|
12
|
+
#
|
|
13
|
+
# @return [Boolean]
|
|
14
|
+
def self.test(object, other)
|
|
15
|
+
return object == other if object.is_a?(Decoratable)
|
|
16
|
+
object == other || test_for_decorator(object, other)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @private
|
|
20
|
+
def self.test_for_decorator(object, other)
|
|
21
|
+
other.respond_to?(:decorated?) && other.decorated? &&
|
|
22
|
+
other.respond_to?(:object) && test(object, other.object)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|