class Curlybars::RenderingSupport
Attributes
cache[R]
cached_calls[R]
contexts[R]
file_name[R]
global_helpers[R]
start_time[R]
timeout[R]
variables[R]
Public Class Methods
new(timeout, contexts, variables, file_name, global_helpers_providers = [], cache = ->(key, &block) { block.call }
click to toggle source
# File lib/curlybars/rendering_support.rb, line 3 def initialize(timeout, contexts, variables, file_name, global_helpers_providers = [], cache = ->(key, &block) { block.call }) @timeout = timeout @start_time = Time.now @contexts = contexts @variables = variables @file_name = file_name @cached_calls = {} @cache = cache @global_helpers = {} global_helpers_providers.each do |provider| provider.allowed_methods.each do |global_helper_name| symbol = global_helper_name.to_sym @global_helpers[symbol] = provider.method(symbol) end end end
Public Instance Methods
cached_call(meth)
click to toggle source
# File lib/curlybars/rendering_support.rb, line 106 def cached_call(meth) return cached_calls[meth] if cached_calls.key? meth instrument(meth) { cached_calls[meth] = meth.call(*arguments_for_signature(meth, [], {})) } end
call(helper, helper_path, helper_position, arguments, options, &block)
click to toggle source
# File lib/curlybars/rendering_support.rb, line 112 def call(helper, helper_path, helper_position, arguments, options, &block) parameters = helper.parameters parameter_types = parameters.map(&:first) # parameters has value [[:rest]] when the presenter is using method_missing to catch all calls has_invalid_parameters = parameter_types.map { |type| type != :req }.any? && parameter_types != [:rest] if has_invalid_parameters source_location = helper.source_location file_path = source_location ? source_location.first : "n/a" line_number = source_location ? helper.source_location.last : "n/a" message = "#{file_path}:#{line_number} - `#{helper_path}` bad signature " message << "for #{helper} - helpers must have only required parameters" raise Curlybars::Error::Render.new('invalid_helper_signature', message, helper_position) end instrument(helper) do helper.call(*arguments_for_signature(helper, arguments, options), &block) end end
check_context_is_hash_or_enum_of_presenters(collection, path, position)
click to toggle source
# File lib/curlybars/rendering_support.rb, line 38 def check_context_is_hash_or_enum_of_presenters(collection, path, position) return if presenter_collection?(collection) message = "`#{path}` is not an array of presenters or a hash of such" raise Curlybars::Error::Render.new('context_is_not_an_array_of_presenters', message, position) end
check_context_is_presenter(context, path, position)
click to toggle source
# File lib/curlybars/rendering_support.rb, line 31 def check_context_is_presenter(context, path, position) return if presenter?(context) message = "`#{path}` is not a context type object" raise Curlybars::Error::Render.new('context_is_not_a_presenter', message, position) end
check_timeout!()
click to toggle source
# File lib/curlybars/rendering_support.rb, line 23 def check_timeout! return unless timeout.present? return unless (Time.now - start_time) > timeout message = "Rendering took too long (> #{timeout} seconds)" raise ::Curlybars::Error::Render.new('timeout', message, nil) end
coerce_to_hash!(collection, path, position)
click to toggle source
# File lib/curlybars/rendering_support.rb, line 138 def coerce_to_hash!(collection, path, position) check_context_is_hash_or_enum_of_presenters(collection, path, position) if collection.is_a?(Hash) collection elsif collection.respond_to? :each_with_index collection.each_with_index.map { |value, index| [index, value] }.to_h else raise "Collection is not coerceable to hash" end end
optional_presenter_cache(presenter, template_cache_key, buffer) { |cache_buffer| ... }
click to toggle source
# File lib/curlybars/rendering_support.rb, line 161 def optional_presenter_cache(presenter, template_cache_key, buffer) presenter_cache_key = presenter.respond_to?(:cache_key) ? presenter.cache_key : nil if presenter_cache_key cache_key = "#{presenter_cache_key}/#{template_cache_key}" buffer << cache.call(cache_key) do # Output from the block must be isolated from the main output buffer SafeBuffer.new.tap do |cache_buffer| yield cache_buffer end end else yield buffer end end
path(path, position)
click to toggle source
# File lib/curlybars/rendering_support.rb, line 67 def path(path, position) return global_helpers[path.to_sym] if global_helpers.key?(path.to_sym) check_traverse_not_too_deep(path, position) path_split_by_slashes = path.split('/') backward_steps_on_contexts = path_split_by_slashes.count - 1 base_context_position = contexts.length - backward_steps_on_contexts return -> {} unless base_context_position > 0 base_context_index = base_context_position - 1 base_context = contexts[base_context_index] dotted_path_side = path_split_by_slashes.last chain = dotted_path_side.split('.') method_to_return = chain.pop resolved = chain.inject(base_context) do |context, meth| next context if meth == 'this' next context.count if meth == 'length' && presenter_collection?(context) raise_if_not_traversable(context, meth, position) outcome = instrument(context.method(meth)) { context.public_send(meth) } return -> {} if outcome.nil? outcome end return -> { resolved } if method_to_return == 'this' if method_to_return == 'length' && presenter_collection?(resolved) return -> { resolved.count } end raise_if_not_traversable(resolved, method_to_return, position) resolved.method(method_to_return.to_sym) end
position(line_number, line_offset)
click to toggle source
# File lib/curlybars/rendering_support.rb, line 134 def position(line_number, line_offset) Curlybars::Position.new(file_name, line_number, line_offset) end
presenter?(context)
click to toggle source
# File lib/curlybars/rendering_support.rb, line 149 def presenter?(context) context.respond_to? :allows_method? end
presenter_collection?(collection)
click to toggle source
# File lib/curlybars/rendering_support.rb, line 153 def presenter_collection?(collection) collection = collection.values if collection.is_a?(Hash) collection.respond_to?(:each) && collection.all? do |presenter| presenter?(presenter) end end
to_bool(condition)
click to toggle source
# File lib/curlybars/rendering_support.rb, line 45 def to_bool(condition) condition != false && condition != [] && condition != {} && condition != 0 && condition != '' && !condition.nil? end
variable(variable_path, position)
click to toggle source
# File lib/curlybars/rendering_support.rb, line 54 def variable(variable_path, position) check_traverse_not_too_deep(variable_path, position) variable_split_by_slashes = variable_path.split('/') variable = variable_split_by_slashes.last.to_sym backward_steps_on_variables = variable_split_by_slashes.count - 1 variables_position = variables.length - backward_steps_on_variables scope = variables.first(variables_position).reverse.find do |vars| vars.key? variable end return scope[variable] if scope end
Private Instance Methods
arguments_for_signature(helper, arguments, options)
click to toggle source
# File lib/curlybars/rendering_support.rb, line 190 def arguments_for_signature(helper, arguments, options) return [] if helper.parameters.empty? return arguments if helper.parameters.map(&:first) == [:rest] number_of_parameters_available_for_arguments = helper.parameters.length - 1 arguments_that_can_fit = arguments.first(number_of_parameters_available_for_arguments) nil_padding_length = number_of_parameters_available_for_arguments - arguments_that_can_fit.length nil_padding = Array.new(nil_padding_length) [arguments_that_can_fit, nil_padding, options].flatten(1) end
check_context_allows_method(context, meth, position)
click to toggle source
# File lib/curlybars/rendering_support.rb, line 208 def check_context_allows_method(context, meth, position) return if context.allows_method?(meth.to_sym) message = "`#{meth}` is not available - " message += "add `allow_methods :#{meth}` to #{context.class} to allow this path" raise Curlybars::Error::Render.new('unallowed_path', message, position, meth: meth.to_sym) end
check_context_has_method(context, meth, position)
click to toggle source
# File lib/curlybars/rendering_support.rb, line 216 def check_context_has_method(context, meth, position) return if context.respond_to?(meth.to_sym) message = "`#{meth}` is not available in #{context.class}" raise Curlybars::Error::Render.new('unallowed_path', message, position) end
check_traverse_not_too_deep(traverse, position)
click to toggle source
# File lib/curlybars/rendering_support.rb, line 223 def check_traverse_not_too_deep(traverse, position) return unless traverse.count('.') > Curlybars.configuration.traversing_limit message = "`#{traverse}` too deep" raise Curlybars::Error::Render.new('traverse_too_deep', message, position) end
instrument(meth) { || ... }
click to toggle source
# File lib/curlybars/rendering_support.rb, line 182 def instrument(meth, &block) # Instruments only callables that give enough details (eg. methods) return yield unless meth.respond_to?(:name) && meth.respond_to?(:owner) payload = { presenter: meth.owner, method: meth.name } ActiveSupport::Notifications.instrument("call_to_presenter.curlybars", payload, &block) end
raise_if_not_traversable(context, meth, position)
click to toggle source
# File lib/curlybars/rendering_support.rb, line 202 def raise_if_not_traversable(context, meth, position) check_context_is_presenter(context, meth, position) check_context_allows_method(context, meth, position) check_context_has_method(context, meth, position) end