ethon 0.8.1 → 0.9.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7a8e3e2daabb0f28d13cfec209027f4a3dfb588f
4
- data.tar.gz: b5dc230d462c0972fa15bb811906e70ca4b6d8a7
3
+ metadata.gz: 6fabe1040444c048c297b35b8785962b405cf22b
4
+ data.tar.gz: 024c7e7ed7c3ef61dbbae1375b25626e1e5bbdf4
5
5
  SHA512:
6
- metadata.gz: ba31326c9c9dd60a8635a32edde9b4936307e602097b2ca696333410aea60746fbda37b7c2d179ac2be01a46ccee66debfdd014deb073f87ffeb1fdeae00540b
7
- data.tar.gz: 9f2b01584a1f175f9032febbd0fbc1b80a56dcda094a2825746e6e7ccca8254f07fc3ae7bf5c5249e7b75f52914a9229201dfb1a0e26fb17af457a6cfae5efe9
6
+ metadata.gz: 9e5e32e36f44d8f909d5d90e5a337959ced86643e16792e346fac504e6c53e34b548fae11a5f8334ac36b9594cbdeacc6c9fa2014a6ed73ed3e57a89a19e2801
7
+ data.tar.gz: f350cc872574938c24a4080dde53bd6fc97dbc78b30399bac1a9b5e68702f64920bc722ed53fa77dca41a4a3298b53955e151e1d19826be0d7a9a75c1733a316
@@ -1,19 +1,26 @@
1
- script: "bundle exec rake"
1
+ language: ruby
2
+ cache: bundler
2
3
  sudo: false
4
+
3
5
  bundler_args: --without perf
6
+ script: bundle exec rake
7
+
4
8
  rvm:
5
9
  - 1.8.7
6
10
  - 1.9.2
7
11
  - 1.9.3
8
- - 2.0.0
12
+ - 2.0.0-p648
13
+ - 2.1.8
14
+ - 2.2.4
15
+ - 2.3.0
9
16
  - ruby-head
10
17
  - ree
11
- - jruby-head
12
18
  - jruby-18mode
13
19
  - jruby-19mode
20
+ - jruby-head
21
+ - rbx-2
22
+
14
23
  matrix:
15
24
  allow_failures:
25
+ - rvm: ree
16
26
  - rvm: ruby-head
17
- - rvm: jruby-head
18
- - rvm: rbx-18mode
19
- - rvm: rbx-19mode
@@ -2,10 +2,16 @@
2
2
 
3
3
  ## Master
4
4
 
