sortable_nested_set 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -1,32 +1,44 @@
1
- jQuery is required.
1
+ = Sortable Nested Set
2
2
 
3
- In the Gemfile
4
- gem 'sortable_nested_set'
3
+ == Requirements
5
4
 
6
- In each "Category" model (e.g. "Directory"):
7
- acts_as_sortable_nested_set_of :items
8
- where :items is the pluralized Item model (e.g. "File").
5
+ jQuery is required.
9
6
 
10
- In each "Category" controller:
11
- handles_sortable_nested_set
12
- to include menu actions.
7
+ == Usage
13
8
 
14
- In the routes config file:
15
- sortable_nested_set_for :categories
16
- for each pluralized "Category" model.
9
+ Add the gem to your Gemfile.
17
10
 
18
- In each "Category" migration:
19
- name:string
11
+ gem 'sortable_nested_set'
12
+
13
+ In each "Category" (e.g. "Directory") migration:
20
14
  lft:integer
21
15
  rgt:integer
16
+ depth:integer [optional]
22
17
 
23
- In each "Item" migration:
24
- position:integer
18
+ In the routing configuration file:
19
+ nest the "Category" and "Item" resources
20
+
21
+ In each "Category" model:
22
+ acts_as_sortable_nested_set_of :items
23
+ where :items is the pluralized, underscored "Item" model (e.g. "ImageFile"=>:image_files).
24
+
25
+ In the "Category" and "Item" controllers:
26
+ [new] use param[:category_id]
27
+ [destroy] respond_to format.js (and provide associated view)
28
+
29
+ In the "Category" and "Items" models:
30
+ provide #to_s.
25
31
 
26
32
  In the <head> section of the layout(s) where the trees will be displayed:
27
33
  yield(:head)
34
+ after jQuery is loaded.
35
+
36
+ == Support for acts_as_list
37
+
38
+ When an "Item" model acts_as_list, SortableNestedSet will use its position for sorting.
39
+
40
+ == Helpers
28
41
 
29
- Helpers:
30
42
  sns_options
31
43
  sns_lineage
32
- sortable_nested_set_tree
44
+ sns_tree
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.3
1
+ 0.3.0
@@ -0,0 +1,35 @@
1
+ class SortableNestedSetController < ActionController::Base
2
+ def sns_sort
3
+ category_class = params[:category_class].classify.constantize
4
+
5
+ matches = params[:moved].match(/(\w+)_(\d+)$/)
6
+ moved_type = matches[1]
7
+ moved_id = matches[2].to_i
8
+ moved_parent = nil
9
+ tree = {} # category_id => [children of move_type]
10
+
11
+ for node in params[:tree].scan(/(\w+)\[(\d+)\]=(\w+)/)
12
+ type = node[0]
13
+ next unless type == moved_type
14
+
15
+ id = node[1].to_i
16
+ parent = (node[2] == 'root') ? nil : node[2].to_i
17
+
18
+ moved_parent = parent if id == moved_id
19
+
20
+ tree[parent] ||= []
21
+ tree[parent] << id
22
+ end
23
+
24
+ if moved_type == category_class.sns_items_type.to_s.singularize
25
+ item = category_class.sns_items_type.to_s.classify.constantize.find(moved_id)
26
+ item.remove_from_list if item.respond_to?(:remove_from_list) # acts_as_list support
27
+ item.update_attribute(category_class.name.foreign_key, moved_parent)
28
+ item.insert_at(tree[moved_parent].index(moved_id)) if item.respond_to?(:insert_at) # acts_as_list support
29
+ else
30
+ moved_type.classify.constantize.find(moved_id).move(moved_parent, tree[moved_parent])
31
+ end
32
+
33
+ render :nothing => true
34
+ end
35
+ end
@@ -1,5 +1,5 @@
1
1
  module SortableNestedSetHelper
2
- def sortable_nested_set_tree(class_or_category)
2
+ def sns_tree(class_or_category)
3
3
  if class_or_category.is_a?(Class)
