sparklines 0.4.0 → 0.4.1

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/CHANGELOG CHANGED
@@ -1,4 +1,11 @@
1
1
 
2
+ 0.4.1
3
+
4
+ * Converted to Hoe for rakefile
5
+ * Added whisker graph [Luke Francl]
6
+ * General cleanup and bug fixes
7
+ * Experimental label option
8
+
2
9
  0.3.0
3
10
 
4
11
  * Changed to a Class for maintainability
data/Manifest.txt ADDED
@@ -0,0 +1,7 @@
1
+ Rakefile
2
+ Manifest.txt
3
+ README
4
+ CHANGELOG
5
+ lib/sparklines.rb
6
+ test/test_all.rb
7
+ test/output
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+
2
+ require 'rubygems'
3
+ require 'hoe'
4
+ $:.unshift(File.dirname(__FILE__) + "/lib")
5
+ require 'sparklines'
6
+
7
+ Hoe.new('Sparklines', Sparklines::VERSION) do |p|
8
+ p.name = "sparklines"
9
+ p.author = "Geoffrey Grosenbach"
10
+ p.description = "Tiny graphs."
11
+ p.email = 'boss@topfunky.com'
12
+ p.summary = "Tiny graphs."
13
+ p.url = "http://nubyonrails.com/pages/sparklines"
14
+ p.clean_globs = ['test/output/*']
15
+
16
+ # * extra_deps - An array of rubygem dependencies.
17
+ end
data/lib/sparklines.rb CHANGED
@@ -1,7 +1,6 @@
1
1
 
2
2
  require 'rubygems'
3
3
  require 'RMagick'
4
- require 'mathn'
5
4
 
6
5
  =begin rdoc
7
6
 
@@ -56,6 +55,7 @@ Graph types:
56
55
  pie
57
56
  smooth
58
57
  bar
58
+ whisker
59
59
 
60
60
  General Defaults:
61
61
 
@@ -74,7 +74,7 @@ Licensed under the MIT license.
74
74
  =end
75
75
  class Sparklines
76
76
 
77
- VERSION = '0.4.0'
77
+ VERSION = '0.4.1'
78
78
 
79
79
  @@label_margin = 5.0
80
80
  @@pointsize = 10.0
@@ -109,7 +109,7 @@ class Sparklines
109
109
  :label => nil
110
110
  }
111
111
 
112
- # Hack for HashWithIndifferentAccess
112
+ # HACK for HashWithIndifferentAccess
113
113
  options_sym = Hash.new
114
114
  options.keys.each do |key|
115
115
  options_sym[key.to_sym] = options[key]
@@ -119,7 +119,7 @@ class Sparklines
119
119
 
120
120
  # Call the appropriate method for actual plotting.
121
121
  sparkline = self.new(data, options_sym)
122
- if %w(area bar pie smooth discrete).include? options_sym[:type]
122
+ if %w(area bar pie smooth discrete whisker).include? options_sym[:type]
123
123
  sparkline.send options_sym[:type]
124
124
  else
125
125
  sparkline.plot_error options_sym
@@ -133,7 +133,7 @@ class Sparklines
133
133
  end
134
134
  end
135
135
 
136
- end
136
+ end # class methods
137
137
 
138
138
  def initialize(data=[], options={})
139
139
  @data = Array(data)
@@ -141,6 +141,7 @@ class Sparklines
141
141
  normalize_data
142
142
  end
143
143
 
144
+ ##
144
145
  # Creates a continuous area sparkline. Relevant options.
145
146
  #
146
147
  # :step - An integer that determines the distance between each point on the sparkline. Defaults to 2.
@@ -164,6 +165,7 @@ class Sparklines
164
165
  # :above_color - A string or color code representing the color to draw values above or equal the upper value. Defaults to red.
165
166
  #
166
167
  # :below_color - A string or color code representing the color to draw values below the upper value. Defaults to gray.
168
+
167
169
  def area
168
170
 
169
171
  step = @options[:step].to_i
