sequel_orderable 0.0.1

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/CHANGELOG ADDED
@@ -0,0 +1 @@
1
+ * 2007.12.02 Initial working revision.
data/COPYING ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2007 Sharon Rosner, Wayne E. Seguin, Aman Gupta
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to
5
+ deal in the Software without restriction, including without limitation the
6
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
+ sell copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,187 @@
1
+ = Sequel Orderable Plugin
2
+
3
+ Allows for model instances to be part of an ordered list,
4
+ based on a 'position' field in the database.
5
+
6
+ == Basic Usage
7
+
8
+ Load the plugin into the model:
9
+
10
+ is :orderable
11
+
12
+ Given:
13
+
14
+ class Item < Sequel::Model(:items)
15
+ set_schema do
16
+ primary_key :id
17
+ varchar :name
18
+ int :position
19
+ end
20
+ is :orderable, :field => :position
21
+ end
22
+
23
+ item = Item[1]
24
+
25
+ The plugin provides access to the previous and next item in the list
26
+
27
+ item.next
28
+ item.prev
29
+
30
+ And methods to change the position of an item (and update affected items accordingly)
31
+
32
+ item.move_to(new_position)
33
+ item.move_to_top
34
+ item.move_to_bottom
35
+ item.move_up
36
+ item.move_down
37
+
38
+ == Scoping
39
+
40
+ You can scope the position field by another field.
41
+
42
+ For example, to allow each user to have their own a distinct orderable list:
43
+
44
+ class UserItem < Sequel::Model(:items)
45
+ set_schema do
46
+ primary_key :id
47
+ varchar :name
48
+ int :user_id
49
+ int :pos
50
+ end
51
+ is :orderable, :field => :pos, :scope => :user_id
52
+ end
53
+
54
+ All the defined methods will operate within the 'user_id' field's scope.
55
+
56
+ == Examples
57
+
58
+ # Input: irb
59
+ require "sequel"
60
+
61
+ DB = Sequel.sqlite
62
+ class Item < Sequel::Model(:items)
63
+ set_schema do
64
+ primary_key :id
65
+ varchar :name
66
+ int :position
67
+ end
68
+ is :orderable, :field => :position
69
+ end
70
+
71
+ Item.create_table!
72
+ Item.create :name => "alice", :position => 2
73
+ Item.create :name => "bob", :position => 1
74
+ Item.create :name => "charlie", :position => 4
75
+ Item.create :name => "darwin", :position => 3
76
+
77
+ Item.print
78
+
79
+ Item[:name => "alice"].move_down
80
+ Item.print
81
+ Item[:name => "darwin"].move_to_top
82
+ Item.print
83
+ Item[:name => "alice"].next
84
+ Item.print
85
+ Item[:name => "bob"].prev
86
+ Item.print
87
+ Item[:name => "darwin"].move_to(3)
88
+ Item.print
89
+ Item[:name => "bob"].move_to_bottom
90
+ Item.print
91
+
92
+
93
+ # Output
94
+ >> Item.print
95
+ +--+-------+--------+
96
+ |id|name |position|
97
+ +--+-------+--------+
98
+ | 2|bob | 1|
99
+ | 1|alice | 2|
100
+ | 4|darwin | 3|
101
+ | 3|charlie| 4|
102
+ +--+-------+--------+
103
+ => nil
104
+
105
+ >> Item[:name => "alice"].move_down
106
+ => {:position=>3}
107
+
108
+ >> Item.print
109
+ +--+-------+--------+
110
+ |id|name |position|
111
+ +--+-------+--------+
112
+ | 2|bob | 1|
113
+ | 4|darwin | 2|
114
+ | 1|alice | 3|
115
+ | 3|charlie| 4|
116
+ +--+-------+--------+
117
+ => nil
118
+
119
+ >> Item[:name => "darwin"].move_to_top
120
+ => {:position=>1}
121
+
122
+ >> Item.print
123
+ +--+-------+--------+
124
+ |id|name |position|
125
+ +--+-------+--------+
126
+ | 4|darwin | 1|
127
+ | 2|bob | 2|
128
+ | 1|alice | 3|
129
+ | 3|charlie| 4|
130
+ +--+-------+--------+
131
+ => nil
132
+
133
+ >> Item[:name => "alice"].next
134
+ => #<Item:0x119dbc8 @values={:position=>4, :name=>"charlie", :id=>3}, newfalse
135
+
136
+ >> Item.print
137
+ +--+-------+--------+
138
+ |id|name |position|
139
+ +--+-------+--------+
140
+ | 4|darwin | 1|
141
+ | 2|bob | 2|
142
+ | 1|alice | 3|
143
+ | 3|charlie| 4|
144
+ +--+-------+--------+
145
+ => nil
146
+
147
+ >> Item[:name => "bob"].prev
148
+ => #<Item:0x1184bb4 @values={:position=>1, :name=>"darwin", :id=>4}, newfalse
149
+
150
+ >> Item.print
151
+ +--+-------+--------+
152
+ |id|name |position|
153
+ +--+-------+--------+
154
+ | 4|darwin | 1|
155
+ | 2|bob | 2|
156
+ | 1|alice | 3|
157
+ | 3|charlie| 4|
158
+ +--+-------+--------+
159
+ => nil
160
+
161
+ >> Item[:name => "darwin"].move_to(3)
162
+ => {:position=>3}
163
+
164
+ >> Item.print
165
+ +--+-------+--------+
166
+ |id|name |position|
167
+ +--+-------+--------+
168
+ | 2|bob | 1|
169
+ | 1|alice | 2|
170
+ | 4|darwin | 3|
171
+ | 3|charlie| 4|
172
+ +--+-------+--------+
173
+ => nil
174
+
175
+ >> Item[:name => "bob"].move_to_bottom
176
+ => {:position=>4}
177
+
178
+ >> Item.print
179
+ +--+-------+--------+
180
+ |id|name |position|
181
+ +--+-------+--------+
182
+ | 1|alice | 1|
183
+ | 4|darwin | 2|
184
+ | 3|charlie| 3|
185
+ | 2|bob | 4|
186
+ +--+-------+--------+
187
+ => nil
data/Rakefile ADDED
@@ -0,0 +1,158 @@
1
+ ##############################################################################
2
+ # Constants
3
+ ##############################################################################
4
+
5
+ PluginName = "sequel_orderable"
6
+ Version = "0.0.1"
7
+ Title = "Orderable Sequel Plugin"
8
+ Summary = "Sequel Plugin"
9
+ Authors = "Wayne E. Seguin & Aman Gupta"
10
+ Emails = "wayneeseugin@gmail.com,sequel@tmm1.net"
11
+ Homepage = "http://sequel.rubyforge.org"
12
+
13
+ ##############################################################################
14
+ # Gem Management
15
+ ##############################################################################
16
+ require "rake"
17
+ require "rake/clean"
18
+ require "rake/gempackagetask"
19
+ require "rake/rdoctask"
20
+ require "fileutils"
21
+
22
+ include FileUtils
23
+
24
+ CLEAN.include ["**/.*.sw?", "pkg/*", ".config", "doc/*", "coverage/*"]
25
+
26
+ RDocOptions = [
27
+ "--quiet", "--title", Title,
28
+ "--opname", "index.html",
29
+ "--line-numbers",
30
+ "--main", "README",
31
+ "--inline-source"
32
+ ]
33
+
34
+ desc "Packages up the Sequel Plugin: #{PluginName}."
35
+ task :default => [:package]
36
+ task :package => [:clean]
37
+
38
+ task :doc => [:rdoc]
39
+
40
+ Rake::RDocTask.new do |rdoc|
41
+ rdoc.rdoc_dir = "doc/rdoc"
42
+ rdoc.options += RDocOptions
43
+ rdoc.main = "README"
44
+ rdoc.title = Title
45
+ rdoc.rdoc_files.add ["README", "COPYING", "lib/#{PluginName}.rb", "lib/**/*.rb"]
46
+ end
47
+
48
+ spec = Gem::Specification.new do |s|
49
+ s.name = PluginName
50
+ s.version = Version
51
+ s.platform = Gem::Platform::RUBY
52
+ s.has_rdoc = true
53
+ s.extra_rdoc_files = ["README", "CHANGELOG", "COPYING"]
54
+ s.rdoc_options += RDocOptions# +
55
+ #["--exclude", "^(examples|extras)\/", "--exclude", "lib/sequel.rb"]
56
+ s.summary = Summary
57
+ s.description = Summary
58
+ s.author = Authors
59
+ s.email = Emails
60
+ s.homepage = Homepage
61
+ # change this to the plugin name, if the plugin has command line portion
62
+ #s.executables = ["sequel"]
63
+
64
+ s.add_dependency("sequel")
65
+ #s.add_dependency("sequel", ">= 0.4.1")
66
+
67
+ #s.required_ruby_version = ">= 1.8.4" # Sequel should take care of this :)
68
+
69
+ s.files = %w(COPYING README Rakefile) + Dir.glob("{bin,doc,spec,lib}/**/*")
70
+
71
+ s.require_path = "lib"
72
+ s.bindir = "bin"
73
+ end
74
+
75
+ Rake::GemPackageTask.new(spec) do |p|
76
+ p.need_tar = true
77
+ p.gem_spec = spec
78
+ end
79
+
80
+ task :release => [:package] do
81
+ sh %{rubyforge login}
82
+ sh %{rubyforge add_release sequel #{PluginName} #{Version} pkg/#{PluginName}-#{Version}.tgz}
83
+ sh %{rubyforge add_file sequel #{PluginName} #{Version} pkg/#{PluginName}-#{Version}.gem}
84
+ end
85
+
86
+ task :install do
87
+ sh %{rake package}
88
+ sh %{sudo gem install pkg/#{PluginName}-#{Version}.gem}
89
+ end
90
+
91
+ task :install_no_docs do
92
+ sh %{rake package}
93
+ sh %{sudo gem install pkg/#{PluginName}-#{Version} --no-rdoc --no-ri}
94
+ end
95
+
96
+ task :uninstall => [:clean] do
97
+ sh %{sudo gem uninstall #{PluginName}}
98
+ end
99
+
100
+ desc "Update docs and upload to rubyforge.org"
101
+ task :doc_rforge do
102
+ sh %{rake doc}
103
+ sh %{scp -r doc/rdoc/* ciconia@rubyforge.org:/var/www/gforge-projects/sequel/plugins/#{PluginName}}
104
+ end
105
+
106
+ ##############################################################################
107
+ # rSpec
108
+ ##############################################################################
109
+
110
+ require "spec/rake/spectask"
111
+
112
+ desc "Run specs with coverage"
113
+ Spec::Rake::SpecTask.new("spec") do |spec_task|
114
+ spec_task.spec_opts = File.read("spec/spec.opts").split("\n")
115
+ spec_task.spec_files = FileList["spec/*_spec.rb"].sort
116
+ spec_task.rcov = true
117
+ end
118
+
119
+ desc "Run specs without coverage"
120
+ Spec::Rake::SpecTask.new("spec_no_cov") do |spec_task|
121
+ spec_task.spec_opts = File.read("spec/spec.opts").split("\n")
122
+ spec_task.spec_files = FileList["spec/*_spec.rb"].sort
123
+ end
124
+
125
+ desc "Run all specs with coverage"
126
+ Spec::Rake::SpecTask.new("specs") do |spec_task|
127
+ spec_task.spec_opts = File.read("spec/spec.opts").split("\n")
128
+ spec_task.spec_files = FileList["spec/**/*_spec.rb"].sort
129
+ spec_task.rcov = true
130
+ end
131
+
132
+ desc "Run all specs without coverage"
133
+ Spec::Rake::SpecTask.new("specs_no_cov") do |spec_task|
134
+ spec_task.spec_opts = File.read("spec/spec.opts").split("\n")
135
+ spec_task.spec_files = FileList["spec/**/*_spec.rb"].sort
136
+ end
137
+
138
+ desc "Run all specs and output html"
139
+ Spec::Rake::SpecTask.new("specs_html") do |spec_task|
140
+ spec_task.spec_opts = ["--format", "html"]
141
+ spec_task.spec_files = Dir["spec/**/*_spec.rb"].sort
142
+ end
143
+
144
+ ##############################################################################
145
+ # Statistics
146
+ ##############################################################################
147
+
148
+ STATS_DIRECTORIES = [
149
+ %w(Code lib/),
150
+ %w(Spec spec/)
151
+ ].collect { |name, dir| [ name, "./#{dir}" ] }.select { |name, dir| File.directory?(dir) }
152
+
153
+ desc "Report code statistics (KLOCs, etc) from the application"
154
+ task :stats do
155
+ require "extra/stats"
156
+ verbose = true
157
+ CodeStatistics.new(*STATS_DIRECTORIES).to_s
158
+ end
@@ -0,0 +1,84 @@
1
+ module Sequel
2
+ module Plugins
3
+ module Orderable
4
+
5
+ def self.apply(model, opts = {})
6
+ position_field = opts[:field] || :position
7
+ scope_field = opts[:scope]
8
+
9
+ model.class_def(:at_position) do |p|
10
+ if scope_field
11
+ dataset.first(scope_field => @values[scope_field], position_field => p)
12
+ else
13
+ dataset.first(position_field => p)
14
+ end
15
+ end
16
+
17
+ model.class_def(:move_to) do |pos|
18
+ # XXX: error checking, negative pos?
19
+ cur_pos = position
20
+ return self if pos == cur_pos
21
+
22
+ db.transaction do
23
+ if pos < cur_pos
24
+ ds = self.class.filter {position_field >= pos and position_field < cur_pos}
25
+ ds.filter!(scope_field => @values[scope_field]) if scope_field
26
+ ds.update(position_field => "#{position_field} + 1".lit)
27
+ elsif pos > cur_pos
28
+ ds = self.class.filter {position_field > cur_pos and position_field <= pos}
29
+ ds.filter!(scope_field => @values[scope_field]) if scope_field
30
+ ds.update(position_field => "#{position_field} - 1".lit)
31
+ end
32
+ set(position_field => pos)
33
+ end
34
+ end
35
+
36
+ model.class_def(:move_to_bottom) do
37
+ ds = dataset
38
+ ds = ds.filter(scope_field => @values[scope_field]) if scope_field
39
+ last = ds.select(:max[position_field] => :max).first.values[:max].to_i
40
+ self.move_to(last)
41
+ end
42
+
43
+ model.class_def(:position) {self[position_field]}
44
+
45
+ if scope_field
46
+ model.dataset.order!(scope_field, position_field)
47
+ else
48
+ model.dataset.order!(position_field)
49
+ end
50
+
51
+ model.send(:include, InstanceMethods)
52
+ end
53
+
54
+ module InstanceMethods
55
+ def prev(n = 1)
56
+ target = position - n
57
+ # XXX: error checking, negative target?
58
+ return self if position == target
59
+ at_position(target)
60
+ end
61
+
62
+ def next(n = 1)
63
+ target = position + n
64
+ at_position(target)
65
+ end
66
+
67
+ def move_up(n = 1)
68
+ # XXX: position == 1 already?
69
+ self.move_to(position-n)
70
+ end
71
+
72
+ def move_down(n = 1)
73
+ # XXX: what if we're already at the bottom
74
+ self.move_to(position+n)
75
+ end
76
+
77
+ def move_to_top
78
+ self.move_to(1)
79
+ end
80
+ end
81
+
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,116 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ DB = Sequel.sqlite
4
+
5
+ class Item < Sequel::Model(:items)
6
+
7
+ set_schema do
8
+ primary_key :id
9
+ varchar :name
10
+ int :pos
11
+ end
12
+
13
+ is :orderable, :field => :pos
14
+
15
+ end
16
+
17
+ describe Item do
18
+ before(:all) {
19
+ Item.create_table!
20
+
21
+ Item.create :name => "one", :pos => 3
22
+ Item.create :name => "two", :pos => 2
23
+ Item.create :name => "three", :pos => 1
24
+ }
25
+
26
+ it "should return rows in order of position" do
27
+ Item.map(&:pos).should == [1,2,3]
28
+ Item.map(&:name).should == %w[ three two one ]
29
+ end
30
+
31
+ it "should define prev and next" do
32
+ i = Item[:name => "two"]
33
+ i.prev.should == Item[:name => "three"]
34
+ i.next.should == Item[:name => "one"]
35
+ end
36
+
37
+ it "should define move_to" do
38
+ Item[:name => "two"].move_to(1)
39
+ Item.map(&:name).should == %w[ two three one ]
40
+
41
+ Item[:name => "two"].move_to(3)
42
+ Item.map(&:name).should == %w[ three one two ]
43
+ end
44
+
45
+ it "should define move_to_top and move_to_bottom" do
46
+ Item[:name => "two"].move_to_top
47
+ Item.map(&:name).should == %w[ two three one ]
48
+
49
+ Item[:name => "two"].move_to_bottom
50
+ Item.map(&:name).should == %w[ three one two ]
51
+ end
52
+
53
+ it "should define move_up and move_down" do
54
+ Item[:name => "one"].move_up
55
+ Item.map(&:name).should == %w[ one three two ]
56
+
57
+ Item[:name => "three"].move_down
58
+ Item.map(&:name).should == %w[ one two three ]
59
+ end
60
+
61
+ end
62
+
63
+ class ListItem < Sequel::Model(:list_items)
64
+
65
+ set_schema do
66
+ primary_key :id
67
+ int :list_id
68
+ varchar :name
69
+ int :position
70
+ end
71
+
72
+ is :orderable, :scope => :list_id
73
+
74
+ end
75
+
76
+ describe ListItem do
77
+
78
+ before(:all) {
79
+ ListItem.create_table!
80
+
81
+ ListItem.create :name => "a", :list_id => 1, :position => 3
82
+ ListItem.create :name => "b", :list_id => 1, :position => 2
83
+ ListItem.create :name => "c", :list_id => 1, :position => 1
84
+
85
+ ListItem.create :name => "d", :list_id => 2, :position => 1
86
+ ListItem.create :name => "e", :list_id => 2, :position => 2
87
+ ListItem.create :name => "f", :list_id => 2, :position => 3
88
+ }
89
+
90
+ it "should print in order with scope provided" do
91
+ ListItem.map(&:name).should == %w[ c b a d e f ]
92
+ end
93
+
94
+ it "should fetch prev and next records with scope" do
95
+ b = ListItem[:name => "b"]
96
+ b.next.name.should == "a"
97
+ b.prev.name.should == "c"
98
+ b.next.next.should be_nil
99
+ b.prev.prev.should be_nil
100
+
101
+ e = ListItem[:name => "e"]
102
+ e.next.name.should == "f"
103
+ e.prev.name.should == "d"
104
+ e.next.next.should be_nil
105
+ e.prev.prev.should be_nil
106
+ end
107
+
108
+ it "should move only within the scope provided" do
109
+ ListItem[:name => "b"].move_to_top
110
+ ListItem.map(&:name).should == %w[ b c a d e f ]
111
+
112
+ ListItem[:name => "c"].move_to_bottom
113
+ ListItem.map(&:name).should == %w[ b a c d e f ]
114
+ end
115
+
116
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,6 @@
1
+ --colour
2
+ --loadby
3
+ mtime
4
+ --backtrace
5
+ --format
6
+ specdoc
@@ -0,0 +1,6 @@
1
+ require "sequel"
2
+ require File.join(File.dirname(__FILE__), "../lib/sequel_orderable")
3
+
4
+ class Symbol
5
+ def to_proc() lambda{ |object, *args| object.send(self, *args) } end
6
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sequel_orderable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Wayne E. Seguin & Aman Gupta
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2007-12-04 00:00:00 +02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: sequel
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: "0"
23
+ version:
24
+ description: Sequel Plugin
25
+ email: wayneeseugin@gmail.com,sequel@tmm1.net
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files:
31
+ - README
32
+ - CHANGELOG
33
+ - COPYING
34
+ files:
35
+ - COPYING
36
+ - README
37
+ - Rakefile
38
+ - spec/sequel_orderable_spec.rb
39
+ - spec/spec.opts
40
+ - spec/spec_helper.rb
41
+ - lib/sequel_orderable.rb
42
+ - CHANGELOG
43
+ has_rdoc: true
44
+ homepage: http://sequel.rubyforge.org
45
+ post_install_message:
46
+ rdoc_options:
47
+ - --quiet
48
+ - --title
49
+ - Orderable Sequel Plugin
50
+ - --opname
51
+ - index.html
52
+ - --line-numbers
53
+ - --main
54
+ - README
55
+ - --inline-source
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ version:
70
+ requirements: []
71
+
72
+ rubyforge_project:
73
+ rubygems_version: 0.9.5
74
+ signing_key:
75
+ specification_version: 2
76
+ summary: Sequel Plugin
77
+ test_files: []
78
+