class ActiveExplorer::Exploration

Constants

ASSOCIATION_FILTER_VALUES

Public Class Methods

new(object, depth: 5, class_filter: nil, attribute_filter: nil, attribute_limit: nil, association_filter: nil, parent_object: nil) click to toggle source

Creates new exploration and generates exploration hash.

@param association_filter [Array]

Values of array: `:has_many`, `:has_one`, `:belongs_to`, `:all`.
When empty then it "follows previous association" (i.e. uses `:belongs_to` when previous assoc. was `:belongs_to` and
uses `:has_xxx` when previous assoc. was `:has_xxx`). To always follow all associations you must specify
all associations (e.g. uses `ActiveExplorer::Exploration::ASSOCIATION_FILTER_VALUES` as a value).

@param class_filter [Array or Hash]

If Array is used then it means to show only those classes in Array.
When Hash is used then it can have these keys:
  - `:show` - Shows these classes, ignores at all other classes.
  - `:ignore` - Stops processing at these, does not show it and does not go to children. Processing goes back to parent.
Use plural form (e.g. `books`).

@param depth [Integer]

How deep into the subobjects should the explorere go. Depth 1 is only direct children. Depth 0 returns no children.
# File lib/exploration.rb, line 27
def initialize(object, depth: 5, class_filter: nil, attribute_filter: nil, attribute_limit: nil, association_filter: nil, parent_object: nil)
  raise TypeError, "Parameter 'class_filter' must be Array or Hash but is #{class_filter.class}." unless class_filter.nil? || class_filter.is_a?(Array) || class_filter.is_a?(Hash)
  raise TypeError, "Parameter 'association_filter' must be Array but is #{association_filter.class}." unless association_filter.nil? || association_filter.is_a?(Array)
  raise TypeError, "Parameter 'association_filter' must only contain values #{ASSOCIATION_FILTER_VALUES.to_s[1..-2]}." unless association_filter.nil? || association_filter.empty? || (association_filter & ASSOCIATION_FILTER_VALUES).any?

  @object = object
  @depth = depth
  @parent_object = parent_object

  @attribute_limit = attribute_limit || ActiveExplorer::Config.attribute_limit
  @attribute_filter = attribute_filter || ActiveExplorer::Config.attribute_filter

  @hash = { class_name: make_safe(@object.class.name),
            attributes: attributes }

  unless @depth.zero?
    @class_filter = class_filter || ActiveExplorer::Config.class_filter
    @class_filter = { show: @class_filter } if @class_filter.is_a?(Array)

    if @class_filter
      [:show, :ignore].each do |group|
        @class_filter[group] = @class_filter[group].present? ? each_val_to_s(@class_filter[group]) : []
      end
    end

    @association_filter = association_filter || ActiveExplorer::Config.association_filter
    @association_filter = ASSOCIATION_FILTER_VALUES if @association_filter.present? && @association_filter.include?(:all)

    @associations = associtations(@object, @class_filter, @association_filter)

    subobject_hash = subobjects_hash(@object, @associations)
    @hash[:subobjects] = subobject_hash unless subobject_hash.empty?
  end
end

Public Instance Methods

attributes() click to toggle source
# File lib/exploration.rb, line 62
def attributes
  attrs = @object.attributes.symbolize_keys
  attrs = attrs.first(@attribute_limit).to_h if @attribute_limit

  return attrs if @attribute_filter.nil?

  filter = @attribute_filter[@object.class.name.downcase.pluralize.to_sym]

  if filter
    attrs.select { |key| filter.include?(key) }
  else
    attrs
  end
end
get_hash() click to toggle source
# File lib/exploration.rb, line 77
def get_hash
  @hash.dup
end
to_console() click to toggle source
# File lib/exploration.rb, line 81
def to_console
  Writer.new(self).write
end
to_image(file, origin_as_root: false) click to toggle source
# File lib/exploration.rb, line 85
def to_image(file, origin_as_root: false)
  Painter.new(self, file).paint(origin_as_root: origin_as_root)
end

Private Instance Methods

