acts_as_nested_interval 0.2.0.pre → 0.2.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
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`