dm-is-list 0.9.5 → 0.9.6
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -1
- data/lib/dm-is-list.rb +2 -2
- data/lib/dm-is-list/is/list.rb +42 -38
- data/lib/dm-is-list/is/version.rb +1 -1
- data/spec/integration/list_spec.rb +12 -2
- data/spec/spec_helper.rb +1 -1
- metadata +3 -3
data/Rakefile
CHANGED
@@ -45,5 +45,5 @@ end
|
|
45
45
|
desc 'Run specifications'
|
46
46
|
Spec::Rake::SpecTask.new(:spec) do |t|
|
47
47
|
t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
|
48
|
-
t.spec_files = Pathname.glob(
|
48
|
+
t.spec_files = Pathname.glob((ROOT + 'spec/**/*_spec.rb').to_s)
|
49
49
|
end
|
data/lib/dm-is-list.rb
CHANGED
@@ -2,10 +2,10 @@
|
|
2
2
|
require 'rubygems'
|
3
3
|
require 'pathname'
|
4
4
|
|
5
|
-
gem 'dm-core', '=0.9.
|
5
|
+
gem 'dm-core', '=0.9.6'
|
6
6
|
require 'dm-core'
|
7
7
|
|
8
|
-
gem 'dm-adjust', '=0.9.
|
8
|
+
gem 'dm-adjust', '=0.9.6'
|
9
9
|
require 'dm-adjust'
|
10
10
|
|
11
11
|
require Pathname(__FILE__).dirname.expand_path / 'dm-is-list' / 'is' / 'list.rb'
|
data/lib/dm-is-list/is/list.rb
CHANGED
@@ -19,31 +19,32 @@ module DataMapper
|
|
19
19
|
# @option :scope<Array> an array of attributes that should be used to scope lists
|
20
20
|
#
|
21
21
|
def is_list(options={})
|
22
|
-
options = { :scope => [] }.merge(options)
|
22
|
+
options = { :scope => [], :first => 1 }.merge(options)
|
23
23
|
|
24
24
|
extend DataMapper::Is::List::ClassMethods
|
25
25
|
include DataMapper::Is::List::InstanceMethods
|
26
26
|
|
27
27
|
property :position, Integer unless properties.detect{|p| p.name == :position && p.type == Integer}
|
28
28
|
|
29
|
-
@
|
29
|
+
@list_options = options
|
30
30
|
|
31
31
|
before :save do
|
32
32
|
if self.new_record?
|
33
33
|
# a position has been set before save => open up and make room for item
|
34
34
|
# no position has been set => move to bottom of my scope-list (or keep detached?)
|
35
|
-
self.
|
35
|
+
self.send(:move_without_saving, (self.position || :lowest))
|
36
36
|
else
|
37
37
|
# if the scope has changed, we need to detach our item from the old list
|
38
38
|
if self.list_scope != self.original_list_scope
|
39
|
-
oldpos = self.original_values[:position]
|
40
39
|
newpos = self.position
|
41
40
|
|
42
41
|
self.detach(self.original_list_scope) # removing from old list
|
43
|
-
self.
|
42
|
+
self.send(:move_without_saving, newpos || :lowest) # moving to pos or bottom of new list
|
44
43
|
|
45
|
-
elsif self.attribute_dirty?(:position)
|
46
|
-
self.
|
44
|
+
elsif self.attribute_dirty?(:position) && !self.moved
|
45
|
+
self.send(:move_without_saving, self.position)
|
46
|
+
else
|
47
|
+
self.moved = false
|
47
48
|
end
|
48
49
|
# a (new) position has been set => move item to this position (only if position has been set manually)
|
49
50
|
# the scope has changed => detach from old list, and possibly move into position
|
@@ -57,13 +58,13 @@ module DataMapper
|
|
57
58
|
|
58
59
|
# we need to make sure that STI-models will inherit the list_scope.
|
59
60
|
after_class_method :inherited do |retval, target|
|
60
|
-
target.instance_variable_set(:@
|
61
|
+
target.instance_variable_set(:@list_options, @list_options.dup)
|
61
62
|
end
|
62
63
|
|
63
64
|
end
|
64
65
|
|
65
66
|
module ClassMethods
|
66
|
-
attr_reader :
|
67
|
+
attr_reader :list_options
|
67
68
|
|
68
69
|
##
|
69
70
|
# use this function to repair / build your lists.
|
@@ -75,19 +76,20 @@ module DataMapper
|
|
75
76
|
# @param scope [Hash]
|
76
77
|
#
|
77
78
|
def repair_list(scope={})
|
78
|
-
return false unless scope.keys.all?{|s|
|
79
|
+
return false unless scope.keys.all?{|s| list_options[:scope].include?(s) || s == :order }
|
79
80
|
all({:order => [:position.asc]}.merge(scope)).each_with_index{ |item,i| item.position = i+1; item.save }
|
80
81
|
end
|
81
82
|
end
|
82
83
|
|
83
84
|
module InstanceMethods
|
85
|
+
attr_accessor :moved
|
84
86
|
|
85
87
|
def list_scope
|
86
|
-
self.class.
|
88
|
+
self.class.list_options[:scope].map{|p| [p,attribute_get(p)]}.to_hash
|
87
89
|
end
|
88
90
|
|
89
91
|
def original_list_scope
|
90
|
-
self.class.
|
92
|
+
self.class.list_options[:scope].map{|p| [p,original_values.key?(p) ? original_values[p] : attribute_get(p)]}.to_hash
|
91
93
|
end
|
92
94
|
|
93
95
|
def list_query
|
@@ -112,6 +114,19 @@ module DataMapper
|
|
112
114
|
self.class.repair_list(list_scope.merge(:order => order))
|
113
115
|
end
|
114
116
|
|
117
|
+
def detach(scope=list_scope)
|
118
|
+
list(scope).all(:position.gt => position).adjust!({:position => -1},true)
|
119
|
+
self.position = nil
|
120
|
+
end
|
121
|
+
|
122
|
+
def left_sibling
|
123
|
+
list.reverse.first(:position.lt => position)
|
124
|
+
end
|
125
|
+
|
126
|
+
def right_sibling
|
127
|
+
list.first(:position.gt => position)
|
128
|
+
end
|
129
|
+
|
115
130
|
##
|
116
131
|
# move item to a position in the list. position should _only_ be changed through this
|
117
132
|
#
|
@@ -143,52 +158,41 @@ module DataMapper
|
|
143
158
|
# does all the actual movement in #move, but does not save afterwards. this is used internally in
|
144
159
|
# before :save, and will probably be marked private. should not be used by organic beings.
|
145
160
|
#
|
146
|
-
# @see
|
161
|
+
# @see move
|
162
|
+
private
|
147
163
|
def move_without_saving(vector)
|
148
164
|
if vector.is_a? Hash then action,object = vector.keys[0],vector.values[0] else action = vector end
|
149
165
|
|
150
|
-
|
151
|
-
|
166
|
+
minpos = self.class.list_options[:first]
|
167
|
+
prepos = self.original_values[:position] || self.position
|
168
|
+
maxpos = list.last ? (list.last == self ? prepos : list.last.position + 1) : minpos
|
152
169
|
newpos = case action
|
153
|
-
when :highest then
|
170
|
+
when :highest then minpos
|
154
171
|
when :lowest then maxpos
|
155
|
-
when :higher,:up then [position-1,
|
172
|
+
when :higher,:up then [position-1,minpos].max
|
156
173
|
when :lower,:down then [position+1,maxpos].min
|
157
174
|
when :above then object.position
|
158
175
|
when :below then object.position+1
|
159
|
-
when :to then [object.to_i,maxpos].min
|
176
|
+
when :to then [minpos,[object.to_i,maxpos].min].max
|
177
|
+
else [action.to_i,maxpos].min
|
160
178
|
end
|
161
179
|
|
162
180
|
return false if !newpos || ([:above,:below].include?(action) && list_scope != object.list_scope)
|
163
|
-
return true if newpos == position || (newpos == maxpos && position == maxpos-1)
|
181
|
+
return true if newpos == position && position == prepos || (newpos == maxpos && position == maxpos-1)
|
164
182
|
|
165
183
|
if !position
|
166
184
|
list.all(:position.gte => newpos).adjust!({:position => +1},true) unless action == :lowest
|
167
|
-
elsif newpos >
|
185
|
+
elsif newpos > prepos
|
168
186
|
newpos -= 1 if [:lowest,:above,:below,:to].include?(action)
|
169
|
-
list.all(:position =>
|
170
|
-
elsif newpos <
|
171
|
-
list.all(:position => newpos..
|
187
|
+
list.all(:position => prepos..newpos).adjust!({:position => -1},true)
|
188
|
+
elsif newpos < prepos
|
189
|
+
list.all(:position => newpos..prepos).adjust!({:position => +1},true)
|
172
190
|
end
|
173
191
|
|
174
192
|
self.position = newpos
|
175
|
-
|
193
|
+
self.moved = true
|
176
194
|
true
|
177
195
|
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
196
|
end
|
193
197
|
end # List
|
194
198
|
end # Is
|
@@ -57,15 +57,23 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
|
|
57
57
|
Todo.get(6).position.should == 3
|
58
58
|
end
|
59
59
|
end
|
60
|
+
|
61
|
+
it 'should rearrange items when setting position yourself' do
|
62
|
+
repository(:default) do |repos|
|
63
|
+
Todo.get(2).update_attributes(:position => 1)
|
64
|
+
Todo.get(2).position.should == 1
|
65
|
+
Todo.get(1).position.should == 2
|
66
|
+
end
|
67
|
+
end
|
60
68
|
end
|
61
69
|
|
62
70
|
describe 'movement' do
|
63
71
|
it 'should rearrange items correctly when moving :higher' do
|
64
72
|
repository(:default) do |repos|
|
65
73
|
Todo.get(3).move :higher
|
74
|
+
Todo.get(4).position.should == 1
|
66
75
|
Todo.get(3).position.should == 2
|
67
76
|
Todo.get(2).position.should == 3
|
68
|
-
Todo.get(4).position.should == 1
|
69
77
|
end
|
70
78
|
end
|
71
79
|
|
@@ -128,7 +136,9 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
|
|
128
136
|
item.user_id = 1
|
129
137
|
item.save
|
130
138
|
|
131
|
-
item.
|
139
|
+
item.list_scope.should != item.original_list_scope
|
140
|
+
item.position.should == 1
|
141
|
+
Todo.get(1).position.should == 2
|
132
142
|
Todo.get(5).position.should == 1
|
133
143
|
|
134
144
|
item.user_id = 2
|
data/spec/spec_helper.rb
CHANGED
@@ -10,7 +10,7 @@ def load_driver(name, default_uri)
|
|
10
10
|
lib = "do_#{name}"
|
11
11
|
|
12
12
|
begin
|
13
|
-
gem lib, '
|
13
|
+
gem lib, '>=0.9.5'
|
14
14
|
require lib
|
15
15
|
DataMapper.setup(name, ENV["#{name.to_s.upcase}_SPEC_URI"] || default_uri)
|
16
16
|
DataMapper::Repository.adapters[:default] = DataMapper::Repository.adapters[name]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dm-is-list
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sindre Aarsaether
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-10-12 00:00:00 -06:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -20,7 +20,7 @@ dependencies:
|
|
20
20
|
requirements:
|
21
21
|
- - "="
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: 0.9.
|
23
|
+
version: 0.9.6
|
24
24
|
version:
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: hoe
|