pvn 0.0.5 → 0.0.6

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.
Files changed (37) hide show
  1. data/README.markdown +3 -2
  2. data/bin/modify_timestamps.rb +31 -0
  3. data/bin/populate.rb +231 -0
  4. data/bin/quote.rb +206 -0
  5. data/lib/pvn/app.rb +4 -1
  6. data/lib/pvn/io/element.rb +9 -0
  7. data/lib/pvn/subcommands/base/command.rb +15 -15
  8. data/lib/pvn/subcommands/base/options.rb +4 -0
  9. data/lib/pvn/subcommands/diff/command.rb +46 -0
  10. data/lib/pvn/subcommands/diff/differ.rb +65 -0
  11. data/lib/pvn/subcommands/diff/local_differ.rb +103 -0
  12. data/lib/pvn/subcommands/diff/options.rb +28 -0
  13. data/lib/pvn/subcommands/diff/repository_differ.rb +145 -0
  14. data/lib/pvn/subcommands/log/command.rb +29 -11
  15. data/lib/pvn/subcommands/log/options.rb +0 -4
  16. data/lib/pvn/subcommands/pct/command.rb +10 -176
  17. data/lib/pvn/subcommands/pct/diffcount.rb +4 -2
  18. data/lib/pvn/subcommands/pct/differ.rb +34 -0
  19. data/lib/pvn/subcommands/pct/local_differ.rb +103 -0
  20. data/lib/pvn/subcommands/pct/options.rb +0 -4
  21. data/lib/pvn/subcommands/pct/repository_differ.rb +78 -0
  22. data/lib/pvn/subcommands/revision/base_option.rb +48 -0
  23. data/lib/pvn/subcommands/revision/revision_option.rb +2 -37
  24. data/lib/pvn/subcommands/status/command.rb +13 -8
  25. data/lib/pvn/subcommands/status/options.rb +0 -4
  26. data/lib/svnx/cat/command.rb +2 -0
  27. data/lib/svnx/info/entry.rb +3 -0
  28. data/lib/synoption/exception.rb +12 -0
  29. data/lib/synoption/set.rb +26 -0
  30. data/test/integration/pvn/subcommands/diff/command_test.rb +153 -0
  31. data/test/integration/svnx/log/test.rb +1 -1
  32. data/test/unit/pvn/revision/entry_test.rb +0 -13
  33. data/test/unit/svnx/info/entries_test.rb +3 -3
  34. data/test/unit/svnx/info/entry_test.rb +3 -2
  35. data/test/unit/svnx/status/entries_test.rb +1 -1
  36. data/test/unit/synoption/set_test.rb +34 -18
  37. metadata +20 -4
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ require 'pvn/io/element'
5
+ require 'pvn/subcommands/diff/options'
6
+ require 'pvn/subcommands/base/command'
7
+ require 'tempfile'
8
+
9
+ $io = $stdout
10
+
11
+ module PVN::Subcommands::Diff
12
+ class Differ
13
+ include Loggable
14
+
15
+ attr_reader :whitespace
16
+ attr_reader :revision
17
+
18
+ def initialize options
19
+ end
20
+
21
+ def to_revision_string rev
22
+ rev ? "revision #{rev}" : "working copy"
23
+ end
24
+
25
+ def write_to_temp entry, lines
26
+ Tempfile.open('pvn') do |to|
27
+ topath = to.path
28
+ info "topath: #{topath}"
29
+ to.puts lines
30
+ to.close
31
+ cmd = "diff -u"
32
+ label = " -L '#{entry.path} (revision 0)'"
33
+ 2.times do
34
+ cmd << label
35
+ end
36
+ cmd << " #{frpath}"
37
+ cmd << " #{entry.path}"
38
+ IO.popen(cmd) do |io|
39
+ puts io.readlines
40
+ end
41
+ end
42
+ end
43
+
44
+ def run_diff_command entry, fromrev, torev, frompath, topath
45
+ info "whitespace: #{whitespace}"
46
+
47
+ cmd = "diff -u"
48
+ if whitespace
49
+ cmd << " -x -b -x -w -x --ignore-eol-style"
50
+ end
51
+
52
+ [ fromrev, torev ].each do |rev|
53
+ revstr = to_revision_string rev
54
+ cmd << " -L '#{entry.path}\t(#{revstr})'"
55
+ end
56
+ cmd << " #{frompath}"
57
+ cmd << " #{topath}"
58
+ $io.puts "Index: #{entry.path}"
59
+ $io.puts "==================================================================="
60
+ IO.popen(cmd) do |io|
61
+ $io.puts io.readlines
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ require 'pvn/io/element'
5
+ require 'pvn/subcommands/diff/options'
6
+ require 'tempfile'
7
+ require 'pvn/subcommands/diff/differ'
8
+
9
+ module PVN::Subcommands::Diff
10
+ class LocalDiffer < Differ
11
+ def initialize options
12
+ super
13
+
14
+ paths = options.paths
15
+ paths = %w{ . } if paths.empty?
16
+
17
+ info "paths: #{paths}".yellow
18
+
19
+ allentries = Array.new
20
+
21
+ # we sort only the sub-entries, so the order in which paths were specified is preserved
22
+
23
+ @whitespace = options.whitespace
24
+
25
+ paths.each do |path|
26
+ info "path: #{path}".red
27
+ elmt = PVN::IO::Element.new :local => path
28
+ entries = elmt.find_files_by_status
29
+
30
+ allentries.concat entries.sort_by { |n| n.path }
31
+ end
32
+
33
+ info "allentries: #{allentries}"
34
+
35
+ allentries.each do |entry|
36
+ info "entry: #{entry.inspect}".on_blue
37
+ case entry.status
38
+ when 'modified'
39
+ show_as_modified entry
40
+ when 'deleted'
41
+ show_as_deleted entry
42
+ when 'added'
43
+ show_as_added entry
44
+ end
45
+ end
46
+ end
47
+
48
+ def read_working_copy entry
49
+ pn = Pathname.new entry.path
50
+ pn.readlines
51
+ end
52
+
53
+ def show_as_added entry
54
+ Tempfile.open('pvn') do |from|
55
+ # from is an empty file
56
+ from.close
57
+
58
+ # I think this is always revision 0
59
+ run_diff_command entry, 0, 0, from.path, entry.path
60
+ end
61
+ end
62
+
63
+ def show_as_deleted entry
64
+ elmt = PVN::IO::Element.new :local => entry.path
65
+
66
+ svninfo = elmt.get_info
67
+ lines = elmt.cat_remote
68
+
69
+ Tempfile.open('pvn') do |from|
70
+ from.puts lines
71
+ from.close
72
+ Tempfile.open('pvn') do |to|
73
+ # to is an empty file
74
+ to.close
75
+ run_diff_command entry, svninfo.revision, nil, from.path, to.path
76
+ end
77
+ end
78
+ end
79
+
80
+ def show_as_modified entry
81
+ # only doing working copy to remote now
82
+ elmt = PVN::IO::Element.new :local => entry.path
83
+
84
+ svninfo = elmt.get_info
85
+ remotelines = elmt.cat_remote
86
+
87
+ fromrev = svninfo.revision
88
+ torev = nil # AKA working copy
89
+
90
+ wclines = read_working_copy entry
91
+
92
+ Tempfile.open('pvn') do |from|
93
+ from.puts remotelines
94
+ from.close
95
+ Tempfile.open('pvn') do |to|
96
+ to.puts wclines
97
+ to.close
98
+ run_diff_command entry, fromrev, torev, from.path, to.path
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ require 'synoption/boolean_option'
5
+ require 'pvn/subcommands/revision/multiple_revisions_option'
6
+ require 'pvn/subcommands/revision/base_option'
7
+ require 'pvn/subcommands/base/options'
8
+
9
+ module PVN::Subcommands::Diff
10
+ class WhitespaceOption < PVN::BooleanOption
11
+ def initialize optargs = Hash.new
12
+ super :whitespace, '-w', 'ignore all whitespace', false
13
+ end
14
+ end
15
+
16
+ class ChangeOption < PVN::BaseRevisionOption
17
+ def initialize optargs = Hash.new
18
+ super :change, '-c', 'use the given revision against the previous one', nil
19
+ end
20
+ end
21
+
22
+ class OptionSet < PVN::Subcommands::Base::OptionSet
23
+ has_option :revision, PVN::MultipleRevisionsRegexpOption
24
+ has_option :change, ChangeOption
25
+ has_option :whitespace, WhitespaceOption
26
+ has_option :help, PVN::Subcommands::Base::HelpOption
27
+ end
28
+ end
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ require 'pvn/io/element'
5
+ require 'pvn/subcommands/diff/options'
6
+ require 'tempfile'
7
+ require 'pvn/subcommands/diff/differ'
8
+
9
+ module PVN::Subcommands::Diff
10
+ # this will replace RevisionEntry
11
+ class Revision
12
+ include Loggable
13
+
14
+ attr_reader :value
15
+ attr_reader :from
16
+ attr_reader :to
17
+
18
+ def initialize from, to = nil
19
+ if to
20
+ @from = from
21
+ @to = to
22
+ else
23
+ @from, @to = from.split(':')
24
+ end
25
+ info "@from: #{@from}"
26
+ info "@to: #{@to}"
27
+ end
28
+
29
+ def to_s
30
+ str = @from.to_s
31
+ if @to
32
+ str << ':' << @to.to_s
33
+ end
34
+ str
35
+ end
36
+
37
+ def head?
38
+ @to == nil
39
+ end
40
+ end
41
+
42
+ class RepositoryDiffer < Differ
43
+ include Loggable
44
+
45
+ attr_reader :whitespace
46
+ attr_reader :revision
47
+
48
+ def initialize options
49
+ paths = options.paths
50
+ paths = %w{ . } if paths.empty?
51
+
52
+ info "paths: #{paths}"
53
+
54
+ allentries = Array.new
55
+
56
+ # we sort only the sub-entries, so the order in which paths were specified is preserved
57
+
58
+ @whitespace = options.whitespace
59
+ rev = options.revision
60
+ change = options.change
61
+ info "change: #{change}"
62
+ info "rev: #{rev}"
63
+ info "rev: #{rev.class}"
64
+
65
+ if change
66
+ @revision = Revision.new(change.to_i - 1, change.to_i)
67
+ elsif rev.kind_of?(Array)
68
+ if rev.size == 2
69
+ # this is some contorting, since -rx:y does not mean comparing the files
70
+ # in changelist x; it means all the entries from x+1 through y, inclusive.
71
+
72
+ ### $$$ this doesn't handle dates:
73
+ @revision = Revision.new(rev[0].to_i + 1, rev[0].to_i)
74
+ else
75
+ from, to = rev[0].split(':')
76
+ @revision = Revision.new(from.to_i + 1, to)
77
+ end
78
+ end
79
+
80
+ info "revision: #{@revision}; #{@revision.class}".on_blue
81
+
82
+ # maps from pathnames to { :first, :last } revision updated.
83
+ paths_to_revisions = Hash.new
84
+
85
+ paths.each do |path|
86
+ logentries = get_log_entries path, @revision
87
+ logentries.each do |logentry|
88
+ info "logentry: #{logentry}".yellow
89
+ info "logentry.revision: #{logentry.revision}".yellow
90
+ info "logentry.paths: #{logentry.paths}".yellow
91
+
92
+ # should add the revision to this, so we know which is the first
93
+ # version to compare against the last:
94
+
95
+ logentry.paths.each do |lp|
96
+ info "lp: #{lp.inspect}".cyan
97
+ allentries << { :path => lp, :revision => logentry.revision }
98
+ rec = paths_to_revisions[lp.name]
99
+ if rec
100
+ rec[:last] = logentry.revision
101
+ else
102
+ rec = { :first => logentry.revision, :action => lp.action }
103
+ end
104
+ paths_to_revisions[lp.name] = rec
105
+ end
106
+ end
107
+ end
108
+
109
+ paths_to_revisions.sort.each do |name, revisions|
110
+ puts "name: #{name}"
111
+ puts "revisions: #{revisions}"
112
+ end
113
+
114
+ info "allentries: #{allentries}"
115
+
116
+ allentries.each do |entry|
117
+ info "entry: #{entry.inspect}".on_blue
118
+ next if true
119
+ case entry.status
120
+ when 'modified'
121
+ show_as_modified entry
122
+ when 'deleted'
123
+ show_as_deleted entry
124
+ when 'added'
125
+ show_as_added entry
126
+ end
127
+ end
128
+ end
129
+
130
+ def get_log_entries path, revision
131
+ cmdargs = Hash.new
132
+ cmdargs[:path] = path
133
+ cmdargs[:revision] = revision.to_s
134
+ cmdargs[:verbose] = true
135
+
136
+ # should be conditional on revision:
137
+ cmdargs[:use_cache] = false
138
+
139
+ logargs = SVNx::LogCommandArgs.new cmdargs
140
+ elmt = PVN::IO::Element.new :local => path
141
+ log = elmt.log logargs
142
+ entries = log.entries
143
+ end
144
+ end
145
+ end
@@ -31,10 +31,33 @@ module PVN::Subcommands::Log
31
31
  example "pvn log -r 122 -v", "Prints log entry for revision 122, with the files in that change."
