archimate 2.0.0 → 2.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8b8d5f63a78b5e98d3791d8d58ea7474ad874325
4
- data.tar.gz: 3cfc66538c6cbaf0792e96ca208712deff1ea194
3
+ metadata.gz: 37d66a474e8d614dd2ccbbf746511e3c4b769ef7
4
+ data.tar.gz: 50e1139f2d69beb50da6e702bd0e20d226f615fa
5
5
  SHA512:
6
- metadata.gz: fe459d02990c355cd440e1460ec8bfac1019a6c3984918d4ee5ea53e9cb8709fd16bcfdf3f29ea3de5b7dbfaca01459143ec6de1294a9d402137e59fafcef1f2
7
- data.tar.gz: 1d91a6c7e3cf0f0e0d7aaba0ba1068f0b74c5d08eca30d9e7541e5f15c066b237552eaab001bfa0cc6510459b07d754036bdd18825016213cd55ceca331d10d9
6
+ metadata.gz: e406c34a0b01b3f839f2ff8e9a688e7a5222ed02203bb0db45fdb03a71f6db5a9d8f9b9c575fa661ae3ce12ed8c7448cc340acea05c042097418eab8bffc498b
7
+ data.tar.gz: 64c3e07d73f2b053c790fb37a3d8f32129f35da594a03fc4a7fe4b6cb66ed15e4e27d1ef83fcd4d5b0e66bd4ce56962aa82bc567717ddaef4878d52eac109d84
data/.gitignore CHANGED
@@ -12,6 +12,7 @@
12
12
  /tmp/
13
13
  /vendor/
14
14
  .DS_Store
15
+ *.bak
15
16
  *.tags
16
17
  tags
17
18
  .srclib-cache/
@@ -27,7 +27,6 @@ Gem::Specification.new do |spec|
27
27
  spec.add_runtime_dependency "ruby-progressbar", "~>1.8.1"
28
28
  spec.add_runtime_dependency "parallel", "~> 1.11"
29
29
  spec.add_runtime_dependency "ruby-enum", "~> 0.7.1"
30
- # spec.add_runtime_dependency "diff-lcs", "~> 1.3"
31
30
 
32
31
  spec.add_development_dependency "bundler"
33
32
  spec.add_development_dependency "rake"
@@ -66,12 +66,13 @@ module Archimate
66
66
  autoload :FileFormat, 'archimate/file_format'
67
67
  autoload :MaybeIO, 'archimate/maybe_io'
68
68
  autoload :ProgressIndicator, 'archimate/progress_indicator'
69
+ autoload :DerivedRelations, 'archimate/derived_relations'
69
70
 
70
71
  require "archimate/version"
71
72
  require "archimate/config"
72
73
  require "archimate/logging"
73
74
  require "archimate/color"
74
- require 'archimate/data_model'
75
+ require "archimate/data_model"
75
76
 
76
77
  # Reads the given file and returns the Archimate model
77
78
  #
@@ -52,6 +52,7 @@ module Archimate
52
52
  end
53
53
 
54
54
  # Diagrams that this element is referenced in.
55
+ # @todo this implementation is broken - in_model no longer exists
55
56
  def diagrams
56
57
  @diagrams ||= in_model.diagrams.select do |diagram|
57
58
  diagram.element_ids.include?(id)
@@ -59,6 +60,7 @@ module Archimate
59
60
  end
60
61
 
61
62
  # Relationships that this element is referenced in.
63
+ # @todo this implementation is broken - in_model no longer exists
62
64
  def relationships
63
65
  @relationships ||= in_model.relationships.select do |relationship|
64
66
  relationship.source == id || relationship.target == id
@@ -70,6 +72,7 @@ module Archimate
70
72
  # 2. Child `documentation` (and different `xml:lang` attribute values)
71
73
  # 3. Child `properties`
72
74
  # 4. Any other elements
75
+ # @todo this implementation is broken - in_model no longer exists
73
76
  def merge(element)
74
77
  super
75
78
  element.diagrams.each { |diagram| diagram.replace(element, self) }
@@ -170,6 +170,12 @@ module Archimate
170
170
  # end
171
171
  # end
172
172
 
173
+ def make_unique_id
174
+ unique_id = random_id
175
+ unique_id = random_id while @index_hash.key?(unique_id)
176
+ unique_id
177
+ end
178
+
173
179
  private
