ordered_tree 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +43 -7
- data/VERSION +1 -1
- data/lib/ordered_tree/class_methods.rb +0 -5
- data/lib/ordered_tree/instance_methods/list.rb +2 -1
- data/lib/ordered_tree/instance_methods/misc.rb +7 -0
- data/lib/ordered_tree.rb +19 -0
- data/ordered_tree.gemspec +3 -4
- data/spec/fixtures/page.rb +4 -0
- data/spec/fixtures/person.rb +1 -0
- data/spec/ordered_tree_spec.rb +31 -0
- data/spec/spec_helper.rb +8 -1
- metadata +5 -6
- data/README.rdoc +0 -19
data/README.textile
CHANGED
@@ -33,8 +33,8 @@ v.1.2
|
|
33
33
|
|
34
34
|
<pre>
|
35
35
|
class Person < ActiveRecord::Base
|
36
|
-
|
37
|
-
|
36
|
+
ordered_tree :foreign_key => :parent_id,
|
37
|
+
:order => :position
|
38
38
|
end
|
39
39
|
|
40
40
|
class CreatePeople < ActiveRecord::Migration
|
@@ -61,6 +61,8 @@ has_many :children,
|
|
61
61
|
:order => :position
|
62
62
|
</pre>
|
63
63
|
|
64
|
+
_Note: @:parent_id@ and @:position@ are default values for the @foreign_key@ and @order@, respectively._
|
65
|
+
|
64
66
|
h2. Overview
|
65
67
|
|
66
68
|
<pre>
|
@@ -111,13 +113,47 @@ h2. Overview
|
|
111
113
|
destroy_and_orphan_children
|
112
114
|
|
113
115
|
destroy_and_parent_adopts_children
|
114
|
-
|
115
116
|
</pre>
|
116
117
|
|
117
|
-
h2.
|
118
|
+
h2. Setting the scope
|
118
119
|
|
119
|
-
|
120
|
+
If you want to have multiple trees in the same database (let's say you want to have multiple sets of pages for the different tenants on your database), you should set a scope. A scope basically says: "work within this scope when possible".
|
121
|
+
|
122
|
+
How does it work?
|
123
|
+
|
124
|
+
Let's say I have a multi-tenant CMS app. I want each site to have their own Page tree. So Page uses @ordered_tree :scope => :site@ so that when I do this:
|
125
|
+
|
126
|
+
pre. @site_1 = Site.create :name => "First site"
|
127
|
+
@site_2 = Site.create :name => "Second site"
|
128
|
+
Page.create(:site => @site_1).position # returns 1
|
129
|
+
Page.create(:site => @site_2).position # returns 1
|
130
|
+
Page.create(:site => @site_2).position # returns 2
|
131
|
+
Page.create(:site => @site_1).position # returns 2
|
132
|
+
|
133
|
+
Warning: @Page.roots@ will always return all the root pages (all pages with parent_id of 0). That's because there's no way to know which pages you want to see. That means, when you want to get a site's root pages, go through the site: @@site.pages.roots@
|
134
|
+
|
135
|
+
Here are all the ways to define the scope:
|
136
|
+
|
137
|
+
h3. Give a symbol without _id:
|
138
|
+
|
139
|
+
@ordered_tree :scope => :site@
|
120
140
|
|
121
|
-
|
141
|
+
@ordered_tree@ will add @_id@ to @:site@ so it becomes: @:site_id@
|
122
142
|
|
123
|
-
|
143
|
+
Thus, you can also pass:
|
144
|
+
|
145
|
+
h3. Give a symbol with _id
|
146
|
+
|
147
|
+
@ordered_tree :scope => :site_id@
|
148
|
+
|
149
|
+
h3. Override the scope_condition method
|
150
|
+
|
151
|
+
For more complex stuff, in the model that has @ordered_tree@, define a method like this:
|
152
|
+
|
153
|
+
pre. def scope_condition
|
154
|
+
"site_id = #{site_id} AND user_login = '#{user_login}'"
|
155
|
+
end
|
156
|
+
|
157
|
+
h2. Install
|
158
|
+
|
159
|
+
gem 'ordered_tree'
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.2
|
@@ -1,12 +1,7 @@
|
|
1
1
|
module OrderedTree
|
2
2
|
module ClassMethods
|
3
3
|
extend ActiveSupport::Concern
|
4
|
-
|
5
4
|
included do
|
6
|
-
belongs_to :parent_node, :class_name => name, :foreign_key => ordered_tree_config[:foreign_key]
|
7
|
-
has_many :child_nodes, :class_name => name, :foreign_key => ordered_tree_config[:foreign_key], :order => ordered_tree_config[:order]
|
8
|
-
scope :roots, lambda { { :conditions => {ordered_tree_config[:foreign_key] => 0}, :order => ordered_tree_config[:order].to_s } }
|
9
|
-
|
10
5
|
def foreign_key_column
|
11
6
|
:"#{ordered_tree_config[:foreign_key]}"
|
12
7
|
end
|
@@ -8,7 +8,8 @@ module OrderedTree
|
|
8
8
|
# return is cached
|
9
9
|
# use self_and_siblings(true) to force a reload
|
10
10
|
def self_and_siblings(reload = false)
|
11
|
-
parent(reload) ? parent.children(reload) : self.class.roots(
|
11
|
+
#parent(reload) ? parent.children(reload) : self.class.roots(scope_condition)
|
12
|
+
parent(reload) ? parent.children(reload) : self.class.roots(scope_condition)
|
12
13
|
end
|
13
14
|
|
14
15
|
# returns an array of the object's siblings, excluding itself
|
data/lib/ordered_tree.rb
CHANGED
@@ -25,6 +25,25 @@ module OrderedTree #:nodoc:
|
|
25
25
|
self.ordered_tree_config[:foreign_key] ||= :parent_id
|
26
26
|
self.ordered_tree_config[:order] ||= :position
|
27
27
|
self.ordered_tree_config.update(options) if options.is_a?(Hash)
|
28
|
+
|
29
|
+
belongs_to :parent_node, :class_name => self.name, :foreign_key => ordered_tree_config[:foreign_key]
|
30
|
+
has_many :child_nodes, :class_name => self.name, :foreign_key => ordered_tree_config[:foreign_key], :order => ordered_tree_config[:order]
|
31
|
+
scope :roots, lambda { |*args|
|
32
|
+
scope_condition = args[0]
|
33
|
+
where(self.ordered_tree_config[:foreign_key].to_sym => 0).where(scope_condition).order(self.ordered_tree_config[:order])
|
34
|
+
}
|
35
|
+
|
36
|
+
# If the scope is something like :person, then turn it into :person_id
|
37
|
+
if self.ordered_tree_config[:scope].is_a?(Symbol) && self.ordered_tree_config[:scope].to_s !~ /_id$/
|
38
|
+
self.ordered_tree_config[:scope] = "#{self.ordered_tree_config[:scope]}_id".intern
|
39
|
+
end
|
40
|
+
|
41
|
+
if self.ordered_tree_config[:scope].is_a?(Symbol) # ie :person_id
|
42
|
+
define_method "scope_condition" do
|
43
|
+
self.class.send(:sanitize_sql_hash_for_conditions, {self.class.ordered_tree_config[:scope].to_sym => send(self.class.ordered_tree_config[:scope].to_sym)})
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
28
47
|
include OrderedTree::ClassMethods
|
29
48
|
include OrderedTree::InstanceMethods
|
30
49
|
end #ordered_tree
|
data/ordered_tree.gemspec
CHANGED
@@ -5,16 +5,15 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{ordered_tree}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Ramon Tayag"]
|
12
|
-
s.date = %q{2011-06-
|
12
|
+
s.date = %q{2011-06-13}
|
13
13
|
s.description = %q{Uses parent_id and position to create an ordered tree.}
|
14
14
|
s.email = %q{ramon@tayag.net}
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE.txt",
|
17
|
-
"README.rdoc",
|
18
17
|
"README.textile"
|
19
18
|
]
|
20
19
|
s.files = [
|
@@ -24,7 +23,6 @@ Gem::Specification.new do |s|
|
|
24
23
|
"Gemfile.lock",
|
25
24
|
"Guardfile",
|
26
25
|
"LICENSE.txt",
|
27
|
-
"README.rdoc",
|
28
26
|
"README.textile",
|
29
27
|
"Rakefile",
|
30
28
|
"VERSION",
|
@@ -36,6 +34,7 @@ Gem::Specification.new do |s|
|
|
36
34
|
"lib/ordered_tree/instance_methods/misc.rb",
|
37
35
|
"lib/ordered_tree/instance_methods/tree.rb",
|
38
36
|
"ordered_tree.gemspec",
|
37
|
+
"spec/fixtures/page.rb",
|
39
38
|
"spec/fixtures/person.rb",
|
40
39
|
"spec/ordered_tree_spec.rb",
|
41
40
|
"spec/spec_helper.rb"
|
data/spec/fixtures/person.rb
CHANGED
data/spec/ordered_tree_spec.rb
CHANGED
@@ -6,6 +6,37 @@ describe OrderedTree do
|
|
6
6
|
@people = Person.all
|
7
7
|
end
|
8
8
|
|
9
|
+
describe "when a scope is supplied" do
|
10
|
+
# This is especially important for working with root items that may belong to different accounts
|
11
|
+
it "should only work within that scope" do
|
12
|
+
# when the scope is an association
|
13
|
+
ordered_tree Page, :scope => :person do
|
14
|
+
Page.create(:person => @people[0]).position.should == 1
|
15
|
+
Page.create(:person => @people[2]).position.should == 1
|
16
|
+
Page.create(:person => @people[0]).position.should == 2
|
17
|
+
Page.create(:person => @people[1]).position.should == 1
|
18
|
+
end
|
19
|
+
|
20
|
+
# when the scope is an association id
|
21
|
+
ordered_tree Page, :scope => :person_id do
|
22
|
+
Page.create(:person => @people[0]).position.should == 3
|
23
|
+
Page.create(:person => @people[2]).position.should == 2
|
24
|
+
Page.create(:person => @people[0]).position.should == 4
|
25
|
+
Page.create(:person => @people[1]).position.should == 2
|
26
|
+
end
|
27
|
+
|
28
|
+
# when the scope_condition method is overridden
|
29
|
+
Page.class_eval do
|
30
|
+
def scope_condition
|
31
|
+
"person_id = #{person_id} AND name = '#{name}'"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
Page.create(:person => @people[3], :name => "frankenstein").position.should == 1
|
35
|
+
Page.create(:person => @people[3], :name => "steiners").position.should == 1
|
36
|
+
Page.create(:person => @people[3], :name => "frankenstein").position.should == 2
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
9
40
|
describe "when assigning parent" do
|
10
41
|
it "should do a bunch of tests on validation" do
|
11
42
|
# "should not allow an ancestor of a node to be a child of that node"
|
data/spec/spec_helper.rb
CHANGED
@@ -5,6 +5,7 @@ require 'active_record'
|
|
5
5
|
require 'rspec'
|
6
6
|
require 'ordered_tree'
|
7
7
|
require 'spec/fixtures/person'
|
8
|
+
require 'spec/fixtures/page'
|
8
9
|
|
9
10
|
#Allow to connect to SQLite
|
10
11
|
ActiveRecord::Base.establish_connection(
|
@@ -20,7 +21,7 @@ RSpec.configure do |config|
|
|
20
21
|
end
|
21
22
|
|
22
23
|
def reset_database
|
23
|
-
%W(people).each do |table_name|
|
24
|
+
%W(people pages).each do |table_name|
|
24
25
|
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS '#{table_name}'")
|
25
26
|
end
|
26
27
|
ActiveRecord::Base.connection.create_table(:people) do |t|
|
@@ -29,6 +30,12 @@ def reset_database
|
|
29
30
|
t.string :name
|
30
31
|
#add_index :people, [:parent_id], :name => "index_people_on_parent_id"
|
31
32
|
end
|
33
|
+
ActiveRecord::Base.connection.create_table(:pages) do |t|
|
34
|
+
t.integer :parent_id, :null => false, :default => 0
|
35
|
+
t.integer :position
|
36
|
+
t.string :name
|
37
|
+
t.integer :person_id
|
38
|
+
end
|
32
39
|
end
|
33
40
|
|
34
41
|
def ordered_tree(klass, *opts)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ordered_tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 31
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 2
|
10
|
+
version: 0.1.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ramon Tayag
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-06-
|
18
|
+
date: 2011-06-13 00:00:00 +08:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -160,7 +160,6 @@ extensions: []
|
|
160
160
|
|
161
161
|
extra_rdoc_files:
|
162
162
|
- LICENSE.txt
|
163
|
-
- README.rdoc
|
164
163
|
- README.textile
|
165
164
|
files:
|
166
165
|
- .rvmrc
|
@@ -169,7 +168,6 @@ files:
|
|
169
168
|
- Gemfile.lock
|
170
169
|
- Guardfile
|
171
170
|
- LICENSE.txt
|
172
|
-
- README.rdoc
|
173
171
|
- README.textile
|
174
172
|
- Rakefile
|
175
173
|
- VERSION
|
@@ -181,6 +179,7 @@ files:
|
|
181
179
|
- lib/ordered_tree/instance_methods/misc.rb
|
182
180
|
- lib/ordered_tree/instance_methods/tree.rb
|
183
181
|
- ordered_tree.gemspec
|
182
|
+
- spec/fixtures/page.rb
|
184
183
|
- spec/fixtures/person.rb
|
185
184
|
- spec/ordered_tree_spec.rb
|
186
185
|
- spec/spec_helper.rb
|
data/README.rdoc
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
= ordered_tree
|
2
|
-
|
3
|
-
Description goes here.
|
4
|
-
|
5
|
-
== Contributing to ordered_tree
|
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) 2011 Ramon Tayag. See LICENSE.txt for
|
18
|
-
further details.
|
19
|
-
|