32
32
  example "pvn log -u barney", "Prints log entries only for user 'barney', with the default limit."
33
33
 
34
- def initialize options = nil
35
- return unless options
34
+ def init options
35
+ paths = options.paths
36
36
 
37
- path = options.paths[0] || "."
37
+ paths = %w{ . } if paths.empty?
38
+
39
+ info "paths: #{paths}"
40
+
41
+ allentries = Array.new
42
+
43
+ paths.each do |path|
44
+ allentries.concat find_entries_for_path path, options
45
+ end
46
+
47
+ # we can show relative revisions for a single path, without filtering by
48
+ # user, or by limit or revision.
49
+
50
+ show_relative = !options.user && paths.size == 1 && !options.revision
51
+
52
+ # this should be refined to options.revision.head?
53
+ from_head = show_relative
54
+ from_tail = show_relative && !options.limit
55
+
56
+ ef = PVN::Log::EntriesFormatter.new options.color, allentries, from_head, from_tail
57
+ puts ef.format
58
+ end
59
+
60
+ def find_entries_for_path path, options
38
61
  cmdargs = Hash.new
39
62
  cmdargs[:path] = path
40
63
 
@@ -55,10 +78,6 @@ module PVN::Subcommands::Log
55
78
  log = elmt.log logargs
56
79
  entries = log.entries
