sortable_nested_set 0.1.26 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+ gem "nested_set"
6
+
7
+ # Add dependencies to develop your gem here.
8
+ # Include everything needed to run rake, tests, features, etc.
9
+ group :development do
10
+ gem "shoulda", ">= 0"
11
+ gem "bundler", "~> 1.0.0"
12
+ gem "jeweler", "~> 1.5.1"
13
+ gem "rcov", ">= 0"
14
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,62 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ abstract (1.0.0)
5
+ actionpack (3.0.3)
6
+ activemodel (= 3.0.3)
7
+ activesupport (= 3.0.3)
8
+ builder (~> 2.1.2)
9
+ erubis (~> 2.6.6)
10
+ i18n (~> 0.4)
11
+ rack (~> 1.2.1)
12
+ rack-mount (~> 0.6.13)
13
+ rack-test (~> 0.5.6)
14
+ tzinfo (~> 0.3.23)
15
+ activemodel (3.0.3)
16
+ activesupport (= 3.0.3)
17
+ builder (~> 2.1.2)
18
+ i18n (~> 0.4)
19
+ activerecord (3.0.3)
20
+ activemodel (= 3.0.3)
21
+ activesupport (= 3.0.3)
22
+ arel (~> 2.0.2)
23
+ tzinfo (~> 0.3.23)
24
+ activesupport (3.0.3)
25
+ arel (2.0.6)
26
+ builder (2.1.2)
27
+ erubis (2.6.6)
28
+ abstract (>= 1.0.0)
29
+ git (1.2.5)
30
+ i18n (0.5.0)
31
+ jeweler (1.5.1)
32
+ bundler (~> 1.0.0)
33
+ git (>= 1.2.5)
34
+ rake
35
+ nested_set (1.6.1)
36
+ activerecord (>= 3.0.0)
37
+ railties (>= 3.0.0)
38
+ rack (1.2.1)
39
+ rack-mount (0.6.13)
40
+ rack (>= 1.0.0)
41
+ rack-test (0.5.6)
42
+ rack (>= 1.0)
43
+ railties (3.0.3)
44
+ actionpack (= 3.0.3)
45
+ activesupport (= 3.0.3)
46
+ rake (>= 0.8.7)
47
+ thor (~> 0.14.4)
48
+ rake (0.8.7)
49
+ rcov (0.9.9)
50
+ shoulda (2.11.3)
51
+ thor (0.14.6)
52
+ tzinfo (0.3.23)
53
+
54
+ PLATFORMS
55
+ ruby
56
+
57
+ DEPENDENCIES
58
+ bundler (~> 1.0.0)
59
+ jeweler (~> 1.5.1)
60
+ nested_set
61
+ rcov
62
+ shoulda
data/README CHANGED
@@ -1,3 +1,29 @@
1
- To copy support files (images, stylesheets, javascript, etc.), run:
1
+ jQuery is required.
2
2
 
3
- rake sortable_nested_set:sync
3
+ In the Gemfile
4
+ gem 'sortable_nested_set'
5
+
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").
9
+
10
+ In each "Category" controller:
11
+ handles_sortable_nested_set
12
+ to include menu actions.
13
+
14
+ In the routes config file:
15
+ sortable_nested_set_for :categories
16
+ for each pluralized "Category" model.
17
+
18
+ In each "Category" migration:
19
+ name:string
20
+ lft:integer
21
+ rgt:integer
22
+
23
+ In each "Item" migration:
24
+ position:integer
25
+
26
+ Helpers:
27
+ sns_options
28
+ sns_lineage
29
+ sortable_nested_set_tree
data/README.rdoc ADDED
@@ -0,0 +1,19 @@
1
+ = sortable_nested_set
2
+
3
+ Description goes here.
4
+
5
+ == Contributing to sortable_nested_set
6
+
7
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
8
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
9
+ * Fork the project
10
+ * Start a feature/bugfix branch
11
+ * Commit and push until you are happy with your contribution
12
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2010 Jason Murad. See LICENSE.txt for
18
+ further details.
19
+
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "sortable_nested_set"
16
+ gem.homepage = "http://github.com/jrmurad/sortable_nested_set"
17
+ gem.license = "MIT"
18
+ gem.summary = %Q{reusable menu for navigation and modification or hierarchical categories}
19
+ gem.description = %Q{reusable menu for navigation and modification or hierarchical categories}
20
+ gem.email = "jason@thriess.com"
21
+ gem.authors = ["Jason Murad"]
22
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
23
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
25
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
26
+ end
27
+ Jeweler::RubygemsDotOrgTasks.new
28
+
29
+ require 'rake/testtask'
30
+ Rake::TestTask.new(:test) do |test|
31
+ test.libs << 'lib' << 'test'
32
+ test.pattern = 'test/**/test_*.rb'
33
+ test.verbose = true
34
+ end
35
+
36
+ require 'rcov/rcovtask'
37
+ Rcov::RcovTask.new do |test|
38
+ test.libs << 'test'
39
+ test.pattern = 'test/**/test_*.rb'
40
+ test.verbose = true
41
+ end
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "sortable_nested_set #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.2
@@ -1,36 +1,39 @@
1
- require 'awesome_nested_set/helper'
2
- include CollectiveIdea::Acts::NestedSet::Helper
3
-
4
1
  module SortableNestedSetHelper
