archimate-diff 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.projectile +18 -0
- data/.rubocop.yml +13 -0
- data/.travis.yml +4 -0
- data/.yardocs +15 -0
- data/Gemfile +5 -0
- data/Guardfile +27 -0
- data/LICENSE +201 -0
- data/README.md +94 -0
- data/Rakefile +29 -0
- data/archimate-diff.gemspec +51 -0
- data/exe/archidiff +7 -0
- data/exe/archidiff-summary +7 -0
- data/exe/archimerge +7 -0
- data/exe/fmtxml +7 -0
- data/lib/archimate-diff.rb +33 -0
- data/lib/archimate/diff/archimate_array_reference.rb +113 -0
- data/lib/archimate/diff/archimate_identified_node_reference.rb +41 -0
- data/lib/archimate/diff/archimate_node_attribute_reference.rb +70 -0
- data/lib/archimate/diff/archimate_node_reference.rb +80 -0
- data/lib/archimate/diff/change.rb +49 -0
- data/lib/archimate/diff/cli/conflict_resolver.rb +41 -0
- data/lib/archimate/diff/cli/diff.rb +33 -0
- data/lib/archimate/diff/cli/diff_summary.rb +103 -0
- data/lib/archimate/diff/cli/merge.rb +51 -0
- data/lib/archimate/diff/cli/merger.rb +111 -0
- data/lib/archimate/diff/conflict.rb +31 -0
- data/lib/archimate/diff/conflicts.rb +89 -0
- data/lib/archimate/diff/conflicts/base_conflict.rb +53 -0
- data/lib/archimate/diff/conflicts/deleted_items_child_updated_conflict.rb +30 -0
- data/lib/archimate/diff/conflicts/deleted_items_referenced_conflict.rb +63 -0
- data/lib/archimate/diff/conflicts/path_conflict.rb +51 -0
- data/lib/archimate/diff/delete.rb +41 -0
- data/lib/archimate/diff/difference.rb +113 -0
- data/lib/archimate/diff/insert.rb +43 -0
- data/lib/archimate/diff/merge.rb +70 -0
- data/lib/archimate/diff/move.rb +51 -0
- data/lib/archimate/diff/version.rb +6 -0
- metadata +453 -0
| @@ -0,0 +1,51 @@ | |
| 1 | 
            +
            # coding: utf-8
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 3 | 
            +
            lib = File.expand_path('../lib', __FILE__)
         | 
| 4 | 
            +
            $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
         | 
| 5 | 
            +
            require 'archimate-diff'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            Gem::Specification.new do |spec|
         | 
| 8 | 
            +
              spec.name          = "archimate-diff"
         | 
| 9 | 
            +
              spec.version       = Archimate::Diff::VERSION
         | 
| 10 | 
            +
              spec.authors       = ["Mark Morga"]
         | 
| 11 | 
            +
              spec.email         = ["markmorga@gmail.com", "mmorga@rackspace.com"]
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              spec.summary       = "Archimate-Diff Tools"
         | 
| 14 | 
            +
              spec.description   = "A collection of tools to diff and merge ArchiMate files"
         | 
| 15 | 
            +
              spec.homepage      = "http://github.com/mmorga/archimate-diff"
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              spec.files         = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
         | 
| 18 | 
            +
              spec.bindir        = "exe"
         | 
| 19 | 
            +
              spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
         | 
| 20 | 
            +
              spec.require_paths = ["lib"]
         | 
| 21 | 
            +
              spec.metadata["yard.run"] = "yri" # use "yard" to build full HTML docs.
         | 
| 22 | 
            +
              spec.required_ruby_version = '>= 2.3.0'
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              spec.add_runtime_dependency "archimate", "~> 1.1"
         | 
| 25 | 
            +
              spec.add_runtime_dependency "nokogiri", "~> 1.6"
         | 
| 26 | 
            +
              spec.add_runtime_dependency "thor", "~> 0.19"
         | 
| 27 | 
            +
              spec.add_runtime_dependency "highline", "~> 1.7"
         | 
| 28 | 
            +
              spec.add_runtime_dependency "ruby-progressbar", "~>1.8.1"
         | 
