acts_as_nested_interval 0.2.0 → 0.3.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +11 -4
- data/lib/acts_as_nested_interval.rb +3 -30
- data/lib/acts_as_nested_interval/associations.rb +41 -0
- data/lib/acts_as_nested_interval/callbacks.rb +18 -8
- data/lib/acts_as_nested_interval/configuration.rb +9 -2
- data/lib/acts_as_nested_interval/core_ext/rational.rb +7 -0
- data/lib/acts_as_nested_interval/instance_methods.rb +49 -106
- data/lib/acts_as_nested_interval/version.rb +1 -1
- data/test/acts_as_nested_interval_test.rb +64 -160
- metadata +35 -87
- data/test/dummy/README.rdoc +0 -28
- data/test/dummy/Rakefile +0 -6
- data/test/dummy/app/assets/javascripts/application.js +0 -13
- data/test/dummy/app/assets/stylesheets/application.css +0 -15
- data/test/dummy/app/controllers/application_controller.rb +0 -5
- data/test/dummy/app/helpers/application_helper.rb +0 -2
- data/test/dummy/app/models/region.rb +0 -5
- data/test/dummy/app/views/layouts/application.html.erb +0 -14
- data/test/dummy/bin/bundle +0 -3
- data/test/dummy/bin/rails +0 -4
- data/test/dummy/bin/rake +0 -4
- data/test/dummy/config.ru +0 -4
- data/test/dummy/config/application.rb +0 -23
- data/test/dummy/config/boot.rb +0 -5
- data/test/dummy/config/database.yml +0 -31
- data/test/dummy/config/environment.rb +0 -5
- data/test/dummy/config/environments/development.rb +0 -37
- data/test/dummy/config/environments/production.rb +0 -83
- data/test/dummy/config/environments/test.rb +0 -39
- data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/test/dummy/config/initializers/cookies_serializer.rb +0 -3
- data/test/dummy/config/initializers/filter_parameter_logging.rb +0 -4
- data/test/dummy/config/initializers/inflections.rb +0 -16
- data/test/dummy/config/initializers/mime_types.rb +0 -4
- data/test/dummy/config/initializers/session_store.rb +0 -3
- data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
- data/test/dummy/config/locales/en.yml +0 -23
- data/test/dummy/config/routes.rb +0 -56
- data/test/dummy/config/secrets.yml +0 -22
- data/test/dummy/db/migrate/20120302143528_create_regions.rb +0 -15
- data/test/dummy/db/migrate/20121004204252_change_interval_precision.rb +0 -6
- data/test/dummy/db/schema.rb +0 -31
- data/test/dummy/log/development.log +0 -140
- data/test/dummy/log/test.log +0 -7048
- data/test/dummy/public/404.html +0 -67
- data/test/dummy/public/422.html +0 -67
- data/test/dummy/public/500.html +0 -66
- data/test/dummy/public/favicon.ico +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 727dd5393b81d22ca456a5ac3f2661287f909a42
|
4
|
+
data.tar.gz: ff0dc310818337c838c1ac91c9f6a9e7054bc282
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fadcf0336a54a3d6deb0ae23d5a11c96c1e229a39653dca8568a1f9bf0b174057e7cb422751517409a1635857690d1a89925d959986896eeb68fcdb57b2fe39b
|
7
|
+
data.tar.gz: b33657f0b7cc2458a00827bb7b0b7fd24d39e8217bec60253acbbce2ff2487d572e0fe0669ee091d30bf7c09460bae0c7e6385b52b005af4a96d3016781707c2
|
data/README.md
CHANGED
@@ -17,8 +17,10 @@ If your database supports recursive queryes (`WITH RECURSIVE`) or specific custo
|
|
17
17
|
## Install
|
18
18
|
|
19
19
|
```ruby
|
20
|
-
# add to Gemfile
|
21
|
-
gem 'acts_as_nested_interval'
|
20
|
+
# add to Gemfile when you use Ruby > 2.0 or Rails >= 4.0
|
21
|
+
gem 'acts_as_nested_interval', '~> 0.2.0'
|
22
|
+
# Orherwise
|
23
|
+
gem 'acts_as_nested_interval', '~> 0.1.1' # This version could have less features than actual version, check legacy branch.
|
22
24
|
```
|
23
25
|
|
24
26
|
```sh
|
@@ -49,6 +51,7 @@ add_index :regions, :lftp
|
|
49
51
|
add_index :regions, :lftq
|
50
52
|
add_index :regions, :lft
|
51
53
|
add_index :regions, :rgt
|
54
|
+
add_index :regions, [:lftp, :lftq, :rgtq, :rgtp], unique: true
|
52
55
|
```
|
53
56
|
|
54
57
|
## Usage
|
@@ -59,8 +62,11 @@ point data types in the database.
|
|
59
62
|
This act provides these named scopes:
|
60
63
|
|
61
64
|
```ruby
|
62
|
-
Region.roots
|
63
|
-
Region.preorder
|
65
|
+
Region.roots # returns roots of tree.
|
66
|
+
Region.preorder # returns records for preorder traversal.
|
67
|
+
Region.ancestors_of(node) # returns all ancestors of given node
|
68
|
+
Region.descendants_of(node) # returns all descendants of given node
|
69
|
+
Region.siblings_of(node) # returns all siblings of given node
|
64
70
|
```
|
65
71
|
|
66
72
|
This act provides these instance methods:
|
@@ -70,6 +76,7 @@ Region.parent # returns parent of record.
|
|
70
76
|
Region.children # returns children of record.
|
71
77
|
Region.ancestors # returns scoped ancestors of record.
|
72
78
|
Region.descendants # returns scoped descendants of record.
|
79
|
+
Region.siblings # returns scoped siblings of record.
|
73
80
|
Region.depth # returns depth of record.
|
74
81
|
```
|
75
82
|
|
@@ -5,12 +5,14 @@
|
|
5
5
|
# https://github.com/clyfe
|
6
6
|
|
7
7
|
require 'acts_as_nested_interval/core_ext/integer'
|
8
|
+
require 'acts_as_nested_interval/core_ext/rational'
|
8
9
|
require 'acts_as_nested_interval/version'
|
9
10
|
require 'acts_as_nested_interval/constants'
|
10
11
|
require 'acts_as_nested_interval/configuration'
|
11
12
|
require 'acts_as_nested_interval/callbacks'
|
12
13
|
require 'acts_as_nested_interval/instance_methods'
|
13
14
|
require 'acts_as_nested_interval/class_methods'
|
15
|
+
require 'acts_as_nested_interval/associations'
|
14
16
|
|
15
17
|
# This act implements a nested-interval tree. You can find all descendants
|
16
18
|
# or all ancestors with just one select query. You can insert and delete
|
@@ -28,46 +30,17 @@ module ActsAsNestedInterval
|
|
28
30
|
# * <tt>:dependent</tt> -- dependency between the parent node and children nodes (default :restrict)
|
29
31
|
def acts_as_nested_interval(options = {})
|
30
32
|
# Refactored
|
31
|
-
# TODO: table_exists?
|
32
33
|
cattr_accessor :nested_interval
|
33
34
|
|
34
35
|
self.nested_interval = Configuration.new( self, **options )
|
35
36
|
|
36
|
-
if nested_interval.fraction_cache?
|
37
|
-
scope :preorder, -> { order(rgt: :desc, lftp: :asc) }
|
38
|
-
else
|
39
|
-
scope :preorder, -> { order('1.0 * rgtp / rgtq DESC, lftp ASC') }
|
40
|
-
end
|
41
|
-
# When?
|
42
|
-
#scope :preorder, -> { order('nested_interval_rgt(lftp, lftq) DESC, lftp ASC') }
|
43
|
-
|
44
|
-
|
45
|
-
# OLD CODE
|
46
|
-
#cattr_accessor :nested_interval_foreign_key
|
47
|
-
#cattr_accessor :nested_interval_scope_columns
|
48
|
-
cattr_accessor :nested_interval_lft_index
|
49
|
-
#cattr_accessor :nested_interval_dependent
|
50
|
-
|
51
|
-
cattr_accessor :virtual_root
|
52
|
-
self.virtual_root = !!options[:virtual_root]
|
53
|
-
|
54
|
-
#self.nested_interval_foreign_key = options[:foreign_key] || :parent_id
|
55
|
-
#self.nested_interval_scope_columns = Array(options[:scope_columns])
|
56
|
-
self.nested_interval_lft_index = options[:lft_index]
|
57
|
-
#self.nested_interval_dependent = options[:dependent] || :restrict_with_exception
|
58
|
-
|
59
|
-
belongs_to :parent, class_name: name, foreign_key: nested_interval.foreign_key
|
60
|
-
has_many :children, class_name: name, foreign_key: nested_interval.foreign_key,
|
61
|
-
dependent: nested_interval.dependent
|
62
|
-
scope :roots, -> { where(nested_interval.foreign_key => nil) }
|
63
|
-
|
64
37
|
if self.table_exists? # Fix problem with migrating without table
|
65
38
|
include ActsAsNestedInterval::InstanceMethods
|
66
39
|
include ActsAsNestedInterval::Callbacks
|
40
|
+
include ActsAsNestedInterval::Associations
|
67
41
|
extend ActsAsNestedInterval::ClassMethods
|
68
42
|
end
|
69
43
|
end
|
70
44
|
end
|
71
45
|
end
|
72
46
|
|
73
|
-
#ActiveRecord::Base.send :extend, ActsAsNestedInterval
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module ActsAsNestedInterval
|
2
|
+
module Associations
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
belongs_to :parent, class_name: name, foreign_key: nested_interval.foreign_key
|
7
|
+
has_many :children, class_name: name, foreign_key: nested_interval.foreign_key,
|
8
|
+
dependent: nested_interval.dependent
|
9
|
+
scope :roots, -> { where(nested_interval.foreign_key => nil) }
|
10
|
+
|
11
|
+
scope :ancestors_of, ->(node){ where("rgt >= CAST(:rgt AS FLOAT) AND lft < CAST(:lft AS FLOAT)", rgt: node.rgt, lft: node.lft) }
|
12
|
+
scope :subtree_of, ->(node){ where( "lft BETWEEN :lft AND :rgt", rgt: node.rgt, lft: node.lft ) } # Simple version
|
13
|
+
scope :descendants_of, ->(node){ subtree_of(node).where.not(id: node.id) }
|
14
|
+
scope :siblings_of, ->(node){ fkey = nested_interval.foreign_key; where( fkey => node.send(fkey) ).where.not(id: node.id) }
|
15
|
+
|
16
|
+
if nested_interval.fraction_cache?
|
17
|
+
scope :preorder, -> { order(rgt: :desc, lftp: :asc) }
|
18
|
+
else
|
19
|
+
scope :preorder, -> { order('1.0 * rgtp / rgtq DESC, lftp ASC') }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def ancestor_of?(node)
|
24
|
+
left < node.left && right >= node.right
|
25
|
+
end
|
26
|
+
|
27
|
+
def ancestors
|
28
|
+
nested_interval_scope.ancestors_of(self)
|
29
|
+
end
|
30
|
+
|
31
|
+
def descendants
|
32
|
+
nested_interval_scope.descendants_of(self)
|
33
|
+
end
|
34
|
+
|
35
|
+
def siblings
|
36
|
+
nested_interval_scope.siblings_of(self)
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -17,26 +17,36 @@ module ActsAsNestedInterval
|
|
17
17
|
|
18
18
|
# Creates record.
|
19
19
|
def create_nested_interval
|
20
|
-
|
21
|
-
set_nested_interval_for_top
|
22
|
-
else
|
23
|
-
set_nested_interval *parent.lock!.next_child_lft
|
24
|
-
end
|
20
|
+
set_nested_interval(parent.present? ? parent.next_child_lft : next_root_lft )
|
25
21
|
end
|
26
22
|
|
27
23
|
# Updates record, updating descendants if parent association updated,
|
28
24
|
# in which case caller should first acquire table lock.
|
29
25
|
def update_nested_interval
|
30
|
-
|
31
|
-
|
26
|
+
return if moving?
|
27
|
+
unless node_moved?
|
32
28
|
db_self = self.class.find(id).lock!
|
33
29
|
write_attribute(nested_interval.foreign_key, db_self.read_attribute(nested_interval.foreign_key))
|
34
|
-
set_nested_interval db_self.lftp, db_self.lftq
|
30
|
+
set_nested_interval Rational(db_self.lftp, db_self.lftq)
|
35
31
|
else
|
36
32
|
# No locking in this case -- caller should have acquired table lock.
|
37
33
|
update_nested_interval_move
|
38
34
|
end
|
39
35
|
end
|
36
|
+
|
37
|
+
def move!
|
38
|
+
return if moving?
|
39
|
+
self.class.nested_interval.moving = true
|
40
|
+
transaction do
|
41
|
+
yield
|
42
|
+
end
|
43
|
+
ensure
|
44
|
+
self.class.nested_interval.moving = false
|
45
|
+
end
|
46
|
+
|
47
|
+
def moving?
|
48
|
+
!!self.class.nested_interval.moving
|
49
|
+
end
|
40
50
|
|
41
51
|
end
|
42
52
|
end
|
@@ -2,13 +2,16 @@ module ActsAsNestedInterval
|
|
2
2
|
class Configuration
|
3
3
|
|
4
4
|
attr_reader :foreign_key, :dependent, :scope_columns
|
5
|
+
attr_accessor :moving
|
5
6
|
|
6
7
|
# multiple_roots - allow more than one root
|
7
|
-
def initialize( model, virtual_root: false, foreign_key: :parent_id, dependent: :restrict_with_exception, scope_columns: [] )
|
8
|
+
def initialize( model, virtual_root: false, foreign_key: :parent_id, dependent: :restrict_with_exception, scope_columns: [], moveable: true)
|
8
9
|
@multiple_roots = !!virtual_root
|
9
|
-
@foreign_key = foreign_key
|
10
|
+
@foreign_key = foreign_key.to_sym
|
10
11
|
@dependent = dependent
|
11
12
|
@scope_columns = *scope_columns
|
13
|
+
@moveable = moveable && !model.readonly_attributes.include?(foreign_key) # Fix issue #9
|
14
|
+
@moving = false
|
12
15
|
|
13
16
|
check_model_columns( model )
|
14
17
|
end
|
@@ -21,6 +24,10 @@ module ActsAsNestedInterval
|
|
21
24
|
@fraction_cache
|
22
25
|
end
|
23
26
|
|
27
|
+
def moveable?
|
28
|
+
@moveable
|
29
|
+
end
|
30
|
+
|
24
31
|
private
|
25
32
|
|
26
33
|
def check_model_columns( model )
|
@@ -1,50 +1,23 @@
|
|
1
|
+
using Mediant
|
2
|
+
|
1
3
|
module ActsAsNestedInterval
|
2
4
|
module InstanceMethods
|
5
|
+
|
3
6
|
extend ActiveSupport::Concern
|
4
7
|
|
5
8
|
# selectively define #descendants according to table features
|
6
9
|
included do
|
7
|
-
|
8
|
-
if nested_interval.fraction_cache?
|
9
|
-
|
10
|
-
def descendants
|
11
|
-
nested_interval_scope.where( "lftp > :lftp AND lft BETWEEN :lft AND :rgt", lftp: lftp, rgt: rgt, lft: lft )
|
12
|
-
end
|
13
|
-
|
14
|
-
else
|
15
|
-
|
16
|
-
def descendants
|
17
|
-
quoted_table_name = self.class.quoted_table_name
|
18
|
-
nested_interval_scope.where <<-SQL
|
19
|
-
( #{quoted_table_name}.lftp != #{rgtp} OR
|
20
|
-
#{quoted_table_name}.lftq != #{rgtq}
|
21
|
-
) AND
|
22
|
-
#{quoted_table_name}.lftp BETWEEN
|
23
|
-
1 + #{quoted_table_name}.lftq * CAST(#{lftp} AS BIGINT) / #{lftq} AND
|
24
|
-
#{quoted_table_name}.lftq * CAST(#{rgtp} AS BIGINT) / #{rgtq}
|
25
|
-
SQL
|
26
|
-
end
|
27
|
-
|
28
|
-
end
|
29
|
-
|
10
|
+
validate :disallow_circular_dependency
|
30
11
|
end
|
31
12
|
|
32
|
-
def set_nested_interval(
|
33
|
-
self.lftp, self.lftq =
|
13
|
+
def set_nested_interval(rational)
|
14
|
+
self.lftp, self.lftq = rational.numerator, rational.denominator
|
34
15
|
self.rgtp = rgtp if has_attribute?(:rgtp)
|
35
16
|
self.rgtq = rgtq if has_attribute?(:rgtq)
|
36
17
|
self.lft = lft if has_attribute?(:lft)
|
37
18
|
self.rgt = rgt if has_attribute?(:rgt)
|
38
19
|
end
|
39
20
|
|
40
|
-
def set_nested_interval_for_top
|
41
|
-
if nested_interval.multiple_roots?
|
42
|
-
set_nested_interval(*next_root_lft)
|
43
|
-
else
|
44
|
-
set_nested_interval 0, 1
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
21
|
def nested_interval_scope
|
49
22
|
conditions = {}
|
50
23
|
nested_interval.scope_columns.each do |column_name|
|
@@ -53,80 +26,33 @@ module ActsAsNestedInterval
|
|
53
26
|
self.class.where conditions
|
54
27
|
end
|
55
28
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
errors.add nested_interval.foreign_key, "is descendant"
|
64
|
-
raise ActiveRecord::RecordInvalid, self
|
65
|
-
end
|
66
|
-
rescue ActiveRecord::RecordNotFound => e # root
|
29
|
+
def recalculate_nested_interval!
|
30
|
+
move! do
|
31
|
+
lftr = parent.present? ? parent.next_child_lft : next_root_lft
|
32
|
+
set_nested_interval( lftr )
|
33
|
+
save!
|
34
|
+
self.recalculate_nested_interval!
|
35
|
+
children.preorder.map(&:recalculate_nested_interval!)
|
67
36
|
end
|
68
|
-
|
69
|
-
if read_attribute(nested_interval.foreign_key).nil? # root move
|
70
|
-
set_nested_interval_for_top
|
71
|
-
else # child move
|
72
|
-
set_nested_interval *parent.next_child_lft
|
73
|
-
end
|
74
|
-
cpp = db_self.lftq * rgtp - db_self.rgtq * lftp
|
75
|
-
cpq = db_self.rgtp * lftp - db_self.lftp * rgtp
|
76
|
-
cqp = db_self.lftq * rgtq - db_self.rgtq * lftq
|
77
|
-
cqq = db_self.rgtp * lftq - db_self.lftp * rgtq
|
37
|
+
end
|
78
38
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
mysql = false #["MySQL", "Mysql2"].include?(connection.adapter_name)
|
83
|
-
var = ->(v) { mysql ? vars.add?(v) ? "(@#{v} := #{v})" : "@#{v}" : v }
|
84
|
-
multiply = ->(c, b) { "#{c} * #{var.(b)}" }
|
85
|
-
add = ->(a, b) { "#{a} + #{b}" }
|
86
|
-
one = sprintf("%#.30f", 1)
|
87
|
-
divide = ->(p, q) { "#{one} * (#{p}) / (#{q})" }
|
39
|
+
# Rewrite method
|
40
|
+
def update_nested_interval_move
|
41
|
+
return unless self.class.nested_interval.moveable?
|
88
42
|
|
89
|
-
if
|
90
|
-
|
91
|
-
|
92
|
-
updates[:rgt] = -> { divide.(updates[:rgtp].(), updates[:rgtq].()) } if has_attribute?(:rgt)
|
43
|
+
if parent.present? and self.ancestor_of?(parent)
|
44
|
+
errors.add nested_interval.foreign_key, "is descendant"
|
45
|
+
raise ActiveRecord::RecordInvalid, self
|
93
46
|
end
|
94
47
|
|
95
|
-
|
96
|
-
|
97
|
-
updates[:lft] = -> { divide.(updates[:lftp].(), updates[:lftq].()) } if has_attribute?(:lft)
|
98
|
-
|
99
|
-
sql = updates.map { |k, v| "#{k} = #{v.()}" }.join(', ')
|
100
|
-
|
101
|
-
db_self.descendants.update_all sql
|
48
|
+
# TODO: Do it by DB
|
49
|
+
self.recalculate_nested_interval!
|
102
50
|
end
|
103
51
|
|
104
|
-
def ancestor_of?(node)
|
105
|
-
node.lftp == lftp && node.lftq == lftq ||
|
106
|
-
node.lftp > node.lftq * lftp / lftq &&
|
107
|
-
node.lftp <= node.lftq * rgtp / rgtq &&
|
108
|
-
(node.lftp != rgtp || node.lftq != rgtq)
|
109
|
-
end
|
110
|
-
|
111
|
-
def ancestors
|
112
|
-
nested_interval_scope.where("rgt >= CAST(:rgt AS FLOAT) AND lft < CAST(:lft AS FLOAT)", rgt: rgt, lft: lft)
|
113
|
-
end
|
114
|
-
|
115
|
-
#def ancestors
|
116
|
-
#sqls = ['0 = 1']
|
117
|
-
#p, q = lftp, lftq
|
118
|
-
#while p != 0
|
119
|
-
#x = p.inverse(q)
|
120
|
-
#p, q = (x * p - 1) / q, x
|
121
|
-
#sqls << "lftq = #{q} AND lftp = #{p}"
|
122
|
-
#end
|
123
|
-
#nested_interval_scope.where(sqls * ' OR ')
|
124
|
-
#end
|
125
|
-
|
126
52
|
# Returns depth by counting ancestors up to 0 / 1.
|
127
53
|
def depth
|
128
54
|
if new_record?
|
129
|
-
if
|
55
|
+
if parent.nil?
|
130
56
|
return 0
|
131
57
|
else
|
132
58
|
return parent.depth + 1
|
@@ -167,22 +93,39 @@ module ActsAsNestedInterval
|
|
167
93
|
# Returns left end of interval for next child.
|
168
94
|
def next_child_lft
|
169
95
|
if child = children.order('lftq DESC').first
|
170
|
-
return
|
96
|
+
return left.mediant( child.left )
|
171
97
|
else
|
172
|
-
return
|
98
|
+
return left.mediant( right )
|
173
99
|
end
|
174
100
|
end
|
175
101
|
|
176
102
|
# Returns left end of interval for next root.
|
177
103
|
def next_root_lft
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
return vr.lftp + child.lftp, vr.lftq + child.lftq
|
182
|
-
else
|
183
|
-
return vr.lftp + vr.rgtp, vr.lftq + vr.rgtq
|
184
|
-
end
|
104
|
+
last_root = nested_interval_scope.roots.order( rgtp: :desc, rgtq: :desc ).first
|
105
|
+
raise Exception.new("Only one root allowed") if last_root.present? && !self.class.nested_interval.multiple_roots?
|
106
|
+
last_root.try(:right) || 0.to_r
|
185
107
|
end
|
186
108
|
|
109
|
+
# Check if node is moved (parent changed)
|
110
|
+
def node_moved?
|
111
|
+
send(:"#{nested_interval.foreign_key}_changed?") # TODO: Check if parent moved?
|
112
|
+
end
|
113
|
+
|
114
|
+
def left
|
115
|
+
Rational(lftp, lftq)
|
116
|
+
end
|
117
|
+
|
118
|
+
def right
|
119
|
+
Rational(rgtp, rgtq)
|
120
|
+
end
|
121
|
+
|
122
|
+
protected
|
123
|
+
|
124
|
+
def disallow_circular_dependency
|
125
|
+
if parent == self
|
126
|
+
errors.add(self.class.nested_interval.foreign_key, 'cannot refer back to self')
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
187
130
|
end
|
188
131
|
end
|