parlement 0.14 → 0.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (205) hide show
  1. data/CHANGES +41 -1
  2. data/MEMORY +66 -5
  3. data/README +10 -5
  4. data/Rakefile +15 -23
  5. data/app/controllers/account_controller.rb +48 -43
  6. data/app/controllers/{application.rb → application_controller.rb} +15 -12
  7. data/app/controllers/elt_controller.rb +77 -32
  8. data/app/controllers/subscriber_controller.rb +11 -10
  9. data/app/helpers/application_helper.rb +14 -1
  10. data/app/helpers/elt_helper.rb +9 -7
  11. data/app/models/elt.rb +25 -24
  12. data/app/models/mail.rb +44 -47
  13. data/app/models/person_notify.rb +2 -2
  14. data/app/models/user.rb +128 -2
  15. data/app/models/user_notify.rb +15 -15
  16. data/app/views/account/_login.rhtml +39 -39
  17. data/app/views/account/_show.rhtml +22 -30
  18. data/app/views/account/signup.rhtml +2 -2
  19. data/app/views/elt/_choice.rhtml +6 -6
  20. data/app/views/elt/_elt.rhtml +27 -32
  21. data/app/views/elt/choices.rhtml +16 -18
  22. data/app/views/elt/list/_byDate.rhtml +14 -14
  23. data/app/views/elt/list/_byVote.rhtml +15 -15
  24. data/app/views/elt/list/_children.rhtml +48 -40
  25. data/app/views/elt/list/_subscribers.rhtml +1 -1
  26. data/app/views/elt/new.rhtml +22 -21
  27. data/app/views/elt/rss.rxml +4 -11
  28. data/app/views/elt/show.rhtml +65 -61
  29. data/app/views/elt/vote_rss.rxml +4 -11
  30. data/app/views/layouts/top.rhtml +39 -50
  31. data/app/views/person/_listElts.rhtml +1 -1
  32. data/app/views/person/show.rhtml +1 -1
  33. data/{vendor/plugins/login_engine/app → app}/views/user_notify/change_password.rhtml +0 -0
  34. data/{vendor/plugins/login_engine/app → app}/views/user_notify/delete.rhtml +0 -0
  35. data/{vendor/plugins/login_engine/app → app}/views/user_notify/forgot_password.rhtml +0 -0
  36. data/{vendor/plugins/login_engine/app → app}/views/user_notify/pending_delete.rhtml +0 -0
  37. data/{vendor/plugins/login_engine/app → app}/views/user_notify/signup.rhtml +0 -0
  38. data/config/boot.rb +97 -32
  39. data/config/environment.rb +37 -35
  40. data/config/environments/development.rb +2 -3
  41. data/config/environments/production.rb +3 -0
  42. data/config/initializers/string_ruby_1.8.rb +10 -0
  43. data/config/routes.rb +17 -22
  44. data/db/schema.rb +102 -74
  45. data/lib/tasks/rspec.rake +167 -0
  46. data/public/404.html +25 -7
  47. data/public/500.html +26 -7
  48. data/public/dispatch.cgi +0 -0
  49. data/public/dispatch.fcgi +0 -0
  50. data/public/dispatch.rb +0 -0
  51. data/public/images/live_tree_branch_collapsed_icon.gif +0 -0
  52. data/public/images/live_tree_branch_expanded_icon.gif +0 -0
  53. data/public/images/live_tree_leaf_icon.gif +0 -0
  54. data/public/javascripts/application.js +258 -0
  55. data/public/javascripts/controls.js +544 -414
  56. data/public/javascripts/dragdrop.js +229 -198
  57. data/public/javascripts/effects.js +499 -459
  58. data/public/javascripts/prototype.js +2926 -1121
  59. data/public/javascripts/shadedborder.js +68 -50
  60. data/public/stylesheets/default.css +34 -34
  61. data/public/stylesheets/live_tree.css +0 -0
  62. data/public/stylesheets/scaffold.css +6 -6
  63. data/script/about +0 -0
  64. data/script/autospec +6 -0
  65. data/script/benchmarker +0 -0
  66. data/script/breakpointer +0 -0
  67. data/script/console +0 -0
  68. data/script/dbconsole +3 -0
  69. data/script/destroy +0 -0
  70. data/script/generate +0 -0
  71. data/script/plugin +0 -0
  72. data/script/profiler +0 -0
  73. data/script/runner +0 -0
  74. data/script/server +0 -0
  75. data/script/spec +10 -0
  76. data/script/spec_server +9 -0
  77. data/test/unit/attachment_test.rb +4 -4
  78. data/test/unit/choice_test.rb +1 -1
  79. data/test/unit/elt_test.rb +9 -9
  80. data/test/unit/mail_notify_test.rb +2 -2
  81. data/test/unit/mail_test.rb +18 -11
  82. data/test/unit/person_notify_test.rb +1 -1
  83. data/test/unit/person_test.rb +1 -1
  84. data/test/unit/subscriber_test.rb +1 -1
  85. data/test/unit/user_test.rb +81 -0
  86. data/test/unit/visit_test.rb +6 -6
  87. data/vendor/plugins/activerecord_foreign_key_extensions/init.rb +2 -0
  88. data/vendor/plugins/activerecord_foreign_key_extensions/lib/active_record_extensions.rb +182 -0
  89. data/vendor/plugins/activerecord_text_id_extensions/init.rb +2 -0
  90. data/vendor/plugins/activerecord_text_id_extensions/lib/active_record_extensions.rb +24 -0
  91. data/vendor/plugins/acts_as_nested_set/README +15 -0
  92. data/vendor/plugins/acts_as_nested_set/init.rb +1 -0
  93. data/vendor/plugins/acts_as_nested_set/lib/active_record/acts/nested_set.rb +210 -0
  94. data/vendor/plugins/acts_as_nested_set/test/nested_set_test.rb +269 -0
  95. data/vendor/plugins/acts_as_tree/README +26 -0
  96. data/vendor/plugins/acts_as_tree/Rakefile +22 -0
  97. data/vendor/plugins/acts_as_tree/init.rb +1 -0
  98. data/vendor/plugins/acts_as_tree/lib/active_record/acts/tree.rb +96 -0
  99. data/vendor/plugins/{output_compression/CHANGELOG → acts_as_tree/test/abstract_unit.rb} +0 -0
  100. data/vendor/plugins/acts_as_tree/test/acts_as_tree_test.rb +219 -0
  101. data/vendor/plugins/acts_as_tree/test/database.yml +0 -0
  102. data/vendor/plugins/acts_as_tree/test/fixtures/mixin.rb +0 -0
  103. data/vendor/plugins/acts_as_tree/test/fixtures/mixins.yml +0 -0
  104. data/vendor/plugins/acts_as_tree/test/schema.rb +0 -0
  105. data/vendor/plugins/classic_pagination/CHANGELOG +152 -0
  106. data/vendor/plugins/classic_pagination/README +18 -0
  107. data/vendor/plugins/{output_compression/rakefile → classic_pagination/Rakefile} +22 -22
  108. data/vendor/plugins/classic_pagination/init.rb +33 -0
  109. data/vendor/plugins/classic_pagination/install.rb +1 -0
  110. data/vendor/plugins/classic_pagination/lib/pagination.rb +405 -0
  111. data/vendor/plugins/classic_pagination/lib/pagination_helper.rb +135 -0
  112. data/vendor/plugins/classic_pagination/test/fixtures/companies.yml +24 -0
  113. data/vendor/plugins/classic_pagination/test/fixtures/company.rb +9 -0
  114. data/vendor/plugins/classic_pagination/test/fixtures/developer.rb +7 -0
  115. data/vendor/plugins/classic_pagination/test/fixtures/developers.yml +21 -0
  116. data/vendor/plugins/classic_pagination/test/fixtures/developers_projects.yml +13 -0
  117. data/vendor/plugins/classic_pagination/test/fixtures/project.rb +3 -0
  118. data/vendor/plugins/classic_pagination/test/fixtures/projects.yml +7 -0
  119. data/vendor/plugins/classic_pagination/test/fixtures/replies.yml +13 -0
  120. data/vendor/plugins/classic_pagination/test/fixtures/reply.rb +5 -0
  121. data/vendor/plugins/classic_pagination/test/fixtures/schema.sql +42 -0
  122. data/vendor/plugins/classic_pagination/test/fixtures/topic.rb +3 -0
  123. data/vendor/plugins/classic_pagination/test/fixtures/topics.yml +22 -0
  124. data/vendor/plugins/classic_pagination/test/helper.rb +117 -0
  125. data/vendor/plugins/classic_pagination/test/pagination_helper_test.rb +38 -0
  126. data/vendor/plugins/classic_pagination/test/pagination_test.rb +177 -0
  127. data/vendor/plugins/file_column/lib/file_column.rb +1 -1
  128. data/vendor/plugins/file_column/test/file_column_test.rb +0 -0
  129. metadata +151 -197
  130. data/app/helpers/live_tree.rb +0 -238
  131. data/app/views/elt/_form.rhtml +0 -31
  132. data/app/views/elt/show_tree.rhtml +0 -8
  133. data/config/environments/user_environment.rb +0 -1
  134. data/db/ROOT/Titemagli.txt +0 -3
  135. data/db/ROOT/titemagli.txt +0 -9
  136. data/public/javascripts/behaviour.js +0 -254
  137. data/public/javascripts/ie7-load.htc +0 -1
  138. data/public/javascripts/ie7.js +0 -6
  139. data/public/javascripts/live_tree.js +0 -749
  140. data/public/javascripts/mybehaviour.js +0 -225
  141. data/public/javascripts/scriptaculous.js +0 -47
  142. data/public/javascripts/slider.js +0 -283
  143. data/public/stylesheets/blue.css +0 -471
  144. data/vendor/plugins/engines/CHANGELOG +0 -241
  145. data/vendor/plugins/engines/MIT-LICENSE +0 -21
  146. data/vendor/plugins/engines/README +0 -64
  147. data/vendor/plugins/engines/Rakefile +0 -32
  148. data/vendor/plugins/engines/UPGRADING +0 -93
  149. data/vendor/plugins/engines/about.yml +0 -7
  150. data/vendor/plugins/engines/generators/plugin_migration/USAGE +0 -45
  151. data/vendor/plugins/engines/generators/plugin_migration/plugin_migration_generator.rb +0 -79
  152. data/vendor/plugins/engines/generators/plugin_migration/templates/plugin_migration.erb +0 -13
  153. data/vendor/plugins/engines/init.rb +0 -40
  154. data/vendor/plugins/engines/install.rb +0 -32
  155. data/vendor/plugins/engines/lib/engines.rb +0 -323
  156. data/vendor/plugins/engines/lib/engines/deprecated_config_support.rb +0 -135
  157. data/vendor/plugins/engines/lib/engines/plugin.rb +0 -214
  158. data/vendor/plugins/engines/lib/engines/plugin_list.rb +0 -31
  159. data/vendor/plugins/engines/lib/engines/plugin_migrator.rb +0 -60
  160. data/vendor/plugins/engines/lib/engines/rails_extensions.rb +0 -6
  161. data/vendor/plugins/engines/lib/engines/rails_extensions/active_record.rb +0 -19
  162. data/vendor/plugins/engines/lib/engines/rails_extensions/dependencies.rb +0 -143
  163. data/vendor/plugins/engines/lib/engines/rails_extensions/migrations.rb +0 -155
  164. data/vendor/plugins/engines/lib/engines/rails_extensions/public_asset_helpers.rb +0 -116
  165. data/vendor/plugins/engines/lib/engines/rails_extensions/rails.rb +0 -20
  166. data/vendor/plugins/engines/lib/engines/rails_extensions/rails_initializer.rb +0 -86
  167. data/vendor/plugins/engines/lib/engines/rails_extensions/routing.rb +0 -77
  168. data/vendor/plugins/engines/lib/engines/rails_extensions/templates.rb +0 -140
  169. data/vendor/plugins/engines/lib/engines/testing.rb +0 -87
  170. data/vendor/plugins/engines/tasks/engines.rake +0 -149
  171. data/vendor/plugins/login_engine/CHANGELOG +0 -22
  172. data/vendor/plugins/login_engine/README +0 -344
  173. data/vendor/plugins/login_engine/app/controllers/user_controller.rb +0 -262
  174. data/vendor/plugins/login_engine/app/helpers/user_helper.rb +0 -88
  175. data/vendor/plugins/login_engine/app/models/user.rb +0 -7
  176. data/vendor/plugins/login_engine/app/models/user_notify.rb +0 -75
  177. data/vendor/plugins/login_engine/app/views/user/_edit.rhtml +0 -11
  178. data/vendor/plugins/login_engine/app/views/user/_password.rhtml +0 -9
  179. data/vendor/plugins/login_engine/app/views/user/change_password.rhtml +0 -17
  180. data/vendor/plugins/login_engine/app/views/user/edit.rhtml +0 -23
  181. data/vendor/plugins/login_engine/app/views/user/forgot_password.rhtml +0 -18
  182. data/vendor/plugins/login_engine/app/views/user/home.rhtml +0 -7
  183. data/vendor/plugins/login_engine/app/views/user/login.rhtml +0 -17
  184. data/vendor/plugins/login_engine/app/views/user/logout.rhtml +0 -8
  185. data/vendor/plugins/login_engine/app/views/user/signup.rhtml +0 -17
  186. data/vendor/plugins/login_engine/db/migrate/001_initial_schema.rb +0 -25
  187. data/vendor/plugins/login_engine/init_engine.rb +0 -11
  188. data/vendor/plugins/login_engine/install.rb +0 -4
  189. data/vendor/plugins/login_engine/lib/login_engine.rb +0 -62
  190. data/vendor/plugins/login_engine/lib/login_engine/authenticated_system.rb +0 -113
  191. data/vendor/plugins/login_engine/lib/login_engine/authenticated_user.rb +0 -155
  192. data/vendor/plugins/login_engine/public/stylesheets/login_engine.css +0 -81
  193. data/vendor/plugins/login_engine/test/fixtures/users.yml +0 -41
  194. data/vendor/plugins/login_engine/test/functional/user_controller_test.rb +0 -536
  195. data/vendor/plugins/login_engine/test/mocks/mail.rb +0 -14
  196. data/vendor/plugins/login_engine/test/mocks/time.rb +0 -19
  197. data/vendor/plugins/login_engine/test/test_helper.rb +0 -11
  198. data/vendor/plugins/login_engine/test/unit/user_test.rb +0 -114
  199. data/vendor/plugins/output_compression/MIT-LICENSE +0 -20
  200. data/vendor/plugins/output_compression/README +0 -4
  201. data/vendor/plugins/output_compression/init.rb +0 -1
  202. data/vendor/plugins/output_compression/lib/output_compression.rb +0 -84
  203. data/vendor/plugins/output_compression/test/output_test.rb +0 -11
  204. data/vendor/plugins/output_compression/test/test_controller.rb +0 -3
  205. data/vendor/plugins/output_compression/test/test_helper.rb +0 -14
@@ -0,0 +1,2 @@
1
+ require 'active_record/schema_dumper'
2
+ require 'active_record_extensions'
@@ -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