4
4
  @category_class = class_or_category
5
5
  @categories = @category_class.includes(@category_class.sns_items_type)
@@ -8,8 +8,7 @@ module SortableNestedSetHelper
8
8
  @categories = class_or_category.self_and_descendants.includes(@category_class.sns_items_type)
9
9
  end
10
10
 
11
- sns_sort_path = "sns_sort_#{@category_class.name.underscore.pluralize}_path"
12
- render 'sortable_nested_set/tree', :categories => @categories.arrange, :sns_sort_path => sns_sort_path
11
+ render 'sortable_nested_set/tree', :category_class => @category_class, :categories => @categories.arrange
13
12
  end
14
13
 
15
14
  def sns_lineage(category, options={})
@@ -27,8 +26,6 @@ module SortableNestedSetHelper
27
26
  end
28
27
 
29
28
  def sns_options(category_class, mover=nil)
30
- sorted_nested_set_options(category_class, lambda(&:left_column_name), mover) do |category, level|
31
- '.' * level + category.name
32
- end
29
+ sorted_nested_set_options(category_class, lambda(&:left_column_name), mover) { |cat, lvl| "#{'.'*lvl}#{cat}" }
33
30
  end
34
31
  end
@@ -1,20 +1,14 @@
1
- <% cpath = category.class.name.underscore + '_path' %>
2
-
3
1
  <div>
4
2
  <span class="toggle open"></span>
5
3
  <span class="handle"><%= category %></span>
6
4
  <%= link_to image_tag('mega_menu.png'), category, :class => :mega_menu %>
7
5
  <div class="mega_menu_links">
8
6
  <%= link_to_unless_current 'show', category %> |
9
- <%= link_to 'edit', send("edit_#{cpath}", category) %> |
10
- <%= link_to 'remove',
11
- send("sns_destroy_category_#{cpath}", category),
12
- :confirm => 'Are you sure?',
13
- :method => :delete,
14
- :remote => !current_page?(send(cpath, category)) %> |
7
+ <%= link_to 'edit', polymorphic_path(category, :action => :edit) %> |
8
+ <%= link_to 'remove', category, :confirm => 'Are you sure?', :method => :delete, :remote => !current_page?(category) %> |
15
9
  <%= link_to "add #{category.class.sns_subcategories_type.to_s.singularize}",
16
- send("sns_add_subcategory_#{cpath}", category) %> |
10
+ polymorphic_path([category, category.class], :action => :new) %> |
17
11
  <%= link_to "add #{category.class.sns_items_type.to_s.singularize}",
18
- send("sns_add_item_#{cpath}", category) %>
12
+ polymorphic_path([category, category.class.sns_items_class], :action => :new) %>
19
13
  </div>
20
14
  </div>
@@ -4,12 +4,8 @@
4
4
  <%= link_to image_tag('mega_menu.png'), item, :class => :mega_menu %>
5
5
  <div class="mega_menu_links">
6
6
  <%= link_to 'show', item %> |
7
- <%= link_to 'edit', send("edit_#{item.class.name.underscore}_path", item) %> |
8
- <%= link_to 'remove',
9
- send("sns_destroy_item_#{item.class.name.underscore}_path", item),
10
- :confirm => 'Are you sure?',
11
- :method => :delete,
12
- :remote => true %>
7
+ <%= link_to 'edit', polymorphic_path(item, :action => :edit) %> |
8
+ <%= link_to 'remove', item, :confirm => 'Are you sure?', :method => :delete, :remote => true %>
13
9
  </div>
14
10
  </div>
15
11
  </li>
@@ -5,14 +5,12 @@
5
5
  'sortable_nested_set' %>
6
6
  <% end %>
7
7
 