| 29 | 
            +
              spec.add_runtime_dependency "parallel", "~> 1.11"
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              spec.add_development_dependency "bundler"
         | 
| 32 | 
            +
              spec.add_development_dependency "rake"
         | 
| 33 | 
            +
              spec.add_development_dependency "minitest"
         | 
| 34 | 
            +
              spec.add_development_dependency "minitest-matchers"
         | 
| 35 | 
            +
              spec.add_development_dependency "minitest-color"
         | 
| 36 | 
            +
              spec.add_development_dependency "minitest-profile"
         | 
| 37 | 
            +
              spec.add_development_dependency "pry"
         | 
| 38 | 
            +
              spec.add_development_dependency "pry-byebug"
         | 
| 39 | 
            +
              spec.add_development_dependency "guard"
         | 
| 40 | 
            +
              spec.add_development_dependency "guard-minitest"
         | 
| 41 | 
            +
              spec.add_development_dependency "guard-bundler"
         | 
| 42 | 
            +
              spec.add_development_dependency "ruby-prof"
         | 
| 43 | 
            +
              spec.add_development_dependency "simplecov"
         | 
| 44 | 
            +
              spec.add_development_dependency "simplecov-json"
         | 
| 45 | 
            +
              spec.add_development_dependency "kramdown"
         | 
| 46 | 
            +
              spec.add_development_dependency "yard"
         | 
| 47 | 
            +
              spec.add_development_dependency "guard-ctags-bundler"
         | 
| 48 | 
            +
              spec.add_development_dependency "faker"
         | 
| 49 | 
            +
              spec.add_development_dependency "rsense"
         | 
| 50 | 
            +
              spec.add_development_dependency "awesome_print"
         | 
| 51 | 
            +
            end
         | 
    
        data/exe/archidiff
    ADDED
    
    
    
        data/exe/archimerge
    ADDED
    
    
    
        data/exe/fmtxml
    ADDED
    
    
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "archimate"
         | 
| 4 | 
            +
            require 'archimate/diff/difference'
         | 
| 5 | 
            +
            require 'archimate/diff/archimate_node_reference'
         | 
| 6 | 
            +
            require 'archimate/diff/archimate_identified_node_reference'
         | 
| 7 | 
            +
            require 'archimate/diff/archimate_array_reference'
         | 
| 8 | 
            +
            require 'archimate/diff/archimate_node_attribute_reference'
         | 
| 9 | 
            +
            require 'archimate/diff/change'
         | 
| 10 | 
            +
            require 'archimate/diff/conflict'
         | 
| 11 | 
            +
            require 'archimate/diff/conflicts'
         | 
| 12 | 
            +
            require 'archimate/diff/delete'
         | 
| 13 | 
            +
            require 'archimate/diff/insert'
         | 
| 14 | 
            +
            require 'archimate/diff/merge'
         | 
| 15 | 
            +
            require 'archimate/diff/move'
         | 
| 16 | 
            +
            require "archimate/diff/version"
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            module Archimate
         | 
| 19 | 
            +
              module Diff
         | 
| 20 | 
            +
                module Cli
         | 
| 21 | 
            +
                  autoload :ConflictResolver, 'archimate/diff/cli/conflict_resolver'
         | 
| 22 | 
            +
                  autoload :Diff, 'archimate/diff/cli/diff'
         | 
| 23 | 
            +
                  autoload :DiffSummary, 'archimate/diff/cli/diff_summary'
         | 
| 24 | 
            +
                  autoload :Merge, 'archimate/diff/cli/merge'
         | 
| 25 | 
            +
                  autoload :Merger, 'archimate/diff/cli/merger'
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              # Computes the set of differences between base and remote models
         | 
| 30 | 
            +
              def self.diff(base, remote)
         | 
| 31 | 
            +
                base.diff(remote)
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| @@ -0,0 +1,113 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Archimate
         | 
| 4 | 
            +
              module Diff
         | 
| 5 | 
            +
                class ArchimateArrayReference < ArchimateNodeReference
         | 
| 6 | 
            +
                  using DataModel::DiffableArray
         | 