57
80
 
58
- # this should be refined to options.revision.head?
59
- from_head = !options.revision
60
- from_tail = !options.limit && !options.revision
61
-
62
81
  info { "options: #{options}" }
63
82
  info { "options.user: #{options.user}" }
64
83
 
@@ -69,10 +88,9 @@ module PVN::Subcommands::Log
69
88
  # don't show relative revisions, since we've got a slice out of the list:
70
89
  from_head = nil
71
90
  from_tail = nil
72
- end
73
-
74
- ef = PVN::Log::EntriesFormatter.new options.color, entries, from_head, from_tail
75
- puts ef.format
91
+ end
92
+
93
+ entries
76
94
  end
77
95
 
78
96
  def find_entries_for_user entries, user, limit
@@ -37,9 +37,5 @@ module PVN::Subcommands::Log
37
37
  has_option :limit, LimitOption
38
38
  has_option :user, UserOption
39
39
  has_option :verbose, PVN::BooleanOption, [ :verbose, '-v', [ "include the files in the change" ], false ]
40
-
41
- def paths
42
- unprocessed
43
- end
44
40
  end
45
41
  end
@@ -1,20 +1,14 @@
1
1
  #!/usr/bin/ruby -w
2
2
  # -*- ruby -*-
3
3
 
4
- require 'pvn/io/element'
5
4
  require 'pvn/subcommands/pct/options'
