sequel_plus 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2009 Michael Lang. All rights reserved.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ 'Software'), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # sequel_plus
2
+
3
+ This library starts the collection of plugins and possibly extension I assemble for the Ruby Sequel
4
+ ORM.
5
+
6
+ Currently, it only contains a plugin for Trees to mimic the Rails acts_as_tree plugin.
7
+
8
+ NOTE: Authors of other plugins and extensions for Sequel are welcome to contact me for inclusion
9
+ of your plugin and extension to this project.
10
+
11
+ Released under MIT license.
12
+
13
+ # For the Impatient
14
+
15
+ This gem is released to gemcutter. Rubyforge is not utilized.
16
+
17
+ gem install sequel_plus
18
+
19
+ And then, in your project:
20
+
21
+ require 'sequel'
22
+ require 'sequel_plus'
23
+
24
+ class Node < Sequel::Model
25
+ plugin :tree
26
+ end
27
+
28
+ # Note on Patches/Pull Requests
29
+
30
+ * First release!
31
+
32
+ # Copyright
33
+
34
+ Copyright 2009 Michael Lang. All rights reserved.
35
+ Released under MIT license. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,62 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "sequel_plus"
8
+ gem.summary = "provides plugins and extensions for Sequel"
9
+ gem.description = "Provides plugins and extensions for Sequel"
10
+ gem.email = "mwlang@cybrains.net"
11
+ gem.homepage = "http://github.com/mwlang/sequel_plus"
12
+ gem.authors = ["Michael Lang"]
13
+ gem.files = [
14
+ "LICENSE",
15
+ "README.md",
16
+ "Rakefile",
17
+ "lib/sequel_plus.rb",
18
+ "lib/**/*",
19
+ "test/*"
20
+ ]
21
+
22
+ gem.add_development_dependency "bacon", ">= 1.0.0"
23
+ gem.add_dependency "sequel", ">= 3.0.0"
24
+ end
25
+ Jeweler::GemcutterTasks.new
26
+ rescue LoadError
27
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
28
+ end
29
+
30
+ require 'rake/testtask'
31
+ Rake::TestTask.new(:test) do |test|
32
+ test.libs << 'lib' << 'test'
33
+ test.pattern = 'test/**/test_*.rb'
34
+ test.verbose = true
35
+ end
36
+
37
+ begin
38
+ require 'rcov/rcovtask'
39
+ Rcov::RcovTask.new do |test|
40
+ test.libs << 'test'
41
+ test.pattern = 'test/**/test_*.rb'
42
+ test.verbose = true
43
+ end
44
+ rescue LoadError
45
+ task :rcov do
46
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
47
+ end
48
+ end
49
+
50
+ task :test => :check_dependencies
51
+
52
+ task :default => :test
53
+
54
+ require 'rake/rdoctask'
55
+ Rake::RDocTask.new do |rdoc|
56
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
57
+
58
+ rdoc.rdoc_dir = 'rdoc'
59
+ rdoc.title = "sequel_plus #{version}"
60
+ rdoc.rdoc_files.include('README*')
61
+ rdoc.rdoc_files.include('lib/**/*.rb')
62
+ end
@@ -0,0 +1,90 @@
1
+ module Sequel
2
+ module Plugins
3
+ # The Tree plugin adds additional associations and methods that allow you to
4
+ # treat a Model as a tree.
5
+ #
6
+ # A column for holding the parent key is required and is :parent_id by default.
7
+ # This may be overridden by passing column name via :key
8
+ #
9
+ # Optionally, a column to control order of nodes returned can be specified
10
+ # by passing column name via :order.
11
+ #
12
+ # Examples:
13
+ #
14
+ # class Node < Sequel::Model
15
+ # plugin :tree
16
+ # end
17
+ #
18
+ # class OrderedNode < Sequel::Model(:nodes)
19
+ # plugin :tree, :order => :position
20
+ # end
21
+ #
22
+ module Tree
23
+ def self.configure(model, opts = {})
24
+ model.instance_eval do
25
+ @parent_column = opts[:key] || :parent_id
26
+ @order_column = opts[:order]
27
+
28
+ many_to_one :parent, :class => self, :key => @parent_column
29
+ one_to_many :children, :class => self, :key => @parent_column, :order => @order_column
30
+ end
31
+ end
32
+
33
+ module ClassMethods
34
+ # Returns list of all root nodes (those with no parent nodes).
35
+ #
36
+ # TreeClass.roots # => [root1, root2]
37
+ def roots
38
+ roots_dataset.all
39
+ end
40
+
41
+ # Returns the dataset for retrieval of all root nodes
42
+ #
43
+ # TreeClass.roots_dataset => Sequel#Dataset
44
+ def roots_dataset
45
+ filter(@parent_column => nil).order(@order_column)
46
+ end
47
+ end
48
+
49
+ module InstanceMethods
50
+ # Returns list of ancestors, starting from parent until root.
51
+ #
52
+ # subchild1.ancestors # => [child1, root]
53
+ def ancestors
54
+ node, nodes = self, []
55
+ nodes << node = node.parent while node.parent
56
+ nodes
57
+ end
58
+
59
+ # Returns list of ancestors, starting from parent until root.
60
+ #
61
+ # subchild1.ancestors # => [child1, root]
62
+ def descendants
63
+ nodes = self.children
64
+ self.children.each{|c| nodes + c.descendants}
65
+ nodes
66
+ end
67
+
68
+ # Returns the root node of the tree that this node descends from
69
+ # This node is returned if it is a root node itself.
70
+ def root
71
+ ancestors.last || self
72
+ end
73
+
74
+ # Returns all siblings of the current node.
75
+ #
76
+ # subchild1.siblings # => [subchild2]
77
+ def siblings
78
+ self_and_siblings - [self]
79
+ end
80
+
81
+ # Returns all siblings and a reference to the current node.
82
+ #
83
+ # subchild1.self_and_siblings # => [subchild1, subchild2]
84
+ def self_and_siblings
85
+ parent ? parent.children : model.roots
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1 @@
1
+ require 'sequel_plus/sequel_tree'
data/test/helper.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'sequel'
3
+ require 'bacon'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'sequel_plus'
@@ -0,0 +1,157 @@
1
+ require 'helper'
2
+
3
+ DB = Sequel.sqlite
4
+
5
+ DB.create_table :nodes do
6
+ primary_key :id
7
+ String :name
8
+ Integer :parent_id
9
+ Integer :position
10
+ end
11
+
12
+ NODES = [
13
+ {:id => 1, :name => 'one', :parent_id => nil, :position => 1},
14
+ {:id => 2, :name => 'two', :parent_id => nil, :position => 2},
15
+ {:id => 3, :name => 'three', :parent_id => nil, :position => 3},
16
+ {:id => 4, :name => "two.one", :parent_id => 2, :position => 1},
17
+ {:id => 5, :name => "two.two", :parent_id => 2, :position => 2},
18
+ {:id => 6, :name => "two.two.one", :parent_id => 5, :position => 1},
19
+ {:id => 7, :name => "one.two", :parent_id => 1, :position => 2},
20
+ {:id => 8, :name => "one.one", :parent_id => 1, :position => 1},
21
+ {:id => 9, :name => "five", :parent_id => nil, :position => 5},
22
+ {:id => 10, :name => "four", :parent_id => nil, :position => 4},
23
+ {:id => 11, :name => "five.one", :parent_id => 9, :position => 1},
24
+ {:id => 12, :name => "two.three", :parent_id => 2, :position => 3},
25
+ ]
26
+
27
+ DB.create_table :lorems do
28
+ primary_key :id
29
+ String :name
30
+ Integer :ipsum_id
31
+ Integer :neque
32
+ end
33
+
34
+ LOREMS = [
35
+ {:id => 1, :name => 'Lorem', :ipsum_id => nil, :neque => 4},
36
+ {:id => 2, :name => 'Ipsum', :ipsum_id => nil, :neque => 3},
37
+ {:id => 4, :name => "Neque", :ipsum_id => 2, :neque => 2},
38
+ {:id => 5, :name => "Porro", :ipsum_id => 2, :neque => 1},
39
+ ]
40
+
41
+ NODES.each{|node| DB[:nodes].insert(node)}
42
+ LOREMS.each{|lorem| DB[:lorems].insert(lorem)}
43
+
44
+ class Node < Sequel::Model
45
+ plugin :tree
46
+ end
47
+
48
+ class NaturalNode < Sequel::Model(:nodes)
49
+ plugin :tree
50
+ end
51
+
52
+ class OrderedNode < Sequel::Model(:nodes)
53
+ plugin :tree, :order => :position
54
+ end
55
+
56
+ class Lorem < Sequel::Model
57
+ plugin :tree, :key => :ipsum_id, :order => :neque
58
+ end
59
+
60
+ describe Sequel::Plugins::Tree do
61
+
62
+ it "should instantiate" do
63
+ Node.all.size.should == 12
64
+ end
65
+
66
+ it "should find top level nodes" do
67
+ Node.roots.count.should == 5
68
+ end
69
+
70
+ it "should find all descendants of a node" do
71
+ two = Node.find(:id => 2)
72
+ two.name.should == "two"
73
+ two.descendants.map{|m| m[:id]}.should == [4, 5, 12]
74
+ end
75
+
76
+ it "should find all ancestors of a node" do
77
+ twotwoone = Node.find(:id => 6)
78
+ twotwoone.name.should == "two.two.one"
79
+ twotwoone.ancestors.map{|m| m[:id]}.should == [5, 2]
80
+ end
81
+
82
+ it "should find all siblings of a node, excepting self" do
83
+ twoone = Node.find(:id => 4)
84
+ twoone.name.should == "two.one"
85
+ twoone.siblings.map{|m| m[:id]}.should == [5, 12]
86
+ end
87
+
88
+ it "should find all siblings of a node, including self" do
89
+ twoone = Node.find(:id => 4)
90
+ twoone.name.should == "two.one"
91
+ twoone.self_and_siblings.map{|m| m[:id]}.should == [4, 5, 12]
92
+ end
93
+
94
+ it "should find siblings for root nodes" do
95
+ three = Node.find(:id => 3)
96
+ three.name.should == "three"
97
+ three.self_and_siblings.map{|m| m[:id]}.should == [1, 2, 3, 9, 10]
98
+ end
99
+
100
+ it "should find correct root for a node" do
101
+ twotwoone = Node.find(:id => 6)
102
+ twotwoone.name.should == "two.two.one"
103
+ twotwoone.root[:id].should == 2
104
+
105
+ three = Node.find(:id => 3)
106
+ three.name.should == "three"
107
+ three.root[:id].should == 3
108
+
109
+ fiveone = Node.find(:id => 11)
110
+ fiveone.name.should == "five.one"
111
+ fiveone.root[:id].should == 9
112
+ end
113
+
114
+ describe "Nodes in natural database order" do
115
+ it "iterate top-level nodes in natural database order" do
116
+ NaturalNode.roots.count.should == 5
117
+ NaturalNode.roots.inject([]){|ids, p| ids << p.position}.should == [1, 2, 3, 5, 4]
118
+ end
119
+
120
+ it "should have children" do
121
+ one = NaturalNode.find(:id => 1)
122
+ one.name.should == "one"
123
+ one.children.count.should == 2
124
+ end
125
+
126
+ it "children should be natural database order" do
127
+ one = NaturalNode.find(:id => 1)
128
+ one.name.should == "one"
129
+ one.children.map{|m| m[:position]}.should == [2, 1]
130
+ end
131
+ end
132
+
133
+ describe "Nodes in specified order" do
134
+ it "iterate top-level nodes in order by position" do
135
+ OrderedNode.roots.count.should == 5
136
+ OrderedNode.roots.inject([]){|ids, p| ids << p.position}.should == [1, 2, 3, 4, 5]
137
+ end
138
+
139
+ it "children should be in specified order" do
140
+ one = OrderedNode.find(:id => 1)
141
+ one.name.should == "one"
142
+ one.children.map{|m| m[:position]}.should == [1, 2]
143
+ end
144
+ end
145
+
146
+ describe "Lorems in specified order" do
147
+ it "iterate top-level nodes in order by position" do
148
+ Lorem.roots.count.should == 2
149
+ Lorem.roots.inject([]){|ids, p| ids << p.neque}.should == [3, 4]
150
+ end
151
+
152
+ it "children should be specified order" do
153
+ one = Lorem.find(:id => 2)
154
+ one.children.map{|m| m[:neque]}.should == [1, 2]
155
+ end
156
+ end
157
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sequel_plus
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Michael Lang
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-12 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: bacon
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.0.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: sequel
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 3.0.0
34
+ version:
35
+ description: Provides plugins and extensions for Sequel
36
+ email: mwlang@cybrains.net
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - LICENSE
43
+ - README.md
44
+ files:
45
+ - LICENSE
46
+ - README.md
47
+ - Rakefile
48
+ - lib/sequel_plus.rb
49
+ - lib/sequel_plus/sequel_tree.rb
50
+ - test/helper.rb
51
+ - test/test_sequel_tree.rb
52
+ has_rdoc: true
53
+ homepage: http://github.com/mwlang/sequel_plus
54
+ licenses: []
55
+
56
+ post_install_message:
57
+ rdoc_options:
58
+ - --charset=UTF-8
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: "0"
66
+ version:
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.3.5
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: provides plugins and extensions for Sequel
80
+ test_files:
81
+ - test/helper.rb
82
+ - test/test_sequel_tree.rb