| 7 | 
            +
                  using DataModel::DiffablePrimitive
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  attr_reader :array_index
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  def initialize(array, array_index)
         | 
| 12 | 
            +
                    unless array.is_a?(Array)
         | 
| 13 | 
            +
                      raise(
         | 
| 14 | 
            +
                        TypeError,
         | 
| 15 | 
            +
                        "array argument must be an Array, was #{array.class}"
         | 
| 16 | 
            +
                      )
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
                    unless array_index.is_a?(Integer)
         | 
| 19 | 
            +
                      raise(
         | 
| 20 | 
            +
                        TypeError,
         | 
| 21 | 
            +
                        "array_index argument must be a Integer, was #{array_index.class} #{array_index.inspect}"
         | 
| 22 | 
            +
                      )
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
                    unless array_index >= 0 && array_index < array.size
         | 
| 25 | 
            +
                      raise(
         | 
| 26 | 
            +
                        ArgumentError,
         | 
| 27 | 
            +
                        "array_index argument a valid index for array #{array_index.inspect}"
         | 
| 28 | 
            +
                      )
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
                    super(array)
         | 
| 31 | 
            +
                    @array_index = array_index
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  def value
         | 
| 35 | 
            +
                    archimate_node[array_index]
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  def to_s
         | 
| 39 | 
            +
                    value.to_s
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  def lookup_in_model(model)
         | 
| 43 | 
            +
                    result = lookup_parent_in_model(model)
         | 
| 44 | 
            +
                    raise TypeError, "result was #{result.class} expected Array" unless result.is_a?(Array)
         | 
| 45 | 
            +
                    result[array_index]
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  def path(options = {})
         | 
| 49 | 
            +
                    @array_ref_path ||= [
         | 
| 50 | 
            +
                      super,
         | 
| 51 | 
            +
                      case value
         | 
| 52 | 
            +
                      when DataModel::Referenceable
         | 
| 53 | 
            +
                        value.id
         | 
| 54 | 
            +
                      else
         | 
| 55 | 
            +
                        array_index
         | 
| 56 | 
            +
                      end
         | 
| 57 | 
            +
                    ].map(&:to_s).reject(&:empty?).join("/")
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  # For inserts - we can't be sure of what is available (without an expensive sort)
         | 
| 61 | 
            +
                  # So lookup the first previous value that exists in to_model and insert it after that
         | 
| 62 | 
            +
                  # value instead of a fixed index.
         | 
| 63 | 
            +
                  def find_insert_index_in_ary(ary)
         | 
| 64 | 
            +
                    return -1 if array_index.zero?
         | 
| 65 | 
            +
                    my_idx = (array_index - 1).downto(0).find(-1) do |idx|
         | 
| 66 | 
            +
                      ary.smart_include?(archimate_node[idx])
         | 
| 67 | 
            +
                    end
         | 
| 68 | 
            +
                    ary.smart_find(archimate_node[my_idx])
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                  def insert(to_model)
         | 
| 72 | 
            +
                    ary_in_model = lookup_parent_in_model(to_model)
         | 
| 73 | 
            +
                    insert_idx = find_insert_index_in_ary(ary_in_model) + 1
         | 
| 74 | 
            +
                    ary_in_model.insert(insert_idx, value)
         | 
| 75 | 
            +
                  end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  def delete(to_model)
         | 
| 78 | 
            +
                    ary_in_model = lookup_parent_in_model(to_model)
         | 
| 79 | 
            +
                    if ary_in_model.nil?
         | 
| 80 | 
            +
                      $stderr.puts "lookup parent in model failed for path #{path}"
         | 
| 81 | 
            +
                      return nil
         | 
| 82 | 
            +
                    end
         | 
| 83 | 
            +
                    idx = ary_in_model.smart_find(value)
         | 
| 84 | 
            +
                    if idx
         | 
| 85 | 
            +
                      ary_in_model.delete_at(idx)
         | 
| 86 | 
            +
                    else
         | 
| 87 | 
            +
                      $stderr.puts "Couldn't find item #{value.inspect} in path #{path} to delete in to_model"
         | 
