require 'fiona7/builder/widget_builder' require 'fiona7/builder/widget_updater' require 'set' require 'tsort' module Fiona7 module Builder class BatchWidgetWriter class WidgetGraph include TSort def initialize(nodes) @nodes = nodes @dependencies = {} @nodes.each do |node| @dependencies[node] = node.dependencies.map do |id| node = @nodes.find {|n| n.id.to_sym == id.to_sym } end.compact end end def tsort_each_node(&block) @nodes.each(&block) end def tsort_each_child(node, &block) if !@dependencies[node] raise Scrivito::ScrivitoError, "Broken widget dependency graph" end @dependencies[node].each(&block) end end class WidgetNode attr_reader :id, :values, :builder def initialize(id, values, builder) @id, @values, @builder = id, values, builder end def dependencies Set.new.tap do |dependencies| values.each do |_, value| if value.kind_of?(Hash) && (list = value[:list] || value["list"]) dependencies.merge(list.map {|c| c[:widget] || c['widget']}) end end end end def eql?(other) id.eql?(other.id) end def hash id.hash end end def initialize(obj, id_map, widget_pool, path_map) @obj, @id_map, @widget_pool, @path_map = obj, id_map, widget_pool, path_map @pool_changed = false end def write widget_nodes = @widget_pool.map do |widget_id, values| obj_id = @id_map[widget_id] if values.nil? # TODO: this widget can be GC-ed @pool_changed = true @path_map.delete(widget_id) next end if obj_id builder = WidgetUpdater.new(@obj, widget_id, values.merge(_id: obj_id), @path_map) else builder = WidgetBuilder.new(@obj, widget_id, values, @path_map) @pool_changed = true end WidgetNode.new(widget_id, values, builder) end.compact widget_graph = WidgetGraph.new(widget_nodes) widget_graph.tsort.each do |widget_node| widget_node.builder.build @path_map[widget_node.id] = widget_node.builder.path end end def pool_changed? !!@pool_changed end def path_map @path_map end end end end