acts_as_ordered_tree 0.0.7 → 1.0.3

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/init.rb DELETED
@@ -1,2 +0,0 @@
1
- require "rubygems"
2
- require "bundler/setup"
@@ -1,36 +0,0 @@
1
- require "enumerator"
2
-
3
- module ActsAsOrderedTree
4
- # Enhanced enumerator
5
- #
6
- # Allows to use array specific methods like +empty?+, +reverse?+ and so on
7
- class Iterator < Enumerator
8
- class NullArgument < ArgumentError; end
9
- NA = NullArgument.new
10
-
11
- def initialize(*args, &block)
12
- @enumerator = Enumerator.new(*args, &block)
13
-
14
- super() do |yielder|
15
- @enumerator.each do |e|
16
- yielder << e
17
- end
18
- end
19
- end
20
-
21
- # Delegate everything to underlying array
22
- def method_missing(method_id, *args, &block)
23
- if method_id !~ /^(__|instance_eval|class|object_id)/
24
- to_ary!.__send__(method_id, *args, &block)
25
- else
26
- super
27
- end
28
- end
29
-
30
- private
31
- def to_ary!
32
- @enumerator = @enumerator.to_a unless @enumerator.is_a?(Array)
33
- @enumerator
34
- end
35
- end
36
- end
@@ -1,100 +0,0 @@
1
- require "active_support/concern"
2
- require "active_support/core_ext/object/with_options"
3
-
4
- module ActsAsOrderedTree
5
- module List
6
- extend ActiveSupport::Concern
7
-
8
- included do
9
- include PatchedMethods
10
- scope :ordered, order(position_column)
11
-
12
- with_options :if => :parent_changed? do |opts|
13
- opts.before_update :remove_from_old_list
14
- opts.before_update :add_to_list_bottom
15
- end
16
-
17
- define_model_callbacks :reorder
18
- around_update :__around_reorder, :if => :position_changed?,
19
- :unless => :parent_changed?
20
- end
21
-
22
- # Returns true if record has changes in +parent_id+
23
- def position_changed?
24
- changes.has_key?(position_column.to_s)
25
- end
26
-
27
- private
28
- # Turn off reorder callbacks temporary
29
- def skip_reorder_callbacks(skip = true) #:nodoc:
30
- @skip_reorder_callbacks = skip
31
- result = yield
32
- @skip_reorder_callbacks = false
33
-
34
- result
35
- end
36
-
37
- def __around_reorder #:nodoc:
38
- if @skip_reorder_callbacks
39
- yield
40
- else
41
- run_callbacks(:reorder) { yield }
42
- end
43
- end
44
-
45
- # It should invoke callbacks, so we patch +acts_as_list+ methods
46
- module PatchedMethods #:nodoc:all
47
- private
48
- def remove_from_old_list
49
- unchanged = self.class.find(id)
50
- unchanged.send(:decrement_positions_on_lower_items)
51
-
52
- nil
53
- end
54
-
55
- # This has the effect of moving all the higher items up one.
56
- def decrement_positions_on_higher_items(position)
57
- higher_than(position).each do |node|
58
- node.decrement!(position_column)
59
- end
60
- end
61
-
62
- # This has the effect of moving all the lower items up one.
63
- def decrement_positions_on_lower_items
64
- return unless in_list?
65
- lower_than(position).each do |node|
66
- node.decrement!(position_column)
67
- end
68
- end
69
-
70
- # This has the effect of moving all the higher items down one.
71
- def increment_positions_on_higher_items
72
- return unless in_list?
73
-
74
- higher_than(self[position_column]).each do |node|
75
- node.increment!(position_column)
76
- end
77
- end
78
-
79
- def increment_positions_on_all_items
80
- self_and_siblings.each do |sib|
81
- sib.increment!(position_column)
82
- end
83
- end
84
-
85
- def increment_positions_on_lower_items(position)
86
- lower_than(position).each do |node|
87
- node.increment!(position_column)
88
- end
89
- end
90
-
91
- def lower_than(position)
92
- acts_as_list_class.where(scope_condition).where("#{position_column} >= ?", position.to_i)
93
- end
94
-
95
- def higher_than(position)
96
- acts_as_list_class.where(scope_condition).where("#{position_column} < ?", position.to_i)
97
- end
98
- end
99
- end # module List
100
- end # module ActsAsOrderedTree
@@ -1,156 +0,0 @@
1
- require "active_support/concern"
2
-
3
- module ActsAsOrderedTree
4
- module Tree
5
- extend ActiveSupport::Concern
6
-
7
- included do
8
- # remove +acts_as_tree+ version of +roots+ method
9
- class << self
10
- remove_method :roots
11
-
12
- # Retrieve first root node
13
- #
14
- # Replacement for native +ActsAsTree.root+ method
15
- def root
16
- roots.first
17
- end
18
- end
19
-
20
- scope :roots, where(parent_column => nil).order(quoted_position_column)
21
-
22
- validate :validate_incest
23
-
24
- define_model_callbacks :move
25
- around_update :__around_move, :if => :parent_changed?
26
- end
27
-
28
- # == Instance methods
29
-
30
- # returns a Enumerator of ancestors, starting from parent until root
31
- def ancestors
32
- Iterator.new do |yielder|
33
- node = self
34
- yielder << node while node = node.parent
35
- end
36
- end
37
-
38
- # returns a Enumerator of ancestors, including self
39
- def self_and_ancestors
40
- Iterator.new do |y|
41
- y << self
42
- ancestors.each { |a| y << a }
43
- end
44
- end
45
-
46
- # returns a Enumerator of node's descendants, traversing depth first
47
- #
48
- # == Example
49
- # The tree:
50
- # # * root
51
- # # * child_1
52
- # # * grandchild_1_1
53
- # # * grandchild_1_2
54
- # # * child_2
55
- # # * grandchild_2_1
56
- #
57
- # root.descendants # => [root,
58
- # # child_1, grandchild_1_1, grandchild_1_2,
59
- # # child_2, grandchild_2_1]
60
- def descendants
61
- Iterator.new do |yielder|
62
- children.each do |child|
63
- yielder << child
64
-
65
- child.descendants.each do |grandchild|
66
- yielder << grandchild
67
- end
68
- end
69
- end
70
- end # def descendants
71
-
72
- def self_and_descendants
73
- Iterator.new do |y|
74
- y << self
75
- descendants.each { |x| y << x }
76
- end
77
- end
78
-
79
- # Returns depth of current node
80
- def depth
81
- ancestors.count
82
- end
83
- alias level depth
84
-
85
- # Return +true+ if +self+ is root node
86
- def root?
87
- self[parent_column].nil?
88
- end
89
-
90
- # Return +true+ if +self+ is leaf node
91
- def leaf?
92
- children.empty?
93
- end
94
-
95
- # Returns true if record has changes in +parent_id+
96
- def parent_changed?
97
- changes.has_key?(parent_column.to_s)
98
- end
99
-
100
- # Move node to other parent, make it last child of new parent
101
- def move_to_child_of(another_parent)
102
- transaction do
103
- self.parent = another_parent
104
-
105
- p_changed = parent_changed?
106
- save if p_changed
107
-
108
- skip_reorder_callbacks(p_changed) { move_to_bottom }
109
-
110
- parent.children.reload
111
- end
112
- end
113
-
114
- # Move node to position of +another_node+, shift down lower items
115
- def move_to_above_of(another_node)
116
- p_changed = parent != another_node.parent
117
-
118
- transaction do
119
- move_to_child_of(another_node.parent)
120
-
121
- skip_reorder_callbacks(p_changed) do
122
- insert_at(another_node[position_column])
123
- end
124
-
125
- another_node.parent.children.reload if another_node.parent.present?
126
- another_node.reload
127
- end
128
- end
129
-
130
- # Move node to the next of +another_node+, shift down lower items
131
- def move_to_bottom_of(another_node)
132
- p_changed = parent != another_node.parent
133
-
134
- transaction do
135
- move_to_child_of(another_node.parent)
136
-
137
- skip_reorder_callbacks(p_changed) do
138
- insert_at(another_node[position_column] + 1)
139
- end
140
-
141
- another_node.parent.children.reload if another_node.parent.present?
142
- another_node.reload
143
- end
144
- end
145
-
146
- protected
147
- def validate_incest #:nodoc:
148
- errors.add(:parent, :linked_to_self) if parent == self
149
- errors.add(:parent, :linked_to_descendant) if descendants.include?(parent)
150
- end
151
-
152
- def __around_move #:nodoc:
153
- run_callbacks(:move) { yield }
154
- end
155
- end # module Tree
156
- end # module ActsAsOrderedTree
@@ -1,3 +0,0 @@
1
- database:
2
- adapter: sqlite3
3
- database: ":memory:"
@@ -1,73 +0,0 @@
1
- require File.expand_path('../test_helper', __FILE__)
2
-
3
- describe ActsAsOrderedTree::Iterator do
4
- let(:iterator) do
5
- ActsAsOrderedTree::Iterator.new([1, 2, 3, 4, 2, 3])
6
- end
7
-
8
- let(:blanks) { ActsAsOrderedTree::Iterator.new([1, nil, 3]) }
9
-
10
- it "should have random access" do
11
- iterator[1].should eq(2)
12
- iterator.at(1).should eq(2)
13
- iterator.fetch(1).should eq(2)
14
- iterator.values_at(1, 2).should eq([2, 3])
15
- iterator.last.should eq(3)
16
- iterator.slice(1, 2).should have(2).items
17
- iterator.sample.should be_a(Fixnum)
18
- end
19
-
20
- it "should support operators" do
21
- (iterator + [5]).should have(7).items
22
- (iterator - [4]).should have(5).items
23
- (iterator * 2).should have(12).items
24
- (iterator & [4]).should have(1).items
25
- (iterator | [4]).should have(4).items
26
- iterator.concat([5]).should have(7).items
27
- end
28
-
29
- it "should find left index" do
30
- iterator.find_index(2).should eq(1)
31
- iterator.find_index { |n| n == 2 }.should eq(1)
32
- end
33
-
34
- it "should find right index" do
35
- iterator.rindex(2).should eq(4)
36
- iterator.rindex { |n| n == 2 }.should eq(4)
37
- end
38
-
39
- it "should be compacted" do
40
- blanks.compact.should have(2).items
41
- end
42
-
43
- it "should be mutable" do
44
- iter = ActsAsOrderedTree::Iterator.new([1, 2])
45
- iter << 3 # [1, 2, 3]
46
-
47
- iter.should have(3).items
48
-
49
- iter.insert(1, 99) # [1, 99, 2, 3]
50
- iter.at(1).should eq(99)
51
-
52
- last = iter.pop # [1, 99, 2]
53
- iter.last.should eq(2)
54
- last.should eq(3)
55
-
56
- first = iter.shift # [99, 2]
57
- iter.first.should eq(99)
58
- first.should eq(1)
59
-
60
- iter.unshift(100) # [100, 99, 2]
61
- iter.first.should eq(100)
62
-
63
- iter.push(4)
64
- iter.should have(4).items
65
- iter.last.should eq(4)
66
- end
67
-
68
- it "should raise NoMethodError" do
69
- iter = ActsAsOrderedTree::Iterator.new([1, 2])
70
-
71
- lambda { iter.__undefined_method__ }.should raise_error(NoMethodError)
72
- end
73
- end
@@ -1,53 +0,0 @@
1
- require File.expand_path('../../init', __FILE__)
2
-
3
- require "rspec"
4
- require "rspec-expectations"
5
-
6
- require "simplecov"
7
- SimpleCov.start
8
-
9
- require "acts_as_ordered_tree"
10
- require "logger"
11
-
12
- config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
13
- ActiveRecord::Base.establish_connection(config['database'])
14
- ActiveRecord::Base.logger = Logger.new(ENV['DEBUG'] ? $stderr : '/dev/null')
15
-
16
- # Create schema
17
- ActiveRecord::Base.connection.create_table :nodes do |t|
18
- t.integer :parent_id
19
- t.integer :position
20
- t.string :name
21
- end
22
-
23
- class Node < ActiveRecord::Base
24
- acts_as_ordered_tree
25
-
26
- before_reorder :on_before_reorder
27
- after_reorder :on_after_reorder
28
- around_reorder :on_around_reorder
29
- before_move :on_before_move
30
- after_move :on_after_move
31
- around_move :on_around_move
32
-
33
- def self.debug
34
- buf = StringIO.new("", "w")
35
-
36
- roots.each do |n|
37
- buf.puts "! #{n.name}"
38
- n.descendants.each do |d|
39
- buf.puts "#{' ' * d.level * 2} (##{d.id}): #{d.name} @ #{d.position}"
40
- end
41
- end
42
-
43
- print buf.string
44
- end
45
-
46
- # stub
47
- def on_before_reorder;end
48
- def on_after_reorder;end
49
- def on_around_reorder;yield end
50
- def on_before_move; end
51
- def on_after_move; end
52
- def on_around_move; yield end
53
- end