class Pretentious::Deconstructor

Deconstructor - decompose an object into its parts

Public Class Methods

block_param_names(proc) click to toggle source
# File lib/pretentious/deconstructor.rb, line 222
def self.block_param_names(proc)
  parameters_to_join = []

  parameters = proc.target_proc.parameters

  parameters.each { |p| parameters_to_join << p[1].to_s }
  parameters_to_join
end
block_params_generator(proc, separator = '|') click to toggle source
# File lib/pretentious/deconstructor.rb, line 231
def self.block_params_generator(proc, separator = '|')
  if proc.target_proc.parameters.size > 0
    return "#{separator}#{block_param_names(proc).join(', ')}#{separator}"
  end

  ''
end
primitive?(value) click to toggle source
# File lib/pretentious/deconstructor.rb, line 217
def self.primitive?(value)
  value.is_a?(String) || value.is_a?(Fixnum) || value.is_a?(TrueClass) || value.is_a?(FalseClass) ||
    value.is_a?(NilClass) || value.is_a?(Symbol) || value.is_a?(Class)
end
proc_body(context, proc, indentation = '') click to toggle source
# File lib/pretentious/deconstructor.rb, line 247
def self.proc_body(context, proc, indentation = '')
  if proc.return_value.size == 1
    "#{indentation}  #{context.value_of(proc.return_value[0])}\n"
  else
    "#{indentation}  \# Variable return values ... can't figure out what goes in here...\n"
  end
end

Public Instance Methods

build_output(context, indentation_level, declarations) click to toggle source
# File lib/pretentious/deconstructor.rb, line 197
def build_output(context, indentation_level, declarations)
  output_buffer = ''
  indentation = ''
  indentation_level.times { indentation << ' ' }
  declarations[:declaration].select { |d| d[:used_by] != :inline }.each do |d|
    if !context.was_declared_previously?(d[:id])
      var_name = context.pick_name(d[:id])
      output_buffer << "#{indentation}#{var_name} = #{construct(context, d, indentation)}\n"
    elsif context.was_declared_previously?(d[:id])
      context.register_instance_variable(d[:id])
    end
  end
  output_buffer
end
build_tree(target_object) click to toggle source

creates a tree on how the object was created

# File lib/pretentious/deconstructor.rb, line 137
def build_tree(target_object)
  tree = { class: get_test_class(target_object), id: target_object.object_id, composition: [] }
  if target_object.is_a? Array
    tree[:composition] = deconstruct_array(target_object)
  elsif target_object.is_a? Hash
    tree[:composition] = deconstruct_hash(target_object)
  elsif target_object.is_a? Pretentious::RecordedProc
    tree[:composition] = deconstruct_proc(target_object)
    tree[:given_block] = target_object.given_block?
    tree[:recorded_proc] = target_object
    tree[:id] = target_object.target_proc.object_id
    tree[:block_params] = self.class.block_param_names(target_object)
  elsif target_object.methods.include? :_get_init_arguments
    args = target_object._get_init_arguments
    if args.nil?
      if self.class.primitive?(target_object)
        tree[:composition] = target_object
      elsif target_object.class == File
        tree[:composition] << build_tree(target_object.path)
      else
        tree[:composition] = UnResolved.new(target_object)
      end
    else
      tree[:params_types] = args[:params_types]
      args[:params].each { |p| tree[:composition] << build_tree(p) }

      tree[:block] = build_tree(args[:block]) unless args[:block].nil?
    end

  else
    tree[:composition] = target_object
  end
  tree
end
deconstruct(method_call_collection, *target_objects) click to toggle source
# File lib/pretentious/deconstructor.rb, line 172
def deconstruct(method_call_collection, *target_objects)
  @declaration_order = []
  @dependencies = {}

  target_objects.each do |target_object|
    tree = build_tree target_object
    dfs(tree)
  end

  method_call_collection.each do |m|
    update_ref_counts(m[:params], m)
  end

  inline

  { declaration: @declaration_order, dependency: @dependencies }