@@ -231,9 +233,9 @@ class Sparklines
231
233
  @canvas.to_blob
232
234
  end
233
235
 
236
+ ##
237
+ # A bar graph.
234
238
 
235
- # Draws a bar graph.
236
- #
237
239
  def bar
238
240
  step = @options[:step].to_i
239
241
  height = @options[:height].to_f
@@ -260,6 +262,7 @@ class Sparklines
260
262
  end
261
263
 
262
264
 
265
+ ##
263
266
  # Creates a discretized sparkline
264
267
  #
265
268
  # :height - An integer that determines what the height of the sparkline will be. Defaults to 14
@@ -269,6 +272,7 @@ class Sparklines
269
272
  # :above_color - A string or color code representing the color to draw values above or equal the upper value. Defaults to red.
270
273
  #
271
274
  # :below_color - A string or color code representing the color to draw values below the upper value. Defaults to gray.
275
+
272
276
  def discrete
273
277
 
274
278
  height = @options[:height].to_i
@@ -295,6 +299,7 @@ class Sparklines
295
299
  end
296
300
 
297
301
 
302
+ ##
298
303
  # Creates a pie-chart sparkline
299
304
  #
300
305
  # :diameter - An integer that determines what the size of the sparkline will be. Defaults to 20
@@ -302,8 +307,8 @@ class Sparklines
302
307
  # :share_color - A string or color code representing the color to draw the share of the pie represented by percent. Defaults to red.
303
308
  #
304
309
  # :remain_color - A string or color code representing the color to draw the pie not taken by the share color. Defaults to lightgrey.
305
- def pie
306
310
 
311
+ def pie
307
312
  diameter = @options[:diameter].to_i
308
313
  background_color = @options[:background_color]
309
314
 
@@ -362,6 +367,7 @@ class Sparklines
362
367
  end
363
368
 
364
369
 
370
+ ##
365
371
  # Creates a smooth sparkline.
366
372
  #
367
373
  # :step - An integer that determines the distance between each point on the sparkline. Defaults to 2.
@@ -379,6 +385,7 @@ class Sparklines
379
385
  # :max_color - A string or color code representing the color that the dot drawn at the largest value will be displayed as. Defaults to green.
380
386
  #
381
387
  # :last_color - A string or color code representing the color that the dot drawn at the last value will be displayed as. Defaults to red.
388
+
382
389
  def smooth
383
390
 
384
391
  step = @options[:step].to_i
@@ -416,7 +423,65 @@ class Sparklines
416
423
  end
417
424
 
418
425
 
426
+ ##
427
+ # Creates a whisker sparkline to track on/off type data. There are five states:
428
+ # on, off, no value, exceptional on, exceptional off. On values create an up
429
+ # whisker and off values create a down whisker. Exceptional values may be
430
+ # colored differently than regular values to indicate, for example, a shut out.
431
+ # No value produces an empty row to indicate a tie.
432
+ #
433
+ # * results - an array of integer values between -2 and 2. -2 is exceptional
434
+ # down, 1 is regular down, 0 is no value, 1 is up, and 2 is exceptional up.
435
+ # * options - a hash that takes parameters
436
+ #
437
+ # :height - height of the sparkline
438
+ #
439
+ # :whisker_color - the color of regular whiskers; defaults to black
440
+ #
441
+ # :exception_color - the color of exceptional whiskers; defaults to red
442
+
443
+ def whisker
444
+
445
+ # step = @options[:step].to_i
446
+ height = @options[:height].to_i
447
+ background_color = @options[:background_color]
448
+
449
+ create_canvas((@data.size - 1) * 2, height, background_color)
450
+
451
+ whisker_color = @options[:whisker_color] || 'black'
452
+ exception_color = @options[:exception_color] || 'red'
453
+
454
+ i = 0
455
+ @data.each do |r|
456
+ color = whisker_color
457
+
458
+ if ( (r == 2 || r == -2) && exception_color )
459
+ color = exception_color
460
+ end
461
+
462
+ y_mid_point = (r >= 1) ? (@canvas.rows/2.0 - 1).ceil : (@canvas.rows/2.0).floor
463
+
464
+ y_end_point = y_mid_point
465
+ if ( r > 0)
466
+ y_end_point = 0
467
+ end
468
+
469
+ if ( r < 0 )
470
+ y_end_point = @canvas.rows
471
+ end
472
+
473
+ @draw.stroke( color )
474
+ @draw.line( i, y_mid_point, i, y_end_point )
475
+ i += 2
476
+ end
477
+
478
+ @draw.draw(@canvas)
479
+ @canvas.to_blob
480
+ end
481
+
482
+ ##
419
483
  # Draw the error Sparkline.
