dm-is-list 0.9.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 +20 -0
- data/README +58 -0
- data/Rakefile +56 -0
- data/TODO +3 -0
- data/lib/dm-is-list.rb +13 -0
- data/lib/dm-is-list/is/list.rb +195 -0
- data/spec/integration/list_spec.rb +181 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +28 -0
- metadata +71 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Sindre Aarsaether (somebee.com)
|
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
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
dm-is-list
|
2
|
+
==========
|
3
|
+
|
4
|
+
DataMapper plugin for creating and organizing lists.
|
5
|
+
|
6
|
+
== Installation
|
7
|
+
|
8
|
+
Download dm-more and install dm-is-list. Remember to require it in your app.
|
9
|
+
|
10
|
+
== Getting started
|
11
|
+
|
12
|
+
Lets say we have a user-class, and we want to give users the possibility of
|
13
|
+
having their own todo-lists
|
14
|
+
|
15
|
+
class Todo
|
16
|
+
include DataMapper::Resource
|
17
|
+
|
18
|
+
property :id, Serial
|
19
|
+
property :title, String
|
20
|
+
property :done, DateTime
|
21
|
+
|
22
|
+
belongs_to :user
|
23
|
+
|
24
|
+
# here we define that this should be a list, scoped on :user_id
|
25
|
+
is :list, :scope => [:user_id]
|
26
|
+
end
|
27
|
+
|
28
|
+
You can now move objects around like this:
|
29
|
+
|
30
|
+
item = Todo.get(1)
|
31
|
+
other = Todo.get(2)
|
32
|
+
|
33
|
+
item.move(:highest) # moves to top of list
|
34
|
+
item.move(:lowest) # moves to bottom of list
|
35
|
+
item.move(:up) # moves one up (:higher and :up is the same)
|
36
|
+
item.move(:down) # moves one up (:lower and :down is the same)
|
37
|
+
item.move(:to => position) # moves item to a specific position
|
38
|
+
item.move(:above => other) # moves item above the other item.*
|
39
|
+
item.move(:below => other) # moves item above the other item.*
|
40
|
+
|
41
|
+
* won't move if the other item is in another scope. (should this be allowed?)
|
42
|
+
|
43
|
+
The list will try to act as intelligently as possible. If you set the position
|
44
|
+
manually, and then save, the list will reorganize itself to correctly:
|
45
|
+
|
46
|
+
item.position = 3 # setting position manually
|
47
|
+
item.save # the list will now move the item correctly, and updating others
|
48
|
+
|
49
|
+
If you move items between scopes, the list will also try to do what you most
|
50
|
+
likely want to do:
|
51
|
+
|
52
|
+
item.user_id # => 1
|
53
|
+
item.user_id = 2 # giving this item to another user
|
54
|
+
item.save # the list will now have detached item from old list, and inserted
|
55
|
+
# at the bottom of the new (user 2) list.
|
56
|
+
|
57
|
+
If something is not behaving intuitively, it is a bug, and should be reported.
|
58
|
+
Report it here: http://wm.lighthouseapp.com/projects/4819-datamapper/overview
|
data/Rakefile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
require 'spec/rake/spectask'
|
6
|
+
require 'pathname'
|
7
|
+
|
8
|
+
CLEAN.include '{log,pkg}/'
|
9
|
+
|
10
|
+
spec = Gem::Specification.new do |s|
|
11
|
+
s.name = 'dm-is-list'
|
12
|
+
s.version = '0.9.2'
|
13
|
+
s.platform = Gem::Platform::RUBY
|
14
|
+
s.has_rdoc = true
|
15
|
+
s.extra_rdoc_files = %w[ README LICENSE TODO ]
|
16
|
+
s.summary = 'DataMapper plugin for creating and organizing lists'
|
17
|
+
s.description = s.summary
|
18
|
+
s.author = 'Sindre Aarsaether'
|
19
|
+
s.email = 'sindre [a] identu [d] no'
|
20
|
+
s.homepage = 'http://github.com/sam/dm-more/tree/master/dm-is-list'
|
21
|
+
s.require_path = 'lib'
|
22
|
+
s.files = FileList[ '{lib,spec}/**/*.rb', 'spec/spec.opts', 'Rakefile', *s.extra_rdoc_files ]
|
23
|
+
s.add_dependency('dm-core', "=#{s.version}")
|
24
|
+
end
|
25
|
+
|
26
|
+
task :default => [ :spec ]
|
27
|
+
|
28
|
+
WIN32 = (RUBY_PLATFORM =~ /win32|mingw|cygwin/) rescue nil
|
29
|
+
SUDO = WIN32 ? '' : ('sudo' unless ENV['SUDOLESS'])
|
30
|
+
|
31
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
32
|
+
pkg.gem_spec = spec
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "Install #{spec.name} #{spec.version} (default ruby)"
|
36
|
+
task :install => [ :package ] do
|
37
|
+
sh "#{SUDO} gem install --local pkg/#{spec.name}-#{spec.version} --no-update-sources", :verbose => false
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "Uninstall #{spec.name} #{spec.version} (default ruby)"
|
41
|
+
task :uninstall => [ :clobber ] do
|
42
|
+
sh "#{SUDO} gem uninstall #{spec.name} -v#{spec.version} -I -x", :verbose => false
|
43
|
+
end
|
44
|
+
|
45
|
+
namespace :jruby do
|
46
|
+
desc "Install #{spec.name} #{spec.version} with JRuby"
|
47
|
+
task :install => [ :package ] do
|
48
|
+
sh %{#{SUDO} jruby -S gem install --local pkg/#{spec.name}-#{spec.version} --no-update-sources}, :verbose => false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
desc 'Run specifications'
|
53
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
54
|
+
t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
|
55
|
+
t.spec_files = Pathname.glob(Pathname.new(__FILE__).dirname + 'spec/**/*_spec.rb')
|
56
|
+
end
|
data/lib/dm-is-list.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
gem 'dm-core', '=0.9.2'
|
6
|
+
require 'dm-core'
|
7
|
+
|
8
|
+
gem 'dm-adjust', '=0.9.2'
|
9
|
+
require 'dm-adjust'
|
10
|
+
|
11
|
+
require Pathname(__FILE__).dirname.expand_path / 'dm-is-list' / 'is' / 'list.rb'
|
12
|
+
|
13
|
+
DataMapper::Model.append_extensions DataMapper::Is::List
|
@@ -0,0 +1,195 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Is
|
3
|
+
module List
|
4
|
+
|
5
|
+
##
|
6
|
+
# method for making your model a list.
|
7
|
+
# it will define a :position property if it does not exist, so be sure to have a
|
8
|
+
# position-column in your database (will be added automatically on auto_migrate)
|
9
|
+
# if the column has a different name, simply make a :position-property and set a
|
10
|
+
# custom :field
|
11
|
+
#
|
12
|
+
# @example [Usage]
|
13
|
+
# is :list # put this in your model to make it act as a list.
|
14
|
+
# is :list, :scope => [:user_id] # you can also define scopes
|
15
|
+
# is :list, :scope => [:user_id, :context_id] # also works with multiple params
|
16
|
+
#
|
17
|
+
# @param options <Hash> a hash of options
|
18
|
+
#
|
19
|
+
# @option :scope<Array> an array of attributes that should be used to scope lists
|
20
|
+
#
|
21
|
+
def is_list(options={})
|
22
|
+
options = { :scope => [] }.merge(options)
|
23
|
+
|
24
|
+
extend DataMapper::Is::List::ClassMethods
|
25
|
+
include DataMapper::Is::List::InstanceMethods
|
26
|
+
|
27
|
+
property :position, Integer unless properties.detect{|p| p.name == :position && p.type == Integer}
|
28
|
+
|
29
|
+
@list_scope = options[:scope]
|
30
|
+
|
31
|
+
before :save do
|
32
|
+
if self.new_record?
|
33
|
+
# a position has been set before save => open up and make room for item
|
34
|
+
# no position has been set => move to bottom of my scope-list (or keep detached?)
|
35
|
+
self.position ? self.move_without_saving(:to => self.position) : self.move_without_saving(:lowest)
|
36
|
+
else
|
37
|
+
# if the scope has changed, we need to detach our item from the old list
|
38
|
+
if self.list_scope != self.original_list_scope
|
39
|
+
oldpos = self.original_values[:position]
|
40
|
+
newpos = self.position
|
41
|
+
|
42
|
+
self.detach(self.original_list_scope) # removing from old list
|
43
|
+
self.move_without_saving(oldpos ? {:to => newpos} : :lowest) # moving to pos or bottom of new list
|
44
|
+
|
45
|
+
elsif self.attribute_dirty?(:position)
|
46
|
+
self.move_without_saving(:to => self.position)
|
47
|
+
end
|
48
|
+
# a (new) position has been set => move item to this position (only if position has been set manually)
|
49
|
+
# the scope has changed => detach from old list, and possibly move into position
|
50
|
+
# the scope and position has changed => detach from old, move to pos in new
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
before :destroy do
|
55
|
+
self.detach
|
56
|
+
end
|
57
|
+
|
58
|
+
# we need to make sure that STI-models will inherit the list_scope.
|
59
|
+
after_class_method :inherited do |target|
|
60
|
+
target.instance_variable_set(:@list_scope, @list_scope.dup)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
module ClassMethods
|
66
|
+
attr_reader :list_scope
|
67
|
+
|
68
|
+
##
|
69
|
+
# use this function to repair / build your lists.
|
70
|
+
#
|
71
|
+
# @example [Usage]
|
72
|
+
# MyModel.repair_list # repairs the list, given that lists are not scoped
|
73
|
+
# MyModel.repair_list(:user_id => 1) # fixes the list for user 1, given that the scope is [:user_id]
|
74
|
+
#
|
75
|
+
# @param scope [Hash]
|
76
|
+
#
|
77
|
+
def repair_list(scope={})
|
78
|
+
return false unless scope.keys.all?{|s| list_scope.include?(s) || s == :order }
|
79
|
+
all({:order => [:position.asc]}.merge(scope)).each_with_index{ |item,i| item.position = i+1; item.save }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
module InstanceMethods
|
84
|
+
|
85
|
+
def list_scope
|
86
|
+
self.class.list_scope.map{|p| [p,attribute_get(p)]}.to_hash
|
87
|
+
end
|
88
|
+
|
89
|
+
def original_list_scope
|
90
|
+
self.class.list_scope.map{|p| [p,original_values.key?(p) ? original_values[p] : attribute_get(p)]}.to_hash
|
91
|
+
end
|
92
|
+
|
93
|
+
def list_query
|
94
|
+
list_scope.merge(:order => [:position.asc])
|
95
|
+
end
|
96
|
+
|
97
|
+
def list(scope=list_query)
|
98
|
+
self.class.all(scope)
|
99
|
+
end
|
100
|
+
|
101
|
+
##
|
102
|
+
# repair the list this item belongs to
|
103
|
+
#
|
104
|
+
def repair_list
|
105
|
+
self.class.repair_list(list_scope)
|
106
|
+
end
|
107
|
+
|
108
|
+
##
|
109
|
+
# reorder the list this item belongs to
|
110
|
+
#
|
111
|
+
def reorder_list(order)
|
112
|
+
self.class.repair_list(list_scope.merge(:order => order))
|
113
|
+
end
|
114
|
+
|
115
|
+
##
|
116
|
+
# move item to a position in the list. position should _only_ be changed through this
|
117
|
+
#
|
118
|
+
# @example [Usage]
|
119
|
+
# * node.move :higher # moves node higher unless it is at the top of parent
|
120
|
+
# * node.move :lower # moves node lower unless it is at the bottom of parent
|
121
|
+
# * node.move :below => other # moves this node below other resource in the set
|
122
|
+
#
|
123
|
+
# @param vector <Symbol, Hash> A symbol, or a key-value pair that describes the requested movement
|
124
|
+
#
|
125
|
+
# @option :higher<Symbol> move item higher
|
126
|
+
# @option :up<Symbol> move item higher
|
127
|
+
# @option :highest<Symbol> move item to the top of the list
|
128
|
+
# @option :lower<Symbol> move item lower
|
129
|
+
# @option :down<Symbol> move item lower
|
130
|
+
# @option :lowest<Symbol> move item to the bottom of the list
|
131
|
+
# @option :above<Resource> move item above other item. must be in same scope
|
132
|
+
# @option :below<Resource> move item below other item. must be in same scope
|
133
|
+
# @option :to<Fixnum> move item to a specific location in the list
|
134
|
+
#
|
135
|
+
# @return <TrueClass, FalseClass> returns false if it cannot move to the position, otherwise true
|
136
|
+
# @see move_without_saving
|
137
|
+
def move(vector)
|
138
|
+
move_without_saving(vector)
|
139
|
+
save
|
140
|
+
end
|
141
|
+
|
142
|
+
##
|
143
|
+
# does all the actual movement in #move, but does not save afterwards. this is used internally in
|
144
|
+
# before :save, and will probably be marked private. should not be used by organic beings.
|
145
|
+
#
|
146
|
+
# @see move_without_saving
|
147
|
+
def move_without_saving(vector)
|
148
|
+
if vector.is_a? Hash then action,object = vector.keys[0],vector.values[0] else action = vector end
|
149
|
+
|
150
|
+
prepos = self.original_values[:position]||self.position
|
151
|
+
maxpos = list.last ? (list.last == self ? prepos : list.last.position + 1) : 1
|
152
|
+
newpos = case action
|
153
|
+
when :highest then 1
|
154
|
+
when :lowest then maxpos
|
155
|
+
when :higher,:up then [position-1,1].max
|
156
|
+
when :lower,:down then [position+1,maxpos].min
|
157
|
+
when :above then object.position
|
158
|
+
when :below then object.position+1
|
159
|
+
when :to then [object.to_i,maxpos].min
|
160
|
+
end
|
161
|
+
|
162
|
+
return false if !newpos || ([:above,:below].include?(action) && list_scope != object.list_scope)
|
163
|
+
return true if newpos == position || (newpos == maxpos && position == maxpos-1)
|
164
|
+
|
165
|
+
if !position
|
166
|
+
list.all(:position.gte => newpos).adjust!({:position => +1},true) unless action == :lowest
|
167
|
+
elsif newpos > position
|
168
|
+
newpos -= 1 if [:lowest,:above,:below,:to].include?(action)
|
169
|
+
list.all(:position => position..newpos).adjust!({:position => -1},true)
|
170
|
+
elsif newpos < position
|
171
|
+
list.all(:position => newpos..position).adjust!({:position => +1},true)
|
172
|
+
end
|
173
|
+
|
174
|
+
self.position = newpos
|
175
|
+
|
176
|
+
true
|
177
|
+
end
|
178
|
+
|
179
|
+
def detach(scope=list_scope)
|
180
|
+
list(scope).all(:position.gt => position).adjust!({:position => -1},true)
|
181
|
+
self.position = nil
|
182
|
+
end
|
183
|
+
|
184
|
+
def left_sibling
|
185
|
+
list.reverse.first(:position.lt => position)
|
186
|
+
end
|
187
|
+
|
188
|
+
def right_sibling
|
189
|
+
list.first(:position.gt => position)
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
end # List
|
194
|
+
end # Is
|
195
|
+
end # DataMapper
|
@@ -0,0 +1,181 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
|
3
|
+
|
4
|
+
if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
|
5
|
+
describe 'DataMapper::Is::List' do
|
6
|
+
|
7
|
+
class User
|
8
|
+
include DataMapper::Resource
|
9
|
+
|
10
|
+
property :id, Serial
|
11
|
+
property :name, String
|
12
|
+
|
13
|
+
has n, :todos
|
14
|
+
end
|
15
|
+
|
16
|
+
class Todo
|
17
|
+
include DataMapper::Resource
|
18
|
+
|
19
|
+
property :id, Serial
|
20
|
+
property :title, String
|
21
|
+
|
22
|
+
belongs_to :user
|
23
|
+
|
24
|
+
is :list, :scope => [:user_id]
|
25
|
+
end
|
26
|
+
|
27
|
+
before :all do
|
28
|
+
User.auto_migrate!(:default)
|
29
|
+
Todo.auto_migrate!(:default)
|
30
|
+
|
31
|
+
u1 = User.create!(:name => "Johnny")
|
32
|
+
Todo.create!(:user => u1, :title => "Write down what is needed in a list-plugin")
|
33
|
+
Todo.create!(:user => u1, :title => "Complete a temporary version of is-list")
|
34
|
+
Todo.create!(:user => u1, :title => "Squash bugs in nested-set")
|
35
|
+
|
36
|
+
u2 = User.create!(:name => "Freddy")
|
37
|
+
Todo.create!(:user => u2, :title => "Eat tasty cupcake")
|
38
|
+
Todo.create!(:user => u2, :title => "Procrastinate on paid work")
|
39
|
+
Todo.create!(:user => u2, :title => "Go to sleep")
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'automatic positioning' do
|
44
|
+
it 'should get the shadow variable of the last position' do
|
45
|
+
repository(:default) do |repos|
|
46
|
+
Todo.get(3).position=8
|
47
|
+
Todo.get(3).dirty?.should == true
|
48
|
+
Todo.get(3).attribute_dirty?(:position).should == true
|
49
|
+
Todo.get(3).original_values[:position].should == 3
|
50
|
+
Todo.get(3).list_scope.should == Todo.get(3).original_list_scope
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should insert items into the list automatically' do
|
55
|
+
repository(:default) do |repos|
|
56
|
+
Todo.get(3).position.should == 3
|
57
|
+
Todo.get(6).position.should == 3
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'movement' do
|
63
|
+
it 'should rearrange items correctly when moving :higher' do
|
64
|
+
repository(:default) do |repos|
|
65
|
+
Todo.get(3).move :higher
|
66
|
+
Todo.get(3).position.should == 2
|
67
|
+
Todo.get(2).position.should == 3
|
68
|
+
Todo.get(4).position.should == 1
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should rearrange items correctly when moving :lower' do
|
73
|
+
repository(:default) do |repos|
|
74
|
+
Todo.get(3).position.should == 2
|
75
|
+
Todo.get(2).position.should == 3
|
76
|
+
Todo.get(3).move :lower
|
77
|
+
Todo.get(3).position.should == 3
|
78
|
+
Todo.get(2).position.should == 2
|
79
|
+
|
80
|
+
Todo.get(4).position.should == 1
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should rearrange items correctly when moving :highest or :lowest' do
|
85
|
+
repository(:default) do |repos|
|
86
|
+
|
87
|
+
# list 1
|
88
|
+
Todo.get(1).position.should == 1
|
89
|
+
Todo.get(1).move(:lowest)
|
90
|
+
Todo.get(1).position.should == 3
|
91
|
+
|
92
|
+
# list 2
|
93
|
+
Todo.get(6).position.should == 3
|
94
|
+
Todo.get(6).move(:highest)
|
95
|
+
Todo.get(6).position.should == 1
|
96
|
+
Todo.get(5).position.should == 3
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'should not rearrange when trying to move top-item up, or bottom item down' do
|
101
|
+
repository(:default) do |repos|
|
102
|
+
Todo.get(6).position.should == 1
|
103
|
+
Todo.get(6).move(:higher).should == false
|
104
|
+
Todo.get(6).position.should == 1
|
105
|
+
|
106
|
+
Todo.get(1).position.should == 3
|
107
|
+
Todo.get(2).position.should == 1
|
108
|
+
Todo.get(1).move(:lower).should == false
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should rearrange items correctly when moving :above or :below' do
|
113
|
+
repository(:default) do |repos|
|
114
|
+
Todo.get(6).position.should == 1
|
115
|
+
Todo.get(5).position.should == 3
|
116
|
+
Todo.get(6).move(:below => Todo.get(5))
|
117
|
+
Todo.get(6).position.should == 3
|
118
|
+
Todo.get(5).position.should == 2
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe 'scoping' do
|
124
|
+
it 'should detach from old list if scope changed' do
|
125
|
+
repository(:default) do |repos|
|
126
|
+
item = Todo.get(4)
|
127
|
+
item.position.should == 1
|
128
|
+
item.user_id = 1
|
129
|
+
item.save
|
130
|
+
|
131
|
+
item.position.should == 4
|
132
|
+
Todo.get(5).position.should == 1
|
133
|
+
|
134
|
+
item.user_id = 2
|
135
|
+
item.position = 1
|
136
|
+
item.save
|
137
|
+
|
138
|
+
item.position.should == 1
|
139
|
+
Todo.get(5).position.should == 2
|
140
|
+
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'should not allow you to move item into another scope' do
|
145
|
+
repository(:default) do |repos|
|
146
|
+
item = Todo.get(1)
|
147
|
+
item.position.should == 1
|
148
|
+
item.move(:below => Todo.get(5)).should == false
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should detach from list when deleted' do
|
153
|
+
repository(:default) do |repos|
|
154
|
+
item = Todo.get(4)
|
155
|
+
item.position.should == 1
|
156
|
+
Todo.get(5).position.should == 2
|
157
|
+
item.destroy
|
158
|
+
|
159
|
+
Todo.get(5).position.should == 1
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe 'reparation' do
|
166
|
+
it 'should fix them lists' do
|
167
|
+
repository(:default) do |repos|
|
168
|
+
# Need to do this with a batch update, as setting position = 20 wont
|
169
|
+
# work (it will set it to bottom of list, not more)
|
170
|
+
Todo.all(:position => 3).update!(:position => 20)
|
171
|
+
|
172
|
+
item = Todo.get(6)
|
173
|
+
item.position.should == 20
|
174
|
+
item.repair_list
|
175
|
+
item.position.should == 3
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'rspec', '>=1.1.3'
|
3
|
+
require 'spec'
|
4
|
+
require 'pathname'
|
5
|
+
require Pathname(__FILE__).dirname.expand_path.parent + 'lib/dm-is-list'
|
6
|
+
|
7
|
+
def load_driver(name, default_uri)
|
8
|
+
return false if ENV['ADAPTER'] != name.to_s
|
9
|
+
|
10
|
+
lib = "do_#{name}"
|
11
|
+
|
12
|
+
begin
|
13
|
+
gem lib, '=0.9.2'
|
14
|
+
require lib
|
15
|
+
DataMapper.setup(name, ENV["#{name.to_s.upcase}_SPEC_URI"] || default_uri)
|
16
|
+
DataMapper::Repository.adapters[:default] = DataMapper::Repository.adapters[name]
|
17
|
+
true
|
18
|
+
rescue Gem::LoadError => e
|
19
|
+
warn "Could not load #{lib}: #{e}"
|
20
|
+
false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
ENV['ADAPTER'] ||= 'sqlite3'
|
25
|
+
|
26
|
+
HAS_SQLITE3 = load_driver(:sqlite3, 'sqlite3::memory:')
|
27
|
+
HAS_MYSQL = load_driver(:mysql, 'mysql://localhost/dm_core_test')
|
28
|
+
HAS_POSTGRES = load_driver(:postgres, 'postgres://postgres@localhost/dm_core_test')
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dm-is-list
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sindre Aarsaether
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-06-25 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: dm-core
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - "="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.9.2
|
23
|
+
version:
|
24
|
+
description: DataMapper plugin for creating and organizing lists
|
25
|
+
email: sindre [a] identu [d] no
|
26
|
+
executables: []
|
27
|
+
|
28
|
+
extensions: []
|
29
|
+
|
30
|
+
extra_rdoc_files:
|
31
|
+
- README
|
32
|
+
- LICENSE
|
33
|
+
- TODO
|
34
|
+
files:
|
35
|
+
- lib/dm-is-list/is/list.rb
|
36
|
+
- lib/dm-is-list.rb
|
37
|
+
- spec/integration/list_spec.rb
|
38
|
+
- spec/spec_helper.rb
|
39
|
+
- spec/spec.opts
|
40
|
+
- Rakefile
|
41
|
+
- README
|
42
|
+
- LICENSE
|
43
|
+
- TODO
|
44
|
+
has_rdoc: true
|
45
|
+
homepage: http://github.com/sam/dm-more/tree/master/dm-is-list
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.0.1
|
67
|
+
signing_key:
|
68
|
+
specification_version: 2
|
69
|
+
summary: DataMapper plugin for creating and organizing lists
|
70
|
+
test_files: []
|
71
|
+
|