end
deconstruct_array(array) click to toggle source
# File lib/pretentious/deconstructor.rb, line 255
def deconstruct_array(array)
  composition = []
  array.each do |v|
    if Pretentious::Deconstructor.primitive?(v)
      composition << v
    elsif v.is_a? Hash
      composition << deconstruct_hash(v)
    elsif v.is_a? Array
      composition << deconstruct_array(v)
    else
      composition << Reference.new(build_tree(v))
    end
  end
  composition
end
deconstruct_hash(hash) click to toggle source
# File lib/pretentious/deconstructor.rb, line 271
def deconstruct_hash(hash)
  composition = {}
  hash.each do |k, v|
    if Pretentious::Deconstructor.primitive?(v)
      composition[k] = v
    elsif v.is_a? Hash
      composition[k] = deconstruct_hash(v)
    elsif v.is_a? Array
      composition[k] = deconstruct_array(v)
    else
      composition[k] = Reference.new(build_tree(v))
    end
  end
  composition
end
deconstruct_proc(proc) click to toggle source
# File lib/pretentious/deconstructor.rb, line 287
def deconstruct_proc(proc)
  return nil if proc.return_value.size != 1
  return build_tree(proc.return_value[0]) unless proc.return_value[0].nil?
end
deconstruct_to_ruby(context, indentation_level = 0, *target_objects) click to toggle source
# File lib/pretentious/deconstructor.rb, line 212
def deconstruct_to_ruby(context, indentation_level = 0, *target_objects)
  declarations, _dependencies = generate_declarations context, [], *target_objects
  build_output(context, indentation_level, declarations)
end
dfs(tree) click to toggle source
# File lib/pretentious/deconstructor.rb, line 59
def dfs(tree)
  if !tree.is_a? Hash
    value = tree
    definition = { id: value.object_id,
                   class: tree.class,
                   value: value,
                   used_by: [] }
    unless @dependencies.include? value.object_id
      @dependencies[value.object_id] = definition
      @declaration_order << definition
    end
    value.object_id
  else
    ref = []

    definition = { id: tree[:id],
                   class: tree[:class],
                   params_types: tree[:params_types],
                   used_by: [] }

    if tree[:class] == Hash
      definition[:value] = dfs_hash(tree[:composition], ref)
    elsif tree[:class] == Array
      definition[:value] = dfs_array(tree[:composition], ref)
    elsif tree[:class] == Pretentious::RecordedProc
      definition[:recorded_proc] = tree[:recorded_proc]

      if !tree[:composition].nil?
        ref << dfs(tree[:composition])
      else
        dfs(tree[:composition])
      end

    elsif tree[:composition].is_a? Array
      tree[:composition].each { |t| ref << dfs(t) }
    else
      ref << dfs(tree[:composition])
    end

    # evaluate given block composition
    ref << dfs(tree[:block]) if tree[:block]

    definition[:ref] = ref

    unless @dependencies.include? tree[:id]
      @declaration_order << definition
      @dependencies[tree[:id]] = definition

      ref.each { |r| @dependencies[r][:used_by] << definition }
    end

    tree[:id]
  end
end
dfs_array(arr, refs) click to toggle source
# File lib/pretentious/deconstructor.rb, line 22
def dfs_array(arr, refs)
  value = []
  arr.each do |v|
    if Pretentious::Deconstructor.primitive?(v)
      value << v
    elsif v.is_a? Hash
      value << dfs_hash(v, refs)
    elsif v.is_a? Array
      value << dfs_array(v, refs)
    elsif v.is_a? Reference
      refs << v.tree[:id]
      value << Reference.new(dfs(v.tree))
    elsif value << v
    end
  end
  value
end
dfs_hash(hash, refs) click to toggle source
# File lib/pretentious/deconstructor.rb, line 40
def dfs_hash(hash, refs)
  value = {}
  hash.each do |k, v|
    if Pretentious::Deconstructor.primitive?(v)
      value[k] = v
    elsif v.is_a? Hash
      value[k] = dfs_hash(v, refs)
    elsif v.is_a? Array
      value[k] = dfs_array(v, refs)
    elsif v.is_a? Reference
      refs << v.tree[:id]
      value[k] = Reference.new(dfs(v.tree))
    else
      value[k] = v
    end
  end
  value