484
+
420
485
  def plot_error(options={})
421
486
  create_canvas(40, 15, 'white')
422
487
 
@@ -439,23 +504,27 @@ private
439
504
  end
440
505
  end
441
506
 
442
- # * arr - an array of points (represented as two element arrays)
507
+ ##
508
+ # * :arr - an array of points (represented as two element arrays)
509
+
443
510
  def open_ended_polyline(arr)
444
511
  0.upto(arr.length - 2) { |i|
445
512
  @draw.line(arr[i][0], arr[i][1], arr[i+1][0], arr[i+1][1])
446
513
  }
447
514
  end
448
515
 
516
+ ##
449
517
  # Create an image to draw on and a drawable to do the drawing with.
450
518
  #
451
- # TODO Refactor into smaller functions
519
+ # TODO Refactor into smaller methods
520
+
452
521
  def create_canvas(w, h, bkg_col)
453
522
  @draw = Magick::Draw.new
454
523
  @draw.pointsize = @@pointsize # TODO Use height
455
524
  @canvas = Magick::Image.new(w , h) { self.background_color = bkg_col }
456
-
525
+
457
526
  # Make room for label and last value
458
- if !@options[:label].nil?
527
+ unless @options[:label].nil?
459
528
  @options[:has_last] = true
460
529
  @label_width = calculate_width(@options[:label])
461
530
  @data_last_width = calculate_width(@data.last)
@@ -463,19 +532,19 @@ private
463
532
  @label_and_data_last_width = @label_width + @data_last_width + @@label_margin * 7.0
464
533
  w += @label_and_data_last_width
465
534
  end
466
-
535
+
467
536
  @canvas = Magick::Image.new(w , h) { self.background_color = bkg_col }
468
537
  @canvas.format = "PNG"
469
-
538
+
470
539
  # Draw label and last value
471
- if !@options[:label].nil?
540
+ unless @options[:label].nil?
472
541
  if ENV.has_key?('MAGICK_FONT_PATH')
473
542
  vera_font_path = File.expand_path('Vera.ttf', ENV['MAGICK_FONT_PATH'])
474
543
  @font = File.exists?(vera_font_path) ? vera_font_path : nil
475
544
  else
476
545
  @font = nil
477
546
  end
478
-
547
+
479
548
  @draw.fill = 'black'
480
549
  @draw.font = @font if @font
481
550
  @draw.gravity = Magick::WestGravity
@@ -483,7 +552,7 @@ private
483
552
  @label_width, 1.0,
484
553
  w - @label_and_data_last_width + @@label_margin, h - calculate_caps_height/2.0,
485
554
  @options[:label])
486
-
555
+
487
556
  @draw.fill = 'red'
488
557
  @draw.annotate( @canvas,
489
558
  @data_last_width, 1.0,
@@ -491,9 +560,11 @@ private
491
560
  @data.last.to_s)
492
561
  end
493
562
  end
494
-
563
+
564
+ ##
495
565
  # Utility to draw a coloured box
496
566
  # Centred on pt, offset off in each direction, fill color is col
567
+
497
568
  def drawbox(pt, offset, color)
498
569
  @draw.stroke 'transparent'
499
570
  @draw.fill(color)
@@ -508,5 +579,4 @@ private
508
579
  @draw.get_type_metrics(@canvas, 'X').height
509
580
  end
510
581
 
511
-
512
582
  end
