elasticsearch-model-extensions 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -0
- data/Gemfile +2 -1
- data/README.md +5 -0
- data/gemfiles/rails41.gemfile +11 -0
- data/gemfiles/rails41.gemfile.lock +109 -0
- data/lib/elasticsearch/model/extensions/association_path_finding/association_path_finder.rb +19 -0
- data/lib/elasticsearch/model/extensions/association_path_finding/mapping_node.rb +68 -0
- data/lib/elasticsearch/model/extensions/association_path_finding/shortest_path.rb +108 -0
- data/lib/elasticsearch/model/extensions/batch_updating.rb +9 -52
- data/lib/elasticsearch/model/extensions/batch_updating/batch_updater.rb +68 -0
- data/lib/elasticsearch/model/extensions/configuration.rb +33 -5
- data/lib/elasticsearch/model/extensions/dependency_tracking.rb +17 -21
- data/lib/elasticsearch/model/extensions/dependency_tracking/dependency_tracker.rb +52 -0
- data/lib/elasticsearch/model/extensions/mapping_reflection.rb +7 -80
- data/lib/elasticsearch/model/extensions/mapping_reflection/mapping_reflector.rb +107 -0
- data/lib/elasticsearch/model/extensions/outer_document_updating.rb +0 -21
- data/lib/elasticsearch/model/extensions/partial_updating.rb +16 -116
- data/lib/elasticsearch/model/extensions/partial_updating/partial_updater.rb +149 -0
- data/lib/elasticsearch/model/extensions/proxy.rb +0 -0
- data/lib/elasticsearch/model/extensions/update_callback.rb +3 -2
- data/lib/elasticsearch/model/extensions/version.rb +1 -1
- data/spec/batch_updating/batch_updater_spec.rb +50 -0
- data/spec/batch_updating_spec.rb +134 -0
- data/spec/integration_spec.rb +350 -4
- data/spec/partial_updating_spec.rb +36 -6
- data/spec/setup/articles_with_comments.rb +2 -4
- data/spec/setup/articles_with_comments_and_delayed_jobs.rb +2 -4
- data/spec/setup/authors_and_books_with_tags.rb +183 -0
- data/spec/setup/items_and_categories.rb +0 -0
- data/spec/setup/sqlite.rb +1 -1
- data/spec/setup/undefine.rb +6 -6
- data/spec/spec_helper.rb +6 -0
- metadata +20 -4
- data/lib/elasticsearch/model/extensions/mapping_node.rb +0 -65
- data/lib/elasticsearch/model/extensions/shortest_path.rb +0 -106
@@ -44,12 +44,10 @@ class ::Article < ActiveRecord::Base
|
|
44
44
|
include Elasticsearch::Model::Extensions::BatchUpdating
|
45
45
|
include Elasticsearch::Model::Extensions::PartialUpdating
|
46
46
|
|
47
|
-
DEPENDENT_CUSTOM_ATTRIBUTES = {
|
48
|
-
%w| comments | => %w| num_comments |
|
49
|
-
}
|
50
|
-
|
51
47
|
include Elasticsearch::Model::Extensions::DependencyTracking
|
52
48
|
|
49
|
+
tracks_attributes_dependencies %w| comments | => %w| num_comments |
|
50
|
+
|
53
51
|
settings index: {number_of_shards: 1, number_of_replicas: 0} do
|
54
52
|
mapping do
|
55
53
|
indexes :title, type: 'string', analyzer: 'snowball'
|
@@ -0,0 +1,183 @@
|
|
1
|
+
load 'setup/undefine.rb'
|
2
|
+
|
3
|
+
require 'elasticsearch/model/extensions/all'
|
4
|
+
|
5
|
+
ActiveRecord::Schema.define(:version => 1) do
|
6
|
+
create_table :authors do |t|
|
7
|
+
t.string :nickname
|
8
|
+
t.datetime :created_at, :default => 'NOW()'
|
9
|
+
end
|
10
|
+
|
11
|
+
create_table :author_profiles do |t|
|
12
|
+
t.integer :author_id
|
13
|
+
t.string :description
|
14
|
+
t.datetime :created_at, :default => 'NOW()'
|
15
|
+
end
|
16
|
+
|
17
|
+
create_table :books do |t|
|
18
|
+
t.integer :author_id
|
19
|
+
t.string :title
|
20
|
+
t.datetime :created_at, :default => 'NOW()'
|
21
|
+
end
|
22
|
+
|
23
|
+
create_table :tags do |t|
|
24
|
+
t.string :taggable_type
|
25
|
+
t.integer :taggable_id
|
26
|
+
t.string :body
|
27
|
+
t.datetime :created_at, :default => 'NOW()'
|
28
|
+
t.datetime :deleted_at, :null => true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Author < ActiveRecord::Base
|
33
|
+
has_many :books
|
34
|
+
has_one :profile, class_name: 'AuthorProfile'
|
35
|
+
has_many :tags, as: :taggable
|
36
|
+
|
37
|
+
accepts_nested_attributes_for :books
|
38
|
+
accepts_nested_attributes_for :profile
|
39
|
+
accepts_nested_attributes_for :tags
|
40
|
+
|
41
|
+
include Elasticsearch::Model
|
42
|
+
include Elasticsearch::Model::Callbacks
|
43
|
+
include Elasticsearch::Model::Extensions::IndexOperations
|
44
|
+
include Elasticsearch::Model::Extensions::BatchUpdating
|
45
|
+
include Elasticsearch::Model::Extensions::PartialUpdating
|
46
|
+
|
47
|
+
include Elasticsearch::Model::Extensions::DependencyTracking
|
48
|
+
|
49
|
+
tracks_attributes_dependencies (
|
50
|
+
{
|
51
|
+
%w| books | => %w| num_books |,
|
52
|
+
%w| tags | => %w| num_tags |
|
53
|
+
}
|
54
|
+
)
|
55
|
+
|
56
|
+
settings index: {number_of_shards: 1, number_of_replicas: 0} do
|
57
|
+
mapping do
|
58
|
+
indexes :nickname, type: 'string', analyzer: 'snowball', include_in_all: true
|
59
|
+
indexes :created_at, type: 'date'
|
60
|
+
indexes :books, type: 'object' do
|
61
|
+
indexes :title, type: 'string', include_in_all: true
|
62
|
+
end
|
63
|
+
indexes :profile, type: 'object' do
|
64
|
+
indexes :description, type: 'string', include_in_all: true
|
65
|
+
end
|
66
|
+
indexes :tags, type: 'object' do
|
67
|
+
indexes :body, type: 'string', include_in_all: true
|
68
|
+
end
|
69
|
+
indexes :num_books, type: 'long'
|
70
|
+
indexes :num_tags, type: 'long'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def num_books
|
75
|
+
books.count
|
76
|
+
end
|
77
|
+
|
78
|
+
def num_tags
|
79
|
+
tags.count
|
80
|
+
end
|
81
|
+
|
82
|
+
# Required by AuthorProfile's `OuterDocumentUpdating`
|
83
|
+
include Elasticsearch::Model::Extensions::MappingReflection
|
84
|
+
end
|
85
|
+
|
86
|
+
class ::AuthorProfile < ActiveRecord::Base
|
87
|
+
belongs_to :author
|
88
|
+
end
|
89
|
+
|
90
|
+
class ::Book < ActiveRecord::Base
|
91
|
+
belongs_to :author
|
92
|
+
has_many :tags, as: :taggable
|
93
|
+
|
94
|
+
accepts_nested_attributes_for :tags
|
95
|
+
|
96
|
+
include Elasticsearch::Model
|
97
|
+
include Elasticsearch::Model::Callbacks
|
98
|
+
include Elasticsearch::Model::Extensions::IndexOperations
|
99
|
+
include Elasticsearch::Model::Extensions::BatchUpdating
|
100
|
+
include Elasticsearch::Model::Extensions::PartialUpdating
|
101
|
+
include Elasticsearch::Model::Extensions::OuterDocumentUpdating
|
102
|
+
|
103
|
+
include Elasticsearch::Model::Extensions::DependencyTracking
|
104
|
+
|
105
|
+
tracks_attributes_dependencies %w| tags | => %w| num_tags |
|
106
|
+
|
107
|
+
settings index: {number_of_shards: 1, number_of_replicas: 0} do
|
108
|
+
mapping do
|
109
|
+
indexes :title, type: 'string', analyzer: 'snowball', include_in_all: true
|
110
|
+
indexes :created_at, type: 'date'
|
111
|
+
indexes :tags, type: 'object' do
|
112
|
+
indexes :body, type: 'string', include_in_all: true
|
113
|
+
end
|
114
|
+
indexes :num_tags, type: 'long'
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def num_tags
|
119
|
+
tags.count
|
120
|
+
end
|
121
|
+
|
122
|
+
# Required by Review's `OuterDocumentUpdating`
|
123
|
+
include Elasticsearch::Model::Extensions::MappingReflection
|
124
|
+
|
125
|
+
partially_updates_document_of ::Author, records_to_update_documents: -> book { Author.find(book.author_id) } do |t, changed_fields|
|
126
|
+
t.partially_update_document(*changed_fields)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
class ::Tag < ActiveRecord::Base
|
131
|
+
belongs_to :taggable, polymorphic: true
|
132
|
+
|
133
|
+
default_scope { where(arel_table[:deleted_at].eq(nil)) }
|
134
|
+
|
135
|
+
include Elasticsearch::Model
|
136
|
+
include Elasticsearch::Model::Extensions::IndexOperations
|
137
|
+
include Elasticsearch::Model::Extensions::BatchUpdating
|
138
|
+
include Elasticsearch::Model::Extensions::PartialUpdating
|
139
|
+
include Elasticsearch::Model::Extensions::OuterDocumentUpdating
|
140
|
+
|
141
|
+
def assigned_to_author?
|
142
|
+
taggable_type == 'Author'
|
143
|
+
end
|
144
|
+
|
145
|
+
def assigned_to_book?
|
146
|
+
taggable_type == 'Book'
|
147
|
+
end
|
148
|
+
|
149
|
+
partially_updates_document_of ::Book, records_to_update_documents: -> tag { Book.find(tag.taggable_id) } do |t, changed_fields|
|
150
|
+
t.partially_update_document(*changed_fields)
|
151
|
+
end
|
152
|
+
|
153
|
+
partially_updates_document_of ::Author, records_to_update_documents: -> tag { Author.find(tag.taggable_id) } do |t, changed_fields|
|
154
|
+
t.partially_update_document(*changed_fields)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
Book.delete_all
|
159
|
+
Book.__elasticsearch__.create_index! force: true
|
160
|
+
|
161
|
+
Author.delete_all
|
162
|
+
Author.__elasticsearch__.create_index! force: true
|
163
|
+
|
164
|
+
Author.create! nickname: 'Mikoto',
|
165
|
+
books_attributes: [
|
166
|
+
{title: 'Test', tags_attributes: [{body: 'testing'}]}
|
167
|
+
],
|
168
|
+
tags_attributes: [
|
169
|
+
{body: 'testing'}
|
170
|
+
]
|
171
|
+
|
172
|
+
Author.create! nickname: 'Kuroko',
|
173
|
+
books_attributes: [
|
174
|
+
{title: 'Code & Test', tags_attributes: [{body: 'testing'}, {body: 'coding'}]},
|
175
|
+
{title: 'Code', tags_attributes: [{body: 'coding'}]}
|
176
|
+
],
|
177
|
+
tags_attributes: [
|
178
|
+
{body: 'testing'},
|
179
|
+
{body: 'coding'}
|
180
|
+
]
|
181
|
+
|
182
|
+
Book.__elasticsearch__.refresh_index!
|
183
|
+
Author.__elasticsearch__.refresh_index!
|
File without changes
|
data/spec/setup/sqlite.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'active_record'
|
2
2
|
require 'logger'
|
3
3
|
|
4
|
-
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => "
|
4
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => "test.db")
|
5
5
|
logger = ::Logger.new(STDERR)
|
6
6
|
logger.formatter = lambda { |s, d, p, m| "\e[2;36m#{m}\e[0m\n" }
|
7
7
|
ActiveRecord::Base.logger = logger unless ENV['QUIET']
|
data/spec/setup/undefine.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
# Required to clear classes defined in `load 'setup/*.rb'` to make subsequent `load 'setup/*.rb'` to work correctly.
|
2
|
+
%i| Article Comment AuthorProfile Author Tag Book |.each do |class_name|
|
3
|
+
if Object.const_defined?(class_name)
|
4
|
+
Object.send(:remove_const, class_name)
|
5
|
+
STDERR.puts "Undefined #{class_name}"
|
6
|
+
end
|
7
7
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elasticsearch-model-extensions
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yusuke KUOKA
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-12-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -54,9 +54,15 @@ files:
|
|
54
54
|
- README.md
|
55
55
|
- Rakefile
|
56
56
|
- elasticsearch-model-extensions.gemspec
|
57
|
+
- gemfiles/rails41.gemfile
|
58
|
+
- gemfiles/rails41.gemfile.lock
|
57
59
|
- lib/elasticsearch/model/extensions.rb
|
58
60
|
- lib/elasticsearch/model/extensions/all.rb
|
61
|
+
- lib/elasticsearch/model/extensions/association_path_finding/association_path_finder.rb
|
62
|
+
- lib/elasticsearch/model/extensions/association_path_finding/mapping_node.rb
|
63
|
+
- lib/elasticsearch/model/extensions/association_path_finding/shortest_path.rb
|
59
64
|
- lib/elasticsearch/model/extensions/batch_updating.rb
|
65
|
+
- lib/elasticsearch/model/extensions/batch_updating/batch_updater.rb
|
60
66
|
- lib/elasticsearch/model/extensions/callback.rb
|
61
67
|
- lib/elasticsearch/model/extensions/configuration.rb
|
62
68
|
- lib/elasticsearch/model/extensions/delayed_job.rb
|
@@ -65,15 +71,19 @@ files:
|
|
65
71
|
- lib/elasticsearch/model/extensions/delayed_job/index_document_job.rb
|
66
72
|
- lib/elasticsearch/model/extensions/delayed_job/partially_update_document_job.rb
|
67
73
|
- lib/elasticsearch/model/extensions/dependency_tracking.rb
|
74
|
+
- lib/elasticsearch/model/extensions/dependency_tracking/dependency_tracker.rb
|
68
75
|
- lib/elasticsearch/model/extensions/destroy_callback.rb
|
69
76
|
- lib/elasticsearch/model/extensions/index_operations.rb
|
70
|
-
- lib/elasticsearch/model/extensions/mapping_node.rb
|
71
77
|
- lib/elasticsearch/model/extensions/mapping_reflection.rb
|
78
|
+
- lib/elasticsearch/model/extensions/mapping_reflection/mapping_reflector.rb
|
72
79
|
- lib/elasticsearch/model/extensions/outer_document_updating.rb
|
73
80
|
- lib/elasticsearch/model/extensions/partial_updating.rb
|
74
|
-
- lib/elasticsearch/model/extensions/
|
81
|
+
- lib/elasticsearch/model/extensions/partial_updating/partial_updater.rb
|
82
|
+
- lib/elasticsearch/model/extensions/proxy.rb
|
75
83
|
- lib/elasticsearch/model/extensions/update_callback.rb
|
76
84
|
- lib/elasticsearch/model/extensions/version.rb
|
85
|
+
- spec/batch_updating/batch_updater_spec.rb
|
86
|
+
- spec/batch_updating_spec.rb
|
77
87
|
- spec/example/articles_with_comments.rb
|
78
88
|
- spec/integration_spec.rb
|
79
89
|
- spec/outer_document_updating_spec.rb
|
@@ -81,9 +91,11 @@ files:
|
|
81
91
|
- spec/setup/articles.rb
|
82
92
|
- spec/setup/articles_with_comments.rb
|
83
93
|
- spec/setup/articles_with_comments_and_delayed_jobs.rb
|
94
|
+
- spec/setup/authors_and_books_with_tags.rb
|
84
95
|
- spec/setup/elasticsearch/model.rb
|
85
96
|
- spec/setup/elasticsearch/start.rb
|
86
97
|
- spec/setup/elasticsearch/stop.rb
|
98
|
+
- spec/setup/items_and_categories.rb
|
87
99
|
- spec/setup/sqlite.rb
|
88
100
|
- spec/setup/undefine.rb
|
89
101
|
- spec/spec_helper.rb
|
@@ -114,6 +126,8 @@ summary: A set of extensions for elasticsearch-model which aims to ease the burd
|
|
114
126
|
of things like re-indexing, verbose/complex mapping that you may face once you started
|
115
127
|
using elasticsearch seriously.
|
116
128
|
test_files:
|
129
|
+
- spec/batch_updating/batch_updater_spec.rb
|
130
|
+
- spec/batch_updating_spec.rb
|
117
131
|
- spec/example/articles_with_comments.rb
|
118
132
|
- spec/integration_spec.rb
|
119
133
|
- spec/outer_document_updating_spec.rb
|
@@ -121,9 +135,11 @@ test_files:
|
|
121
135
|
- spec/setup/articles.rb
|
122
136
|
- spec/setup/articles_with_comments.rb
|
123
137
|
- spec/setup/articles_with_comments_and_delayed_jobs.rb
|
138
|
+
- spec/setup/authors_and_books_with_tags.rb
|
124
139
|
- spec/setup/elasticsearch/model.rb
|
125
140
|
- spec/setup/elasticsearch/start.rb
|
126
141
|
- spec/setup/elasticsearch/stop.rb
|
142
|
+
- spec/setup/items_and_categories.rb
|
127
143
|
- spec/setup/sqlite.rb
|
128
144
|
- spec/setup/undefine.rb
|
129
145
|
- spec/spec_helper.rb
|
@@ -1,65 +0,0 @@
|
|
1
|
-
require_relative 'shortest_path'
|
2
|
-
|
3
|
-
module Elasticsearch
|
4
|
-
module Model
|
5
|
-
module Extensions
|
6
|
-
class MappingNode < ShortestPath::Node
|
7
|
-
def self.from_class(klass)
|
8
|
-
name = klass.document_type.intern
|
9
|
-
|
10
|
-
new(klass: klass, name: name, mapping: klass.mapping.to_hash[name])
|
11
|
-
end
|
12
|
-
|
13
|
-
def initialize(klass:, name:, mapping:, through_class:nil)
|
14
|
-
@klass = klass
|
15
|
-
@name = name
|
16
|
-
@mapping = mapping
|
17
|
-
@through_class = through_class
|
18
|
-
end
|
19
|
-
|
20
|
-
def name
|
21
|
-
@name
|
22
|
-
end
|
23
|
-
|
24
|
-
def relates_to_class?(klass)
|
25
|
-
@klass == klass || @through_class == klass
|
26
|
-
end
|
27
|
-
|
28
|
-
def klass
|
29
|
-
@klass
|
30
|
-
end
|
31
|
-
|
32
|
-
def through_class
|
33
|
-
@through_class
|
34
|
-
end
|
35
|
-
|
36
|
-
def each(&block)
|
37
|
-
associations = @klass.reflect_on_all_associations
|
38
|
-
|
39
|
-
props = @mapping[:properties]
|
40
|
-
fields = props.keys
|
41
|
-
|
42
|
-
edges = fields.map { |f|
|
43
|
-
a = associations.find { |a| a.name == f }
|
44
|
-
|
45
|
-
if a && a.options[:polymorphic] != true
|
46
|
-
through_class = if a.options[:through]
|
47
|
-
a.options[:through].to_s.classify.constantize
|
48
|
-
end
|
49
|
-
|
50
|
-
dest = MappingNode.new(klass: a.class_name.constantize, name: f.to_s.pluralize.intern, mapping: props[f], through_class: through_class)
|
51
|
-
|
52
|
-
edge_class.new(name: f, destination: dest)
|
53
|
-
end
|
54
|
-
}.reject(&:nil?)
|
55
|
-
|
56
|
-
if block.nil?
|
57
|
-
edges
|
58
|
-
else
|
59
|
-
edges.each(&block)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
@@ -1,106 +0,0 @@
|
|
1
|
-
module Elasticsearch
|
2
|
-
module Model
|
3
|
-
module Extensions
|
4
|
-
class ShortestPath
|
5
|
-
MAX_DEPTH = 5
|
6
|
-
|
7
|
-
class Node
|
8
|
-
include Enumerable
|
9
|
-
|
10
|
-
def each(&block)
|
11
|
-
raise "A required method #{self.class}#each is not implemented."
|
12
|
-
end
|
13
|
-
|
14
|
-
def name
|
15
|
-
raise "A required method #{self.class}#name is not implemented."
|
16
|
-
end
|
17
|
-
|
18
|
-
def each_with_name(&block)
|
19
|
-
iterator = each.lazy.map do |edge|
|
20
|
-
[edge, edge.name]
|
21
|
-
end
|
22
|
-
|
23
|
-
if block.nil?
|
24
|
-
iterator
|
25
|
-
else
|
26
|
-
iterator.each(&block)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def hash
|
31
|
-
name.hash
|
32
|
-
end
|
33
|
-
|
34
|
-
def eql?(other)
|
35
|
-
self.class == other.class && (name.eql? other.name)
|
36
|
-
end
|
37
|
-
|
38
|
-
def edge_class
|
39
|
-
ShortestPath::Edge
|
40
|
-
end
|
41
|
-
|
42
|
-
def breadth_first_search(&block)
|
43
|
-
ShortestPath.breadth_first_search self, &block
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
class Edge
|
48
|
-
def initialize(name:, destination:)
|
49
|
-
@name = name
|
50
|
-
@destination = destination
|
51
|
-
end
|
52
|
-
|
53
|
-
def name
|
54
|
-
@name
|
55
|
-
end
|
56
|
-
|
57
|
-
def destination
|
58
|
-
@destination
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
module ClassMethods
|
63
|
-
def breadth_first_search(node, &block)
|
64
|
-
original_paths = node.each.map { |e| [e] }
|
65
|
-
paths = original_paths
|
66
|
-
|
67
|
-
depth = 0
|
68
|
-
|
69
|
-
loop {
|
70
|
-
a = paths.select { |p|
|
71
|
-
if block.call(p.last)
|
72
|
-
p
|
73
|
-
end
|
74
|
-
}
|
75
|
-
|
76
|
-
return a if a.size != 0
|
77
|
-
raise RuntimeError, 'Maximum depth exceeded while calculating the shortest path' if depth >= Elasticsearch::Model::Extensions::ShortestPath::MAX_DEPTH
|
78
|
-
|
79
|
-
paths = paths.flat_map { |p|
|
80
|
-
p.last.destination.each.map { |e|
|
81
|
-
p + [e]
|
82
|
-
}
|
83
|
-
}
|
84
|
-
|
85
|
-
depth += 1
|
86
|
-
}
|
87
|
-
end
|
88
|
-
|
89
|
-
def depth_first_search(node, &block)
|
90
|
-
node.each.select do |edge|
|
91
|
-
if block.call(edge)
|
92
|
-
[[edge]]
|
93
|
-
else
|
94
|
-
depth_first_search(edge.destination, &block).map do |path|
|
95
|
-
[edge] + path
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
extend ClassMethods
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|