acfs 1.0.0.dev.1.b305 → 1.0.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.
Files changed (89) hide show
  1. checksums.yaml +5 -13
  2. data/CHANGELOG.md +64 -0
  3. data/README.md +2 -2
  4. data/acfs.gemspec +4 -4
  5. data/lib/acfs.rb +7 -5
  6. data/lib/acfs/adapter/base.rb +0 -2
  7. data/lib/acfs/adapter/typhoeus.rb +17 -13
  8. data/lib/acfs/collections/paginatable.rb +10 -10
  9. data/lib/acfs/configuration.rb +4 -5
  10. data/lib/acfs/errors.rb +10 -9
  11. data/lib/acfs/global.rb +16 -7
  12. data/lib/acfs/location.rb +11 -11
  13. data/lib/acfs/middleware/base.rb +1 -2
  14. data/lib/acfs/middleware/json.rb +27 -0
  15. data/lib/acfs/middleware/logger.rb +0 -2
  16. data/lib/acfs/middleware/msgpack.rb +30 -0
  17. data/lib/acfs/middleware/print.rb +0 -2
  18. data/lib/acfs/middleware/serializer.rb +39 -0
  19. data/lib/acfs/operation.rb +5 -5
  20. data/lib/acfs/request.rb +4 -4
  21. data/lib/acfs/request/callbacks.rb +2 -3
  22. data/lib/acfs/resource.rb +2 -2
  23. data/lib/acfs/resource/attributes.rb +10 -37
  24. data/lib/acfs/resource/attributes/base.rb +10 -19
  25. data/lib/acfs/resource/attributes/boolean.rb +10 -8
  26. data/lib/acfs/resource/attributes/date_time.rb +6 -9
  27. data/lib/acfs/resource/attributes/dict.rb +37 -0
  28. data/lib/acfs/resource/attributes/float.rb +11 -5
  29. data/lib/acfs/resource/attributes/integer.rb +7 -5
  30. data/lib/acfs/resource/attributes/list.rb +13 -6
  31. data/lib/acfs/resource/attributes/string.rb +3 -5
  32. data/lib/acfs/resource/attributes/uuid.rb +8 -17
  33. data/lib/acfs/resource/loadable.rb +0 -1
  34. data/lib/acfs/resource/locatable.rb +0 -4
  35. data/lib/acfs/resource/operational.rb +0 -2
  36. data/lib/acfs/resource/persistence.rb +17 -17
  37. data/lib/acfs/resource/query_methods.rb +3 -5
  38. data/lib/acfs/resource/service.rb +0 -2
  39. data/lib/acfs/resource/validation.rb +0 -2
  40. data/lib/acfs/response.rb +1 -2
  41. data/lib/acfs/response/formats.rb +1 -2
  42. data/lib/acfs/response/status.rb +3 -5
  43. data/lib/acfs/runner.rb +21 -11
  44. data/lib/acfs/service.rb +4 -6
  45. data/lib/acfs/service/middleware.rb +20 -30
  46. data/lib/acfs/service/middleware/stack.rb +63 -0
  47. data/lib/acfs/singleton_resource.rb +0 -2
  48. data/lib/acfs/stub.rb +30 -21
  49. data/lib/acfs/util.rb +1 -1
  50. data/lib/acfs/version.rb +4 -2
  51. data/spec/acfs/adapter/typhoeus_spec.rb +12 -4
  52. data/spec/acfs/collection_spec.rb +45 -33
  53. data/spec/acfs/configuration_spec.rb +9 -1
  54. data/spec/acfs/global_spec.rb +21 -3
  55. data/spec/acfs/middleware/json_spec.rb +63 -0
  56. data/spec/acfs/middleware/msgpack_spec.rb +60 -0
  57. data/spec/acfs/operation_spec.rb +10 -0
  58. data/spec/acfs/request/callbacks_spec.rb +8 -8
  59. data/spec/acfs/request_spec.rb +5 -5
  60. data/spec/acfs/resource/attributes/boolean_spec.rb +40 -9
  61. data/spec/acfs/resource/attributes/date_time_spec.rb +29 -35
  62. data/spec/acfs/resource/attributes/dict_spec.rb +75 -0
  63. data/spec/acfs/resource/attributes/float_spec.rb +48 -9
  64. data/spec/acfs/resource/attributes/integer_spec.rb +34 -0
  65. data/spec/acfs/resource/attributes/list_spec.rb +43 -19
  66. data/spec/acfs/resource/attributes/uuid_spec.rb +31 -54
  67. data/spec/acfs/resource/attributes_spec.rb +17 -31
  68. data/spec/acfs/resource/locatable_spec.rb +2 -2
  69. data/spec/acfs/resource/persistance_spec.rb +65 -34
  70. data/spec/acfs/resource/query_methods_spec.rb +87 -87
  71. data/spec/acfs/resource/validation_spec.rb +4 -5
  72. data/spec/acfs/response/formats_spec.rb +1 -1
  73. data/spec/acfs/response/status_spec.rb +1 -1
  74. data/spec/acfs/runner_spec.rb +28 -3
  75. data/spec/acfs/service/middleware_spec.rb +4 -20
  76. data/spec/acfs/service_spec.rb +3 -5
  77. data/spec/acfs/singleton_resource_spec.rb +1 -2
  78. data/spec/acfs/stub_spec.rb +137 -22
  79. data/spec/acfs_spec.rb +22 -19
  80. data/spec/spec_helper.rb +2 -1
  81. data/spec/support/service.rb +10 -6
  82. data/spec/support/shared/find_callbacks.rb +7 -7
  83. metadata +37 -30
  84. data/lib/acfs/middleware/json_decoder.rb +0 -16
  85. data/lib/acfs/middleware/json_encoder.rb +0 -20
  86. data/lib/acfs/middleware/msgpack_decoder.rb +0 -26
  87. data/lib/acfs/middleware/msgpack_encoder.rb +0 -19
  88. data/spec/acfs/middleware/json_decoder_spec.rb +0 -45
  89. data/spec/acfs/middleware/msgpack_decoder_spec.rb +0 -36
@@ -1,7 +1,6 @@
1
1
  require 'acfs/service/middleware'
2
2
 
3
3
  module Acfs
4
-
5
4
  # User {Acfs::Service} to define your services. That includes
6
5
  # an identity used to identify the service in configuration files
7
6
  # and middlewares the service uses.
@@ -35,14 +34,14 @@ module Acfs
35
34
  # @return [Location]
36
35
  #
37
36
  def location(resource_class, opts = {})
38
- opts.reverse_merge! self.options
37
+ opts.reverse_merge! options
39
38
 
40
39
  action = opts[:action] || :list
41
40
 
42
- path = if Hash === opts[:path] && opts[:path].has_key?(action)
41
+ path = if opts[:path].is_a?(Hash) && opts[:path].key?(action)
43
42
  opts[:path].fetch(action)
44
43
  else
45
- path = if Hash === opts[:path]
44
+ path = if opts[:path].is_a?(Hash)
46
45
  opts[:path][:all].to_s
47
46
  else
48
47
  opts[:path].to_s
@@ -59,7 +58,6 @@ module Acfs
59
58
  end
60
59
 
61
60
  class << self
62
-
63
61
  # @api public
64
62
  #
65
63
  # @overload identity()
@@ -83,7 +81,7 @@ module Acfs
83
81
  #
84
82
  def base_url
85
83
  unless (base = Acfs::Configuration.current.locate identity)
86
- raise ArgumentError, "#{identity} not configured. Add `locate '#{identity.to_s.underscore}', 'http://service.url/'` to your configuration."
84
+ raise ArgumentError.new "#{identity} not configured. Add `locate '#{identity.to_s.underscore}', 'http://service.url/'` to your configuration."
87
85
  end
88
86
 
89
87
  base.to_s
@@ -1,6 +1,7 @@
1
+ require 'acfs/service/middleware/stack'
2
+
1
3
  module Acfs
2
4
  class Service
3
-
4
5
  # Module providing all function to register middlewares
5
6
  # on services and process queued request through the
6
7
  # middleware stack.
@@ -16,28 +17,25 @@ module Acfs
16
17
  end
17
18
 
18
19
  module ClassMethods
19
-
20
- # @api public
20
+ # @!method use(klass, *args, &block)
21
+ # @api public
21
22
  #
22
- # Register a new middleware to be used for this service.
23
+ # Register a new middleware to be used for this service.
23
24
  #
24
- # @example
25
- # class MyService < Acfs::Service
26
- # self.base_url = 'http://my.srv'
27
- # use Acfs::Middleware::JsonDecoder
28
- # end
25
+ # @example
26
+ # class MyService < Acfs::Service
27
+ # self.base_url = 'http://my.srv'
28
+ # use Acfs::Middleware::JSON
29
+ # end
29
30
  #
30
- # @param [Class] klass Middleware class to instantiate and append to middleware stack.
31
- # @param [Hash, Object] options Options to delegate to middleware class initializer.
32
- # @return [undefined]
31
+ # @param [Class] klass Middleware class to append
32
+ # @param [Array<Object>] args Arguments passed to klass initialize
33
+ # @param [Proc] block Block passed to klass initialize
34
+ # @return [undefined]
33
35
  #
34
- def use(klass, options = {})
35
- @middlewares ||= []
36
-
37
- return false if @middlewares.include? klass
38
-
39
- @middlewares << klass
40
- @middleware = klass.new(middleware, options)
36
+ def use(klass, *args, &block)
37
+ # Backward compatible behavior
38
+ middleware.insert(0, klass, *args, &block)
41
39
  end
42
40
 
43
41
  # @api private
@@ -47,19 +45,11 @@ module Acfs
47
45
  # @return [#call]
48
46
  #
49
47
  def middleware
50
- @middleware ||= proc { |request| request}
48
+ @middleware ||= Stack.new
51
49
  end
52
50
 
53
- # @api public
54
- #
55
- # Clear all registered middlewares.
56
- #
57
- # @return [undefined]
58
- #
59
- def clear
60
- @middleware = nil
61
- @middlewares = []
62
- end
51
+ # @deprecated
52
+ delegate :clear, to: :middleware
63
53
  end
64
54
  end
65
55
  end
@@ -0,0 +1,63 @@
1
+ module Acfs
2
+ class Service
3
+ module Middleware
4
+ class Stack
5
+ include Enumerable
6
+
7
+ MUTEX = Mutex.new
8
+ IDENTITY = -> (i) { i }
9
+
10
+ attr_reader :middlewares
11
+
12
+ def initialize
13
+ @middlewares = []
14
+ end
15
+
16
+ def call(request)
17
+ build! unless @app
18
+
19
+ @app.call request
20
+ end
21
+
22
+ def build!
23
+ return if @app
24
+
25
+ MUTEX.synchronize do
26
+ return if @app
27
+
28
+ @app = build
29
+ end
30
+ end
31
+
32
+ def build(app = IDENTITY)
33
+ middlewares.reverse.inject(app) do |next_middleware, current_middleware|
34
+ klass, args, block = current_middleware
35
+ args ||= []
36
+
37
+ if klass.is_a?(Class)
38
+ klass.new(next_middleware, *args, &block)
39
+ elsif klass.respond_to?(:call)
40
+ lambda do |env|
41
+ next_middleware.call(klass.call(env, *args))
42
+ end
43
+ else
44
+ fail "Invalid middleware, doesn't respond to `call`: #{klass.inspect}"
45
+ end
46
+ end
47
+ end
48
+
49
+ def insert(index, klass, *args, &block)
50
+ middlewares.insert(index, [klass, args, block])
51
+ end
52
+
53
+ def each
54
+ middlewares.each { |x| yield x.first }
55
+ end
56
+
57
+ def clear
58
+ middlewares.clear
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,5 +1,4 @@
1
1
  module Acfs
2
-
3
2
  # Acfs SingletonResources
4
3
  #
5
4
  # Usage explanation:
@@ -14,7 +13,6 @@ module Acfs
14
13
  # always only a single instance of the resource is being returned
15
14
  #
16
15
  class SingletonResource < Acfs::Resource
17
-
18
16
  # @api public
19
17
  #
20
18
  # Destroy resource by sending a DELETE request.
@@ -1,7 +1,6 @@
1
1
  require 'rack/utils'
2
2
 
3
3
  module Acfs
4
-
5
4
  # Global handler for stubbing resources.
6
5
  #
7
6
  class Stub
@@ -14,7 +13,7 @@ module Acfs
14
13
 
15
14
  @opts[:with].stringify_keys! if @opts[:with].is_a? Hash
16
15
  @opts[:return].stringify_keys! if @opts[:return].is_a? Hash
17
- @opts[:return].map! { |h| h.stringify_keys! if h.is_a? Hash } if @opts[:return].is_a? Array
16
+ @opts[:return].map! {|h| h.stringify_keys! if h.is_a? Hash } if @opts[:return].is_a? Array
18
17
  end
19
18
 
20
19
  def accept?(op)
@@ -22,14 +21,21 @@ module Acfs
22
21
 
23
22
  params = op.full_params.stringify_keys
24
23
  data = op.data.stringify_keys
25
-
26
- return true if opts[:with] == params || data == opts[:with]
27
- return true if (opts[:with].nil? && params.empty? && data.empty?)
28
-
29
- return true if opts[:with].reject { |k, v| v.nil? } == params.reject { |k, v| v.nil? }
30
- return true if opts[:with].reject { |k, v| v.nil? } == data.reject { |k, v| v.nil? }
31
-
32
- false
24
+ with = opts[:with]
25
+
26
+ return true if with.nil?
27
+
28
+ case opts.fetch(:match, :inclusion)
29
+ when :legacy
30
+ return true if with.empty? && params.empty? && data.empty?
31
+ return true if with.reject {|_, v| v.nil? } == params.reject {|_, v| v.nil? }
32
+ return true if with.reject {|_, v| v.nil? } == data.reject {|_, v| v.nil? }
33
+ false
34
+ when :inclusion
35
+ with.each_pair.all? do |k, v|
36
+ (params.key?(k) && params[k] == v) || (data.key?(k) && data[k] == v)
37
+ end
38
+ end
33
39
  end
34
40
 
35
41
  def calls
@@ -50,17 +56,20 @@ module Acfs
50
56
  if err
51
57
  raise_error op, err, opts[:return]
52
58
  elsif data
59
+ data = data.call(op) if data.respond_to?(:call)
60
+
53
61
  response = Acfs::Response.new op.request,
54
- headers: opts[:headers] || {},
55
- status: opts[:status] || 200,
56
- data: data || {}
62
+ headers: opts[:headers] || {},
63
+ status: opts[:status] || 200,
64
+ data: data || {}
57
65
  op.call data, response
58
66
  else
59
- raise ArgumentError, 'Unsupported stub.'
67
+ raise ArgumentError.new 'Unsupported stub.'
60
68
  end
61
69
  end
62
70
 
63
71
  private
72
+
64
73
  def raise_error(op, name, data)
65
74
  raise name if name.is_a? Class
66
75
  data.stringify_keys! if data.respond_to? :stringify_keys!
@@ -69,13 +78,12 @@ module Acfs
69
78
  end
70
79
 
71
80
  class << self
72
-
73
81
  # Stub a resource with given handler block. An already created handler
74
82
  # for same resource class will be overridden.
75
83
  #
76
- def resource(klass, action, opts = {}, &block)
84
+ def resource(klass, action, opts = {}, &_block)
77
85
  action = action.to_sym
78
- raise ArgumentError, "Unknown action `#{action}`." unless ACTIONS.include? action
86
+ raise ArgumentError.new "Unknown action `#{action}`." unless ACTIONS.include? action
79
87
 
80
88
  Stub.new(opts).tap do |stub|
81
89
  stubs[klass] ||= {}
@@ -96,11 +104,11 @@ module Acfs
96
104
  @enabled ||= false
97
105
  end
98
106
 
99
- def enable;
107
+ def enable
100
108
  @enabled = true
101
109
  end
102
110
 
103
- def disable;
111
+ def disable
104
112
  @enabled = false
105
113
  end
106
114
 
@@ -118,7 +126,7 @@ module Acfs
118
126
  return false unless (classes = stubs[op.resource])
119
127
  return false unless (stubs = classes[op.action])
120
128
 
121
- accepted_stubs = stubs.select { |stub| stub.accept? op }
129
+ accepted_stubs = stubs.select {|stub| stub.accept? op }
122
130
 
123
131
  raise AmbiguousStubError.new stubs: accepted_stubs, operation: op if accepted_stubs.size > 1
124
132
 
@@ -142,8 +150,9 @@ module Acfs
142
150
  end
143
151
 
144
152
  private
153
+
145
154
  def pretty_print
146
- out = String.new
155
+ out = ''
147
156
  stubs.each do |klass, actions|
148
157
  out << ' ' << klass.name << ":\n"
149
158
  actions.each do |action, stubs|
@@ -7,7 +7,7 @@ module Acfs
7
7
  end
8
8
 
9
9
  def __invoke__
10
- __callbacks__.each{|c| c.call self}
10
+ __callbacks__.each {|c| c.call self }
11
11
  end
12
12
  end
13
13
 
@@ -3,10 +3,12 @@ module Acfs
3
3
  MAJOR = 1
4
4
  MINOR = 0
5
5
  PATCH = 0
6
- STAGE = 'dev'
6
+ STAGE = nil
7
7
 
8
8
  STRING = [MAJOR, MINOR, PATCH, STAGE].reject(&:nil?).join('.')
9
9
 
10
- def self.to_s; STRING end
10
+ def self.to_s
11
+ STRING
12
+ end
11
13
  end
12
14
  end
@@ -5,16 +5,24 @@ describe Acfs::Adapter::Typhoeus do
5
5
  before { WebMock.allow_net_connect! }
6
6
 
7
7
  it 'raises an error' do
8
- request1 = Acfs::Request.new 'http://altimos.de/404.1' do |rsp|
8
+ request1 = Acfs::Request.new 'http://altimos.de/404.1' do |_rsp|
9
9
  raise '404-1'
10
10
  end
11
- request2 = Acfs::Request.new 'http://altimos.de/404.2' do |rsp|
11
+ request2 = Acfs::Request.new 'http://altimos.de/404.2' do |_rsp|
12
12
  raise '404-2'
13
13
  end
14
14
  adapter.queue request1
15
15
  adapter.queue request2
16
16
 
17
- expect{ adapter.start }.to raise_error /404\-[12]/
18
- expect{ adapter.start }.to_not raise_error
17
+ expect { adapter.start }.to raise_error(/404\-[12]/)
18
+ expect { adapter.start }.to_not raise_error
19
+ end
20
+
21
+ it 'passes arguments to typhoeus hydra' do
22
+ value = {key: 1, key2: 2}
23
+
24
+ expect(::Typhoeus::Hydra).to receive(:new).with(value)
25
+
26
+ described_class.new(**value).send :hydra
19
27
  end
20
28
  end
@@ -11,47 +11,59 @@ describe Acfs::Collection do
11
11
 
12
12
  context 'without explicit page parameter' do
13
13
  before do
14
- stub_request(:get, 'http://users.example.org/users').
15
- to_return response([{id: 1, name: 'Anon', age: 12, born_at: 'Berlin'}],
16
- headers: {'X-Total-Pages' => '2'})
14
+ stub_request(:get, 'http://users.example.org/users')
15
+ .to_return response([{id: 1, name: 'Anon', age: 12, born_at: 'Berlin'}],
16
+ headers: {
17
+ 'X-Total-Pages' => '2',
18
+ 'X-Total-Count' => '10'
19
+ })
17
20
  end
18
21
 
19
22
  its(:total_pages) { should eq 2 }
20
23
  its(:current_page) { should eq 1 }
24
+ its(:total_count) { should eq 10 }
21
25
  end
22
26
 
23
27
  context 'with page parameter' do
24
28
  let(:params) { {page: 2} }
25
29
  before do
26
- stub_request(:get, 'http://users.example.org/users?page=2').
27
- to_return response([{id: 1, name: 'Anon', age: 12, born_at: 'Berlin'}],
28
- headers: {'X-Total-Pages' => '2'})
30
+ stub_request(:get, 'http://users.example.org/users?page=2')
31
+ .to_return response([{id: 1, name: 'Anon', age: 12, born_at: 'Berlin'}],
32
+ headers: {
33
+ 'X-Total-Pages' => '2',
34
+ 'X-Total-Count' => '10'
35
+ })
29
36
  end
30
37
 
31
38
  its(:total_pages) { should eq 2 }
32
39
  its(:current_page) { should eq 2 }
40
+ its(:total_count) { should eq 10 }
33
41
  end
34
42
 
35
43
  context 'with non-numerical page parameter' do
36
44
  let(:params) { {page: 'e546f5'} }
37
45
  before do
38
- stub_request(:get, 'http://users.example.org/users?page=e546f5').
39
- to_return response([{id: 1, name: 'Anon', age: 12, born_at: 'Berlin'}],
40
- headers: {'X-Total-Pages' => '2'})
46
+ stub_request(:get, 'http://users.example.org/users?page=e546f5')
47
+ .to_return response([{id: 1, name: 'Anon', age: 12, born_at: 'Berlin'}],
48
+ headers: {
49
+ 'X-Total-Pages' => '2',
50
+ 'X-Total-Count' => '10'
51
+ })
41
52
  end
42
53
 
43
54
  its(:total_pages) { should eq 2 }
44
55
  its(:current_page) { should eq 'e546f5' }
56
+ its(:total_count) { should eq 10 }
45
57
  end
46
58
 
47
59
  describe '#next_page' do
48
60
  before do
49
- stub_request(:get, 'http://users.example.org/users').
50
- to_return response([{id: 1, name: 'Anon', age: 12, born_at: 'Berlin'}],
51
- headers: {
52
- 'X-Total-Pages' => '2',
53
- 'Link' => '<http://users.example.org/users?page=2>; rel="next"'
54
- })
61
+ stub_request(:get, 'http://users.example.org/users')
62
+ .to_return response([{id: 1, name: 'Anon', age: 12, born_at: 'Berlin'}],
63
+ headers: {
64
+ 'X-Total-Pages' => '2',
65
+ 'Link' => '<http://users.example.org/users?page=2>; rel="next"'
66
+ })
55
67
  end
56
68
  let!(:req) do
57
69
  stub_request(:get, 'http://users.example.org/users?page=2').to_return response([])
@@ -70,12 +82,12 @@ describe Acfs::Collection do
70
82
 
71
83
  describe '#prev_page' do
72
84
  before do
73
- stub_request(:get, 'http://users.example.org/users?page=2').
74
- to_return response([{id: 2, name: 'Anno', age: 1604, born_at: 'Santa Maria'}],
75
- headers: {
76
- 'X-Total-Pages' => '2',
77
- 'Link' => '<http://users.example.org/users>; rel="prev"'
78
- })
85
+ stub_request(:get, 'http://users.example.org/users?page=2')
86
+ .to_return response([{id: 2, name: 'Anno', age: 1604, born_at: 'Santa Maria'}],
87
+ headers: {
88
+ 'X-Total-Pages' => '2',
89
+ 'Link' => '<http://users.example.org/users>; rel="prev"'
90
+ })
79
91
  end
80
92
  let!(:req) do
81
93
  stub_request(:get, 'http://users.example.org/users').to_return response([])
@@ -94,12 +106,12 @@ describe Acfs::Collection do
94
106
 
95
107
  describe '#first_page' do
96
108
  before do
97
- stub_request(:get, 'http://users.example.org/users?page=2').
98
- to_return response([{id: 2, name: 'Anno', age: 1604, born_at: 'Santa Maria'}],
99
- headers: {
100
- 'X-Total-Pages' => '2',
101
- 'Link' => '<http://users.example.org/users>; rel="first"'
102
- })
109
+ stub_request(:get, 'http://users.example.org/users?page=2')
110
+ .to_return response([{id: 2, name: 'Anno', age: 1604, born_at: 'Santa Maria'}],
111
+ headers: {
112
+ 'X-Total-Pages' => '2',
113
+ 'Link' => '<http://users.example.org/users>; rel="first"'
114
+ })
103
115
  end
104
116
  let!(:req) do
105
117
  stub_request(:get, 'http://users.example.org/users').to_return response([])
@@ -118,12 +130,12 @@ describe Acfs::Collection do
118
130
 
119
131
  describe '#last_page' do
120
132
  before do
121
- stub_request(:get, 'http://users.example.org/users?page=2').
122
- to_return response([{id: 2, name: 'Anno', age: 1604, born_at: 'Santa Maria'}],
123
- headers: {
124
- 'X-Total-Pages' => '2',
125
- 'Link' => '<http://users.example.org/users?page=12>; rel="last"'
126
- })
133
+ stub_request(:get, 'http://users.example.org/users?page=2')
134
+ .to_return response([{id: 2, name: 'Anno', age: 1604, born_at: 'Santa Maria'}],
135
+ headers: {
136
+ 'X-Total-Pages' => '2',
137
+ 'Link' => '<http://users.example.org/users?page=12>; rel="last"'
138
+ })
127
139
  end
128
140
  let!(:req) do
129
141
  stub_request(:get, 'http://users.example.org/users?page=12').to_return response([])