6
5
  require 'pvn/subcommands/base/command'
7
- require 'svnx/info/command'
8
- require 'svnx/status/command'
9
- require 'svnx/cat/command'
10
- require 'pvn/subcommands/pct/diffcount'
11
- require 'set'
6
+ require 'pvn/subcommands/pct/local_differ'
7
+ require 'pvn/subcommands/pct/repository_differ'
12
8
 
13
9
  module PVN::Subcommands::Pct
14
10
  class Command < PVN::Subcommands::Base::Command
15
11
 
16
- DEFAULT_LIMIT = 15
17
-
18
12
  subcommands [ "pct" ]
19
13
  description "Compares revisions as a percentage of lines modified."
20
14
  usage "[OPTIONS] FILE..."
@@ -33,177 +27,17 @@ module PVN::Subcommands::Pct
33
27
  optscls
34
28
 
35
29
  example "pvn pct foo.rb", "Prints the changes in foo.rb, working copy against BASE."
36
- example "pvn pct -r114:121", "Prints the changes for all files, revision 114 against 121. (not yet supported)"
30
+ example "pvn pct -r114:121", "Prints the changes for all files, revision 114 against 121."
37
31
  example "pvn pct -rHEAD", "Prints the changes, working copy against HEAD. (not yet supported)"
