acts_as_nested_interval 0.2.0.pre → 0.2.0.pre2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7ca996b586ce5012168cde429320be3243c3d709
4
- data.tar.gz: f8b4566aa2f030c896d1675ec0bad92eb717eeee
3
+ metadata.gz: d0b2b592385c77289fa506e90644ef852fba8e8f
4
+ data.tar.gz: a3c26bcb5485afb6922d413cb821137d2f8e9abd
5
5
  SHA512:
6
- metadata.gz: 1bda1e75d4f062d0220336ea18320b826f5b789e5db7f496fa3e9541bf35287919510fd170cfabbd1db4e4910e49d155319329027991699fe697313da078c4cb
7
- data.tar.gz: 60ddeeecca41bb1f7b549f0d62e23a24e9aa832e8ad3870843b204cccf5bb14e6c6e2919a0393ff8709b0cb49e26c031df4e7bb2575aa6ec77e2a76ebf3c5dc2
6
+ metadata.gz: 8143a530d3281764572e7d7c48309225c3a7754298e23f36d61efd19bfe337fda61e49b56f8d96681786021b3124e9c899fb526fbe02783adbb87b1a76470a58
7
+ data.tar.gz: 5695037b46285612a58e79b1832d23d6632c861d1d5374516c75ec22bf1cc252df215e44c21b59f5176c468b48be222cf0380f2de22a5470372fb060a8d5f318
data/README.md CHANGED
@@ -77,6 +77,8 @@ Example:
77
77
 