end
generate_declarations(context, method_call_collection, *target_objects) click to toggle source
# File lib/pretentious/deconstructor.rb, line 190
def generate_declarations(context, method_call_collection, *target_objects)
  target_objects.each do |target_object|
    context.merge_variable_map(target_object)
  end
  deconstruct method_call_collection, *target_objects
end
get_test_class(target_object) click to toggle source
# File lib/pretentious/deconstructor.rb, line 292
def get_test_class(target_object)
  target_object.respond_to?(:test_class) ? target_object.test_class : target_object.class
end
inline() click to toggle source
# File lib/pretentious/deconstructor.rb, line 122
def inline
  @dependencies.each do |id, definition|
    next if definition[:used_by].size != 1
    next if !definition.include?(:value) || !self.class.primitive?(definition[:value])
    ref = definition[:used_by][0]
    definition[:used_by] = :inline
    references = ref[:ref]
    if references
      new_ref = references.collect { |c| c == id ? definition : c }
      ref[:ref] = new_ref
    end
  end
end
proc_to_ruby(context, proc, indentation = '') click to toggle source
# File lib/pretentious/deconstructor.rb, line 239
def proc_to_ruby(context, proc, indentation = '')
  output_buffer = ''
  output_buffer << "proc { #{self.class.block_params_generator(proc)}\n"
  output_buffer << self.class.proc_body(context, proc, indentation)
  output_buffer << "#{indentation}}\n"
  output_buffer
end
update_ref_counts(params_arr, method_call) click to toggle source
# File lib/pretentious/deconstructor.rb, line 114
def update_ref_counts(params_arr, method_call)
  params_arr.each do |p|
    if @dependencies.include? p.object_id
      @dependencies[p.object_id][:used_by] << method_call
    end
  end
end

Private Instance Methods

construct(context, definition, indentation = '') click to toggle source
# File lib/pretentious/deconstructor.rb, line 341
def construct(context, definition, indentation = '')
  if definition.include? :value
    if definition[:value].is_a? Hash
      output_hash(context, definition[:value])
    elsif definition[:value].is_a? Array
      output_array(context, definition[:value])
    elsif definition[:value].is_a? UnResolved
      "#{definition[:value].target_object.class.to_s}.new # parameters unresolvable. The watcher needs to be installed before this object is created"
    else
      context.value_of(definition[:value])
    end
  elsif definition[:class] == Pretentious::RecordedProc
    proc_to_ruby(context, definition[:recorded_proc], indentation)
  else
    params = []
    if definition[:ref] && definition[:ref].size > 0

      params_types = definition[:params_types]
      definition[:ref].each_with_index do |v, index|
        type = :param
        if params_types && params_types[index]
          type = params_types[index][0]
        end

        # to inline?
        if v.is_a? Hash
          params << context.value_of(v[:value])
        else
          params << (type == :block ? "&#{context.pick_name(v)}" : context.pick_name(v))
        end
      end
      "#{definition[:class]}.new(#{params.join(', ')})"
    else
      "#{definition[:class]}.new"
    end

  end
end
output_array(context, arr) click to toggle source
# File lib/pretentious/deconstructor.rb, line 298
def output_array(context, arr)
  output_buffer = '['
  array_elements = []
  arr.each do |v|
    value = Pretentious.value_ize(context, v)
    if v.is_a? Hash
      value = output_hash(context, v)
    elsif v.is_a? Array
      value = output_array(context, v)
    elsif v.is_a? Reference
      value = context.pick_name(v.tree)
    end
    array_elements << value
  end
  output_buffer << array_elements.join(', ')
  output_buffer << ']'
  output_buffer
end
output_hash(context, hash) click to toggle source
# File lib/pretentious/deconstructor.rb, line 317
def output_hash(context, hash)
  output_buffer = '{ '
  hash_elements = []
  hash.each do |k, v|
    value = context.value_of(v)
    if v.is_a? Hash
      value = output_hash(context, v)
    elsif v.is_a? Array
      value = output_array(context, v)
    elsif v.is_a? Reference
      value = context.pick_name(v.tree)
    end

    if k.is_a? Symbol
      hash_elements << "#{k}: #{value}"
    else
      hash_elements << "#{context.value_of(k)} => #{value}"
    end
  end
  output_buffer << hash_elements.join(', ')
  output_buffer << ' }'
  output_buffer
end