chef-api 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 89bca373e08eb29df75521fe78473fb1a566e9af
4
- data.tar.gz: 4565d8fa17cfe68bbe4d204045a5f5d114ff5f92
3
+ metadata.gz: 80b62ceb2d3f3e1b7046d2115d6b741bc983499b
4
+ data.tar.gz: ade937778aea1f66776c212918e73565ed5f1bd1
5
5
  SHA512:
6
- metadata.gz: ee5206c83877648adfda095c52d688b46fd1e4a46b7745c8e9cfdf3db8e6ed4f014d353fb91e6dc38549671ff5acaa6632ee6da1edabc0b9aba9dfd77db3c0f1
7
- data.tar.gz: 46561de5a2908f9cd2dbe6781de8c67b9b625153aad5a964b532337ea775efef99408176913e9dd6d31d7440ea7c36a74bef53b725fce019f98b412ba8924078
6
+ metadata.gz: ebb178741c859052b3762e52ec0d9da19b6b9bb9df52099319e028813e03b776e1ca8401dc9506bcd65a3be8a683e631c0acc89da3d1090a98a0e549e9d16b87
7
+ data.tar.gz: bd0cb8413f6add6ddac6c2f60d38a68b87d63e905a20eb2e92b36a77b7afd61d204de15242be4287e8e3d9717c38034b63fc664eae086f7823c9444281552422
data/CHANGELOG.md CHANGED
@@ -1,6 +1,9 @@
1
1
  ChefAPI Changelog
2
2
  =================
3
3
 
4
+ v0.4.0 (2014-07-05)
5
+ -------------------
6
+ - Support multipart POST
4
7
 
5
8
  v0.3.0 (2014-06-18)
6
9
  -------------------
data/chef-api.gemspec CHANGED
@@ -22,4 +22,5 @@ Gem::Specification.new do |spec|
22
22
 
23
23
  spec.add_dependency 'logify', '~> 0.1'
24
24
  spec.add_dependency 'mixlib-authentication', '~> 1.3'
25
+ spec.add_dependency 'mime-types', '~> 2.3'
25
26
  end
@@ -204,7 +204,16 @@ module ChefAPI
204
204
  if data.respond_to?(:read)
205
205
  request.body_stream = data
206
206
  elsif data.is_a?(Hash)
207
- request.form_data = data
207
+ # If any of the values in the hash are File-like, assume this is a
208
+ # multi-part post
209
+ if data.values.any? { |value| value.respond_to?(:read) }
210
+ multipart = Multipart::Body.new(data)
211
+ request.content_length = multipart.content_length
212
+ request.content_type = multipart.content_type
213
+ request.body_stream = multipart.stream
214
+ else
215
+ request.form_data = data
216
+ end
208
217
  else
209
218
  request.body = data
210
219
  end
@@ -503,4 +512,165 @@ module ChefAPI
503
512
  end
504
513
  end
505
514
  end
