sparklines 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
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