78
78
  ```ruby
79
79
  class Region < ActiveRecord::Base
80
+ include ActsAsNestedInterval
81
+
80
82
  acts_as_nested_interval
81
83
  end
82
84
 
@@ -17,7 +17,7 @@ module ActsAsNestedInterval
17
17
 
18
18
  # Creates record.
19
19
  def create_nested_interval
20
- if read_attribute(nested_interval_foreign_key).nil?
20
+ if read_attribute(nested_interval.foreign_key).nil?
21
21
  set_nested_interval_for_top
22
22
  else
23
23
  set_nested_interval *parent.lock!.next_child_lft
@@ -27,10 +27,10 @@ module ActsAsNestedInterval
27
27
  # Updates record, updating descendants if parent association updated,
28
28
  # in which case caller should first acquire table lock.
29
29
  def update_nested_interval
30
- changed = send(:"#{nested_interval_foreign_key}_changed?")
30
+ changed = send(:"#{nested_interval.foreign_key}_changed?")
31
31
  if !changed
32
- db_self = self.class.find(id, :lock => true)
33
- write_attribute(nested_interval_foreign_key, db_self.read_attribute(nested_interval_foreign_key))
32
+ db_self = self.class.find(id).lock!
33
+ write_attribute(nested_interval.foreign_key, db_self.read_attribute(nested_interval.foreign_key))
34
34
  set_nested_interval db_self.lftp, db_self.lftq
35
35
  else
36
36
  # No locking in this case -- caller should have acquired table lock.
@@ -21,7 +21,7 @@ module ActsAsNestedInterval
21
21
  update_subtree = ->(node){
22
22
  node.create_nested_interval
23
23
  node.save
24
- node.class.unscoped.where(nested_interval_foreign_key => node.id).find_each &update_subtree
24
+ node.class.unscoped.where(nested_interval.foreign_key => node.id).find_each &update_subtree
25
25
  }
26
26
  unscoped.roots.find_each &update_subtree
27
27
 
@@ -0,0 +1,35 @@
1
+ module ActsAsNestedInterval
2
+ class Configuration
3
+
4
+ attr_reader :foreign_key, :dependent, :scope_columns
5
+
6
+ # 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
+ @multiple_roots = !!virtual_root
9
+ @foreign_key = foreign_key
10
+ @dependent = dependent
11
+ @scope_columns = *scope_columns
12
+
13
+ check_model_columns( model )
14
+ end
15
+
16
+ def multiple_roots?
17
+ @multiple_roots
18
+ end
19
+
20
+ def fraction_cache?
21
+ @fraction_cache
22
+ end
23
+
24
+ private
25
+
26
+ def check_model_columns( model )
27
+ if missing_column = Constants::REQUIRED_COLUMNS.detect { |col| !model.columns_hash.has_key?( col.to_s ) }
28
+ raise Constants::MissingColumn.new( missing_column )
29
+ end
30
+
31
+ @fraction_cache = [:lft, :rgt].all? { |col| model.columns_hash.has_key?( col.to_s ) }
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,8 @@
1
+ module ActsAsNestedInterval
2
+ module Constants
3
+ # Required fields by gem
4
+ REQUIRED_COLUMNS = [:rgtp, :rgtq, :lftp, :lftq]
5
+
6
+ class MissingColumn < Exception; end
7
+ end
8
+ end
@@ -5,9 +5,7 @@ module ActsAsNestedInterval
5
5
  # selectively define #descendants according to table features
6
6
  included do
7
7
 
8
- #alias_method :parent_changed?, :"#{ nested_interval_foreign_key }_changed?"
9
-
10
- if columns_hash["lft"]
8
+ if nested_interval.fraction_cache?
11
9
 
12
10
  def descendants
13
11
  #quoted_table_name = self.class.quoted_table_name
@@ -18,31 +16,31 @@ module ActsAsNestedInterval
18
16
  nested_interval_scope.where( "lftp > :lftp AND lft BETWEEN :lft AND :rgt", lftp: lftp, rgt: rgt, lft: lft )
19
17
  end
20
18
 
21
- elsif nested_interval_lft_index
19
+ #elsif nested_interval_lft_index
22
20
 
23
- def descendants
24
- quoted_table_name = self.class.quoted_table_name
25
- nested_interval_scope.where <<-SQL
26
- #{lftp} < #{quoted_table_name}.lftp AND
27
- 1.0 * #{quoted_table_name}.lftp / #{quoted_table_name}.lftq BETWEEN
28
- #{1.0 * lftp / lftq} AND
29
- #{1.0 * rgtp / rgtq}
30
- SQL
31
- end
21
+ #def descendants
22
+ #quoted_table_name = self.class.quoted_table_name
23
+ #nested_interval_scope.where <<-SQL
24
+ ##{lftp} < #{quoted_table_name}.lftp AND
25
+ #1.0 * #{quoted_table_name}.lftp / #{quoted_table_name}.lftq BETWEEN
26
+ ##{1.0 * lftp / lftq} AND
27
+ ##{1.0 * rgtp / rgtq}
28
+ #SQL
29
+ #end
32
30
 
33
- elsif connection.adapter_name == "MySQL"
31
+ #elsif connection.adapter_name == "MySQL"
34
32
 
35
- def descendants
36
- quoted_table_name = self.class.quoted_table_name
37
- nested_interval_scope.where <<-SQL
38
- ( #{quoted_table_name}.lftp != #{rgtp} OR
39
- #{quoted_table_name}.lftq != #{rgtq}
40
- ) AND
41
- #{quoted_table_name}.lftp BETWEEN
42
- 1 + #{quoted_table_name}.lftq * #{lftp} DIV #{lftq} AND
43
- #{quoted_table_name}.lftq * #{rgtp} DIV #{rgtq}
44
- SQL
45
- end
33
+ #def descendants
34
+ #quoted_table_name = self.class.quoted_table_name
35
+ #nested_interval_scope.where <<-SQL
36
+ #( #{quoted_table_name}.lftp != #{rgtp} OR
37
+ ##{quoted_table_name}.lftq != #{rgtq}
38
+ #) AND
39
+ ##{quoted_table_name}.lftp BETWEEN
40
+ #1 + #{quoted_table_name}.lftq * #{lftp} DIV #{lftq} AND
41
+ ##{quoted_table_name}.lftq * #{rgtp} DIV #{rgtq}
42
+ #SQL
43
+ #end
46
44
 
47
45
  else
48
46
 
@@ -71,7 +69,7 @@ module ActsAsNestedInterval
71
69
  end
72
70
 
73
71
  def set_nested_interval_for_top
74
- if self.class.virtual_root
72
+ if nested_interval.multiple_roots?
75
73
  set_nested_interval(*next_root_lft)
76
74
  else
77
75
  set_nested_interval 0, 1
@@ -80,7 +78,7 @@ module ActsAsNestedInterval
80
78
 
81
79
  def nested_interval_scope
82
80
  conditions = {}
83
- nested_interval_scope_columns.each do |column_name|
81
+ nested_interval.scope_columns.each do |column_name|
84
82
  conditions[column_name] = send(column_name)
85
83
  end
86
84
  self.class.where conditions
@@ -88,18 +86,18 @@ module ActsAsNestedInterval
88
86
 
89
87
  # Rewrite method
90
88
  def update_nested_interval_move
91
- return if self.class.readonly_attributes.include?(nested_interval_foreign_key.to_sym) # Fix issue #9
89
+ return if self.class.readonly_attributes.include?(nested_interval.foreign_key.to_sym) # Fix issue #9
92
90
  begin
93
91
  db_self = self.class.find(id)
94
- db_parent = self.class.find(read_attribute(nested_interval_foreign_key))
92
+ db_parent = self.class.find(read_attribute(nested_interval.foreign_key))
95
93
  if db_self.ancestor_of?(db_parent)
96
- errors.add nested_interval_foreign_key, "is descendant"
94
+ errors.add nested_interval.foreign_key, "is descendant"
97
95
  raise ActiveRecord::RecordInvalid, self
98
96
  end
99
97
  rescue ActiveRecord::RecordNotFound => e # root
100
98
  end
101
99
 
102
- if read_attribute(nested_interval_foreign_key).nil? # root move
100
+ if read_attribute(nested_interval.foreign_key).nil? # root move
103
101
  set_nested_interval_for_top
104
102
  else # child move
105
103
  set_nested_interval *parent.next_child_lft
@@ -1,3 +1,3 @@
1
1
  module ActsAsNestedInterval
2
- VERSION = "0.2.0.pre"
2
+ VERSION = "0.2.0.pre2"
3
3
  end
@@ -6,6 +6,8 @@
6
6
 
7
7
  require 'acts_as_nested_interval/core_ext/integer'
8
8
  require 'acts_as_nested_interval/version'
9
+ require 'acts_as_nested_interval/constants'
10
+ require 'acts_as_nested_interval/configuration'
9
11
  require 'acts_as_nested_interval/callbacks'
10
12
  require 'acts_as_nested_interval/instance_methods'
11
13
  require 'acts_as_nested_interval/class_methods'
@@ -25,33 +27,41 @@ module ActsAsNestedInterval
25
27
  # * <tt>:virtual_root</tt> -- whether to compute root's interval as in an upper root (default false)
26
28
  # * <tt>:dependent</tt> -- dependency between the parent node and children nodes (default :restrict)
27
29
  def acts_as_nested_interval(options = {})
28
- cattr_accessor :nested_interval_foreign_key
29
- cattr_accessor :nested_interval_scope_columns
30
+ # Refactored
31
+ # TODO: table_exists?
32
+ cattr_accessor :nested_interval
33
+
34
+ self.nested_interval = Configuration.new( self, **options )
35
+
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
30
48
  cattr_accessor :nested_interval_lft_index
31
- cattr_accessor :nested_interval_dependent
49
+ #cattr_accessor :nested_interval_dependent
32
50
 
33
51
  cattr_accessor :virtual_root
34
52
  self.virtual_root = !!options[:virtual_root]
35
53
 
36
- self.nested_interval_foreign_key = options[:foreign_key] || :parent_id
37
- self.nested_interval_scope_columns = Array(options[:scope_columns])
54
+ #self.nested_interval_foreign_key = options[:foreign_key] || :parent_id
55
+ #self.nested_interval_scope_columns = Array(options[:scope_columns])
38
56
  self.nested_interval_lft_index = options[:lft_index]
39
- self.nested_interval_dependent = options[:dependent] || :restrict_with_exception
57
+ #self.nested_interval_dependent = options[:dependent] || :restrict_with_exception
40
58
 
41
- belongs_to :parent, class_name: name, foreign_key: nested_interval_foreign_key
42
- has_many :children, class_name: name, foreign_key: nested_interval_foreign_key,
43
- dependent: nested_interval_dependent
44
- scope :roots, -> { where(nested_interval_foreign_key => nil) }
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) }
45
63
 
46
64
  if self.table_exists? # Fix problem with migrating without table
47
- if columns_hash["rgt"]
48
- scope :preorder, -> { order('rgt DESC, lftp ASC') }
49
- elsif columns_hash["rgtp"] && columns_hash["rgtq"]
50
- scope :preorder, -> { order('1.0 * rgtp / rgtq DESC, lftp ASC') }
51
- else
52
- scope :preorder, -> { order('nested_interval_rgt(lftp, lftq) DESC, lftp ASC') }
53
- end
54
-
55
65
  include ActsAsNestedInterval::InstanceMethods
56
66
  include ActsAsNestedInterval::Callbacks
57
67
  extend ActsAsNestedInterval::ClassMethods
@@ -1,5 +1,9 @@
1
1
  require 'test_helper'
2
2
 
3
+ def set_virtual_root( value )
4
+ Region.nested_interval.instance_eval("@multiple_roots = #{ value }")
5
+ end
6
+
3
7
  class ActsAsNestedIntervalTest < ActiveSupport::TestCase
4
8
  def test_modular_inverse
5
9
  assert_equal [nil, 1, 5, nil, 7, 2, nil, 4, 8], (0...9).map { |k| k.inverse(9) }
@@ -195,7 +199,7 @@ class ActsAsNestedIntervalTest < ActiveSupport::TestCase
195
199
  end
196
200
 
197
201
  def test_virtual_root_order
198
- Region.virtual_root = true
202
+ set_virtual_root( true )
199
203
  r1 = Region.create name: "1"
200
204
  r2 = Region.create name: "2"
201
205
  r3 = Region.create name: "3"
@@ -204,7 +208,7 @@ class ActsAsNestedIntervalTest < ActiveSupport::TestCase
204
208
  end
205
209
 
206
210
  def test_virtual_root_allocation
207
- Region.virtual_root = true
211
+ set_virtual_root( true )
208
212
  r1 = Region.create name: "Europe"
209
213
  r2 = Region.create name: "Romania", :parent => r1
210
214
  r3 = Region.create name: "Asia"
@@ -215,7 +219,7 @@ class ActsAsNestedIntervalTest < ActiveSupport::TestCase
215
219
  end
216
220
 
217
221
  def test_rebuild_nested_interval_tree
218
- Region.virtual_root = true
222
+ set_virtual_root( true )
219
223
  r1 = Region.create name: "Europe"
220
224
  r2 = Region.create name: "Romania", parent: r1
221
225
  r3 = Region.create name: "Asia"
@@ -227,7 +231,7 @@ class ActsAsNestedIntervalTest < ActiveSupport::TestCase
227
231
  end
228
232
 
229
233
  def test_root_update_keeps_interval
230
- Region.virtual_root = true
234
+ set_virtual_root( true )
231
235
  r1 = Region.create name: "Europe"
232
236
  r2 = Region.create name: "Romania", parent: r1
233
237
  r3 = Region.create name: "Asia"
@@ -239,7 +243,7 @@ class ActsAsNestedIntervalTest < ActiveSupport::TestCase
239
243
  end
240
244
 
241
245
  def test_move_to_root_recomputes_interval
242
- Region.virtual_root = true
246
+ set_virtual_root( true )
243
247
  r1 = Region.create name: "Europe"
244
248
  r2 = Region.create name: "Romania", parent: r1
245
249
  r3 = Region.create name: "Asia"
@@ -13,9 +13,6 @@
13
13
 
14
14
  ActiveRecord::Schema.define(version: 20121004204252) do
15
15
 
16
- # These are extensions that must be enabled in order to support this database
17
- enable_extension "plpgsql"
18
-
19
16
  create_table "regions", force: true do |t|
20
17
  t.boolean "fiction", default: false, null: false
21
18
  t.integer "region_id"
@@ -15,3 +15,18 @@ Migrating to ChangeIntervalPrecision (20121004204252)
15
15
  SQL (1.0ms) INSERT INTO "schema_migrations" ("version") VALUES ($1) [["version", "20121004204252"]]
16
16
   (17.3ms) COMMIT
17
17
  ActiveRecord::SchemaMigration Load (0.9ms) SELECT "schema_migrations".* FROM "schema_migrations"
18
+  (1710.7ms) CREATE TABLE `schema_migrations` (`version` varchar(255) NOT NULL) ENGINE=InnoDB
19
+  (119.1ms) CREATE UNIQUE INDEX `unique_schema_migrations` ON `schema_migrations` (`version`)
20
+ ActiveRecord::SchemaMigration Load (0.9ms) SELECT `schema_migrations`.* FROM `schema_migrations`
21
+ Migrating to CreateRegions (20120302143528)
22
+  (87.2ms) CREATE TABLE `regions` (`id` int(11) auto_increment PRIMARY KEY, `fiction` tinyint(1) DEFAULT 0 NOT NULL, `region_id` int(11), `lftp` int(11) NOT NULL, `lftq` int(11) NOT NULL, `rgtp` int(11) NOT NULL, `rgtq` int(11) NOT NULL, `lft` float NOT NULL, `rgt` float NOT NULL, `name` varchar(255) NOT NULL) ENGINE=InnoDB
23
+  (0.4ms) BEGIN
24
+ SQL (0.9ms) INSERT INTO `schema_migrations` (`version`) VALUES ('20120302143528')
25
+  (23.7ms) COMMIT
26
+ Migrating to ChangeIntervalPrecision (20121004204252)
27
+  (157.5ms) ALTER TABLE `regions` CHANGE `lft` `lft` decimal(31,30) NOT NULL
28
+  (102.6ms) ALTER TABLE `regions` CHANGE `rgt` `rgt` decimal(31,30) NOT NULL
29
+  (0.5ms) BEGIN
30
+ SQL (1.4ms) INSERT INTO `schema_migrations` (`version`) VALUES ('20121004204252')
31
+  (23.8ms) COMMIT
32
+ ActiveRecord::SchemaMigration Load (0.8ms) SELECT `schema_migrations`.* FROM `schema_migrations`