@@ -21,10 +21,29 @@ class SparklinesTest < Test::Unit::TestCase
21
21
 
22
22
  def test_each_graph_with_label
23
23
  %w{pie area discrete smooth bar}.each do |type|
24
- quick_graph("labeled_#{type}", :type => type, :label => 'Comments for you')
24
+ quick_graph("labeled_#{type}", :type => type, :label => 'Glucose')
25
25
  end
26
26
  end
27
27
 
28
+ def test_whisker_random
29
+ # Need data ranging from -2 to +2
30
+ @data = (1..40).map { |i| rand(3) * (rand(2) == 1 ? -1 : 1) }
31
+ quick_graph("whisker", :type => 'whisker')
32
+ end
33
+
34
+ def test_whisker_non_exceptional
35
+ @data = [1,1,1,1,1,1,1,1,1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1]
36
+ quick_graph("whisker_non_exceptional", :type => 'whisker')
37
+ end
38
+
39
+ ##
40
+ # Send random values in the range (-9..9)
41
+
42
+ def test_whisker_junk
43
+ @data = (1..40).map { |i| rand(10) * (rand(2) == 1 ? -1 : 1) }
44
+ quick_graph("whisker_junk", :type => 'whisker')
45
+ end
46
+
28
47
  def test_pie
29
48
  # Test extremes which previously did not work right
30
49
  [0, 1, 45, 95, 99, 100].each do |value|
metadata CHANGED
@@ -1,18 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.11
2
+ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: sparklines
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.4.0
7
- date: 2006-07-31 00:00:00 -07:00
8
- summary: Tiny graphs for concise data.
6
+ version: 0.4.1
7
+ date: 2006-10-20 00:00:00 -07:00
8
+ summary: Tiny graphs.
9
9
  require_paths:
10
10
  - lib
11
+ - test
11
12
  email: boss@topfunky.com
12
- homepage: http://www.topfunky.com
13
+ homepage: http://nubyonrails.com/pages/sparklines
13
14
  rubyforge_project: sparklines
14
- description: Make tiny graphs for use on websites or documents.
15
- autorequire: gruff
15
+ description: Tiny graphs.
16
+ autorequire:
16
17
  default_executable:
17
18
  bindir: bin
18
19
  has_rdoc: true
@@ -25,18 +26,19 @@ required_ruby_version: !ruby/object:Gem::Version::Requirement
25
26
  platform: ruby
26
27
  signing_key:
27
28
  cert_chain:
29
+ post_install_message:
28
30
  authors:
29
31
  - Geoffrey Grosenbach
30
32
  files:
31
- - rakefile
33
+ - Rakefile
34
+ - Manifest.txt
32
35
  - README
33
36
  - CHANGELOG
34
- - MIT-LICENSE
35
37
  - lib/sparklines.rb
36
- - test/all_test.rb
38
+ - test/test_all.rb
37
39
  - test/output
38
- test_files: []
39
-
40
+ test_files:
41
+ - test/test_all.rb
40
42
  rdoc_options: []
41
43
 
42
44
  extra_rdoc_files: []
@@ -45,7 +47,15 @@ executables: []
45
47
 
46
48
  extensions: []
47
49
 
48
- requirements:
49
- - none
50
- dependencies: []
50
+ requirements: []
51
51
 
