class TurboStreamer::Template

Attributes

template_lookup_options[RW]

Public Class Methods

new(context, *args, &block) click to toggle source
Calls superclass method TurboStreamer::new
# File lib/turbostreamer/template.rb, line 11
def initialize(context, *args, &block)
  @context = context
  super(*args, &block)
end

Public Instance Methods

array!(collection = BLANK, *attributes, &block) click to toggle source
Calls superclass method TurboStreamer#array!
# File lib/turbostreamer/template.rb, line 38
def array!(collection = BLANK, *attributes, &block)
  options = attributes.extract_options!

  if options.key?(:partial)
    partial! options.merge(collection: collection)
  else
    super
  end
end
cache!(key=nil, options={}) { |self| ... } click to toggle source

Caches the json constructed within the block passed. Has the same signature as the `cache` helper method in `ActionView::Helpers::CacheHelper` and so can be used in the same way.

Example:

json.cache! ['v1', @person], expires_in: 10.minutes do
  json.extract! @person, :name, :age
end
# File lib/turbostreamer/template.rb, line 57
def cache!(key=nil, options={})
  if @context.controller.perform_caching
    value = _cache_fragment_for(key, options) do
      _capture { _scope { yield self }; }
    end

    inject!(value)
  else
    yield
  end
end
cache_collection!(collection, options = {}) { |keys_to_collection_map| ... } click to toggle source

Caches a collection of objects using fetch_multi, if supported. Requires a block for each item in the array. Accepts optional 'key' attribute in options (e.g. key: 'v1').

Example:

json.cache_collection! @people, expires_in: 10.minutes do |person|

json.partial! 'person', :person => person

end

# File lib/turbostreamer/template.rb, line 78
def cache_collection!(collection, options = {}, &block)
  if @context.controller.perform_caching
    keys_to_collection_map = _keys_to_collection_map(collection, options)
    results = _read_multi_fragment_cache(keys_to_collection_map.keys, options)
    
    array! do
      keys_to_collection_map.each_key do |key|
        if results[key]
          inject!(results[key])
        else
          value = _write_fragment_cache(key, options) do
            _capture { _scope { yield keys_to_collection_map[key] } }
          end
          inject!(value)
        end
      end
    end
  else
    array! collection, options, &block
  end
end
cache_if!(condition, *args) { || ... } click to toggle source

Conditionally catches the json depending in the condition given as first parameter. Has the same signature as the `cache` helper method in `ActionView::Helpers::CacheHelper` and so can be used in the same way.

Example:

json.cache_if! !admin?, @person, expires_in: 10.minutes do
  json.extract! @person, :name, :age
end
# File lib/turbostreamer/template.rb, line 109
def cache_if!(condition, *args, &block)
  condition ? cache!(*args, &block) : yield
end
partial!(name_or_options, locals = {}) click to toggle source
# File lib/turbostreamer/template.rb, line 16
def partial!(name_or_options, locals = {})
  if name_or_options.class.respond_to?(:model_name) && name_or_options.respond_to?(:to_partial_path)
    @context.render(name_or_options, json: self)
  else
    if name_or_options.is_a?(Hash)
      options = name_or_options
    else
      if locals.one? && (locals.keys.first == :locals)
        options = locals.merge(partial: name_or_options)
      else
        options = { partial: name_or_options, locals: locals }
      end
      # partial! 'name', foo: 'bar'
      as = locals.delete(:as)
      options[:as] = as if as.present?
      options[:collection] = locals[:collection] if locals.key?(:collection)
    end
    
    _render_partial_with_options options
  end
end

Private Instance Methods

_cache_fragment_for(key, options, &block) click to toggle source
# File lib/turbostreamer/template.rb, line 173
def _cache_fragment_for(key, options, &block)
  key = _cache_key(key, options)
  _read_fragment_cache(key, options) || _write_fragment_cache(key, options, &block)
end
_keys_to_collection_map(collection, options) click to toggle source
# File lib/turbostreamer/template.rb, line 162
def _keys_to_collection_map(collection, options)
  key = options.delete(:key)
  
  collection.inject({}) do |result, item|
    key = key.respond_to?(:call) ? key.call(item) : key
    cache_key = key ? [key, item] : item
    result[_cache_key(cache_key, options)] = item
    result
  end
end
_read_fragment_cache(key, options = nil) click to toggle source
# File lib/turbostreamer/template.rb, line 184
def _read_fragment_cache(key, options = nil)
  @context.controller.instrument_fragment_cache :read_fragment, key do
    ::Rails.cache.read(key, options)
  end
end
_read_multi_fragment_cache(keys, options = nil) click to toggle source
# File lib/turbostreamer/template.rb, line 178
def _read_multi_fragment_cache(keys, options = nil)
  @context.controller.instrument_fragment_cache :read_multi_fragment, keys do
    ::Rails.cache.read_multi(*keys, options)
  end
end
_render_partial_with_options(options) click to toggle source
# File lib/turbostreamer/template.rb, line 115
def _render_partial_with_options(options)

  options.reverse_merge! ::TurboStreamer::Template.template_lookup_options
  as = options[:as]&.to_sym
  options[:locals] ||= {}
  options[:locals][:json] = self

  if as && options.key?(:collection)
    # Option 1, nice simple, fast, calls find_template once
    array! { @context.render(options) }

    # Option 2, the jBuilder way, slow because find_template for every item
    # in the collection (a method which is known as one of the heaviest parts
    # of Action View)
    # as = as.to_sym
    # collection = options.delete(:collection)
    # locals = options.delete(:locals)
    # array! collection do |member|
    #   member_locals = locals.clone
    #   member_locals.merge! collection: collection
    #   member_locals.merge! as => member
    #   _render_partial options.merge(locals: member_locals)
    # end

    # Option 3, the fastest, haven't looked into precisely why, but would need
    # to customeize to the rails version
    # lookup_context = @context.view_renderer.lookup_context
    # options[:locals][:json] = self
    # options[:locals][:collection] = options[:collection]
    #
    # pr = ActionView::PartialRenderer.new(lookup_context)
    # pr.send(:setup, @context, options, as, nil)
    # path = pr.instance_variable_get(:@path)
    # a, b, c = pr.send(:retrieve_variable, path, as)
    # template_keys = pr.send(:retrieve_template_keys, a).compact
    # # + [:"#{a}__counter", :"#{a}_iteration"]
    # template = pr.send(:find_partial, path, template_keys)
    # locals = options[:locals]
    # array! options[:collection] do |member|
    #   locals[as] = member
    #   template.render(@context, locals)
    # end
  else
    @context.render(options)
  end
end
_write_fragment_cache(key, options = nil) { || ... } click to toggle source
# File lib/turbostreamer/template.rb, line 190
def _write_fragment_cache(key, options = nil)
  @context.controller.instrument_fragment_cache :write_fragment, key do
    yield.tap do |value|
      ::Rails.cache.write(key, value, options)
    end
  end