rest-core 2.0.4 → 2.1.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.
@@ -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