parlement 0.14 → 0.17
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +41 -1
- data/MEMORY +66 -5
- data/README +10 -5
- data/Rakefile +15 -23
- data/app/controllers/account_controller.rb +48 -43
- data/app/controllers/{application.rb → application_controller.rb} +15 -12
- data/app/controllers/elt_controller.rb +77 -32
- data/app/controllers/subscriber_controller.rb +11 -10
- data/app/helpers/application_helper.rb +14 -1
- data/app/helpers/elt_helper.rb +9 -7
- data/app/models/elt.rb +25 -24
- data/app/models/mail.rb +44 -47
- data/app/models/person_notify.rb +2 -2
- data/app/models/user.rb +128 -2
- data/app/models/user_notify.rb +15 -15
- data/app/views/account/_login.rhtml +39 -39
- data/app/views/account/_show.rhtml +22 -30
- data/app/views/account/signup.rhtml +2 -2
- data/app/views/elt/_choice.rhtml +6 -6
- data/app/views/elt/_elt.rhtml +27 -32
- data/app/views/elt/choices.rhtml +16 -18
- data/app/views/elt/list/_byDate.rhtml +14 -14
- data/app/views/elt/list/_byVote.rhtml +15 -15
- data/app/views/elt/list/_children.rhtml +48 -40
- data/app/views/elt/list/_subscribers.rhtml +1 -1
- data/app/views/elt/new.rhtml +22 -21
- data/app/views/elt/rss.rxml +4 -11
- data/app/views/elt/show.rhtml +65 -61
- data/app/views/elt/vote_rss.rxml +4 -11
- data/app/views/layouts/top.rhtml +39 -50
- data/app/views/person/_listElts.rhtml +1 -1
- data/app/views/person/show.rhtml +1 -1
- data/{vendor/plugins/login_engine/app → app}/views/user_notify/change_password.rhtml +0 -0
- data/{vendor/plugins/login_engine/app → app}/views/user_notify/delete.rhtml +0 -0
- data/{vendor/plugins/login_engine/app → app}/views/user_notify/forgot_password.rhtml +0 -0
- data/{vendor/plugins/login_engine/app → app}/views/user_notify/pending_delete.rhtml +0 -0
- data/{vendor/plugins/login_engine/app → app}/views/user_notify/signup.rhtml +0 -0
- data/config/boot.rb +97 -32
- data/config/environment.rb +37 -35
- data/config/environments/development.rb +2 -3
- data/config/environments/production.rb +3 -0
- data/config/initializers/string_ruby_1.8.rb +10 -0
- data/config/routes.rb +17 -22
- data/db/schema.rb +102 -74
- data/lib/tasks/rspec.rake +167 -0
- data/public/404.html +25 -7
- data/public/500.html +26 -7
- data/public/dispatch.cgi +0 -0
- data/public/dispatch.fcgi +0 -0
- data/public/dispatch.rb +0 -0
- data/public/images/live_tree_branch_collapsed_icon.gif +0 -0
- data/public/images/live_tree_branch_expanded_icon.gif +0 -0
- data/public/images/live_tree_leaf_icon.gif +0 -0
- data/public/javascripts/application.js +258 -0
- data/public/javascripts/controls.js +544 -414
- data/public/javascripts/dragdrop.js +229 -198
- data/public/javascripts/effects.js +499 -459
- data/public/javascripts/prototype.js +2926 -1121
- data/public/javascripts/shadedborder.js +68 -50
- data/public/stylesheets/default.css +34 -34
- data/public/stylesheets/live_tree.css +0 -0
- data/public/stylesheets/scaffold.css +6 -6
- data/script/about +0 -0
- data/script/autospec +6 -0
- data/script/benchmarker +0 -0
- data/script/breakpointer +0 -0
- data/script/console +0 -0
- data/script/dbconsole +3 -0
- data/script/destroy +0 -0
- data/script/generate +0 -0
- data/script/plugin +0 -0
- data/script/profiler +0 -0
- data/script/runner +0 -0
- data/script/server +0 -0
- data/script/spec +10 -0
- data/script/spec_server +9 -0
- data/test/unit/attachment_test.rb +4 -4
- data/test/unit/choice_test.rb +1 -1
- data/test/unit/elt_test.rb +9 -9
- data/test/unit/mail_notify_test.rb +2 -2
- data/test/unit/mail_test.rb +18 -11
- data/test/unit/person_notify_test.rb +1 -1
- data/test/unit/person_test.rb +1 -1
- data/test/unit/subscriber_test.rb +1 -1
- data/test/unit/user_test.rb +81 -0
- data/test/unit/visit_test.rb +6 -6
- data/vendor/plugins/activerecord_foreign_key_extensions/init.rb +2 -0
- data/vendor/plugins/activerecord_foreign_key_extensions/lib/active_record_extensions.rb +182 -0
- data/vendor/plugins/activerecord_text_id_extensions/init.rb +2 -0
- data/vendor/plugins/activerecord_text_id_extensions/lib/active_record_extensions.rb +24 -0
- data/vendor/plugins/acts_as_nested_set/README +15 -0
- data/vendor/plugins/acts_as_nested_set/init.rb +1 -0
- data/vendor/plugins/acts_as_nested_set/lib/active_record/acts/nested_set.rb +210 -0
- data/vendor/plugins/acts_as_nested_set/test/nested_set_test.rb +269 -0
- data/vendor/plugins/acts_as_tree/README +26 -0
- data/vendor/plugins/acts_as_tree/Rakefile +22 -0
- data/vendor/plugins/acts_as_tree/init.rb +1 -0
- data/vendor/plugins/acts_as_tree/lib/active_record/acts/tree.rb +96 -0
- data/vendor/plugins/{output_compression/CHANGELOG → acts_as_tree/test/abstract_unit.rb} +0 -0
- data/vendor/plugins/acts_as_tree/test/acts_as_tree_test.rb +219 -0
- data/vendor/plugins/acts_as_tree/test/database.yml +0 -0
- data/vendor/plugins/acts_as_tree/test/fixtures/mixin.rb +0 -0
- data/vendor/plugins/acts_as_tree/test/fixtures/mixins.yml +0 -0
- data/vendor/plugins/acts_as_tree/test/schema.rb +0 -0
- data/vendor/plugins/classic_pagination/CHANGELOG +152 -0
- data/vendor/plugins/classic_pagination/README +18 -0
- data/vendor/plugins/{output_compression/rakefile → classic_pagination/Rakefile} +22 -22
- data/vendor/plugins/classic_pagination/init.rb +33 -0
- data/vendor/plugins/classic_pagination/install.rb +1 -0
- data/vendor/plugins/classic_pagination/lib/pagination.rb +405 -0
- data/vendor/plugins/classic_pagination/lib/pagination_helper.rb +135 -0
- data/vendor/plugins/classic_pagination/test/fixtures/companies.yml +24 -0
- data/vendor/plugins/classic_pagination/test/fixtures/company.rb +9 -0
- data/vendor/plugins/classic_pagination/test/fixtures/developer.rb +7 -0
- data/vendor/plugins/classic_pagination/test/fixtures/developers.yml +21 -0
- data/vendor/plugins/classic_pagination/test/fixtures/developers_projects.yml +13 -0
- data/vendor/plugins/classic_pagination/test/fixtures/project.rb +3 -0
- data/vendor/plugins/classic_pagination/test/fixtures/projects.yml +7 -0
- data/vendor/plugins/classic_pagination/test/fixtures/replies.yml +13 -0
- data/vendor/plugins/classic_pagination/test/fixtures/reply.rb +5 -0
- data/vendor/plugins/classic_pagination/test/fixtures/schema.sql +42 -0
- data/vendor/plugins/classic_pagination/test/fixtures/topic.rb +3 -0
- data/vendor/plugins/classic_pagination/test/fixtures/topics.yml +22 -0
- data/vendor/plugins/classic_pagination/test/helper.rb +117 -0
- data/vendor/plugins/classic_pagination/test/pagination_helper_test.rb +38 -0
- data/vendor/plugins/classic_pagination/test/pagination_test.rb +177 -0
- data/vendor/plugins/file_column/lib/file_column.rb +1 -1
- data/vendor/plugins/file_column/test/file_column_test.rb +0 -0
- metadata +151 -197
- data/app/helpers/live_tree.rb +0 -238
- data/app/views/elt/_form.rhtml +0 -31
- data/app/views/elt/show_tree.rhtml +0 -8
- data/config/environments/user_environment.rb +0 -1
- data/db/ROOT/Titemagli.txt +0 -3
- data/db/ROOT/titemagli.txt +0 -9
- data/public/javascripts/behaviour.js +0 -254
- data/public/javascripts/ie7-load.htc +0 -1
- data/public/javascripts/ie7.js +0 -6
- data/public/javascripts/live_tree.js +0 -749
- data/public/javascripts/mybehaviour.js +0 -225
- data/public/javascripts/scriptaculous.js +0 -47
- data/public/javascripts/slider.js +0 -283
- data/public/stylesheets/blue.css +0 -471
- data/vendor/plugins/engines/CHANGELOG +0 -241
- data/vendor/plugins/engines/MIT-LICENSE +0 -21
- data/vendor/plugins/engines/README +0 -64
- data/vendor/plugins/engines/Rakefile +0 -32
- data/vendor/plugins/engines/UPGRADING +0 -93
- data/vendor/plugins/engines/about.yml +0 -7
- data/vendor/plugins/engines/generators/plugin_migration/USAGE +0 -45
- data/vendor/plugins/engines/generators/plugin_migration/plugin_migration_generator.rb +0 -79
- data/vendor/plugins/engines/generators/plugin_migration/templates/plugin_migration.erb +0 -13
- data/vendor/plugins/engines/init.rb +0 -40
- data/vendor/plugins/engines/install.rb +0 -32
- data/vendor/plugins/engines/lib/engines.rb +0 -323
- data/vendor/plugins/engines/lib/engines/deprecated_config_support.rb +0 -135
- data/vendor/plugins/engines/lib/engines/plugin.rb +0 -214
- data/vendor/plugins/engines/lib/engines/plugin_list.rb +0 -31
- data/vendor/plugins/engines/lib/engines/plugin_migrator.rb +0 -60
- data/vendor/plugins/engines/lib/engines/rails_extensions.rb +0 -6
- data/vendor/plugins/engines/lib/engines/rails_extensions/active_record.rb +0 -19
- data/vendor/plugins/engines/lib/engines/rails_extensions/dependencies.rb +0 -143
- data/vendor/plugins/engines/lib/engines/rails_extensions/migrations.rb +0 -155
- data/vendor/plugins/engines/lib/engines/rails_extensions/public_asset_helpers.rb +0 -116
- data/vendor/plugins/engines/lib/engines/rails_extensions/rails.rb +0 -20
- data/vendor/plugins/engines/lib/engines/rails_extensions/rails_initializer.rb +0 -86
- data/vendor/plugins/engines/lib/engines/rails_extensions/routing.rb +0 -77
- data/vendor/plugins/engines/lib/engines/rails_extensions/templates.rb +0 -140
- data/vendor/plugins/engines/lib/engines/testing.rb +0 -87
- data/vendor/plugins/engines/tasks/engines.rake +0 -149
- data/vendor/plugins/login_engine/CHANGELOG +0 -22
- data/vendor/plugins/login_engine/README +0 -344
- data/vendor/plugins/login_engine/app/controllers/user_controller.rb +0 -262
- data/vendor/plugins/login_engine/app/helpers/user_helper.rb +0 -88
- data/vendor/plugins/login_engine/app/models/user.rb +0 -7
- data/vendor/plugins/login_engine/app/models/user_notify.rb +0 -75
- data/vendor/plugins/login_engine/app/views/user/_edit.rhtml +0 -11
- data/vendor/plugins/login_engine/app/views/user/_password.rhtml +0 -9
- data/vendor/plugins/login_engine/app/views/user/change_password.rhtml +0 -17
- data/vendor/plugins/login_engine/app/views/user/edit.rhtml +0 -23
- data/vendor/plugins/login_engine/app/views/user/forgot_password.rhtml +0 -18
- data/vendor/plugins/login_engine/app/views/user/home.rhtml +0 -7
- data/vendor/plugins/login_engine/app/views/user/login.rhtml +0 -17
- data/vendor/plugins/login_engine/app/views/user/logout.rhtml +0 -8
- data/vendor/plugins/login_engine/app/views/user/signup.rhtml +0 -17
- data/vendor/plugins/login_engine/db/migrate/001_initial_schema.rb +0 -25
- data/vendor/plugins/login_engine/init_engine.rb +0 -11
- data/vendor/plugins/login_engine/install.rb +0 -4
- data/vendor/plugins/login_engine/lib/login_engine.rb +0 -62
- data/vendor/plugins/login_engine/lib/login_engine/authenticated_system.rb +0 -113
- data/vendor/plugins/login_engine/lib/login_engine/authenticated_user.rb +0 -155
- data/vendor/plugins/login_engine/public/stylesheets/login_engine.css +0 -81
- data/vendor/plugins/login_engine/test/fixtures/users.yml +0 -41
- data/vendor/plugins/login_engine/test/functional/user_controller_test.rb +0 -536
- data/vendor/plugins/login_engine/test/mocks/mail.rb +0 -14
- data/vendor/plugins/login_engine/test/mocks/time.rb +0 -19
- data/vendor/plugins/login_engine/test/test_helper.rb +0 -11
- data/vendor/plugins/login_engine/test/unit/user_test.rb +0 -114
- data/vendor/plugins/output_compression/MIT-LICENSE +0 -20
- data/vendor/plugins/output_compression/README +0 -4
- data/vendor/plugins/output_compression/init.rb +0 -1
- data/vendor/plugins/output_compression/lib/output_compression.rb +0 -84
- data/vendor/plugins/output_compression/test/output_test.rb +0 -11
- data/vendor/plugins/output_compression/test/test_controller.rb +0 -3
- data/vendor/plugins/output_compression/test/test_helper.rb +0 -14
@@ -0,0 +1,24 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class SchemaDumper
|
3
|
+
private
|
4
|
+
alias old_table_text_id table
|
5
|
+
|
6
|
+
def table(table, stream)
|
7
|
+
text_id = false
|
8
|
+
@connection.columns(table).each { |c|
|
9
|
+
if c.name == 'id' and c.type.to_s == 'text'
|
10
|
+
text_id = true
|
11
|
+
end
|
12
|
+
}
|
13
|
+
|
14
|
+
old_table_text_id(table, stream)
|
15
|
+
|
16
|
+
if text_id
|
17
|
+
stream.puts " remove_column :#{table}, :id"
|
18
|
+
stream.puts " add_column :#{table}, :id, :text, :null => false, :primary_key => true"
|
19
|
+
stream.puts " execute 'ALTER TABLE #{table} ADD CONSTRAINT #{table}_pkey PRIMARY KEY (id)'"
|
20
|
+
stream.puts
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
ActsAsNestedSet
|
2
|
+
==============
|
3
|
+
|
4
|
+
This acts_as extension provides Nested Set functionality. Nested Set is similiar to Tree, but with the added feature that you can select the children and all of their descendents with a single query. A good use case for this is a threaded post system, where you want to display every reply to a comment without multiple selects.
|
5
|
+
|
6
|
+
|
7
|
+
Example
|
8
|
+
=======
|
9
|
+
|
10
|
+
class Product < ActiveRecord::Base
|
11
|
+
acts_as_nested_set
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license
|
@@ -0,0 +1 @@
|
|
1
|
+
ActiveRecord::Base.send :include, ActiveRecord::Acts::NestedSet
|
@@ -0,0 +1,210 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Acts #:nodoc:
|
3
|
+
module NestedSet #:nodoc:
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
# This +acts_as+ extension provides Nested Set functionality. Nested Set is similiar to Tree, but with
|
9
|
+
# the added feature that you can select the children and all of their descendents with
|
10
|
+
# a single query. A good use case for this is a threaded post system, where you want
|
11
|
+
# to display every reply to a comment without multiple selects.
|
12
|
+
#
|
13
|
+
# A Google search for "Nested Set" should point you to in the right direction to explain the
|
14
|
+
# database theory. I figured out a bunch of this from
|
15
|
+
# http://threebit.net/tutorials/nestedset/tutorial1.html
|
16
|
+
#
|
17
|
+
# Instead of picturing a leaf node structure with children pointing back to their parent,
|
18
|
+
# the best way to imagine how this works is to think of the parent entity surrounding all
|
19
|
+
# of its children, and its parent surrounding it, etc. Assuming that they are lined up
|
20
|
+
# horizontally, we store the left and right boundries in the database.
|
21
|
+
#
|
22
|
+
# Imagine:
|
23
|
+
# root
|
24
|
+
# |_ Child 1
|
25
|
+
# |_ Child 1.1
|
26
|
+
# |_ Child 1.2
|
27
|
+
# |_ Child 2
|
28
|
+
# |_ Child 2.1
|
29
|
+
# |_ Child 2.2
|
30
|
+
#
|
31
|
+
# If my cirlces in circles description didn't make sense, check out this sweet
|
32
|
+
# ASCII art:
|
33
|
+
#
|
34
|
+
# ___________________________________________________________________
|
35
|
+
# | Root |
|
36
|
+
# | ____________________________ ____________________________ |
|
37
|
+
# | | Child 1 | | Child 2 | |
|
38
|
+
# | | __________ _________ | | __________ _________ | |
|
39
|
+
# | | | C 1.1 | | C 1.2 | | | | C 2.1 | | C 2.2 | | |
|
40
|
+
# 1 2 3_________4 5________6 7 8 9_________10 11_______12 13 14
|
41
|
+
# | |___________________________| |___________________________| |
|
42
|
+
# |___________________________________________________________________|
|
43
|
+
#
|
44
|
+
# The numbers represent the left and right boundries. The table then might
|
45
|
+
# look like this:
|
46
|
+
# ID | PARENT | LEFT | RIGHT | DATA
|
47
|
+
# 1 | 0 | 1 | 14 | root
|
48
|
+
# 2 | 1 | 2 | 7 | Child 1
|
49
|
+
# 3 | 2 | 3 | 4 | Child 1.1
|
50
|
+
# 4 | 2 | 5 | 6 | Child 1.2
|
51
|
+
# 5 | 1 | 8 | 13 | Child 2
|
52
|
+
# 6 | 5 | 9 | 10 | Child 2.1
|
53
|
+
# 7 | 5 | 11 | 12 | Child 2.2
|
54
|
+
#
|
55
|
+
# So, to get all children of an entry, you
|
56
|
+
# SELECT * WHERE CHILD.LEFT IS BETWEEN PARENT.LEFT AND PARENT.RIGHT
|
57
|
+
#
|
58
|
+
# To get the count, it's <tt>(LEFT - RIGHT + 1)/2</tt>, etc.
|
59
|
+
#
|
60
|
+
# To get the direct parent, it falls back to using the +PARENT_ID+ field.
|
61
|
+
#
|
62
|
+
# There are instance methods for all of these.
|
63
|
+
#
|
64
|
+
# The structure is good if you need to group things together; the downside is that
|
65
|
+
# keeping data integrity is a pain, and both adding and removing an entry
|
66
|
+
# require a full table write.
|
67
|
+
#
|
68
|
+
# This sets up a +before_destroy+ callback to prune the tree correctly if one of its
|
69
|
+
# elements gets deleted.
|
70
|
+
#
|
71
|
+
module ClassMethods
|
72
|
+
# Configuration options are:
|
73
|
+
#
|
74
|
+
# * +parent_column+ - specifies the column name to use for keeping the position integer (default: +parent_id+)
|
75
|
+
# * +left_column+ - column name for left boundry data, default +lft+
|
76
|
+
# * +right_column+ - column name for right boundry data, default +rgt+
|
77
|
+
# * +scope+ - restricts what is to be considered a list. Given a symbol, it'll attach <tt>_id</tt>
|
78
|
+
# (if it hasn't already been added) and use that as the foreign key restriction. It's also possible
|
79
|
+
# to give it an entire string that is interpolated if you need a tighter scope than just a foreign key.
|
80
|
+
# Example: <tt>acts_as_nested_set :scope => 'todo_list_id = #{todo_list_id} AND completed = 0'</tt>
|
81
|
+
def acts_as_nested_set(options = {})
|
82
|
+
configuration = { :parent_column => "parent_id", :left_column => "lft", :right_column => "rgt", :scope => "1 = 1" }
|
83
|
+
|
84
|
+
configuration.update(options) if options.is_a?(Hash)
|
85
|
+
|
86
|
+
configuration[:scope] = "#{configuration[:scope]}_id".intern if configuration[:scope].is_a?(Symbol) && configuration[:scope].to_s !~ /_id$/
|
87
|
+
|
88
|
+
if configuration[:scope].is_a?(Symbol)
|
89
|
+
scope_condition_method = %(
|
90
|
+
def scope_condition
|
91
|
+
if #{configuration[:scope].to_s}.nil?
|
92
|
+
"#{configuration[:scope].to_s} IS NULL"
|
93
|
+
else
|
94
|
+
"#{configuration[:scope].to_s} = \#{#{configuration[:scope].to_s}}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
)
|
98
|
+
else
|
99
|
+
scope_condition_method = "def scope_condition() \"#{configuration[:scope]}\" end"
|
100
|
+
end
|
101
|
+
|
102
|
+
class_eval <<-EOV
|
103
|
+
include ActiveRecord::Acts::NestedSet::InstanceMethods
|
104
|
+
|
105
|
+
#{scope_condition_method}
|
106
|
+
|
107
|
+
def left_col_name() "#{configuration[:left_column]}" end
|
108
|
+
|
109
|
+
def right_col_name() "#{configuration[:right_column]}" end
|
110
|
+
|
111
|
+
def parent_column() "#{configuration[:parent_column]}" end
|
112
|
+
|
113
|
+
EOV
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
module InstanceMethods
|
118
|
+
# Returns +true+ is this is a root node.
|
119
|
+
def root?
|
120
|
+
parent_id = self[parent_column]
|
121
|
+
(parent_id == 0 || parent_id.nil?) && (self[left_col_name] == 1) && (self[right_col_name] > self[left_col_name])
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns +true+ is this is a child node
|
125
|
+
def child?
|
126
|
+
parent_id = self[parent_column]
|
127
|
+
!(parent_id == 0 || parent_id.nil?) && (self[left_col_name] > 1) && (self[right_col_name] > self[left_col_name])
|
128
|
+
end
|
129
|
+
|
130
|
+
# Returns +true+ if we have no idea what this is
|
131
|
+
def unknown?
|
132
|
+
!root? && !child?
|
133
|
+
end
|
134
|
+
|
135
|
+
# Adds a child to this object in the tree. If this object hasn't been initialized,
|
136
|
+
# it gets set up as a root node. Otherwise, this method will update all of the
|
137
|
+
# other elements in the tree and shift them to the right, keeping everything
|
138
|
+
# balanced.
|
139
|
+
def add_child( child )
|
140
|
+
self.reload
|
141
|
+
child.reload
|
142
|
+
|
143
|
+
if child.root?
|
144
|
+
raise "Adding sub-tree isn\'t currently supported"
|
145
|
+
else
|
146
|
+
if ( (self[left_col_name] == nil) || (self[right_col_name] == nil) )
|
147
|
+
# Looks like we're now the root node! Woo
|
148
|
+
self[left_col_name] = 1
|
149
|
+
self[right_col_name] = 4
|
150
|
+
|
151
|
+
# What do to do about validation?
|
152
|
+
return nil unless self.save
|
153
|
+
|
154
|
+
child[parent_column] = self.id
|
155
|
+
child[left_col_name] = 2
|
156
|
+
child[right_col_name]= 3
|
157
|
+
return child.save
|
158
|
+
else
|
159
|
+
# OK, we need to add and shift everything else to the right
|
160
|
+
child[parent_column] = self.id
|
161
|
+
right_bound = self[right_col_name]
|
162
|
+
child[left_col_name] = right_bound
|
163
|
+
child[right_col_name] = right_bound + 1
|
164
|
+
self[right_col_name] += 2
|
165
|
+
self.class.base_class.transaction {
|
166
|
+
self.class.base_class.update_all( "#{left_col_name} = (#{left_col_name} + 2)", "#{scope_condition} AND #{left_col_name} >= #{right_bound}" )
|
167
|
+
self.class.base_class.update_all( "#{right_col_name} = (#{right_col_name} + 2)", "#{scope_condition} AND #{right_col_name} >= #{right_bound}" )
|
168
|
+
self.save
|
169
|
+
child.save
|
170
|
+
}
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Returns the number of nested children of this object.
|
176
|
+
def children_count
|
177
|
+
return (self[right_col_name] - self[left_col_name] - 1)/2
|
178
|
+
end
|
179
|
+
|
180
|
+
# Returns a set of itself and all of its nested children
|
181
|
+
def full_set
|
182
|
+
self.class.base_class.find(:all, :conditions => "#{scope_condition} AND (#{left_col_name} BETWEEN #{self[left_col_name]} and #{self[right_col_name]})" )
|
183
|
+
end
|
184
|
+
|
185
|
+
# Returns a set of all of its children and nested children
|
186
|
+
def all_children
|
187
|
+
self.class.base_class.find(:all, :conditions => "#{scope_condition} AND (#{left_col_name} > #{self[left_col_name]}) and (#{right_col_name} < #{self[right_col_name]})" )
|
188
|
+
end
|
189
|
+
|
190
|
+
# Returns a set of only this entry's immediate children
|
191
|
+
def direct_children
|
192
|
+
self.class.base_class.find(:all, :conditions => "#{scope_condition} and #{parent_column} = #{self.id}", :order => left_col_name)
|
193
|
+
end
|
194
|
+
|
195
|
+
# Prunes a branch off of the tree, shifting all of the elements on the right
|
196
|
+
# back to the left so the counts still work.
|
197
|
+
def before_destroy
|
198
|
+
return if self[right_col_name].nil? || self[left_col_name].nil?
|
199
|
+
dif = self[right_col_name] - self[left_col_name] + 1
|
200
|
+
|
201
|
+
self.class.base_class.transaction {
|
202
|
+
self.class.base_class.delete_all( "#{scope_condition} and #{left_col_name} > #{self[left_col_name]} and #{right_col_name} < #{self[right_col_name]}" )
|
203
|
+
self.class.base_class.update_all( "#{left_col_name} = (#{left_col_name} - #{dif})", "#{scope_condition} AND #{left_col_name} >= #{self[right_col_name]}" )
|
204
|
+
self.class.base_class.update_all( "#{right_col_name} = (#{right_col_name} - #{dif} )", "#{scope_condition} AND #{right_col_name} >= #{self[right_col_name]}" )
|
205
|
+
}
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
@@ -0,0 +1,269 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'active_record'
|
5
|
+
|
6
|
+
$:.unshift File.dirname(__FILE__) + '/../lib'
|
7
|
+
require File.dirname(__FILE__) + '/../init'
|
8
|
+
|
9
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
|
10
|
+
|
11
|
+
class Mixin < ActiveRecord::Base
|
12
|
+
end
|
13
|
+
|
14
|
+
class NestedSet < Mixin
|
15
|
+
acts_as_nested_set :scope => "root_id IS NULL"
|
16
|
+
|
17
|
+
def self.table_name() "mixins" end
|
18
|
+
end
|
19
|
+
|
20
|
+
class NestedSetWithStringScope < Mixin
|
21
|
+
acts_as_nested_set :scope => 'root_id = #{root_id}'
|
22
|
+
|
23
|
+
def self.table_name() "mixins" end
|
24
|
+
end
|
25
|
+
|
26
|
+
class NestedSetWithSymbolScope < Mixin
|
27
|
+
acts_as_nested_set :scope => :root
|
28
|
+
|
29
|
+
def self.table_name() "mixins" end
|
30
|
+
end
|
31
|
+
|
32
|
+
class NestedSetSuperclass < Mixin
|
33
|
+
acts_as_nested_set :scope => :root
|
34
|
+
|
35
|
+
def self.table_name() "mixins" end
|
36
|
+
end
|
37
|
+
|
38
|
+
class NestedSetSubclass < NestedSetSuperclass
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
class MixinNestedSetTest < Test::Unit::TestCase
|
43
|
+
|
44
|
+
def setup
|
45
|
+
ActiveRecord::Schema.define(:version => 1) do
|
46
|
+
create_table :mixins do |t|
|
47
|
+
t.integer :pos, :parent_id, :lft, :rgt, :root_id
|
48
|
+
t.timestamps
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
(1..10).each { |counter| NestedSet.create! }
|
53
|
+
|
54
|
+
[ [0, 1, 10, NestedSetSuperclass],
|
55
|
+
[11, 2, 5, NestedSetSubclass],
|
56
|
+
[12, 3, 4, NestedSetSuperclass],
|
57
|
+
[11, 6, 9, NestedSetSuperclass],
|
58
|
+
[13, 7, 8, NestedSetSubclass]
|
59
|
+
].each do |sti|
|
60
|
+
sti[3].create! :parent_id => sti[0], :lft => sti[1], :rgt => sti[2], :root_id => 3100
|
61
|
+
end
|
62
|
+
|
63
|
+
[ [0, 1, 20],
|
64
|
+
[16, 2, 7],
|
65
|
+
[17, 3, 4],
|
66
|
+
[17, 5, 6],
|
67
|
+
[16, 14, 13],
|
68
|
+
[20, 9, 10],
|
69
|
+
[20, 11, 12],
|
70
|
+
[16, 8, 19],
|
71
|
+
[23, 15, 16],
|
72
|
+
[23, 17, 18]
|
73
|
+
].each do |set|
|
74
|
+
NestedSetWithStringScope.create! :parent_id => set[0], :lft => set[1], :rgt => set[2], :root_id => 42
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def teardown
|
79
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
80
|
+
ActiveRecord::Base.connection.drop_table(table)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_mixing_in_methods
|
85
|
+
ns = NestedSet.new
|
86
|
+
assert(ns.respond_to?(:all_children))
|
87
|
+
assert_equal(ns.scope_condition, "root_id IS NULL")
|
88
|
+
check_method_mixins ns
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_string_scope
|
92
|
+
ns = NestedSetWithStringScope.new
|
93
|
+
ns.root_id = 1
|
94
|
+
assert_equal(ns.scope_condition, "root_id = 1")
|
95
|
+
ns.root_id = 42
|
96
|
+
assert_equal(ns.scope_condition, "root_id = 42")
|
97
|
+
check_method_mixins ns
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_symbol_scope
|
101
|
+
ns = NestedSetWithSymbolScope.new
|
102
|
+
ns.root_id = 1
|
103
|
+
assert_equal(ns.scope_condition, "root_id = 1")
|
104
|
+
ns.root_id = 42
|
105
|
+
assert_equal(ns.scope_condition, "root_id = 42")
|
106
|
+
check_method_mixins ns
|
107
|
+
end
|
108
|
+
|
109
|
+
def check_method_mixins(obj)
|
110
|
+
[:scope_condition, :left_col_name, :right_col_name, :parent_column, :root?, :add_child,
|
111
|
+
:children_count, :full_set, :all_children, :direct_children].each { |symbol| assert(obj.respond_to?(symbol)) }
|
112
|
+
end
|
113
|
+
|
114
|
+
def set(id)
|
115
|
+
NestedSet.find(id)
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_adding_children
|
119
|
+
assert(set(1).unknown?)
|
120
|
+
assert(set(2).unknown?)
|
121
|
+
set(1).add_child set(2)
|
122
|
+
|
123
|
+
# Did we maintain adding the parent_ids?
|
124
|
+
assert(set(1).root?)
|
125
|
+
assert(set(2).child?)
|
126
|
+
assert(set(2).parent_id == set(1).id)
|
127
|
+
|
128
|
+
# Check boundies
|
129
|
+
assert_equal(set(1).lft, 1)
|
130
|
+
assert_equal(set(2).lft, 2)
|
131
|
+
assert_equal(set(2).rgt, 3)
|
132
|
+
assert_equal(set(1).rgt, 4)
|
133
|
+
|
134
|
+
# Check children cound
|
135
|
+
assert_equal(set(1).children_count, 1)
|
136
|
+
|
137
|
+
set(1).add_child set(3)
|
138
|
+
|
139
|
+
#check boundries
|
140
|
+
assert_equal(set(1).lft, 1)
|
141
|
+
assert_equal(set(2).lft, 2)
|
142
|
+
assert_equal(set(2).rgt, 3)
|
143
|
+
assert_equal(set(3).lft, 4)
|
144
|
+
assert_equal(set(3).rgt, 5)
|
145
|
+
assert_equal(set(1).rgt, 6)
|
146
|
+
|
147
|
+
# How is the count looking?
|
148
|
+
assert_equal(set(1).children_count, 2)
|
149
|
+
|
150
|
+
set(2).add_child set(4)
|
151
|
+
|
152
|
+
# boundries
|
153
|
+
assert_equal(set(1).lft, 1)
|
154
|
+
assert_equal(set(2).lft, 2)
|
155
|
+
assert_equal(set(4).lft, 3)
|
156
|
+
assert_equal(set(4).rgt, 4)
|
157
|
+
assert_equal(set(2).rgt, 5)
|
158
|
+
assert_equal(set(3).lft, 6)
|
159
|
+
assert_equal(set(3).rgt, 7)
|
160
|
+
assert_equal(set(1).rgt, 8)
|
161
|
+
|
162
|
+
# Children count
|
163
|
+
assert_equal(set(1).children_count, 3)
|
164
|
+
assert_equal(set(2).children_count, 1)
|
165
|
+
assert_equal(set(3).children_count, 0)
|
166
|
+
assert_equal(set(4).children_count, 0)
|
167
|
+
|
168
|
+
set(2).add_child set(5)
|
169
|
+
set(4).add_child set(6)
|
170
|
+
|
171
|
+
assert_equal(set(2).children_count, 3)
|
172
|
+
|
173
|
+
|
174
|
+
# Children accessors
|
175
|
+
assert_equal(set(1).full_set.length, 6)
|
176
|
+
assert_equal(set(2).full_set.length, 4)
|
177
|
+
assert_equal(set(4).full_set.length, 2)
|
178
|
+
|
179
|
+
assert_equal(set(1).all_children.length, 5)
|
180
|
+
assert_equal(set(6).all_children.length, 0)
|
181
|
+
|
182
|
+
assert_equal(set(1).direct_children.length, 2)
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
def test_snipping_tree
|
187
|
+
big_tree = NestedSetWithStringScope.find(16)
|
188
|
+
|
189
|
+
# Make sure we have the right one
|
190
|
+
assert_equal(3, big_tree.direct_children.length)
|
191
|
+
assert_equal(10, big_tree.full_set.length)
|
192
|
+
assert_equal [17, 23, 20], big_tree.direct_children.map(&:id)
|
193
|
+
|
194
|
+
NestedSetWithStringScope.find(20).destroy
|
195
|
+
|
196
|
+
big_tree = NestedSetWithStringScope.find(16)
|
197
|
+
|
198
|
+
assert_equal(9, big_tree.full_set.length)
|
199
|
+
assert_equal(2, big_tree.direct_children.length)
|
200
|
+
|
201
|
+
assert_equal(1, NestedSetWithStringScope.find(16).lft)
|
202
|
+
assert_equal(2, NestedSetWithStringScope.find(17).lft)
|
203
|
+
assert_equal(3, NestedSetWithStringScope.find(18).lft)
|
204
|
+
assert_equal(4, NestedSetWithStringScope.find(18).rgt)
|
205
|
+
assert_equal(5, NestedSetWithStringScope.find(19).lft)
|
206
|
+
assert_equal(6, NestedSetWithStringScope.find(19).rgt)
|
207
|
+
assert_equal(7, NestedSetWithStringScope.find(17).rgt)
|
208
|
+
assert_equal(8, NestedSetWithStringScope.find(23).lft)
|
209
|
+
assert_equal(15, NestedSetWithStringScope.find(24).lft)
|
210
|
+
assert_equal(16, NestedSetWithStringScope.find(24).rgt)
|
211
|
+
assert_equal(17, NestedSetWithStringScope.find(25).lft)
|
212
|
+
assert_equal(18, NestedSetWithStringScope.find(25).rgt)
|
213
|
+
assert_equal(19, NestedSetWithStringScope.find(23).rgt)
|
214
|
+
assert_equal(20, NestedSetWithStringScope.find(16).rgt)
|
215
|
+
end
|
216
|
+
|
217
|
+
def test_deleting_root
|
218
|
+
NestedSetWithStringScope.find(16).destroy
|
219
|
+
assert_equal 15, NestedSetWithStringScope.count
|
220
|
+
end
|
221
|
+
|
222
|
+
def test_common_usage
|
223
|
+
NestedSet.find(1).add_child(NestedSet.find(2))
|
224
|
+
assert_equal(1, NestedSet.find(1).direct_children.length)
|
225
|
+
|
226
|
+
NestedSet.find(2).add_child(NestedSet.find(3))
|
227
|
+
assert_equal(1, NestedSet.find(1).direct_children.length)
|
228
|
+
|
229
|
+
# Local cache is now out of date!
|
230
|
+
# Problem: the update_alls update all objects up the tree
|
231
|
+
assert_equal(2, NestedSet.find(1).all_children.length)
|
232
|
+
|
233
|
+
assert_equal(1, NestedSet.find(1).lft)
|
234
|
+
assert_equal(2, NestedSet.find(2).lft)
|
235
|
+
assert_equal(3, NestedSet.find(3).lft)
|
236
|
+
assert_equal(4, NestedSet.find(3).rgt)
|
237
|
+
assert_equal(5, NestedSet.find(2).rgt)
|
238
|
+
assert_equal(6, NestedSet.find(1).rgt)
|
239
|
+
|
240
|
+
assert(NestedSet.find(1).root?)
|
241
|
+
|
242
|
+
begin
|
243
|
+
NestedSet.find(4).add_child(NestedSet.find(1))
|
244
|
+
fail
|
245
|
+
rescue
|
246
|
+
end
|
247
|
+
|
248
|
+
assert_equal(2, NestedSet.find(1).all_children.length)
|
249
|
+
|
250
|
+
NestedSet.find(1).add_child NestedSet.find(4)
|
251
|
+
|
252
|
+
assert_equal(3, NestedSet.find(1).all_children.length)
|
253
|
+
end
|
254
|
+
|
255
|
+
def test_inheritance
|
256
|
+
parent = NestedSetWithStringScope.find(11)
|
257
|
+
child = NestedSetWithStringScope.find(12)
|
258
|
+
grandchild = NestedSetWithStringScope.find(13)
|
259
|
+
assert_equal 5, parent.full_set.size
|
260
|
+
assert_equal 2, child.full_set.size
|
261
|
+
assert_equal 4, parent.all_children.size
|
262
|
+
assert_equal 1, child.all_children.size
|
263
|
+
assert_equal 2, parent.direct_children.size
|
264
|
+
assert_equal 1, child.direct_children.size
|
265
|
+
child.destroy
|
266
|
+
assert_equal 3, parent.full_set.size
|
267
|
+
end
|
268
|
+
|
269
|
+
end
|