5
- [Full Changelog](https://github.com/typhoeus/ethon/compare/v0.8.1...master)
5
+ [Full Changelog](https://github.com/typhoeus/ethon/compare/v0.9.0...master)
6
+
7
+ ## 0.9.0
8
+
9
+ [Full Changelog](https://github.com/typhoeus/ethon/compare/v0.8.1...v0.9.0)
6
10
 
7
11
  ## 0.8.1
8
12
 
13
+ [Full Changelog](https://github.com/typhoeus/ethon/compare/v0.8.0...v0.8.1)
14
+
9
15
  * Support optional escaping of params.
10
16
  ([Tasos Laskos](https://github.com/zapotek)
11
17
  * `Easy::Mirror`: Reduced object allocations and method calls during info handling.
@@ -13,6 +19,11 @@
13
19
 
14
20
  ## 0.8.0
15
21
 
22
+ [Full Changelog](https://github.com/typhoeus/ethon/compare/v0.7.3...v0.7.4)
23
+
24
+ * `Easy::Mirror`: Reduced object allocations and method calls during info handling.
25
+ ([Tasos Laskos](https://github.com/zapotek)
26
+
16
27
  ## 0.7.4
17
28
 
18
29
  [Full Changelog](https://github.com/typhoeus/ethon/compare/v0.7.3...v0.7.4)
data/Gemfile CHANGED
@@ -1,22 +1,27 @@
1
1
  source "https://rubygems.org"
2
2
  gemspec
3
3
 
4
- gem "rake"
4
+ if Gem.ruby_version < Gem::Version.new("1.9.3")
5
+ gem "rake", "< 11"
6
+ else
7
+ gem "rake"
8
+ end
5
9
 
6
10
  group :development, :test do
7
- gem "rspec", "~> 2.11"
11
+ gem "rspec", "~> 3.4"
8
12
 
9
- gem "sinatra", :git => "https://github.com/sinatra/sinatra.git"
13
+ gem "sinatra"
10
14
  gem "json"
11
15
  gem "mime-types", "~> 1.18"
12
16
 
13
17
  unless ENV["CI"]
14
18
  gem "guard-rspec", "~> 0.7"
15
- gem 'rb-fsevent', '~> 0.9.1'
19
+ gem "rb-fsevent", "~> 0.9.1"
16
20
  end
17
21
  end
18
22
 
19
23
  group :perf do
20
- gem "patron", "~> 0.4"
21
- gem "curb", "~> 0.8.0"
24
+ gem "benchmark-ips"
25
+ gem "patron"
26
+ gem "curb"
22
27
  end
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-2014 Hans Hasselberg
1
+ Copyright (c) 2012-2016 Hans Hasselberg
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/README.md CHANGED
@@ -71,7 +71,7 @@ everything up correctly.
71
71
 
72
72
  (The MIT License)
73
73
 
74
- Copyright © 2012-2014 [Hans Hasselberg](http://www.hans.io)
74
+ Copyright © 2012-2016 [Hans Hasselberg](http://www.hans.io)
75
75
 
76
76
  Permission is hereby granted, free of charge, to any person obtaining a
77
77
  copy of this software and associated documentation files (the "Software"),
@@ -25,6 +25,7 @@ module Ethon
25
25
  base.attach_function :easy_strerror, :curl_easy_strerror, [:easy_code], :string
26
26
  base.attach_function :easy_escape, :curl_easy_escape, [:pointer, :pointer, :int], :pointer
27
27
  base.attach_function :easy_reset, :curl_easy_reset, [:pointer], :void
28
+ base.attach_function :easy_duphandle, :curl_easy_duphandle, [:pointer], :pointer
28
29
 
29
30
  base.attach_function :formadd, :curl_formadd, [:pointer, :pointer, :varargs], :int
30
31
  base.attach_function :formfree, :curl_formfree, [:pointer], :void
@@ -5,10 +5,15 @@ module Ethon
5
5
  # easy or multi interface.
6
6
  module Options
7
7
 
8
+ OPTION_STRINGS = { :easy => 'easy_options', :multi => 'multi_options' }.freeze
9
+ FOPTION_STRINGS = { :easy => 'EASY_OPTIONS', :multi => 'MULTI_OPTIONS' }.freeze
10
+ FTYPES = [:long, :string, :ffipointer, :callback, :debug_callback, :off_t]
11
+ FUNCS = Hash[*[:easy, :multi].zip([:easy, :multi].map { |t| Hash[*FTYPES.zip(FTYPES.map { |ft| "#{t}_setopt_#{ft}" }).flatten] }).flatten]
8
12
  # Sets appropriate option for easy, depending on value type.
9
13
  def set_option(option, value, handle, type = :easy)
10
- raise NameError, "Ethon::Curls::Options unknown type #{type}." unless respond_to?("#{type.to_s.downcase}_options")
11
- opthash=send("#{type.to_s.downcase}_options")
14
+ type = type.to_sym unless type.is_a?(Symbol)
15
+ raise NameError, "Ethon::Curls::Options unknown type #{type}." unless respond_to?(OPTION_STRINGS[type])
16
+ opthash=send(OPTION_STRINGS[type], nil)
12
17
  raise Errors::InvalidOption.new(option) unless opthash.include?(option)
13
18
 
14
19
  case opthash[option][:type]
@@ -90,8 +95,7 @@ module Ethon
90
95
  tv=((value<0) ? value.abs-1 : value)
91
96
  raise Errors::InvalidValue.new(option,value) unless tv<(1<<bits)
92
97
  end
93
-
94
- send("#{type}_setopt_#{func}", handle, opthash[option][:opt], value)
98
+ send(FUNCS[type][func], handle, opthash[option][:opt], value)
95
99
  end
96
100
 
97
101
  OPTION_TYPE_BASE = {
@@ -174,25 +178,24 @@ module Ethon
174
178
  else
175
179
  raise ArgumentError, "Ethon::Curls::Options #{ftype} #{name} Expected no opts." unless opts.nil?
176
180
  end
177
-
178
- opthash=const_get("#{ftype.to_s.upcase}_OPTIONS")
179
- opthash[name]={:type=>type, :opt=>OPTION_TYPE_BASE[OPTION_TYPE_MAP[type]]+num, :opts=>opts}
181
+ opthash=const_get(FOPTION_STRINGS[ftype])
182
+ opthash[name] = { :type => type,
183
+ :opt => OPTION_TYPE_BASE[OPTION_TYPE_MAP[type]] + num,
184
+ :opts => opts }
180
185
  end
181
186
 
182
187
  def self.option_alias(ftype,name,*aliases)
183
- opthash=const_get("#{ftype.to_s.upcase}_OPTIONS")
188
+ opthash=const_get(FOPTION_STRINGS[ftype])
184
189
  aliases.each { |a| opthash[a]=opthash[name] }
185
190
  end
186
191
 
187
192
  def self.option_type(type)
188
- cname="#{type.to_s.upcase}_OPTIONS"
189
- c=const_set(cname,{})
190
- eval %Q<
191
- def #{type.to_s.downcase}_options(rt=nil)
192
- return #{cname}.map { |k,v| [k,v[:opt]] } if rt==:enum
193
- #{cname}
194
- end
195
- >
193
+ cname = FOPTION_STRINGS[type]
194
+ const_set(cname, {})
195
+ define_method(OPTION_STRINGS[type]) do |rt|
196
+ return Ethon::Curls::Options.const_get(cname).map { |k, v| [k, v[:opt]] } if rt == :enum
197
+ Ethon::Curls::Options.const_get(cname)
198
+ end
196
199
  end
197
200
 
198
201
  # Curl multi options, refer
@@ -415,7 +418,7 @@ module Ethon
415
418
  option_alias :easy, :keypasswd, :sslkeypasswd
416
419
  option :easy, :sslengine, :string, 89
417
420
  option :easy, :sslengine_default, :none, 90
418
- option :easy, :sslversion, :enum, 32, [:default, :tlsv1, :sslv2, :sslv3]
421
+ option :easy, :sslversion, :enum, 32, [:default, :tlsv1, :sslv2, :sslv3, :tlsv1_0, :tlsv1_1, :tlsv1_2]
419
422
  option :easy, :ssl_verifypeer, :bool, 64
420
423
  option :easy, :cainfo, :string, 65
421
424
  option :easy, :issuercert, :string, 170
@@ -259,6 +259,17 @@ module Ethon
259
259
  set_callbacks
260
260
  end
261
261
 
262
+ # Clones libcurl session handle. This means that all options that is set in
263
+ # the current handle will be set on duplicated handle.
264
+ def dup
265
+ e = super
266
+ e.handle = Curl.easy_duphandle(handle)
267
+ e.instance_variable_set(:@body_write_callback, nil)
268
+ e.instance_variable_set(:@header_write_callback, nil)
269
+ e.instance_variable_set(:@debug_callback, nil)
270
+ e.set_callbacks
271
+ e
272
+ end
262
273
  # Url escapes the value.
263
274
  #
264
275
  # @example Url escape.
@@ -13,6 +13,12 @@ module Ethon
13
13
  @handle ||= FFI::AutoPointer.new(Curl.easy_init, Curl.method(:easy_cleanup))
14
14
  end
15
15
 
16
+ # Sets a pointer to the curl easy handle.
17
+ # @param [ ::FFI::Pointer ] Easy handle that will be assigned.
18
+ def handle=(h)
19
+ @handle = h
20
+ end
21
+
16
22
  # Perform the easy request.
17
23
  #
18
24
  # @example Perform the request.
@@ -21,7 +27,9 @@ module Ethon
21
27
  # @return [ Integer ] The return code.
22
28
  def perform
23
29
  @return_code = Curl.easy_perform(handle)
24
- Ethon.logger.debug { "ETHON: performed #{self.log_inspect}" }
30
+ if Ethon.logger.level.zero?
31
+ Ethon.logger.debug { "ETHON: performed #{log_inspect}" }
32
+ end
25
33
  complete
26
34
  @return_code
27
35
  end
@@ -20,22 +20,20 @@ module Ethon
20
20
  @escape.nil? ? true : false
21
21
  end
22
22
 
23
- Curl.easy_options.each do |opt,props|
24
- eval %Q<
25
- def #{opt}=(value)
26
- Curl.set_option(:#{opt}, value, handle)
23
+ Curl.easy_options(nil).each do |opt, props|
24
+ method_name = "#{opt}=".freeze
25
+ unless method_defined? method_name
26
+ define_method(method_name) do |value|
27
+ Curl.set_option(opt, value, handle)
27
28
  value
28
29
  end
29
- > unless method_defined? opt.to_s+"="
30
- if props[:type]==:callback then
31
- eval %Q<
32
- def #{opt}(&block)
33
- @procs ||= {}
34
- @procs[:#{opt}]=block
35
- Curl.set_option(:#{opt}, block, handle)
36
- nil
37
- end
38
- > unless method_defined? opt.to_s
30
+ end
31
+ next if props[:type] != :callback || method_defined?(opt)
32
+ define_method(opt) do |&block|
33
+ @procs ||= {}
34
+ @procs[opt.to_sym] = block
35
+ Curl.set_option(opt, block, handle)
36
+ nil
39
37
  end
40
38
  end
41
39
  end
@@ -1,5 +1,5 @@
1
1
  module Ethon
2
2
 
3
3
  # Ethon version.
4
- VERSION = '0.8.1'
4
+ VERSION = '0.9.0'
5
5
  end
@@ -3,111 +3,79 @@ require 'ethon'
3
3
  require 'open-uri'
4
4
  require 'patron'
5
5
  require 'curb'
6
- require "net/http"
6
+ require 'net/http'
7
7
  require 'cgi'
8
- require 'benchmark'
9
-
10
- Benchmark.bm do |bm|
8
+ require 'benchmark/ips'
9
+
10
+ require_relative '../spec/support/server'
11
+ require_relative '../spec/support/localhost_server'
12
+
13
+ LocalhostServer.new(TESTSERVER.new, 3000)
14
+ LocalhostServer.new(TESTSERVER.new, 3001)
15
+ LocalhostServer.new(TESTSERVER.new, 3002)
16
+
17
+ url = 'http://localhost:3000/'.freeze
18
+ uri = URI.parse('http://localhost:3000/').freeze
19
+ ethon = Ethon::Easy.new(url: url)
20
+ patron = Patron::Session.new
21
+ patron_url = Patron::Session.new(base_url: url)
22
+ curb = Curl::Easy.new(url)
23
+
24
+ puts '[Creation]'
25
+ Benchmark.ips do |x|
26
+ x.report('String.new') { '' }
27
+ x.report('Easy.new') { Ethon::Easy.new }
28
+ end
11
29
 
12
- [100_000].each do |i|
13
- puts "[ #{i} Creations]"
30
+ puts '[Escape]'
31
+ Benchmark.ips do |x|
32
+ x.report('CGI.escape') { CGI.escape("まつもと") }
33
+ x.report('Easy.escape') { ethon.escape("まつもと") }
34
+ end
14
35
 
15
- bm.report("String.new ") do
16
- i.times { String.new }
17
- end
36
+ puts '[Requests]'
37
+ Benchmark.ips do |x|
38
+ x.report('net/http') { Net::HTTP.get_response(uri) }
39
+ x.report('open-uri') { open url }
18
40
 
19
- bm.report("Easy.new ") do
20
- i.times { Ethon::Easy.new }
21
- end
41
+ x.report('patron') do
42
+ patron.base_url = url
43
+ patron.get('/')
22
44
  end
23
45
 
24
- GC.start
25
-
26
- [100_000].each do |i|
27
- puts "[ #{i} Escapes]"
28
-
29
- bm.report("CGI::escape ") do
30
- i.times { CGI::escape("まつもと") }
31
- end
46
+ x.report('patron reuse') { patron_url.get('/') }
32
47
 
33
- bm.report("Easy.escape ") do
34
- e = Ethon::Easy.new
35
- i.times { e.escape("まつもと") }
36
- end
48
+ x.report('curb') do
49
+ curb.url = url
50
+ curb.perform
37
51
  end
38
52
 
39
- GC.start
40
-
41
- [1000].each do |i|
42
- puts "[ #{i} Requests]"
43
-
44
- bm.report("net/http ") do
45
- uri = URI.parse("http://localhost:3001/")
46
- i.times { Net::HTTP.get_response(uri) }
47
- end
48
-
49
- bm.report("open-uri ") do
50
- i.times { open "http://localhost:3001/" }
51
- end
53
+ x.report('curb reuse') { curb.perform }
52
54
 
53
- bm.report("patron ") do
54
- sess = Patron::Session.new
55
- i.times do
56
- sess.base_url = "http://localhost:3001"
57
- sess.get("/")
58
- end
59
- end
60
-
61
- bm.report("patron reuse ") do
62
- sess = Patron::Session.new
63
- sess.base_url = "http://localhost:3001"
64
- i.times do
65
- sess.get("/")
66
- end
67
- end
68
-
69
- bm.report("curb reuse ") do
70
- easy = Curl::Easy.new("http://localhost:3001")
71
- i.times do
72
- easy.perform
73
- end
74
- end
75
-
76
- bm.report("Easy.perform ") do
77
- easy = Ethon::Easy.new
78
- i.times do
79
- easy.url = "http://localhost:3001/"
80
- easy.prepare
81
- easy.perform
82
- end
83
- end
84
-
85
- bm.report("Easy.perform reuse") do
86
- easy = Ethon::Easy.new
87
- easy.url = "http://localhost:3001/"
88
- easy.prepare
89
- i.times { easy.perform }
90
- end
55
+ x.report('Easy.perform') do
56
+ ethon.url = url
57
+ ethon.perform
91
58
  end
92
59
 
93
- GC.start
94
-
95
- puts "[ 4 delayed Requests ]"
60
+ x.report('Easy.perform reuse') { ethon.perform }
61
+ end
96
62
 
97
- bm.report("net/http ") do
63
+ puts "[ 4 delayed Requests ]"
64
+ Benchmark.ips do |x|
65
+ x.report('net/http') do
98
66
  3.times do |i|
99
67
  uri = URI.parse("http://localhost:300#{i}/?delay=1")
100
68
  Net::HTTP.get_response(uri)
101
69
  end
102
70
  end
103
71
 
104
- bm.report("open-uri ") do
72
+ x.report("open-uri") do
105
73
  3.times do |i|
106
74
  open("http://localhost:300#{i}/?delay=1")
107
75
  end
108
76
  end
109
77
 
110
- bm.report("patron ") do
78
+ x.report("patron") do
111
79
  sess = Patron::Session.new
112
80
  3.times do |i|
113
81
  sess.base_url = "http://localhost:300#{i}/?delay=1"
@@ -115,21 +83,19 @@ Benchmark.bm do |bm|
115
83
  end
116
84
  end
117
85
 
118
- bm.report("Easy.perform ") do
86
+ x.report("Easy.perform") do
119
87
  easy = Ethon::Easy.new
120
88
  3.times do |i|
121
89
  easy.url = "http://localhost:300#{i}/?delay=1"
122
- easy.prepare
123
90
  easy.perform
124
91
  end
125
92
  end
126
93
 
127
- bm.report("Multi.perform ") do
94
+ x.report("Multi.perform") do
128
95
  multi = Ethon::Multi.new
129
96
  3.times do |i|
130
97
  easy = Ethon::Easy.new
131
98
  easy.url = "http://localhost:300#{i}/?delay=1"
132
- easy.prepare
133
99
  multi.add(easy)
134
100
  end
135
101
  multi.perform
@@ -88,7 +88,7 @@ describe Ethon::Easy::Informations do
88
88
 
89
89
  describe "#supports_zlib?" do
90
90
  it "returns true" do
91
- Kernel.should_receive(:warn) #deprecation warning
91
+ expect(Kernel).to receive(:warn)
92
92
  expect(easy.supports_zlib?).to be_truthy
93
93
  end
94
94
  end
@@ -25,6 +25,7 @@ describe Ethon::Easy::Operations do
25
25
  let(:password) { nil }
26
26
 
27
27
  before do
28
+ Ethon.logger.level = Logger::DEBUG
28
29
  easy.url = url
29
30
  easy.timeout = timeout
30
31
  easy.connecttimeout = connect_timeout
@@ -51,7 +52,7 @@ describe Ethon::Easy::Operations do
51
52
  end
52
53
 
53
54
  it "calls Curl.easy_cleanup" do
54
- FFI::AutoPointer.any_instance.should_receive(:free)
55
+ expect_any_instance_of(FFI::AutoPointer).to receive(:free)
55
56
  easy.cleanup
56
57
  end
57
58
 
@@ -99,6 +99,83 @@ describe Ethon::Easy do
99
99
  end
100
100
  end
101
101
 
102
+ describe "#dup" do
103
+ let!(:easy) do
104
+ easy = Ethon::Easy.new
105
+ easy.url = "http://localhost:3001/"
106
+ easy.on_complete { 'on_complete' }
107
+ easy.on_headers { 'on_headers' }
108
+ easy.response_body = 'test_body'
109
+ easy.response_headers = 'test_headers'
110
+ easy
111
+ end
112
+ let!(:e) { easy.dup }
113
+
114
+ it "sets a new handle" do
115
+ expect(e.handle).not_to eq(easy.handle)
116
+ end
117
+
118
+ it "preserves url" do
119
+ expect(e.url).to eq(easy.url)
120
+ end
121
+
122
+ it "preserves on_complete callback" do
123
+ expect(e.on_complete).to be(easy.on_complete)
124
+ end
125
+
126
+ it "preserves on_headers callback" do
127
+ expect(e.on_headers).to be(easy.on_headers)
128
+ end
129
+
130
+ it 'preserves body_write_callback of original handle' do
131
+ expect { easy.perform }.to change { easy.response_body }
132
+ expect { easy.perform }.not_to change { e.response_body }
133
+ end
134
+
135
+ it 'sets new body_write_callback of duplicated handle' do
136
+ expect { e.perform }.to change { e.response_body }
137
+ expect { e.perform }.not_to change { easy.response_body }
138
+ end
139
+
140
+ it 'preserves headers_write_callback of original handle' do
141
+ expect { easy.perform }.to change { easy.response_headers }
142
+ expect { easy.perform }.not_to change { e.response_headers }
143
+ end
144
+
145
+ it 'sets new headers_write_callback of duplicated handle' do
146
+ expect { e.perform }.to change { e.response_headers }
147
+ expect { e.perform }.not_to change { easy.response_headers }
148
+ end
149
+
150
+ it "resets response_body" do
151
+ expect(e.response_body).to be_empty
152
+ end
153
+
154
+ it "resets response_headers" do
155
+ expect(e.response_headers).to be_empty
156
+ end
157
+
158
+ it "sets response_body for duplicated Easy" do
159
+ e.perform
160
+ expect(e.response_body).not_to be_empty
161
+ end
162
+
163
+ it "sets response_headers for duplicated Easy" do
164
+ e.perform
165
+ expect(e.response_headers).not_to be_empty
166
+ end
167
+
168
+ it "preserves response_body for original Easy" do
169
+ e.perform
170
+ expect(easy.response_body).to eq('test_body')
171
+ end
172
+
173
+ it "preserves response_headers for original Easy" do
174
+ e.perform
175
+ expect(easy.response_headers).to eq('test_headers')
176
+ end
177
+ end
178
+
102
179
  describe "#mirror" do
103
180
  it "returns a Mirror" do
104
181
  expect(easy.mirror).to be_a(Ethon::Easy::Mirror)
@@ -4,7 +4,7 @@ require 'zlib'
4
4
  require 'sinatra/base'
5
5
 
6
6
  TESTSERVER = Sinatra.new do
7
- set :logging, false
7
+ set :logging, nil
8
8
 
9
9
  fail_count = 0
10
10
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ethon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hans Hasselberg
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-10 00:00:00.000000000 Z
11
+ date: 2016-04-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi