class Bluepine::Attributes::Visitor
An abstract Visitor
for traversing {Attribute}.
Sub-classes must implement the methods correspond to {Attribute} classes.
@example Implements string
visitor for {StringAttribute}
class SimpleVisitor < Bluepine::Attributes::Visitor def visit_string(attribute, *args) "Hello #{attribute.name}" end end # Usage username = Attributes.create(:string, :username) visitor = StringVisitor.new visitor.visit(username) # => "Hello username"
@abstract
Constants
- MethodNotFound
Public Instance Methods
Traveres a visitable object and calls corresponding method based-on sub-classes' impementations.
@example When attribute
is an instance of {Attribute}.
object = Attributes.create(:object, :user) { } visit(object) # => visit_object
@example When attribute
is a {Symbol}.
visit(:user) # => visit_user or visit_schema(attr, of: :user)
@param [Attribute|Symbol] The Attribute
object or Symbol
# File lib/bluepine/attributes/visitor.rb, line 36 def visit(attribute, *args) method, attribute = find_method!(attribute, *args) send(method, attribute, *args) end
Performs visitor logic when no corresponding method can be found (Catch-all).
# File lib/bluepine/attributes/visitor.rb, line 43 def visit_attribute(attribute, options = {}) raise NotImplementedError end
# File lib/bluepine/attributes/visitor.rb, line 47 def visit_schema(attribute, options = {}) raise NotImplementedError end
Private Instance Methods
Finds a vistor method.
@return Array<String, Attribute> Pair of method name and Attribute
object
It'll return a first callable method in the chains or return nil
when no methods can be found. If attribute
is a Symbol. It'll stop looking any further.
find_method(:integer) # => `visit_integer`
If it's an instance of Attribute
. It'll look up in ancestors chain and return the first callable method.
object = Attributes.create(:object, :name) { ... } find_method(object) => `visit_object`
# File lib/bluepine/attributes/visitor.rb, line 67 def find_method(attribute, *args) compose( curry(:resolve_methods), curry(:respond_to_visitor?), curry(:normalize_symbol, attribute, args) ).(attribute) end
# File lib/bluepine/attributes/visitor.rb, line 75 def find_method!(attribute, *args) method, attribute = find_method(attribute, *args) raise MethodNotFound, normalize_method(attribute) unless method [method, attribute] end
Creates Attribute
from symbol e.g. :integer to IntegerAttribute
# File lib/bluepine/attributes/visitor.rb, line 103 def normalize_attribute(attribute, options = {}) return attribute unless Attributes.key?(attribute) Attributes.create(attribute, attribute.to_s, options) end
# File lib/bluepine/attributes/visitor.rb, line 94 def normalize_method(method) return unless method # Cannot use `chomp("Attribute")` here, because the top most class is `Attribute` "visit_" + method.to_s.demodulize.gsub(/(\w+)Attribute/, "\\1").underscore end
# File lib/bluepine/attributes/visitor.rb, line 129 def normalize_schema_symbol(schema, *args) args.last[:of] = schema if args.last.is_a?(Hash) [:schema, :schema] end
# File lib/bluepine/attributes/visitor.rb, line 114 def normalize_symbol(attribute, args, method) # When we can't find method e.g. `visit_{method}` # and `attribute` is a symbol (e.g. :user), # we'll enforce the same logic for all visitor sub-classes # by calling `visit_schema` with of: attribute if !method && attribute.kind_of?(Symbol) method, attribute = normalize_schema_symbol(attribute, *args) end [ normalize_method(method), normalize_attribute(attribute, *args), ] end
Returns list of method Symbols from given Attribute
@return [Symbol] Method symbols e.g. [:string, StringAttribute
, Attribute]
# File lib/bluepine/attributes/visitor.rb, line 86 def resolve_methods(attribute) return [attribute] if attribute.kind_of?(Symbol) # Finds all ancestors in hierarchy up to `Attribute` parents = attribute.class.ancestors.map(&:to_s) parents.slice(0, parents.index(Attribute.name).to_i + 1) end
Finds method from method list that respond_to `visit_{attribute}` call
# File lib/bluepine/attributes/visitor.rb, line 110 def respond_to_visitor?(methods) methods.find { |m| respond_to?(normalize_method(m)) } end