Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

A refactor, for readability.

  • Loading branch information...
commit 960c44793683666e5674708d635be726e796ee1d 1 parent ea5952f
@james-dowling james-dowling authored
Showing with 135 additions and 80 deletions.
  1. +135 −80 lumberjack.rb
View
215 lumberjack.rb
@@ -1,44 +1,43 @@
class Lumberjack
-
+
def self.construct(initial_scope = [], &block)
builder = new(initial_scope)
- builder.__process(block)
+ builder.__process(block)
end
-
+
@@methods_to_keep = /^__/, /class/, /instance_eval/, /method_missing/,
- /instance_variable_(g|s)et/
-
+ /instance_variable_(g|s)et/
+
instance_methods.each do |m|
undef_method m unless @@methods_to_keep.find { |r| r.match m }
end
-
+
def initialize(initial_scope)
@initial_scope = initial_scope
end
-
+
def __process(block)
prepare_scope
instance_eval(&block) if block
tree
rescue
- puts @scope.inspect
raise $!
end
def /(ignore_me) # syntatic sugar for scope resolution
self
end
-
+
def load_tree_file(filename)
File.open filename, 'r' do |f|
eval f.read, binding, __FILE__, __LINE__
end
end
-
+
def shared_branch(branch_name, &block)
instance_variable_set "@#{branch_name}", lambda(&block)
end
-
+
def graft_branch(branch_name)
branch = instance_variable_get("@#{branch_name}")
raise "Attemption to graft branch #{branch_name} which is undefined" unless branch
@@ -50,93 +49,149 @@ def prune(method, value)
twig.respond_to?(method) && twig.send(method) == value
end
end
-
+
def method_missing(*args, &block)
- # if we only have one arg, and no block, then we're trying to build a
- # module scope, i.e. a/b/c/d would resolve to A::B::C::D, so let's start
- # recording the bits...
- if args.length == 1 and block.nil?
- (@bits ||= []) << args[0]
- self # return us coz we respond to / which does nothing but look good!
+ if still_modifying_scope?(args, block)
+ push_to_scope_stack_and_return_self(args)
+ else
+ assign_to_current_scope(*args, &block)
+ end
+ end
+
+ private
+
+ def assign_to_current_scope(*args, &block)
+ case current_scope_type
+ when :instance
+ assign_to_instance_with(*args, &block)
+ when :array
+ assign_to_array_with(*args, &block)
+ end
+ end
+
+ def assign_to_array_with(*args, &block)
+ klass = args.shift
+ if within_a_scope?
+ module_scope = @scope_stack.collect { |bit| classify bit.to_s }.join('::')
+ instance = eval("#{module_scope}::#{classify klass.to_s}").new(*args)
+ @scope_stack = nil
else
- #
- # now we've changed this here, we're assuming any scope that responds
- # to << must be a collection, so we treat it as such - i can't think
- # of any scenarios where this may bunk up, but best to remind myself
- # for later just in case...
- #
- if !current_scope.respond_to?(:<<) # we're working inside an Instance
- accessor = args.shift # grab the accessor name
- if accessor.to_s[-1].chr == '!' # hacky hack instance something
- instance = eval(classify(accessor.to_s[0...-1])).new(*args)
- current_scope.send("#{accessor.to_s[0...-1]}=", instance)
- if block # we got a block, change scope to set accessors
- append_scope_with instance
- instance_eval(&block)
- jump_out_of_scope
- end
- elsif block and args.empty? # we're making an accessor into an array of Instances
- if current_scope.send("#{accessor}").nil?
- current_scope.send("#{accessor}=", [])
- end
- collection = current_scope.send("#{accessor}")
- append_scope_with collection
- instance_eval(&block)
- jump_out_of_scope
- else # it's just a plain old assignment to the accessor
- if args.length == 1
- current_scope.send("#{accessor}=", *args)
- else # it looks like someone is trying to pass an array... so
- #
- # THIS BIT IS FUCKED, take this crap out, it makes the API
- # way too confusing and inconsistent
- #
- current_scope.send("#{accessor}=", args)
- end
- end
- else # scope is an Array, so create an Instance
- klass = args.shift
- # :w
-
- if @bits and @bits.any?
- module_scope = @bits.collect { |bit| classify bit.to_s }.join('::')
- instance = eval("#{module_scope}::#{classify klass.to_s}").new(*args)
- @bits = nil
- else
- instance = eval(classify(klass.to_s)).new(*args)
- end
- current_scope << instance # add this instance to the scoped Array
- if block # we got a block, change scope to set accessors
- append_scope_with instance
- instance_eval(&block)
- jump_out_of_scope
- end
- end
+ instance = eval(classify(klass.to_s)).new(*args)
end
+ current_scope << instance # add this instance to the scoped Array
+ assign_accessors_within_scope(instance, &block) if block
+ end
+
+ def assign_accessors_within_scope(instance, &block)
+ evaluate_block_within_context(instance, &block)
+ end
+
+ def assign_array_of_subvalues_to_accessor(accessor, &block)
+ evaluate_block_within_context(current_accessor(accessor), &block)
+ end
+
+ def evaluate_block_within_context(accessor, &block)
+ append_scope_with accessor
+ instance_eval(&block)
+ jump_out_of_scope
+ end
+
+ def within_a_scope?
+ @scope_stack and @scope_stack.any?
+ end
+
+ def assign_to_instance_with(*args, &block)
+ accessor = args.shift
+ case instance_assignment_behaviour_for(accessor, args, block)
+ when :assign_subvalues_to_instance
+ assign_subvalues_to_instance(accessor, args, &block)
+ when :assign_array_of_subvalues_to_accessor
+ assign_array_of_subvalues_to_accessor(accessor, &block)
+ when :assign_directly_to_accessor
+ current_scope.send("#{accessor}=", *args)
+ when :assign_array_directly_to_accessor
+ current_scope.send("#{accessor}=", args)
+ else
+ raise "unknown assignment behaviour '#{assignment_behaviour_for(accessor)}' for accessor '#{acccessor}'"
+ end
+ end
+
+ def current_accessor(accessor)
+ if current_accessor_undefined?(accessor)
+ set_current_accessor_as_empty_array(accessor)
+ end
+ current_scope.send("#{accessor}")
+ end
+
+ def set_current_accessor_as_empty_array(accessor)
+ current_scope.send("#{accessor}=", [])
+ end
+
+ def current_accessor_undefined?(accessor)
+ current_scope.send("#{accessor}").nil?
+ end
+
+ def assign_subvalues_to_instance(accessor, args, &block)
+ instance = eval(classify(accessor.to_s[0...-1])).new(args)
+ current_scope.send("#{accessor.to_s[0...-1]}=", instance)
+ set_accessors_within_scope(instance, &block) if block
+ end
+
+ def set_accessors_within_scope(instance, &block)
+ append_scope_with instance
+ instance_eval(&block)
+ jump_out_of_scope
+ end
+
+ def instance_assignment_behaviour_for(accessor, args, block)
+ if accessor.to_s[-1].chr == '!' #accessor is an actual instance
+ :assign_subvalues_to_instance
+ elsif block and args.empty? #accessor is to refer to an array
+ :assign_array_of_subvalues_to_accessor
+ elsif args.length == 1
+ :assign_directly_to_accessor
+ else
+ :assign_array_directly_to_accessor
+ end
+ end
+
+
+ def current_scope_type
+ # we're assuming any scope that responds to << must be a collection,
+ current_scope.respond_to?(:<<) ? :array : :instance
+ end
+
+ def push_to_scope_stack_and_return_self(args)
+ (@scope_stack ||= []) << args[0]
+ self
+ end
+
+ def still_modifying_scope?(args, block)
+ # if we only have one arg, and no block, then we're trying to build a
+ # module scope, i.e. a/b/c/d would resolve to A::B::C::D,
+ args.length == 1 and block.nil?
end
-
-private
def prepare_scope
@scope = [@initial_scope]
end
-
+
def append_scope_with(new_scope)
scope.push new_scope
end
-
+
def jump_out_of_scope
scope.pop
end
-
+
def current_scope
scope.last
end
-
+
def tree
scope.first
end
-
+
def scope
@scope
end
@@ -145,5 +200,5 @@ def classify(str)
camels = str.split('_')
camels.collect { |c| c.capitalize }.join
end
-
-end
+
+end
Please sign in to comment.
Something went wrong with that request. Please try again.