174
180
 
175
181
  # Only used by [#find_default_organization]
@@ -261,12 +267,6 @@ module Archimate
261
267
  ref
262
268
  end
263
269
 
264
- def make_unique_id
265
- unique_id = random_id
266
- unique_id = random_id while @index_hash.key?(unique_id)
267
- unique_id
268
- end
269
-
270
270
  def random_id
271
271
  @random ||= Random.new
272
272
  format("%08x", @random.rand(0xffffffff))
@@ -11,6 +11,26 @@ module Archimate
11
11
  # by having a tag name of "relationship" and an attribute of xsi:type="AccessRelationship" where AccessRelationship is
12
12
  # a derived type from RelationshipType.
13
13
  class Relationship
14
+ # @todo: this should be removed once concrete Relationships are used.
15
+ # @deprecated
16
+ WEIGHTS = {
17
+ 'GroupingRelationship' => 0,
18
+ 'JunctionRelationship' => 0,
19
+ 'AssociationRelationship' => 0,
20
+ 'SpecialisationRelationship' => 1,
21
+ 'FlowRelationship' => 2,
22
+ 'TriggeringRelationship' => 3,
23
+ 'InfluenceRelationship' => 4,
24
+ 'AccessRelationship' => 5,
25
+ 'ServingRelationship' => 6,
26
+ 'UsedByRelationship' => 6,
27
+ 'RealizationRelationship' => 7,
28
+ 'RealisationRelationship' => 7,
29
+ 'AssignmentRelationship' => 8,
30
+ 'AggregationRelationship' => 9,
31
+ 'CompositionRelationship' => 10
32
+ }
33
+
14
34
  include Comparison
15
35
 
16
36
  # @!attribute [r] id
@@ -44,9 +64,13 @@ module Archimate
44
64
  # @!attribute [r] access_type
45
65
  # @return [AccessTypeEnum, NilClass]
46
66
  model_attr :access_type
67
+ # @!attribute [r] derived
68
+ # @return [Boolean] this is a derived relation if true
69
+ model_attr :derived
47
70
 
48
71
  def initialize(id:, name: nil, documentation: nil, type: nil,
49
- properties: [], source:, target:, access_type: nil)
72
+ properties: [], source:, target:, access_type: nil,
73
+ derived: false)
50
74
  @id = id
51
75
  @name = name
52
76
  @documentation = documentation
@@ -55,6 +79,7 @@ module Archimate
55
79
  @source = source
56
80
  @target = target
57
81
  @access_type = access_type
82
+ @derived = derived
58
83
  end
59
84
 
60
85
  def replace(entity, with_entity)
@@ -99,6 +124,90 @@ module Archimate
99
124
  super
100
125
  access_type ||= relationship.access_type
101
126
  end
127
+
128
+ def weight
129
+ WEIGHTS.fetch(type, 0)
130
+ end
131
+ end
132
+
133
+ # Relationship Classifications: Structural, Dynamic, Dependency, Other
134
+ # • No relationships are allowed between two relationships
135
+ # • All relationships connected with relationship connectors must be of
136
+ # the same type
137
+ # • A chain of relationships of the same type that connects two elements,
138
+ # and is in turn connected via relationship connectors, is valid only if
139
+ # a direct relationship of that same type between those two elements is
140
+ # valid
141
+ # • A relationship connecting an element with a second relationship can
142
+ # only be an aggregation, composition, or association; aggregation or
143
+ # composition are valid only from a composite element to that second
144
+ # relationship
145
+ #
146
+ # Aggregation, composition, and specialization relationships are always
147
+ # permitted between two elements of the same type, and association is
148
+ # always allowed between any two elements, and between any element and
149
+ # relationship.
150
+
151
+ class Composition < Relationship
152
+ CLASSIFICATION = :structural
153
+ WEIGHT = 10
154
+ end
155
+
156
+ class Aggregation < Relationship
157
+ CLASSIFICATION = :structural
158
+ WEIGHT = 9
159
+ end
160
+
161
+ class Assignment < Relationship
162
+ CLASSIFICATION = :structural
163
+ WEIGHT = 8
164
+ end
165
+
166
+ class Realization < Relationship
167
+ CLASSIFICATION = :structural
168
+ WEIGHT = 7
169
+ end
170
+
171
+ class Serving < Relationship
172
+ CLASSIFICATION = :dependency
173
+ WEIGHT = 6
174
+ end
175
+
176
+ class Access < Relationship
177
+ CLASSIFICATION = :dependency
178
+ WEIGHT = 5
179
+ end
180
+
181
+ class Influence < Relationship
182
+ CLASSIFICATION = :dependency
183
+ WEIGHT = 4
184
+ end
185
+
186
+ class Triggering < Relationship
187
+ CLASSIFICATION = :dynamic
188
+ WEIGHT = 3
189
+ end
190
+
191
+ class Flow < Relationship
192
+ CLASSIFICATION = :dynamic
193
+ WEIGHT = 2
194
+ end
195
+
196
+ class Specialization < Relationship
197
+ CLASSIFICATION = :other
198
+ WEIGHT = 1
199
+ end
200
+
201
+ class Association < Relationship
202
+ CLASSIFICATION = :other
203
+ WEIGHT = 0
204
+ end
205
+
206
+ # Junction is a relationship connector
207
+ # • All relationships connected with relationship connectors must be of the same type
208
+ class Junction < Relationship
209
+ CLASSIFICATION = :other
210
+ WEIGHT = 0
102
211
  end
