require 'set' module Liquid # A drop in liquid is a class which allows you to export DOM like things to liquid. # Methods of drops are callable. # The main use for liquid drops is to implement lazy loaded objects. # If you would like to make data available to the web designers which you don't want loaded unless needed then # a drop is a great way to do that. # # Example: # # class ProductDrop < Liquid::Drop # def top_sales # Shop.current.products.find(:all, :order => 'sales', :limit => 10 ) # end # end # # tmpl = Liquid::Template.parse( ' {% for product in product.top_sales %} {{ product.name }} {%endfor%} ' ) # tmpl.render('product' => ProductDrop.new ) # will invoke top_sales query. # # Your drop can either implement the methods sans any parameters # or implement the liquid_method_missing(name) method which is a catch all. class Drop attr_writer :context # Catch all for the method def liquid_method_missing(method) return nil unless @context && @context.strict_variables raise Liquid::UndefinedDropMethod, "undefined method #{method}" end # called by liquid to invoke a drop def invoke_drop(method_or_key) if self.class.invokable?(method_or_key) send(method_or_key) else liquid_method_missing(method_or_key) end end def key?(_name) true end def inspect self.class.to_s end def to_liquid self end def to_s self.class.name end alias_method :[], :invoke_drop # Check for method existence without invoking respond_to?, which creates symbols def self.invokable?(method_name) invokable_methods.include?(method_name.to_s) end def self.invokable_methods @invokable_methods ||= begin blacklist = Liquid::Drop.public_instance_methods + [:each] if include?(Enumerable) blacklist += Enumerable.public_instance_methods blacklist -= [:sort, :count, :first, :min, :max, :include?] end whitelist = [:to_liquid] + (public_instance_methods - blacklist) Set.new(whitelist.map(&:to_s)) end end end end