| 88 | 
            +
                    end
         | 
| 89 | 
            +
                  end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                  def change(to_model, from_value)
         | 
| 92 | 
            +
                    ary_in_model = lookup_parent_in_model(to_model)
         | 
| 93 | 
            +
                    idx = ary_in_model.smart_find(from_value)
         | 
| 94 | 
            +
                    if idx.nil?
         | 
| 95 | 
            +
                      $stderr.puts "Couldn't find value #{from_value.inspect} in path #{path} to change in to_model, adding to end of list"
         | 
| 96 | 
            +
                      idx = ary_in_model.size
         | 
| 97 | 
            +
                    end
         | 
| 98 | 
            +
                    ary_in_model[idx] = value
         | 
| 99 | 
            +
                  end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                  def move(to_model, _from_ref)
         | 
| 102 | 
            +
                    ary_in_model = lookup_parent_in_model(to_model)
         | 
| 103 | 
            +
                    insert_idx = parent.previous_item_index(ary_in_model, value) + 1
         | 
| 104 | 
            +
                    current_idx = ary_in_model.smart_find(value)
         | 
| 105 | 
            +
                    deleted_value = ary_in_model.delete_at(current_idx)
         | 
| 106 | 
            +
                    ary_in_model.insert(
         | 
| 107 | 
            +
                      insert_idx,
         | 
| 108 | 
            +
                      deleted_value
         | 
| 109 | 
            +
                    )
         | 
| 110 | 
            +
                  end
         | 
| 111 | 
            +
                end
         | 
| 112 | 
            +
              end
         | 
| 113 | 
            +
            end
         | 
| @@ -0,0 +1,41 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Archimate
         | 
| 4 | 
            +
              module Diff
         | 
| 5 | 
            +
                class ArchimateReferenceableReference < ArchimateNodeReference
         | 
| 6 | 
            +
                  using DataModel::DiffableArray
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def initialize(archimate_node)
         | 
| 9 | 
            +
                    unless archimate_node.is_a?(DataModel::Referenceable)
         | 
| 10 | 
            +
                      raise(
         | 
| 11 | 
            +
                        TypeError,
         | 
| 12 | 
            +
                        "archimate_node is a #{archimate_node.class}, Referenceable was expected"
         | 
| 13 | 
            +
                      )
         | 
| 14 | 
            +
                    end
         | 
| 15 | 
            +
                    super
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def lookup_in_model(model)
         | 
| 19 | 
            +
                    raise TypeError unless model.is_a?(DataModel::Model)
         | 
| 20 | 
            +
                    # There can be only one Model so return the model argument if
         | 
| 21 | 
            +
                    # this node reference is a Model. This escape is required in case
         | 
| 22 | 
            +
                    # the Model this is being applied to has a different id than the
         | 
| 23 | 
            +
                    # model of the attribute this reference refers to.
         | 
| 24 | 
            +
                    return model if archimate_node.is_a?(DataModel::Model)
         | 
| 25 | 
            +
                    model.lookup(archimate_node.id)
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  def to_s
         | 
| 29 | 
            +
                    archimate_node.to_s
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  def value
         | 
| 33 | 
            +
                    archimate_node
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  def parent
         | 
| 37 | 
            +
                    archimate_node.parent
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
            end
         | 
| @@ -0,0 +1,70 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Archimate
         | 
| 4 | 
            +
              module Diff
         | 
| 5 | 
            +
                class ArchimateNodeAttributeReference < ArchimateNodeReference
         | 
| 6 | 
            +
                  attr_reader :attribute
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def initialize(archimate_node, attribute)
         | 
| 9 | 
            +
                    unless archimate_node.is_a?(DataModel::ArchimateNode)
         | 
| 10 | 
            +
                      raise(
         | 
| 11 | 
            +
                        TypeError,
         | 
| 12 | 
            +
                        "archimate_node must be an ArchimateNode, was #{archimate_node.class}"
         | 
| 13 | 
            +
                      )
         | 
| 14 | 
            +
                    end
         | 
| 15 | 
            +
                    unless attribute.is_a?(Symbol)
         | 
