tracksperanto 2.2.4 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,9 @@
1
+ === 2.3.0 / 2011-17-04
2
+
3
+ * Fixed issue#1 (Last tracker in some Flame exports receives an empty shift channel)
4
+ * Nicer progress reports
5
+ * Modified the Accumulator to support nested iterations and random access
6
+
1
7
  === 2.2.4 / 2011-04-04
2
8
 
3
9
  * Properly deal with negative frames happening in Shake scripts sometimes. We will now recognize a script starting at negative
data/MIT_LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2004-2010 Julik Tarkhanov
1
+ Copyright (c) 2004-2011 Julik Tarkhanov
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/Manifest.txt CHANGED
@@ -56,6 +56,7 @@ lib/tracksperanto/ext_io.rb
56
56
  lib/tracksperanto/flame_builder.rb
57
57
  lib/tracksperanto/format_detector.rb
58
58
  lib/tracksperanto/keyframe.rb
59
+ lib/tracksperanto/pbar.rb
59
60
  lib/tracksperanto/progressive_io.rb
60
61
  lib/tracksperanto/returning.rb
61
62
  lib/tracksperanto/safety.rb
data/bin/tracksperanto CHANGED
@@ -17,6 +17,7 @@ require 'progressbar'
17
17
  require "net/http"
18
18
  require "open-uri"
19
19
  require "timeout"
20
+ require File.dirname(__FILE__) + '/../lib/tracksperanto/pbar'
20
21
 
21
22
  def version_check(version_present)
22
23
  begin
@@ -104,12 +105,11 @@ unless File.exist?(input_file)
104
105
  exit(-1)
105
106
  end
106
107
 
107
- pbar = ProgressBar.new("Converting", 100, $stderr)
108
- pipe = Tracksperanto::Pipeline::Base.new(:progress_block => lambda{|p,m| pbar.set(p) }, :middleware_tuples => $middlewares)
108
+ pbar = Tracksperanto::PBar.new("Converting", 100, $stderr)
109
+ pipe = Tracksperanto::Pipeline::Base.new(:progress_block => lambda{|p,m| pbar.set_with_message(p, m) }, :middleware_tuples => $middlewares)
109
110
  pipe.exporters = [Tracksperanto.get_exporter(writer_klass_name)] if writer_klass_name
110
111
  pipe.run(input_file, options)
111
112
  pbar.finish
112
113
 
113
- puts "Converted #{pipe.converted_points} trackers with #{pipe.converted_keyframes} keys."
114
114
  puts disclaimer
115
115
  version_check(Tracksperanto::VERSION)
@@ -44,6 +44,9 @@ class Tracksperanto::Export::FlameStabilizer < Tracksperanto::Export::Base
44
44
  t.tolerance 100
45
45
  end
46
46
  end
47
+
48
+ # Write the finalizing "End"
49
+ @writer.write_loose!("end")
47
50
  end
48
51
 
49
52
  def start_tracker_segment(tracker_name)
@@ -66,6 +69,11 @@ class Tracksperanto::Export::FlameStabilizer < Tracksperanto::Export::Base
66
69
  # how many keyframes they should contain
67
70
  write_shift_channel("shift/x", @x_shift_values)
68
71
  write_shift_channel("shift/y", @y_shift_values)
72
+
73
+ # And finish with the offset channels. The order of channels is important!
74
+ # (otherwise the last tracker's shift animation is not imported by Flame)
75
+ # https://github.com/guerilla-di/tracksperanto/issues/1
76
+ write_offset_channels
69
77
  end
70
78
 
71
79
  private
@@ -140,7 +148,6 @@ class Tracksperanto::Export::FlameStabilizer < Tracksperanto::Export::Base
140
148
  write_ref_width_and_height
141
149
  write_ref_channels(x, y)
142
150
  write_deltax_and_deltay_channels
143
- write_offset_channels
144
151
  end
145
152
 
146
153
  def write_track_channels
@@ -75,13 +75,15 @@ class Tracksperanto::Import::FlameStabilizer < Tracksperanto::Import::Base
75
75
  report_progress("Extracting setup size")
76
76
  self.width, self.height = extract_width_and_height_from_stream(@io)
77
77
  report_progress("Extracting all animation channels")
78
- channels = extract_channels_from_stream(@io)
78
+ channels, names = extract_channels_from_stream(@io)
79
79
 
80
80
  raise "The setup contained no channels that we could process" if channels.empty?
81
81
  raise "A channel was nil" if channels.find{|e| e.nil? }
82
82
 
83
83
  report_progress("Assembling tracker curves from channels")
84
- scavenge_trackers_from_channels(channels) {|t| yield(t) }
84
+ scavenge_trackers_from_channels(channels, names) {|t| yield(t) }
85
+ ensure
86
+ channels.clear
85
87
  end
86
88
 
87
89
  private
@@ -103,54 +105,20 @@ class Tracksperanto::Import::FlameStabilizer < Tracksperanto::Import::Base
103
105
  end
104
106
 
105
107
  end
106
- =begin
107
- Here's how a Flame channel looks like
108
- The Size will not be present if there are no keyframes
109
-
110
- Channel tracker1/ref/x
111
- Extrapolation constant
112
- Value 770.41
113
- Size 4
114
- KeyVersion 1
115
- Key 0
116
- Frame 1
117
- Value 770.41
118
- Interpolation constant
119
- End
120
- Key 1
121
- Frame 44
122
- Value 858.177
123
- Interpolation constant
124
- RightSlope 2.31503
125
- LeftSlope 2.31503
126
- End
127
- Key 2
128
- Frame 74
129
- Value 939.407
130
- Interpolation constant
131
- RightSlope 2.24201
132
- LeftSlope 2.24201
133
- End
134
- Key 3
135
- Frame 115
136
- Value 1017.36
137
- Interpolation constant
138
- End
139
- Colour 50 50 50
140
- End
141
- =end
142
108
 
143
109
  def extract_channels_from_stream(io)
144
- channels = []
110
+ channels = Tracksperanto::Accumulator.new
111
+ names = []
145
112
  channel_matcher = /Channel (.+)\n/
146
113
  until io.eof?
147
114
  line = io.gets
148
115
  if line =~ channel_matcher && channel_is_useful?(line)
149
116
  report_progress("Extracting channel #{$1}")
150
117
  channels << ChannelBlock.new(io, $1)
118
+ names << $1
151
119
  end
152
120
  end
153
- channels
121
+ [channels, names]
154
122
  end
155
123
 
156
124
  USEFUL_CHANNELS = %w( /shift/x /shift/y /ref/x /ref/y ).map(&Regexp.method(:new))
@@ -166,21 +134,32 @@ Channel tracker1/ref/x
166
134
 
167
135
  REF_CHANNEL = "ref" # or "track" - sometimes works sometimes don't
168
136
 
169
- def scavenge_trackers_from_channels(channels)
170
- channels.select{|e| e.name =~ /\/#{REF_CHANNEL}\/x/}.each do | track_x |
171
- report_progress("Detected reference channel #{track_x}")
172
- yield grab_tracker(channels, track_x)
137
+ def scavenge_trackers_from_channels(channels, names)
138
+
139
+
140
+ channels.each do |c|
141
+ next unless c.name =~ /\/#{REF_CHANNEL}\/x/
142
+
143
+ report_progress("Detected reference channel #{c.name}")
144
+
145
+ yield grab_tracker(channels, c, names)
173
146
  end
174
147
  end
175
148
 
176
- def grab_tracker(channels, track_x)
149
+ def grab_tracker(channels, track_x, names)
177
150
  t = Tracksperanto::Tracker.new(:name => track_x.name.split('/').shift)
178
151
 
179
152
  report_progress("Extracting tracker #{t.name}")
180
153
 
181
- track_y = channels.find{|e| e.name == "#{t.name}/#{REF_CHANNEL}/y" }
182
- shift_x = channels.find{|e| e.name == "#{t.name}/shift/x" }
183
- shift_y = channels.find{|e| e.name == "#{t.name}/shift/y" }
154
+ # This takes a LONG time when we have alot of channels, we need a precache of
155
+ # some sort to do this
156
+ ref_idx = names.index("#{t.name}/#{REF_CHANNEL}/y")
157
+ shift_x_idx = names.index("#{t.name}/shift/x")
158
+ shift_y_idx = names.index("#{t.name}/shift/y")
159
+
160
+ track_y = channels[ref_idx]
161
+ shift_x = channels[shift_x_idx]
162
+ shift_y = channels[shift_y_idx]
184
163
 
185
164
  shift_tuples = zip_curve_tuples(shift_x, shift_y)
186
165
  track_tuples = zip_curve_tuples(track_x, track_y)
@@ -207,4 +186,41 @@ Channel tracker1/ref/x
207
186
  end || track_tuples[0]
208
187
  base_track_tuple[1..2]
209
188
  end
210
- end
189
+ end
190
+
191
+ __END__
192
+
193
+ Here's how a Flame channel looks like
194
+ The Size will not be present if there are no keyframes
195
+
196
+ Channel tracker1/ref/x
197
+ Extrapolation constant
198
+ Value 770.41
199
+ Size 4
200
+ KeyVersion 1
201
+ Key 0
202
+ Frame 1
203
+ Value 770.41
204
+ Interpolation constant
205
+ End
206
+ Key 1
207
+ Frame 44
208
+ Value 858.177
209
+ Interpolation constant
210
+ RightSlope 2.31503
211
+ LeftSlope 2.31503
212
+ End
213
+ Key 2
214
+ Frame 74
215
+ Value 939.407
216
+ Interpolation constant
217
+ RightSlope 2.24201
218
+ LeftSlope 2.24201
219
+ End
220
+ Key 3
221
+ Frame 115
222
+ Value 1017.36
223
+ Interpolation constant
224
+ End
225
+ Colour 50 50 50
226
+ End
@@ -78,7 +78,7 @@ module Tracksperanto::ShakeGrammar
78
78
  end
79
79
  end
80
80
 
81
- INT_ATOM = /^(-?\d+)$/
81
+ INT_ATOM = /^(\d+)$/
82
82
  FLOAT_ATOM = /^([\-\d\.]+)$/
83
83
  STR_ATOM = /^\"/
84
84
  AT_FRAME = /^@(-?\d+)/
@@ -243,7 +243,7 @@ class Tracksperanto::Import::ShakeScript < Tracksperanto::Import::Base
243
243
  def collect_stabilizer_tracker(name, x_curve, y_curve)
244
244
  return unless valid_curves?(x_curve, y_curve)
245
245
 
246
- report_progress("Scavenging tracker #{name}")
246
+ report_progress("Assembling Stabilizer node tracker #{name}")
247
247
 
248
248
  keyframes = zip_curve_tuples(x_curve, y_curve).map do | (frame, x, y) |
249
249
  Tracksperanto::Keyframe.new(:frame => frame - 1, :abs_x => x, :abs_y => y)
@@ -26,6 +26,7 @@ class Tracksperanto::Import::Syntheyes < Tracksperanto::Import::Base
26
26
  @last_tracker.push(k)
27
27
  report_progress("Adding keyframe #{frame} to #{name}")
28
28
  end
29
+
29
30
  yield(@last_tracker) if @last_tracker && @last_tracker.any?
30
31
 
31
32
  end
data/lib/pipeline/base.rb CHANGED
@@ -140,52 +140,54 @@ module Tracksperanto::Pipeline
140
140
  @ios.push(io_with_progress)
141
141
 
142
142
  @accumulator = Tracksperanto::Accumulator.new
143
+ begin
144
+
145
+ # OBSOLETE - for this version we are going to permit it.
146
+ if importer.respond_to?(:stream_parse)
147
+ STDERR.puts "Import::Base#stream_parse(io) is obsolete, please rewrite your importer to use each instead"
148
+ importer.receiver = @accumulator
149
+ importer.stream_parse(io_with_progress)
150
+ else
151
+ importer.io = io_with_progress
152
+ importer.each {|t| @accumulator.push(t) unless t.empty? }
153
+ end
143
154
 
144
- # OBSOLETE - for this version we are going to permit it.
145
- if importer.respond_to?(:stream_parse)
146
- STDERR.puts "Import::Base#stream_parse(io) is obsolete, please rewrite your importer to use each instead"
147
- importer.receiver = @accumulator
148
- importer.stream_parse(io_with_progress)
149
- else
150
- importer.io = io_with_progress
151
- importer.each {|t| @accumulator.push(t) unless t.empty? }
152
- end
153
-
154
- report_progress(percent_complete = 50.0, "Validating #{@accumulator.size} imported trackers")
155
- raise NoTrackersRecoveredError if @accumulator.size.zero?
155
+ report_progress(percent_complete = 50.0, "Validating #{@accumulator.size} imported trackers")
156
+ raise NoTrackersRecoveredError if @accumulator.size.zero?
156
157
 
157
- report_progress(percent_complete, "Starting export")
158
+ report_progress(percent_complete, "Starting export")
158
159
 
159
- percent_per_tracker = (100.0 - percent_complete) / @accumulator.size
160
+ percent_per_tracker = (100.0 - percent_complete) / @accumulator.size
160
161
 
161
- # Use the width and height provided by the parser itself
162
- exporter.start_export(importer.width, importer.height)
162
+ # Use the width and height provided by the parser itself
163
+ exporter.start_export(importer.width, importer.height)
163
164
 
164
- # Now send each tracker through the middleware chain
165
- @accumulator.each_with_index do | t, tracker_idx |
165
+ # Now send each tracker through the middleware chain
166
+ @accumulator.each_with_index do | t, tracker_idx |
166
167
 
167
- kf_weight = percent_per_tracker / t.keyframes.length
168
- points += 1
169
- exporter.start_tracker_segment(t.name)
170
- t.each_with_index do | kf, idx |
171
- keyframes += 1
172
- exporter.export_point(kf.frame, kf.abs_x, kf.abs_y, kf.residual)
173
- report_progress(
174
- percent_complete += kf_weight,
175
- "Writing keyframe #{idx+1} of #{t.name.inspect}, #{@accumulator.size - tracker_idx} trackers to go"
176
- )
168
+ kf_weight = percent_per_tracker / t.keyframes.length
169
+ points += 1
170
+ exporter.start_tracker_segment(t.name)
171
+ t.each_with_index do | kf, idx |
172
+ keyframes += 1
173
+ exporter.export_point(kf.frame, kf.abs_x, kf.abs_y, kf.residual)
174
+ report_progress(
175
+ percent_complete += kf_weight,
176
+ "Writing keyframe #{idx+1} of #{t.name.inspect}, #{@accumulator.size - tracker_idx} trackers to go"
177
+ )
178
+ end
179
+ exporter.end_tracker_segment
177
180
  end
178
- exporter.end_tracker_segment
179
- end
180
- exporter.end_export
181
+ exporter.end_export
181
182
 
182
- report_progress(100.0, "Wrote #{points} points and #{keyframes} keyframes")
183
+ report_progress(100.0, "Wrote #{points} points and #{keyframes} keyframes")
183
184
 
184
- [points, keyframes]
185
- ensure
186
- @accumulator.clear
187
- @ios.map!{|e| e.close! rescue e.close }
188
- @ios.clear
185
+ [points, keyframes]
186
+ ensure
187
+ @accumulator.clear
188
+ @ios.map!{|e| e.close! rescue e.close }
189
+ @ios.clear
190
+ end
189
191
  end
190
192
 
191
193
  # Setup output files and return a single output
data/lib/tracksperanto.rb CHANGED
@@ -4,7 +4,7 @@ require 'tempfile'
4
4
 
5
5
  module Tracksperanto
6
6
  PATH = File.expand_path(File.dirname(__FILE__))
7
- VERSION = '2.2.4'
7
+ VERSION = '2.3.0'
8
8
 
9
9
  module Import; end
10
10
  module Export; end
@@ -17,26 +17,27 @@
17
17
  class Tracksperanto::Accumulator
18
18
  include Enumerable
19
19
 
20
+ DELIM = "\n"
21
+
20
22
  # Returns the number of objects stored so far
21
23
  attr_reader :size
22
24
 
23
25
  def initialize
24
26
  @store = Tracksperanto::BufferIO.new
25
-
26
27
  @size = 0
27
- @byte_size = 0
28
28
 
29
29
  super
30
30
  end
31
31
 
32
+ def empty?
33
+ @size.zero?
34
+ end
35
+
32
36
  # Store an object
33
37
  def push(object_to_store)
34
- @store.seek(@byte_size)
35
-
36
38
  blob = marshal_object(object_to_store)
37
39
  @store.write(blob)
38
40
  @size += 1
39
- @byte_size = @byte_size + blob.size
40
41
 
41
42
  object_to_store
42
43
  end
@@ -46,27 +47,57 @@ class Tracksperanto::Accumulator
46
47
  # Retreive each stored object in succession. All other Enumerable
47
48
  # methods are also available (but be careful with Enumerable#map and to_a)
48
49
  def each
49
- @store.rewind
50
- @size.times { yield(recover_object) }
50
+ with_separate_read_io do | iterable |
51
+ @size.times { yield(recover_object_from(iterable)) }
52
+ end
51
53
  end
52
54
 
53
55
  # Calls close! on the datastore and deletes the objects in it
54
56
  def clear
55
57
  @store.close!
56
58
  @size = 0
57
- @offsets = []
59
+ end
60
+
61
+ # Retreive a concrete object at index
62
+ def [](idx)
63
+ idx.respond_to?(:each) ? idx.map{|i| recover_at(i) } : recover_at(idx)
58
64
  end
59
65
 
60
66
  private
61
67
 
68
+ def recover_at(idx)
69
+ with_separate_read_io do | iterable |
70
+ iterable.seek(0)
71
+
72
+ # Do not unmarshal anything but wind the IO in fixed offsets
73
+ idx.times do
74
+ skip_bytes = iterable.gets("\t").to_i
75
+ iterable.seek(iterable.pos + skip_bytes)
76
+ end
77
+
78
+ recover_object_from(iterable)
79
+ end
80
+ end
81
+
82
+ # We first ensure that we have a disk-backed file, then reopen it as read-only
83
+ # and iterate through that (we will have one IO handle per loop nest)
84
+ def with_separate_read_io
85
+ iterable = File.open(@store.to_file.path, "r")
86
+ yield(iterable)
87
+ ensure
88
+ iterable.close
89
+ end
90
+
62
91
  def marshal_object(object_to_store)
63
92
  d = Marshal.dump(object_to_store)
64
- blob = [d.size, "\t", d, "\n"].join
93
+ blob = [d.size, "\t", d, DELIM].join
65
94
  end
66
95
 
67
- def recover_object
96
+ def recover_object_from(io)
68
97
  # Up to the tab is the amount of bytes to read
69
- demarshal_bytes = @store.gets("\t").strip.to_i
70
- Marshal.load(@store.read(demarshal_bytes))
98
+ demarshal_bytes = io.gets("\t").to_i
99
+ blob = io.read(demarshal_bytes)
100
+
101
+ Marshal.load(blob)
71
102
  end
72
103
  end
@@ -30,18 +30,34 @@ class Tracksperanto::BufferIO < DelegateClass(IO)
30
30
  __setobj__(nil)
31
31
  end
32
32
 
33
+ # Sometimes you just need to upgrade to a File forcibly (for example if you want)
34
+ # to have an object with many iterators sitting on it. We also flush here.
35
+ def to_file
36
+ replace_with_tempfile unless @tempfile_in
37
+ flush
38
+ self
39
+ end
40
+
41
+ # Tells whether this one is on disk
42
+ def file_backed?
43
+ @tempfile_in
44
+ end
45
+
33
46
  private
34
47
 
48
+ def replace_with_tempfile
49
+ sio = __getobj__
50
+ tf = Tempfile.new("tracksperanto-xbuf")
51
+ tf.write(sio.string)
52
+ tf.flush # Needed of we will reopen this file soon from another thread/loop
53
+ sio.string = ""
54
+ GC.start
55
+ __setobj__(tf)
56
+
57
+ @tempfile_in = true
58
+ end
59
+
35
60
  def replace_with_tempfile_if_needed
36
- return if @tempfile_in
37
- io = __getobj__
38
- if io.pos > MAX_IN_MEM_BYTES
39
- tf = Tempfile.new("tracksperanto-xbuf")
40
- tf.write(io.string)
41
- io.string = ""
42
- GC.start
43
- __setobj__(tf)
44
- @tempfile_in = true
45
- end
61
+ replace_with_tempfile if !@tempfile_in && pos > MAX_IN_MEM_BYTES
46
62
  end
47
63
  end
@@ -0,0 +1,13 @@
1
+ # We make use of the implementation details of the progress bar to also show our current status
2
+ class Tracksperanto::PBar < ::ProgressBar
3
+
4
+ def fmt_title
5
+ "%20s" % @title
6
+ end
7
+
8
+ def set_with_message(pcnt, message)
9
+ @title = message
10
+ set(pcnt)
11
+ end
12
+
13
+ end
@@ -122,14 +122,6 @@ Channel tracker1/ref/dy
122
122
  Interpolation constant
123
123
  End
124
124
  End
125
- Channel tracker1/offset/x
126
- Extrapolation constant
127
- Value 0
128
- End
129
- Channel tracker1/offset/y
130
- Extrapolation constant
131
- Value 0
132
- End
133
125
  Channel tracker1/shift/x
134
126
  Extrapolation constant
135
127
  Value 0
@@ -408,6 +400,14 @@ Channel tracker1/shift/y
408
400
  RightSlope 2.400
409
401
  End
410
402
  End
403
+ Channel tracker1/offset/x
404
+ Extrapolation constant
405
+ Value 0
406
+ End
407
+ Channel tracker1/offset/y
408
+ Extrapolation constant
409
+ Value 0
410
+ End
411
411
  Channel tracker2/track/x
412
412
  Extrapolation constant
413
413
  Value 960
@@ -508,14 +508,6 @@ Channel tracker2/ref/dy
508
508
  Interpolation constant
509
509
  End
510
510
  End
511
- Channel tracker2/offset/x
512
- Extrapolation constant
513
- Value 0
514
- End
515
- Channel tracker2/offset/y
516
- Extrapolation constant
517
- Value 0
518
- End
519
511
  Channel tracker2/shift/x
520
512
  Extrapolation constant
521
513
  Value 0
@@ -794,6 +786,14 @@ Channel tracker2/shift/y
794
786
  RightSlope 2.400
795
787
  End
796
788
  End
789
+ Channel tracker2/offset/x
790
+ Extrapolation constant
791
+ Value 0
792
+ End
793
+ Channel tracker2/offset/y
794
+ Extrapolation constant
795
+ Value 0
796
+ End
797
797
  Channel tracker3/track/x
798
798
  Extrapolation constant
799
799
  Value 960
@@ -894,14 +894,6 @@ Channel tracker3/ref/dy
894
894
  Interpolation constant
895
895
  End
896
896
  End
897
- Channel tracker3/offset/x
898
- Extrapolation constant
899
- Value 0
900
- End
901
- Channel tracker3/offset/y
902
- Extrapolation constant
903
- Value 0
904
- End
905
897
  Channel tracker3/shift/x
906
898
  Extrapolation constant
907
899
  Value 0
@@ -928,6 +920,14 @@ Channel tracker3/shift/y
928
920
  RightSlope 2.400
929
921
  End
930
922
  End
923
+ Channel tracker3/offset/x
924
+ Extrapolation constant
925
+ Value 0
926
+ End
927
+ Channel tracker3/offset/y
928
+ Extrapolation constant
929
+ Value 0
930
+ End
931
931
  ChannelEnd
932
932
  Tracker 0
933
933
  Active yes
@@ -959,3 +959,4 @@ Tracker 2
959
959
  FixedX no
960
960
  FixedY no
961
961
  Tolerance 100
962
+ End
@@ -151,8 +151,7 @@ class FlameImportTest < Test::Unit::TestCase
151
151
 
152
152
  def test_from_tracksperanto
153
153
  fixture = File.open(File.dirname(__FILE__) + '/samples/flame_stabilizer/fromTracksperanto.stabilizer')
154
- parser = Tracksperanto::Import::FlameStabilizer.new(:io => fixture)
155
- trackers = parser.to_a
154
+ trackers = Tracksperanto::Import::FlameStabilizer.new(:io => fixture).to_a
156
155
 
157
156
  assert_equal 3, trackers.length
158
157
  assert_equal 3, trackers[0].length
@@ -17,6 +17,49 @@ class TestAccumulator < Test::Unit::TestCase
17
17
  assert_equal 50_000, a.size
18
18
  end
19
19
 
20
+ def test_accumulator_saves_shitload_of_objs
21
+ a = Tracksperanto::Accumulator.new
22
+ 4.times { a.push("A \tstring") }
23
+ a.each {|e| assert_equal "A \tstring", e }
24
+ end
25
+
26
+ def test_accumulator_empty
27
+ a = Tracksperanto::Accumulator.new
28
+ assert a.empty?
29
+ a.push(1)
30
+ assert !a.empty?
31
+ end
32
+
33
+ def test_accumulator_supports_nested_iteration
34
+ a = Tracksperanto::Accumulator.new
35
+ ("A".."Z").each{|e| a << e}
36
+
37
+ accumulated = []
38
+ seen_g = false
39
+ a.each do | first_level |
40
+ if first_level == "G"
41
+ seen_g = true
42
+ # Force a nested iteration and break it midway
43
+ a.each do | second_level |
44
+ accumulated.push(second_level)
45
+ break if second_level == "E"
46
+ end
47
+ elsif seen_g
48
+ assert_equal "H", first_level
49
+ return
50
+ end
51
+ end
52
+ end
53
+
54
+ def test_random_access
55
+ a = Tracksperanto::Accumulator.new
56
+ letters = ("A".."Z").map{|e| "#{e}\r\nWow!"}.to_a
57
+ letters.map(&a.method(:push))
58
+
59
+ assert_equal "B\r\nWow!", a[1]
60
+ assert_equal "E\r\nWow!", a[4]
61
+ end
62
+
20
63
  def test_clear_calls_close_on_buffer
21
64
  io = Tracksperanto::BufferIO.new
22
65
  flexmock(io).should_receive(:close!)
@@ -19,4 +19,15 @@ class TestBufferIO < Test::Unit::TestCase
19
19
  flexmock(f).should_receive(:close!).once
20
20
  io.close!
21
21
  end
22
+
23
+ def test_to_file_forces_immediate_promotion_to_file
24
+ io = Tracksperanto::BufferIO.new
25
+ io.write("a" * 3000)
26
+ assert_equal 3000, io.pos
27
+ assert !io.file_backed?
28
+
29
+ f = io.to_file
30
+ assert_equal 3000, f.pos
31
+ assert f.file_backed?
32
+ end
22
33
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tracksperanto
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
4
+ hash: 3
5
5
  prerelease:
6
6
  segments:
7
7
  - 2
8
- - 2
9
- - 4
10
- version: 2.2.4
8
+ - 3
9
+ - 0
10
+ version: 2.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Julik Tarkhanov
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-04-06 00:00:00 +02:00
18
+ date: 2011-04-17 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -134,6 +134,7 @@ files:
134
134
  - lib/tracksperanto/flame_builder.rb
135
135
  - lib/tracksperanto/format_detector.rb
136
136
  - lib/tracksperanto/keyframe.rb
137
+ - lib/tracksperanto/pbar.rb
137
138
  - lib/tracksperanto/progressive_io.rb
138
139
  - lib/tracksperanto/returning.rb
139
140
  - lib/tracksperanto/safety.rb