Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision f12b3596

Von Moritz Bunkus vor fast 12 Jahren hinzugefügt

  • ID f12b3596807e5a46c6ed3e86c43e01492d2dc6c1
  • Vorgänger 0fc82c30
  • Nachfolger a341d959

ActsAsList: neu: remove_from_list(), add_to_list(), Unit-Tests

Unterschiede anzeigen:

SL/DB/Helper/ActsAsList.pm
3 3
use strict;
4 4

  
5 5
use parent qw(Exporter);
6
our @EXPORT = qw(move_position_up move_position_down reorder_list configure_acts_as_list);
6
our @EXPORT = qw(move_position_up move_position_down add_to_list remove_from_list reorder_list configure_acts_as_list);
7 7

  
8 8
use Carp;
9 9

  
......
35 35
  do_move($self, 'down');
36 36
}
37 37

  
38
sub remove_from_list {
39
  my ($self) = @_;
40

  
41
  my $worker = sub {
42
    remove_position($self);
43

  
44
    # Set to NULL manually because $self->update_attributes() would
45
    # trigger the before_save() hook from this very plugin assigning a
46
    # number at the end of the list again.
47
    my $table           = $self->meta->table;
48
    my $column          = column_name($self);
49
    my $primary_key_col = ($self->meta->primary_key)[0];
50
    my $sql             = <<SQL;
51
      UPDATE ${table}
52
      SET ${column} = NULL
53
      WHERE ${primary_key_col} = ?
54
SQL
55
    $self->db->dbh->do($sql, undef, $self->$primary_key_col);
56
    $self->$column(undef);
57
  };
58

  
59
  return $self->db->in_transaction ? $worker->() : $self->db->do_transaction($worker);
60
}
61

  
62
sub add_to_list {
63
  my ($self, %params) = @_;
64

  
65
  croak "Invalid parameter 'position'" unless ($params{position} || '') =~ m/^ (?: before | after | first | last ) $/x;
66

  
67
  if ($params{position} eq 'last') {
68
    set_position($self);
69
    $self->save;
70
    return;
71
  }
72

  
73
  my $table               = $self->meta->table;
74
  my $primary_key_col     = ($self->meta->primary_key)[0];
75
  my $column              = column_name($self);
76
  my ($group_by, @values) = get_group_by_where($self);
77
  $group_by               = " AND ${group_by}" if $group_by;
78
  my $new_position;
79

  
80
  if ($params{position} eq 'first') {
81
    $new_position = 1;
82

  
83
  } else {
84
    # Can only be 'before' or 'after' -- 'last' has been checked above
85
    # already.
86

  
87
    my $reference = $params{reference};
88
    croak "Missing parameter 'reference'" if !$reference;
89

  
90
    my $reference_pos;
91
    if (ref $reference) {
92
      $reference_pos = $reference->$column;
93
    } else {
94
      ($reference_pos) = $self->db->dbh->selectrow_array(qq|SELECT ${column} FROM ${table} WHERE ${primary_key_col} = ?|, undef, $reference);
95
    }
96

  
97
    $new_position = $params{position} eq 'before' ? $reference_pos : $reference_pos + 1;
98
  }
99

  
100
  my $query = <<SQL;
101
    UPDATE ${table}
102
    SET ${column} = ${column} + 1
103
    WHERE (${column} > ?)
104
      ${group_by}
105
SQL
106

  
107
  my $worker = sub {
108
    $self->db->dbh->do($query, undef, $new_position - 1, @values);
109
    $self->update_attributes($column => $new_position);
110
  };
111

  
112
  return $self->db->in_transaction ? $worker->() : $self->db->do_transaction($worker);
113
}
114

  
38 115
sub reorder_list {
39 116
  my ($class_or_self, @ids) = @_;
40 117

  
......
75 152
  my $group_by = get_spec(ref $self, 'group_by') || [];
76 153
  $group_by    = [ $group_by ] if $group_by && !ref $group_by;
77 154

  
78
  my @where    = map { my $value = $self->$_; defined($value) ? "(${_} = " . $value . ")" : "(${_} IS NULL)" } @{ $group_by };
155
  my (@where, @values);
156
  foreach my $column (@{ $group_by }) {
157
    my $value = $self->$column;
158
    push @values, $value if defined $value;
159
    push @where,  defined($value) ? "(${column} = ?)" : "(${column} IS NULL)";
160
  }
79 161

  
80
  return join ' AND ', @where;
162
  return (join(' AND ', @where), @values);
81 163
}
82 164

  
83 165
sub set_position {
......
86 168

  
87 169
  return 1 if defined $self->$column;
88 170

  
89
  my $table        = $self->meta->table;
90
  my $where        = get_group_by_where($self);
91
  $where           = " WHERE ${where}" if $where;
92
  my $sql = <<SQL;
93
    SELECT COALESCE(max(${column}), 0)
171
  my $table               = $self->meta->table;
172
  my ($group_by, @values) = get_group_by_where($self);
173
  my $where               = $group_by ? " WHERE ${group_by}" : '';
174
  my $sql                 = <<SQL;
175
    SELECT COALESCE(MAX(${column}), 0)
94 176
    FROM ${table}
95 177
    ${where}
96 178
SQL
97 179

  
98
  my $max_position = $self->db->dbh->selectrow_arrayref($sql)->[0];
180
  my $max_position = $self->db->dbh->selectrow_arrayref($sql, undef, @values)->[0];
99 181
  $self->$column($max_position + 1);
100 182

  
101 183
  return 1;
......
108 190
  $self->load;
109 191
  return 1 unless defined $self->$column;
110 192

  
111
  my $table    = $self->meta->table;
112
  my $value    = $self->$column;
113
  my $group_by = get_group_by_where($self);
114
  $group_by    = ' AND ' . $group_by if $group_by;
115
  my $sql      = <<SQL;
193
  my $table               = $self->meta->table;
194
  my $value               = $self->$column;
195
  my ($group_by, @values) = get_group_by_where($self);
196
  $group_by               = ' AND ' . $group_by if $group_by;
197
  my $sql                 = <<SQL;
116 198
    UPDATE ${table}
117 199
    SET ${column} = ${column} - 1
118
    WHERE (${column} > ${value}) ${group_by}
200
    WHERE (${column} > ?)
201
     ${group_by}
119 202
SQL
120 203

  
121
  $self->db->dbh->do($sql);
204
  $self->db->dbh->do($sql, undef, $value, @values);
122 205

  
123 206
  return 1;
124 207
}
......
132 215

  
133 216
  my $table                                        = $self->meta->table;
