Merge remote-tracking branch 'origin/master' into feature/rspec3-updates
# Conflicts: # .travis.yml # Gemfile # Guardfile # draper.gemspec # lib/generators/rspec/templates/decorator_spec.rb # spec/draper/collection_decorator_spec.rb # spec/draper/decoratable_spec.rb # spec/draper/decorated_association_spec.rb # spec/draper/decorates_assigned_spec.rb # spec/draper/decorator_spec.rb # spec/draper/factory_spec.rb # spec/draper/finders_spec.rb # spec/draper/view_context/build_strategy_spec.rb # spec/draper/view_context_spec.rb # spec/dummy/fast_spec/post_decorator_spec.rb # spec/dummy/spec/models/post_spec.rb # spec/generators/controller/controller_generator_spec.rb # spec/generators/decorator/decorator_generator_spec.rb # spec/support/shared_examples/view_helpers.rb
This commit is contained in:
commit
cb9af6c9dc
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
engines:
|
||||
duplication:
|
||||
enabled: true
|
||||
config:
|
||||
languages:
|
||||
- ruby
|
||||
fixme:
|
||||
enabled: true
|
||||
rubocop:
|
||||
enabled: true
|
||||
ratings:
|
||||
paths:
|
||||
- "**.rb"
|
||||
exclude_paths:
|
||||
- spec/
|
|
@ -0,0 +1,24 @@
|
|||
## Description
|
||||
Detail your changes here.
|
||||
A few sentences describing the overall goals of the pull request's commits will suffice.
|
||||
Some questions you might answer:
|
||||
|
||||
* Why was this change required?
|
||||
* Did you have any tough decisions to make? Which one(s) did you go with and why?
|
||||
* Are there any deployment impacts to this change?
|
||||
* Is there something you aren't happy with or that needs extra attention?
|
||||
|
||||
## Testing
|
||||
Outline steps to test your changes.
|
||||
|
||||
1. Go here.
|
||||
1. Click this.
|
||||
1. See that.
|
||||
|
||||
## To-Dos
|
||||
- [ ] tests
|
||||
- [ ] documentation
|
||||
|
||||
## References
|
||||
* [GitHub Issue ####](https://github.com/drapergem/draper/issues/####)
|
||||
* [GitHub Pull Request ####](https://github.com/drapergem/draper/pull/####)
|
|
@ -1,5 +1,7 @@
|
|||
*.gem
|
||||
*.rvmrc
|
||||
.rvmrc
|
||||
.ruby-version
|
||||
.ruby-gemset
|
||||
.bundle
|
||||
Gemfile.lock
|
||||
pkg/*
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
AllCops:
|
||||
TargetRubyVersion: 2.4
|
||||
DisplayCopNames: true
|
||||
Exclude:
|
||||
- 'spec/dummy/**/*'
|
||||
|
||||
Style/StringLiterals:
|
||||
Enabled: false
|
||||
|
||||
Metrics/LineLength:
|
||||
Max: 100
|
30
.travis.yml
30
.travis.yml
|
@ -1,15 +1,29 @@
|
|||
env:
|
||||
global:
|
||||
- CC_TEST_REPORTER_ID=b7ba588af2a540fa96c267b3655a2afe31ea29976dc25905a668dd28d5e88915
|
||||
|
||||
language: ruby
|
||||
sudo: false
|
||||
cache: bundler
|
||||
|
||||
services:
|
||||
- mongodb
|
||||
|
||||
rvm:
|
||||
- 2.1.5
|
||||
- 2.2.1
|
||||
- 2.2.2
|
||||
- 2.2.3
|
||||
- 2.3.5
|
||||
- 2.4.3
|
||||
- 2.5.4
|
||||
- 2.6.2
|
||||
- ruby-head
|
||||
|
||||
env:
|
||||
- "RAILS_VERSION=4.0"
|
||||
- "RAILS_VERSION=4.1"
|
||||
- "RAILS_VERSION=4.2"
|
||||
matrix:
|
||||
allow_failures:
|
||||
- rvm: ruby-head
|
||||
|
||||
before_script:
|
||||
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
||||
- chmod +x ./cc-test-reporter
|
||||
- ./cc-test-reporter before-build
|
||||
|
||||
after_script:
|
||||
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
||||
|
|
36
CHANGELOG.md
36
CHANGELOG.md
|
@ -1,5 +1,41 @@
|
|||
# Draper Changelog
|
||||
|
||||
## 3.1.0
|
||||
* Rails 6 support [#841](https://github.com/drapergem/draper/pull/841)
|
||||
* Include ORM query methods in `CollectionDecorator` (e.g. `includes`) [#845](https://github.com/drapergem/draper/pull/845)
|
||||
* Document the fix for view context leaking in specs [#847](https://github.com/drapergem/draper/pull/847)
|
||||
|
||||
## 3.0.1
|
||||
* Let `decorator_class` infer anonymous class decorator from superclass [#820](https://github.com/drapergem/draper/pull/820)
|
||||
* When inferring decorator fails, show original class instead of `ActiveRecord::Base` [#821](https://github.com/drapergem/draper/pull/821)
|
||||
* ActiveJob compatibility and documentation [#817](https://github.com/drapergem/draper/pull/817)
|
||||
|
||||
## 3.0.0 - 2017
|
||||
|
||||
### Breaking Changes
|
||||
* Rename UninferrableSourceError to UninferrableObjectError [#768](https://github.com/drapergem/draper/pull/768)
|
||||
* Remove conflicting source aliases: `source`, `to_source`, `source_class` and `source_class?` [#786](https://github.com/drapergem/draper/pull/786)
|
||||
|
||||
### New Features
|
||||
* Generator for creating `ApplicationDecorator` that other decorators inherit from [#796](https://github.com/drapergem/draper/pull/796)
|
||||
* Draper configuration with ability to customize the controller Draper uses [#788](https://github.com/drapergem/draper/pull/788)
|
||||
* Added support for Rails 5 API-only applications [#793](https://github.com/drapergem/draper/pull/793)
|
||||
* Added support for Rails runner [#739](https://github.com/drapergem/draper/pull/739)
|
||||
|
||||
### Other Changes
|
||||
* Clear view context when the controller changes [#799](https://github.com/drapergem/draper/pull/799)
|
||||
* Removed previously deprecated functionality [#785](https://github.com/drapergem/draper/pull/785)
|
||||
* Only delegate === if other is an instance of a class that inherits from `Decorator` [#720](https://github.com/drapergem/draper/pull/720)
|
||||
* Always default to `CollectionDecorator` when `NameError` is raised [#795](https://github.com/drapergem/draper/pull/795)
|
||||
* Fixed issues in order to support Rails 5.1
|
||||
* Fixed a bug where helpers were used inside a decorator and this decorator was used outside of controller context
|
||||
|
||||
## 3.0.0.pre1 - 2016-07-10
|
||||
|
||||
* Added support for Rails 5, dropped 4.0 - 4.2
|
||||
* Ruby >= 2.2 is required, matching Rails 5
|
||||
* Dropped support for ActiveModelSerializers 0.8
|
||||
|
||||
## 2.1.0 - 2015-03-26
|
||||
|
||||
* Cleared most issues and merged a few PRs
|
||||
|
|
17
Gemfile
17
Gemfile
|
@ -3,20 +3,13 @@ source "https://rubygems.org"
|
|||
gemspec
|
||||
|
||||
platforms :ruby do
|
||||
gem "sqlite3"
|
||||
gem 'sqlite3', '~> 1.3.6'
|
||||
end
|
||||
|
||||
platforms :jruby do
|
||||
gem "minitest", ">= 3.0"
|
||||
gem "activerecord-jdbcsqlite3-adapter", ">= 1.3.0.beta2"
|
||||
gem "minitest"
|
||||
gem "activerecord-jdbcsqlite3-adapter"
|
||||
end
|
||||
|
||||
group :development, :test do
|
||||
gem 'guard-rspec', require: false
|
||||
gem 'ruby_gntp'
|
||||
gem 'colorize'
|
||||
end
|
||||
|
||||
version = ENV["RAILS_VERSION"] || "4.1"
|
||||
|
||||
eval_gemfile File.expand_path("../gemfiles/#{version}.gemfile", __FILE__)
|
||||
gem "rails", "~> 5.0"
|
||||
gem "mongoid", github: "mongodb/mongoid"
|
||||
|
|
17
Guardfile
17
Guardfile
|
@ -1,29 +1,26 @@
|
|||
notification :gntp, host: '127.0.0.1'
|
||||
|
||||
def rspec_guard(options = {}, &block)
|
||||
opts = {
|
||||
:cmd => 'rspec'
|
||||
options = {
|
||||
version: 2,
|
||||
notification: false
|
||||
}.merge(options)
|
||||
|
||||
guard 'rspec', opts, &block
|
||||
guard 'rspec', options, &block
|
||||
end
|
||||
|
||||
rspec_guard :spec_paths => %w{spec/draper spec/generators} do
|
||||
rspec_guard spec_paths: %w{spec/draper spec/generators} do
|
||||
watch(%r{^spec/.+_spec\.rb$})
|
||||
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
||||
watch('spec/spec_helper.rb') { "spec" }
|
||||
end
|
||||
|
||||
rspec_guard :spec_paths => ['spec/integration'], cmd: 'RAILS_ENV=development rspec' do
|
||||
rspec_guard spec_paths: 'spec/integration', env: {'RAILS_ENV' => 'development'} do
|
||||
watch(%r{^spec/.+_spec\.rb$})
|
||||
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
||||
watch('spec/spec_helper.rb') { "spec" }
|
||||
end
|
||||
|
||||
rspec_guard :spec_paths => ['spec/integration'], cmd: 'RAILS_ENV=production rspec' do
|
||||
rspec_guard spec_paths: 'spec/integration', env: {'RAILS_ENV' => 'production'} do
|
||||
watch(%r{^spec/.+_spec\.rb$})
|
||||
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
||||
watch('spec/spec_helper.rb') { "spec" }
|
||||
end
|
||||
|
||||
# vim: set ts=8 sw=2 tw=0 ft=ruby et :
|
||||
|
|
107
README.md
107
README.md
|
@ -1,8 +1,9 @@
|
|||
# Draper: View Models for Rails
|
||||
|
||||
[![TravisCI Build Status](https://travis-ci.org/drapergem/draper.svg?branch=master)](http://travis-ci.org/drapergem/draper)
|
||||
[![Code Climate](https://codeclimate.com/github/drapergem/draper.png)](https://codeclimate.com/github/drapergem/draper)
|
||||
[![Inline docs](http://inch-ci.org/github/drapergem/draper.png?branch=master)](http://inch-ci.org/github/drapergem/draper)
|
||||
[![Code Climate](https://codeclimate.com/github/drapergem/draper.svg)](https://codeclimate.com/github/drapergem/draper)
|
||||
[![Test Coverage](https://api.codeclimate.com/v1/badges/0d40c43951d516bf6985/test_coverage)](https://codeclimate.com/github/drapergem/draper/test_coverage)
|
||||
[![Inline docs](http://inch-ci.org/github/drapergem/draper.svg?branch=master)](http://inch-ci.org/github/drapergem/draper)
|
||||
|
||||
Draper adds an object-oriented layer of presentation logic to your Rails
|
||||
application.
|
||||
|
@ -48,7 +49,7 @@ end
|
|||
But it makes you a little uncomfortable. `publication_status` lives in a
|
||||
nebulous namespace spread across all controllers and view. Down the road, you
|
||||
might want to display the publication status of a `Book`. And, of course, your
|
||||
design calls for a slighly different formatting to the date for a `Book`.
|
||||
design calls for a slightly different formatting to the date for a `Book`.
|
||||
|
||||
Now your helper method can either switch based on the input class type (poor
|
||||
Ruby style), or you break it out into two methods, `book_publication_status` and
|
||||
|
@ -107,13 +108,13 @@ Decorators are the ideal place to:
|
|||
|
||||
## Installation
|
||||
|
||||
Add Draper to your Gemfile:
|
||||
As of version 3.0.0, Draper is only compatible with Rails 5 / Ruby 2.2 and later. Add Draper to your Gemfile.
|
||||
|
||||
```ruby
|
||||
gem 'draper', '~> 1.3'
|
||||
gem 'draper'
|
||||
```
|
||||
|
||||
And run `bundle install` within your app's directory.
|
||||
After that, run `bundle install` within your app's directory.
|
||||
|
||||
If you're upgrading from a 0.x release, the major changes are outlined [in the
|
||||
wiki](https://github.com/drapergem/draper/wiki/Upgrading-to-1.0).
|
||||
|
@ -132,6 +133,12 @@ end
|
|||
|
||||
### Generators
|
||||
|
||||
To create an `ApplicationDecorator` that all generated decorators inherit from, run...
|
||||
|
||||
```
|
||||
rails generate draper:install
|
||||
```
|
||||
|
||||
When you have Draper installed and generate a controller...
|
||||
|
||||
```
|
||||
|
@ -276,6 +283,19 @@ omitted.
|
|||
delegate :current_page, :per_page, :offset, :total_entries, :total_pages
|
||||
```
|
||||
|
||||
If needed, you can then set the collection_decorator_class of your CustomDecorator as follows:
|
||||
```ruby
|
||||
class ArticleDecorator < Draper::Decorator
|
||||
def self.collection_decorator_class
|
||||
PaginatingDecorator
|
||||
end
|
||||
end
|
||||
|
||||
ArticleDecorator.decorate_collection(@articles.paginate)
|
||||
# => Collection decorated by PaginatingDecorator
|
||||
# => Members decorated by ArticleDecorator
|
||||
```
|
||||
|
||||
### Decorating Associated Objects
|
||||
|
||||
You can automatically decorate associated models when the primary model is
|
||||
|
@ -307,6 +327,17 @@ your `ArticleDecorator` and they'll return decorated objects:
|
|||
@article = ArticleDecorator.find(params[:id])
|
||||
```
|
||||
|
||||
### Decorated Query Methods
|
||||
By default, Draper will decorate all [QueryMethods](https://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html)
|
||||
of ActiveRecord.
|
||||
If you're using another ORM, in order to support it, you can tell Draper to use a custom strategy:
|
||||
|
||||
```ruby
|
||||
Draper.configure do |config|
|
||||
config.default_query_methods_strategy = :mongoid
|
||||
end
|
||||
```
|
||||
|
||||
### When to Decorate Objects
|
||||
|
||||
Decorators are supposed to behave very much like the models they decorate, and
|
||||
|
@ -348,6 +379,18 @@ you'll have access to an ArticleDecorator object instead. In your controller you
|
|||
can continue to use the `@article` instance variable to manipulate the model -
|
||||
for example, `@article.comments.build` to add a new blank comment for a form.
|
||||
|
||||
## Configuration
|
||||
Draper works out the box well, but also provides a hook for you to configure its
|
||||
default functionality. For example, Draper assumes you have a base `ApplicationController`.
|
||||
If your base controller is named something different (e.g. `BaseController`),
|
||||
you can tell Draper to use it by adding the following to an initializer:
|
||||
|
||||
```ruby
|
||||
Draper.configure do |config|
|
||||
config.default_controller = BaseController
|
||||
end
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
Draper supports RSpec, MiniTest::Rails, and Test::Unit, and will add the
|
||||
|
@ -379,6 +422,15 @@ In your `Spork.prefork` block of `spec_helper.rb`, add this:
|
|||
require 'draper/test/rspec_integration'
|
||||
```
|
||||
|
||||
#### Custom Draper Controller ViewContext
|
||||
If running tests in an engine setting with a controller other than "ApplicationController," set a custom controller in `spec_helper.rb`
|
||||
|
||||
```ruby
|
||||
config.before(:each, type: :decorator) do |example|
|
||||
Draper::ViewContext.controller = ExampleEngine::CustomRootController.new
|
||||
end
|
||||
```
|
||||
|
||||
### Isolated Tests
|
||||
|
||||
In tests, Draper needs to build a view context to access helper methods. By
|
||||
|
@ -423,6 +475,20 @@ preferred stubbing technique (this example uses RSpec's `stub` method):
|
|||
helpers.stub(users_path: '/users')
|
||||
```
|
||||
|
||||
### View context leakage
|
||||
As mentioned before, Draper needs to build a view context to access helper methods. In MiniTest, the view context is
|
||||
cleared during `before_setup` preventing any view context leakage. In RSpec, the view context is cleared before each
|
||||
`decorator`, `controller`, and `mailer` spec. However, if you use decorators in other types of specs
|
||||
(e.g. `job`), you may still experience the view context leaking from the previous spec. To solve this, add the
|
||||
following to your `spec_helper` for each type of spec you are experiencing the leakage:
|
||||
|
||||
```ruby
|
||||
config.before(:each, type: :type) { Draper::ViewContext.clear! }
|
||||
```
|
||||
|
||||
_Note_: The `:type` above is just a placeholder. Replace `:type` with the type of spec you are experiencing
|
||||
the leakage from.
|
||||
|
||||
## Advanced usage
|
||||
|
||||
### Shared Decorator Methods
|
||||
|
@ -455,7 +521,10 @@ end
|
|||
|
||||
When your decorator calls `delegate_all`, any method called on the decorator not
|
||||
defined in the decorator itself will be delegated to the decorated object. This
|
||||
is a very permissive interface.
|
||||
includes calling `super` from within the decorator. A call to `super` from within
|
||||
the decorator will first try to call the method on the parent decorator class. If
|
||||
the method does not exist on the parent decorator class, it will then try to call
|
||||
the method on the decorated `object`. This is a very permissive interface.
|
||||
|
||||
If you want to strictly control which methods are called within views, you can
|
||||
choose to only delegate certain methods from the decorator to the source model:
|
||||
|
@ -564,24 +633,40 @@ end
|
|||
|
||||
This is only necessary when proxying class methods.
|
||||
|
||||
Once this association between the decorator and the model is set up, you can call
|
||||
`SomeModel.decorator_class` to access class methods defined in the decorator.
|
||||
If necessary, you can check if your model is decorated with `SomeModel.decorator_class?`.
|
||||
|
||||
### Making Models Decoratable
|
||||
|
||||
Models get their `decorate` method from the `Draper::Decoratable` module, which
|
||||
is included in `ActiveRecord::Base` and `Mongoid::Document` by default. If
|
||||
you're [using another
|
||||
ORM](https://github.com/drapergem/draper/wiki/Using-other-ORMs) (including
|
||||
versions of Mongoid prior to 3.0), or want to decorate plain old Ruby objects,
|
||||
you're using another ORM, or want to decorate plain old Ruby objects,
|
||||
you can include this module manually.
|
||||
|
||||
### Active Job Integration
|
||||
|
||||
[Active Job](http://edgeguides.rubyonrails.org/active_job_basics.html) allows you to pass ActiveRecord
|
||||
objects to background tasks directly and performs the necessary serialization and deserialization. In
|
||||
order to do this, arguments to a background job must implement [Global ID](https://github.com/rails/globalid).
|
||||
Decorated objects implement Global ID by delegating to the object they are decorating. This means
|
||||
you can pass decorated objects to background jobs, however, the object won't be decorated when it is
|
||||
deserialized.
|
||||
|
||||
## Contributors
|
||||
|
||||
Draper was conceived by Jeff Casimir and heavily refined by Steve Klabnik and a
|
||||
great community of open source
|
||||
[contributors](https://github.com/drapergem/draper/contributors).
|
||||
|
||||
### Core Team
|
||||
### Current maintainers
|
||||
|
||||
* Cliff Braton (cliff.braton@gmail.com)
|
||||
|
||||
### Historical maintainers
|
||||
|
||||
* Jeff Casimir (jeff@jumpstartlab.com)
|
||||
* Steve Klabnik (steve@jumpstartlab.com)
|
||||
* Vasiliy Ermolovich
|
||||
* Andrew Haines
|
||||
* Sean Linsley
|
||||
|
|
2
Rakefile
2
Rakefile
|
@ -64,6 +64,6 @@ namespace "db" do
|
|||
run_in_dummy_app "rm -f db/*.sqlite3"
|
||||
run_in_dummy_app "RAILS_ENV=development rake db:schema:load db:seed"
|
||||
run_in_dummy_app "RAILS_ENV=production rake db:schema:load db:seed"
|
||||
run_in_dummy_app "RAILS_ENV=test rake db:schema:load"
|
||||
run_in_dummy_app "RAILS_ENV=test rake db:environment:set db:schema:load"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
$:.push File.expand_path("../lib", __FILE__)
|
||||
require "draper/version"
|
||||
require File.join(__dir__, "lib", "draper", "version")
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = "draper"
|
||||
|
@ -17,15 +15,20 @@ Gem::Specification.new do |s|
|
|||
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
||||
s.require_paths = ["lib"]
|
||||
|
||||
s.add_dependency 'activesupport', '>= 3.0'
|
||||
s.add_dependency 'actionpack', '>= 3.0'
|
||||
s.add_dependency 'request_store', '~> 1.0'
|
||||
s.add_dependency 'activemodel', '>= 3.0'
|
||||
s.required_ruby_version = '>= 2.2.2'
|
||||
|
||||
s.add_dependency 'activesupport', '>= 5.0'
|
||||
s.add_dependency 'actionpack', '>= 5.0'
|
||||
s.add_dependency 'request_store', '>= 1.0'
|
||||
s.add_dependency 'activemodel', '>= 5.0'
|
||||
s.add_dependency 'activemodel-serializers-xml', '>= 1.0'
|
||||
|
||||
s.add_development_dependency 'ammeter'
|
||||
s.add_development_dependency 'rake', '>= 0.9.2'
|
||||
s.add_development_dependency 'rspec-rails', '~> 3.3'
|
||||
s.add_development_dependency 'minitest-rails', '>= 1.0'
|
||||
s.add_development_dependency 'rake'
|
||||
s.add_development_dependency 'rspec-rails'
|
||||
s.add_development_dependency 'minitest-rails'
|
||||
s.add_development_dependency 'capybara'
|
||||
s.add_development_dependency 'active_model_serializers'
|
||||
s.add_development_dependency 'active_model_serializers', '>= 0.10'
|
||||
s.add_development_dependency 'rubocop'
|
||||
s.add_development_dependency 'simplecov'
|
||||
end
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
gem "rails", "~> 4.0.0"
|
||||
gem "mongoid", "~> 4.0"
|
||||
gem "devise", "~> 3.0.0"
|
|
@ -1,3 +0,0 @@
|
|||
gem "rails", "~> 4.1.0"
|
||||
gem "mongoid", "~> 4.0"
|
||||
gem "devise", "~> 3.2"
|
|
@ -1,3 +0,0 @@
|
|||
gem "rails", "~> 4.2.0"
|
||||
gem "mongoid", "~> 4.0"
|
||||
gem "devise", "~> 3.4"
|
|
@ -9,7 +9,9 @@ require 'active_support/core_ext/hash/reverse_merge'
|
|||
require 'active_support/core_ext/name_error'
|
||||
|
||||
require 'draper/version'
|
||||
require 'draper/configuration'
|
||||
require 'draper/view_helpers'
|
||||
require 'draper/compatibility/api_only'
|
||||
require 'draper/delegation'
|
||||
require 'draper/automatic_delegation'
|
||||
require 'draper/finders'
|
||||
|
@ -21,19 +23,23 @@ require 'draper/factory'
|
|||
require 'draper/decorated_association'
|
||||
require 'draper/helper_support'
|
||||
require 'draper/view_context'
|
||||
require 'draper/query_methods'
|
||||
require 'draper/collection_decorator'
|
||||
require 'draper/undecorate'
|
||||
require 'draper/decorates_assigned'
|
||||
require 'draper/railtie' if defined?(Rails)
|
||||
|
||||
module Draper
|
||||
extend Draper::Configuration
|
||||
|
||||
def self.setup_action_controller(base)
|
||||
base.class_eval do
|
||||
include Draper::Compatibility::ApiOnly if base == ActionController::API
|
||||
include Draper::ViewContext
|
||||
extend Draper::HelperSupport
|
||||
extend Draper::DecoratesAssigned
|
||||
|
||||
before_filter :activate_draper
|
||||
before_action :activate_draper
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -55,9 +61,9 @@ module Draper
|
|||
end
|
||||
end
|
||||
|
||||
class UninferrableSourceError < NameError
|
||||
class UninferrableObjectError < NameError
|
||||
def initialize(klass)
|
||||
super("Could not infer a source for #{klass}.")
|
||||
super("Could not infer an object for #{klass}.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,12 +2,14 @@ module Draper
|
|||
module AutomaticDelegation
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
# Delegates missing instance methods to the source object.
|
||||
# Delegates missing instance methods to the source object. Note: This will delegate `super`
|
||||
# method calls to `object` as well. Calling `super` will first try to call the method on
|
||||
# the parent decorator class. If no method exists on the parent class, it will then try
|
||||
# to call the method on the `object`.
|
||||
def method_missing(method, *args, &block)
|
||||
return super unless delegatable?(method)
|
||||
|
||||
self.class.delegate method
|
||||
send(method, *args, &block)
|
||||
object.send(method, *args, &block)
|
||||
end
|
||||
|
||||
# Checks if the decorator responds to an instance method, or is able to
|
||||
|
@ -18,6 +20,8 @@ module Draper
|
|||
|
||||
# @private
|
||||
def delegatable?(method)
|
||||
return if private_methods.include?(method)
|
||||
|
||||
object.respond_to?(method)
|
||||
end
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ module Draper
|
|||
class CollectionDecorator
|
||||
include Enumerable
|
||||
include Draper::ViewHelpers
|
||||
include Draper::QueryMethods
|
||||
extend Draper::Delegation
|
||||
|
||||
# @return the collection being decorated.
|
||||
|
@ -42,17 +43,7 @@ module Draper
|
|||
@decorated_collection ||= object.map{|item| decorate_item(item)}
|
||||
end
|
||||
|
||||
# Delegated to the decorated collection when using the block form
|
||||
# (`Enumerable#find`) or to the decorator class if not
|
||||
# (`ActiveRecord::FinderMethods#find`)
|
||||
def find(*args, &block)
|
||||
if block_given?
|
||||
decorated_collection.find(*args, &block)
|
||||
else
|
||||
ActiveSupport::Deprecation.warn("Using ActiveRecord's `find` on a CollectionDecorator is deprecated. Call `find` on a model, and then decorate the result", caller)
|
||||
decorate_item(object.find(*args))
|
||||
end
|
||||
end
|
||||
delegate :find, to: :decorated_collection
|
||||
|
||||
def to_s
|
||||
"#<#{self.class.name} of #{decorator_class || "inferred decorators"} for #{object.inspect}>"
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
module Draper
|
||||
module Compatibility
|
||||
# Draper expects your `ApplicationController` to include `ActionView::Rendering`. The
|
||||
# `ApplicationController` generated by Rails 5 API-only applications (created with
|
||||
# `rails new --api`) don't by default. However, including `ActionView::Rendering` in
|
||||
# `ApplicatonController` breaks `render :json` due to `render_to_body` being overridden.
|
||||
#
|
||||
# This compatibility patch fixes the issue by restoring the original `render_to_body`
|
||||
# method after including `ActionView::Rendering`. Ultimately, including `ActionView::Rendering`
|
||||
# in an ActionController::API may not be supported functionality by Rails (see Rails issue
|
||||
# for more detail: https://github.com/rails/rails/issues/27211). This hack is meant to be a
|
||||
# temporary solution until we can find a way to not rely on the controller layer.
|
||||
module ApiOnly
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
alias :previous_render_to_body :render_to_body
|
||||
include ActionView::Rendering
|
||||
alias :render_to_body :previous_render_to_body
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,22 @@
|
|||
module Draper
|
||||
module Compatibility
|
||||
# [Active Job](http://edgeguides.rubyonrails.org/active_job_basics.html) allows you to pass
|
||||
# ActiveRecord objects to background tasks directly and performs the necessary serialization
|
||||
# and deserialization. In order to do this, arguments to a background job must implement
|
||||
# [Global ID](https://github.com/rails/globalid).
|
||||
#
|
||||
# This compatibility patch implements Global ID for decorated objects by delegating to the object
|
||||
# that is decorated. This means you can pass decorated objects to background jobs, but
|
||||
# the object won't be decorated when it is deserialized. This patch is meant as an intermediate
|
||||
# fix until we can find a way to deserialize the decorated object correctly.
|
||||
module GlobalID
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
include ::GlobalID::Identification
|
||||
|
||||
delegate :to_global_id, :to_signed_global_id, to: :object
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,23 @@
|
|||
module Draper
|
||||
module Configuration
|
||||
def configure
|
||||
yield self
|
||||
end
|
||||
|
||||
def default_controller
|
||||
@@default_controller ||= ApplicationController
|
||||
end
|
||||
|
||||
def default_controller=(controller)
|
||||
@@default_controller = controller
|
||||
end
|
||||
|
||||
def default_query_methods_strategy
|
||||
@@default_query_methods_strategy ||= :active_record
|
||||
end
|
||||
|
||||
def default_query_methods_strategy=(strategy)
|
||||
@@default_query_methods_strategy = strategy
|
||||
end
|
||||
end
|
||||
end
|
|
@ -48,7 +48,6 @@ module Draper
|
|||
end
|
||||
|
||||
module ClassMethods
|
||||
|
||||
# Decorates a collection of objects. Used at the end of a scope chain.
|
||||
#
|
||||
# @example
|
||||
|
@ -56,8 +55,7 @@ module Draper
|
|||
# @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))
|
||||
decorator_class.decorate_collection(all, options.reverse_merge(with: nil))
|
||||
end
|
||||
|
||||
def decorator_class?
|
||||
|
@ -70,16 +68,16 @@ module Draper
|
|||
# `Product` maps to `ProductDecorator`).
|
||||
#
|
||||
# @return [Class] the inferred decorator class.
|
||||
def decorator_class
|
||||
def decorator_class(called_on = self)
|
||||
prefix = respond_to?(:model_name) ? model_name : name
|
||||
decorator_name = "#{prefix}Decorator"
|
||||
decorator_name.constantize
|
||||
rescue NameError => error
|
||||
decorator_name_constant = decorator_name.safe_constantize
|
||||
return decorator_name_constant unless decorator_name_constant.nil?
|
||||
|
||||
if superclass.respond_to?(:decorator_class)
|
||||
superclass.decorator_class
|
||||
superclass.decorator_class(called_on)
|
||||
else
|
||||
raise unless error.missing_name?(decorator_name)
|
||||
raise Draper::UninferrableDecoratorError.new(self)
|
||||
raise Draper::UninferrableDecoratorError.new(called_on)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -87,7 +85,7 @@ module Draper
|
|||
#
|
||||
# @return [Boolean]
|
||||
def ===(other)
|
||||
super || (other.respond_to?(:object) && super(other.object))
|
||||
super || (other.is_a?(Draper::Decorator) && super(other.object))
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
module Draper
|
||||
# @private
|
||||
class DecoratedAssociation
|
||||
|
||||
def initialize(owner, association, options)
|
||||
options.assert_valid_keys(:with, :scope, :context)
|
||||
|
||||
|
@ -30,6 +29,5 @@ module Draper
|
|||
|
||||
@decorated = factory.decorate(associated, context_args: owner.context)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
require 'draper/compatibility/global_id'
|
||||
|
||||
module Draper
|
||||
class Decorator
|
||||
include Draper::ViewHelpers
|
||||
include Draper::Compatibility::GlobalID if defined?(GlobalID)
|
||||
extend Draper::Delegation
|
||||
|
||||
include ActiveModel::Serialization
|
||||
|
@ -10,8 +13,6 @@ module Draper
|
|||
# @return the object being decorated.
|
||||
attr_reader :object
|
||||
alias_method :model, :object
|
||||
alias_method :source, :object # TODO: deprecate this
|
||||
alias_method :to_source, :object # TODO: deprecate this
|
||||
|
||||
# @return [Hash] extra data to be used in user-defined methods.
|
||||
attr_accessor :context
|
||||
|
@ -72,15 +73,10 @@ module Draper
|
|||
# Checks whether this decorator class has a corresponding {object_class}.
|
||||
def self.object_class?
|
||||
object_class
|
||||
rescue Draper::UninferrableSourceError
|
||||
rescue Draper::UninferrableObjectError
|
||||
false
|
||||
end
|
||||
|
||||
class << self # TODO deprecate this
|
||||
alias_method :source_class, :object_class
|
||||
alias_method :source_class?, :object_class?
|
||||
end
|
||||
|
||||
# Automatically decorates ActiveRecord finder methods, so that you can use
|
||||
# `ProductDecorator.find(id)` instead of
|
||||
# `ProductDecorator.decorate(Product.find(id))`.
|
||||
|
@ -203,18 +199,6 @@ module Draper
|
|||
super || object.instance_of?(klass)
|
||||
end
|
||||
|
||||
if RUBY_VERSION < "2.0"
|
||||
# nasty hack to stop 1.9.x using the delegated `to_s` in `inspect`
|
||||
alias_method :_to_s, :to_s
|
||||
|
||||
def inspect
|
||||
ivars = instance_variables.map do |name|
|
||||
"#{name}=#{instance_variable_get(name).inspect}"
|
||||
end
|
||||
_to_s.insert(-2, " #{ivars.join(", ")}")
|
||||
end
|
||||
end
|
||||
|
||||
delegate :to_s
|
||||
|
||||
# In case object is nil
|
||||
|
@ -241,10 +225,9 @@ module Draper
|
|||
# @return [Class] the class created by {decorate_collection}.
|
||||
def self.collection_decorator_class
|
||||
name = collection_decorator_name
|
||||
name.constantize
|
||||
rescue NameError => error
|
||||
raise if name && !error.missing_name?(name)
|
||||
Draper::CollectionDecorator
|
||||
name_constant = name && name.safe_constantize
|
||||
|
||||
name_constant || Draper::CollectionDecorator
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -259,22 +242,23 @@ module Draper
|
|||
end
|
||||
|
||||
def self.object_class_name
|
||||
raise NameError if name.nil? || name.demodulize !~ /.+Decorator$/
|
||||
return nil if name.nil? || name.demodulize !~ /.+Decorator$/
|
||||
name.chomp("Decorator")
|
||||
end
|
||||
|
||||
def self.inferred_object_class
|
||||
name = object_class_name
|
||||
name.constantize
|
||||
rescue NameError => error
|
||||
raise if name && !error.missing_name?(name)
|
||||
raise Draper::UninferrableSourceError.new(self)
|
||||
name_constant = name && name.safe_constantize
|
||||
return name_constant unless name_constant.nil?
|
||||
|
||||
raise Draper::UninferrableObjectError.new(self)
|
||||
end
|
||||
|
||||
def self.collection_decorator_name
|
||||
plural = object_class_name.pluralize
|
||||
raise NameError if plural == object_class_name
|
||||
"#{plural}Decorator"
|
||||
singular = object_class_name
|
||||
plural = singular && singular.pluralize
|
||||
|
||||
"#{plural}Decorator" unless plural == singular
|
||||
end
|
||||
|
||||
def handle_multiple_decoration(options)
|
||||
|
|
|
@ -3,7 +3,6 @@ module Draper
|
|||
# do not have to extend this module directly; it is extended by
|
||||
# {Decorator.decorates_finders}.
|
||||
module Finders
|
||||
|
||||
def find(id, options = {})
|
||||
decorate(object_class.find(id), options)
|
||||
end
|
||||
|
|
|
@ -2,11 +2,8 @@ module Draper
|
|||
# Provides access to helper methods - both Rails built-in helpers, and those
|
||||
# defined in your application.
|
||||
class HelperProxy
|
||||
|
||||
# @overload initialize(view_context)
|
||||
def initialize(view_context = nil)
|
||||
view_context ||= current_view_context # backwards compatibility
|
||||
|
||||
def initialize(view_context)
|
||||
@view_context = view_context
|
||||
end
|
||||
|
||||
|
@ -35,10 +32,5 @@ module Draper
|
|||
view_context.send(name, *args, &block)
|
||||
end
|
||||
end
|
||||
|
||||
def current_view_context
|
||||
ActiveSupport::Deprecation.warn("wrong number of arguments (0 for 1) passed to Draper::HelperProxy.new", caller[1..-1])
|
||||
Draper::ViewContext.current.view_context
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,13 +3,11 @@ module Draper
|
|||
# so that you can stop typing `h.` everywhere, at the cost of mixing in a
|
||||
# bazillion methods.
|
||||
module LazyHelpers
|
||||
|
||||
# Sends missing methods to the {HelperProxy}.
|
||||
def method_missing(method, *args, &block)
|
||||
helpers.send(method, *args, &block)
|
||||
rescue NoMethodError
|
||||
super
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
require_relative 'query_methods/load_strategy'
|
||||
|
||||
module Draper
|
||||
module QueryMethods
|
||||
# Proxies missing query methods to the source class if the strategy allows.
|
||||
def method_missing(method, *args, &block)
|
||||
return super unless strategy.allowed? method
|
||||
|
||||
object.send(method, *args, &block).decorate
|
||||
end
|
||||
|
||||
def respond_to_missing?(method, include_private = false)
|
||||
strategy.allowed?(method) || super
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Configures the strategy used to proxy the query methods, which defaults to `:active_record`.
|
||||
def strategy
|
||||
@strategy ||= LoadStrategy.new(Draper.default_query_methods_strategy)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
module Draper
|
||||
module QueryMethods
|
||||
module LoadStrategy
|
||||
def self.new(name)
|
||||
const_get(name.to_s.camelize).new
|
||||
end
|
||||
|
||||
class ActiveRecord
|
||||
def allowed?(method)
|
||||
::ActiveRecord::Relation::VALUE_METHODS.include? method
|
||||
end
|
||||
end
|
||||
|
||||
class Mongoid
|
||||
def allowed?(method)
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,8 +3,6 @@ require 'rails/railtie'
|
|||
module ActiveModel
|
||||
class Railtie < Rails::Railtie
|
||||
generators do |app|
|
||||
app ||= Rails.application # Rails 3.0.x does not yield `app`
|
||||
|
||||
Rails::Generators.configure! app.config.generators
|
||||
require_relative '../generators/controller_override'
|
||||
end
|
||||
|
@ -13,7 +11,6 @@ end
|
|||
|
||||
module Draper
|
||||
class Railtie < Rails::Railtie
|
||||
|
||||
config.after_initialize do |app|
|
||||
app.config.paths.add 'app/decorators', eager_load: true
|
||||
|
||||
|
@ -23,19 +20,19 @@ module Draper
|
|||
end
|
||||
end
|
||||
|
||||
initializer "draper.setup_action_controller" do |app|
|
||||
initializer 'draper.setup_action_controller' do
|
||||
ActiveSupport.on_load :action_controller do
|
||||
Draper.setup_action_controller self
|
||||
end
|
||||
end
|
||||
|
||||
initializer "draper.setup_action_mailer" do |app|
|
||||
initializer 'draper.setup_action_mailer' do
|
||||
ActiveSupport.on_load :action_mailer do
|
||||
Draper.setup_action_mailer self
|
||||
end
|
||||
end
|
||||
|
||||
initializer "draper.setup_orm" do |app|
|
||||
initializer 'draper.setup_orm' do
|
||||
[:active_record, :mongoid].each do |orm|
|
||||
ActiveSupport.on_load orm do
|
||||
Draper.setup_orm self
|
||||
|
@ -43,28 +40,22 @@ module Draper
|
|||
end
|
||||
end
|
||||
|
||||
initializer "draper.setup_active_model_serializers" do |app|
|
||||
ActiveSupport.on_load :active_model_serializers do
|
||||
if defined?(ActiveModel::ArraySerializerSupport)
|
||||
Draper::CollectionDecorator.send :include, ActiveModel::ArraySerializerSupport
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
initializer "draper.minitest-rails_integration" do |app|
|
||||
initializer 'draper.minitest-rails_integration' do
|
||||
ActiveSupport.on_load :minitest do
|
||||
require "draper/test/minitest_integration"
|
||||
require 'draper/test/minitest_integration'
|
||||
end
|
||||
end
|
||||
|
||||
console do
|
||||
def initialize_view_context
|
||||
require 'action_controller/test_case'
|
||||
ApplicationController.new.view_context
|
||||
Draper.default_controller.new.view_context
|
||||
Draper::ViewContext.build
|
||||
end
|
||||
|
||||
rake_tasks do
|
||||
Dir[File.join(File.dirname(__FILE__),'tasks/*.rake')].each { |f| load f }
|
||||
end
|
||||
console { initialize_view_context }
|
||||
|
||||
runner { initialize_view_context }
|
||||
|
||||
rake_tasks { Dir[File.join(File.dirname(__FILE__), 'tasks/*.rake')].each { |f| load f } }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,22 +1,9 @@
|
|||
require 'rake/testtask'
|
||||
|
||||
test_task = if Rails.version.to_f < 3.2
|
||||
require 'rails/test_unit/railtie'
|
||||
Rake::TestTask
|
||||
else
|
||||
require 'rails/test_unit/sub_test_task'
|
||||
Rails::SubTestTask
|
||||
end
|
||||
require 'rails/test_unit/railtie'
|
||||
|
||||
namespace :test do
|
||||
test_task.new(:decorators => "test:prepare") do |t|
|
||||
Rake::TestTask.new(decorators: "test:prepare") do |t|
|
||||
t.libs << "test"
|
||||
t.pattern = "test/decorators/**/*_test.rb"
|
||||
end
|
||||
end
|
||||
|
||||
if Rails.version.to_f < 4.2 && Rake::Task.task_defined?('test:run')
|
||||
Rake::Task['test:run'].enhance do
|
||||
Rake::Task['test:decorators'].invoke
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,14 +1,7 @@
|
|||
module Draper
|
||||
module DeviseHelper
|
||||
def sign_in(resource_or_scope, resource = nil)
|
||||
scope = begin
|
||||
Devise::Mapping.find_scope!(resource_or_scope)
|
||||
rescue RuntimeError => e
|
||||
# Draper 1.0 didn't require the mapping to exist
|
||||
ActiveSupport::Deprecation.warn("#{e.message}.\nUse `sign_in :user, mock_user` instead.", caller)
|
||||
:user
|
||||
end
|
||||
|
||||
scope = Devise::Mapping.find_scope!(resource_or_scope)
|
||||
_stub_current_scope scope, resource || resource_or_scope
|
||||
end
|
||||
|
||||
|
|
|
@ -7,11 +7,7 @@ module Draper
|
|||
end
|
||||
|
||||
RSpec.configure do |config|
|
||||
if RSpec::Core::Version::STRING.starts_with?("3")
|
||||
config.include DecoratorExampleGroup, file_path: %r{spec/decorators}, type: :decorator
|
||||
else
|
||||
config.include DecoratorExampleGroup, example_group: {file_path: %r{spec/decorators}}, type: :decorator
|
||||
end
|
||||
|
||||
[:decorator, :controller, :mailer].each do |type|
|
||||
config.before(:each, type: type) { Draper::ViewContext.clear! }
|
||||
|
|
|
@ -3,9 +3,9 @@ module Draper
|
|||
|
||||
class TestCase < ::ActiveSupport::TestCase
|
||||
module ViewContextTeardown
|
||||
def teardown
|
||||
super
|
||||
def before_setup
|
||||
Draper::ViewContext.clear!
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -29,14 +29,10 @@ module Draper
|
|||
end
|
||||
end
|
||||
|
||||
if defined?(ActionController::TestCase)
|
||||
class ActionController::TestCase
|
||||
include Draper::TestCase::ViewContextTeardown
|
||||
end
|
||||
if defined? ActionController::TestCase
|
||||
ActionController::TestCase.include Draper::TestCase::ViewContextTeardown
|
||||
end
|
||||
|
||||
if defined?(ActionMailer::TestCase)
|
||||
class ActionMailer::TestCase
|
||||
include Draper::TestCase::ViewContextTeardown
|
||||
end
|
||||
if defined? ActionMailer::TestCase
|
||||
ActionMailer::TestCase.include Draper::TestCase::ViewContextTeardown
|
||||
end
|
||||
|
|
|
@ -6,4 +6,12 @@ module Draper
|
|||
object
|
||||
end
|
||||
end
|
||||
|
||||
def self.undecorate_chain(object)
|
||||
if object.respond_to?(:decorated?) && object.decorated?
|
||||
undecorate_chain(object.object)
|
||||
else
|
||||
object
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
module Draper
|
||||
VERSION = "2.1.0"
|
||||
VERSION = '3.1.0'
|
||||
end
|
||||
|
|
|
@ -20,8 +20,10 @@ module Draper
|
|||
RequestStore.store[:current_controller]
|
||||
end
|
||||
|
||||
# Sets the current controller.
|
||||
# Sets the current controller. Clears view context when we are setting
|
||||
# different controller.
|
||||
def self.controller=(controller)
|
||||
clear! if RequestStore.store[:current_controller] != controller
|
||||
RequestStore.store[:current_controller] = controller
|
||||
end
|
||||
|
||||
|
@ -82,23 +84,5 @@ module Draper
|
|||
def self.build_strategy
|
||||
@build_strategy ||= Draper::ViewContext::BuildStrategy.new(:full)
|
||||
end
|
||||
|
||||
# @deprecated Use {controller} instead.
|
||||
def self.current_controller
|
||||
ActiveSupport::Deprecation.warn("Draper::ViewContext.current_controller is deprecated (use controller instead)", caller)
|
||||
self.controller || ApplicationController.new
|
||||
end
|
||||
|
||||
# @deprecated Use {controller=} instead.
|
||||
def self.current_controller=(controller)
|
||||
ActiveSupport::Deprecation.warn("Draper::ViewContext.current_controller= is deprecated (use controller instead)", caller)
|
||||
self.controller = controller
|
||||
end
|
||||
|
||||
# @deprecated Use {build} instead.
|
||||
def self.build_view_context
|
||||
ActiveSupport::Deprecation.warn("Draper::ViewContext.build_view_context is deprecated (use build instead)", caller)
|
||||
build
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,7 +2,6 @@ module Draper
|
|||
module ViewContext
|
||||
# @private
|
||||
module BuildStrategy
|
||||
|
||||
def self.new(name, &block)
|
||||
const_get(name.to_s.camelize).new(&block)
|
||||
end
|
||||
|
@ -37,12 +36,20 @@ module Draper
|
|||
attr_reader :block
|
||||
|
||||
def controller
|
||||
(Draper::ViewContext.controller || ApplicationController.new).tap do |controller|
|
||||
controller.request ||= ActionController::TestRequest.new if defined?(ActionController::TestRequest)
|
||||
end
|
||||
Draper::ViewContext.controller ||= Draper.default_controller.new
|
||||
Draper::ViewContext.controller.tap do |controller|
|
||||
controller.request ||= new_test_request controller if defined?(ActionController::TestRequest)
|
||||
end
|
||||
end
|
||||
|
||||
def new_test_request(controller)
|
||||
is_above_rails_5_1 ? ActionController::TestRequest.create(controller) : ActionController::TestRequest.create
|
||||
end
|
||||
|
||||
def is_above_rails_5_1
|
||||
ActionController::TestRequest.method(:create).parameters.first == [:req, :controller_class]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,6 @@ module Draper
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
|
||||
# Access the helpers proxy to call built-in and user-defined
|
||||
# Rails helpers from a class context.
|
||||
#
|
||||
|
@ -14,7 +13,6 @@ module Draper
|
|||
Draper::ViewContext.current
|
||||
end
|
||||
alias_method :h, :helpers
|
||||
|
||||
end
|
||||
|
||||
# Access the helpers proxy to call built-in and user-defined
|
||||
|
@ -32,6 +30,5 @@ module Draper
|
|||
helpers.localize(*args)
|
||||
end
|
||||
alias_method :l, :localize
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,13 +5,13 @@ require "rails/generators/rails/scaffold_controller/scaffold_controller_generato
|
|||
module Rails
|
||||
module Generators
|
||||
class ControllerGenerator
|
||||
hook_for :decorator, default: true do |generator|
|
||||
hook_for :decorator, type: :boolean, default: true do |generator|
|
||||
invoke generator, [name.singularize]
|
||||
end
|
||||
end
|
||||
|
||||
class ScaffoldControllerGenerator
|
||||
hook_for :decorator, default: true
|
||||
hook_for :decorator, type: :boolean, default: true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
module Draper
|
||||
module Generators
|
||||
class InstallGenerator < Rails::Generators::Base
|
||||
source_root File.expand_path("templates", __dir__)
|
||||
|
||||
desc 'Creates an ApplicationDecorator, if none exists.'
|
||||
|
||||
def create_application_decorator
|
||||
file = 'application_decorator.rb'
|
||||
copy_file file, "app/decorators/#{file}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
class ApplicationDecorator < Draper::Decorator
|
||||
# Define methods for all decorated objects.
|
||||
# Helpers are accessed through `helpers` (aka `h`). For example:
|
||||
#
|
||||
# def percent_amount
|
||||
# h.number_to_percentage object.amount, precision: 2
|
||||
# end
|
||||
end
|
|
@ -4,10 +4,10 @@ module MiniTest
|
|||
module Generators
|
||||
class DecoratorGenerator < Base
|
||||
def self.source_root
|
||||
File.expand_path('../templates', __FILE__)
|
||||
File.expand_path("templates", __dir__)
|
||||
end
|
||||
|
||||
class_option :spec, :type => :boolean, :default => false, :desc => "Use MiniTest::Spec DSL"
|
||||
class_option :spec, type: :boolean, default: false, desc: "Use MiniTest::Spec DSL"
|
||||
|
||||
check_class_collision suffix: "DecoratorTest"
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module Rails
|
||||
module Generators
|
||||
class DecoratorGenerator < NamedBase
|
||||
source_root File.expand_path("../templates", __FILE__)
|
||||
source_root File.expand_path("templates", __dir__)
|
||||
check_class_collision suffix: "Decorator"
|
||||
|
||||
class_option :parent, type: :string, desc: "The parent class for the generated decorator"
|
||||
|
@ -24,13 +24,6 @@ module Rails
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Rails 3.0.X compatibility, stolen from https://github.com/jnunemaker/mongomapper/pull/385/files#L1R32
|
||||
unless methods.include?(:module_namespacing)
|
||||
def module_namespacing
|
||||
yield if block_given?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
module Rspec
|
||||
module Generators
|
||||
class DecoratorGenerator < ::Rails::Generators::NamedBase
|
||||
source_root File.expand_path('../templates', __FILE__)
|
||||
source_root File.expand_path("templates", __dir__)
|
||||
|
||||
def create_spec_file
|
||||
template 'decorator_spec.rb', File.join('spec/decorators', class_path, "#{singular_name}_decorator_spec.rb")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
module TestUnit
|
||||
module Generators
|
||||
class DecoratorGenerator < ::Rails::Generators::NamedBase
|
||||
source_root File.expand_path('../templates', __FILE__)
|
||||
source_root File.expand_path("templates", __dir__)
|
||||
check_class_collision suffix: "DecoratorTest"
|
||||
|
||||
def create_test_file
|
||||
template 'decorator_test.rb', File.join('test/decorators', class_path, "#{singular_name}_decorator_test.rb")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,7 +7,6 @@ module Draper
|
|||
|
||||
describe "#initialize" do
|
||||
describe "options validation" do
|
||||
|
||||
it "does not raise error on valid options" do
|
||||
valid_options = {with: Decorator, context: {}}
|
||||
expect{CollectionDecorator.new([], valid_options)}.not_to raise_error
|
||||
|
@ -121,7 +120,6 @@ module Draper
|
|||
end
|
||||
|
||||
describe "#find" do
|
||||
context "with a block" do
|
||||
it "decorates Enumerable#find" do
|
||||
decorator = CollectionDecorator.new([])
|
||||
|
||||
|
@ -130,20 +128,6 @@ module Draper
|
|||
end
|
||||
end
|
||||
|
||||
context "without a block" do
|
||||
it "decorates object.find" do
|
||||
object = []
|
||||
found = double(decorate: :decorated)
|
||||
decorator = CollectionDecorator.new(object)
|
||||
|
||||
expect(object).to receive(:find).and_return(found)
|
||||
ActiveSupport::Deprecation.silence do
|
||||
expect(decorator.find(1)).to be :decorated
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#to_ary" do
|
||||
# required for `render @collection` in Rails
|
||||
it "delegates to the decorated collection" do
|
||||
|
@ -157,7 +141,7 @@ module Draper
|
|||
it "delegates array methods to the decorated collection" do
|
||||
decorator = CollectionDecorator.new([])
|
||||
|
||||
expect(decorator.decorated_collection).to receive(:[]).with(42).and_return(:delegated)
|
||||
allow(decorator.decorated_collection).to receive(:[]).with(42).and_return(:delegated)
|
||||
expect(decorator[42]).to be :delegated
|
||||
end
|
||||
|
||||
|
@ -302,6 +286,5 @@ module Draper
|
|||
expect(decorator.replace([:foo, :bar])).to be decorator
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
require 'spec_helper'
|
||||
|
||||
module Draper
|
||||
RSpec.describe Configuration do
|
||||
it 'yields Draper on configure' do
|
||||
Draper.configure { |config| expect(config).to be Draper }
|
||||
end
|
||||
|
||||
describe '#default_controller' do
|
||||
it 'defaults default_controller to ApplicationController' do
|
||||
expect(Draper.default_controller).to be ApplicationController
|
||||
end
|
||||
|
||||
it 'allows customizing default_controller through configure' do
|
||||
default = Draper.default_controller
|
||||
|
||||
Draper.configure do |config|
|
||||
config.default_controller = CustomController
|
||||
end
|
||||
|
||||
expect(Draper.default_controller).to be CustomController
|
||||
|
||||
Draper.default_controller = default
|
||||
end
|
||||
end
|
||||
|
||||
describe '#default_query_methods_strategy' do
|
||||
let!(:default) { Draper.default_query_methods_strategy }
|
||||
|
||||
subject { Draper.default_query_methods_strategy }
|
||||
|
||||
context 'when there is no custom strategy' do
|
||||
it { is_expected.to eq(:active_record) }
|
||||
end
|
||||
|
||||
context 'when using a custom strategy' do
|
||||
before do
|
||||
Draper.configure do |config|
|
||||
config.default_query_methods_strategy = :mongoid
|
||||
end
|
||||
end
|
||||
|
||||
after { Draper.default_query_methods_strategy = default }
|
||||
|
||||
it { is_expected.to eq(:mongoid) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -22,7 +22,8 @@ module Draper
|
|||
|
||||
it "uses the #decorator_class" do
|
||||
product = Product.new
|
||||
allow(product).to receive(:decorator_class) { OtherDecorator }
|
||||
allow(product).to receive_messages decorator_class: OtherDecorator
|
||||
|
||||
expect(product.decorate).to be_an_instance_of OtherDecorator
|
||||
end
|
||||
end
|
||||
|
@ -72,6 +73,16 @@ module Draper
|
|||
expect(Product).to receive(:decorator_class).and_return(:some_decorator)
|
||||
expect(product.decorator_class).to be :some_decorator
|
||||
end
|
||||
|
||||
it "specifies the class that #decorator_class was first called on (superclass)" do
|
||||
person = Person.new
|
||||
expect { person.decorator_class }.to raise_error(Draper::UninferrableDecoratorError, 'Could not infer a decorator for Person.')
|
||||
end
|
||||
|
||||
it "specifies the class that #decorator_class was first called on (subclass)" do
|
||||
child = Child.new
|
||||
expect { child.decorator_class }.to raise_error(Draper::UninferrableDecoratorError, 'Could not infer a decorator for Child.')
|
||||
end
|
||||
end
|
||||
|
||||
describe "#==" do
|
||||
|
@ -108,37 +119,42 @@ module Draper
|
|||
end
|
||||
|
||||
it "is true for a decorated instance" do
|
||||
decorator = double(object: Product.new)
|
||||
decorator = Product.new.decorate
|
||||
|
||||
expect(Product === decorator).to be_truthy
|
||||
end
|
||||
|
||||
it "is true for a decorated derived instance" do
|
||||
decorator = double(object: Class.new(Product).new)
|
||||
decorator = Class.new(Product).new.decorate
|
||||
|
||||
expect(Product === decorator).to be_truthy
|
||||
end
|
||||
|
||||
it "is false for a decorated unrelated instance" do
|
||||
decorator = double(object: Model.new)
|
||||
decorator = Other.new.decorate
|
||||
|
||||
expect(Product === decorator).to be_falsey
|
||||
end
|
||||
|
||||
it "is false for a non-decorator which happens to respond to object" do
|
||||
decorator = double(object: Product.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]
|
||||
allow(Product).to receive(scoping_method).and_return(scoped)
|
||||
allow(Product).to receive(:all).and_return(scoped)
|
||||
|
||||
expect(Product.decorator_class).to 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"}}
|
||||
allow(Product).to receive(scoping_method).and_return([])
|
||||
allow(Product).to receive(:all).and_return([])
|
||||
|
||||
expect(Product.decorator_class).to receive(:decorate_collection).with([], options)
|
||||
Product.decorate(options)
|
||||
|
@ -182,6 +198,15 @@ module Draper
|
|||
end
|
||||
end
|
||||
|
||||
context "when the decorator contains name error" do
|
||||
it "throws an NameError" do
|
||||
# We imitate ActiveSupport::Autoload behavior here in order to cause lazy NameError exception raising
|
||||
allow_any_instance_of(Module).to receive(:const_missing) { Class.new { any_nonexisting_method_name } }
|
||||
|
||||
expect{Model.decorator_class}.to raise_error { |error| expect(error).to be_an_instance_of(NameError) }
|
||||
end
|
||||
end
|
||||
|
||||
context "when the decorator can't be inferred" do
|
||||
it "throws an UninferrableDecoratorError" do
|
||||
expect{Model.decorator_class}.to raise_error UninferrableDecoratorError
|
||||
|
@ -190,10 +215,22 @@ module Draper
|
|||
|
||||
context "when an unrelated NameError is thrown" do
|
||||
it "re-raises that error" do
|
||||
allow_any_instance_of(String).to receive(:constantize) { Draper::Base }
|
||||
# Not related to safe_constantize behavior, we just want to raise a NameError inside the function
|
||||
allow_any_instance_of(String).to receive(:safe_constantize) { Draper::Base }
|
||||
expect{Product.decorator_class}.to raise_error NameError, /Draper::Base/
|
||||
end
|
||||
end
|
||||
|
||||
context "when an anonymous class is given" do
|
||||
it "infers the decorator from a superclass" do
|
||||
anonymous_class = Class.new(Product) do
|
||||
def self.name
|
||||
to_s
|
||||
end
|
||||
end
|
||||
expect(anonymous_class.decorator_class).to be ProductDecorator
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
module Draper
|
||||
RSpec.describe DecoratedAssociation do
|
||||
|
||||
Rspec.describe DecoratedAssociation do
|
||||
describe "#initialize" do
|
||||
it "accepts valid options" do
|
||||
valid_options = {with: Decorator, scope: :foo, context: {}}
|
||||
|
@ -40,7 +39,7 @@ module Draper
|
|||
describe "#call" do
|
||||
it "calls the factory" do
|
||||
factory = double
|
||||
allow(Factory).to receive(:new).and_return(factory)
|
||||
allow(Factory).to receive_messages(new: factory)
|
||||
associated = double
|
||||
owner_context = {foo: "bar"}
|
||||
object = double(association: associated)
|
||||
|
@ -54,7 +53,7 @@ module Draper
|
|||
|
||||
it "memoizes" do
|
||||
factory = double
|
||||
allow(Factory).to receive(:new).and_return(factory)
|
||||
allow(Factory).to receive_messages(new: factory)
|
||||
owner = double(object: double(association: double), context: {})
|
||||
decorated_association = DecoratedAssociation.new(owner, :association, {})
|
||||
decorated = double
|
||||
|
@ -67,7 +66,7 @@ module Draper
|
|||
context "when the :scope option was given" do
|
||||
it "applies the scope before decoration" do
|
||||
factory = double
|
||||
allow(Factory).to receive(:new).and_return(factory)
|
||||
allow(Factory).to receive_messages(new: factory)
|
||||
scoped = double
|
||||
object = double(association: double(applied_scope: scoped))
|
||||
owner = double(object: object, context: {})
|
||||
|
@ -79,6 +78,5 @@ module Draper
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
module Draper
|
||||
RSpec.describe DecoratesAssigned do
|
||||
describe DecoratesAssigned do
|
||||
let(:controller_class) do
|
||||
Class.new do
|
||||
extend DecoratesAssigned
|
||||
|
@ -28,14 +28,14 @@ module Draper
|
|||
end
|
||||
|
||||
it "creates a factory" do
|
||||
expect(Factory).to receive(:new).once
|
||||
allow(Factory).to receive(:new).once
|
||||
controller_class.decorates_assigned :article, :author
|
||||
end
|
||||
|
||||
it "passes options to the factory" do
|
||||
options = {foo: "bar"}
|
||||
|
||||
expect(Factory).to receive(:new).with(options)
|
||||
allow(Factory).to receive(:new).with(options)
|
||||
controller_class.decorates_assigned :article, :author, options
|
||||
end
|
||||
|
||||
|
@ -43,7 +43,7 @@ module Draper
|
|||
it "decorates the instance variable" do
|
||||
object = double
|
||||
factory = double
|
||||
allow(Factory).to receive(:new).and_return(factory)
|
||||
allow(Factory).to receive_messages(new: factory)
|
||||
|
||||
controller_class.decorates_assigned :article
|
||||
controller = controller_class.new
|
||||
|
@ -55,7 +55,7 @@ module Draper
|
|||
|
||||
it "memoizes" do
|
||||
factory = double
|
||||
allow(Factory).to receive(:new).and_return(factory)
|
||||
allow(Factory).to receive_messages(new: factory)
|
||||
|
||||
controller_class.decorates_assigned :article
|
||||
controller = controller_class.new
|
||||
|
@ -66,5 +66,6 @@ module Draper
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -145,13 +145,6 @@ module Draper
|
|||
ProductDecorator.decorate_collection([], options)
|
||||
end
|
||||
end
|
||||
|
||||
context "when a NameError is thrown" do
|
||||
it "re-raises that error" do
|
||||
allow_any_instance_of(String).to receive(:constantize) { Draper::DecoratedEnumerableProxy }
|
||||
expect{ProductDecorator.decorate_collection([])}.to raise_error NameError, /Draper::DecoratedEnumerableProxy/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".decorates" do
|
||||
|
@ -181,42 +174,40 @@ module Draper
|
|||
protect_class Namespaced::ProductDecorator
|
||||
|
||||
context "when not set by .decorates" do
|
||||
it "raises an UninferrableSourceError for a so-named 'Decorator'" do
|
||||
expect{Decorator.object_class}.to raise_error UninferrableSourceError
|
||||
it "raises an UninferrableObjectError for a so-named 'Decorator'" do
|
||||
expect{Decorator.object_class}.to raise_error UninferrableObjectError
|
||||
end
|
||||
|
||||
it "raises an UninferrableSourceError for anonymous decorators" do
|
||||
expect{Class.new(Decorator).object_class}.to raise_error UninferrableSourceError
|
||||
it "raises an UninferrableObjectError for anonymous decorators" do
|
||||
expect{Class.new(Decorator).object_class}.to raise_error UninferrableObjectError
|
||||
end
|
||||
|
||||
it "raises an UninferrableSourceError for a decorator without a model" do
|
||||
skip
|
||||
expect{OtherDecorator.object_class}.to raise_error UninferrableSourceError
|
||||
it "raises an UninferrableObjectError for a decorator without a model" do
|
||||
SomeDecorator = Class.new(Draper::Decorator)
|
||||
expect{SomeDecorator.object_class}.to raise_error UninferrableObjectError
|
||||
end
|
||||
|
||||
it "raises an UninferrableSourceError for other naming conventions" do
|
||||
expect{ProductPresenter.object_class}.to raise_error UninferrableSourceError
|
||||
it "raises an UninferrableObjectError for other naming conventions" do
|
||||
ProductPresenter = Class.new(Draper::Decorator)
|
||||
expect{ProductPresenter.object_class}.to raise_error UninferrableObjectError
|
||||
end
|
||||
|
||||
it "infers the source for '<Model>Decorator'" do
|
||||
it "infers the object class for '<Model>Decorator'" do
|
||||
expect(ProductDecorator.object_class).to be Product
|
||||
end
|
||||
|
||||
it "infers namespaced sources" do
|
||||
it "infers the object class for namespaced decorators" do
|
||||
expect(Namespaced::ProductDecorator.object_class).to be Namespaced::Product
|
||||
end
|
||||
|
||||
context "when an unrelated NameError is thrown" do
|
||||
it "re-raises that error" do
|
||||
allow_any_instance_of(String).to receive(:constantize) { SomethingThatDoesntExist }
|
||||
# Not related to safe_constantize behavior, we just want to raise a NameError inside the function
|
||||
allow_any_instance_of(String).to receive(:safe_constantize) { SomethingThatDoesntExist }
|
||||
expect{ProductDecorator.object_class}.to raise_error NameError, /SomethingThatDoesntExist/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "is aliased to .source_class" do
|
||||
expect(ProductDecorator.source_class).to be Product
|
||||
end
|
||||
end
|
||||
|
||||
describe ".object_class?" do
|
||||
|
@ -227,13 +218,24 @@ module Draper
|
|||
end
|
||||
|
||||
it "returns false when .object_class is not inferrable" do
|
||||
allow(Decorator).to receive(:object_class).and_raise(UninferrableSourceError.new(Decorator))
|
||||
allow(Decorator).to receive(:object_class).and_raise(UninferrableObjectError.new(Decorator))
|
||||
|
||||
expect(Decorator.object_class?).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
it "is aliased to .source_class?" do
|
||||
allow(Decorator).to receive(:object_class).and_return(Model)
|
||||
expect(Decorator.source_class?).to be_truthy
|
||||
describe '.collection_decorator_class' do
|
||||
it 'defaults to CollectionDecorator' do
|
||||
allow_any_instance_of(String).to receive(:safe_constantize) { nil }
|
||||
expect(ProductDecorator.collection_decorator_class).to be Draper::CollectionDecorator
|
||||
end
|
||||
|
||||
it 'infers collection decorator based on name' do
|
||||
expect(ProductDecorator.collection_decorator_class).to be ProductsDecorator
|
||||
end
|
||||
|
||||
it 'infers collection decorator base on name for namespeced model' do
|
||||
expect(Namespaced::ProductDecorator.collection_decorator_class).to be Namespaced::ProductsDecorator
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -335,7 +337,6 @@ module Draper
|
|||
|
||||
expect(decorator.object).to be object
|
||||
expect(decorator.model).to be object
|
||||
expect(decorator.to_source).to be object
|
||||
end
|
||||
|
||||
it "is aliased to #model" do
|
||||
|
@ -344,20 +345,6 @@ module Draper
|
|||
|
||||
expect(decorator.model).to be object
|
||||
end
|
||||
|
||||
it "is aliased to #source" do
|
||||
object = Model.new
|
||||
decorator = Decorator.new(object)
|
||||
|
||||
expect(decorator.source).to be object
|
||||
end
|
||||
|
||||
it "is aliased to #to_source" do
|
||||
object = Model.new
|
||||
decorator = Decorator.new(object)
|
||||
|
||||
expect(decorator.to_source).to be object
|
||||
end
|
||||
end
|
||||
|
||||
describe "aliasing object to object class name" do
|
||||
|
@ -479,13 +466,15 @@ module Draper
|
|||
it "returns only the object's attributes that are implemented by the decorator" do
|
||||
decorator = Decorator.new(double(attributes: {foo: "bar", baz: "qux"}))
|
||||
allow(decorator).to receive(:foo)
|
||||
|
||||
expect(decorator.attributes).to eq({foo: "bar"})
|
||||
end
|
||||
end
|
||||
|
||||
describe ".model_name" do
|
||||
it "delegates to the source class" do
|
||||
allow(Decorator).to receive(:object_class) { double(model_name: :delegated) }
|
||||
it "delegates to the object class" do
|
||||
allow(Decorator).to receive(:object_class).and_return(double(model_name: :delegated))
|
||||
|
||||
expect(Decorator.model_name).to be :delegated
|
||||
end
|
||||
end
|
||||
|
@ -592,12 +581,51 @@ module Draper
|
|||
expect(decorator.hello_world).to be :delegated
|
||||
end
|
||||
|
||||
it "adds delegated methods to the decorator when they are used" do
|
||||
decorator = Decorator.new(double(hello_world: :delegated))
|
||||
it 'delegates `super` to parent class first' do
|
||||
parent_decorator_class = Class.new(Decorator) do
|
||||
def hello_world
|
||||
"parent#hello_world"
|
||||
end
|
||||
end
|
||||
|
||||
expect(decorator.methods).not_to include :hello_world
|
||||
decorator.hello_world
|
||||
expect(decorator.methods).to include :hello_world
|
||||
child_decorator_class = Class.new(parent_decorator_class) do
|
||||
def hello_world
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
decorator = child_decorator_class.new(double(hello_world: 'object#hello_world'))
|
||||
expect(decorator.hello_world).to eq 'parent#hello_world'
|
||||
end
|
||||
|
||||
it 'delegates `super` to object if method does not exist on parent class' do
|
||||
decorator_class = Class.new(Decorator) do
|
||||
def hello_world
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
decorator = decorator_class.new(double(hello_world: 'object#hello_world'))
|
||||
expect(decorator.hello_world).to eq 'object#hello_world'
|
||||
end
|
||||
|
||||
it 'raises `NoMethodError` when `super` is called on for method that does not exist' do
|
||||
decorator_class = Class.new(Decorator) do
|
||||
def hello_world
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
decorator = decorator_class.new(double)
|
||||
expect{decorator.hello_world}.to raise_error NoMethodError
|
||||
end
|
||||
|
||||
it "allows decorator to decorate different classes of objects" do
|
||||
decorator_1 = Decorator.new(double)
|
||||
decorator_2 = Decorator.new(double(hello_world: :delegated))
|
||||
|
||||
decorator_2.hello_world
|
||||
expect(decorator_1.methods).not_to include :hello_world
|
||||
end
|
||||
|
||||
it "passes blocks to delegated methods" do
|
||||
|
@ -616,7 +644,7 @@ module Draper
|
|||
|
||||
it "delegates already-delegated methods" do
|
||||
object = Class.new{ delegate :bar, to: :foo }.new
|
||||
allow(object).to receive(:foo) { double(bar: :delegated) }
|
||||
allow(object).to receive_messages foo: double(bar: :delegated)
|
||||
decorator = Decorator.new(object)
|
||||
|
||||
expect(decorator.bar).to be :delegated
|
||||
|
@ -636,26 +664,47 @@ module Draper
|
|||
expect{decorator.hello_world}.to raise_error NoMethodError
|
||||
expect(decorator.methods).not_to include :hello_world
|
||||
end
|
||||
|
||||
context 'when decorator overrides a public method defined on the object with a private' do
|
||||
let(:decorator_class) do
|
||||
Class.new(Decorator) do
|
||||
private
|
||||
|
||||
def hello_world
|
||||
'hello world'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
let(:object) { Class.new { def hello_world; end }.new }
|
||||
|
||||
it 'does not delegate the public method defined on the object' do
|
||||
decorator = decorator_class.new(object)
|
||||
|
||||
expect{ decorator.hello_world }.to raise_error NoMethodError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context ".method_missing" do
|
||||
context "without a source class" do
|
||||
context "without an object class" do
|
||||
it "raises a NoMethodError on missing methods" do
|
||||
expect{Decorator.hello_world}.to raise_error NoMethodError
|
||||
end
|
||||
end
|
||||
|
||||
context "with a source class" do
|
||||
it "delegates methods that exist on the source class" do
|
||||
context "with an object class" do
|
||||
it "delegates methods that exist on the object class" do
|
||||
object_class = Class.new
|
||||
allow(object_class).to receive(:hello_world).and_return(:delegated)
|
||||
allow(Decorator).to receive(:object_class).and_return(object_class)
|
||||
allow(object_class).to receive_messages hello_world: :delegated
|
||||
allow(Decorator).to receive_messages object_class: object_class
|
||||
|
||||
expect(Decorator.hello_world).to be :delegated
|
||||
end
|
||||
|
||||
it "does not delegate methods that do not exist on the source class" do
|
||||
allow(Decorator).to receive(:object_class) { Class.new }
|
||||
it "does not delegate methods that do not exist on the object class" do
|
||||
allow(Decorator).to receive_messages object_class: Class.new
|
||||
|
||||
expect{Decorator.hello_world}.to raise_error NoMethodError
|
||||
end
|
||||
end
|
||||
|
@ -693,7 +742,7 @@ module Draper
|
|||
end
|
||||
|
||||
describe ".respond_to?" do
|
||||
context "without a source class" do
|
||||
context "without a object class" do
|
||||
it "returns true for its own class methods" do
|
||||
Decorator.class_eval{def self.hello_world; end}
|
||||
|
||||
|
@ -705,16 +754,16 @@ module Draper
|
|||
end
|
||||
end
|
||||
|
||||
context "with a source class" do
|
||||
context "with a object class" do
|
||||
it "returns true for its own class methods" do
|
||||
Decorator.class_eval{def self.hello_world; end}
|
||||
allow(Decorator).to receive(:object_class) { Class.new }
|
||||
allow(Decorator).to receive_messages object_class: Class.new
|
||||
|
||||
expect(Decorator).to respond_to :hello_world
|
||||
end
|
||||
|
||||
it "returns true for the source's class methods" do
|
||||
allow(Decorator).to receive(:object_class) { double(hello_world: :delegated) }
|
||||
it "returns true for the object's class methods" do
|
||||
allow(Decorator).to receive_messages object_class: double(hello_world: :delegated)
|
||||
|
||||
expect(Decorator).to respond_to :hello_world
|
||||
end
|
||||
|
@ -732,7 +781,7 @@ module Draper
|
|||
|
||||
describe ".respond_to_missing?" do
|
||||
it "allows .method to be called on delegated class methods" do
|
||||
allow(Decorator).to receive(:object_class) { double(hello_world: :delegated) }
|
||||
allow(Decorator).to receive_messages object_class: double(hello_world: :delegated)
|
||||
|
||||
expect(Decorator.method(:hello_world)).not_to be_nil
|
||||
end
|
||||
|
@ -740,7 +789,7 @@ module Draper
|
|||
end
|
||||
|
||||
describe "class spoofing" do
|
||||
it "pretends to be a kind of the source class" do
|
||||
it "pretends to be a kind of the object class" do
|
||||
decorator = Decorator.new(Model.new)
|
||||
|
||||
expect(decorator.kind_of?(Model)).to be_truthy
|
||||
|
@ -754,7 +803,7 @@ module Draper
|
|||
expect(decorator.is_a?(Decorator)).to be_truthy
|
||||
end
|
||||
|
||||
it "pretends to be an instance of the source class" do
|
||||
it "pretends to be an instance of the object class" do
|
||||
decorator = Decorator.new(Model.new)
|
||||
|
||||
expect(decorator.instance_of?(Model)).to be_truthy
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
require 'spec_helper'
|
||||
require 'support/shared_examples/view_helpers'
|
||||
SimpleCov.command_name 'test:unit'
|
||||
|
||||
module Draper
|
||||
describe Draper do
|
||||
describe '.setup_action_controller' do
|
||||
it 'includes api only compatability if base is ActionController::API' do
|
||||
base = ActionController::API
|
||||
|
||||
Draper.setup_action_controller(base)
|
||||
|
||||
expect(base.included_modules).to include(Draper::Compatibility::ApiOnly)
|
||||
end
|
||||
|
||||
it 'does not include api only compatibility if base ActionController::Base' do
|
||||
base = ActionController::Base
|
||||
|
||||
Draper.setup_action_controller(base)
|
||||
|
||||
expect(base.included_modules).not_to include(Draper::Compatibility::ApiOnly)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,8 +1,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
module Draper
|
||||
RSpec.describe Factory do
|
||||
|
||||
Rspec.describe Factory do
|
||||
describe "#initialize" do
|
||||
it "accepts valid options" do
|
||||
valid_options = {with: Decorator, context: {foo: "bar"}}
|
||||
|
@ -64,7 +63,7 @@ module Draper
|
|||
allow(Factory::Worker).to receive(:new).and_return(worker)
|
||||
options = {foo: "bar"}
|
||||
|
||||
expect(worker).to receive(:call).with(options)
|
||||
allow(worker).to receive(:call).with(options)
|
||||
factory.decorate(double, options)
|
||||
end
|
||||
|
||||
|
@ -72,27 +71,25 @@ module Draper
|
|||
it "sets the passed context" do
|
||||
factory = Factory.new(context: {foo: "bar"})
|
||||
worker = ->(*){}
|
||||
allow(Factory::Worker).to receive(:new).and_return(worker)
|
||||
allow(Factory::Worker).to receive_messages new: worker
|
||||
|
||||
expect(worker).to receive(:call).with(baz: 'qux', context: { foo: 'bar' })
|
||||
expect(worker).to receive(:call).with(baz: "qux", context: {foo: "bar"})
|
||||
factory.decorate(double, {baz: "qux"})
|
||||
end
|
||||
|
||||
it "is overridden by explicitly-specified context" do
|
||||
factory = Factory.new(context: {foo: "bar"})
|
||||
worker = ->(*){}
|
||||
allow(Factory::Worker).to receive(:new) { worker }
|
||||
allow(Factory::Worker).to receive_messages new: worker
|
||||
|
||||
expect(worker).to receive(:call).with(context: {baz: "qux"})
|
||||
factory.decorate(double, context: {baz: "qux"})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
RSpec.describe Factory::Worker do
|
||||
|
||||
Rspec.describe Factory::Worker do
|
||||
describe "#call" do
|
||||
it "calls the decorator method" do
|
||||
object = double
|
||||
|
@ -101,7 +98,7 @@ module Draper
|
|||
decorator = ->(*){}
|
||||
allow(worker).to receive(:decorator){ decorator }
|
||||
|
||||
expect(decorator).to receive(:call).with(object, options).and_return(:decorated)
|
||||
allow(decorator).to receive(:call).with(object, options).and_return(:decorated)
|
||||
expect(worker.call(options)).to be :decorated
|
||||
end
|
||||
|
||||
|
@ -109,7 +106,7 @@ module Draper
|
|||
it "calls it" do
|
||||
worker = Factory::Worker.new(double, double)
|
||||
decorator = ->(*){}
|
||||
allow(worker).to receive(:decorator) { decorator }
|
||||
allow(worker).to receive_messages decorator: decorator
|
||||
context = {foo: "bar"}
|
||||
|
||||
expect(decorator).to receive(:call).with(anything(), context: context)
|
||||
|
@ -118,7 +115,7 @@ module Draper
|
|||
|
||||
it "receives arguments from the :context_args option" do
|
||||
worker = Factory::Worker.new(double, double)
|
||||
allow(worker).to receive(:decorator) { ->(*){} }
|
||||
allow(worker).to receive_messages decorator: ->(*){}
|
||||
context = ->{}
|
||||
|
||||
expect(context).to receive(:call).with(:foo, :bar)
|
||||
|
@ -127,7 +124,7 @@ module Draper
|
|||
|
||||
it "wraps non-arrays passed to :context_args" do
|
||||
worker = Factory::Worker.new(double, double)
|
||||
allow(worker).to receive(:decorator) { ->(*){} }
|
||||
allow(worker).to receive_messages decorator: ->(*){}
|
||||
context = ->{}
|
||||
hash = {foo: "bar"}
|
||||
|
||||
|
@ -140,7 +137,7 @@ module Draper
|
|||
it "doesn't call it" do
|
||||
worker = Factory::Worker.new(double, double)
|
||||
decorator = ->(*){}
|
||||
allow(worker).to receive(:decorator) { decorator }
|
||||
allow(worker).to receive_messages decorator: decorator
|
||||
context = {foo: "bar"}
|
||||
|
||||
expect(decorator).to receive(:call).with(anything(), context: context)
|
||||
|
@ -151,7 +148,7 @@ module Draper
|
|||
it "does not pass the :context_args option to the decorator" do
|
||||
worker = Factory::Worker.new(double, double)
|
||||
decorator = ->(*){}
|
||||
allow(worker).to receive(:decorator) { decorator }
|
||||
allow(worker).to receive_messages decorator: decorator
|
||||
|
||||
expect(decorator).to receive(:call).with(anything(), foo: "bar")
|
||||
worker.call(foo: "bar", context_args: [])
|
||||
|
|
|
@ -99,7 +99,7 @@ module Draper
|
|||
describe ".all" do
|
||||
it "returns a decorated collection" do
|
||||
found = [Product.new, Product.new]
|
||||
allow(Product).to receive(:all).and_return(found)
|
||||
allow(Product).to receive_messages all: found
|
||||
decorator = ProductDecorator.all
|
||||
|
||||
expect(decorator).to be_a Draper::CollectionDecorator
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
require 'spec_helper'
|
||||
require 'active_record'
|
||||
|
||||
module Draper
|
||||
module QueryMethods
|
||||
describe LoadStrategy do
|
||||
describe '#new' do
|
||||
subject { described_class.new(:active_record) }
|
||||
|
||||
it { is_expected.to be_an_instance_of(LoadStrategy::ActiveRecord) }
|
||||
end
|
||||
end
|
||||
|
||||
describe LoadStrategy::ActiveRecord do
|
||||
describe '#allowed?' do
|
||||
it 'checks whether or not ActiveRecord::Relation::VALUE_METHODS has the given method' do
|
||||
allow(::ActiveRecord::Relation::VALUE_METHODS).to receive(:include?)
|
||||
|
||||
described_class.new.allowed? :foo
|
||||
|
||||
expect(::ActiveRecord::Relation::VALUE_METHODS).to have_received(:include?).with(:foo)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,63 @@
|
|||
require 'spec_helper'
|
||||
require_relative '../dummy/app/decorators/post_decorator'
|
||||
|
||||
Post = Struct.new(:id) { }
|
||||
|
||||
module Draper
|
||||
describe QueryMethods do
|
||||
let(:fake_strategy) { instance_double(QueryMethods::LoadStrategy::ActiveRecord) }
|
||||
|
||||
before { allow(QueryMethods::LoadStrategy).to receive(:new).and_return(fake_strategy) }
|
||||
|
||||
describe '#method_missing' do
|
||||
let(:collection) { [ Post.new, Post.new ] }
|
||||
let(:collection_decorator) { PostDecorator.decorate_collection(collection) }
|
||||
|
||||
context 'when strategy allows collection to call the method' do
|
||||
let(:results) { spy(:results) }
|
||||
|
||||
before do
|
||||
allow(fake_strategy).to receive(:allowed?).with(:some_query_method).and_return(true)
|
||||
allow(collection).to receive(:send).with(:some_query_method).and_return(results)
|
||||
end
|
||||
|
||||
it 'calls the method on the collection and decorate it results' do
|
||||
collection_decorator.some_query_method
|
||||
|
||||
expect(results).to have_received(:decorate)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when strategy does not allow collection to call the method' do
|
||||
before { allow(fake_strategy).to receive(:allowed?).with(:some_query_method).and_return(false) }
|
||||
|
||||
it 'raises NoMethodError' do
|
||||
expect { collection_decorator.some_query_method }.to raise_exception(NoMethodError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#respond_to?" do
|
||||
let(:collection) { [ Post.new, Post.new ] }
|
||||
let(:collection_decorator) { PostDecorator.decorate_collection(collection) }
|
||||
|
||||
subject { collection_decorator.respond_to?(:some_query_method) }
|
||||
|
||||
context 'when strategy allows collection to call the method' do
|
||||
before do
|
||||
allow(fake_strategy).to receive(:allowed?).with(:some_query_method).and_return(true)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(true) }
|
||||
end
|
||||
|
||||
context 'when strategy does not allow collection to call the method' do
|
||||
before do
|
||||
allow(fake_strategy).to receive(:allowed?).with(:some_query_method).and_return(false)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,20 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Draper, '.undecorate_chain' do
|
||||
let!(:object) { Model.new }
|
||||
let!(:decorated_inner) { Class.new(Draper::Decorator).new(object) }
|
||||
let!(:decorated_outer) { Class.new(Draper::Decorator).new(decorated_inner) }
|
||||
|
||||
it 'undecorates full chain of decorated objects' do
|
||||
expect(Draper.undecorate_chain(decorated_outer)).to equal object
|
||||
end
|
||||
|
||||
it 'passes a non-decorated object through' do
|
||||
expect(Draper.undecorate_chain(object)).to equal object
|
||||
end
|
||||
|
||||
it 'passes a non-decorator object through' do
|
||||
object = Object.new
|
||||
expect(Draper.undecorate_chain(object)).to equal object
|
||||
end
|
||||
end
|
|
@ -14,7 +14,7 @@ module Draper
|
|||
context "when a current controller is set" do
|
||||
it "returns the controller's view context" do
|
||||
view_context = fake_view_context
|
||||
allow(ViewContext).to receive(:controller) { fake_controller(view_context) }
|
||||
allow(ViewContext).to receive_messages controller: fake_controller(view_context)
|
||||
strategy = ViewContext::BuildStrategy::Full.new
|
||||
|
||||
expect(strategy.call).to be view_context
|
||||
|
@ -23,31 +23,47 @@ module Draper
|
|||
|
||||
context "when a current controller is not set" do
|
||||
it "uses ApplicationController" do
|
||||
view_context = fake_view_context
|
||||
stub_const "ApplicationController", double(new: fake_controller(view_context))
|
||||
strategy = ViewContext::BuildStrategy::Full.new
|
||||
|
||||
expect(strategy.call).to be view_context
|
||||
expect(Draper::ViewContext.controller).to be_nil
|
||||
view_context = ViewContext::BuildStrategy::Full.new.call
|
||||
expect(view_context.controller).to eq Draper::ViewContext.controller
|
||||
expect(view_context.controller).to be_an ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
it "adds a request if one is not defined" do
|
||||
controller = Class.new(ActionController::Base).new
|
||||
allow(ViewContext).to receive(:controller) { controller }
|
||||
allow(ViewContext).to receive_messages controller: controller
|
||||
strategy = ViewContext::BuildStrategy::Full.new
|
||||
|
||||
expect(controller.request).to be_nil
|
||||
strategy.call
|
||||
expect(controller.request).to be_an ActionController::TestRequest
|
||||
expect(controller.params).to eq({})
|
||||
expect(controller.params).to be_empty
|
||||
|
||||
# sanity checks
|
||||
expect(controller.view_context.request).to be controller.request
|
||||
expect(controller.view_context.params).to be controller.params
|
||||
end
|
||||
|
||||
it "compatible with rails 5.1 change on ActionController::TestRequest.create method" do
|
||||
ActionController::TestRequest.class_eval do
|
||||
if ActionController::TestRequest.method(:create).parameters.first == []
|
||||
def create controller_class
|
||||
create
|
||||
end
|
||||
end
|
||||
end
|
||||
controller = Class.new(ActionController::Base).new
|
||||
allow(ViewContext).to receive_messages controller: controller
|
||||
strategy = ViewContext::BuildStrategy::Full.new
|
||||
|
||||
expect(controller.request).to be_nil
|
||||
strategy.call
|
||||
expect(controller.request).to be_an ActionController::TestRequest
|
||||
end
|
||||
|
||||
it "adds methods to the view context from the constructor block" do
|
||||
allow(ViewContext).to receive(:controller) { fake_controller }
|
||||
allow(ViewContext).to receive(:controller).and_return(fake_controller)
|
||||
strategy = ViewContext::BuildStrategy::Full.new do
|
||||
def a_helper_method; end
|
||||
end
|
||||
|
@ -57,7 +73,7 @@ module Draper
|
|||
|
||||
it "includes modules into the view context from the constructor block" do
|
||||
view_context = Object.new
|
||||
allow(ViewContext).to receive(:controller) { fake_controller(view_context) }
|
||||
allow(ViewContext).to receive(:controller).and_return(fake_controller(view_context))
|
||||
helpers = Module.new do
|
||||
def a_helper_method; end
|
||||
end
|
||||
|
|
|
@ -18,7 +18,7 @@ module Draper
|
|||
|
||||
describe ".controller" do
|
||||
it "returns the stored controller from RequestStore" do
|
||||
allow(RequestStore).to receive(:store) { { current_controller: :stored_controller } }
|
||||
allow(RequestStore).to receive_messages store: {current_controller: :stored_controller}
|
||||
|
||||
expect(ViewContext.controller).to be :stored_controller
|
||||
end
|
||||
|
@ -27,24 +27,52 @@ module Draper
|
|||
describe ".controller=" do
|
||||
it "stores a controller in RequestStore" do
|
||||
store = {}
|
||||
allow(RequestStore).to receive(:store).and_return(store)
|
||||
allow(RequestStore).to receive_messages store: store
|
||||
|
||||
ViewContext.controller = :stored_controller
|
||||
expect(store[:current_controller]).to be :stored_controller
|
||||
end
|
||||
|
||||
it "cleans context when controller changes" do
|
||||
store = {
|
||||
current_controller: :stored_controller,
|
||||
current_view_context: :stored_view_context
|
||||
}
|
||||
|
||||
allow(RequestStore).to receive_messages store: store
|
||||
|
||||
ViewContext.controller = :other_stored_controller
|
||||
|
||||
expect(store).to include(current_controller: :other_stored_controller)
|
||||
expect(store).not_to include(:current_view_context)
|
||||
end
|
||||
|
||||
it "doesn't clean context when controller is the same" do
|
||||
store = {
|
||||
current_controller: :stored_controller,
|
||||
current_view_context: :stored_view_context
|
||||
}
|
||||
|
||||
allow(RequestStore).to receive_messages store: store
|
||||
|
||||
ViewContext.controller = :stored_controller
|
||||
|
||||
expect(store).to include(current_controller: :stored_controller)
|
||||
expect(store).to include(current_view_context: :stored_view_context)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".current" do
|
||||
it "returns the stored view context from RequestStore" do
|
||||
allow(RequestStore).to receive(:store) { { current_view_context: :stored_view_context } }
|
||||
allow(RequestStore).to receive_messages store: {current_view_context: :stored_view_context}
|
||||
|
||||
expect(ViewContext.current).to be :stored_view_context
|
||||
end
|
||||
|
||||
context "when no view context is stored" do
|
||||
it "builds a view context" do
|
||||
allow(RequestStore).to receive(:store).and_return({})
|
||||
allow(ViewContext).to receive(:build_strategy).and_return( ->{ :new_view_context })
|
||||
allow(RequestStore).to receive_messages store: {}
|
||||
allow(ViewContext).to receive_messages build_strategy: ->{ :new_view_context }
|
||||
allow(HelperProxy).to receive(:new).with(:new_view_context).and_return(:new_helper_proxy)
|
||||
|
||||
expect(ViewContext.current).to be :new_helper_proxy
|
||||
|
@ -52,8 +80,8 @@ module Draper
|
|||
|
||||
it "stores the built view context" do
|
||||
store = {}
|
||||
allow(RequestStore).to receive(:store).and_return(store)
|
||||
allow(ViewContext).to receive(:build_strategy).and_return( ->{ :new_view_context })
|
||||
allow(RequestStore).to receive_messages store: store
|
||||
allow(ViewContext).to receive_messages build_strategy: ->{ :new_view_context }
|
||||
allow(HelperProxy).to receive(:new).with(:new_view_context).and_return(:new_helper_proxy)
|
||||
|
||||
ViewContext.current
|
||||
|
@ -65,7 +93,7 @@ module Draper
|
|||
describe ".current=" do
|
||||
it "stores a helper proxy for the view context in RequestStore" do
|
||||
store = {}
|
||||
allow(RequestStore).to receive(:store).and_return(store)
|
||||
allow(RequestStore).to receive_messages store: store
|
||||
allow(HelperProxy).to receive(:new).with(:stored_view_context).and_return(:stored_helper_proxy)
|
||||
|
||||
ViewContext.current = :stored_view_context
|
||||
|
@ -76,7 +104,7 @@ module Draper
|
|||
describe ".clear!" do
|
||||
it "clears the stored controller and view controller" do
|
||||
store = {current_controller: :stored_controller, current_view_context: :stored_view_context}
|
||||
allow(RequestStore).to receive(:store).and_return(store)
|
||||
allow(RequestStore).to receive_messages store: store
|
||||
|
||||
ViewContext.clear!
|
||||
expect(store).not_to have_key :current_controller
|
||||
|
@ -86,7 +114,7 @@ module Draper
|
|||
|
||||
describe ".build" do
|
||||
it "returns a new view context using the build strategy" do
|
||||
allow(ViewContext).to receive(:build_strategy).and_return( ->{ :new_view_context })
|
||||
allow(ViewContext).to receive_messages build_strategy: ->{ :new_view_context }
|
||||
|
||||
expect(ViewContext.build).to be :new_view_context
|
||||
end
|
||||
|
@ -94,7 +122,7 @@ module Draper
|
|||
|
||||
describe ".build!" do
|
||||
it "returns a helper proxy for the new view context" do
|
||||
allow(ViewContext).to receive(:build_strategy).and_return( ->{ :new_view_context })
|
||||
allow(ViewContext).to receive_messages build_strategy: ->{ :new_view_context }
|
||||
allow(HelperProxy).to receive(:new).with(:new_view_context).and_return(:new_helper_proxy)
|
||||
|
||||
expect(ViewContext.build!).to be :new_helper_proxy
|
||||
|
@ -102,8 +130,8 @@ module Draper
|
|||
|
||||
it "stores the helper proxy" do
|
||||
store = {}
|
||||
allow(RequestStore).to receive(:store) { store }
|
||||
allow(ViewContext).to receive(:build_strategy).and_return( ->{ :new_view_context })
|
||||
allow(RequestStore).to receive_messages store: store
|
||||
allow(ViewContext).to receive_messages build_strategy: ->{ :new_view_context }
|
||||
allow(HelperProxy).to receive(:new).with(:new_view_context).and_return(:new_helper_proxy)
|
||||
|
||||
ViewContext.build!
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
class ApplicationController < ActionController::Base
|
||||
include LocalizedUrls
|
||||
protect_from_forgery
|
||||
end
|
|
@ -0,0 +1,4 @@
|
|||
class BaseController < ActionController::Base
|
||||
include LocalizedUrls
|
||||
protect_from_forgery
|
||||
end
|
|
@ -1,4 +1,4 @@
|
|||
class PostsController < ApplicationController
|
||||
class PostsController < BaseController
|
||||
decorates_assigned :post
|
||||
|
||||
def show
|
||||
|
@ -8,7 +8,7 @@ class PostsController < ApplicationController
|
|||
def mail
|
||||
post = Post.find(params[:id])
|
||||
email = PostMailer.decorated_email(post).deliver
|
||||
render text: email.body
|
||||
render html: email.body.to_s.html_safe
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
class PublishPostJob < ActiveJob::Base
|
||||
queue_as :default
|
||||
|
||||
def perform(post)
|
||||
post.save!
|
||||
end
|
||||
end
|
|
@ -0,0 +1,3 @@
|
|||
class ApplicationRecord < ActiveRecord::Base
|
||||
self.abstract_class = true
|
||||
end
|
|
@ -1,3 +1,3 @@
|
|||
class Post < ActiveRecord::Base
|
||||
class Post < ApplicationRecord
|
||||
# attr_accessible :title, :body
|
||||
end
|
||||
|
|
|
@ -20,14 +20,16 @@
|
|||
<dt>Helpers from the controller:</dt>
|
||||
<dd id="goodnight_moon"><%= post.goodnight_moon %></dd>
|
||||
|
||||
<% unless defined? mailer %>
|
||||
<dt>Path with decorator:</dt>
|
||||
<dd id="path_with_decorator"><%= post_path(post) %></dd>
|
||||
<dd id="path_with_decorator"><%= post_url(post) %></dd>
|
||||
|
||||
<dt>Path with model:</dt>
|
||||
<dd id="path_with_model"><%= post.path_with_model %></dd>
|
||||
|
||||
<dt>Path with id:</dt>
|
||||
<dd id="path_with_id"><%= post.path_with_id %></dd>
|
||||
<% end %>
|
||||
|
||||
<dt>URL with decorator:</dt>
|
||||
<dd id="url_with_decorator"><%= post_url(post) %></dd>
|
||||
|
|
|
@ -38,9 +38,6 @@ module Dummy
|
|||
# Configure the default encoding used in templates for Ruby 1.9.
|
||||
config.encoding = "utf-8"
|
||||
|
||||
# Configure sensitive parameters which will be filtered from the log file.
|
||||
config.filter_parameters += [:password]
|
||||
|
||||
# Enable escaping HTML in JSON.
|
||||
config.active_support.escape_html_entities_in_json = true
|
||||
|
||||
|
|
|
@ -2,4 +2,4 @@ require 'rubygems'
|
|||
|
||||
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__)
|
||||
|
||||
require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
|
||||
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
||||
|
|
|
@ -28,4 +28,6 @@ Dummy::Application.configure do
|
|||
config.active_support.deprecation = :stderr
|
||||
|
||||
config.eager_load = false
|
||||
|
||||
config.active_job.queue_adapter = :test
|
||||
end
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
Draper.configure do |config|
|
||||
config.default_controller = BaseController
|
||||
end
|
|
@ -0,0 +1,4 @@
|
|||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# Configure sensitive parameters which will be filtered from the log file.
|
||||
Rails.application.config.filter_parameters += [:password]
|
|
@ -1,54 +1,117 @@
|
|||
development:
|
||||
# Configure available database sessions. (required)
|
||||
sessions:
|
||||
# Defines the default session. (required)
|
||||
# Configure available database clients. (required)
|
||||
clients:
|
||||
# Defines the default client. (required)
|
||||
default:
|
||||
# Defines the name of the default database that Mongoid can connect to.
|
||||
# (required).
|
||||
database: dummy_development
|
||||
# Provides the hosts the default session can connect to. Must be an array
|
||||
# Provides the hosts the default client can connect to. Must be an array
|
||||
# of host:port pairs. (required)
|
||||
hosts:
|
||||
- localhost:27017
|
||||
options:
|
||||
# Change whether the session persists in safe mode by default.
|
||||
# (default: false)
|
||||
# safe: false
|
||||
# Change the default write concern. (default = { w: 1 })
|
||||
# write:
|
||||
# w: 1
|
||||
|
||||
# Change the default consistency model to :eventual or :strong.
|
||||
# :eventual will send reads to secondaries, :strong sends everything
|
||||
# to master. (default: :eventual)
|
||||
# consistency: :eventual
|
||||
# Change the default read preference. Valid options for mode are: :secondary,
|
||||
# :secondary_preferred, :primary, :primary_preferred, :nearest
|
||||
# (default: primary)
|
||||
# read:
|
||||
# mode: :secondary_preferred
|
||||
# tag_sets:
|
||||
# - use: web
|
||||
|
||||
# The name of the user for authentication.
|
||||
# user: 'user'
|
||||
|
||||
# The password of the user for authentication.
|
||||
# password: 'password'
|
||||
|
||||
# The user's database roles.
|
||||
# roles:
|
||||
# - 'dbOwner'
|
||||
|
||||
# Change the default authentication mechanism. Valid options are: :scram,
|
||||
# :mongodb_cr, :mongodb_x509, and :plain. (default on 3.0 is :scram, default
|
||||
# on 2.4 and 2.6 is :plain)
|
||||
# auth_mech: :scram
|
||||
|
||||
# The database or source to authenticate the user against. (default: admin)
|
||||
# auth_source: admin
|
||||
|
||||
# Force a the driver cluster to behave in a certain manner instead of auto-
|
||||
# discovering. Can be one of: :direct, :replica_set, :sharded. Set to :direct
|
||||
# when connecting to hidden members of a replica set.
|
||||
# connect: :direct
|
||||
|
||||
# Changes the default time in seconds the server monitors refresh their status
|
||||
# via ismaster commands. (default: 10)
|
||||
# heartbeat_frequency: 10
|
||||
|
||||
# The time in seconds for selecting servers for a near read preference. (default: 5)
|
||||
# local_threshold: 5
|
||||
|
||||
# The timeout in seconds for selecting a server for an operation. (default: 30)
|
||||
# server_selection_timeout: 30
|
||||
|
||||
# The maximum number of connections in the connection pool. (default: 5)
|
||||
# max_pool_size: 5
|
||||
|
||||
# The minimum number of connections in the connection pool. (default: 1)
|
||||
# min_pool_size: 1
|
||||
|
||||
# The time to wait, in seconds, in the connection pool for a connection
|
||||
# to be checked in before timing out. (default: 5)
|
||||
# wait_queue_timeout: 5
|
||||
|
||||
# The time to wait to establish a connection before timing out, in seconds.
|
||||
# (default: 5)
|
||||
# connect_timeout: 5
|
||||
|
||||
# The timeout to wait to execute operations on a socket before raising an error.
|
||||
# (default: 5)
|
||||
# socket_timeout: 5
|
||||
|
||||
# The name of the replica set to connect to. Servers provided as seeds that do
|
||||
# not belong to this replica set will be ignored.
|
||||
# replica_set: name
|
||||
|
||||
# Whether to connect to the servers via ssl. (default: false)
|
||||
# ssl: true
|
||||
|
||||
# The certificate file used to identify the connection against MongoDB.
|
||||
# ssl_cert: /path/to/my.cert
|
||||
|
||||
# The private keyfile used to identify the connection against MongoDB.
|
||||
# Note that even if the key is stored in the same file as the certificate,
|
||||
# both need to be explicitly specified.
|
||||
# ssl_key: /path/to/my.key
|
||||
|
||||
# A passphrase for the private key.
|
||||
# ssl_key_pass_phrase: password
|
||||
|
||||
# Whether or not to do peer certification validation. (default: true)
|
||||
# ssl_verify: true
|
||||
|
||||
# The file containing a set of concatenated certification authority certifications
|
||||
# used to validate certs passed from the other end of the connection.
|
||||
# ssl_ca_cert: /path/to/ca.cert
|
||||
|
||||
# How many times Moped should attempt to retry an operation after
|
||||
# failure. (default: 30)
|
||||
# max_retries: 30
|
||||
|
||||
# The time in seconds that Moped should wait before retrying an
|
||||
# operation on failure. (default: 1)
|
||||
# retry_interval: 1
|
||||
# Configure Mongoid specific options. (optional)
|
||||
options:
|
||||
# Configuration for whether or not to allow access to fields that do
|
||||
# not have a field definition on the model. (default: true)
|
||||
# allow_dynamic_fields: true
|
||||
|
||||
# Enable the identity map, needed for eager loading. (default: false)
|
||||
# identity_map_enabled: false
|
||||
|
||||
# Includes the root model name in json serialization. (default: false)
|
||||
# include_root_in_json: false
|
||||
|
||||
# Include the _type field in serializaion. (default: false)
|
||||
# Include the _type field in serialization. (default: false)
|
||||
# include_type_for_serialization: false
|
||||
|
||||
# Preload all models in development, needed when models use
|
||||
# inheritance. (default: false)
|
||||
# preload_models: false
|
||||
|
||||
# Protect id and type from mass assignment. (default: true)
|
||||
# protect_sensitive_fields: true
|
||||
|
||||
# Raise an error when performing a #find and the document is not found.
|
||||
# (default: true)
|
||||
# raise_not_found_error: true
|
||||
|
@ -57,23 +120,23 @@ development:
|
|||
# existing method. (default: false)
|
||||
# scope_overwrite_exception: false
|
||||
|
||||
# Skip the database version check, used when connecting to a db without
|
||||
# admin access. (default: false)
|
||||
# skip_version_check: false
|
||||
|
||||
# User Active Support's time zone in conversions. (default: true)
|
||||
# Use Active Support's time zone in conversions. (default: true)
|
||||
# use_activesupport_time_zone: true
|
||||
|
||||
# Ensure all times are UTC in the app side. (default: false)
|
||||
# use_utc: false
|
||||
|
||||
# Set the Mongoid and Ruby driver log levels when not in a Rails
|
||||
# environment. The Mongoid logger will be set to the Rails logger
|
||||
# otherwise.(default: :info)
|
||||
# log_level: :info
|
||||
test:
|
||||
sessions:
|
||||
clients:
|
||||
default:
|
||||
database: dummy_test
|
||||
hosts:
|
||||
- localhost:27017
|
||||
options:
|
||||
# In the test environment we lower the retries and retry interval to
|
||||
# low amounts for fast failures.
|
||||
max_retries: 1
|
||||
retry_interval: 0
|
||||
read:
|
||||
mode: :primary
|
||||
max_pool_size: 1
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
class CreatePosts < ActiveRecord::Migration
|
||||
class CreatePosts < ActiveRecord::Migration[4.2]
|
||||
def change
|
||||
create_table :posts do |t|
|
||||
|
||||
|
|
|
@ -11,11 +11,11 @@
|
|||
#
|
||||
# It's strongly recommended to check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(:version => 20121019115657) do
|
||||
ActiveRecord::Schema.define(version: 20121019115657) do
|
||||
|
||||
create_table "posts", :force => true do |t|
|
||||
t.datetime "created_at", :null => false
|
||||
t.datetime "updated_at", :null => false
|
||||
create_table "posts", force: true do |t|
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -13,4 +13,4 @@ RSpec::Core::RakeTask.new :fast_spec do |t|
|
|||
t.pattern = "fast_spec/**/*_spec.rb"
|
||||
end
|
||||
|
||||
task :default => [:test, :spec, :fast_spec]
|
||||
task default: [:test, :spec, :fast_spec]
|
||||
|
|
|
@ -2,15 +2,11 @@ require_relative '../rails_helper'
|
|||
|
||||
RSpec.describe Draper::CollectionDecorator do
|
||||
describe "#active_model_serializer" do
|
||||
it "returns ActiveModel::ArraySerializer" do
|
||||
it "returns ActiveModel::Serializer::CollectionSerializer" do
|
||||
collection_decorator = Draper::CollectionDecorator.new([])
|
||||
if defined?(ActiveModel::ArraySerializerSupport)
|
||||
collection_serializer = collection_decorator.active_model_serializer
|
||||
else
|
||||
collection_serializer = ActiveModel::Serializer.serializer_for(collection_decorator)
|
||||
end
|
||||
|
||||
expect(collection_serializer).to be ActiveModel::ArraySerializer
|
||||
expect(collection_serializer).to be ActiveModel::Serializer::CollectionSerializer
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -51,14 +51,5 @@ if defined?(Devise)
|
|||
|
||||
expect(helper.current_user).to be_nil
|
||||
end
|
||||
|
||||
it "is backwards-compatible" do
|
||||
user = double("User")
|
||||
ActiveSupport::Deprecation.silence do
|
||||
sign_in user
|
||||
end
|
||||
|
||||
expect(helper.current_user).to be user
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -54,13 +54,11 @@ RSpec.describe PostDecorator do
|
|||
end
|
||||
|
||||
it "serializes to XML" do
|
||||
pending("Rails < 3.2 does not use `serializable_hash` in `to_xml`") if Rails.version.to_f < 3.2
|
||||
|
||||
xml = Capybara.string(decorator.to_xml)
|
||||
expect(xml).to have_css "post > updated-at", text: "overridden"
|
||||
end
|
||||
|
||||
it "uses a test view context from ApplicationController" do
|
||||
expect(Draper::ViewContext.current.controller).to be_an ApplicationController
|
||||
it "uses a test view context from BaseController" do
|
||||
expect(Draper::ViewContext.current.controller).to be_an BaseController
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
RSpec.describe PublishPostJob, type: :job do
|
||||
let(:post) { Post.create.decorate }
|
||||
|
||||
subject(:job) { described_class.perform_later(post) }
|
||||
|
||||
it 'queues the job' do
|
||||
expect { job }.to have_enqueued_job(described_class).with(post.object)
|
||||
end
|
||||
end
|
|
@ -10,14 +10,6 @@ RSpec.describe PostMailer do
|
|||
expect(email_body).to have_content "Today"
|
||||
end
|
||||
|
||||
it "can use path helpers with a model" do
|
||||
expect(email_body).to have_css "#path_with_model", text: "/en/posts/#{post.id}"
|
||||
end
|
||||
|
||||
it "can use path helpers with an id" do
|
||||
expect(email_body).to have_css "#path_with_id", text: "/en/posts/#{post.id}"
|
||||
end
|
||||
|
||||
it "can use url helpers with a model" do
|
||||
expect(email_body).to have_css "#url_with_model", text: "http://www.example.com:12345/en/posts/#{post.id}"
|
||||
end
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe ApplicationRecord do
|
||||
it { expect(described_class.superclass).to eq ActiveRecord::Base }
|
||||
|
||||
it { expect(described_class.abstract_class).to be_truthy }
|
||||
end
|
|
@ -2,5 +2,14 @@ require_relative '../spec_helper'
|
|||
require_relative '../shared_examples/decoratable'
|
||||
|
||||
RSpec.describe Post do
|
||||
it_behaves_like "a decoratable model"
|
||||
it_behaves_like 'a decoratable model'
|
||||
|
||||
it { should be_a ApplicationRecord }
|
||||
|
||||
describe '#to_global_id' do
|
||||
let(:post) { Post.create }
|
||||
subject { post.to_global_id }
|
||||
|
||||
it { is_expected.to eq post.decorate.to_global_id }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,8 +11,6 @@ RSpec.shared_examples_for "a decoratable model" do
|
|||
|
||||
describe "#==" do
|
||||
it "is true for other instances' decorators" do
|
||||
pending "Mongoid < 3.1 overrides `#==`" if defined?(Mongoid) && Mongoid::VERSION.to_f < 3.1 && described_class < Mongoid::Document
|
||||
|
||||
described_class.create
|
||||
one = described_class.first
|
||||
other = described_class.first
|
||||
|
|
|
@ -51,14 +51,5 @@ if defined?(Devise)
|
|||
|
||||
assert helper.current_user.nil?
|
||||
end
|
||||
|
||||
it "is backwards-compatible" do
|
||||
user = Object.new
|
||||
ActiveSupport::Deprecation.silence do
|
||||
sign_in user
|
||||
end
|
||||
|
||||
assert_same user, helper.current_user
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,12 +13,12 @@ describe "A decorator test" do
|
|||
it_does_not_leak_view_context
|
||||
end
|
||||
|
||||
describe "A controller test" do
|
||||
tests Class.new(ActionController::Base)
|
||||
describe "A controller decorator test" do
|
||||
subject { Class.new(ActionController::Base) }
|
||||
|
||||
it_does_not_leak_view_context
|
||||
end
|
||||
|
||||
describe "A mailer test" do
|
||||
describe "A mailer decorator test" do
|
||||
it_does_not_leak_view_context
|
||||
end
|
||||
|
|
|
@ -51,14 +51,5 @@ if defined?(Devise)
|
|||
|
||||
assert helper.current_user.nil?
|
||||
end
|
||||
|
||||
def test_backwards_compatibility
|
||||
user = Object.new
|
||||
ActiveSupport::Deprecation.silence do
|
||||
sign_in user
|
||||
end
|
||||
|
||||
assert_same user, helper.current_user
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,7 +14,7 @@ class DecoratorTest < Draper::TestCase
|
|||
end
|
||||
|
||||
class ControllerTest < ActionController::TestCase
|
||||
tests Class.new(ActionController::Base)
|
||||
subject{ Class.new(ActionController::Base) }
|
||||
|
||||
it_does_not_leak_view_context
|
||||
end
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
require 'spec_helper'
|
||||
require_relative '../../dummy/spec/rails_helper'
|
||||
require 'rails'
|
||||
require 'dummy/config/environment'
|
||||
require 'ammeter/init'
|
||||
require 'generators/controller_override'
|
||||
require 'generators/rails/decorator_generator'
|
||||
SimpleCov.command_name 'test:generator'
|
||||
|
||||
RSpec.describe Rails::Generators::ControllerGenerator do
|
||||
describe Rails::Generators::ControllerGenerator do
|
||||
destination File.expand_path("../tmp", __FILE__)
|
||||
|
||||
before { prepare_destination }
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
require 'spec_helper'
|
||||
require 'rspec/rails'
|
||||
# require_relative '../../dummy/spec/rails_helper'
|
||||
require 'dummy/config/environment'
|
||||
require 'ammeter/init'
|
||||
require 'generators/rails/decorator_generator'
|
||||
|
||||
RSpec.describe Rails::Generators::DecoratorGenerator do
|
||||
describe Rails::Generators::DecoratorGenerator do
|
||||
destination File.expand_path("../tmp", __FILE__)
|
||||
|
||||
before { prepare_destination }
|
||||
|
@ -41,6 +40,7 @@ RSpec.describe Rails::Generators::DecoratorGenerator do
|
|||
|
||||
context "with an ApplicationDecorator" do
|
||||
before do
|
||||
allow_any_instance_of(Object).to receive(:require)
|
||||
allow_any_instance_of(Object).to receive(:require).with("application_decorator").and_return(
|
||||
stub_const "ApplicationDecorator", Class.new
|
||||
)
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
require 'spec_helper'
|
||||
require 'dummy/config/environment'
|
||||
require 'ammeter/init'
|
||||
require 'generators/draper/install_generator'
|
||||
|
||||
describe Draper::Generators::InstallGenerator do
|
||||
destination File.expand_path('../tmp', __FILE__)
|
||||
|
||||
before { prepare_destination }
|
||||
after(:all) { FileUtils.rm_rf destination_root }
|
||||
|
||||
describe 'the application decorator' do
|
||||
subject { file('app/decorators/application_decorator.rb') }
|
||||
|
||||
before { run_generator }
|
||||
|
||||
it { is_expected.to contain 'class ApplicationDecorator' }
|
||||
end
|
||||
end
|
|
@ -1,6 +1,7 @@
|
|||
require 'spec_helper'
|
||||
require 'support/dummy_app'
|
||||
require 'support/matchers/have_text'
|
||||
SimpleCov.command_name 'test:integration'
|
||||
|
||||
app = DummyApp.new(ENV["RAILS_ENV"])
|
||||
spec_types = {
|
||||
|
@ -38,6 +39,8 @@ app.start_server do
|
|||
expect(page).to have_text("Goodnight, moon!").in("#goodnight_moon")
|
||||
end
|
||||
|
||||
# _path helpers aren't available in mailers
|
||||
if type == :view
|
||||
it "can be passed to path helpers" do
|
||||
expect(page).to have_text("/en/posts/1").in("#path_with_decorator")
|
||||
end
|
||||
|
@ -49,6 +52,7 @@ app.start_server do
|
|||
it "can use path helpers with an id" do
|
||||
expect(page).to have_text("/en/posts/1").in("#path_with_id")
|
||||
end
|
||||
end
|
||||
|
||||
it "can be passed to url helpers" do
|
||||
expect(page).to have_text("http://www.example.com:12345/en/posts/1").in("#url_with_decorator")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
require 'rubygems'
|
||||
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
|
||||
require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
|
||||
Bundler.require(:default) if defined?(Bundler)
|
||||
Bundler.require :default
|
||||
|
||||
require "benchmark"
|
||||
require "draper"
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
require 'simplecov'
|
||||
SimpleCov.start do
|
||||
add_filter 'spec'
|
||||
add_group 'Draper', 'lib/draper'
|
||||
add_group 'Generators', 'lib/generators'
|
||||
end
|
||||
|
||||
require 'bundler/setup'
|
||||
require 'draper'
|
||||
require 'rails/version'
|
||||
require 'action_controller'
|
||||
require 'action_controller/test_case'
|
||||
|
||||
|
@ -32,20 +38,23 @@ class Model; include Draper::Decoratable; end
|
|||
class Product < Model; end
|
||||
class SpecialProduct < Product; end
|
||||
class Other < Model; end
|
||||
class Person < Model; end
|
||||
class Child < Person; end
|
||||
class ProductDecorator < Draper::Decorator; end
|
||||
class ProductsDecorator < Draper::CollectionDecorator; end
|
||||
|
||||
class ProductPresenter < Draper::Decorator; end
|
||||
|
||||
class OtherDecorator < Draper::Decorator; end
|
||||
|
||||
module Namespaced
|
||||
class Product < Model; end
|
||||
class ProductDecorator < Draper::Decorator; end
|
||||
|
||||
ProductsDecorator = Class.new(Draper::CollectionDecorator)
|
||||
class OtherDecorator < Draper::Decorator; end
|
||||
end
|
||||
|
||||
ApplicationController = Class.new(ActionController::Base)
|
||||
CustomController = Class.new(ActionController::Base)
|
||||
|
||||
# After each example, revert changes made to the class
|
||||
def protect_class(klass)
|
||||
before { stub_const klass.name, Class.new(klass) }
|
||||
|
|
|
@ -3,12 +3,12 @@ require 'spec_helper'
|
|||
RSpec.shared_examples_for "view helpers" do |subject|
|
||||
describe "#helpers" do
|
||||
it "returns the current view context" do
|
||||
allow(Draper::ViewContext).to receive(:current) { :current_view_context }
|
||||
allow(Draper::ViewContext).to receive_messages current: :current_view_context
|
||||
expect(subject.helpers).to be :current_view_context
|
||||
end
|
||||
|
||||
it "is aliased to #h" do
|
||||
allow(Draper::ViewContext).to receive(:current) { :current_view_context }
|
||||
allow(Draper::ViewContext).to receive_messages current: :current_view_context
|
||||
expect(subject.h).to be :current_view_context
|
||||
end
|
||||
end
|
||||
|
@ -22,24 +22,26 @@ RSpec.shared_examples_for "view helpers" do |subject|
|
|||
end
|
||||
|
||||
it "delegates to #helpers" do
|
||||
expect(helpers).to receive(:localize).with(:an_object, some: "parameter")
|
||||
allow(subject).to receive(:helpers).and_return(double)
|
||||
expect(subject.helpers).to receive(:localize).with(:an_object, some: "parameter")
|
||||
subject.localize(:an_object, some: "parameter")
|
||||
end
|
||||
|
||||
it "is aliased to #l" do
|
||||
expect(helpers).to receive(:localize).with(:an_object, some: 'parameter')
|
||||
allow(subject).to receive_messages helpers: double
|
||||
expect(subject.helpers).to receive(:localize).with(:an_object, some: "parameter")
|
||||
subject.l(:an_object, some: "parameter")
|
||||
end
|
||||
end
|
||||
|
||||
describe ".helpers" do
|
||||
it "returns the current view context" do
|
||||
allow(Draper::ViewContext).to receive(:current) { :current_view_context }
|
||||
allow(Draper::ViewContext).to receive_messages current: :current_view_context
|
||||
expect(subject.class.helpers).to be :current_view_context
|
||||
end
|
||||
|
||||
it "is aliased to .h" do
|
||||
allow(Draper::ViewContext).to receive(:current) { :current_view_context }
|
||||
allow(Draper::ViewContext).to receive(:current).and_return(:current_view_context)
|
||||
expect(subject.class.h).to be :current_view_context
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue