rest-core 3.2.0 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +23 -0
  3. data/Gemfile +1 -1
  4. data/README.md +11 -10
  5. data/Rakefile +2 -1
  6. data/lib/rest-core.rb +0 -1
  7. data/lib/rest-core/builder.rb +59 -12
  8. data/lib/rest-core/client/universal.rb +11 -10
  9. data/lib/rest-core/middleware.rb +20 -0
  10. data/lib/rest-core/middleware/cache.rb +12 -10
  11. data/lib/rest-core/middleware/default_headers.rb +2 -2
  12. data/lib/rest-core/middleware/default_payload.rb +2 -26
  13. data/lib/rest-core/middleware/default_query.rb +2 -10
  14. data/lib/rest-core/middleware/json_request.rb +2 -1
  15. data/lib/rest-core/middleware/json_response.rb +5 -2
  16. data/lib/rest-core/test.rb +3 -12
  17. data/lib/rest-core/version.rb +1 -1
  18. data/rest-core.gemspec +11 -12
  19. data/task/gemgem.rb +1 -5
  20. data/test/test_auth_basic.rb +4 -4
  21. data/test/test_builder.rb +20 -4
  22. data/test/test_cache.rb +19 -20
  23. data/test/test_clash.rb +1 -1
  24. data/test/test_clash_response.rb +11 -11
  25. data/test/test_client.rb +10 -10
  26. data/test/test_client_oauth1.rb +3 -3
  27. data/test/test_config.rb +1 -1
  28. data/test/test_default_headers.rb +13 -0
  29. data/test/test_default_payload.rb +11 -3
  30. data/test/test_default_query.rb +12 -4
  31. data/test/test_error_detector.rb +1 -1
  32. data/test/test_error_detector_http.rb +1 -1
  33. data/test/test_error_handler.rb +5 -5
  34. data/test/test_event_source.rb +16 -16
  35. data/test/test_follow_redirect.rb +6 -6
  36. data/test/test_future.rb +2 -2
  37. data/test/test_json_request.rb +9 -4
  38. data/test/test_json_response.rb +9 -9
  39. data/test/test_oauth1_header.rb +9 -9
  40. data/test/test_oauth2_header.rb +3 -3
  41. data/test/test_parse_link.rb +4 -4
  42. data/test/test_payload.rb +21 -21
  43. data/test/test_promise.rb +7 -7
  44. data/test/test_query_response.rb +5 -5
  45. data/test/test_rest-client.rb +7 -6
  46. data/test/test_simple.rb +5 -5
  47. data/test/test_smash.rb +1 -1
  48. data/test/test_smash_response.rb +11 -11
  49. data/test/test_thread_pool.rb +1 -1
  50. data/test/test_timeout.rb +3 -3
  51. data/test/test_universal.rb +12 -2
  52. metadata +9 -10
  53. data/lib/rest-core/wrapper.rb +0 -72
  54. data/test/test_wrapper.rb +0 -36
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fbe0fe23ff10ec39cf8a2b367616b1c332bd0d42
4
- data.tar.gz: 0a7132a6620ecab1c35fd69fdff24733161aafad
3
+ metadata.gz: 2aa111679bf7c7b75c113cea9c30e4c52c709ed0
4
+ data.tar.gz: fe0dda1aea907ef4141f7125a7dfb9a881d42441
5
5
  SHA512:
6
- metadata.gz: d59288134f72ed58c335e743e439479d93de99f6c4278d871733ec845790d65cede5986e276f0a8cd0f0aa39f1cf2ee38325ef468e74cbcb4a08bf3cb01ed2cc
7
- data.tar.gz: 73a39d9b95793cc496d05e6c9370e0da5bd7c3ae22374e3ebe4ebc71d3dfd0d2778f885a9b8395fe1c8d38fd143b767df45d57ccbfcb6ae957116a8139ba2c41
6
+ metadata.gz: 4e634ba680b5cca9604ea54a5182e0afe161bf44e80b1f7748d8ffc922429023fb99fd009aea9a3c4b001b5531e9de315947de3db6229020de30c2bc88b8d9c7
7
+ data.tar.gz: 1a110e340a1f5e64e5ede750e6bca7a9ef38fa2137d27c53e900be9026b0d8d8f61664e7fc7032954bcb975ea45a6eea221e74ea7e01c9a62b3ca89c19f0b225
data/CHANGES.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # CHANGES
2
2
 
3
+ ## rest-core 3.3.0 -- 2014-08-25
4
+
5
+ ### Incompatible changes
6
+
7
+ * Removed `RC::Wrapper`. Apparently it's introducing more troubles than the
8
+ benefit than it brings. Currently, only `RC::Cache` is really using it,
9
+ and now the old functionality is merged back to `RC::Builder`.
10
+
11
+ * Therefore `RC::Cache` is no longer accepting a block.
12
+
13
+ * `RC::Universal` is then updated accordingly to respect the new `RC::Cache`.
14
+
15
+ ### Enhancements
16
+
17
+ * Now `RC::DefaultQuery`, `RC::DefaultPayload`, and `RC::DefaultHeaders`
18
+ work the same way. Previously they merge hashes slightly differently.
19
+
20
+ * Introduced `RC::Middleware#member=` in addition to `RC::Middleware#member`.
21
+
22
+ * RC::JsonResponse would now strip the problematic UTF-8 BOM before parsing.
23
+ This was introduced because Stackoverflow would return it. We also
24
+ try to not raise any encoding issues here.
25
+
3
26
  ## rest-core 3.2.0 -- 2014-06-27
4
27
 
5
28
  ### Enhancements
data/Gemfile CHANGED
@@ -6,7 +6,7 @@ gemspec
6
6
  gem 'rest-client'
7
7
 
8
8
  gem 'rake'
9
- gem 'bacon'
9
+ gem 'pork'
10
10
  gem 'muack'
11
11
  gem 'webmock'
12
12
 
data/README.md CHANGED
@@ -409,18 +409,19 @@ module RestCore
409
409
  use DefaultPayload, {}
410
410
  use JsonRequest , false
411
411
  use AuthBasic , nil, nil
412
+ use CommonLogger , method(:puts)
413
+ use ErrorHandler , nil
414
+ use ErrorDetectorHttp
415
+
416
+ use SmashResponse , false
417
+ use ClashResponse , false
418
+ use JsonResponse , false
419
+ use QueryResponse , false
420
+
421
+ use Cache , {}, 600 # default :expires_in 600 but the default
422
+ # cache {} didn't support it
412
423
 
413
424
  use FollowRedirect, 10
414
- use CommonLogger , method(:puts)
415
- use Cache , {}, 600 do # default :expires_in 600 but the default
416
- # cache {} didn't support it
417
- use ErrorHandler, nil
418
- use ErrorDetectorHttp
419
- use SmashResponse, false
420
- use ClashResponse, false
421
- use JsonResponse, false
422
- use QueryResponse, false
423
- end
424
425
  end
425
426
  end