103
212
  end
104
213
  end
@@ -12,6 +12,7 @@ module Archimate
12
12
  "FlowRelationship" => "flows to",
13
13
  "InfluenceRelationship" => "influenecs",
14
14
  "RealisationRelationship" => "realizes",
15
+ "RealizationRelationship" => "realizes",
15
16
  "SpecialisationRelationship" => "specializes",
16
17
  "TriggeringRelationship" => "triggers",
17
18
  "UsedByRelationship" => "is used by"
@@ -28,6 +29,7 @@ module Archimate
28
29
  define :FlowRelationship, "FlowRelationship"
29
30
  define :InfluenceRelationship, "InfluenceRelationship"
30
31
  define :RealisationRelationship, "RealisationRelationship"
32
+ define :RealizationRelationship, "RealizationRelationship"
31
33
  define :SpecialisationRelationship, "SpecialisationRelationship"
32
34
  define :SpecializationRelationship, "SpecializationRelationship"
33
35
  define :TriggeringRelationship, "TriggeringRelationship"
@@ -0,0 +1,95 @@
1
+ module Archimate
2
+ class DerivedRelations
3
+ PASS_ALL = lambda { |_item| true }
4
+ FAIL_ALL = lambda { |_item| false }
5
+
6
+ def initialize(model)
7
+ @model = model
8
+ end
9
+
10
+ # This returns a set of derived relationships in this model between the
11
+ # elements in the `start_elements` argument, including relationships that
12
+ # pass the `relationship_filter` recursively until either a target_filter
13
+ # is matched and/or the stop_filter is matched.
14
+ #
15
+ # TODO: Rules for derived relations
16
+ #
17
+ # Assumptions:
18
+ #
19
+ # 1. If an existing relation exists for found "derived" relation, it shouldn't
20
+ # be included in the results.
21
+ # 2. Any path of length 1 inherently identifies an instance of #1 so they can
22
+ # be skipped.
23
+ #
24
+ # @param start_elements [Array<Element>] collection of starting nodes
25
+ # @param relationship_filter [Boolean, lambda(Relationship) => Boolean]
26
+ # filter for the kinds of relationships to follow.
27
+ # @param target_filter [lambda(Element) => Boolean] if true, then a derived
28
+ # relationship is added to the results
29
+ # @param stop_filter [lambda(Element) => Boolean] if true, then relationships
30
+ # below this element are not followed
31
+ def derived_relations(start_elements, relationship_filter, target_filter, stop_filter = FAIL_ALL)
32
+ traverse(start_elements, relationship_filter, stop_filter)
33
+ .reject { |path| Array(path).size <= 1 } # See #2 above
34
+ .select { |path| target_filter.call(path.last.target) }
35
+ .map { |path|
36
+ DataModel::Relationship.new(
37
+ id: @model.make_unique_id,
38
+ type: derived_relationship_type(path),
39
+ source: path.first.source,
40
+ target: path.last.target,
41
+ derived: true
42
+ )
43
+ }
44
+ .uniq { |rel| [rel.type, rel.source, rel.target] }
45
+ end
46
+
47
+ def derived_relationship_type(path)
48
+ DataModel::Relationship::WEIGHTS.rassoc(
49
+ path
50
+ .map(&:weight)
51
+ .min
52
+ ).first
53
+ end
54
+
55
+ # traverse returns an Array of paths (Array<Relationship>)
56
+ # between each of the start elements and elements that pass the
57
+ # relation_filter.
58
+ #
59
+ # @param start_elements [Array<Element>] initial elements to start the
60
+ # model traversal
61
+ # @param relation_filter [Proc(Relationship) => Boolean] only relationships
62
+ # that pass the relation_filter will be followed
63
+ # @param stop_filter [Proc(Element) => Boolean] the traversal will not
64
+ # proceed further than relationship targets that the stop_filter
65
+ # returns true for.
66
+ def traverse(start_elements, relation_filter, stop_filter, from_path = [])
67
+ return [] if from_path.size > 100
68
+ start_elements.each_with_object([]) do |el, result|
69
+ immediate_rels = element_relationships(el)
70
+ .select(&relation_filter)
71
+ .reject { |rel| from_path.include?(rel) }
72
+
73
+ result.concat(immediate_rels)
74
+ result.concat(
75
+ *immediate_rels
76
+ .reject { |rel| rel.target.nil? || (stop_filter && stop_filter.call(rel)) }
77
+ .map { |rel|
78
+ traverse([rel.target], relation_filter, stop_filter, from_path + [rel])
79
+ .map { |path| Array(path).unshift(rel) }
80
+ }
81
+ )
82
+ end
83
+ end
84
+
85
+ def element_by_name(name)
86
+ @model.elements.find { |el| el.name.to_s == name.to_s }
87
+ end
88
+
89
+ def element_relationships(el)
90
+ @model
91
+ .relationships
92
+ .select { |rel| rel.source.id == el.id }
93
+ end
94
+ end
95
+ end
@@ -46,22 +46,6 @@ module Archimate
46
46
  class Cypher
