og 0.16.0 → 0.17.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.
- data/CHANGELOG +485 -0
- data/README +35 -12
- data/Rakefile +4 -7
- data/benchmark/bench.rb +1 -1
- data/doc/AUTHORS +3 -3
- data/doc/RELEASES +153 -2
- data/doc/config.txt +0 -7
- data/doc/tutorial.txt +7 -0
- data/examples/README +5 -0
- data/examples/mysql_to_psql.rb +25 -50
- data/examples/run.rb +62 -77
- data/install.rb +1 -1
- data/lib/og.rb +45 -106
- data/lib/og/collection.rb +156 -0
- data/lib/og/entity.rb +131 -0
- data/lib/og/errors.rb +10 -15
- data/lib/og/manager.rb +115 -0
- data/lib/og/{mixins → mixin}/hierarchical.rb +43 -37
- data/lib/og/{mixins → mixin}/orderable.rb +35 -35
- data/lib/og/{mixins → mixin}/timestamped.rb +0 -6
- data/lib/og/{mixins → mixin}/tree.rb +0 -4
- data/lib/og/relation.rb +178 -0
- data/lib/og/relation/belongs_to.rb +14 -0
- data/lib/og/relation/has_many.rb +62 -0
- data/lib/og/relation/has_one.rb +17 -0
- data/lib/og/relation/joins_many.rb +69 -0
- data/lib/og/relation/many_to_many.rb +17 -0
- data/lib/og/relation/refers_to.rb +31 -0
- data/lib/og/store.rb +223 -0
- data/lib/og/store/filesys.rb +113 -0
- data/lib/og/store/madeleine.rb +4 -0
- data/lib/og/store/memory.rb +291 -0
- data/lib/og/store/mysql.rb +283 -0
- data/lib/og/store/psql.rb +238 -0
- data/lib/og/store/sql.rb +599 -0
- data/lib/og/store/sqlite.rb +190 -0
- data/lib/og/store/sqlserver.rb +262 -0
- data/lib/og/types.rb +19 -0
- data/lib/og/validation.rb +0 -4
- data/test/og/{mixins → mixin}/tc_hierarchical.rb +21 -23
- data/test/og/{mixins → mixin}/tc_orderable.rb +15 -14
- data/test/og/mixin/tc_timestamped.rb +38 -0
- data/test/og/store/tc_filesys.rb +71 -0
- data/test/og/tc_relation.rb +36 -0
- data/test/og/tc_store.rb +290 -0
- data/test/og/tc_types.rb +21 -0
- metadata +54 -40
- data/examples/mock_example.rb +0 -50
- data/lib/og/adapters/base.rb +0 -706
- data/lib/og/adapters/filesys.rb +0 -117
- data/lib/og/adapters/mysql.rb +0 -350
- data/lib/og/adapters/oracle.rb +0 -368
- data/lib/og/adapters/psql.rb +0 -272
- data/lib/og/adapters/sqlite.rb +0 -265
- data/lib/og/adapters/sqlserver.rb +0 -356
- data/lib/og/database.rb +0 -290
- data/lib/og/enchant.rb +0 -149
- data/lib/og/meta.rb +0 -407
- data/lib/og/testing/mock.rb +0 -165
- data/lib/og/typemacros.rb +0 -24
- data/test/og/adapters/tc_filesys.rb +0 -83
- data/test/og/adapters/tc_sqlite.rb +0 -86
- data/test/og/adapters/tc_sqlserver.rb +0 -96
- data/test/og/tc_automanage.rb +0 -46
- data/test/og/tc_lifecycle.rb +0 -105
- data/test/og/tc_many_to_many.rb +0 -61
- data/test/og/tc_meta.rb +0 -55
- data/test/og/tc_validation.rb +0 -89
- data/test/tc_og.rb +0 -364
@@ -0,0 +1,156 @@
|
|
1
|
+
module Og
|
2
|
+
|
3
|
+
# An 'active' collection that reflects a relation.
|
4
|
+
# A collection stores entitities that participate in
|
5
|
+
# a relation.
|
6
|
+
|
7
|
+
class Collection
|
8
|
+
|
9
|
+
# The owner of this collection.
|
10
|
+
|
11
|
+
attr_accessor :owner
|
12
|
+
|
13
|
+
# The members of this collection. Keeps the objects
|
14
|
+
# tha belong to this collection.
|
15
|
+
|
16
|
+
attr_accessor :members
|
17
|
+
|
18
|
+
# A method used to add insert objects in the collection.
|
19
|
+
|
20
|
+
attr_accessor :insert_proc
|
21
|
+
|
22
|
+
# A method used to find the objects that belong to the
|
23
|
+
# collection.
|
24
|
+
|
25
|
+
attr_accessor :find_proc
|
26
|
+
|
27
|
+
# The default find options.
|
28
|
+
|
29
|
+
attr_accessor :find_options
|
30
|
+
|
31
|
+
# Is the collection in build mode?
|
32
|
+
|
33
|
+
attr_accessor :building
|
34
|
+
|
35
|
+
# Is the collection loaded?
|
36
|
+
|
37
|
+
attr_accessor :loaded
|
38
|
+
|
39
|
+
# Initialize the collection.
|
40
|
+
|
41
|
+
def initialize(owner = nil, insert_proc = nil, find_proc = nil, find_options = {})
|
42
|
+
@owner = owner
|
43
|
+
@insert_proc = insert_proc
|
44
|
+
@find_proc = find_proc
|
45
|
+
@find_options = find_options
|
46
|
+
@members = []
|
47
|
+
@loaded = false
|
48
|
+
@building = false
|
49
|
+
end
|
50
|
+
|
51
|
+
# Load the members of the collection.
|
52
|
+
|
53
|
+
def load_members
|
54
|
+
unless @loaded
|
55
|
+
@members = @owner.send(@find_proc, @find_options)
|
56
|
+
@loaded = true
|
57
|
+
end
|
58
|
+
@members
|
59
|
+
end
|
60
|
+
|
61
|
+
# Reload the collection.
|
62
|
+
|
63
|
+
def reload(options)
|
64
|
+
@find_options = options
|
65
|
+
@members = @owner.send(@find_proc, @find_options)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Convert the collection to an array.
|
69
|
+
|
70
|
+
def to_ary
|
71
|
+
load_members
|
72
|
+
@members
|
73
|
+
end
|
74
|
+
|
75
|
+
# Defined to avoid the method missing overhead.
|
76
|
+
|
77
|
+
def each(&block)
|
78
|
+
load_members
|
79
|
+
@members.each(&block)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Defined to avoid the method missing overhead.
|
83
|
+
|
84
|
+
def [](idx)
|
85
|
+
load_members
|
86
|
+
@members[idx]
|
87
|
+
end
|
88
|
+
|
89
|
+
# Add a new member to the collection.
|
90
|
+
|
91
|
+
def push(obj)
|
92
|
+
@members.push(obj)
|
93
|
+
unless @building or owner.unsaved?
|
94
|
+
@owner.send(@insert_proc, obj)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
alias_method :<<, :push
|
98
|
+
alias_method :add, :push
|
99
|
+
|
100
|
+
# Delete a member from the collection.
|
101
|
+
|
102
|
+
def delete(*objects)
|
103
|
+
objects = objects.flatten
|
104
|
+
|
105
|
+
objects.reject! { |obj| @members.delete(obj) if obj.unsaved? }
|
106
|
+
return if objects.empty?
|
107
|
+
|
108
|
+
@owner.transaction do
|
109
|
+
objects.each do |obj|
|
110
|
+
obj.delete
|
111
|
+
@members.delete(obj)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Delete all members of the collection.
|
117
|
+
|
118
|
+
def clear
|
119
|
+
@owner.transaction do
|
120
|
+
@members.each { |obj| obj.delete }
|
121
|
+
end
|
122
|
+
@members.clear
|
123
|
+
end
|
124
|
+
|
125
|
+
# Redirect all other methods to the members array.
|
126
|
+
|
127
|
+
def method_missing(symbol, *args, &block)
|
128
|
+
load_members
|
129
|
+
@members.send(symbol, *args, &block)
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def check_type(obj)
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
__END__
|
142
|
+
|
143
|
+
=== Examples
|
144
|
+
|
145
|
+
article.comments.to_ary
|
146
|
+
|
147
|
+
for article in article.comments
|
148
|
+
end
|
149
|
+
|
150
|
+
blog = Blog.new
|
151
|
+
blog.categories << Category.new
|
152
|
+
blog.categories << Category.new
|
153
|
+
blog.enties << Entry.new
|
154
|
+
blog.save
|
155
|
+
|
156
|
+
blog.categories.unlink(1)
|
data/lib/og/entity.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'og/relation'
|
2
|
+
|
3
|
+
module Og
|
4
|
+
|
5
|
+
# Include this module to classes to make them managable by Og.
|
6
|
+
|
7
|
+
module EntityMixin
|
8
|
+
|
9
|
+
def self.append_features(base)
|
10
|
+
super
|
11
|
+
|
12
|
+
base.extend(ClassMethods)
|
13
|
+
|
14
|
+
base.module_eval <<-end_eval, __FILE__, __LINE__
|
15
|
+
def save
|
16
|
+
self.class.ogmanager.store.save(self)
|
17
|
+
return self
|
18
|
+
end
|
19
|
+
alias_method :save!, :save
|
20
|
+
|
21
|
+
def insert
|
22
|
+
self.class.ogmanager.store.insert(self)
|
23
|
+
return self
|
24
|
+
end
|
25
|
+
|
26
|
+
def update(*properties)
|
27
|
+
properties = nil if properties.empty?
|
28
|
+
self.class.ogmanager.store.update(self, properties)
|
29
|
+
end
|
30
|
+
|
31
|
+
def update_properties(set)
|
32
|
+
self.class.ogmanager.store.update_properties(self, set)
|
33
|
+
end
|
34
|
+
|
35
|
+
def reload
|
36
|
+
self.class.ogmanager.store.reload(self, self.pk)
|
37
|
+
end
|
38
|
+
alias_method :reload!, :reload
|
39
|
+
|
40
|
+
def delete
|
41
|
+
self.class.ogmanager.store.delete(self)
|
42
|
+
end
|
43
|
+
alias_method :delete!, :delete
|
44
|
+
|
45
|
+
def transaction(&block)
|
46
|
+
self.class.ogmanager.store.transaction(&block)
|
47
|
+
end
|
48
|
+
end_eval
|
49
|
+
|
50
|
+
base.send(:include, RelationMacros)
|
51
|
+
end
|
52
|
+
|
53
|
+
module ClassMethods
|
54
|
+
def create(*args)
|
55
|
+
obj = self.new(*args)
|
56
|
+
yield(obj) if block_given?
|
57
|
+
ogmanager.store.save(obj)
|
58
|
+
return obj
|
59
|
+
end
|
60
|
+
|
61
|
+
def load(pk)
|
62
|
+
ogmanager.store.load(pk, self)
|
63
|
+
end
|
64
|
+
alias_method :[], :load
|
65
|
+
|
66
|
+
def update_properties(set, options = nil)
|
67
|
+
ogmanager.store.update_properties(self, set, options)
|
68
|
+
end
|
69
|
+
alias_method :pupdate, :update_properties
|
70
|
+
alias_method :update_property, :update_properties
|
71
|
+
alias_method :batch_update, :update_properties
|
72
|
+
|
73
|
+
def find(options = {})
|
74
|
+
options[:class] = self
|
75
|
+
ogmanager.store.find(options)
|
76
|
+
end
|
77
|
+
alias_method :all, :find
|
78
|
+
|
79
|
+
def find_one(options = {})
|
80
|
+
options[:class] = self
|
81
|
+
ogmanager.store.find_one(options)
|
82
|
+
end
|
83
|
+
alias_method :one, :find_one
|
84
|
+
alias_method :first, :find_one
|
85
|
+
|
86
|
+
def count(options = {})
|
87
|
+
options[:class] = self
|
88
|
+
ogmanager.store.count(options)
|
89
|
+
end
|
90
|
+
|
91
|
+
def delete(obj_or_pk)
|
92
|
+
ogmanager.store.delete(obj_or_pk, self)
|
93
|
+
end
|
94
|
+
alias_method :delete!, :delete
|
95
|
+
|
96
|
+
def transaction(&block)
|
97
|
+
ogmanager.store.transaction(&block)
|
98
|
+
end
|
99
|
+
|
100
|
+
def primary_key
|
101
|
+
unless @__meta and @__meta[:primary_key]
|
102
|
+
self.meta :primary_key, Entity.resolve_primary_key(self)
|
103
|
+
end
|
104
|
+
@__meta[:primary_key].flatten
|
105
|
+
end
|
106
|
+
|
107
|
+
def const_missing(sym)
|
108
|
+
return sym
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
# A helper class.
|
115
|
+
|
116
|
+
class Entity
|
117
|
+
class << self
|
118
|
+
def resolve_primary_key(klass)
|
119
|
+
for p in klass.properties
|
120
|
+
if p.meta[:primary_key]
|
121
|
+
return p.symbol, p.klass
|
122
|
+
end
|
123
|
+
end
|
124
|
+
return :oid, Fixnum
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
include EntityMixin
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
data/lib/og/errors.rb
CHANGED
@@ -1,21 +1,16 @@
|
|
1
|
-
#
|
2
|
-
# (c) 2004-2005 Navel, all rights reserved.
|
3
|
-
# $Id: errors.rb 1 2005-04-11 11:04:30Z gmosx $
|
1
|
+
# $Id$
|
4
2
|
|
5
3
|
module Og
|
4
|
+
|
5
|
+
# This exception is thrown when a low level error happens
|
6
|
+
# in the store.
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
#--
|
10
|
-
# TODO: rename adapter_exception to store_exception.
|
11
|
-
#++
|
12
|
-
|
13
|
-
class SqlException < Exception
|
14
|
-
attr_accessor :adapter_exception, :sql
|
8
|
+
class StoreException < Exception
|
9
|
+
attr_accessor :original_exception, :info
|
15
10
|
|
16
|
-
def initialize(
|
17
|
-
@
|
11
|
+
def initialize(original_exception, info = nil)
|
12
|
+
@original_exception, @info = original_exception, info
|
18
13
|
end
|
19
14
|
end
|
20
|
-
|
21
|
-
end
|
15
|
+
|
16
|
+
end
|
data/lib/og/manager.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'og/entity'
|
2
|
+
require 'og/store'
|
3
|
+
|
4
|
+
module Og
|
5
|
+
|
6
|
+
# A Manager defines the relations between entities, ie Objects
|
7
|
+
# managed by Og.
|
8
|
+
|
9
|
+
class Manager
|
10
|
+
|
11
|
+
# Information about an Entity class
|
12
|
+
|
13
|
+
class EntityInfo
|
14
|
+
attr_accessor :klass
|
15
|
+
attr_accessor :enchanted
|
16
|
+
attr_accessor :options
|
17
|
+
|
18
|
+
|
19
|
+
def initialize(klass = nil)
|
20
|
+
@klass = klass
|
21
|
+
@enchanted = false
|
22
|
+
@options = {}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# The configuration options.
|
27
|
+
|
28
|
+
attr_accessor :options
|
29
|
+
|
30
|
+
# The store used for persistence.
|
31
|
+
|
32
|
+
attr_accessor :store
|
33
|
+
|
34
|
+
# The collection of Entities managed by this manager.
|
35
|
+
|
36
|
+
attr_accessor :entities
|
37
|
+
|
38
|
+
def initialize(options)
|
39
|
+
@options = options
|
40
|
+
@entities = {}
|
41
|
+
end
|
42
|
+
|
43
|
+
# Manage a class. Converts the class to an Entity.
|
44
|
+
|
45
|
+
def manage(klass)
|
46
|
+
return if managed?(klass) or klass.ancestors.include?(Unmanageable)
|
47
|
+
|
48
|
+
info = EntityInfo.new(klass)
|
49
|
+
|
50
|
+
klass.module_eval %{
|
51
|
+
def ==(other)
|
52
|
+
@#{klass.primary_key.first} == other.#{klass.primary_key.first}
|
53
|
+
end
|
54
|
+
}
|
55
|
+
|
56
|
+
klass.instance_variable_set '@ogmanager', self
|
57
|
+
klass.class.send(:attr_accessor, :ogmanager)
|
58
|
+
|
59
|
+
Relation.enchant(klass)
|
60
|
+
@store.enchant(klass, self)
|
61
|
+
|
62
|
+
@entities[klass] = info
|
63
|
+
end
|
64
|
+
|
65
|
+
# Is the class managed by Og?
|
66
|
+
|
67
|
+
def managed?(klass)
|
68
|
+
@entities.include?(klass)
|
69
|
+
end
|
70
|
+
alias_method :entity?, :managed?
|
71
|
+
|
72
|
+
# Use Ruby's advanced reflection features to find
|
73
|
+
# all manageable classes. Managable are all classes that
|
74
|
+
# define Properties.
|
75
|
+
|
76
|
+
def manageable_classes
|
77
|
+
classes = []
|
78
|
+
|
79
|
+
ObjectSpace.each_object(Class) do |c|
|
80
|
+
if c.respond_to?(:__props) and (!c.__props.empty?)
|
81
|
+
classes << c
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
Logger.debug "Og manageable classes: #{classes.inspect}" if $DBG
|
86
|
+
|
87
|
+
return classes
|
88
|
+
end
|
89
|
+
|
90
|
+
# Manage a collection of classes.
|
91
|
+
|
92
|
+
def manage_classes(*classes)
|
93
|
+
classes = manageable_classes if classes.empty?
|
94
|
+
classes.flatten.each { |c| manage(c) }
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
class << self
|
100
|
+
|
101
|
+
# Helper method, useful to initialize Og.
|
102
|
+
|
103
|
+
def setup(options = {})
|
104
|
+
m = Manager.new(options)
|
105
|
+
store = Store.for_name(options[:store])
|
106
|
+
store.destroy(options) if options[:destroy]
|
107
|
+
m.store = store.new(options)
|
108
|
+
m.manage_classes
|
109
|
+
return m
|
110
|
+
end
|
111
|
+
alias_method :connect, :setup
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require 'facet/string/plural'
|
2
|
+
require 'facet/string/singular'
|
3
|
+
require 'facet/string/demodulize'
|
4
|
+
require 'facet/string/underscore'
|
4
5
|
|
5
6
|
require 'glue/dynamic_include'
|
6
7
|
|
@@ -8,6 +9,9 @@ module Og
|
|
8
9
|
|
9
10
|
# Implements the Nested Sets pattern for hierarchical
|
10
11
|
# SQL queries.
|
12
|
+
#--
|
13
|
+
# TODO: use actice collections.
|
14
|
+
#++
|
11
15
|
|
12
16
|
module NestedSets
|
13
17
|
|
@@ -16,9 +20,8 @@ module NestedSets
|
|
16
20
|
:left => 'lft',
|
17
21
|
:right => 'rgt',
|
18
22
|
:type => Fixnum,
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:children => Inflector.plural_name(base)
|
23
|
+
:parent => base.to_s.demodulize.underscore.downcase,
|
24
|
+
:children => base.to_s.demodulize.underscore.downcase.plural
|
22
25
|
}
|
23
26
|
c.update(options) if options
|
24
27
|
|
@@ -26,16 +29,26 @@ module NestedSets
|
|
26
29
|
left = c[:left]
|
27
30
|
right = c[:right]
|
28
31
|
children = c[:children]
|
29
|
-
child =
|
32
|
+
child = children.singular
|
30
33
|
|
31
34
|
if c[:scope].is_a?(Symbol) && c[:scope].to_s !~ /_oid$/
|
32
35
|
c[:scope] = "#{c[:scope]}_oid".intern
|
33
36
|
end
|
37
|
+
|
34
38
|
scope = c[:scope]
|
35
|
-
if scope.is_a?(Symbol)
|
36
|
-
scope = %{(#{scope} ? "#{scope} = \#{@#{scope}}" : "#{scope} IS NULL")}
|
37
|
-
end
|
38
39
|
|
40
|
+
if scope
|
41
|
+
if scope.is_a?(Symbol)
|
42
|
+
scope = %{(#{scope} ? "#{scope} = \#{@#{scope}}" : "#{scope} IS NULL")}
|
43
|
+
end
|
44
|
+
|
45
|
+
cond = 'condition => ' + scope
|
46
|
+
cond_and = ':condition => ' + scope + ' + " AND " +'
|
47
|
+
else
|
48
|
+
cond = ':condition => nil'
|
49
|
+
cond_and = ':condition => '
|
50
|
+
end
|
51
|
+
|
39
52
|
base.module_eval <<-EOE, __FILE__, __LINE__
|
40
53
|
property :#{parent}, Fixnum, :sql_index => true
|
41
54
|
property :#{left}, :#{right}, #{c[:type]}
|
@@ -52,52 +65,43 @@ module NestedSets
|
|
52
65
|
return (@#{right} - @#{left} - 1)/2
|
53
66
|
end
|
54
67
|
|
55
|
-
def full_#{children}(
|
56
|
-
|
68
|
+
def full_#{children}(options = {})
|
69
|
+
options.update(#{cond_and}"(#{left} BETWEEN \#\{@#{left}\} AND \#{@#{right}})")
|
70
|
+
#{base}.all(options)
|
57
71
|
end
|
58
72
|
|
59
|
-
def #{children}(
|
60
|
-
|
73
|
+
def #{children}(options = {})
|
74
|
+
options.update(#{cond_and}"(#{left} > \#\{@#{left}\}) AND (#{right} < \#{@#{right}})")
|
75
|
+
#{base}.all(options)
|
61
76
|
end
|
62
77
|
|
63
|
-
def direct_#{children}(
|
64
|
-
|
78
|
+
def direct_#{children}(options = {})
|
79
|
+
options.update(#{cond_and}"#{parent} = \#{pk}")
|
80
|
+
#{base}.all(options)
|
65
81
|
end
|
66
82
|
|
67
83
|
def add_#{child}(child)
|
68
|
-
self.reload if
|
69
|
-
child.reload if child.
|
84
|
+
self.reload if pk
|
85
|
+
child.reload if child.pk
|
70
86
|
|
71
87
|
if @#{left}.nil? || @#{left} == 0 || @#{right}.nil? || @#{right} == 0
|
72
88
|
@#{left} = 1
|
73
89
|
@#{right} = 2
|
74
90
|
end
|
75
91
|
|
76
|
-
child.#{parent} =
|
92
|
+
child.#{parent} = pk
|
77
93
|
child.#{left} = pivot = @#{right}
|
78
94
|
child.#{right} = pivot + 1
|
79
95
|
@#{right} = pivot + 2
|
80
96
|
|
81
|
-
|
82
|
-
|
83
|
-
|
97
|
+
#{base}.transaction do
|
98
|
+
#{base}.update_property("#{left} = (#{left} + 2)", #{cond_and}"#{left} >= \#{pivot}")
|
99
|
+
#{base}.update_property("#{right} = (#{right} + 2)", #{cond_and}"#{right} >= \#{pivot}")
|
84
100
|
end
|
85
101
|
|
86
102
|
self.save
|
87
103
|
child.save
|
88
104
|
end
|
89
|
-
|
90
|
-
def self.og_pre_delete(conn, obj)
|
91
|
-
return unless (obj.#{left} and obj.#{right})
|
92
|
-
|
93
|
-
span = obj.#{right} - obj.#{left} + 1
|
94
|
-
|
95
|
-
(klass = obj.class).transaction do
|
96
|
-
klass.delete(#{scope} + " AND #{left} > \#{obj.#{left}} AND (#{right} < \#{obj.#{right}})")
|
97
|
-
klass.update("#{left} = (#{left} - \#{span})", "WHERE " + #{scope} + " AND #{left} >= \#{obj.#{right}}")
|
98
|
-
klass.update("#{right} = (#{right} - \#{span})", "WHERE " + #{scope} + " AND #{right} >= \#{obj.#{right}}")
|
99
|
-
end
|
100
|
-
end
|
101
105
|
EOE
|
102
106
|
end
|
103
107
|
|
@@ -121,14 +125,16 @@ end
|
|
121
125
|
module Hierarchical
|
122
126
|
|
123
127
|
def self.append_dynamic_features(base, options)
|
124
|
-
|
128
|
+
o = {
|
125
129
|
:method => :nested_sets,
|
126
130
|
}
|
127
|
-
|
131
|
+
o.update(options) if options
|
128
132
|
|
129
|
-
base.include(NestedSets)
|
133
|
+
base.include(NestedSets, o)
|
130
134
|
end
|
131
135
|
|
132
136
|
end
|
133
137
|
|
134
138
|
end
|
139
|
+
|
140
|
+
__END__
|