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

visit(attribute, *args) click to toggle source

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
visit_attribute(attribute, options = {}) click to toggle source

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
visit_schema(attribute, options = {}) click to toggle source
# File lib/bluepine/attributes/visitor.rb, line 47
def visit_schema(attribute, options = {})
  raise NotImplementedError
end

Private Instance Methods

find_method(attribute, *args) click to toggle source

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
find_method!(attribute, *args) click to toggle source
# 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
normalize_attribute(attribute, options = {}) click to toggle source

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
normalize_method(method) click to toggle source
# 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
normalize_schema_symbol(schema, *args) click to toggle source
# 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
normalize_symbol(attribute, args, method) click to toggle source
# 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
resolve_methods(attribute) click to toggle source

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
respond_to_visitor?(methods) click to toggle source

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