47
47
  attr_reader :output_io
48
48
 
49
- WEIGHTS = {
50
- 'GroupingRelationship' => 0,
51
- 'JunctionRelationship' => 0,
52
- 'AssociationRelationship' => 0,
53
- 'SpecialisationRelationship' => 1,
54
- 'FlowRelationship' => 2,
55
- 'TriggeringRelationship' => 3,
56
- 'InfluenceRelationship' => 4,
57
- 'AccessRelationship' => 5,
58
- 'UsedByRelationship' => 6,
59
- 'RealisationRelationship' => 7,
60
- 'AssignmentRelationship' => 8,
61
- 'AggregationRelationship' => 9,
62
- 'CompositionRelationship' => 10
63
- }
64
-
65
49
  def initialize(output_io)
66
50
  @output_io = output_io
67
51
  end
@@ -136,11 +120,6 @@ module Archimate
136
120
  "(s #{props(nodeId: rel.source)})"
137
121
  end
138
122
 
139
- def weight(t)
140
- return 0 unless WEIGHTS.include?(t)
141
- WEIGHTS[t]
142
- end
143
-
144
123
  def add_docs(h, l)
145
124
  t = l.map(&:text).join("\n").strip
146
125
  return h if t.empty?
@@ -153,7 +132,7 @@ module Archimate
153
132
  name: rel.name.to_s,
154
133
  relationshipId: rel.id,
155
134
  accessType: rel.access_type,
156
- weight: weight(rel.type)
135
+ weight: rel.weight
157
136
  }, rel.documentation
158
137
  )
159
138
  "[r:#{rel.type} #{props(h)}]"
@@ -115,6 +115,7 @@ module Archimate
115
115
  "UsedByRelationship" => %w(used_by uses),
116
116
  "ServingRelationship" => %w(serving served_by),
117
117
  "RealisationRelationship" => %w(realizes realized_by),
118
+ "RealizationRelationship" => %w(realizes realized_by),
118
119
  "AssignmentRelationship" => %w(assigned_to assigned_from),
119
120
  "AggregationRelationship" => %w(aggregates aggregated_by),
120
121
  "CompositionRelationship" => %w(composes composed_by),
@@ -32,7 +32,7 @@ module Archimate
32
32
  event(:on_referenceable, connection),
