simple_nested_set 0.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.
@@ -0,0 +1,42 @@
|
|
1
|
+
module SimpleNestedSet
|
2
|
+
module ActMacro
|
3
|
+
def acts_as_nested_set(options = {})
|
4
|
+
return if acts_as_nested_set?
|
5
|
+
|
6
|
+
include SimpleNestedSet::InstanceMethods
|
7
|
+
extend SimpleNestedSet::ClassMethods
|
8
|
+
|
9
|
+
# TODO get callbacks working
|
10
|
+
# define_callbacks :move, :terminator => "result == false"
|
11
|
+
# before_move :init_as_node
|
12
|
+
|
13
|
+
before_validation :init_as_node
|
14
|
+
before_destroy :prune_branch
|
15
|
+
belongs_to :parent, :class_name => self.name
|
16
|
+
has_many :children, :foreign_key => :parent_id, :class_name => self.base_class.name
|
17
|
+
|
18
|
+
default_scope :order => :lft
|
19
|
+
|
20
|
+
klass = options[:class] || self
|
21
|
+
scopes = Array(options[:scope]).map { |s| s.to_s !~ /_id$/ ? :"#{s}_id" : s }
|
22
|
+
|
23
|
+
nested_set_proc = lambda do |*args|
|
24
|
+
args.empty? ? {} : { :conditions => nested_set.conditions(*args) }
|
25
|
+
end
|
26
|
+
|
27
|
+
scope :nested_set, nested_set_proc do
|
28
|
+
define_method(:scope_columns) { scopes }
|
29
|
+
define_method(:klass) { klass }
|
30
|
+
define_method(:conditions) { |record| scopes.inject({}) { |c, name| c.merge(name => record[name]) } }
|
31
|
+
end
|
32
|
+
|
33
|
+
scope :with_levels, lambda {
|
34
|
+
{ :select => "COUNT(id) AS level" } # TODO ... ?
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def acts_as_nested_set?
|
39
|
+
included_modules.include?(SimpleNestedSet::InstanceMethods)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'active_support/core_ext/hash/slice'
|
2
|
+
|
3
|
+
module SimpleNestedSet
|
4
|
+
module ClassMethods
|
5
|
+
NESTED_SET_ATTRIBUTES = [:parent_id, :left_id, :right_id]
|
6
|
+
|
7
|
+
def create(attributes)
|
8
|
+
with_move_by_attributes(attributes) { super }
|
9
|
+
end
|
10
|
+
|
11
|
+
def create!(attributes)
|
12
|
+
with_move_by_attributes(attributes) { super }
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns the single root
|
16
|
+
def root(*args)
|
17
|
+
nested_set(*args).first(:conditions => { :parent_id => nil })
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns roots when multiple roots (or virtual root, which is the same)
|
21
|
+
def roots(*args)
|
22
|
+
nested_set(*args).scoped(:conditions => { :parent_id => nil } )
|
23
|
+
end
|
24
|
+
|
25
|
+
def leaves(*args)
|
26
|
+
nested_set(*args).scoped(:conditions => 'lft = rgt - 1' )
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def with_move_by_attributes(attributes)
|
32
|
+
transaction do
|
33
|
+
nested_set_attributes = extract_nested_set_attributes!(attributes)
|
34
|
+
yield.tap { |record| record.send(:move_by_attributes, nested_set_attributes) }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def extract_nested_set_attributes!(attributes)
|
39
|
+
result = attributes.slice(*NESTED_SET_ATTRIBUTES)
|
40
|
+
attributes.except!(*NESTED_SET_ATTRIBUTES)
|
41
|
+
result
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,354 @@
|
|
1
|
+
require 'active_support/core_ext/hash/keys'
|
2
|
+
|
3
|
+
module SimpleNestedSet
|
4
|
+
module InstanceMethods
|
5
|
+
def update_attributes(attributes)
|
6
|
+
move_by_attributes(attributes)
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def update_attributes!(attributes)
|
11
|
+
move_by_attributes(attributes)
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns true if the node has the same scope as the given node
|
16
|
+
def same_scope?(other)
|
17
|
+
nested_set.scope_columns.all? { |name| self.send(name) == other.send(name) }
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns the level of this object in the tree, root level is 0
|
21
|
+
def level
|
22
|
+
parent_id.nil? ? 0 : ancestors.count
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns true if this is a root node.
|
26
|
+
def root?
|
27
|
+
parent_id.blank?
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns true is this is a child node
|
31
|
+
def child?
|
32
|
+
!root?
|
33
|
+
end
|
34
|
+
|
35
|
+
def leaf?
|
36
|
+
rgt - lft == 1
|
37
|
+
end
|
38
|
+
|
39
|
+
# compare by left column
|
40
|
+
def <=>(other)
|
41
|
+
lft <=> other.lft
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns the root
|
45
|
+
def root
|
46
|
+
root? ? self : ancestors.first
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns the parent
|
50
|
+
def parent
|
51
|
+
nested_set.klass.find(parent_id) unless root?
|
52
|
+
end
|
53
|
+
|
54
|
+
def ancestor_of?(other)
|
55
|
+
lft < other.lft && rgt > other.rgt
|
56
|
+
end
|
57
|
+
|
58
|
+
def self_or_ancestor_of?(other)
|
59
|
+
self == other || ancestor_of?(other)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns an array of all parents
|
63
|
+
def ancestors
|
64
|
+
nested_set.scoped(:conditions => "lft < #{lft} AND rgt > #{rgt}")
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns the array of all parents and self
|
68
|
+
def self_and_ancestors
|
69
|
+
ancestors + [self]
|
70
|
+
end
|
71
|
+
|
72
|
+
def descendent_of?(other)
|
73
|
+
lft > other.lft && rgt < other.rgt
|
74
|
+
end
|
75
|
+
|
76
|
+
def self_or_descendent_of?(other)
|
77
|
+
self == other || descendent_of?(other)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns a set of all of its children and nested children.
|
81
|
+
def descendants
|
82
|
+
rgt - lft == 1 ? [] : nested_set.scoped(:conditions => ['lft > ? AND rgt < ?', lft, rgt])
|
83
|
+
end
|
84
|
+
|
85
|
+
# Returns a set of itself and all of its nested children.
|
86
|
+
def self_and_descendants
|
87
|
+
[self] + descendants
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns the number of descendents
|
91
|
+
def descendents_count
|
92
|
+
rgt > lft ? (rgt - lft - 1) / 2 : 0
|
93
|
+
end
|
94
|
+
|
95
|
+
# # Returns a set of only this entry's immediate children
|
96
|
+
# def children
|
97
|
+
# rgt - lft == 1 ? [] : nested_set.scoped(:conditions => { :parent_id => id })
|
98
|
+
# end
|
99
|
+
|
100
|
+
# Returns a set of only this entry's immediate children including self
|
101
|
+
def self_and_children
|
102
|
+
[self] + children
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns true if the node has any children
|
106
|
+
def children?
|
107
|
+
descendents_count > 0
|
108
|
+
end
|
109
|
+
alias has_children? children?
|
110
|
+
|
111
|
+
# Returns the array of all children of the parent, except self
|
112
|
+
def siblings
|
113
|
+
without_self(self_and_siblings)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns the array of all children of the parent, included self
|
117
|
+
def self_and_siblings
|
118
|
+
nested_set.scoped(:conditions => { :parent_id => parent_id })
|
119
|
+
end
|
120
|
+
|
121
|
+
# Returns the lefthand sibling
|
122
|
+
def previous_sibling
|
123
|
+
nested_set.first :conditions => { :rgt => lft - 1 }
|
124
|
+
end
|
125
|
+
alias left_sibling previous_sibling
|
126
|
+
|
127
|
+
# Returns the righthand sibling
|
128
|
+
def next_sibling
|
129
|
+
nested_set.first :conditions => { :lft => rgt + 1 }
|
130
|
+
end
|
131
|
+
alias right_sibling next_sibling
|
132
|
+
|
133
|
+
# Returns all descendents that are leaves
|
134
|
+
def leaves
|
135
|
+
rgt - lft == 1 ? [] : nested_set.scoped(:conditions => ["lft > ? AND rgt < ? AND lft = rgt - 1", lft, rgt])
|
136
|
+
end
|
137
|
+
|
138
|
+
# Move the node to the child of another node
|
139
|
+
def move_to_child_of(node)
|
140
|
+
node ? move_to(node, :child) : move_to_root
|
141
|
+
end
|
142
|
+
|
143
|
+
def move_to_root
|
144
|
+
move_to(nil, :root)
|
145
|
+
end
|
146
|
+
|
147
|
+
# moves the node to the left of its left sibling if any
|
148
|
+
def move_left
|
149
|
+
move_to_left_of(left_sibling) if left_sibling
|
150
|
+
end
|
151
|
+
|
152
|
+
# moves the node to the right of its right sibling if any
|
153
|
+
def move_right
|
154
|
+
move_to_right_of(right_sibling) if right_sibling
|
155
|
+
end
|
156
|
+
|
157
|
+
# Move the node to the left of another node
|
158
|
+
def move_to_left_of(node)
|
159
|
+
move_to(node, :left)
|
160
|
+
end
|
161
|
+
|
162
|
+
# Move the node to the left of another node
|
163
|
+
def move_to_right_of(node)
|
164
|
+
move_to(node, :right)
|
165
|
+
end
|
166
|
+
|
167
|
+
protected
|
168
|
+
|
169
|
+
def nested_set
|
170
|
+
@nested_set ||= self.class.nested_set(self)
|
171
|
+
end
|
172
|
+
|
173
|
+
def without_self(scope)
|
174
|
+
scope.scoped :conditions => ["#{self.class.table_name}.id <> ?", id]
|
175
|
+
end
|
176
|
+
|
177
|
+
# before validation set lft and rgt to the end of the tree
|
178
|
+
def init_as_node
|
179
|
+
if lft.nil? || rgt.nil?
|
180
|
+
max_right = nested_set.maximum(:rgt) || 0
|
181
|
+
self.lft = max_right + 1
|
182
|
+
self.rgt = max_right + 2
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Prunes a branch off of the tree, shifting all of the elements on the right
|
187
|
+
# back to the left so the counts still work.
|
188
|
+
def prune_branch
|
189
|
+
unless rgt.nil? || lft.nil?
|
190
|
+
diff = rgt - lft + 1
|
191
|
+
self.class.transaction {
|
192
|
+
nested_set.delete_all "lft > #{lft} AND rgt < #{rgt}"
|
193
|
+
nested_set.update_all "lft = (lft - #{diff})", "lft >= #{rgt}"
|
194
|
+
nested_set.update_all "rgt = (rgt - #{diff} )", "rgt >= #{rgt}"
|
195
|
+
}
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# reload left, right, and parent
|
200
|
+
def reload_nested_set
|
201
|
+
reload :select => 'lft, rgt, parent_id'
|
202
|
+
end
|
203
|
+
|
204
|
+
def move_by_attributes(attributes)
|
205
|
+
return unless attributes.detect { |key, value| [:parent_id, :left_id, :right_id].include?(key.to_sym) }
|
206
|
+
|
207
|
+
attributes.symbolize_keys!
|
208
|
+
attributes.each { |key, value| attributes[key] = nil if value == 'null' }
|
209
|
+
|
210
|
+
parent_id = attributes[:parent_id] ? attributes[:parent_id] : self.parent_id
|
211
|
+
parent = parent_id.blank? ? nil : nested_set.klass.find(parent_id)
|
212
|
+
|
213
|
+
# if left_id is given but blank, set right_id to leftmost sibling
|
214
|
+
if attributes.has_key?(:left_id) && attributes[:left_id].blank?
|
215
|
+
attributes.delete(:left_id)
|
216
|
+
siblings = parent ? parent.children : self.class.roots(self)
|
217
|
+
attributes[:right_id] = siblings.first.id if siblings.first
|
218
|
+
end
|
219
|
+
|
220
|
+
# if right_id is given but blank, set left_id to rightmost sibling
|
221
|
+
if attributes.has_key?(:right_id) && attributes[:right_id].blank?
|
222
|
+
attributes.delete(:right_id)
|
223
|
+
siblings = parent ? parent.children : self.class.roots(self)
|
224
|
+
attributes[:left_id] = siblings.last.id if siblings.last
|
225
|
+
end
|
226
|
+
|
227
|
+
parent_id, left_id, right_id = [:parent_id, :left_id, :right_id].map do |key|
|
228
|
+
value = attributes.delete(key)
|
229
|
+
value.blank? ? nil : value.to_i
|
230
|
+
end
|
231
|
+
|
232
|
+
protect_inconsistent_move!(parent_id, left_id, right_id)
|
233
|
+
|
234
|
+
if left_id && left_id != id
|
235
|
+
move_to_right_of(left_id)
|
236
|
+
elsif right_id && right_id != id
|
237
|
+
move_to_left_of(right_id)
|
238
|
+
elsif parent_id != self.parent_id
|
239
|
+
move_to_child_of(parent_id)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def move_to(target, position)
|
244
|
+
# return if _run_before_move_callbacks == false
|
245
|
+
|
246
|
+
transaction do
|
247
|
+
target.reload_nested_set if target.is_a?(nested_set.klass)
|
248
|
+
self.reload_nested_set
|
249
|
+
|
250
|
+
target = nested_set.klass.find(target) if target && !target.is_a?(ActiveRecord::Base)
|
251
|
+
protect_impossible_move!(position, target) if target
|
252
|
+
|
253
|
+
bound = case position
|
254
|
+
when :child
|
255
|
+
target.rgt
|
256
|
+
when :left
|
257
|
+
target.lft
|
258
|
+
when :right
|
259
|
+
target.rgt + 1
|
260
|
+
when :root
|
261
|
+
roots = self.class.roots
|
262
|
+
roots.empty? ? 1 : roots.last.rgt + 1
|
263
|
+
end
|
264
|
+
|
265
|
+
if bound > rgt
|
266
|
+
bound -= 1
|
267
|
+
other_bound = rgt + 1
|
268
|
+
else
|
269
|
+
other_bound = lft - 1
|
270
|
+
end
|
271
|
+
|
272
|
+
# there would be no change
|
273
|
+
return if bound == rgt || bound == lft
|
274
|
+
|
275
|
+
# we have defined the boundaries of two non-overlapping intervals,
|
276
|
+
# so sorting puts both the intervals and their boundaries in order
|
277
|
+
a, b, c, d = [lft, rgt, bound, other_bound].sort
|
278
|
+
|
279
|
+
parent_id = case position
|
280
|
+
when :child; target.id
|
281
|
+
when :root; nil
|
282
|
+
else target.parent_id
|
283
|
+
end
|
284
|
+
|
285
|
+
sql = <<-sql
|
286
|
+
lft = CASE
|
287
|
+
WHEN lft BETWEEN :a AND :b THEN lft + :d - :b
|
288
|
+
WHEN lft BETWEEN :c AND :d THEN lft + :a - :c
|
289
|
+
ELSE lft END,
|
290
|
+
|
291
|
+
rgt = CASE
|
292
|
+
WHEN rgt BETWEEN :a AND :b THEN rgt + :d - :b
|
293
|
+
WHEN rgt BETWEEN :c AND :d THEN rgt + :a - :c
|
294
|
+
ELSE rgt END,
|
295
|
+
|
296
|
+
parent_id = CASE
|
297
|
+
WHEN id = :id THEN :parent_id
|
298
|
+
ELSE parent_id END
|
299
|
+
sql
|
300
|
+
args = { :a => a, :b => b, :c => c, :d => d, :id => id, :parent_id => parent_id }
|
301
|
+
nested_set.klass.update_all [sql, args], nested_set.conditions(self)
|
302
|
+
|
303
|
+
target.reload_nested_set if target
|
304
|
+
reload_nested_set
|
305
|
+
|
306
|
+
# _run_after_move_callbacks
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def protect_impossible_move!(position, target)
|
311
|
+
positions = [:child, :left, :right, :root]
|
312
|
+
impossible_move!("Position must be one of #{positions.inspect} but is #{position.inspect}.") unless positions.include?(position)
|
313
|
+
impossible_move!("A new node can not be moved") if new_record?
|
314
|
+
impossible_move!("A node can't be moved to itself") if self == target
|
315
|
+
impossible_move!("A node can't be moved to a descendant of itself.") if (lft..rgt).include?(target.lft) && (lft..rgt).include?(target.rgt)
|
316
|
+
impossible_move!("A node can't be moved to a different scope") unless same_scope?(target)
|
317
|
+
end
|
318
|
+
|
319
|
+
def protect_inconsistent_move!(parent_id, left_id, right_id)
|
320
|
+
left = self.class.find(left_id) if left_id
|
321
|
+
right = self.class.find(right_id) if right_id
|
322
|
+
|
323
|
+
if left && right && (!left.right_sibling || left.right_sibling.id != right_id)
|
324
|
+
inconsistent_move! <<-msg
|
325
|
+
Both :left_id (#{left_id.inspect}) and :right_id (#{right_id.inspect}) were given but
|
326
|
+
:right_id (#{right_id}) does not refer to the right_sibling (#{left.right_sibling.inspect})
|
327
|
+
of the node referenced by :left_id (#{left.inspect})
|
328
|
+
msg
|
329
|
+
end
|
330
|
+
|
331
|
+
if left && parent_id && left.parent_id != parent_id
|
332
|
+
inconsistent_move! <<-msg
|
333
|
+
Both :left_id (#{left_id.inspect}) and :parent_id (#{parent_id.inspect}) were given but
|
334
|
+
left.parent_id (#{left.parent_id}) does not equal parent_id
|
335
|
+
msg
|
336
|
+
end
|
337
|
+
|
338
|
+
if right && parent_id && right.parent_id != parent_id
|
339
|
+
inconsistent_move! <<-msg
|
340
|
+
Both :right_id (#{right_id.inspect}) and :parent_id (#{parent_id.inspect}) were given but
|
341
|
+
right.parent_id (#{right.parent_id}) does not equal parent_id
|
342
|
+
msg
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
def inconsistent_move!(message)
|
347
|
+
raise InconsistentMove, "Impossible move: #{message.split("\n").map! { |line| line.strip }.join}"
|
348
|
+
end
|
349
|
+
|
350
|
+
def impossible_move!(message)
|
351
|
+
raise ImpossibleMove, "Impossible move: #{message.split("\n").map! { |line| line.strip }.join}"
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module SimpleNestedSet
|
2
|
+
class InconsistentMove < ActiveRecord::ActiveRecordError ; end
|
3
|
+
class ImpossibleMove < ActiveRecord::ActiveRecordError ; end
|
4
|
+
|
5
|
+
autoload :ActMacro, 'simple_nested_set/act_macro'
|
6
|
+
autoload :ClassMethods, 'simple_nested_set/class_methods'
|
7
|
+
autoload :InstanceMethods, 'simple_nested_set/instance_methods'
|
8
|
+
end
|
9
|
+
|
10
|
+
ActiveRecord::Base.send :extend, SimpleNestedSet::ActMacro
|
metadata
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple_nested_set
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Sven Fuchs
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-08-02 00:00:00 +02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: activerecord
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: -1848230024
|
30
|
+
segments:
|
31
|
+
- 3
|
32
|
+
- 0
|
33
|
+
- 0
|
34
|
+
- beta4
|
35
|
+
version: 3.0.0.beta4
|
36
|
+
type: :runtime
|
37
|
+
version_requirements: *id001
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: activesupport
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
hash: 3
|
47
|
+
segments:
|
48
|
+
- 0
|
49
|
+
version: "0"
|
50
|
+
type: :runtime
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: database_cleaner
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
type: :development
|
65
|
+
version_requirements: *id003
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
name: test_declarative
|
68
|
+
prerelease: false
|
69
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
hash: 3
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
type: :development
|
79
|
+
version_requirements: *id004
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: pathname_local
|
82
|
+
prerelease: false
|
83
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
hash: 3
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
version: "0"
|
92
|
+
type: :development
|
93
|
+
version_requirements: *id005
|
94
|
+
description: "[description]"
|
95
|
+
email: svenfuchs@artweb-design.de
|
96
|
+
executables: []
|
97
|
+
|
98
|
+
extensions: []
|
99
|
+
|
100
|
+
extra_rdoc_files: []
|
101
|
+
|
102
|
+
files:
|
103
|
+
- lib/simple_nested_set.rb
|
104
|
+
- lib/simple_nested_set/act_macro.rb
|
105
|
+
- lib/simple_nested_set/class_methods.rb
|
106
|
+
- lib/simple_nested_set/instance_methods.rb
|
107
|
+
- lib/simple_nested_set/version.rb
|
108
|
+
has_rdoc: true
|
109
|
+
homepage: http://github.com/svenfuchs/simple_nested_set
|
110
|
+
licenses: []
|
111
|
+
|
112
|
+
post_install_message:
|
113
|
+
rdoc_options: []
|
114
|
+
|
115
|
+
require_paths:
|
116
|
+
- lib
|
117
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
118
|
+
none: false
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
hash: 3
|
123
|
+
segments:
|
124
|
+
- 0
|
125
|
+
version: "0"
|
126
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
|
+
none: false
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
hash: 3
|
132
|
+
segments:
|
133
|
+
- 0
|
134
|
+
version: "0"
|
135
|
+
requirements: []
|
136
|
+
|
137
|
+
rubyforge_project: "[none]"
|
138
|
+
rubygems_version: 1.3.7
|
139
|
+
signing_key:
|
140
|
+
specification_version: 3
|
141
|
+
summary: "[summary]"
|
142
|
+
test_files: []
|
143
|
+
|