| 16 | 
            +
                      raise(
         | 
| 17 | 
            +
                        TypeError,
         | 
| 18 | 
            +
                        "Node #{archimate_node.class} attribute must be a sym, was a #{attribute.class} value #{attribute.inspect}"
         | 
| 19 | 
            +
                      )
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
                    unless archimate_node.class.schema.keys.include?(attribute)
         | 
| 22 | 
            +
                      raise(
         | 
| 23 | 
            +
                        ArgumentError,
         | 
| 24 | 
            +
                        "Attribute #{attribute} invalid for class #{archimate_node.class}"
         | 
| 25 | 
            +
                      )
         | 
| 26 | 
            +
                    end
         | 
| 27 | 
            +
                    super(archimate_node)
         | 
| 28 | 
            +
                    @attribute = attribute
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  def ==(other)
         | 
| 32 | 
            +
                    super && attribute == other.attribute
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  def lookup_in_model(model)
         | 
| 36 | 
            +
                    recurse_lookup_in_model(archimate_node, model)[attribute]
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  def to_s
         | 
| 40 | 
            +
                    attribute.to_s
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  def value
         | 
| 44 | 
            +
                    archimate_node[attribute]
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  def path(options = {})
         | 
| 48 | 
            +
                    @node_attribute_ref_path ||= [
         | 
| 49 | 
            +
                      super, @attribute
         | 
| 50 | 
            +
                    ].map(&:to_s).reject(&:empty?).join("/")
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  def insert(to_model)
         | 
| 54 | 
            +
                    lookup_parent_in_model(to_model).set(attribute, value)
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  def delete(to_model)
         | 
| 58 | 
            +
                    lookup_parent_in_model(to_model).delete(attribute)
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  def change(to_model, _from_value)
         | 
| 62 | 
            +
                    lookup_parent_in_model(to_model).set(attribute, value)
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  def move(_to_model)
         | 
| 66 | 
            +
                    raise "Move is not valid for ArchimateNodes"
         | 
| 67 | 
            +
                  end
         | 
| 68 | 
            +
                end
         | 
| 69 | 
            +
              end
         | 
| 70 | 
            +
            end
         | 
| @@ -0,0 +1,80 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Archimate
         | 
| 4 | 
            +
              module Diff
         | 
| 5 | 
            +
                class ArchimateNodeReference
         | 
| 6 | 
            +
                  using DataModel::DiffablePrimitive
         | 
| 7 | 
            +
                  using DataModel::DiffableArray
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  attr_reader :archimate_node
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  # There should be only a few things that are valid here:
         | 
| 12 | 
            +
                  # 1. An archimate node and a attribute name sym
         | 
| 13 | 
            +
                  # 2. An array and index
         | 
| 14 | 
            +
                  # Produces a NodeReference instance for the given parameters
         | 
| 15 | 
            +
                  def self.for_node(node, child_node)
         | 
| 16 | 
            +
                    case node
         | 
| 17 | 
            +
                    when DataModel::ArchimateNode
         | 
| 18 | 
            +
                      ArchimateNodeAttributeReference.new(node, child_node)
         | 
| 19 | 
            +
                    when Array
         | 
| 20 | 
            +
                      ArchimateArrayReference.new(node, child_node)
         | 
| 21 | 
            +
                    else
         | 
| 22 | 
            +
                      raise TypeError, "Node references need to be either an ArchimateNode or an Array"
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def initialize(archimate_node)
         | 
| 27 | 
            +
                    unless archimate_node.is_a?(DataModel::ArchimateNode) || archimate_node.is_a?(Array)
         | 
| 28 | 
            +
                      raise(
         | 
| 29 | 
            +
                        TypeError,
         | 
| 30 | 
            +
                        "archimate_node must be an ArchimateNode or Array, was #{archimate_node.class}"
         | 
| 31 | 
            +
                      )
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
                    raise "new WTF? parent at path #{archimate_node.path} is a #{archimate_node.class} but isn't assigned a model" if archimate_node.in_model.nil? && !archimate_node.is_a?(DataModel::Model)
         | 
| 34 | 
            +
                    @archimate_node = archimate_node
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  def ==(other)
         | 