33
33
  event(:on_future, Sax::FutureReference.new(connection, :source, @attrs["source"])),
34
34
  event(:on_future, Sax::FutureReference.new(connection, :target, @attrs["target"])),
35
- event(:on_future, Sax::FutureReference.new(connection, :relationship, @attrs["relationship"]))
35
+ event(:on_future, Sax::FutureReference.new(connection, :relationship, @attrs["relationship"] || @attrs["archimateRelationship"]))
36
36
  ]
37
37
  end
38
38
 
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Archimate
3
- VERSION = "2.0.0"
3
+ VERSION = "2.0.1"
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: archimate
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Morga
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-10-19 00:00:00.000000000 Z
11
+ date: 2017-10-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -445,7 +445,6 @@ files:
445
445
  - lib/archimate/data_model/property.rb
446
446
  - lib/archimate/data_model/property_definition.rb
447
447
  - lib/archimate/data_model/referenceable.rb
448
- - lib/archimate/data_model/referenceable_collection.rb
449
448
  - lib/archimate/data_model/relationship.rb
450
449
  - lib/archimate/data_model/relationship_type.rb
451
450
  - lib/archimate/data_model/schema_info.rb
@@ -453,6 +452,7 @@ files:
453
452
  - lib/archimate/data_model/view_node.rb
454
453
  - lib/archimate/data_model/viewpoint.rb
455
454
  - lib/archimate/data_model/viewpoint_type.rb
455
+ - lib/archimate/derived_relations.rb
456
456
  - lib/archimate/export/csv_export.rb
457
457
  - lib/archimate/export/cypher.rb
458
458
  - lib/archimate/export/graph_ml.rb
