elasticsearch-model-extensions 0.2.2 → 0.3.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 +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
|