acts_as_better_tree 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in acts_as_better_tree.gemspec
4
+ gemspec
5
+ gem 'sqlite3'
6
+ gem 'rspec'
7
+ gem 'rails', ">= 3.2.5"
8
+ gem "factory_girl"
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Isaac Sloan
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # ActsAsBetterTree
2
+
3
+ acts_as_better_tree is great for anyone who needs a fast tree capable of handling millions of nodes without slowing down on writes like nestedset or on reads like a standard tree.
4
+ It is backwards compatible with acts_as_tree and remains fast with large datasets by storing the ancestry of every node in the field csv_ids.
5
+
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'acts_as_better_tree'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install acts_as_better_tree
20
+
21
+ ## Usage
22
+
23
+ Required fields are parent_id, root_id, csv_ids.
24
+
25
+ create_table :categories do |t|
26
+ t.column :root_id, :integer
27
+ t.column :parent_id, :integer
28
+ t.column :csv_ids, :string
29
+ t.column :name, :string
30
+ end
31
+
32
+ If upgrading from acts_as_tree just add root_id and csv_ids and run Category.build_csv_ids!
33
+
34
+ class Category < ActiveRecord::Base
35
+ acts_as_better_tree :order => "name"
36
+ end
37
+
38
+ Example:
39
+ root
40
+ \_ child1
41
+ \_ subchild1
42
+ \_ subchild2
43
+
44
+ root = Category.create("name" => "root")
45
+ child1 = root.children.create("name" => "child1")
46
+ subchild1 = child1.children.create("name" => "subchild1")
47
+
48
+ root.parent # => nil
49
+ child1.parent # => root
50
+ root.children # => [child1]
51
+ root.children.first.children.first # => subchild1
52
+
53
+ Copyright (c) 2008 Isaac Sloan, released under the MIT license
54
+ Inspired by David Heinemeier Hansson's acts_as_tree
55
+
56
+ ## Contributing
57
+
58
+ 1. Fork it
59
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
60
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
61
+ 4. Push to the branch (`git push origin my-new-feature`)
62
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new('spec')
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/acts_as_better_tree/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Isaac Sloan"]
6
+ gem.email = ["isaac@isaacsloan.com"]
7
+ gem.description = %q{acts_as_better_tree is great for anyone who needs a fast tree capable of handling millions of nodes without slowing down on writes like nestedset or on reads like a standard tree.}
8
+ gem.summary = %q{acts_as_better_tree is backwards compatible with acts_as_tree and remains fast with large datasets by storing the ancestry of every node in the field csv_ids.}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "acts_as_better_tree"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = ActsAsBetterTree::VERSION
17
+ end
@@ -0,0 +1,3 @@
1
+ module ActsAsBetterTree
2
+ VERSION = "0.9.3"
3
+ end
@@ -0,0 +1,179 @@
1
+ require "acts_as_better_tree/version"
2
+
3
+ module ActiveRecord
4
+ module Acts
5
+ module BetterTree
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ def acts_as_better_tree(options = {}, &b)
12
+ configuration = {:order => "id ASC", :destroy_dependent => true }
13
+ configuration.update(options) if options.is_a?(Hash)
14
+
15
+ belongs_to :parent, :class_name => name, :foreign_key => :parent_id
16
+ has_many :children, {:class_name => name, :foreign_key => :parent_id, :order => configuration[:order]}.merge(configuration[:destroy_dependent] ? {:dependent => :destroy} : {}), &b
17
+ has_many :parents_children, {:class_name => name, :primary_key => :parent_id, :foreign_key => :parent_id, :order => configuration[:order]}, &b
18
+ belongs_to :root, :class_name => name, :foreign_key => :root_id
19
+ scope :roots, :order => configuration[:order], :conditions => {:parent_id => nil}
20
+ after_create :assign_csv_ids
21
+ after_validation :update_csv_ids, :on => :update
22
+
23
+ instance_eval do
24
+ include ActiveRecord::Acts::BetterTree::InstanceMethods
25
+
26
+ def root(options = {})
27
+ roots(options).first
28
+ end
29
+
30
+ def traverse(nodes = self.roots, &block)
31
+ nodes.each do |node|
32
+ yield node
33
+ traverse(node.children, &block)
34
+ end
35
+ end
36
+
37
+ # Call this to upgrade an existing acts_as_tree to acts_as_better_tree
38
+ def build_csv_ids!(nodes = self.roots)
39
+ transaction do
40
+ traverse(nodes) do |node|
41
+ node.csv_ids = node.build_csv_ids
42
+ node.save
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ module InstanceMethods
51
+ def parent_foreign_key_changed?
52
+ parent_id_changed?
53
+ end
54
+
55
+ def ancestors
56
+ if self.csv_ids
57
+ ids = self.csv_ids.split(',')[0...-1]
58
+ (@ancestors ||= self.class.where(:id => ids).order('csv_ids ASC'))
59
+ else
60
+ node, nodes = self, []
61
+ nodes << node = node.parent while node.parent
62
+ (@ancestors ||= nodes.reverse)
63
+ end
64
+ end
65
+
66
+ def self_and_ancestors
67
+ ancestors + [self]
68
+ end
69
+
70
+ def self_and_children
71
+ [self] + children
72
+ end
73
+
74
+ def siblings
75
+ self_and_siblings - [self]
76
+ end
77
+
78
+ def self_and_siblings
79
+ unless parent_id.blank?
80
+ self.parents_children
81
+ else
82
+ self.class.roots
83
+ end
84
+ end
85
+
86
+ def ancestor_of?(node)
87
+ node.csv_ids.length > self.csv_ids.length && node.csv_ids.starts_with?(self.csv_ids)
88
+ end
89
+
90
+ def descendant_of?(node)
91
+ self.csv_ids.length > node.csv_ids.length && self.csv_ids.starts_with?(node.csv_ids)
92
+ end
93
+
94
+ def all_children(options = {})
95
+ find_all_children_with_csv_ids(nil, options)
96
+ end
97
+
98
+ def self_and_all_children
99
+ [self] + all_children
100
+ end
101
+
102
+ def depth
103
+ self.csv_ids.scan(/\,/).size
104
+ end
105
+
106
+ def childless?
107
+ return self.class.where(:parent_id => self.id).first.blank?
108
+ end
109
+
110
+ def root?
111
+ return self.parent.blank?
112
+ end
113
+
114
+ def move_to_child_of(category)
115
+ self.update_attributes(:parent_id => category.id)
116
+ end
117
+
118
+ def make_root
119
+ self.update_attributes(:parent_id => nil)
120
+ end
121
+
122
+ def to_csv(nodes = self.children)
123
+ csv = []
124
+ nodes.each do |node|
125
+ if node.childless?
126
+ csv += [node.self_and_ancestors.map(&:name).join(",")]
127
+ else
128
+ csv += [to_csv(node.children)]
129
+ end
130
+ end
131
+ return csv.join("\n")
132
+ end
133
+
134
+ def build_csv_ids
135
+ self.parent_id.blank? ? self.id.to_s : "#{self.parent.csv_ids},#{self.id}"
136
+ end
137
+
138
+ protected
139
+
140
+ def csv_id_like_pattern(prefix = nil)
141
+ (prefix || self.csv_ids) + ',%'
142
+ end
143
+
144
+ def build_root_id
145
+ return (self.parent_id ? self.parent.root_id : self.id)
146
+ end
147
+
148
+ def find_all_children_with_csv_ids(prefix = nil, options = {})
149
+ conditions = [self.class.send(:sanitize_sql, ['csv_ids LIKE ?', csv_id_like_pattern(prefix)])]
150
+ conditions << "(#{self.class.send(:sanitize_sql, options[:conditions])})" unless options[:conditions].blank?
151
+ options.update(:conditions => conditions.join(" AND "))
152
+ self.class.find(:all, options)
153
+ end
154
+
155
+ def assign_csv_ids
156
+ self.update_attributes(:csv_ids => build_csv_ids, :root_id => build_root_id)
157
+ end
158
+
159
+ def update_csv_ids
160
+ return unless parent_foreign_key_changed?
161
+ old_csv_ids = self.csv_ids
162
+ self.csv_ids = build_csv_ids
163
+ self.root_id = build_root_id
164
+ self.class.update_all("csv_ids = Replace(csv_ids, '#{old_csv_ids},', '#{self.csv_ids},'), root_id = #{self.root_id}", "csv_ids like '#{old_csv_ids},%'")
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
170
+
171
+ #ActiveRecord::Base.send :include, ActiveRecord::Acts::BetterTree
172
+ #
173
+ #ActiveRecord::Base.class_eval do
174
+ # include ActiveRecord::Acts::BetterTree
175
+ #end
176
+
177
+ class ActiveRecord::Base
178
+ include ActiveRecord::Acts::BetterTree
179
+ end
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: acts_as_better_tree
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Isaac Sloan
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-02 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: acts_as_better_tree is great for anyone who needs a fast tree capable
15
+ of handling millions of nodes without slowing down on writes like nestedset or on
16
+ reads like a standard tree.
17
+ email:
18
+ - isaac@isaacsloan.com
19
+ executables: []
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - .gitignore
24
+ - Gemfile
25
+ - LICENSE
26
+ - README.md
27
+ - Rakefile
28
+ - acts_as_better_tree.gemspec
29
+ - lib/acts_as_better_tree.rb
30
+ - lib/acts_as_better_tree/version.rb
31
+ homepage: ''
32
+ licenses: []
33
+ post_install_message:
34
+ rdoc_options: []
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ! '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubyforge_project:
51
+ rubygems_version: 1.8.21
52
+ signing_key:
53
+ specification_version: 3
54
+ summary: acts_as_better_tree is backwards compatible with acts_as_tree and remains
55
+ fast with large datasets by storing the ancestry of every node in the field csv_ids.
56
+ test_files: []