Class: Deltoid

Inherits:
Object
  • Object
show all
Defined in:
lib/deltoid.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(strings) ⇒ Deltoid

Returns a new instance of Deltoid.



7
8
9
10
# File 'lib/deltoid.rb', line 7

def initialize(strings)
  @strings = strings
  @parsed = @strings.map { |s| self.class.parse(s) }
end

Class Method Details

.all_children_are_text?(node) ⇒ Boolean

(Internal API)

Returns:

  • (Boolean)


145
146
147
# File 'lib/deltoid.rb', line 145

def self.all_children_are_text?(node)
  node.children.all? { |x| x.text? }
end

.attributes(node) ⇒ Object



135
136
137
138
139
140
141
142
# File 'lib/deltoid.rb', line 135

def self.attributes(node)
  map = {}
  node.attributes.values.each do |attribute|
    name, value = attribute.name, attribute.value
    map[name] = value
  end
  map
end

.attributes_match?(nodes) ⇒ Boolean

Do the attributes of the nodes match?

Returns:

  • (Boolean)


122
123
124
125
# File 'lib/deltoid.rb', line 122

def self.attributes_match?(nodes)
  attributes = nodes.map { |node| attributes(node) }.uniq
  attributes.length == 1
end

.content_match?(nodes) ⇒ Boolean

Does the content of the nodes match? (Note: only applies to nodes whose children are all text nodes.)

Returns:

  • (Boolean)


129
130
131
132
133
# File 'lib/deltoid.rb', line 129

def self.content_match?(nodes)
  return true if nodes.any? { |node| !all_children_are_text?(node) }
  content = nodes.map { |node| node.content }.uniq
  content.length == 1
end

.delta_children_nodes(nodes) ⇒ Object

Returns an array of nodes that are different between nodes.



45
46
47
48
# File 'lib/deltoid.rb', line 45

def self.delta_children_nodes(nodes)
  children = nodes.map { |node| node.children.select { |k| k.element? } }
  delta_nodes_group(children)
end

.delta_documents(documents) ⇒ Object

Returns an array of nodes that are different between documents.



30
31
32
33
# File 'lib/deltoid.rb', line 30

def self.delta_documents(documents)
  roots = documents.map { |document| document.root }
  delta_nodes(roots)
end

.delta_nodes(nodes) ⇒ Object

Returns an array of nodes that are different between nodes.

Idea: could this be rewritten as a special case of delta_nodes_group?



38
39
40
41
42
# File 'lib/deltoid.rb', line 38

def self.delta_nodes(nodes)
  deltas = []
  deltas.concat(nodes) unless match?(nodes)
  deltas.concat(delta_children_nodes(nodes))
end

.delta_nodes_group(nodes_group) ⇒ Object



50
51
52
53
54
55
56
57
# File 'lib/deltoid.rb', line 50

def self.delta_nodes_group(nodes_group)
  matches_group = find_matches_group(nodes_group)
  deltas = find_deltas(nodes_group, matches_group)
  matches_group.each do |matches|
    deltas.concat(delta_children_nodes(matches))
  end
  deltas
end

.find_deltas(nodes_group, matches_group) ⇒ Object

Figure out the deltas (differences) based on:

+nodes_group+ (an array of array of nodes)
+matches_group+ (an array of array of nodes)


111
112
113
# File 'lib/deltoid.rb', line 111

def self.find_deltas(nodes_group, matches_group)
  nodes_group.flatten - matches_group.flatten
end

.find_match(the_node, nodes) ⇒ Object

If the_node matches a node in node_group, return the latter node. Otherwise, return nil.

Note: stops after finding the first match.



101
102
103
104
105
106
# File 'lib/deltoid.rb', line 101

def self.find_match(the_node, nodes)
  nodes.each do |node|
    return node if match?([the_node, node])
  end
  nil
end

.find_matches_group(all_nodes_group) ⇒ Object

Parameters:

+all_nodes_group+ is an array of an array of nodes, with the same
length as the number of documents being diffed.

Returns:

an array of array of nodes that match each other


64
65
66
67
68
69
70
71
72
73
# File 'lib/deltoid.rb', line 64

def self.find_matches_group(all_nodes_group)
  first_nodes = all_nodes_group.first
  remaining_nodes_group = all_nodes_group.drop(1)
  matches_group = []
  first_nodes.each do |node|
    matches = find_matches_in(node, remaining_nodes_group)
    matches_group << matches unless matches.empty?
  end
  matches_group
end

.find_matches_in(node, remaining_nodes_group) ⇒ Object

If node can be found in each of remaining_nodes_group, return an array of each matching node. Otherwise, return [].

Parameters:

+nodes_group+ is an array of an array of nodes, with the same length
as the number of documents being diffed.

Returns:

An array containing each matched node; otherwise [].


83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/deltoid.rb', line 83

def self.find_matches_in(node, remaining_nodes_group)
  matches = [node]
  remaining_nodes_group.each do |nodes_group|
    match = find_match(node, nodes_group)
    if match
      matches << match
    else
      matches = []
      break
    end
  end
  matches
end

.friendly_deltas(deltas, root_nodes) ⇒ Object



18
19
20
21
22
23
24
25
26
27
# File 'lib/deltoid.rb', line 18

def self.friendly_deltas(deltas, root_nodes)
  deltas.map do |delta|
    root = delta.ancestors.last
    {
      :index   => root_nodes.find_index(root),
      :content => delta.content,
      :xpath   => delta.path,
    }
  end
end

.match?(nodes) ⇒ Boolean

Do the nodes match?

Returns:

  • (Boolean)


116
117
118
119
# File 'lib/deltoid.rb', line 116

def self.match?(nodes)
  names = nodes.map { |node| node.name }.uniq
  names.length == 1 && attributes_match?(nodes) && content_match?(nodes)
end

.parse(item) ⇒ Object

(Internal API)



150
151
152
# File 'lib/deltoid.rb', line 150

def self.parse(item)
  Nokogiri::HTML::Document.parse(item)
end

Instance Method Details

#deltaObject

Find the differences between the documents.



13
14
15
16
# File 'lib/deltoid.rb', line 13

def delta
  deltas = self.class.delta_documents(@parsed)
  self.class.friendly_deltas(deltas, @parsed)
end