redshift 1.3.15 → 1.3.16

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.
@@ -0,0 +1,83 @@
1
+ class Object
2
+ def diff other, &block
3
+ d = other - self
4
+ if block
5
+ block.call d
6
+ end
7
+ d
8
+ end
9
+
10
+ def filter
11
+ (yield self) && self
12
+ end
13
+ end
14
+
15
+ class NilClass
16
+ def diff other, &block
17
+ 0
18
+ end
19
+ end
20
+
21
+ class Array
22
+ def diff other, &block
23
+ a = (0...[self.size, (other ? other.size : 0)].min).map do |i|
24
+ self[i].diff(other[i], &block)
25
+ end
26
+ if block
27
+ block.call a, other.size - self.size
28
+ end
29
+ a
30
+ end
31
+
32
+ def filter(&block)
33
+ aa = inject([]) do |a, val|
34
+ v = val.filter(&block)
35
+ a << v if v
36
+ a
37
+ end
38
+ aa.empty? ? nil : aa
39
+ end
40
+ end
41
+
42
+ class Hash
43
+ def diff other, &block
44
+ ha = (self.keys & other.keys).inject({}) do |h,k|
45
+ h[k] = self[k].diff(other[k], &block); h
46
+ end
47
+ if block
48
+ block.call ha, self.keys - other.keys, other.keys - self.keys
49
+ end
50
+ ha
51
+ end
52
+
53
+ def filter(&block)
54
+ ha = inject({}) do |h, (key, val)|
55
+ v = val.filter(&block)
56
+ h[key] = v if v
57
+ h
58
+ end
59
+ ha.empty? ? nil : ha
60
+ end
61
+ end
62
+
63
+ if __FILE__ == $0
64
+
65
+ #r = [{0=>1, 2=>3, 1=>[-1=>-2, 7=>8]}].filter {|x| x>1}
66
+ #p r
67
+ #exit
68
+
69
+ a = {
70
+ "foo" => [ {1=>2}, {3=>4.2} ],
71
+ "bar" => [ 4, 5, [6, 8] ]
72
+ }
73
+
74
+ b = {
75
+ "foo" => [ {1=>2}, {3=>4} ],
76
+ "bar" => [ 4, 7, [6, 9] ]
77
+ }
78
+
79
+ require 'yaml'
80
+
81
+ y a.diff(b).filter {|x| x > -0.1}
82
+
83
+ end
@@ -0,0 +1,386 @@
1
+ autoload :Tempfile, "tempfile"
2
+
3
+ # Interface to gnuplot and, eventually, other plotting apps.
4
+ module Plot
5
+ # +app+ is the name of the plot program. Yields and returns the plot instance.
6
+ # Use the #add, #command, and #show methods on this object.
7
+ def Plot.new(app = ENV['PLOTTER'] || 'gnuplot', &block)
8
+ plot =
9
+ case app
10
+ when /gnuplot/i
11
+ Gnuplot.new app
12
+
13
+ when /^matlab/i
14
+ raise "matlab not supported yet.\n Try 'gnuplot'."
15
+
16
+ else
17
+ raise "Plot doesn't recognize '#{app}'.\n Try 'gnuplot'."
18
+ end
19
+ yield plot if block_given?
20
+ plot
21
+ end
22
+
23
+ class GenericPlot
24
+ # Array of strings that have been sent to the plot program.
25
+ attr_reader :command_history
26
+
27
+ def initialize app
28
+ @app = app
29
+ @files = []
30
+ @command_queue = []
31
+ @command_history = []
32
+ @data_stuff = []
33
+ end
34
+
35
+ def clear_data
36
+ @data_stuff = []
37
+ end
38
+
39
+ # Closes the plot window and cleans up tempfiles, if you want
40
+ # to do that before the user closes the window explicitly.
41
+ # Doesn't work with the -persist option typically used on windows.
42
+ def close
43
+ if @pipe
44
+ @pipe.close
45
+ @pipe = nil
46
+ elsif @pid # only when parent has forked the plot handler
47
+ Process.kill("TERM", @pid)
48
+ Process.wait(@pid)
49
+ @pid = nil
50
+ else
51
+ raise "can't close plot"
52
+ end
53
+ @files = nil # let tempfiles be GC-ed and deleted
54
+ end
55
+
56
+ # Send +str+ to the plot program, or, if #show has not been called yet,
57
+ # queue the string to be sent later when #show is called.
58
+ def command str
59
+ command_history << str
60
+ if @pipe
61
+ @pipe.puts str
62
+ else
63
+ @command_queue << str
64
+ end
65
+ end
66
+
67
+ # Start the plotting program by opening a pipe to it. Send all queued
68
+ # commands. You can continue to send commands by calling #command.
69
+ def show
70
+ unless @pipe
71
+ @pipe = IO.popen @app, "w"
72
+ @pipe.sync = true
73
+ while (cmd=@command_queue.shift)
74
+ @pipe.puts cmd
75
+ end
76
+ end
77
+ end
78
+
79
+ # Add a data element to the plot with specified +data+ array and options
80
+ # and, optionally, a path. If +data+ is a string, it is assumed to be a
81
+ # expression defining a function, such as "sin(x)+1".
82
+ def add data = [], options = nil, path = nil
83
+ case data
84
+ when String
85
+ @data_stuff << [nil, [data, options].join(" "), nil]
86
+ else
87
+ @data_stuff << [data, options, path]
88
+ end
89
+ end
90
+
91
+ def dump data, path = nil
92
+ file =
93
+ if path
94
+ File.new(path, "w")
95
+ else
96
+ Tempfile.new("ruby_plot")
97
+ ## might be better (at least on windows) to send the data
98
+ ## inline, to avoid the "no file" errors when you try to
99
+ ## zoom or replot
100
+ end
101
+
102
+ @files << file
103
+ # This is here to prevent GC from collecting Tempfile object
104
+ # and thereby deleting the temp file from disk.
105
+
106
+ path = file.path
107
+
108
+ if (data.first.first.respond_to? :join rescue false)
109
+ data.each do |group|
110
+ group.each do |point|
111
+ file.puts point.join("\t")
112
+ end
113
+ file.puts
114
+ end
115
+
116
+ elsif (data.first.respond_to? :join rescue false) # assume one group
117
+ for d in data
118
+ if d.respond_to? :join
119
+ file.puts d.join("\t")
120
+ elsif d == nil
121
+ file.puts
122
+ else
123
+ file.puts d.inspect
124
+ end
125
+ end
126
+
127
+ elsif (data.first.respond_to? :each rescue false)
128
+ data.each do |group|
129
+ group.each do |point|
130
+ file.puts point.to_a.join("\t") # to_a in case of narray
131
+ end
132
+ file.puts
133
+ end
134
+
135
+ else # assume one group
136
+ for d in data
137
+ if d.respond_to? :join
138
+ file.puts d.join("\t")
139
+ elsif d == nil
140
+ file.puts
141
+ else
142
+ file.puts d.inspect
143
+ end
144
+ end
145
+ end
146
+
147
+ path
148
+
149
+ ensure
150
+ file.close unless file.closed?
151
+ end
152
+ end
153
+
154
+ class Gnuplot < GenericPlot
155
+ @@gnuplot_counter = 0
156
+
157
+ # Returns an array of the form [major, minor].
158
+ def Gnuplot.version(app)
159
+ @version ||= {}
160
+ @version[app] ||=
161
+ begin
162
+ v = `#{app} --version`.match(/gnuplot\s+(\d+)\.(\d+)/)
163
+ v && v[1..2].map {|s| s.to_i}
164
+ rescue
165
+ nil
166
+ end
167
+ end
168
+
169
+ def Gnuplot.version_at_least?(app, major_minor)
170
+ major, minor = Gnuplot.version(app)
171
+ major_needed, minor_needed = major_minor
172
+ (major == major_needed and minor >= minor_needed) or
173
+ (major > major_needed)
174
+ end
175
+
176
+ # Does the specified gnuplot executable support the 'Close' event?
177
+ def has_working_close_event
178
+ Gnuplot.version_at_least?(@app, [4, 3])
179
+ end
180
+
181
+ # Select the best term choice based on platform and gnuplot version.
182
+ def best_term
183
+ case RUBY_PLATFORM
184
+ when /mswin32|mingw32/
185
+ "win"
186
+ else
187
+ if Gnuplot.version_at_least?(@app, [4, 2])
188
+ "wxt"
189
+ else
190
+ "x11"
191
+ end
192
+ end
193
+ end
194
+
195
+ attr_reader :uniqname
196
+
197
+ def initialize(*)
198
+ super
199
+ @uniqname = next_uniqname
200
+ end
201
+
202
+ def next_uniqname
203
+ "Gnuplot_#{Process.pid}_#{@@gnuplot_counter+=1}"
204
+ end
205
+
206
+ def use3d
207
+ @plot_cmd = "splot"
208
+ end
209
+
210
+ def use2d
211
+ @plot_cmd = "plot"
212
+ end
213
+
214
+ def plot_cmd
215
+ @plot_cmd ||= "plot"
216
+ end
217
+
218
+ def term
219
+ @term ||= ENV["GNUTERM"] || best_term
220
+ end
221
+ attr_writer :term
222
+
223
+ attr_accessor :term_options
224
+
225
+ def set_window_title title
226
+ command "set term #{term} title '#{title}' #{term_options}"
227
+ end
228
+ alias window_title= set_window_title
229
+
230
+ attr_reader :is_to_file
231
+
232
+ def command str
233
+ case str
234
+ when /^\s*set\s+term\s+(\S+)/
235
+ self.term = $1
236
+ if /title\s+['"]?([^'"]*)/ =~ str
237
+ @uniqname = $1
238
+ end
239
+ when /^\s*set\s+output\s/
240
+ @is_to_file = true
241
+ end
242
+ super
243
+ end
244
+
245
+ def commit
246
+ args = @data_stuff.map do |data, options, path|
247
+ if data
248
+ "'#{dump data, path}' #{options}"
249
+ else
250
+ options
251
+ end
252
+ end
253
+
254
+ cmd_line = [plot_cmd, args.join(", ")].join(" ")
255
+
256
+ command "set mouse"
257
+ command cmd_line
258
+ end
259
+ end
260
+
261
+ # Module for creating "fire and forget" plot windows. Instead of a plot window
262
+ # that your main program keeps interacting with, these windows are created
263
+ # with one specific plot and left to be closed by the user. No cleanup is
264
+ # required.
265
+ #
266
+ # The functions in this module cam be accessed in two ways
267
+ #
268
+ # include Plot::PlotUtils
269
+ # gnuplot do ... end
270
+ #
271
+ # or
272
+ #
273
+ # Plot::PlotUtils.gnuplot do ... end
274
+ #
275
+ module PlotUtils
276
+ module_function
277
+
278
+ # Yields and returns the Plot instance. On unix/linux, the returned
279
+ # plot instance can no longer be used to send more plot commands, since
280
+ # it is just a copy of the real plot instance which is in a child process.
281
+ # On windows, be sure not to exit immediately after calling this method,
282
+ # or else tempfiles will be deleted before gnuplot has had a chance to read
283
+ # them.
284
+ def gnuplot(app=nil, &bl)
285
+ begin
286
+ gnuplot_fork(app, &bl)
287
+ rescue NotImplementedError => ex
288
+ raise unless ex.message =~ /fork/
289
+ gnuplot_no_fork(app, &bl)
290
+ end
291
+ end
292
+
293
+ def fork_returning_result # :nodoc:
294
+ read_result, write_result = IO.pipe
295
+ fork do
296
+ read_result.close
297
+ result_setter = proc do |r|
298
+ write_result.write Marshal.dump(r)
299
+ write_result.close
300
+ end
301
+ yield result_setter
302
+ end
303
+ write_result.close
304
+
305
+ begin
306
+ Marshal.load(read_result.read)
307
+ rescue => ex
308
+ ex ## ?
309
+ end
310
+ ensure
311
+ [write_result, read_result].each do |io|
312
+ io.close unless io.closed?
313
+ end
314
+ end
315
+
316
+ def gnuplot_fork(app=nil, &bl) # :nodoc:
317
+ Plot.new(app || 'gnuplot') do |plot|
318
+ unless bl
319
+ raise ArgumentError, "no block given"
320
+ end
321
+ bl[plot]
322
+
323
+ if plot.is_to_file
324
+ plot.commit
325
+ plot.show
326
+ plot.close
327
+ result = plot
328
+
329
+ else
330
+ result = fork_returning_result do |result_setter|
331
+ trap "INT" do exit end
332
+ trap "TERM" do exit end # clean up tempfiles
333
+
334
+ plot.command "set mouse"
335
+ # redundant, but in some gp versions must do this
336
+ # before the set term in the line below:
337
+ plot.set_window_title plot.uniqname
338
+ if plot.has_working_close_event
339
+ plot.command "bind allwindows Close 'exit gnuplot'"
340
+ end
341
+
342
+ plot.commit
343
+ result = plot.dup
344
+ result.clear_data
345
+ result.instance_eval {@files = nil; @pid = Process.pid}
346
+ result_setter.call result
347
+ plot.show
348
+
349
+ if plot.has_working_close_event
350
+ Process.wait # wait for gnuplot to exit
351
+ else
352
+ loop do
353
+ sleep 5
354
+ wmstate = `xprop -name #{plot.uniqname} WM_STATE 2>&1`
355
+ break if not $?.success?
356
+ break if wmstate[/window state:\s*withdrawn/i]
357
+ end
358
+ end
359
+ end
360
+ end
361
+
362
+ return result
363
+ end
364
+ end
365
+
366
+ def gnuplot_no_fork(app=nil, &bl) # :nodoc:
367
+ # assume windows
368
+ Plot.new("#{app || "pgnuplot.exe"} -persist") do |plot|
369
+ # -persist is nicer on windows: supports mouse/key interaction
370
+ bl[plot]
371
+ if plot.is_to_file
372
+ plot.commit
373
+ plot.show
374
+ plot.close
375
+
376
+ else
377
+ plot.command "bind allwindows Close 'exit gnuplot'"
378
+ plot.command "bind allwindows 'q' 'exit gnuplot'"
379
+ plot.commit
380
+ plot.show
381
+ end
382
+ end
383
+ end
384
+ end
385
+
386
+ end