lib/spontaneous/schema/schema_modification.rb
# encoding: UTF-8
module Spontaneous
module Schema
class SchemaModification
def initialize(missing_from_map, missing_from_schema, grouped=false)
@missing_from_map = missing_from_map
@missing_from_schema = missing_from_schema
@grouped = grouped
end
def select_missing(select_type)
@missing_from_schema.select do |reference|
reference.category == select_type
end
end
def added_classes
@missing_from_map[:type].uniq
end
def removed_classes
select_missing(:type)
end
def added_fields
@missing_from_map[:field].uniq
end
def removed_fields
select_missing(:field)
end
def added_boxes
@missing_from_map[:box].uniq
end
def removed_boxes
select_missing(:box)
end
def added_styles
@missing_from_map[:style].uniq
end
def removed_styles
select_missing(:style)
end
def added_layouts
@missing_from_map[:layout].uniq
end
def removed_layouts
select_missing(:layout)
end
def resolvable?
if @grouped
simple_change?
else
all_independent_changes?
end
end
def simple_change?
only_removed_fields? or only_added_items?
end
def only_added_items?
if removed_items.empty?
!added_items.empty?
else
false
end
end
def only_removed_fields?
if removed_items.empty?
false
else
added_items.empty? && removed_items.all? { |uid| uid.category == :field }
end
end
def only_removed_items?
if removed_items.empty?
false
else
added_items.empty?
end
end
def all_independent_changes?
!changes_grouped_by_owner.find { |change| !change.resolvable? }
end
def changes_grouped_by_owner
@changes_grouped ||= create_changes_grouped_by_owner
end
def create_changes_grouped_by_owner
# gulp
added = Hash.new { |hash, key|
hash[key] = Hash.new { | hash, key | hash[key] = [] }
}
removed = Hash.new { |hash, key| hash[key] = [] }
# group changed items by category + UIDs because in the case of removals
# the owner might have gone (e.g. in the case of a box with
# a field that has been removed, the field would show as removed
# and reference the box as owner but that box is no longer present)
removed_items.each do |removal|
removed[change_group_key(removal.category, removal.owner_sid)] << removal
end
@missing_from_map.each do |category, additions|
additions.each do |addition|
added[change_group_key(category, addition.owner_sid)][category] << addition
end
end
owners = added.keys | removed.keys
grouped = []
owners.each do |owner|
grouped << SchemaModification.new(added[owner], removed[owner], true)
end
grouped
end
def change_group_key(category, uid)
[category, uid]
end
def resolve!
if @grouped
resolve_simple
else
changes_grouped_by_owner.each { |change| change.resolve! }
end
end
def resolve_simple
if only_added_items?
added_items.each do |obj|
logger.warn("Adding #{obj} to Schema")
Spontaneous.schema.generate_schema_for(obj)
end
end
if only_removed_items?
removed_items.each do |uid|
uid.destroy
end
end
end
def added_items
@missing_from_map.map { |cat, obj| obj }.flatten
end
def removed_items
@missing_from_schema
end
def owners
if @grouped
added = added_items.map { | obj | obj.schema_owner }
removed = removed_items.map { | uid | uid.schema_owner }
owners = (added | removed).uniq
raise "Invalid grouping of schema modifications" unless owners.length == 1
[owners.first]
else
changes_grouped_by_owner.map { |change| change.owners }
end
end
def error_messages
if @grouped
if !simple_change?
"Unable to resolve changes to #{owners.join(", ")}"
else
nil
end
else
changes_grouped_by_owner.map { |change| change.error_messages }.compact
end
end
class Solution < Array
attr_reader :source
def initialize(source, destinations)
super()
@source, @destinations = source, destinations
self << Action.new(:delete, source)
@destinations.each do |dest|
self << Action.new(:rename, source, dest)
end
end
def description
"Unable to resolve #{source.category} '#{source.name}' of #{source.owner}"
end
end
class Action
attr_reader :action, :source, :dest
def initialize(action, source, dest = nil)
@action, @source, @dest = action, source, dest
end
def description
case action
when :delete
"Delete #{category} '#{source.name}'"
when :rename
"Rename #{category} '#{source.name}' to '#{dest.name}'"
end
end
def category
@source.category
end
def export
hash = [@action.to_s, category, source.name]
hash << dest.name if dest
hash
end
def path
path = ["#{action}?uid=#{source.to_s}"]
path << "ref=#{::Rack::Utils.escape(dest.schema_name)}" if action == :rename
path.join("&")
end
end
def actions
if @grouped
if simple_change?
nil
else
Solution.new(removed_items.first, added_items)
end
else
change = changes_grouped_by_owner.find { |c| !c.simple_change? }
change.actions
end
end
def export
actions.map { |action| action.export }
end
def serialise_http(user)
Spontaneous.serialise_http(export)
end
end
end
end