tracksperanto 2.2.4 → 2.3.0

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.
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