edge 0.2.0 → 0.2.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 +7 -0
- data/CHANGELOG.md +11 -0
- data/README.md +1 -1
- data/lib/edge/forest.rb +69 -66
- data/lib/edge/version.rb +1 -1
- data/spec/forest_spec.rb +10 -0
- metadata +16 -27
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c25599a81a0c7af695a8155fda82079ab4bb21d5
|
4
|
+
data.tar.gz: d870f4eb3cf382ca5e1e828da2a5b1235939a44e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7528d8b4bf34d7bd2f93cead42b34290b3bce459975d885a32e35e6e9ebc4d27440492e7b3a01e66c85ee2a278a5ab20db68dab7d41321cf10724a636a3e5541
|
7
|
+
data.tar.gz: cf043a89a0b47ba43a49270403acef578313cd618b0efc73b79c0f6f0f7e11aee5c0952d1a5705f4d381be081737b894c57fe67f8aa3f4d8ec7bdb2986da6d0e
|
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
@@ -45,7 +45,7 @@ acts_as_forest.
|
|
45
45
|
Location.find_tree usa.id # load a single tree.
|
46
46
|
|
47
47
|
It also provides the with_descendents scope to get all currently selected
|
48
|
-
nodes and all
|
48
|
+
nodes and all their descendents. It can be chained after where scopes, but
|
49
49
|
must not be used after any other type of scope.
|
50
50
|
|
51
51
|
Location.where(name: "Illinois").with_descendents.all # [illinois, chicago]
|
data/lib/edge/forest.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Edge
|
2
2
|
module Forest
|
3
3
|
# acts_as_forest models a tree/multi-tree structure.
|
4
|
-
module
|
4
|
+
module ActsAsForest
|
5
5
|
# options:
|
6
6
|
#
|
7
7
|
# * foreign_key - column name to use for parent foreign_key (default: parent_id)
|
@@ -33,86 +33,89 @@ module Edge
|
|
33
33
|
scope :root, where(forest_foreign_key => nil)
|
34
34
|
|
35
35
|
include Edge::Forest::InstanceMethods
|
36
|
+
extend Edge::Forest::ClassMethods
|
37
|
+
end
|
38
|
+
end
|
36
39
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
records = find_by_sql manager.to_sql
|
51
|
-
|
52
|
-
records_by_id = records.each_with_object({}) { |r, h| h[r.id] = r }
|
53
|
-
|
54
|
-
# Set all children associations to an empty array
|
55
|
-
records.each do |r|
|
56
|
-
children_association = r.association(:children)
|
57
|
-
children_association.target = []
|
58
|
-
end
|
40
|
+
module ClassMethods
|
41
|
+
# Finds entire forest and preloads all associations. It can be used at
|
42
|
+
# the end of an ActiveRecord finder chain.
|
43
|
+
#
|
44
|
+
# Example:
|
45
|
+
# # loads all locations
|
46
|
+
# Location.find_forest
|
47
|
+
#
|
48
|
+
# # loads all nodes with matching names and all there descendants
|
49
|
+
# Category.where(:name => %w{clothing books electronics}).find_forest
|
50
|
+
def find_forest
|
51
|
+
manager = recursive_manager.project(Arel.star)
|
52
|
+
manager.order(forest_order) if forest_order
|
59
53
|
|
60
|
-
|
54
|
+
records = find_by_sql manager.to_sql
|
61
55
|
|
62
|
-
|
63
|
-
parent = records_by_id[r[forest_foreign_key]]
|
64
|
-
if parent
|
65
|
-
r.association(:parent).target = parent
|
66
|
-
parent.association(:children).target.push(r)
|
67
|
-
else
|
68
|
-
top_level_records.push(r)
|
69
|
-
end
|
70
|
-
end
|
56
|
+
records_by_id = records.each_with_object({}) { |r, h| h[r.id] = r }
|
71
57
|
|
72
|
-
|
58
|
+
# Set all children associations to an empty array
|
59
|
+
records.each do |r|
|
60
|
+
children_association = r.association(:children)
|
61
|
+
children_association.target = []
|
73
62
|
end
|
74
63
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
raise ActiveRecord::RecordNotFound unless trees.size == id_or_ids.size
|
83
|
-
trees
|
64
|
+
top_level_records = []
|
65
|
+
|
66
|
+
records.each do |r|
|
67
|
+
parent = records_by_id[r[forest_foreign_key]]
|
68
|
+
if parent
|
69
|
+
r.association(:parent).target = parent
|
70
|
+
parent.association(:children).target.push(r)
|
84
71
|
else
|
85
|
-
|
86
|
-
trees.first
|
72
|
+
top_level_records.push(r)
|
87
73
|
end
|
88
74
|
end
|
89
75
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
76
|
+
top_level_records
|
77
|
+
end
|
78
|
+
|
79
|
+
# Finds an a tree or trees by id.
|
80
|
+
#
|
81
|
+
# If any requested ids are not found it raises
|
82
|
+
# ActiveRecord::RecordNotFound.
|
83
|
+
def find_tree(id_or_ids)
|
84
|
+
trees = where(:id => id_or_ids).find_forest
|
85
|
+
if id_or_ids.kind_of?(Array)
|
86
|
+
raise ActiveRecord::RecordNotFound unless trees.size == id_or_ids.size
|
87
|
+
trees
|
88
|
+
else
|
89
|
+
raise ActiveRecord::RecordNotFound if trees.empty?
|
90
|
+
trees.first
|
96
91
|
end
|
92
|
+
end
|
97
93
|
|
98
|
-
|
99
|
-
|
100
|
-
|
94
|
+
# Returns a new scope that includes previously scoped records and their descendants by subsuming the previous scope into a subquery
|
95
|
+
#
|
96
|
+
# Only where scopes can precede this in a scope chain
|
97
|
+
def with_descendants
|
98
|
+
manager = recursive_manager.project('id')
|
99
|
+
unscoped.where(id: manager)
|
100
|
+
end
|
101
101
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
.project(arel_table.columns)
|
106
|
-
.join(all_nodes)
|
107
|
-
.on(arel_table[forest_foreign_key].eq all_nodes[:id])
|
102
|
+
private
|
103
|
+
def recursive_manager
|
104
|
+
all_nodes = Arel::Table.new(:all_nodes)
|
108
105
|
|
109
|
-
|
106
|
+
original_term = (current_scope || scoped).arel
|
107
|
+
iterated_term = Arel::SelectManager.new Arel::Table.engine
|
108
|
+
iterated_term.from(arel_table)
|
109
|
+
.project(arel_table.columns)
|
110
|
+
.join(all_nodes)
|
111
|
+
.on(arel_table[forest_foreign_key].eq all_nodes[:id])
|
110
112
|
|
111
|
-
|
113
|
+
union = original_term.union(iterated_term)
|
112
114
|
|
113
|
-
|
114
|
-
|
115
|
-
|
115
|
+
as_statement = Arel::Nodes::As.new all_nodes, union
|
116
|
+
|
117
|
+
manager = Arel::SelectManager.new Arel::Table.engine
|
118
|
+
manager.with(:recursive, as_statement).from(all_nodes)
|
116
119
|
end
|
117
120
|
end
|
118
121
|
|
@@ -156,4 +159,4 @@ module Edge
|
|
156
159
|
end
|
157
160
|
end
|
158
161
|
|
159
|
-
ActiveRecord::Base.extend Edge::Forest::
|
162
|
+
ActiveRecord::Base.extend Edge::Forest::ActsAsForest
|
data/lib/edge/version.rb
CHANGED
data/spec/forest_spec.rb
CHANGED
@@ -212,4 +212,14 @@ describe "Edge::Forest" do
|
|
212
212
|
end
|
213
213
|
end
|
214
214
|
end
|
215
|
+
|
216
|
+
describe "self.acts_as_forest" do
|
217
|
+
it 'can be used twice' do
|
218
|
+
Location2 = Class.new(ActiveRecord::Base) do
|
219
|
+
self.table_name = 'locations'
|
220
|
+
acts_as_forest :order => "name"
|
221
|
+
end
|
222
|
+
Location2.find_forest
|
223
|
+
end
|
224
|
+
end
|
215
225
|
end
|
metadata
CHANGED
@@ -1,52 +1,46 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: edge
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
5
|
-
prerelease:
|
4
|
+
version: 0.2.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Jack Christensen
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-03-06 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: activerecord
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - '>='
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: 3.2.0
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - '>='
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: 3.2.0
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: pg
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - '>='
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0'
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - '>='
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '0'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: rspec
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
45
|
- - ~>
|
52
46
|
- !ruby/object:Gem::Version
|
@@ -54,7 +48,6 @@ dependencies:
|
|
54
48
|
type: :development
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
52
|
- - ~>
|
60
53
|
- !ruby/object:Gem::Version
|
@@ -62,33 +55,29 @@ dependencies:
|
|
62
55
|
- !ruby/object:Gem::Dependency
|
63
56
|
name: guard
|
64
57
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
58
|
requirements:
|
67
|
-
- -
|
59
|
+
- - '>='
|
68
60
|
- !ruby/object:Gem::Version
|
69
61
|
version: 0.10.0
|
70
62
|
type: :development
|
71
63
|
prerelease: false
|
72
64
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
65
|
requirements:
|
75
|
-
- -
|
66
|
+
- - '>='
|
76
67
|
- !ruby/object:Gem::Version
|
77
68
|
version: 0.10.0
|
78
69
|
- !ruby/object:Gem::Dependency
|
79
70
|
name: guard-rspec
|
80
71
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
72
|
requirements:
|
83
|
-
- -
|
73
|
+
- - '>='
|
84
74
|
- !ruby/object:Gem::Version
|
85
75
|
version: 0.6.0
|
86
76
|
type: :development
|
87
77
|
prerelease: false
|
88
78
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
79
|
requirements:
|
91
|
-
- -
|
80
|
+
- - '>='
|
92
81
|
- !ruby/object:Gem::Version
|
93
82
|
version: 0.6.0
|
94
83
|
description: Graph functionality for ActiveRecord
|
@@ -100,6 +89,7 @@ extra_rdoc_files: []
|
|
100
89
|
files:
|
101
90
|
- .gitignore
|
102
91
|
- .rspec
|
92
|
+
- CHANGELOG.md
|
103
93
|
- Gemfile
|
104
94
|
- Guardfile
|
105
95
|
- LICENSE
|
@@ -119,27 +109,26 @@ files:
|
|
119
109
|
- spec/spec_helper.rb
|
120
110
|
homepage: https://github.com/JackC/edge
|
121
111
|
licenses: []
|
112
|
+
metadata: {}
|
122
113
|
post_install_message:
|
123
114
|
rdoc_options: []
|
124
115
|
require_paths:
|
125
116
|
- lib
|
126
117
|
required_ruby_version: !ruby/object:Gem::Requirement
|
127
|
-
none: false
|
128
118
|
requirements:
|
129
|
-
- -
|
119
|
+
- - '>='
|
130
120
|
- !ruby/object:Gem::Version
|
131
121
|
version: '0'
|
132
122
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
-
none: false
|
134
123
|
requirements:
|
135
|
-
- -
|
124
|
+
- - '>='
|
136
125
|
- !ruby/object:Gem::Version
|
137
126
|
version: '0'
|
138
127
|
requirements: []
|
139
128
|
rubyforge_project:
|
140
|
-
rubygems_version:
|
129
|
+
rubygems_version: 2.0.0
|
141
130
|
signing_key:
|
142
|
-
specification_version:
|
131
|
+
specification_version: 4
|
143
132
|
summary: Graph functionality for ActiveRecord. Provides tree/forest modeling structure
|
144
133
|
that can load entire trees in a single query.
|
145
134
|
test_files:
|