38
- example "pvn pct -r117", "Prints the changes between revision 117 and the previous revision. (not yet supported)"
39
- example "pvn pct -7", "Prints the changes between relative revision -7 and the previous revision. (not yet supported)"
40
- example "pvn pct -r31 -4", "Prints the changes between revision 31 and relative revision -4. (not yet supported)"
41
-
42
- class << self
43
- # base command aliases new to init, so we're aliasing init to init2
44
- alias_method :init2, :init
45
-
46
- def init options
47
- if options.revision && !options.revision.empty?
48
- CommandRepository.init2 options
49
- else
50
- CommandLocal.init2 options
51
- end
52
- end
53
- end
54
-
55
- def initialize options = nil
56
- end
57
- end
58
-
59
- class CommandLocal < Command
60
- def initialize options
61
- # do we support multiple paths?
62
- path = options.paths[0] || '.'
63
-
64
- elmt = PVN::IO::Element.new :local => path || '.'
65
- modified = elmt.find_modified_files
66
- info "modified: #{modified}".blue
67
-
68
- total = PVN::DiffCount.new
69
-
70
- modified = modified.sort_by { |n| n.path }
71
-
72
- modified.each do |entry|
73
- catcmd = SVNx::CatCommand.new entry.path
74
- info "catcmd: #{catcmd}"
75
-
76
- svn_count = catcmd.execute.size
77
- info "svn_count: #{svn_count}"
78
-
79
- local_count = Pathname.new(entry.path).readlines.size
80
- info "local_count: #{local_count}"
32
+ example "pvn pct -r117", "Prints the changes between revision 117 and the previous revision."
33
+ example "pvn pct -7", "Prints the changes between relative revision -7 and the previous revision."
34
+ example "pvn pct -r31 -4", "Prints the changes between revision 31 and relative revision -4."
81
35
 
82
- dc = PVN::DiffCount.new svn_count, local_count
83
- total << dc
84
- dc.print entry.path
85
- end
86
-
87
- total.print 'total'
88
- end
89
- end
90
-
91
- class CommandRepository < Command
92
- ### $$$ this belongs in Revision
93
- def get_from_to_revisions rev
94
- if rev.kind_of? Array
95
- if rev.size == 1
96
- if md = Regexp.new('(.+):(.+)').match(rev[0])
97
- return [ md[1], md[2] ]
98
- else
99
- return [ (rev[0].to_i - 1).to_s, rev[0] ]
100
- end
101
- else
102
- return [ rev[0], rev[1] ]
103
- end
36
+ def init options
37
+ if options.revision && !options.revision.empty?
38
+ RepositoryDiffer.new options
104
39
  else