8
- <%= render_tree categories, :id => :sns_categories, :class => :sortable, 'data-url' => send(sns_sort_path) do |node, child| %>
9
- <li id="<%= node.class.name.underscore %>_<%= node.id %>" class="sns_category">
10
- <%= render :partial => 'sortable_nested_set/category', :object => node %>
11
-
12
- <ul>
13
- <%= render :partial => 'sortable_nested_set/item', :collection => node.sns_items %>
14
- </ul>
15
-
8
+ <%= render_tree categories, :id => :sns_categories,
9
+ :class => :sortable,
10
+ 'data-url' => sns_sort_path(:category_class => @category_class) do |category, child| %>
11
+ <li id="<%= category.class.name.underscore %>_<%= category.id %>" class="sns_category">
12
+ <%= render :partial => 'sortable_nested_set/category', :object => category %>
13
+ <ul><%= render :partial => 'sortable_nested_set/item', :collection => category.sns_items %></ul>
16
14
  <%= child %>
17
15
  </li>
18
16
  <% end %>
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ Rails::Application.routes.draw do
2
+ put :sns_sort, :controller => :sortable_nested_set
3
+ end
@@ -1,5 +1,4 @@
1
1
  require 'nested_set'
2
- include CollectiveIdea::Acts::NestedSet
3
2
 
4
3
  module SortableNestedSet
5
4
  def self.included(base)
@@ -11,10 +10,12 @@ module SortableNestedSet
11
10
  def acts_as_sortable_nested_set_of(items_type)
12
11
  @subcategories_type = "sub#{name.tableize}".to_sym
13
12
  @items_type = items_type
13
+ @foreign_key = self.class.method_defined?(:parent) ? :parent_id : name.foreign_key
14
14
 
15
15
  instance_eval do
16
16
  def sns_subcategories_type ; @subcategories_type end
17
17
  def sns_items_type ; @items_type end
18
+ def sns_items_class ; @items_type.to_s.singularize.classify.constantize end
18
19
  end
19
20
 
20
21
  class_eval do
@@ -22,16 +23,22 @@ module SortableNestedSet
22
23
  define_method(:sns_items) { send(self.class.sns_items_type) }
23
24
  end
24
25
 
25
- acts_as_nested_set :parent_column => name.foreign_key
26
+ acts_as_nested_set :parent_column => @foreign_key
26
27
 
27
- belongs_to name.underscore.to_sym
28
+ has_many @subcategories_type, :class_name => name, :foreign_key => @foreign_key, :dependent => :destroy
29
+ has_many @items_type,
30
+ :order => (sns_items_class.position_column if sns_items_class.method_defined?(:position_column)),
31
+ :dependent => :destroy
28
32
 
29
- has_many @subcategories_type, :class_name => name, :dependent => :destroy
30
- has_many @items_type, :order => :position, :dependent => :destroy
33
+ after_initialize :default_depth_to_zero
31
34
  end
32
35
  end
33
36
 
34
37
  module InstanceMethods
38
+ def indented_name(indentation_character='.')
39
+ indentation_character * depth + name
40
+ end
41
+
35
42
  def move(parent, children)
36
43
  if children.size == 1
37
44
  move_to_child_of(parent)
@@ -39,7 +46,7 @@ module SortableNestedSet
39
46
  prev_sibling = nil
40
47
 
41
48
  for i in 0...children.size
42
- if children[i] == id
49
+ if children[i] == self.id
43
50
  prev_sibling ? move_to_right_of(prev_sibling) : move_to_left_of(children[i+1])
44
51
  break
45
52
  end
@@ -49,8 +56,10 @@ module SortableNestedSet
49
56
  end
50
57
  end
51
58
 
52
- def indented_name(indentation_character='.')
53
- indentation_character * level + name
59
+ private
60
+
61
+ def default_depth_to_zero
62
+ self.depth ||= 0 if has_attribute?(:depth)
54
63
  end
55
64
  end
56
65
  end