@@ -1,201 +0,0 @@
1
- # frozen_string_literal: true
2
- module Archimate
3
- module DataModel
4
- # This class represents a collection of referenceable objects (i.e. have an `id`)
5
- class ReferenceableCollection
6
- attr_writer :parent_attribute_name
7
-
8
- def in_model
9
- @in_model if defined?(@in_model)
10
- end
11
-
12
- def parent
13
- @parent if defined?(@parent)
14
- end
15
-
16
- def id
17
- object_id.to_s
18
- end
19
-
20
- def primitive?
21
- false
22
- end
23
-
24
- def diff(other)
25
- raise TypeError, "Expected other #{other.class} to be of type #{self.class}" unless other.is_a?(self.class)
26
- raise "Well Hell other #{other.path} in_model is nil" if other.in_model.nil?
27
- raise "Well Hell my path `#{path}` in_model is nil" if in_model.nil?
28
-
29
- result = []
30
- remaining_content = Array.new(self) # @todo I want a copy of the array, not a deep clone
31
- other_enum = other.each_with_index
32
-
33
- loop do
34
- if other_enum.peek[0] == remaining_content[0]
35
- other_enum.next
36
- remaining_content.shift
37
- elsif items_are_changed?(other, other_enum, remaining_content)
38
- result.concat(compute_item_changes(other, other_enum, self, remaining_content[0]))
39
- remaining_content.shift
40
- other_enum.next
41
- elsif !remaining_content.empty? && !other.smart_include?(remaining_content[0])
42
- result << Diff::Delete.new(Diff::ArchimateArrayReference.new(self, smart_find(remaining_content[0])))
43
- remaining_content.shift
44
- elsif !smart_include?(other_enum.peek[0])
45
- result << Diff::Insert.new(Diff::ArchimateArrayReference.new(other, other_enum.next[1]))
46
- elsif smart_include?(other_enum.peek[0])
47
- result << Diff::Move.new(
48
- Diff::ArchimateArrayReference.new(other, other_enum.peek[1]),
49
- Diff::ArchimateArrayReference.new(self, smart_find(other_enum.peek[0]))
50
- )
51
- remaining_item_idx = remaining_content.smart_find(other_enum.peek[0])
52
- if remaining_item_idx
53
- result.concat(compute_item_changes(other, other_enum, self, remaining_content[remaining_item_idx]))
54
- remaining_content.delete_at(remaining_item_idx) if remaining_content.smart_include?(other_enum.peek[0])
55
- end
56
- other_enum.next
57
- else
58
- raise "Unhandled diff case for remaining_content: #{remaining_content[0]} and #{other_enum.peek[0]}"
59
- end
60
- end
61
-
62
- result.concat(
63
- remaining_content
64
- .reject { |item| other.include?(item) }
65
- .map do |item|
66
- Diff::Delete.new(Diff::ArchimateArrayReference.new(self, find_index(item)))
67
- end
68
- )
69
- end
70
-
71
- # @todo This may not continue to live here. Only used by testing.
72
- def patch(diffs)
73
- # @todo Beware, order of diffs could break patching at the moment.
74
- Array(diffs).each do |diff|
75
- case diff
76
- when Diff::Delete
77
- delete_at(smart_find(diff.target.value))
78
- when Diff::Insert
79
- insert(diff.target.array_index, diff.target.value)
80
- when Diff::Change
81
- self[smart_find(diff.changed_from.value)] = diff.target.value
82
- when Diff::Move
83
- insert(diff.target.array_index, delete_at(smart_find(diff.target.value)))
84
- else
85
- raise "Unexpected diff type: #{diff.class}"
86
- end
87
- end
88
- self
89
- end
90
-
91
- def items_are_changed?(other, other_enum, remaining)
92
- !remaining.empty? &&
93
- case remaining[0]
94
- when DataModel::Referenceable
95
- remaining[0].id == other_enum.peek[0].id
96
- when String, DataModel::ArchimateNode
97
- !other.include?(remaining[0]) && !include?(other_enum.peek[0])
98
- else
99
- raise "Unhandled type for #{remaining[0].class}"
100
- end
101
- end
102
-
103
- def compute_item_changes(other, other_enum, _myself, my_item)
104
- case my_item
105
- when DataModel::ArchimateNode
106
- my_item.diff(other_enum.peek[0])
107
- else
108
- my_item.diff(other_enum.peek[0], self, other, other_enum.peek[1], find_index(my_item))
109
- end
110
- end
111
-
112
- def in_model=(model)
113
- puts "#{self.inspect} is frozen" if self.frozen?
114
- @in_model = model
115
- each { |item| item.in_model = model }
116
- end
117
-
118
- def parent=(par)
119
- @parent = par
120
- each { |i| i.parent = self }
121
- end
122
-
123
- def parent_attribute_name
124
- @parent_attribute_name if defined?(@parent_attribute_name)
125
- end
126
-
127
- def build_index(hash_index = {})
128
- reduce(hash_index) { |hi, array_item| array_item.build_index(hi) }
129
- end
130
-
131
- def path(options = {})
132
- [
133
- parent&.path(options),
134
- parent_attribute_name
135
- ].compact.reject(&:empty?).join("/")
136
- end
137
-
138
- def clone
139
- map(&:clone)
140
- end
141
-
142
- def dup
143
- map(&:dup)
144
- end
145
-
146
- def to_s
147
- "#{parent}/#{parent_attribute_name}"
148
- end
149
-
150
- def referenced_identified_nodes
151
- reduce([]) do |a, e|
152
- a.concat(e.referenced_identified_nodes)
153
- end
154
- end
155
-
156
- def find_by_id(an_id)
157
- find { |el| el.id == an_id }
158
- end
159
-
160
- def smart_find(val = nil)
161
- case val
162
- when Referenceable
163
- lazy.map(&:id).find_index(val.id)
164
- else
165
- find_index(val)
166
- end
167
- end
168
-
169
- def smart_include?(val)
170
- case val
171
- when Referenceable
172
- lazy.map(&:id).include?(val.id)
173
- else
174
- include?(val)
175
- end
176
- end
177
-
178
- def smart_intersection(ary)
179
- select { |item| ary.smart_include?(item) }
180
- end
181
-
182
- def after(idx)
183
- return [] if idx >= size - 1
184
- self[idx + 1..-1]
185
- end
186
-
187
- # Given a node in self and ary,
188
- # return the idx of first node p in self that exists in both self and ary
189
- # and is previous to node in self
190
- def previous_item_index(ary, node)
191
- return -1 unless ary.smart_include?(node)
192
- initial_idx = smart_find(node)
193
- return -1 if initial_idx.nil?
194
-
195
- (initial_idx - 1).downto(0).find(-> { -1 }) do |idx|
196
- ary.smart_include?(at(idx))
197
- end
198
- end
199
- end
200
- end
201
- end