acts_as_ordered_tree 0.0.7 → 1.0.3

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