515
+
516
+ require 'cgi'
517
+ require 'mime/types'
518
+
519
+ module Multipart
520
+ BOUNDARY = '------ChefAPIMultipartBoundary'.freeze
521
+
522
+ class Body
523
+ def initialize(params = {})
524
+ params.each do |key, value|
525
+ if value.respond_to?(:read)
526
+ parts << FilePart.new(key, value)
527
+ else
528
+ parts << ParamPart.new(key, value)
529
+ end
530
+ end
531
+
532
+ parts << EndingPart.new
533
+ end
534
+
535
+ def stream
536
+ MultiIO.new(*parts.map(&:io))
537
+ end
538
+
539
+ def content_type
540
+ "multipart/form-data; boundary=#{BOUNDARY}"
541
+ end
542
+
543
+ def content_length
544
+ parts.map(&:size).inject(:+)
545
+ end
546
+
547
+ private
548
+
549
+ def parts
550
+ @parts ||= []
551
+ end
552
+ end
553
+
554
+ class MultiIO
555
+ def initialize(*ios)
556
+ @ios = ios
557
+ @index = 0
558
+ end
559
+
560
+ # Read from IOs in order until `length` bytes have been received.
561
+ def read(length = nil, outbuf = nil)
562
+ got_result = false
563
+ outbuf = outbuf ? outbuf.replace('') : ''
564
+
565
+ while io = current_io
566
+ if result = io.read(length)
567
+ got_result ||= !result.nil?
568
+ result.force_encoding('BINARY') if result.respond_to?(:force_encoding)
569
+ outbuf << result
570
+ length -= result.length if length
571
+ break if length == 0
572
+ end
573
+ advance_io
574
+ end
575
+
576
+ (!got_result && length) ? nil : outbuf
577
+ end
578
+
579
+ def rewind
580
+ @ios.each { |io| io.rewind }
581
+ @index = 0
582
+ end
583
+
584
+ private
585
+
586
+ def current_io
587
+ @ios[@index]
588
+ end
589
+
590
+ def advance_io
591
+ @index += 1
592
+ end
593
+ end
594
+
595
+ #
596
+ # A generic key => value part.
597
+ #
598
+ class ParamPart
599
+ def initialize(name, value)
600
+ @part = build(name, value)
601
+ end
602
+
603
+ def io
604
+ @io ||= StringIO.new(@part)
605
+ end
606
+
607
+ def size
608
+ @part.bytesize
609
+ end
610
+
611
+ private
612
+
613
+ def build(name, value)
614
+ part = %|--#{BOUNDARY}\r\n|
615
+ part << %|Content-Disposition: form-data; name="#{CGI.escape(name)}"\r\n\r\n|
616
+ part << %|#{value}\r\n|
617
+ part
618
+ end
619
+ end
620
+
621
+ #
622
+ # A File part
623
+ #
624
+ class FilePart
625
+ def initialize(name, file)
626
+ @file = file
627
+ @head = build(name, file)
628
+ @foot = "\r\n"
629
+ end
630
+
631
+ def io
632
+ @io ||= MultiIO.new(
633
+ StringIO.new(@head),
634
+ @file,
635
+ StringIO.new(@foot)
636
+ )
637
+ end
638
+
639
+ def size
640
+ @head.bytesize + @file.size + @foot.bytesize
641
+ end
642
+
643
+ private
644
+
645
+ def build(name, file)
646
+ filename = File.basename(file.path)
647
+ mime_type = MIME::Types.type_for(filename)[0] || MIME::Types['application/octet-stream'][0]
648
+
649
+ part = %|--#{BOUNDARY}\r\n|
650
+ part << %|Content-Disposition: form-data; name="#{CGI.escape(name)}"; filename="#{filename}"\r\n|
651
+ part << %|Content-Length: #{file.size}\r\n|
652
+ part << %|Content-Type: #{mime_type.simplified}|
653
+ part << %|Content-Transfer-Encoding: binary\r\n|
654
+ part << %|\r\n|
655
+ part
656
+ end
657
+ end
658
+
659
+ #
660
+ # The end of the entire request
661
+ #
662
+ class EndingPart
663
+ def initialize
664
+ @part = "--#{BOUNDARY}--\r\n\r\n"
665
+ end
666
+
667
+ def io
668
+ @io ||= StringIO.new(@part)
669
+ end
670
+
671
+ def size
672
+ @part.bytesize
673
+ end
674
+ end
675
+ end
506
676
  end
@@ -1,3 +1,3 @@
1
1
  module ChefAPI
2
- VERSION = '0.3.0'
2
+ VERSION = '0.4.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chef-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Seth Vargo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-18 00:00:00.000000000 Z
11
+ date: 2014-07-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: logify
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mime-types
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.3'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.3'
41
55
  description: A tiny Chef API client with minimal dependencies
42
56
  email:
43
57
  - sethvargo@gmail.com
@@ -138,7 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
152
  version: '0'
139
153
  requirements: []
140
154
  rubyforge_project:
141
- rubygems_version: 2.2.2
155
+ rubygems_version: 2.3.0
142
156
  signing_key:
143
157
  specification_version: 4
144
158
  summary: A Chef API client in Ruby