rest-core 2.0.4 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,162 @@
1
+
2
+ # stolen and modified from rest-client
3
+
4
+ require 'rest-core/error'
5
+
6
+ require 'mime/types'
7
+
8
+ require 'stringio'
9
+ require 'tempfile'
10
+
11
+ module RestCore; end
12
+ module RestCore::Payload
13
+ include RestCore
14
+ module_function
15
+
16
+ def generate payload
17
+ if payload.respond_to?(:read)
18
+ Streamed.new(payload)
19
+
20
+ elsif payload.kind_of?(String)
21
+ StreamedString.new(payload)
22
+
23
+ elsif payload.kind_of?(Hash)
24
+ if Middleware.contain_binary?(payload)
25
+ Multipart.new(payload)
26
+
27
+ else
28
+ UrlEncoded.new(payload)
29
+ end
30
+
31
+ else
32
+ raise Error.new("Payload should be either String, Hash, or" \
33
+ " responding to `read', but: #{payload.inspect}")
34
+ end
35
+ end
36
+
37
+ class Streamed
38
+ def initialize payload
39
+ @io = payload
40
+ end
41
+
42
+ attr_reader :io
43
+ alias_method :to_io, :io
44
+
45
+ def read bytes=nil
46
+ io.read(bytes)
47
+ end
48
+
49
+ def headers
50
+ {'Content-Length' => size.to_s}
51
+ end
52
+
53
+ def size
54
+ if io.respond_to?(:size)
55
+ io.size
56
+ elsif io.respond_to?(:stat)
57
+ io.stat.size
58
+ else
59
+ 0
60
+ end
61
+ end
62
+
63
+ def close ; io.close unless closed?; end
64
+ def closed?; io.closed? ; end
65
+ end
66
+
67
+ class StreamedString < Streamed
68
+ def initialize payload
69
+ super(StringIO.new(payload))
70
+ end
71
+ end
72
+
73
+ class UrlEncoded < StreamedString
74
+ def initialize payload
75
+ super(RestCore::Middleware.percent_encode(payload))
76
+ end
77
+
78
+ def headers
79
+ super.merge('Content-Type' => 'application/x-www-form-urlencoded')
80
+ end
81
+ end
82
+
83
+ class Multipart < Streamed
84
+ EOL = "\r\n"
85
+
86
+ def initialize payload
87
+ super(Tempfile.new("rest-core.payload.#{boundary}"))
88
+
89
+ io.binmode
90
+
91
+ payload.each_with_index do |(k, v), i|
92
+ if v.kind_of?(Array)
93
+ v.each{ |vv| part(k, vv) }
94
+ else
95
+ part(k, v)
96
+ end
97
+ end
98
+ io.write("--#{boundary}--#{EOL}")
99
+ io.rewind
100
+ end
101
+
102
+ def part k, v
103
+ io.write("--#{boundary}#{EOL}Content-Disposition: form-data")
104
+ io.write("; name=\"#{k}\"") if k
105
+ if v.respond_to?(:read)
106
+ part_binary(k, v)
107
+ else
108
+ part_plantext(k, v)
109
+ end
110
+ end
111
+
112
+ def part_plantext k, v
113
+ io.write("#{EOL}#{EOL}#{v}#{EOL}")
114
+ end
115
+
116
+ def part_binary k, v
117
+ if v.respond_to?(:original_filename) # Rails
118
+ io.write("; filename=\"#{v.original_filename}\"#{EOL}")
119
+ elsif v.respond_to?(:path) # files
120
+ io.write("; filename=\"#{File.basename(v.path)}\"#{EOL}")
121
+ else # io
122
+ io.write("; filename=\"#{k}\"#{EOL}")
123
+ end
124
+
125
+ # supply your own content type for regular files, will you?
126
+ if v.respond_to?(:content_type) # Rails
127
+ io.write("Content-Type: #{v.content_type}#{EOL}#{EOL}")
128
+ elsif v.respond_to?(:path) && type = mime_type(v.path) # files
129
+ io.write("Content-Type: #{type}#{EOL}#{EOL}")
130
+ else
131
+ io.write(EOL)
132
+ end
133
+
134
+ while data = v.read(8192)
135
+ io.write(data)
136
+ end
137
+
138
+ io.write(EOL)
139
+
140
+ ensure
141
+ v.close if v.respond_to?(:close)
142
+ end
143
+
144
+ def mime_type path
145
+ mime = MIME::Types.type_for(path)
146
+ mime.first && mime.first.content_type
147
+ end
148
+
149
+ def boundary
150
+ @boundary ||= rand(1_000_000).to_s
151
+ end
152
+
153
+ def headers
154
+ super.merge('Content-Type' =>
155
+ "multipart/form-data; boundary=#{boundary}")
156
+ end
157
+
158
+ def close
159
+ io.close! unless io.closed?
160
+ end
161
+ end
162
+ end
@@ -1,4 +1,4 @@
1
1
 