| 38 | 
            +
                    other.is_a?(self.class) &&
         | 
| 39 | 
            +
                      value == other.value
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  def to_s
         | 
| 43 | 
            +
                    value.to_s
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  def lookup_in_model(model)
         | 
| 47 | 
            +
                    recurse_lookup_in_model(archimate_node, model)
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  def recurse_lookup_in_model(node, model)
         | 
| 51 | 
            +
                    return nil if node.nil?
         | 
| 52 | 
            +
                    raise TypeError, "node argument must be ArchimateNode or Array, was a #{node.class}" unless node.is_a?(Array) || node.is_a?(DataModel::ArchimateNode)
         | 
| 53 | 
            +
                    raise TypeError, "model argument must be a Model, was a #{model.class}" unless model.is_a?(DataModel::Model)
         | 
| 54 | 
            +
                    if node.is_a?(DataModel::Model)
         | 
| 55 | 
            +
                      return model
         | 
| 56 | 
            +
                    elsif node.is_a?(DataModel::Referenceable)
         | 
| 57 | 
            +
                      return model.lookup(node.id)
         | 
| 58 | 
            +
                    else
         | 
| 59 | 
            +
                      node_parent_in_model = recurse_lookup_in_model(node.parent, model)
         | 
| 60 | 
            +
                      node_parent_in_model[node.parent_attribute_name] unless node_parent_in_model.nil?
         | 
| 61 | 
            +
                    end
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                  def lookup_parent_in_model(model)
         | 
| 65 | 
            +
                    raise "WTF? parent at path #{path} is a #{parent.class} but isn't assigned a model" if parent.in_model.nil? && !parent.is_a?(DataModel::Model)
         | 
| 66 | 
            +
                    result = recurse_lookup_in_model(parent, model)
         | 
| 67 | 
            +
                    $stderr.puts "Unable to lookup parent with path #{path}" if result.nil?
         | 
| 68 | 
            +
                    result
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                  def parent
         | 
| 72 | 
            +
                    archimate_node
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  def path(options = {})
         | 
| 76 | 
            +
                    @node_ref_path ||= archimate_node.path(options)
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
              end
         | 
| 80 | 
            +
            end
         | 
| @@ -0,0 +1,49 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
            module Archimate
         | 
| 3 | 
            +
              module Diff
         | 
| 4 | 
            +
                class Change < Difference
         | 
| 5 | 
            +
                  using DataModel::DiffableArray
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  # Create a new Change difference
         | 
| 8 | 
            +
                  #
         | 
| 9 | 
            +
                  # @param target [Archimate::Diff::ArchimateNodeReference] reference to
         | 
| 10 | 
            +
                  #   ArchimateNode that was changed
         | 
| 11 | 
            +
                  # @param changed_from [Archimate::Diff::ArchimateNodeReference] Element
         | 
| 12 | 
            +
                  #   that was changed
         | 
| 13 | 
            +
                  def initialize(target, changed_from)
         | 
| 14 | 
            +
                    super(target, changed_from)
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  def to_s
         | 
| 18 | 
            +
                    # Note - the explicit to_s is required to access the DiffableArray
         | 
| 19 | 
            +
                    #        implementation if the parent is an Array.
         | 
| 20 | 
            +
                    "#{diff_type} #{changed_from.parent&.to_s} #{Color.color(target.to_s, :change)} changed to #{target.value}"
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  def apply(to_model)
         | 
| 24 | 
            +
                    unless to_model.is_a?(DataModel::Model)
         | 
| 25 | 
            +
                      throw(
         | 
| 26 | 
            +
                        TypeError,
         | 
| 27 | 
            +
                        "Expected a Archimate::DataModel::Model, was a #{to_model.class}"
         | 
| 28 | 
            +
                      )
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
                    target.change(to_model, changed_from.value)
         | 
| 31 | 
            +
                    to_model
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  def change?
         | 
| 35 | 
            +
                    true
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  def kind
         | 
| 39 | 
            +
                    "Change"
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  private
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  def diff_type
         | 
| 45 | 
            +
                    Color.color('CHANGE:', :change)
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
            end
         |