1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
|
require 'action_view/testing/resolvers'
module RSpec
module Rails
# @api public
# Helpers for optionally rendering views in controller specs.
module ViewRendering
extend ActiveSupport::Concern
# @!attribute [r]
# Returns the controller object instance under test.
attr_reader :controller
# @private
attr_writer :controller
private :controller=
# DSL methods
module ClassMethods
# @see RSpec::Rails::ControllerExampleGroup
def render_views(true_or_false = true)
@render_views = true_or_false
end
# @api private
def render_views?
return @render_views if defined?(@render_views)
if superclass.respond_to?(:render_views?)
superclass.render_views?
else
RSpec.configuration.render_views?
end
end
end
# @api private
def render_views?
self.class.render_views? || !controller.class.respond_to?(:view_paths)
end
# @private
class EmptyTemplateResolver
def self.build(path)
if path.is_a?(::ActionView::Resolver)
ResolverDecorator.new(path)
else
FileSystemResolver.new(path)
end
end
def self.nullify_template_rendering(templates)
templates.map do |template|
::ActionView::Template.new(
"",
template.identifier,
EmptyTemplateHandler,
virtual_path: template.virtual_path,
format: template_format(template),
locals: []
)
end
end
def self.template_format(template)
template.format
end
# Delegates all methods to the submitted resolver and for all methods
# that return a collection of `ActionView::Template` instances, return
# templates with modified source
#
# @private
class ResolverDecorator < ::ActionView::Resolver
(::ActionView::Resolver.instance_methods - Object.instance_methods).each do |method|
undef_method method
end
(::ActionView::Resolver.methods - Object.methods).each do |method|
singleton_class.undef_method method
end
def initialize(resolver)
@resolver = resolver
end
def method_missing(name, *args, &block)
result = @resolver.send(name, *args, &block)
nullify_templates(result)
end
private
def nullify_templates(collection)
return collection unless collection.is_a?(Enumerable)
return collection unless collection.all? { |element| element.is_a?(::ActionView::Template) }
EmptyTemplateResolver.nullify_template_rendering(collection)
end
end
# Delegates find_templates to the submitted path set and then returns
# templates with modified source
#
# @private
class FileSystemResolver < ::ActionView::FileSystemResolver
private
def find_templates(*args)
templates = super
EmptyTemplateResolver.nullify_template_rendering(templates)
end
end
end
# @private
class EmptyTemplateHandler
def self.call(_template, _source = nil)
::Rails.logger.info(" Template rendering was prevented by rspec-rails. Use `render_views` to verify rendered view contents if necessary.")
%("")
end
end
# Used to null out view rendering in controller specs.
#
# @private
module EmptyTemplates
def prepend_view_path(new_path)
super(_path_decorator(*new_path))
end
def append_view_path(new_path)
super(_path_decorator(*new_path))
end
private
def _path_decorator(*paths)
paths.map { |path| EmptyTemplateResolver.build(path) }
end
end
# @private
RESOLVER_CACHE = Hash.new do |hash, path|
hash[path] = EmptyTemplateResolver.build(path)
end
included do
before do
unless render_views?
@_original_path_set = controller.class.view_paths
path_set = @_original_path_set.map { |resolver| RESOLVER_CACHE[resolver] }
controller.class.view_paths = path_set
controller.extend(EmptyTemplates)
end
end
after do
controller.class.view_paths = @_original_path_set unless render_views?
end
end
end
end
end
|