2
2
  module RestCore
3
- VERSION = '2.0.4'
3
+ VERSION = '2.1.0'
4
4
  end
data/rest-core.gemspec CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "rest-core"
5
- s.version = "2.0.4"
5
+ s.version = "2.1.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = [
9
9
  "Cardinal Blue",
10
10
  "Lin Jen-Shin (godfat)"]
11
- s.date = "2013-04-30"
12
- s.description = "Modular Ruby clients interface for REST APIs\n\nThere has been an explosion in the number of REST APIs available today.\nTo address the need for a way to access these APIs easily and elegantly,\nwe have developed rest-core, which consists of composable middleware\nthat allows you to build a REST client for any REST API. Or in the case of\ncommon APIs such as Facebook, Github, and Twitter, you can simply use the\ndedicated clients provided by [rest-more][].\n\n[rest-more]: https://github.com/cardinalblue/rest-more"
11
+ s.date = "2013-05-08"
12
+ s.description = "Modular Ruby clients interface for REST APIs.\n\nThere has been an explosion in the number of REST APIs available today.\nTo address the need for a way to access these APIs easily and elegantly,\nwe have developed rest-core, which consists of composable middleware\nthat allows you to build a REST client for any REST API. Or in the case of\ncommon APIs such as Facebook, Github, and Twitter, you can simply use the\ndedicated clients provided by [rest-more][].\n\n[rest-more]: https://github.com/cardinalblue/rest-more"
13
13
  s.email = ["dev (XD) cardinalblue.com"]
14
14
  s.files = [
15
15
  ".gitignore",
@@ -68,6 +68,7 @@ Gem::Specification.new do |s|
68
68
  "lib/rest-core/util/hmac.rb",
69
69
  "lib/rest-core/util/json.rb",
70
70
  "lib/rest-core/util/parse_query.rb",
71
+ "lib/rest-core/util/payload.rb",
71
72
  "lib/rest-core/version.rb",
72
73
  "lib/rest-core/wrapper.rb",
73
74
  "rest-core.gemspec",
@@ -78,8 +79,9 @@ Gem::Specification.new do |s|
78
79
  "test/test_cache.rb",
79
80
  "test/test_client.rb",
80
81
  "test/test_client_oauth1.rb",
82
+ "test/test_default_payload.rb",
81
83
  "test/test_default_query.rb",
82
- "test/test_em_http_request.rb",
84
+ "test/test_em-http-request.rb",
83
85
  "test/test_error_detector.rb",
84
86
  "test/test_error_detector_http.rb",
85
87
  "test/test_error_handler.rb",
@@ -88,22 +90,26 @@ Gem::Specification.new do |s|
88
90
  "test/test_json_response.rb",
89
91
  "test/test_oauth1_header.rb",
90
92
  "test/test_payload.rb",
93
+ "test/test_rest-client.rb",
91
94
  "test/test_simple.rb",
92
95
  "test/test_timeout.rb",
93
96
  "test/test_universal.rb",
94
97
  "test/test_wrapper.rb"]
95
98
  s.homepage = "https://github.com/cardinalblue/rest-core"
99
+ s.licenses = ["Apache License 2.0"]
100
+ s.post_install_message = "# [rest-core] Incompatible changes for POST requests:\n\n* We no longer support Rails-like POST payload, like translating\n `{:foo => [1, 2]}` to `'foo[]=1&foo[]=2'`. It would now be translated to\n `'foo=1&foo=2'`. If you like `'foo[]'` as the key, simply pass it as\n `{'foo[]' => [1, 2]}`.\n\n* This also applies to nested hashes like `{:foo => {:bar => 1}`. If you\n want that behaviour, just pass `{'foo[bar]' => 1}` which would then be\n translated to `'foo[bar]=1'`.\n"
96
101
  s.require_paths = ["lib"]