426
427
  ```
data/Rakefile CHANGED
@@ -11,5 +11,6 @@ Gemgem.init(dir) do |s|
11
11
  s.name = 'rest-core'
12
12
  s.version = RestCore::VERSION
13
13
  s.homepage = 'https://github.com/godfat/rest-core'
14
- %w[httpclient mime-types timers].each{ |g| s.add_runtime_dependency(g) }
14
+ %w[httpclient mime-types].each{ |g| s.add_runtime_dependency(g) }
15
+ s.add_runtime_dependency('timers', '<4')
15
16
  end
data/lib/rest-core.rb CHANGED
@@ -29,7 +29,6 @@ module RestCore
29
29
  autoload :Error , 'rest-core/error'
30
30
  autoload :Event , 'rest-core/event'
31
31
  autoload :Middleware , 'rest-core/middleware'
32
- autoload :Wrapper , 'rest-core/wrapper'
33
32
  autoload :Promise , 'rest-core/promise'
34
33
  autoload :ThreadPool , 'rest-core/thread_pool'
35
34
  autoload :EventSource , 'rest-core/event_source'
@@ -1,18 +1,61 @@
1
1
 
2
2
  require 'thread'
3
3
  require 'rest-core/client'
4
- require 'rest-core/wrapper'
5
4
 
6
5
  class RestCore::Builder
7
6
  include RestCore
8
- include Wrapper
9
7
 
10
- def self.default_engine
11
- @default_engine ||= RestCore::HttpClient
8
+ singleton_class.module_eval do
9
+ attr_writer :default_engine
10
+ def default_engine
11
+ @default_engine ||= RestCore::HttpClient
12
+ end
13
+
14
+ def client *attrs, &block
15
+ new(&block).to_client(*attrs)
16
+ end
17
+ end
18
+
19
+ def initialize &block
20
+ @engine = nil
21
+ @middles ||= []
22
+ instance_eval(&block) if block_given?
23
+ end
24
+
25
+ attr_reader :middles
26
+ attr_writer :default_engine
27
+ def default_engine
28
+ @default_engine ||= self.class.default_engine
12
29
  end
13
30
 
14
- def self.client *attrs, &block
15
- new(&block).to_client(*attrs)
31
+ def use middle, *args, &block
32
+ middles << [middle, args, block]
33
+ end
34
+
35
+ def run engine
36
+ @engine = engine
37
+ end
38
+
39
+ def members
40
+ middles.map{ |(middle, args, block)|
41
+ if middle.public_method_defined?(:wrapped)
42
+ # TODO: this is hacky... try to avoid calling new!
43
+ middle.members + middle.new(Dry.new, *args, &block).members
44
+ else
45
+ middle.members
46
+ end if middle.respond_to?(:members)
47
+ }.flatten.compact
48
+ end
49
+
50
+ def to_app engine=@engine || default_engine
51
+ # === foldr m.new app middles
52
+ middles.reverse.inject(engine.new){ |app, (middle, args, block)|
53
+ begin
54
+ middle.new(app, *partial_deep_copy(args), &block)
55
+ rescue ArgumentError => e
56
+ raise ArgumentError.new("#{middle}: #{e}")
57
+ end
58
+ }
16
59
  end
17
60
 
18
61
  def to_client *attrs
@@ -33,6 +76,16 @@ class RestCore::Builder
33
76
  client
34
77
  end
35
78
 
79
+ private
80
+ def partial_deep_copy obj
81
+ case obj
82
+ when Array; obj.map{ |o| partial_deep_copy(o) }
83
+ when Hash ; obj.inject({}){ |r, (k, v)| r[k] = partial_deep_copy(v); r }
84
+ when Numeric, Symbol, TrueClass, FalseClass, NilClass; obj
85
+ else begin obj.dup; rescue TypeError; obj; end
86
+ end
87
+ end
88
+
36
89
  def build_struct fields
37
90
  if fields.empty?
38
91
  Struct.new(nil)
@@ -89,10 +142,4 @@ class RestCore::Builder
89
142
  end
90
143
  end
91
144
  end
92
-
93
- def initialize &block
94
- @engine = nil
95
- @middles ||= []
96
- instance_eval(&block) if block_given?
97
- end
98
145
  end
@@ -9,17 +9,18 @@ module RestCore
9
9
  use DefaultPayload, {}
10
10
  use JsonRequest , false
11
11
  use AuthBasic , nil, nil
12
+ use CommonLogger , method(:puts)
13
+ use ErrorHandler , nil
14
+ use ErrorDetectorHttp
15
+
16
+ use SmashResponse , false
17
+ use ClashResponse , false
18
+ use JsonResponse , false
19
+ use QueryResponse , false
20
+
21
+ use Cache , {}, 600 # default :expires_in 600 but the default
22
+ # cache {} didn't support it
12
23
 
13
24
  use FollowRedirect, 10
14
- use CommonLogger , method(:puts)
15
- use Cache , {}, 600 do # default :expires_in 600 but the default
16
- # cache {} didn't support it
17
- use ErrorHandler, nil
18
- use ErrorDetectorHttp
19
- use SmashResponse, false
20
- use ClashResponse, false
21
- use JsonResponse, false
22
- use QueryResponse, false
23
- end
24
25
  end
25
26
  end
@@ -10,6 +10,7 @@ module RestCore::Middleware
10
10
  mod.send(:attr_reader, :app)
11
11
  mem = if mod.respond_to?(:members) then mod.members else [] end
12
12
  src = mem.map{ |member| <<-RUBY }
13
+ attr_writer :#{member}
13
14
  def #{member} env
14
15
  if env.key?('#{member}')
15
16
  env['#{member}']
@@ -113,4 +114,23 @@ module RestCore::Middleware
113
114
  }
114
115
  end
115
116
  public :string_keys
117
+
118
+ # this method is intended to merge payloads if they are non-empty hashes,
119
+ # but prefer the right most one if they are not hashes.
120
+ def merge_hash *hashes
121
+ hashes.reverse_each.inject do |r, i|
122
+ if r.kind_of?(Hash)
123
+ if i.kind_of?(Hash)
124
+ Middleware.string_keys(i).merge(Middleware.string_keys(r))
125
+ elsif r.empty?
126
+ i # prefer non-empty ones
127
+ else
128
+ r # don't try to merge non-hashes
129
+ end
130
+ else
131
+ r
132
+ end
133
+ end
134
+ end
135
+ public :merge_hash
116
136
  end
@@ -1,14 +1,12 @@
1
1
 
2
2
  require 'rest-core/event'
3
3
  require 'rest-core/middleware'
4
- require 'rest-core/wrapper'
5
4
 
6
5
  require 'digest/md5'
7
6
 
8
7
  class RestCore::Cache
9
8
  def self.members; [:cache, :expires_in]; end
10
9
  include RestCore::Middleware
11
- include RestCore::Wrapper
12
10
 
13
11
  def initialize app, cache, expires_in, &block
14
12
  super(&block)
@@ -24,14 +22,18 @@ class RestCore::Cache
24
22
 
25
23
  cache_get(e){ |cached|
26
24
  e[TIMER].cancel if e[TIMER]
27
- wrapped.call(cached, &k)
28
- } || app.call(e){ |res|
29
- wrapped.call(res){ |res_wrapped|
30
- k.call(if (res_wrapped[FAIL] || []).empty?
31
- cache_for(res).merge(res_wrapped)
32
- else
33
- res_wrapped
34
- end)}}
25
+ k.call(cached)
26
+ } || app_call(e, &k)
27
+ end
28
+
29
+ def app_call env
30
+ app.call(env) do |res|
31
+ yield(if (res[FAIL] || []).empty?
32
+ cache_for(res)
33
+ else
34
+ res
35
+ end)
36
+ end
35
37
  end
36
38
 
37
39
  def cache_key env
@@ -5,7 +5,7 @@ class RestCore::DefaultHeaders
5
5
  def self.members; [:headers]; end
6
6
  include RestCore::Middleware
7
7
  def call env, &k
8
- app.call(env.merge(REQUEST_HEADERS =>
9
- @headers.merge(headers(env)).merge(env[REQUEST_HEADERS])), &k)
8
+ h = merge_hash(@headers, headers(env), env[REQUEST_HEADERS])
9
+ app.call(env.merge(REQUEST_HEADERS => h), &k)
10
10
  end
11
11
  end
@@ -4,32 +4,8 @@ require 'rest-core/middleware'
4
4
  class RestCore::DefaultPayload
5
5
  def self.members; [:payload]; end
6
6
  include RestCore::Middleware
7
-
8
- def initialize *args
9
- super
10
- @payload ||= {}
11
- end
12
-
13
7
  def call env, &k
14
- defaults = merge(@payload, payload(env))
15
-
16
- app.call(env.merge(REQUEST_PAYLOAD =>
17
- merge(defaults, env[REQUEST_PAYLOAD])), &k)
18
- end
19
-
20
- # this method is intended to merge payloads if they are non-empty hashes,
21
- # but prefer the right most one if they are not hashes.
22
- def merge lhs, rhs
23
- if rhs.respond_to?(:empty?) && rhs.empty?
24
- lhs
25
- elsif lhs.respond_to?(:merge)
26
- if rhs.respond_to?(:merge)
27
- string_keys(lhs).merge(string_keys(rhs))
28
- else
29
- rhs
30
- end
31
- else
32
- rhs
33
- end
8
+ p = merge_hash(@payload, payload(env), env[REQUEST_PAYLOAD])
9
+ app.call(env.merge(REQUEST_PAYLOAD => p), &k)
34
10
  end
35
11
  end
@@ -4,16 +4,8 @@ require 'rest-core/middleware'
4
4
  class RestCore::DefaultQuery
5
5
  def self.members; [:query]; end
6
6
  include RestCore::Middleware
7
-
8
- def initialize *args
9
- super
10
- @query ||= {}
11
- end
12
-
13
7
  def call env, &k
14
- defaults = string_keys(@query).merge(string_keys(query(env)))
15
-
16
- app.call(env.merge(REQUEST_QUERY =>
17
- defaults.merge(env[REQUEST_QUERY])), &k)
8
+ q = merge_hash(@query, query(env), env[REQUEST_QUERY])
9
+ app.call(env.merge(REQUEST_QUERY => q), &k)
18
10
  end
19
11
  end
@@ -10,7 +10,8 @@ class RestCore::JsonRequest
10
10
 
11
11
  def call env, &k
12
12
  return app.call(env, &k) unless json_request(env)
13
- return app.call(env, &k) unless env[REQUEST_PAYLOAD]
13
+ return app.call(env, &k) unless env[REQUEST_PAYLOAD] &&
14
+ !env[REQUEST_PAYLOAD].empty?
14
15
 
15
16
  app.call(env.merge(
16
17
  REQUEST_HEADERS => JSON_REQUEST_HEADER.merge(env[REQUEST_HEADERS]||{}),
@@ -9,7 +9,8 @@ class RestCore::JsonResponse
9
9
  class ParseError < Json.const_get(:ParseError)
10
10
  attr_reader :cause, :body
11
11
  def initialize cause, body
12
- super("#{cause.message}\nOriginal text: #{body}")
12
+ msg = cause.message.force_encoding('utf-8')
13
+ super("#{msg}\nOriginal text: #{body}")
13
14
  @cause, @body = cause, body
14
15
  end
15
16
  end
@@ -27,7 +28,9 @@ class RestCore::JsonResponse
27
28
  end
28
29
 
29
30
  def process response
30
- body = response[RESPONSE_BODY]
31
+ # StackExchange returns the problematic BOM! in UTF-8, so we need to
32
+ # strip it or it would break JSON parsers (i.e. yajl-ruby and json)
33
+ body = response[RESPONSE_BODY].to_s.sub(/\A\xEF\xBB\xBF/, '')
31
34
  response.merge(RESPONSE_BODY => Json.decode("[#{body}]").first)
32
35
  # [this].first is not needed for yajl-ruby
33
36
  rescue Json.const_get(:ParseError) => error
@@ -1,26 +1,17 @@
1
1
 
2
2
  require 'rest-core'
3
3
 
4
- require 'webmock'
4
+ require 'pork/auto'
5
5
  require 'muack'
6
- require 'bacon'
6
+ require 'webmock'
7
7
 
8
8
  # for testing lighten (serialization)
9
9
  require 'yaml'
10
10
 
11
11
  WebMock.disable_net_connect!(:allow_localhost => true)
12
- Bacon.summary_on_exit
13
- Bacon::Context.send(:include, Muack::API, WebMock::API)
12
+ Pork::Executor.__send__(:include, Muack::API, WebMock::API)
14
13
 
15
14
  module Kernel
16
- def eq? rhs
17
- self == rhs
18
- end
19
-
20
- def lt? rhs
21
- self < rhs
22
- end
23
-
24
15
  def with_img
25
16
  f = Tempfile.new(['img', '.jpg'])
26
17
  n = File.basename(f.path)
@@ -1,4 +1,4 @@
1
1
 
2
2
  module RestCore
3
- VERSION = '3.2.0'
3
+ VERSION = '3.3.0'
4
4
  end
data/rest-core.gemspec CHANGED
@@ -1,14 +1,14 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: rest-core 3.2.0 ruby lib
2
+ # stub: rest-core 3.3.0 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "rest-core"
6
- s.version = "3.2.0"
6
+ s.version = "3.3.0"
7
7
 
8
8
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
9
9
  s.require_paths = ["lib"]
10
10
  s.authors = ["Lin Jen-Shin (godfat)"]
11
- s.date = "2014-06-27"
11
+ s.date = "2014-08-25"
12
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/godfat/rest-more"
13
13
  s.email = ["godfat (XD) godfat.org"]
14
14
  s.files = [
@@ -76,7 +76,6 @@ Gem::Specification.new do |s|
76
76
  "lib/rest-core/util/payload.rb",
77
77
  "lib/rest-core/util/smash.rb",
78
78
  "lib/rest-core/version.rb",
79
- "lib/rest-core/wrapper.rb",
80
79
  "rest-core.gemspec",
81
80
  "task/README.md",
82
81
  "task/gemgem.rb",
@@ -89,6 +88,7 @@ Gem::Specification.new do |s|
89
88
  "test/test_client.rb",
90
89
  "test/test_client_oauth1.rb",
91
90
  "test/test_config.rb",
91
+ "test/test_default_headers.rb",
92
92
  "test/test_default_payload.rb",
93
93
  "test/test_default_query.rb",
94
94
  "test/test_error_detector.rb",
@@ -111,11 +111,10 @@ Gem::Specification.new do |s|
111
111
  "test/test_smash_response.rb",
112
112
  "test/test_thread_pool.rb",
113
113
  "test/test_timeout.rb",
114
- "test/test_universal.rb",
115
- "test/test_wrapper.rb"]
114
+ "test/test_universal.rb"]
116
115
  s.homepage = "https://github.com/godfat/rest-core"
117
116
  s.licenses = ["Apache License 2.0"]
118
- s.rubygems_version = "2.2.2"
117
+ s.rubygems_version = "2.4.1"
119
118
  s.summary = "Modular Ruby clients interface for REST APIs."
120
119
  s.test_files = [
121
120
  "test/test_auth_basic.rb",
@@ -126,6 +125,7 @@ Gem::Specification.new do |s|
126
125
  "test/test_client.rb",
127
126
  "test/test_client_oauth1.rb",
128
127
  "test/test_config.rb",
128
+ "test/test_default_headers.rb",
129
129
  "test/test_default_payload.rb",
130
130
  "test/test_default_query.rb",
131
131
  "test/test_error_detector.rb",
@@ -148,8 +148,7 @@ Gem::Specification.new do |s|
148
148
  "test/test_smash_response.rb",
149
149
  "test/test_thread_pool.rb",
150
150
  "test/test_timeout.rb",
151
- "test/test_universal.rb",
152
- "test/test_wrapper.rb"]
151
+ "test/test_universal.rb"]
153
152
 
154
153
  if s.respond_to? :specification_version then
155
154
  s.specification_version = 4
@@ -157,15 +156,15 @@ Gem::Specification.new do |s|
157
156
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
158
157
  s.add_runtime_dependency(%q<httpclient>, [">= 0"])
159
158
  s.add_runtime_dependency(%q<mime-types>, [">= 0"])
160
- s.add_runtime_dependency(%q<timers>, [">= 0"])
159
+ s.add_runtime_dependency(%q<timers>, ["< 4"])
161
160
  else
162
161
  s.add_dependency(%q<httpclient>, [">= 0"])
163
162
  s.add_dependency(%q<mime-types>, [">= 0"])
164
- s.add_dependency(%q<timers>, [">= 0"])
163
+ s.add_dependency(%q<timers>, ["< 4"])
165
164
  end
166
165
  else
167
166
  s.add_dependency(%q<httpclient>, [">= 0"])
168
167
  s.add_dependency(%q<mime-types>, [">= 0"])
169
- s.add_dependency(%q<timers>, [">= 0"])
168
+ s.add_dependency(%q<timers>, ["< 4"])
170
169
  end
171
170
  end