5
2
  def sortable_nested_set_tree(class_or_category)
6
3
  if class_or_category.is_a?(Class)
7
4
  @category_class = class_or_category
8
- @categories = class_or_category.all(:include => class_or_category.sns_items_type, :order => :lft)
5
+ @categories = class_or_category.for_indented_list_with_items.all
9
6
  else
10
7
  @category_class = class_or_category.class
11
- @categories = class_or_category.self_and_descendants_with_items
12
- @root = class_or_category
8
+ @categories = class_or_category.self_and_descendants.for_indented_list_with_items
13
9
  end
14
10
 
15
- @sortable_ids = @categories.collect(&:id)
16
-
17
- render 'sortable_nested_set/tree',
18
- :category_class => @category_class,
19
- :categories => @categories,
20
- :sortable_ids => @sortable_ids,
21
- :root => @root
11
+ render 'sortable_nested_set/tree', :categories => @categories, :category_class => @category_class
22
12
  end
23
13
 
24
- def sns_lineage(category, options=Hash.new)
25
- content_tag :p, category.lineage(options).collect { |a| link_to(a.name, a) }
26
- .join(image_tag('folder_closed.png', :class => :descent)).html_safe
14
+ def sns_lineage(category, options={})
15
+ content_for :head do
16
+ stylesheet_link_tag 'sortable_nested_set'
17
+ end
18
+
19
+ ancestors = category.send(options[:include_self] ? :self_and_ancestors : :ancestors)
20
+
21
+ links = ancestors.select([:id, :name]).collect do |c|
22
+ link_to(c.name, c, :class => :lineage)
23
+ end
24
+
25
+ content_tag :p, links.join.html_safe unless links.empty?
27
26
  end
28
27
 
29
- def sns_options(class_or_category, mover=nil)
30
- nested_set_options(class_or_category, mover) do |category, descendants, level|
31
- "#{'.' * level} #{category.name}"
28
+ def sns_options(category_class, mover=nil)
29
+ options = []
30
+
31
+ category_class.each_with_level(category_class.for_indented_list) do |category, level|
32
+ if mover.nil? || mover.new_record? || mover.move_possible?(category)
33
+ options << ['.' * level + category.name, category.id]
34
+ end
32
35
  end
36
+
37
+ options
33
38
  end
34
39
  end
35
-
36
- ActionView::Base.send :include, SortableNestedSetHelper
@@ -1,31 +1,18 @@
1
- <% cpath = category.class.name.underscore + '_path' %>
2
-
3
- <%= render 'sortable_nested_set/toggle',
4
- :category_type => category.class.name.underscore,
5
- :id => category.id,
6
- :state => :open %>
7
-
8
- <%= content_tag :span, category, :class => :handle %>
9
-
10
- <% unless local_assigns[:show_mega_menu] == false %>
11
- <%= content_tag :span, link_to('-', category), :id => "toggle_actions_category_#{category.id}", :class => :mega %>
12
-
13
- <%= content_tag :div, :id => "actions_category_#{category.id}", :class => :links do %>
1
+ <div>
2
+ <span class="toggle open"></span>
3
+ <span class="handle"><%= category %></span>
4
+ <span class="mega_menu"></span>
5
+ <div class="mega_menu_links">
14
6
  <%= link_to_unless_current 'show', category %> |
15
- <%= link_to 'edit', send("edit_#{cpath}", category) %> |
16
- <% unless current_page? send(cpath, category) %>
17
- <%= link_to 'remove',
18
- send("sns_destroy_category_#{cpath}", category),
19
- :confirm => 'Are you sure?',
20
- :method => :delete,
21
- :remote => true %> |
22
- <% end %>
7
+ <%= link_to 'edit', send("edit_#{@cpath}", category) %> |
8
+ <%= link_to 'remove',
9
+ send("sns_destroy_category_#{@cpath}", category),
10
+ :confirm => 'Are you sure?',
11
+ :method => :delete,
12
+ :remote => !current_page?(send(@cpath, category)) %> |
23
13
  <%= link_to "add #{category.class.sns_subcategories_type.to_s.singularize}",