97
- s.rubygems_version = "2.0.0"
98
- s.summary = "Modular Ruby clients interface for REST APIs"
102
+ s.rubygems_version = "2.0.3"
103
+ s.summary = "Modular Ruby clients interface for REST APIs."
99
104
  s.test_files = [
100
105
  "test/test_auth_basic.rb",
101
106
  "test/test_builder.rb",
102
107
  "test/test_cache.rb",
103
108
  "test/test_client.rb",
104
109
  "test/test_client_oauth1.rb",
110
+ "test/test_default_payload.rb",
105
111
  "test/test_default_query.rb",
106
- "test/test_em_http_request.rb",
112
+ "test/test_em-http-request.rb",
107
113
  "test/test_error_detector.rb",
108
114
  "test/test_error_detector_http.rb",
109
115
  "test/test_error_handler.rb",
@@ -112,6 +118,7 @@ Gem::Specification.new do |s|
112
118
  "test/test_json_response.rb",
113
119
  "test/test_oauth1_header.rb",
114
120
  "test/test_payload.rb",
121
+ "test/test_rest-client.rb",
115
122
  "test/test_simple.rb",
116
123
  "test/test_timeout.rb",
117
124
  "test/test_universal.rb",
data/task/gemgem.rb CHANGED
@@ -14,6 +14,7 @@ module Gemgem
14
14
 
15
15
  s.description = description.join
16
16
  s.summary = description.first
17
+ s.license = readme['LICENSE'].sub(/.+\n\n/, '').lines.first.strip
17
18
 
18
19
  s.rubygems_version = Gem::VERSION
19
20
  s.date = Time.now.strftime('%Y-%m-%d')
@@ -33,8 +34,8 @@ module Gemgem
33
34
  @readme ||=
34
35
  if path
35
36
  ps = "##{File.read(path)}".
36
- scan(/((#+)[^\n]+\n\n.+?(?=\n\n\2[^#\n]+\n))/m).map(&:first)
37
- ps.inject({'HEADER' => ps.first}){ |r, s, i|
37
+ scan(/((#+)[^\n]+\n\n.+?(?=(\n\n\2[^#\n]+\n)|\Z))/m).map(&:first)
38
+ ps.inject('HEADER' => ps.first){ |r, s, i|
38
39
  r[s[/\w+/]] = s
39
40
  r
40
41
  }
@@ -44,7 +45,7 @@ module Gemgem
44
45
  end
45
46
 
46
47
  def description
47
- @description ||= (readme['DESCRIPTION']||'').sub(/.+\n\n/, '').lines.to_a
48
+ @description ||= (readme['DESCRIPTION']||'').sub(/.+\n\n/, '').lines
48
49
  end
49
50
 
50
51
  def changes
@@ -104,9 +105,9 @@ module Gemgem
104
105
  end
105
106
 
106
107
  def split_lines ruby
107
- ruby.gsub(/(.+?)\[(.+?)\]/){ |s|
108
+ ruby.gsub(/(.+?)\s*=\s*\[(.+?)\]/){ |s|
108
109
  if $2.index(',')
109
- "#{$1}[\n #{$2.split(',').map(&:strip).join(",\n ")}]"
110
+ "#{$1} = [\n #{$2.split(',').map(&:strip).join(",\n ")}]"
110
111
  else
111
112
  s
112
113
  end
@@ -179,7 +180,7 @@ namespace :gem do
179
180
 
180
181
  desc 'Install gem'
181
182
  task :install => [:build] do
182
- sh("#{Gem.ruby} -S gem install pkg/#{Gemgem.gem_tag}")
183
+ sh("#{Gem.ruby} -S gem install pkg/#{Gemgem.gem_tag}.gem")
183
184
  end
184
185
 
185
186
  desc 'Build gem'
@@ -6,6 +6,8 @@ describe RC::AuthBasic do
6
6
  @auth = RC::AuthBasic.new(RC::Dry.new, nil, nil)
7
7
  end
8
8
 
9
+ env = {RC::REQUEST_HEADERS => {}}
10
+
9
11
  should 'do nothing' do
10
12
  @auth.call({}){ |res| res.should.eq({}) }
11
13
  end
@@ -14,9 +16,9 @@ describe RC::AuthBasic do
14
16
  @auth.instance_eval{@username = 'Aladdin'}
15
17
  @auth.instance_eval{@password = 'open sesame'}
16
18
 
17
- @auth.call({}){ |res|
18
- res.should.eq({RC::REQUEST_HEADERS =>
19
- {'Authorization' => 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='}})
19
+ @auth.call(env){ |res|
20
+ res.should.eq(RC::REQUEST_HEADERS =>
21
+ {'Authorization' => 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='})
20
22
  }
21
23
 
22
24
  acc = {'Accept' => 'text/plain'}
@@ -30,11 +32,11 @@ describe RC::AuthBasic do
30
32
 
31
33
  should 'leave a log if username are not both provided' do
32
34
  @auth.instance_eval{@username = 'Aladdin'}
33
- @auth.call({}){ |res| res[RC::LOG].size.should.eq 1 }
35
+ @auth.call(env){ |res| res[RC::LOG].size.should.eq 1 }
34
36
  end
35
37
 
36
38
  should 'leave a log if password are not both provided' do
37
39
  @auth.instance_eval{@password = 'open sesame'}
38
- @auth.call({}){ |res| res[RC::LOG].size.should.eq 1 }
40
+ @auth.call(env){ |res| res[RC::LOG].size.should.eq 1 }
39
41
  end
40
42
  end
data/test/test_cache.rb CHANGED
@@ -149,6 +149,12 @@ describe RC::Cache do
149
149
  }
150
150
  end
151
151
 
152
+ should 'not cache dry run' do
153
+ c = RC::Builder.client{use RC::Cache, {}, nil}.new
154
+ c.url('test')
155
+ c.cache.should.eq({})
156
+ end
157
+
152
158
  should 'update cache if there is cache option set to false' do
153
159
  url, body = "https://cache", 'ok'
154
160
  stub_request(:get, url).to_return(:body => body)
@@ -18,30 +18,30 @@ describe RC::ClientOauth1 do
18
18
  sig = Digest::MD5.hexdigest('e&a=b&c=d')
19
19
  data_sig = data.merge('sig' => sig)
20
20
  data_json = RC::Json.encode(data_sig)
21
- @client = client.new(:data => data, :consumer_secret => 'e')
21
+ c = client.new(:data => data, :consumer_secret => 'e')
22
22
 
23
- @client.send(:calculate_sig).should.eq sig
24
- @client.data_json.should.eq data_json
23
+ c.send(:calculate_sig).should.eq sig
24
+ c.data_json.should.eq data_json
25
25
 
26
- @client.data_json = data_json
27
- @client.data.should.eq data_sig
26
+ c.data_json = data_json
27
+ c.data.should.eq data_sig
28
28
 
29
- @client.data_json = RC::Json.encode(data_sig.merge('sig' => 'wrong'))
30
- @client.data.should.eq({})
29
+ c.data_json = RC::Json.encode(data_sig.merge('sig' => 'wrong'))
30
+ c.data.should.eq({})
31
31
 
32
- @client.data_json = data_json
33
- @client.data.should.eq data_sig
32
+ c.data_json = data_json
33
+ c.data.should.eq data_sig
34
34
 
35
- @client.data_json = 'bad json'
36
- @client.data.should.eq({})
35
+ c.data_json = 'bad json'
36
+ c.data.should.eq({})
37
37
  end
38
38
 
39
39
  should 'have correct default data' do
40
- @client = client.new
41
- @client.data.should.eq({})
42
- @client.data = nil
43
- @client.data['a'] = 'b'
44
- @client.data['a'].should.eq 'b'
40
+ c = client.new
41
+ c.data.should.eq({})
42
+ c.data = nil
43
+ c.data['a'] = 'b'
44
+ c.data['a'].should.eq 'b'
45
45
  end
46
46
 
47
47
  should 'authorize' do
@@ -51,12 +51,12 @@ describe RC::ClientOauth1 do
51
51
  stub_request(:post, 'http://nocalhost').
52
52
  to_return(:body => 'user_id=123&haha=point')
53
53
 
54
- @client = client.new(:request_token_path => 'http://localhost',
55
- :authorize_path => 'http://mocalhost',
56
- :access_token_path => 'http://nocalhost')
54
+ c = client.new(:request_token_path => 'http://localhost',
55
+ :authorize_path => 'http://mocalhost',
56
+ :access_token_path => 'http://nocalhost')
57
57
 
58
- @client.authorize_url!.should.eq 'http://mocalhost?oauth_token=abc'
59
- @client.authorize!.should.eq('user_id' => '123', 'haha' => 'point',
60
- 'authorized' => 'true')
58
+ c.authorize_url!.should.eq 'http://mocalhost?oauth_token=abc'
59
+ c.authorize!.should.eq('user_id' => '123', 'haha' => 'point',
60
+ 'authorized' => 'true')
61
61
  end
62
62
  end
@@ -0,0 +1,38 @@
1
+
2
+ require 'rest-core/test'
3
+
4
+ describe RC::DefaultPayload do
5
+ app = RC::DefaultPayload.new(RC::Dry.new, {})
6
+ env = {RC::REQUEST_PAYLOAD => {}}
7
+
8
+ should 'do nothing' do
9
+ app.call(env){ |r| r[RC::REQUEST_PAYLOAD].should.eq({}) }
10
+ end
11
+
12
+ should 'merge payload' do
13
+ app.instance_eval{@payload = {'pay' => 'load'}}
14
+
15
+ app.call(env){ |r| r.should.eq({RC::REQUEST_PAYLOAD =>
16
+ {'pay' => 'load'}}) }
17
+
18
+ format = {'format' => 'json'}
19
+ env = {RC::REQUEST_PAYLOAD => format}
20
+
21
+ app.call(env){ |r| r.should.eq({RC::REQUEST_PAYLOAD =>
22
+ {'pay' => 'load'}.merge(format)})}
23
+ end
24
+
25
+ should 'accept non-hash payload' do
26
+ u = RC::Universal.new(:log_method => false)
27
+ env = {RC::REQUEST_PAYLOAD => 'payload'}
28
+ u.request_full(env, u.dry)[RC::REQUEST_PAYLOAD].should.eq('payload')
29
+
30
+ u.payload = 'default'
31
+ u.request_full(env, u.dry)[RC::REQUEST_PAYLOAD].should.eq('payload')
32
+ u.request_full({} , u.dry)[RC::REQUEST_PAYLOAD].should.eq('default')
33
+
34
+ u = RC::Builder.client{use RC::DefaultPayload, 'maylord'}.new
35
+ u.request_full({} , u.dry)[RC::REQUEST_PAYLOAD].should.eq('maylord')
36
+ u.request_full(env, u.dry)[RC::REQUEST_PAYLOAD].should.eq('payload')
37
+ end
38
+ end
@@ -6,34 +6,36 @@ describe RC::DefaultQuery do
6
6
  @app = RC::DefaultQuery.new(RC::Dry.new, {})
7
7
  end
8
8
 
9
+ env = {RC::REQUEST_QUERY => {}}
10
+
9
11
  describe 'when given query' do
10
12
  should 'do nothing' do
11
- @app.call({}){ |r| r[RC::REQUEST_QUERY].should.eq({}) }
13
+ @app.call(env){ |r| r[RC::REQUEST_QUERY].should.eq({}) }
12
14
  end
13
15
 
14
16
  should 'merge query' do
15
17
  @app.instance_eval{@query = {'q' => 'uery'}}
16
18
 
17
- @app.call({}){ |r| r.should.eq({RC::REQUEST_QUERY => {'q' => 'uery'}}) }
19
+ @app.call(env){ |r| r.should.eq({RC::REQUEST_QUERY => {'q' => 'uery'}}) }
18
20
 
19
21
  format = {'format' => 'json'}
20
- env = {RC::REQUEST_QUERY => format}
22
+ e = {RC::REQUEST_QUERY => format}
21
23
 
22
- @app.call(env){ |r|
24
+ @app.call(e){ |r|
23
25
  r.should.eq({RC::REQUEST_QUERY => {'q' => 'uery'}.merge(format)}) }
24
26
  end
25
27
 
26
28
  should 'string_keys in query' do
27
- env = {'query' => {:symbol => 'value'}}
28
- @app.call(env){ |r|
29
- r.should.eq({RC::REQUEST_QUERY => {'symbol' => 'value'}}.merge(env))
29
+ e = {'query' => {:symbol => 'value'}}
30
+ @app.call(env.merge(e)){ |r|
31
+ r.should.eq({RC::REQUEST_QUERY => {'symbol' => 'value'}}.merge(e))
30
32
  }
31
33
  end
32
34
  end
33
35
 
34
36
  describe 'when not given query' do
35
37
  should 'merge query with {}' do
36
- @app.call({}){ |r| r.should.eq({RC::REQUEST_QUERY => {}}) }
38
+ @app.call(env){ |r| r.should.eq(RC::REQUEST_QUERY => {}) }
37
39
  end
38
40
  end
39
41
  end