105
- info "rev: #{rev}".bold.white.on_red
106
- end
107
- end
108
-
109
- def get_line_count path, revision
110
- cmdargs = SVNx::CatCommandArgs.new :path => path, :revision => revision
111
- catcmd = SVNx::CatCommand.new cmdargs
112
- catcmd.execute.size
113
- end
114
-
115
- def initialize options
116
- # what was modified between the revisions?
117
-
118
- path = options.paths[0] || "."
119
- info "path: #{path}"
120
-
121
- elmt = PVN::IO::Element.new :local => path || '.'
122
- modified = elmt.find_modified_entries options.revision
123
-
124
- modnames = modified.collect { |m| m.name }.sort.uniq
125
-
126
- fromrev, torev = get_from_to_revisions options.revision
127
-
128
- total = PVN::DiffCount.new
129
-
130
- reporoot = elmt.repo_root
131
-
132
- direlmt = PVN::IO::Element.new :local => '.'
133
- svninfo = direlmt.get_info
134
-
135
- filter = svninfo.url.dup
136
- filter.slice! svninfo.root
137
- info "filter: #{filter}"
138
- filterre = Regexp.new '^' + filter + '/'
139
-
140
- modnames.each do |mod|
141
- fullpath = reporoot + mod
142
- elmt = PVN::IO::Element.new :path => fullpath
143
-
144
- next unless elmt.has_revision? fromrev
145
- next unless elmt.has_revision? torev
146
- next if elmt.get_info.kind == 'dir'
147
-
148
- from_count = get_line_count fullpath, fromrev
149
- to_count = get_line_count fullpath, torev
150
-
151
- dc = PVN::DiffCount.new from_count, to_count
152
- total << dc
153
- mod.slice! filterre
154
- dc.print mod
155
- end
156
-
157
- total.print 'total'
158
- end
159
-
160
-
161
- end
162
-
163
- end
164
-
165
- __END__
166
-
167
- elmt = PVN::IO::Element.new :local => clargs.path || '.'
168
- info "elmt: #{elmt}".red
169
-
170
- stats = { :modified => 0, :added => 0, :deleted => 0 }
171
-
172
- if elmt.directory?
173
- info "elmt.directory?: #{elmt.directory?}"
174
-
175
- # $$$ todo: recurse even when local has been removed (this is the
176
- # awaited "pvn find").
177
-
178
- changed = Array.new
179
- elmt.local.find do |fd|
180
- info "fd: #{fd}; #{fd.class}"
181
- Find.prune if fd.rootname.to_s == '.svn'
182
- if fd.file?
183
- subelmt = PVN::IO::Element.new :local => fd.to_s
184
- info "subelmt: #{subelmt}"
185
- status = subelmt.status
186
- info "status: #{status}".red
187
- end
188
- end
189
-
190
- # info "changed: #{changed}"
191
- elsif elmt.file?
192
- info "elmt.local: #{elmt.local}".cyan
193
-
194
- status = elmt.status
195
- info "status: #{status}"
196
-
197
- case status
198
- when "added"
199
- info "elmt: #{elmt}".green
200
- when "modified"
201
- info "elmt: #{elmt}".yellow
202
- when "deleted"
203
- info "elmt: #{elmt}".red
204
- else
205
- info "elmt: #{elmt}".cyan
206
- end
40
+ LocalDiffer.new options
207
41
  end
208
42
  end
209
43
  end
@@ -5,13 +5,15 @@ module PVN
5
5
  class DiffCount
6
6
  attr_reader :from
7
7
  attr_reader :to
8
+ attr_reader :name
8
9
 
9
- def initialize from = 0, to = 0
10
+ def initialize from = 0, to = 0, name = nil
10
11
  @from = from
11
12
  @to = to
13
+ @name = name
12
14
  end
13
15
 
14
- def print name
16
+ def print name = @name
15
17
  diff = to - from
16
18
  diffpct = diff == 0 ? 0 : 100.0 * diff / from
17
19
 
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ require 'pvn/subcommands/pct/diffcount'
5
+
6
+ module PVN::Subcommands::Pct
7
+ class Differ
8
+ include Loggable
9
+
10
+ def initialize options
11
+ show_diff_counts options
12
+ end
13
+
14
+ def show_diff_counts options
15
+ paths = options.paths
16
+ paths = %w{ . } if paths.empty?
17
+
18
+ info "paths: #{paths}"
19
+
20
+ total = PVN::DiffCount.new 0, 0, 'total'
21
+
22
+ paths.each do |path|
23
+ diff_counts = get_diff_counts path, options
24
+
25
+ diff_counts.each do |dc|
26
+ total << dc
27
+ dc.print
28
+ end
29
+ end
30
+
31
+ total.print
32
+ end
33
+ end
34
+ end