@@ -1,4 +1,12 @@
1
- require 'sortable_nested_set/engine'
2
- require 'sortable_nested_set/acts_as_sortable_nested_set'
3
- require 'sortable_nested_set/handles_sortable_nested_set'
4
- require 'sortable_nested_set/routes'
1
+ module SortableNestedSet
2
+ class Engine < Rails::Engine
3
+ initializer 'nested_fields.add_middleware' do |app|
4
+ require 'acts_as_sortable_nested_set'
5
+ app.middleware.use ActionDispatch::Static, "#{root}/public"
6
+ end
7
+
8
+ config.to_prepare do
9
+ ApplicationController.helper(SortableNestedSetHelper)
10
+ end
11
+ end
12
+ end
@@ -1,38 +1,44 @@
1
- $(document).ready(function() {
2
- function linkHover() {
3
- $(this).next().addClass('link_hover');
4
- }
5
-
6
- function linkUnhover() {
7
- if (!$(this).next().hasClass('pop_up_hover'))
8
- $(this).next().removeClass('link_hover');
9
- }
10
-
11
- function popUpHover() {
12
- $(this).removeClass('link_hover');
13
- $(this).addClass('pop_up_hover');
14
- }
15
-
16
- function popUpUnhover() {
17
- $(this).removeClass('pop_up_hover');
18
- }
19
-
20
- var linkConfig = {
21
- interval: 200,
22
- out: linkUnhover,
23
- over: linkHover,
24
- sensitivity: 4,
25
- timeout: 250
26
- };
27
-
28
- var popUpConfig = {
29
- interval: 0,
30
- out: popUpUnhover,
31
- over: popUpHover,
32
- sensitivity: 4,
33
- timeout: 250
34
- };
1
+ $('#sns_categories span.toggle').live('click', function() {
2
+ $(this).toggleClass('open');
3
+ $(this).toggleClass('closed');
4
+ $(this).closest('li').find('> ul').toggle();
5
+ });
6
+
7
+ function linkHover() {
8
+ $(this).next().addClass('link_hover');
9
+ }
10
+
11
+ function linkUnhover() {
12
+ if (!$(this).next().hasClass('pop_up_hover'))
13
+ $(this).next().removeClass('link_hover');
14
+ }
15
+
16
+ function popUpHover() {
17
+ $(this).removeClass('link_hover');
18
+ $(this).addClass('pop_up_hover');
19
+ }
20
+
21
+ function popUpUnhover() {
22
+ $(this).removeClass('pop_up_hover');
23
+ }
24
+
25
+ var linkConfig = {
26
+ interval: 200,
27
+ out: linkUnhover,
28
+ over: linkHover,
29
+ sensitivity: 4,
30
+ timeout: 250
31
+ };
32
+
33
+ var popUpConfig = {
34
+ interval: 0,
35
+ out: popUpUnhover,
36
+ over: popUpHover,
37
+ sensitivity: 4,
38
+ timeout: 250
39
+ };
35
40
 
41
+ $(document).ready(function() {
36
42
  $('.mega_menu').hoverIntent(linkConfig);
37
43
  $('.mega_menu_links').hoverIntent(popUpConfig);
38
44
 
@@ -54,10 +60,4 @@ $(document).ready(function() {
54
60
  });
55
61
  }
56
62
  });
57
-
58
- $('#sns_categories .toggle').click(function() {
59
- $(this).toggleClass('open');
60
- $(this).toggleClass('closed');
61
- $(this).closest('li').find('> ul').toggle();
62
- });
63
63
  });
@@ -1,3 +1,8 @@
1
+ .toggle {
2
+ cursor: pointer;
3
+ padding-right: 1em;
4
+ }
5
+
1
6
  .mega_menu_links {
2
7
  background: #ffc;
3
8
  border: 1px solid #eea;
@@ -12,8 +17,8 @@
12
17
  }
13
18
 
14
19
  .pop_up_hover {
15
- display: block;
16
20
  border: 1px solid #cca;
21
+ display: block;
17
22
  }
18
23
 
19
24
  #sns_categories, #sns_categories ul {
@@ -23,7 +28,7 @@
23
28
  }
24
29
 
25
30
  #sns_categories {
26
- margin: 0;
31
+ margin: 0 0 10px 0;
27
32
  }