add_error_hash(message) click to toggle source
# File lib/exploration.rb, line 177
def add_error_hash(message)
  @hash[:class_name] = make_safe(@object.class.name)
  @hash[:attributes] = attributes

  @hash[:error_message] = "Error in #{@object.class.name}(#{@object.id}): #{message}"
end
association_type(association) click to toggle source
# File lib/exploration.rb, line 184
def association_type(association)
  association.macro
end
associtations(object, class_filter, association_filter) click to toggle source
# File lib/exploration.rb, line 151
def associtations(object, class_filter, association_filter)
  associations = object.class.reflections.collect do |reflection|
    reflection.second
  end

  if !association_filter.nil? && association_filter.any? && !association_filter.include?(:all)
    associations.select! do |association|
      association_filter.include? association_type(association)
    end
  end

  if class_filter
    if class_filter[:show].any?
      associations.select! do |association|
        should_show?(association)
      end
    elsif class_filter[:ignore].any?
      associations.reject! do |association|
        should_ignore?(association)
      end
    end
  end

  associations
end
each_val_to_s(array) click to toggle source
# File lib/exploration.rb, line 199
def each_val_to_s(array)
  array.collect { |a| a.to_s }
end
explore(object, parent_object:, association_type:) click to toggle source
# File lib/exploration.rb, line 133
def explore(object, parent_object:, association_type:)
  association_filter = if !@association_filter.nil? && @association_filter.any?
                         @association_filter
                       elsif association_type == :belongs_to
                         [:belongs_to]
                       else
                         [:has_many, :has_one]
                       end

  Exploration.new object,
                  depth: @depth - 1,
                  class_filter: @class_filter,
                  attribute_filter: @attribute_filter,
                  attribute_limit: @attribute_limit,
                  association_filter: association_filter,
                  parent_object: parent_object
end
is_parent?(object) click to toggle source
# File lib/exploration.rb, line 203
def is_parent?(object)
  object === @parent_object
end
make_safe(text) click to toggle source

Replace characters that conflict with DOT language (used in GraphViz). These: ‘{`, `}`, `<`, `>`, `|`, ``

# File lib/exploration.rb, line 195
def make_safe(text)
  text.tr('{}<>|\\', '')
end
make_short(text) click to toggle source
# File lib/exploration.rb, line 188
def make_short(text)
  text.length < 70 ? text : text[0..70] + " (...)"
end
should_ignore?(association) click to toggle source
# File lib/exploration.rb, line 211
def should_ignore?(association)
  @class_filter[:ignore].include? association.plural_name.to_s
end
should_show?(association) click to toggle source
# File lib/exploration.rb, line 207
def should_show?(association)
  @class_filter[:show].include? association.plural_name.to_s
end
subobject_hash(association, object, subobject) click to toggle source
# File lib/exploration.rb, line 113
def subobject_hash(association, object, subobject)
  association_type = association_type(association)

  exploration = explore(subobject, parent_object: object, association_type: association_type)

  hash = exploration.get_hash
  hash[:association] = association_type
  hash
end
subobjects_from_association(object, association) click to toggle source
# File lib/exploration.rb, line 123
def subobjects_from_association(object, association)
  subobjects = object.send(association.name)
  subobjects.present? ? subobjects : nil

rescue NameError, ActiveRecord::StatementInvalid, ActiveRecord::RecordNotFound => e
  association_type = association.macro
  add_error_hash("#{e.message} in #{association_type} :#{association.name}")
  nil
end
subobjects_hash(object, associations) click to toggle source
# File lib/exploration.rb, line 91
def subobjects_hash(object, associations)
  associations.each_with_object([]) do |association, results|
    case association_type(association)
      when :belongs_to
        subobject = subobjects_from_association(object, association)

        if subobject.present?
          results.push subobject_hash(association, object, subobject) unless is_parent?(subobject)
        end

      when :has_many, :has_one
        subobjects = subobjects_from_association(object, association)

        if subobjects.present?
          subobjects.each do |subobject|
            results.push subobject_hash(association, object, subobject) unless is_parent?(subobject)
          end
        end
    end
  end
end