24
- send("sns_add_subcategory_#{cpath}", category) %> |
25
- <%= link_to "add #{category.class.sns_items_type.to_s.singularize}", send("sns_add_item_#{cpath}", category) %>
26
- <% end %>
27
- <% end %>
28
-
29
- <%= content_tag :div, :id => "items_#{category.id}", :class => :items do %>
30
- <%= render :partial => 'sortable_nested_set/item', :collection => category.sns_items %>
31
- <% end %>
14
+ send("sns_add_subcategory_#{@cpath}", category) %> |
15
+ <%= link_to "add #{category.class.sns_items_type.to_s.singularize}",
16
+ send("sns_add_item_#{@cpath}", category) %>
17
+ </div>
18
+ </div>
@@ -1,16 +1,15 @@
1
- <%= content_tag :div, :id => "item_#{item.id}", :class => :item do %>
2
- <%= content_tag :span, item, :class => :handle %>
3
-
4
- <%= content_tag :span, link_to('-', item), :id => "toggle_actions_item_#{item.id}", :class => :mega %>
5
-
6
- <%= content_tag :div, :id => "actions_item_#{item.id}", :class => :links do %>
7
- <%= link_to 'show', item %> |
8
- <%= link_to 'edit', send("edit_#{item.class.name.underscore}_path", item) %> |
9
- <%= link_to 'remove',
10
- send("sns_destroy_item_#{item.class.name.underscore}_path", item),
11
- :confirm => 'Are you sure?',
12
- :method => :delete,
13
- :remote => true %>
14
- <% end %>
15
- <% end %>
16
-
1
+ <li id="<%= "#{item.class.name.underscore}_#{item.id}" %>" class="item">
2
+ <div>
3
+ <div class="handle"><%= item %></div>
4
+ <span class="mega_menu"></span>
5
+ <div class="mega_menu_links">
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 %>
13
+ </div>
14
+ </div>
15
+ </li>
@@ -1,53 +1,23 @@
1
- <%= stylesheet_link_tag 'mega_menu', 'menu_tree' %>
2
- <%= javascript_tag nil, :src => 'http://www.google.com/jsapi' %>
3
- <%= javascript_tag "google.load('jquery', '1.4.2')" %>
4
- <%= javascript_include_tag 'jquery.hoverIntent.minified.js', 'mega_menu' %>
5
-
6
- <%= content_tag :ul, :id => :menu, :class => :menu_links do %>
7
- <%= content_tag :li, nil, :class => :placeholder unless local_assigns[:root] %>
1
+ <% content_for :head do %>
2
+ <%= stylesheet_link_tag 'sortable_nested_set' %>
3
+ <%= javascript_include_tag 'sortable_nested_set' %>
4
+ <% end %>
8
5
 
9
- <% prev_level = -1 %>
6
+ <% @cpath = category_class.name.underscore + '_path' %>
7
+ <% prev_level = -1 %>
10
8
 
9
+ <ol id="menu" class="sortable" data-url="<%= send "sns_sort_#{category_class.name.underscore.pluralize}_path" %>">
11
10
  <% category_class.each_with_level(categories) do |category, level| %>
12
- <%=raw '</ul></li>' if level <= prev_level %>
13
- <%=raw '</ul>' * (prev_level - level) if level < prev_level %>
14
-
15
- <li id="category_<%= category.id %>" <%=raw 'class="category"' unless category == root %>>
16
- <%= render :partial => 'sortable_nested_set/category',
17
- :object => category,
18
- :locals => {:show_mega_menu => (category != root)} %>
11
+ <%=raw '</ol></li>' if level <= prev_level %>
12
+ <%=raw '</ol>' * (prev_level - level) if level < prev_level %>
19
13
 
20
- <ul id="subcategories_<%= category.id %>" class="sub_menu_links">
21
- <%= content_tag :li, :class => :placeholder do %>
22
- <%= content_tag :span, nil, :class => :handle %>
23
- <% end %>
14
+ <li id="<%= category_class.name.underscore %>_<%= category.id %>" class="category">
15
+ <%= render :partial => 'sortable_nested_set/category', :object => category %>
16
+ <ol>
17
+ <%= render :partial => 'sortable_nested_set/item', :collection => category.sns_items %>
24
18
 
25
19
  <% prev_level = level %>
26
20
  <% end %>
27
21
 
