require 'geoff' require 'geoff/node_dsl' require 'geoff/container' require 'geoff/dsl_exception_handling' require 'active_support/core_ext/string' class ChildrenDsl include DslExceptionHandling attr_reader :node_dsls attr_writer :parent_node_dsl def initialize options, &block @parent_node_dsl = options[:parent_node_dsl] @parent_rel_type = options[:type] @target = options[:target] @outer_geoff = options[:geoff] @direction = options[:direction] @write_mode = false @node_dsls = {} @container = options[:container] || Container.new eval_with_exceptions(&block) end # Node dsls of children dsl defined inside other children dsl # are merged into outer children dsl. def children &block options = { parent_node_dsl: @parent_node_dsl, parent_rel_type: @type, target: @target, outer_geoff: @geoff, container: @container } children_dsl = ChildrenDsl.new(options, &block) if block_given? @node_dsls.merge children_dsl.node_dsls children_dsl end def to_geoff "#{@outer_geoff}\n#{geoff_lines.join("\n")}".strip end def geoff_lines lines = [] @node_dsls.each do |node_dsl, properties| lines += node_dsl.geoff_lines lines << rel_geoff(node_dsl, properties) unless top_level_node? end lines end def rel_geoff node_dsl, properties directions_ascii = ["<-", "-", "->"] direction_array = @direction == :incoming ? directions_ascii.first(2) : directions_ascii.last(2) properties = properties.dup type = properties.delete :type properties.delete(:clone) "(#{parent_node_name})#{direction_array.first}[:#{type}]#{direction_array.last}(#{node_dsl.node_name})".tap do |r| r << " #{properties.to_json}" if properties.any? end end def b @container.set_recipient_of_node_dsl self @container end def add_node_dsl node_dsl, rel_properties rel_properties ||= {} rel_properties[:type] ||= @parent_rel_type @node_dsls[node_dsl] = rel_properties end def clone c = self.class.new( parent_node_dsl: @parent_node_dsl, parent_rel_type: @type, target: @target, outer_geoff: @geoff, container: @container ) {} node_dsls = {} @node_dsls.each do |node_dsl, rel_properties| if rel_properties[:clone] == false node_dsls[node_dsl] = rel_properties else node_dsls[node_dsl.clone] = rel_properties.dup end end # sort it out, instance_variable_set is probably not the best way to clone object c.instance_variable_set('@node_dsls', node_dsls) c end private # e.g. #company "ACME" do # children "works_at" do # employee 'Bob' # employee 'Lenny' # employee 'Tom' # end #end # def method_missing m, *args, &blk return super unless @write_mode relationship, options = parse_method_missing m, *args options.merge!(target: @target) NodeDsl.new(options, &blk).tap do |n| @node_dsls[n] = relationship end end def parse_method_missing m, *args node_name = args.first relationship = args[1] || {} relationship[:type] ||= @parent_rel_type if type_missing? relationship raise ArgumentError, "Missing relationship type for node #{node_name}" end return [ relationship, { node_name: node_name, klass_name: m.to_s.camelize, rel_type: relationship[:type], container: @container } ] end def type_missing? r !top_level_node? and !r[:type] end def top_level_node? @parent_node_dsl.nil? end def parent_node_name top_level_node? ? 'ROOT' : @parent_node_dsl.node_name end end