134 217
  my $old_position                                 = $self->$column;
135
  my ($comp_sel, $comp_upd, $min_max, $plus_minus) = $direction eq 'up' ? ('<', '>=', 'max', '+') : ('>', '<=', 'min', '-');
136
  my $group_by                                     = get_group_by_where($self);
218
  my ($comp_sel, $comp_upd, $min_max, $plus_minus) = $direction eq 'up' ? ('<', '>=', 'MAX', '+') : ('>', '<=', 'MIN', '-');
219
  my ($group_by, @values)                          = get_group_by_where($self);
137 220
  $group_by                                        = ' AND ' . $group_by if $group_by;
138 221
  my $sql                                          = <<SQL;
139 222
    SELECT ${min_max}(${column})
140 223
    FROM ${table}
141
    WHERE (${column} ${comp_sel} ${old_position})
224
    WHERE (${column} ${comp_sel} ?)
142 225
      ${group_by}
143 226
SQL
144 227

  
145
  my $new_position = $self->db->dbh->selectrow_arrayref($sql)->[0];
228
  my $new_position = $self->db->dbh->selectrow_arrayref($sql, undef, $old_position, @values)->[0];
146 229

  
147 230
  return undef unless defined $new_position;
148 231

  
149 232
  $sql = <<SQL;
