sequel_plus 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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