52
+ dependencies:
53
+ - !ruby/object:Gem::Dependency
54
+ name: hoe
55
+ version_requirement:
56
+ version_requirements: !ruby/object:Gem::Version::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 1.1.1
61
+ version:
data/MIT-LICENSE DELETED
@@ -1,21 +0,0 @@
1
- Copyright (c) 2005 Geoffrey Grosenbach boss@topfunky.com
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
-
data/rakefile DELETED
@@ -1,213 +0,0 @@
1
- require 'rubygems'
2
- require 'rake'
3
- require 'rake/testtask'
4
- require 'rake/rdoctask'
5
- require 'rake/packagetask'
6
- require 'rake/gempackagetask'
7
- require 'rake/contrib/rubyforgepublisher'
8
-
9
- $:.unshift(File.dirname(__FILE__) + "/lib")
10
- require 'sparklines'
11
-
12
- PKG_NAME = 'sparklines'
13
- PKG_VERSION = Sparklines::VERSION
14
- PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
15
-
16
- RELEASE_NAME = "REL #{PKG_VERSION}"
17
-
18
- RUBY_FORGE_PROJECT = "sparklines"
19
- RUBY_FORGE_USER = "topfunky"
20
-
21
- desc "Default Task"
22
- task :default => [ :clean, :test ]
23
-
24
- desc "Clean images generated by tests"
25
- task :clean do
26
- rm FileList['test/output/*.png']
27
- rm_rf "pkg"
28
- end
29
-
30
- # Build a graphic file by running a single test.
31
- #
32
- # rake bar_extreme_values.png
33
- # => Runs test_extreme_values
34
-
35
- rule ".png" do |t|
36
- test_name = t.name.gsub(/\.png/, '')
37
- Rake::Task[:clean].invoke
38
- sh "ruby -Ilib:test test/all_test.rb -n /^test_#{test_name}/"
39
- end
40
-
41
- # Run the unit tests
42
- Rake::TestTask.new { |t|
43
- t.libs << "test"
44
- t.pattern = 'test/*_test.rb'
45
- t.verbose = true
46
- }
47
-
48
-
49
- # Genereate the RDoc documentation
50
- Rake::RDocTask.new { |rdoc|
51
- rdoc.rdoc_dir = 'doc'
52
- rdoc.title = "Sparklines -- Tiny graphs"
53
- rdoc.options << '--line-numbers --inline-source --main README --accessor adv_attr_accessor=M'
54
- rdoc.template = "#{ENV['template']}.rb" if ENV['template']
55
- rdoc.rdoc_files.include('README', 'CHANGELOG')
56
- rdoc.rdoc_files.include('lib/sparklines.rb')
57
- rdoc.rdoc_files.include('lib/sparklines/*.rb')
58
- }
59
-
60
-
61
- # Create compressed packages
62
- spec = Gem::Specification.new do |s|
63
- s.platform = Gem::Platform::RUBY
64
- s.name = PKG_NAME
65
- s.summary = "Tiny graphs for concise data."
66
- s.description = %q{Make tiny graphs for use on websites or documents.}
67
- s.version = PKG_VERSION
68
-
69
- s.author = "Geoffrey Grosenbach"
70
- s.email = "boss@topfunky.com"
71
- s.rubyforge_project = RUBY_FORGE_PROJECT
72
- s.homepage = "http://www.topfunky.com"
73
-
74
- s.has_rdoc = true
75
- s.requirements << 'none'
76
- s.require_path = 'lib'
77
- s.autorequire = 'gruff'
78
-
79
- s.files = [ "rakefile", "README", "CHANGELOG", "MIT-LICENSE" ]
80
- s.files = s.files + Dir.glob( "lib/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
81
- s.files = s.files + Dir.glob( "test/**/*" ).delete_if { |item| item.include?( "\.svn" ) || item.include?("\.png") }
82
- end
83
-
84
- Rake::GemPackageTask.new(spec) do |p|
85
- p.gem_spec = spec
86
- p.need_tar = true
87
- p.need_zip = true
88
- end
89
-
90
- desc "Hackish copy to sparklines plugin for Rails"
91
- task :update_plugin do
92
- cp "lib/sparklines.rb", "../plugins/sparklines/lib/sparklines.rb"
93
- end
94
-
95
- desc "Publish the API documentation"
96
- task :pgem => [:package] do
97
- Rake::SshFilePublisher.new("boss@topfunky.com", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
98
- end
99
-
100
- desc "Publish the release files to RubyForge."
101
- task :release => [:package] do
102
- files = ["gem", "tgz", "zip"].map { |ext| "pkg/#{PKG_FILE_NAME}.#{ext}" }
103
-
104
- if RUBY_FORGE_PROJECT then
105
- require 'net/http'
106
- require 'open-uri'
107
-
108
- project_uri = "http://rubyforge.org/projects/#{RUBY_FORGE_PROJECT}/"
109
- project_data = open(project_uri) { |data| data.read }
110
- group_id = project_data[/[?&]group_id=(\d+)/, 1]
111
- raise "Couldn't get group id" unless group_id
112
-
113
- # This echos password to shell which is a bit sucky
114
- if ENV["RUBY_FORGE_PASSWORD"]
115
- password = ENV["RUBY_FORGE_PASSWORD"]
116
- else
117
- print "#{RUBY_FORGE_USER}@rubyforge.org's password: "
118
- password = STDIN.gets.chomp
119
- end
120
-
121
- login_response = Net::HTTP.start("rubyforge.org", 80) do |http|
122
- data = [
123
- "login=1",
124
- "form_loginname=#{RUBY_FORGE_USER}",
125
- "form_pw=#{password}"
126
- ].join("&")
127
- http.post("/account/login.php", data)
128
- end
129
-
130
- cookie = login_response["set-cookie"]
131
- raise "Login failed" unless cookie
132
- headers = { "Cookie" => cookie }
133
-
134
- release_uri = "http://rubyforge.org/frs/admin/?group_id=#{group_id}"
135
- release_data = open(release_uri, headers) { |data| data.read }
136
- package_id = release_data[/[?&]package_id=(\d+)/, 1]
137
- raise "Couldn't get package id" unless package_id
138
-
139
- first_file = true
140
- release_id = ""
141
-
142
- files.each do |filename|
143
- basename = File.basename(filename)
144
- file_ext = File.extname(filename)
145
- file_data = File.open(filename, "rb") { |file| file.read }
146
-
147
- puts "Releasing #{basename}..."
148
-
149
- release_response = Net::HTTP.start("rubyforge.org", 80) do |http|
150
- release_date = Time.now.strftime("%Y-%m-%d %H:%M")
151
- type_map = {
152
- ".zip" => "3000",
153
- ".tgz" => "3110",
154
- ".gz" => "3110",
155
- ".gem" => "1400"
156
- }; type_map.default = "9999"
157
- type = type_map[file_ext]
158
- boundary = "rubyqMY6QN9bp6e4kS21H4y0zxcvoor"
159
-
160
- query_hash = if first_file then
161
- {
162
- "group_id" => group_id,
163
- "package_id" => package_id,
164
- "release_name" => RELEASE_NAME,
165
- "release_date" => release_date,
166
- "type_id" => type,
167
- "processor_id" => "8000", # Any
168
- "release_notes" => "",
169
- "release_changes" => "",
170
- "preformatted" => "1",
171
- "submit" => "1"
172
- }
173
- else
174
- {
175
- "group_id" => group_id,
176
- "release_id" => release_id,
177
- "package_id" => package_id,
178
- "step2" => "1",
179
- "type_id" => type,
180
- "processor_id" => "8000", # Any
181
- "submit" => "Add This File"
182
- }
183
- end
184
-
185
- query = "?" + query_hash.map do |(name, value)|
186
- [name, URI.encode(value)].join("=")
187
- end.join("&")
188
-
189
- data = [
190
- "--" + boundary,
191
- "Content-Disposition: form-data; name=\"userfile\"; filename=\"#{basename}\"",
192
- "Content-Type: application/octet-stream",
193
- "Content-Transfer-Encoding: binary",
194
- "", file_data, ""
195
- ].join("\x0D\x0A")
196
-
197
- release_headers = headers.merge(
198
- "Content-Type" => "multipart/form-data; boundary=#{boundary}"
199
- )
200
-
201
- target = first_file ? "/frs/admin/qrs.php" : "/frs/admin/editrelease.php"
202
- http.post(target + query, data, release_headers)
203
- end
204
-
205
- if first_file then
206
- release_id = release_response.body[/release_id=(\d+)/, 1]
207
- raise("Couldn't get release id") unless release_id
208
- end
209
-
210
- first_file = false
211
- end
212
- end
213
- end