28
- <%=raw '</ul></li>' * (prev_level + 1) if prev_level > -1 %>
29
- <% end %>
30
-
31
- <% unless categories.empty? %>
32
- <% cpath = categories.first.class.name.pluralize.underscore + '_path' %>
33
-
34
- <% for category_id in sortable_ids %>
35
- <%= sortable_element_reporting_target "items_#{category_id}",
36
- :moved,
37
- :containment => sortable_ids.collect { |cid| "items_#{cid}" },
38
- :dropOnEmpty => true,
39
- :method => :put,
40
- :tag => :div,
41
- :url => send("sns_sort_items_#{cpath}") %>
42
- <% end %>
43
-
44
- <%= sortable_element_reporting_target :menu,
45
- :moved,
46
- :ghosting => true,
47
- :handle => :handle,
48
- :hoverclass => "'hovered'",
49
- :method => :put,
50
- :only => [ :category, :placeholder ],
51
- :tree => true,
52
- :url => send("sns_sort_categories_#{cpath}", :root => root) %>
53
- <% end %>
22
+ <%=raw '</ol></li>' * (prev_level + 1) if prev_level > -1 %>
23
+ </ol>
@@ -0,0 +1 @@
1
+ $('#category_<%= @category.id %>').remove();
@@ -0,0 +1 @@
1
+ $('#item_<%= @item.id %>').remove();
@@ -1,15 +1,14 @@
1
- require 'awesome_nested_set'
1
+ require 'nested_set'
2
2
  include CollectiveIdea::Acts::NestedSet
3
3
 
4
4
  module SortableNestedSet
5
5
  def self.included(base)
6
6
  base.extend ClassMethods
7
+ base.send :include, InstanceMethods
7
8
  end
8
9
 
9
10
  module ClassMethods
10
11
  def acts_as_sortable_nested_set_of(items_type)
11
- send :include, InstanceMethods
12
-
13
12
  @subcategories_type = "sub#{name.tableize}".to_sym
14
13
  @items_type = items_type
15
14
 
@@ -25,40 +24,36 @@ module SortableNestedSet
25
24
 
26
25
  acts_as_nested_set :parent_column => name.foreign_key
27
26
 
28
- belongs_to name.underscore
27
+ attr_accessible parent_column_name
28
+
29
+ belongs_to name.underscore.to_sym
29
30
 
30
31
  has_many @subcategories_type, :class_name => name, :dependent => :destroy
31
32
  has_many @items_type, :order => :position, :dependent => :destroy
33
+
34
+ scope :for_indented_list,
35
+ :select => [parent_column_name, :id, :name, left_column_name, right_column_name],
36
+ :order => left_column_name
37
+ scope :for_indented_list_with_items, lambda { self.for_indented_list.includes(sns_items_type) }
32
38
  end
33
39
  end
34
40
 
35
41
  module InstanceMethods
36
- def self_and_descendants_with_items
37
- nested_set_scope.scoped :include => self.class.sns_items_type, :conditions => [
38
- "#{self.class.quoted_table_name}.#{quoted_left_column_name} >= ? AND
39
- #{self.class.quoted_table_name}.#{quoted_right_column_name} <= ?", left, right
40
- ]
41
- end
42
-
43
- def lineage(options=Hash.new)
44
- conditions = [ 'lft <= ? AND rgt >= ?', lft, rgt ]
45
- unless options[:include_self] == true
46
- conditions[0] << ' AND id != ?'
47
- conditions << id
42
+ def move(parent, children)
43
+ if children.size == 1
44
+ move_to_child_of(parent)
45
+ else
46
+ prev_sibling = nil
47
+
48
+ for i in 0...children.size
49
+ if children[i] == id
50
+ prev_sibling ? move_to_right_of(prev_sibling) : move_to_left_of(children[i+1])
51
+ break
52
+ end
53
+
54
+ prev_sibling = children[i]
55
+ end
48
56
  end
49
-
50
- self.class.find(:all, :select => 'id, name', :conditions => conditions, :order => 'lft ASC')
51
- end
52
-
53
- # descendents() does "SELECT *" but only id field required
54
- def descendant_subcategory_ids(options=Hash.new)
55
- conditions = [ 'lft >= ? AND rgt <= ?', lft, rgt ]
56
- unless options[:include_self] == true
57
- conditions[0] << ' AND id != ?'
58
- conditions << id
59
- end
60
-
61
- self.class.find(:all, :select => :id, :conditions => conditions).collect(&:id)
62
57
  end
63
58
  end
64
59
  end
@@ -1,13 +1,7 @@
1
- require 'sortable_nested_set'
2
- require 'rails'
3
-
4
1
  module SortableNestedSet
5
2
  class Engine < Rails::Engine
6
3
  initializer 'sortable_nested_set.add_middleware' do |app|
7
4
  app.middleware.use ActionDispatch::Static, "#{root}/public"
8
5
  end
9
- config.to_prepare do
10
- ApplicationController.helper(SortableNestedSetHelper)
11
- end
12
6
  end
13
7
  end