require 'rrb/script'
require 'rrb/common_visitor'
module RRB
class ExtractSuperclassVisitor < Visitor
def initialize( namespace, new_class, targets, dumped_info )
@new_superclass = namespace.nested(new_class).name
@targets = targets
@result = []
@dumped_info = dumped_info
end
attr_reader :result
def visit_class( namespace, node )
classname = namespace.nested( node.name )
return unless @targets.include?( classname )
return if node.superclass == nil
@result << Replacer.new_from_id(node.superclass.body,
@dumped_info.shrink_const(namespace,
@new_superclass))
end
end
class ScriptFile
def extract_superclass( namespace, new_class, targets, dumped_info )
visitor = ExtractSuperclassVisitor.new( namespace, new_class, targets,
dumped_info)
@tree.accept( visitor )
@new_script = RRB.replace_str( @input, visitor.result )
end
def add_superclass_def( lines, lineno )
indented = RRB.reindent_lines_node( lines, class_node_on( lineno ) ).join
@new_script = RRB.insert_str( @new_script || @input, lineno, nil, indented )
end
end
class Script
def superclass_def( namespace, new_class, old_superclass, where )
old_superclass = get_dumped_info.shrink_const(namespace, old_superclass.name)
result = [
"class #{new_class} < #{old_superclass}\n",
"end\n"
]
ns = namespace
until ns == where
result = RRB.reindent_lines( result, INDENT_LEVEL )
result.unshift( "#{get_dumped_info[ns].type} #{ns.ary[-1]}\n" )
result.push( "end\n" )
ns = ns.chop
end
result
end
def add_new_class_to_dumped_info(namespace, new_class)
if namespace == Namespace::Toplevel
get_dumped_info[Namespace::Object].consts << new_class
else
get_dumped_info[namespace].consts << new_class
end
end
def extract_superclass( namespace, new_class, targets, path, lineno )
add_new_class_to_dumped_info(namespace, new_class)
@files.each do |scriptfile|
scriptfile.extract_superclass(namespace, new_class, targets,
get_dumped_info )
end
deffile = @files.find{|scriptfile| scriptfile.path == path}
new_superclass = get_dumped_info[targets.first].superclass.class_name
def_str = superclass_def( namespace, new_class,
new_superclass,
deffile.class_on(lineno) )
deffile.add_superclass_def( def_str, lineno )
end
def extract_superclass?( namespace, new_class, targets, path, lineno )
# check namespace exists?
if namespace != RRB::Namespace::Toplevel &&
get_dumped_info[namespace].invalid?
@error_message = "#{namespace.name}: No such namespace"
return false
end
# check all targets exist?
targets.each do |klass|
@error_message = "#{klass.name}: No such class"
return false unless get_dumped_info[klass].type == "class"
end
# check targets have the same superclass
superclass = get_dumped_info[targets.first].superclass
targets.each do |klass|
@error_message = "#{targets.first.name} and #{klass.name} are not sibling classes"
return false unless get_dumped_info[klass].superclass == superclass
end
# check name collision
unless get_dumped_info.resolve_const( namespace, new_class ).nil? then
@error_message = "#{new_class}: already exists"
return false
end
# check where new class is defined
if class_on( path, lineno ).nil? ||
! class_on( path, lineno ).contain?( namespace )
@error_message = "Invalid Position to define new class"
return false
end
return true
end
end
end
syntax highlighted by Code2HTML, v. 0.9.1