Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 013804fd

Von Moritz Bunkus vor mehr als 10 Jahren hinzugefügt

  • ID 013804fdf4b69b04889d8bf8af182eda9e9d95a5
  • Vorgänger f3aa6818
  • Nachfolger dc5f7965

Pflichtenhefte: Dokumentation; Refactoring; Bugfix Diff-Berechnung

Unterschiede anzeigen:

SL/Controller/RequirementSpecVersion.pm
37 37

  
38 38
  $self->version(SL::DB::RequirementSpecVersion->new);
39 39

  
40
  my $previous_version = $self->requirement_spec->previous_version;
41
  my %differences      = $self->calculate_differences(current => $self->requirement_spec, previous => $previous_version);
40
  my $previous_version = $self->requirement_spec->highest_version;
41
  $::lxdebug->message(0, "vers num " . $previous_version->version->version_number . " id " . $previous_version->version_id);
42 42

  
43 43
  if (!$previous_version) {
44 44
    $self->version->description(t8('Initial version.'));
45 45

  
46 46
  } else {
47
    my %differences = $self->calculate_differences(current => $self->requirement_spec, previous => $previous_version);
48

  
47 49
    my @lines;
48 50

  
49 51
    my $fb_diff = $differences{function_blocks};
......
124 126
sub has_item_changed {
125 127
  my ($previous, $current) = @_;
126 128
  croak "Missing previous/current" if !$previous || !$current;
127
  return any { ($previous->$_ || '') ne ($current->$_ || '') } qw(item_type parent_id fb_number title description complexity_id risk_id time_estimation net_sum);
129

  
130
  return 1 if any { ($previous->$_ || '') ne ($current->$_ || '') } qw(item_type fb_number title description complexity_id risk_id);
131
  return 0 if !$current->parent_id;
132
  return $previous->parent->fb_number ne $current->parent->fb_number;
128 133
}
129 134

  
130 135
sub has_text_block_changed {
SL/DB/RequirementSpec.pm
3 3
use strict;
4 4

  
5 5
use Carp;
6
use List::Util qw(max reduce);
6 7
use Rose::DB::Object::Helpers;
7 8

  
8 9
use SL::DB::MetaSetup::RequirementSpec;
......
110 111
  return $copy;
111 112
}
112 113

  
113
sub copy_from {
114
sub _copy_from {
114 115
  my ($self, $source, %attributes) = @_;
115 116

  
116 117
  croak "Missing parameter 'source'" unless $source;
......
156 157
  return $self;
157 158
}
158 159

  
159
sub previous_version {
160
  my ($self) = @_;
160
sub copy_from {
161
  my ($self, $source, %attributes) = @_;
161 162

  
162
  my $and    = $self->version_id ? " AND (version_id <> ?)" : "";
163
  my $id     = $self->db->dbh->selectrow_array(<<SQL, undef, $self->id, ($self->version_id) x !!$self->version_id);
164
   SELECT MAX(id)
165
   FROM requirement_specs
166
   WHERE (working_copy_id = ?) $and
167
SQL
163
  $self->db->with_transaction(sub { $self->_copy_from($source, %attributes); });
164
}
168 165

  
169
  return $id ? SL::DB::RequirementSpec->new(id => $id)->load : undef;
166
sub highest_version {
167
  my ($self) = @_;
168

  
169
  return reduce { $a->version->version_number > $b->version->version_number ? $a : $b } @{ $self->versioned_copies };
170 170
}
171 171

  
172 172
sub is_working_copy {
......
177 177

  
178 178
sub next_version_number {
179 179
  my ($self) = @_;
180
  my $max_number = $self->db->dbh->selectrow_array(<<SQL, undef, $self->id);
181
    SELECT COALESCE(MAX(ver.version_number), 0)
182
    FROM requirement_spec_versions ver
183
    JOIN requirement_specs rs ON (rs.version_id = ver.id)
184
    WHERE rs.working_copy_id = ?
185
SQL
186

  
187
  return $max_number + 1;
180

  
181
  return max(0, map { $_->version->version_number } @{ $self->versioned_copies }) + 1;
188 182
}
189 183

  
190 184
sub create_version {
......
193 187
  croak "Cannot work on a versioned copy" if $self->working_copy_id;
194 188

  
195 189
  my ($copy, $version);
196
  my $ok = $self->db->do_transaction(sub {
190
  my $ok = $self->db->with_transaction(sub {
197 191
    delete $attributes{version_number};
198 192

  
199 193
    $version = SL::DB::RequirementSpecVersion->new(%attributes, version_number => $self->next_version_number)->save;
......
217 211
}
218 212

  
219 213
1;
214
__END__
215

  
216
=pod
217

  
218
=encoding utf8
219

  
220
=head1 NAME
221

  
222
SL::DB::RequirementSpec - RDBO model for requirement specs
223

  
224
=head1 OVERVIEW
225

  
226
The database structure behind requirement specs is a bit involved. The
227
important thing is how working copy/versions are handled.
228

  
229
The table contains three important columns: C<id> (which is also the
230
primary key), C<working_copy_id> and C<version_id>. C<working_copy_id>
231
is a self-referencing column: it can be C<NULL>, but if it isn't then
232
it contains another requirement spec C<id>. C<version_id> on the other
233
hand references the table C<requirement_spec_versions>.
234

  
235
The design is as follows:
236

  
237
=over 2
238

  
239
=item * The user is always working on a working copy. The working copy
240
is identified in the database by having C<working_copy_id> set to
241
C<NULL>.
242

  
243
=item * All other entries in this table are referred to as I<versioned
244
copies>. A versioned copy is a copy of a working frozen at the moment
245
in time it was created. Each versioned copy refers back to the working
246
copy it belongs to: each has its C<working_copy_id> set.
247

  
248
=item * Each versioned copy must reference an entry in the table
249
C<requirement_spec_versions>. Meaning: for each versioned copy
250
C<version_id> must not be C<NULL>.
251

  
252
=item * Directly after creating a versioned copy even the working copy
253
itself points to a certain version via its C<version_id> column: to
254
the same version that the versioned copy just created points
255
to. However, any modification that will be visible to the customer
256
(text, positioning etc but not internal things like time/cost
257
estimation changes) will cause the working copy to be set to 'no
258
version' again. This is achieved via before save hooks in Perl.
259

  
260
=back
261

  
262
=head1 DATABASE TRIGGERS AND CHECKS
263

  
264
Several database triggers and consistency checks exist that manage
265
requirement specs, their items and their dependencies. These are
266
described here instead of in the individual files for the other RDBO
267
models.
268

  
269
=head2 DELETION
270

  
271
When you delete a requirement spec all of its dependencies (items,
272
text blocks, versions etc.) are deleted by triggers.
273

  
274
When you delete an item (either a section or a (sub-)function block)
275
all of its children will be deleted as well. This will trigger the
276
same trigger resulting in a recursive deletion with the bottom-most
277
items being deleted first. Their item dependencies are deleted as
278
well.
279

  
280
=head2 UPDATING
281

  
282
Whenever you update a requirement spec item a trigger will fire that
283
will update the parent's C<time_estimation> column. This also happens
284
when an item is deleted or updated.
285

  
286
=head2 CONSISTENCY CHECKS
287

  
288
Several consistency checks are applied to requirement spec items:
289

  
290
=over 2
291

  
292
=item * Column C<requirement_spec_item.item_type> can only contain one of
293
the values C<section>, C<function-block> or C<sub-function-block>.
294

  
295
=item * Column C<requirement_spec_item.parent_id> must be C<NULL> if
296
C<requirement_spec_item.item_type> is set to C<section> and C<NOT
297
NULL> otherwise.
298

  
299
=back
300

  
301
=head1 FUNCTIONS
302

  
303
=over 4
304

  
305
=item C<copy_from $source, %attributes>
306

  
307
Copies everything (basic attributes like type/title/customer, items,
308
text blocks, time/cost estimation) save for the versions from the
309
other requirement spec object C<$source> into C<$self> and saves
310
it. This is done within a transaction.
311

  
312
C<%attributes> are attributes that are assigned to C<$self> after all
313
the basic attributes from C<$source> have been assigned.
314

  
315
This function can be used for resetting a working copy to a specific
316
version. Example:
317

  
318
 my $requirement_spec = SL::DB::RequirementSpec->new(id => $::form->{id})->load;
319
 my $versioned_copy   = SL::DB::RequirementSpec->new(id => $::form->{versioned_copy_id})->load;
320

  
321
  $requirement_spec->copy_from(
322
    $versioned_copy,
323
    version_id => $versioned_copy->version_id,
324
  );
325

  
326
=item C<create_copy>
327

  
328
Creates and returns a copy of C<$self>. The copy is already
329
saved. Creating the copy happens within a transaction.
330

  
331
=item C<create_version %attributes>
332

  
333
Prerequisites: C<$self> must be a working copy (see L</working_copy>),
334
not a versioned copy.
335

  
336
This function creates a new version for C<$self>. This involves
337
several steps:
338

  
339
=over 2
340

  
341
=item 1. The next version number is calculated using
342
L</next_version_number>.
343

  
344
=item 2. An instance of L<SL::DB::RequirementSpecVersion> is
345
created. Its attributes are copied from C<%attributes> save for the
346
version number which is taken from step 1.
347

  
348
=item 3. A copy of C<$self> is created with L</create_copy>.
349

  
350
=item 4. The version instance created in step is assigned to the copy
351
from step 3.
352

  
353
=item 5. The C<version_id> in C<$self> is set to the copy's ID from
354
step 3.
355

  
356
=back
357

  
358
All this is done within a transaction.
359

  
360
In case of success a two-element list is returned consisting of the
361
copy & version objects created in steps 3 and 2 respectively. In case
362
of a failure an empty list will be returned.
363

  
364
=item C<displayable_name>
365

  
366
Returns a human-readable name for this instance consisting of the type
367
and the title.
368

  
369
=item C<highest_version>
370

  
371
Given a working copy C<$self> this function returns the versioned copy
372
of C<$self> with the highest version number. If such a version exist
373
its instance is returned. Otherwise C<undef> is returned.
374

  
375
This can be used for calculating the difference between the working
376
copy and the last version created for it.
377

  
378
=item C<invalidate_version>
379

  
380
Prerequisites: C<$self> must be a working copy (see L</working_copy>),
381
not a versioned copy.
382

  
383
Sets the C<version_id> field to C<undef> and saves C<$self>.
384

  
385
=item C<is_working_copy>
386

  
387
Returns trueish if C<$self> is a working copy and not a versioned
388
copy. The condition for this is that C<working_copy_id> is C<undef>.
389

  
390
=item C<next_version_number>
391

  
392
Calculates and returns the next version number for this requirement
393
spec. Version numbers start at 1 and are incremented by one for each
394
version created for it, no matter whether or not it has been reverted
395
to a previous version since. It boils down to this pseudo-code:
396

  
397
  if (has_never_had_a_version)
398
    return 1
399
  else
400
    return max(version_number for all versions for this requirement spec) + 1
401

  
402
=item C<sections>
403

  
404
An alias for L</sections_sorted>.
405

  
406
=item C<sections_sorted>
407

  
408
Returns a list of requirement spec items that
409

  
410
This is not a writer. Use the C<items> relationship for that.
411

  
412
=item C<text_blocks_sorted %params>
413

  
414
Returns an array (or an array reference depending on context) of text
415
blocks sorted by their positional column in ascending order. If the
416
C<output_position> parameter is given then only the text blocks
417
belonging to that C<output_position> are returned.
418

  
419
=item C<validate>
420

  
421
Validate values before saving. Returns list or human-readable error
422
messages (if any).
423

  
424
=item C<versioned_copies_sorted %params>
425

  
426
Returns an array (or an array reference depending on context) of
427
versioned copies sorted by their version number in ascending order. If
428
the C<max_version_number> parameter is given then only the versioned
429
copies whose version number is less than or equal to
430
C<max_version_number> are returned.
431

  
432
=back
433

  
434
=head1 BUGS
435

  
436
Nothing here yet.
437

  
438
=head1 AUTHOR
439

  
440
Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
441

  
442
=cut
SL/DB/RequirementSpecItem.pm
126 126
}
127 127

  
128 128
1;
129
__END__
130

  
131
=pod
132

  
133
=encoding utf8
134

  
135
=head1 NAME
136

  
137
SL::DB::RequirementSpecItem - Items for requirement specs
138

  
139
=head1 OVERVIEW
140

  
141
Please see L<SL::DB::RequirementSpec> for the architectual overview.
142

  
143
=head1 FUNCTIONS
144

  
145
=over 4
146

  
147
=item C<child_type>
148

  
149
Returns the C<item_type> for children of C<$self>.
150

  
151
=item C<children_sorted>
152

  
153
Returns an array (or an array reference depending on context) of
154
direct children (not of grandchildren) for C<$self> ordered by their
155
positional column in ascending order.
156

  
157
=item C<section>
158

  
159
Returns the section this item belongs to. It can be C<$self> if
160
C<$self> is already a section, its parent or grandparent.
161

  
162
=item C<validate>
163

  
164
Validates before saving and returns an array of human-readable error
165
messages in case of an error.
166

  
167
=back
168

  
169
=head1 BUGS
170

  
171
Nothing here yet.
172

  
173
=head1 AUTHOR
174

  
175
Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
176

  
177
=cut

Auch abrufbar als: Unified diff