28
33
 
29
34
  #sns_categories li {
@@ -47,11 +52,6 @@
47
52
  color: #8a1f11;
48
53
  }
49
54
 
50
- .toggle {
51
- cursor: pointer;
52
- padding-right: 1em;
53
- }
54
-
55
55
  #sns_categories span.open {
56
56
  background: url(/images/folder_open.png) no-repeat center;
57
57
  }
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{sortable_nested_set}
8
- s.version = "0.2.3"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Jason Murad"]
12
- s.date = %q{2011-01-22}
12
+ s.date = %q{2011-01-25}
13
13
  s.description = %q{reusable menu for navigation and modification or hierarchical categories}
14
14
  s.email = %q{jason@thriess.com}
15
15
  s.extra_rdoc_files = [
@@ -22,17 +22,14 @@ Gem::Specification.new do |s|
22
22
  "README.rdoc",
23
23
  "Rakefile",
24
24
  "VERSION",
25
+ "app/controllers/sortable_nested_set_controller.rb",
25
26
  "app/helpers/sortable_nested_set_helper.rb",
26
27
  "app/views/sortable_nested_set/_category.html.erb",
27
28
  "app/views/sortable_nested_set/_item.html.erb",
28
29
  "app/views/sortable_nested_set/_tree.html.erb",
29
- "app/views/sortable_nested_set/sns_destroy_category.js.erb",
30
- "app/views/sortable_nested_set/sns_destroy_item.js.erb",
30
+ "config/routes.rb",
31
+ "lib/acts_as_sortable_nested_set.rb",
31
32
  "lib/sortable_nested_set.rb",
32
- "lib/sortable_nested_set/acts_as_sortable_nested_set.rb",
33
- "lib/sortable_nested_set/engine.rb",
34
- "lib/sortable_nested_set/handles_sortable_nested_set.rb",
35
- "lib/sortable_nested_set/routes.rb",
36
33
  "public/images/folder_closed.png",
37
34
  "public/images/folder_open.png",
38
35
  "public/images/mega_menu.png",
data/test/helper.rb CHANGED
@@ -12,7 +12,6 @@ require 'shoulda'
12
12
 
13
13
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
14
  $LOAD_PATH.unshift(File.dirname(__FILE__))
15
- require 'sortable_nested_set'
16
15
 
17
16
  class Test::Unit::TestCase
18
17
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 2
8
7
  - 3
9
- version: 0.2.3
8
+ - 0
9
+ version: 0.3.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Jason Murad
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-01-22 00:00:00 -05:00
17
+ date: 2011-01-25 00:00:00 -05:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -101,17 +101,14 @@ files:
101
101
  - README.rdoc
102
102
  - Rakefile
103
103
  - VERSION
104
+ - app/controllers/sortable_nested_set_controller.rb
104
105
  - app/helpers/sortable_nested_set_helper.rb
105
106
  - app/views/sortable_nested_set/_category.html.erb
106
107
  - app/views/sortable_nested_set/_item.html.erb
107
108
  - app/views/sortable_nested_set/_tree.html.erb
108
- - app/views/sortable_nested_set/sns_destroy_category.js.erb
109
- - app/views/sortable_nested_set/sns_destroy_item.js.erb
109
+ - config/routes.rb
110
+ - lib/acts_as_sortable_nested_set.rb
110
111
  - lib/sortable_nested_set.rb
111
- - lib/sortable_nested_set/acts_as_sortable_nested_set.rb
112
- - lib/sortable_nested_set/engine.rb
113
- - lib/sortable_nested_set/handles_sortable_nested_set.rb
114
- - lib/sortable_nested_set/routes.rb
115
112
  - public/images/folder_closed.png
116
113
  - public/images/folder_open.png
117
114
  - public/images/mega_menu.png
@@ -136,7 +133,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
136
133
  requirements:
137
134
  - - ">="
138
135
  - !ruby/object:Gem::Version
139
- hash: 787758848533743446
136
+ hash: 3123587155221195723
140
137
  segments:
141
138
  - 0
142
139
  version: "0"
@@ -1 +0,0 @@
1
- $('#category_<%= @category.id %>').remove();
@@ -1 +0,0 @@
1
- $('#item_<%= @item.id %>').remove();
@@ -1,7 +0,0 @@
1
- module SortableNestedSet
2
- class Engine < Rails::Engine
3
- initializer 'sortable_nested_set.add_middleware' do |app|
4
- app.middleware.use ActionDispatch::Static, "#{root}/public"
5
- end
6
- end
7
- end
@@ -1,74 +0,0 @@
1
- module SortableNestedSet
2
- def self.included(base)
3
- base.extend ClassMethods
4
- end
5
-
6
- module ClassMethods
7
- def handles_sortable_nested_set
8
- class_eval do
9
- define_method(:sns_category_id_field) { "#{controller_name.singularize}_id" }
10
- define_method(:sns_category_class) { controller_name.classify.constantize }
11
- define_method(:sns_items_type) { sns_category_class.sns_items_type }
12
- define_method(:sns_item_class) { sns_items_type.to_s.classify.constantize }
13
- end
14
-
15
- include InstanceMethods
16
- end
17
- end
18
-
19
- module InstanceMethods
20
- def sns_add_subcategory
21
- redirect_to :action => :new, sns_category_id_field => params[:id]
22
- end
23
-
24
- def sns_add_item
25
- redirect_to :controller => sns_items_type, :action => :new, sns_category_id_field => params[:id]
26
- end
27
-
28
- def sns_sort
29
- matches = params[:moved].match(/(\w+)_(\d+)$/)
30
- moved_type = matches[1]
31
- moved_id = matches[2].to_i
32
- moved_parent = nil
33
- tree = {} # category_id => [children of move_type]
34
-
35
- for node in params[:tree].scan(/(\w+)\[(\d+)\]=(\w+)/)
36
- type = node[0]
37
- next unless type == moved_type
38
-
39
- id = node[1].to_i
40
- parent = (node[2] == 'root') ? nil : node[2].to_i
41
-
42
- moved_parent = parent if id == moved_id
43
-
44
- tree[parent] ||= []
45
- tree[parent] << id
46
- end
47
-
48
- if moved_type == sns_items_type.to_s.singularize
49
- item = sns_item_class.find(moved_id)
50
- item.remove_from_list
51
- item.update_attribute(sns_category_id_field, moved_parent)
52
- item.insert_at(tree[moved_parent].index(moved_id))
53
- else
54
- moved_type.classify.constantize.find(moved_id).move(moved_parent, tree[moved_parent])
55
- end
56
-
57
- render :nothing => true
58
- end
59
-
60
- def sns_destroy_category
61
- @category = sns_category_class.find(params[:id])
62
- @category.destroy
63
- render 'sortable_nested_set/sns_destroy_category'
64
- end
65
-
66
- def sns_destroy_item
67
- @item = sns_item_class.find(params[:id])
68
- @item.destroy
69
- render 'sortable_nested_set/sns_destroy_item'
70
- end
71
- end
72
- end
73
-
74
- ActionController::Base.send :include, SortableNestedSet
@@ -1,27 +0,0 @@
1
- module ActionDispatch::Routing
2
- class Mapper
3
- def sortable_nested_set_for(category_type)
4
- item_type = category_type.to_s.classify.constantize.sns_items_type
5
-
6
- resources category_type do
7
- resources category_type, :as => "sub#{category_type}"
8
- resources item_type
9
-
10
- member do
11
- get :sns_toggle
12
- get :sns_add_subcategory
13
- get :sns_add_item
14
- delete :sns_destroy_category
15
- end
16
-
17
- collection do
18
- put :sns_sort
19
- end
20
- end
21
-
22
- resources item_type do
23
- delete :sns_destroy_item, :on => :member, :controller => category_type
24
- end
25
- end
26
- end
27
- end