150 233
    UPDATE ${table}
151
    SET ${column} = ${old_position}
152
    WHERE (${column} = ${new_position})
234
    SET ${column} = ?
235
    WHERE (${column} = ?)
153 236
     ${group_by};
154 237
SQL
155 238

  
156
  $self->db->dbh->do($sql);
239
  $self->db->dbh->do($sql, undef, $old_position, $new_position, @values);
157 240

  
158 241
  $self->update_attributes($column => $new_position);
159 242
}
......
263 346
Swaps the object with the object one step below the current one
264 347
regarding their sort order by exchanging their C<position> values.
265 348

  
349
=item C<add_to_list %params>
350

  
351
Adds this item to the list. The parameter C<position> is required and
352
can be one of C<first>, C<last>, C<before> and C<after>. With C<first>
353
the item is inserted as the first item in the list and all other
354
item's positions are shifted up by one. For C<position = last> the
355
item is inserted at the end of the list.
356

  
357
For C<before> and C<after> an additional parameter C<reference> is
358
required. This is either a Rose model instance or the primary key of
359
one. The current item will then be inserted either before or after the
360
referenced item by shifting all the appropriate item positions up by
361
one.
362

  
363
After this function C<$self>'s positional column has been set and
364
saved to the database.
365

  
366
=item C<remove_from_list>
367

  
368
Sets this items positional column to C<undef>, saves it and moves all
369
following items up by 1.
370

  
266 371
=item C<reorder_list @ids>
267 372

  
268 373
Re-orders the objects given in C<@ids> by their position in C<@ids> by
t/db_helper/acts_as_list.t
1
use Test::More tests => 44;
2
use Test::Exception;
3

  
4
use strict;
5

  
6
use lib 't';
7
use utf8;
8

  
9
use Data::Dumper;
10
use Support::TestSetup;
11

  
12
use_ok 'SL::DB::RequirementSpec';
13
use_ok 'SL::DB::RequirementSpecItem';
14
use_ok 'SL::DB::RequirementSpecTextBlock';
15

  
16
sub reset_state {
17
  "SL::DB::Manager::${_}"->delete_all(all => 1) for qw(RequirementSpecTextBlock RequirementSpecItem RequirementSpec);
18

  
19
  SL::DB::RequirementSpec->new(id => 2, type_id => 1, status_id => 1, customer_id => 12395, hourly_rate => 42.24,  title => "Azumbo")->save;
20

  
21
  SL::DB::RequirementSpecItem->new(requirement_spec_id => 2, parent_id => undef, id => 1, position => 1, fb_number => "A01",    title => "Mühköh",   description => "The Kuh.")->save;
22
  SL::DB::RequirementSpecItem->new(requirement_spec_id => 2, parent_id => undef, id => 2, position => 2, fb_number => "A02",    title => "Geheim",   description => "Kofferkombination")->save;
23
  SL::DB::RequirementSpecItem->new(requirement_spec_id => 2, parent_id => 1,     id => 3, position => 1, fb_number => "FB0001", title => "Yäääh",    description => "Und so")->save;
24
  SL::DB::RequirementSpecItem->new(requirement_spec_id => 2, parent_id => 1,     id => 4, position => 2, fb_number => "FB0012", title => "Blubb",    description => "blabb")->save;
25
  SL::DB::RequirementSpecItem->new(requirement_spec_id => 2, parent_id => 1,     id => 5, position => 3, fb_number => "FB0022", title => "Fingo",    description => "fungo")->save;
26
  SL::DB::RequirementSpecItem->new(requirement_spec_id => 2, parent_id => 4,     id => 6, position => 1, fb_number => "UFB002", title => "Suppi",    description => "Suppa")->save;
27
  SL::DB::RequirementSpecItem->new(requirement_spec_id => 2, parent_id => 4,     id => 7, position => 2, fb_number => "UFB000", title => "Suppa",    description => "Suppi")->save;
28
  SL::DB::RequirementSpecItem->new(requirement_spec_id => 2, parent_id => 2,     id => 8, position => 1, fb_number => "FB0018", title => "Neuneins", description => "Eins")->save;
29

  
30
  SL::DB::RequirementSpec->new->db->dbh->do(qq|SELECT pg_catalog.setval('| . $_->[0] . qq|', | . $_->[1] . qq|, true)|) for (['requirement_spec_items_id_seq', 8], [ 'requirement_specs_id_seq', 2 ]);
31
}
32

  
33
sub values_eq {
34
  my ($val1, $val2) = @_;
35
  return (defined($val1) == defined($val2))
36
      && (!defined($val1) || ($val1 == $val2));
37
}
38

  
39
sub test_positions {
40
  my ($message, @positions) = @_;
41

  
42
  my $failures =
43
    join ' ',
44
    map  { join ':', map { $_ // 'undef' } @{ $_ } }
45
    grep { !values_eq($_->[1], $_->[3]) || !values_eq($_->[2], $_->[4]) }
46
    map  { my $item = SL::DB::RequirementSpecItem->new(id => $_->[0])->load; [ @{ $_ }, $item->parent_id, $item->position ] }
47
    @positions;
48

  
49
  is($failures, '', $message);
50
}
51

  
52
sub new_item {
53
  return SL::DB::RequirementSpecItem->new(requirement_spec_id => 2, fb_number => 'dummy', title => 'dummy', @_);
54
}
55

  
56
sub get_item {
57
  return SL::DB::RequirementSpecItem->new(id => $_[0])->load;
58
}
59

  
60
Support::TestSetup::login();
61
my $item;
62

  
63
# 1
64
# `+- 3
65
#  +- 4
66
#  |  `+- 6
67
#  |   `- 7
68
#  `- 5
69
# 2
70
# `- 8
71

  
72
reset_state();
73
test_positions "reset_state", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 4, 1, 2 ], [ 5, 1, 3 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 2, 1 ];
74

  
75
# Einfügen neuer Objekte: "set_position"
76
new_item(parent_id => 1)->save;
77
test_positions "set_position via new with parent_id NOT NULL", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 4, 1, 2 ], [ 5, 1, 3 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 2, 1 ], [ 9, 1, 4 ];
78

  
79
reset_state();
80
new_item()->save;
81
test_positions "set_position via new with parent_id IS NULL",  [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 4, 1, 2 ], [ 5, 1, 3 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 2, 1 ], [ 9, undef, 3 ];
82

  
83
# Löschen von Objekten: "remove_position"
84
reset_state();
85
get_item(3)->delete;
86
test_positions "remove_position via delete with parent_id NOT NULL",  [ 1, undef, 1 ], [ 2, undef, 2 ], [ 4, 1, 1 ], [ 5, 1, 2 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 2, 1 ];
87

  
88
reset_state();
89
get_item(1)->delete;
90
test_positions "remove_position via delete with parent_id IS NULL 1",  [ 2, undef, 1 ], [ 8, 2, 1 ];
91

  
92
reset_state();
93
get_item(2)->delete;
94
test_positions "remove_position via delete with parent_id IS NULL 2",  [ 1, undef, 1 ], [ 3, 1, 1 ], [ 4, 1, 2 ], [ 5, 1, 3 ], [ 6, 4, 1 ], [ 7, 4, 2 ];
95

  
96
# Hoch schieben
97
reset_state();
98
get_item(3)->move_position_up;
99
test_positions "move_position_up when at top of sub-list", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 4, 1, 2 ], [ 5, 1, 3 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 2, 1 ];
100

  
101
reset_state();
102
get_item(4)->move_position_up;
103
test_positions "move_position_up when in middle of sub-list", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 4, 1, 1 ], [ 3, 1, 2 ], [ 5, 1, 3 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 2, 1 ];
104

  
105
reset_state();
106
get_item(5)->move_position_up;
107
test_positions "move_position_up when at end of sub-list", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 5, 1, 2 ], [ 4, 1, 3 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 2, 1 ];
108

  
109
reset_state();
110
get_item(8)->move_position_up;
111
test_positions "move_position_up when only element in sub-list", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 4, 1, 2 ], [ 5, 1, 3 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 2, 1 ];
112

  
113
# Herunter schieben
114
reset_state();
115
get_item(3)->move_position_down;
116
test_positions "move_position_down when at top of sub-list", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 4, 1, 1 ], [ 3, 1, 2 ], [ 5, 1, 3 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 2, 1 ];
117

  
118
reset_state();
119
get_item(4)->move_position_down;
120
test_positions "move_position_down when in middle of sub-list", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 5, 1, 2 ], [ 4, 1, 3 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 2, 1 ];
121

  
122
reset_state();
123
get_item(5)->move_position_down;
124
test_positions "move_position_down when at bottom of sub-list", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 4, 1, 2 ], [ 5, 1, 3 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 2, 1 ];
125

  
126
reset_state();
127
get_item(8)->move_position_down;
128
test_positions "move_position_down when only element in sub-list", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 4, 1, 2 ], [ 5, 1, 3 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 2, 1 ];
129

  
130
# Listen neu anordnen
131
reset_state();
132
get_item(8)->reorder_list(4, 5, 3);
133
test_positions "reoder_list called as instance method", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 4, 1, 1 ], [ 5, 1, 2 ], [ 3, 1, 3 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 2, 1 ];
134

  
135
reset_state();
136
SL::DB::RequirementSpecItem->reorder_list(4, 5, 3);
137
test_positions "reoder_list called as class method", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 4, 1, 1 ], [ 5, 1, 2 ], [ 3, 1, 3 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 2, 1 ];
138

  
139
# Aus Liste entfernen
140
reset_state();
141
get_item(3)->remove_from_list;
142
test_positions "remove_from_list on top", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, undef ], [ 4, 1, 1 ], [ 5, 1, 2 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 2, 1 ];
143

  
144
reset_state();
145
get_item(4)->remove_from_list;
146
test_positions "remove_from_list on middle", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 4, 1, undef ], [ 5, 1, 2 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 2, 1 ];
147

  
148
reset_state();
149
get_item(5)->remove_from_list;
150
test_positions "remove_from_list on bottom", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 4, 1, 2 ], [ 5, 1, undef ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 2, 1 ];
151

  
152
reset_state();
153
get_item(8)->remove_from_list;
154
test_positions "remove_from_list on only item in list", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 4, 1, 2 ], [ 5, 1, 3 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 2, undef ];
155

  
156
reset_state();
157
$item = get_item(3); $item->remove_from_list; $item->delete;
158
test_positions "remove_from_list and delete afterwards", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 4, 1, 1 ], [ 5, 1, 2 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 2, 1 ];
159

  
160
# Zu Liste hinzufügen
161
reset_state();
162
$item = get_item(8); $item->remove_from_list; $item->parent_id(1); $item->add_to_list(position => 'last');
163
test_positions "add_to_list position 'last'", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 4, 1, 2 ], [ 5, 1, 3 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 1, 4 ];
164

  
165
reset_state();
166
$item = get_item(8); $item->remove_from_list; $item->parent_id(1); $item->add_to_list(position => 'first');
167
test_positions "add_to_list position 'first'", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 2 ], [ 4, 1, 3 ], [ 5, 1, 4 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 1, 1 ];
168

  
169
reset_state();
170
$item = get_item(8); $item->remove_from_list; $item->parent_id(1); $item->add_to_list(position => 'before', reference => 3);
171
test_positions "add_to_list position 'before' first by ID", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 2 ], [ 4, 1, 3 ], [ 5, 1, 4 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 1, 1 ];
172

  
173
reset_state();
174
$item = get_item(8); $item->remove_from_list; $item->parent_id(1); $item->add_to_list(position => 'before', reference => get_item(3));
175
test_positions "add_to_list position 'before' first by reference", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 2 ], [ 4, 1, 3 ], [ 5, 1, 4 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 1, 1 ];
176

  
177
reset_state();
178
$item = get_item(8); $item->remove_from_list; $item->parent_id(1); $item->add_to_list(position => 'before', reference => 4);
179
test_positions "add_to_list position 'before' middle by ID", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 4, 1, 3 ], [ 5, 1, 4 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 1, 2 ];
180

  
181
reset_state();
182
$item = get_item(8); $item->remove_from_list; $item->parent_id(1); $item->add_to_list(position => 'before', reference => get_item(4));
183
test_positions "add_to_list position 'before' middle by reference", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 4, 1, 3 ], [ 5, 1, 4 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 1, 2 ];
184

  
185
reset_state();
186
$item = get_item(8); $item->remove_from_list; $item->parent_id(1); $item->add_to_list(position => 'after', reference => 5);
187
test_positions "add_to_list position 'after' last by ID", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 4, 1, 2 ], [ 5, 1, 3 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 1, 4 ];
188

  
189
reset_state();
190
$item = get_item(8); $item->remove_from_list; $item->parent_id(1); $item->add_to_list(position => 'after', reference => get_item(5));
191
test_positions "add_to_list position 'after' last by reference", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 4, 1, 2 ], [ 5, 1, 3 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 1, 4 ];
192

  
193
reset_state();
194
$item = get_item(8); $item->remove_from_list; $item->parent_id(1); $item->add_to_list(position => 'after', reference => 4);
195
test_positions "add_to_list position 'after' middle by ID", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 4, 1, 2 ], [ 5, 1, 4 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 1, 3 ];
196

  
197
reset_state();
198
$item = get_item(8); $item->remove_from_list; $item->parent_id(1); $item->add_to_list(position => 'after', reference => get_item(4));
199
test_positions "add_to_list position 'after' middle by reference", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 4, 1, 2 ], [ 5, 1, 4 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 1, 3 ];
200

  
201
reset_state();
202
$item = get_item(8); $item->remove_from_list; $item->parent_id(3); $item->add_to_list(position => 'last');
203
test_positions "add_to_list position 'last' in empty", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 4, 1, 2 ], [ 5, 1, 3 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 3, 1 ];
204

  
205
reset_state();
206
$item = get_item(8); $item->remove_from_list; $item->parent_id(3); $item->add_to_list(position => 'first');
207
test_positions "add_to_list position 'first' in empty", [ 1, undef, 1 ], [ 2, undef, 2 ], [ 3, 1, 1 ], [ 4, 1, 2 ], [ 5, 1, 3 ], [ 6, 4, 1 ], [ 7, 4, 2 ], [ 8, 3, 1 ];
208

  
209

  
210

  
211
# Parametervalidierung
212
throws_ok { new_item()->move_position_up   } qr/not.*been.*saved/i, 'move up not saved yet';
213
throws_ok { new_item()->move_position_down } qr/not.*been.*saved/i, 'move down not saved yet';
214

  
215
throws_ok { $item = get_item(8); $item->position(undef); $item->move_position_up   } qr/no.*position.*set.*yet/i, 'move up no position set';
216
throws_ok { $item = get_item(8); $item->position(undef); $item->move_position_down } qr/no.*position.*set.*yet/i, 'move down no position set';
217

  
218
throws_ok { get_item(8)->add_to_list;                      } qr/invalid.*parameter.*position/i,  'missing position';
219
throws_ok { get_item(8)->add_to_list(position => 'gonzo')  } qr/invalid.*parameter.*position/i,  'invalid position';
220
throws_ok { get_item(8)->add_to_list(position => 'before') } qr/missing.*parameter.*reference/i, 'missing reference for position "before"';
221
throws_ok { get_item(8)->add_to_list(position => 'after')  } qr/missing.*parameter.*reference/i, 'missing reference for position "after"';
222

  
223
done_testing();

Auch abrufbar als: Unified diff