couchmodel 0.1.0.beta3 → 0.1.0.beta4

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 (36) hide show
  1. data/README.rdoc +22 -7
  2. data/lib/core_extension/array.rb +18 -2
  3. data/lib/core_extension/string.rb +12 -2
  4. data/lib/couch_model/active_model.rb +31 -8
  5. data/lib/couch_model/base/accessor.rb +8 -5
  6. data/lib/couch_model/base/association.rb +36 -15
  7. data/lib/couch_model/base/finder.rb +1 -0
  8. data/lib/couch_model/base/setup.rb +6 -1
  9. data/lib/couch_model/base.rb +55 -17
  10. data/lib/couch_model/collection.rb +41 -21
  11. data/lib/couch_model/configuration.rb +42 -46
  12. data/lib/couch_model/database.rb +2 -2
  13. data/lib/couch_model/design.rb +18 -14
  14. data/lib/couch_model/row.rb +34 -0
  15. data/lib/couch_model/server.rb +2 -2
  16. data/lib/couch_model/transport.rb +55 -34
  17. data/lib/couch_model/view.rb +9 -5
  18. data/spec/fake_transport.yml +84 -29
  19. data/spec/fake_transport_helper.rb +5 -3
  20. data/spec/integration/basic_spec.rb +26 -12
  21. data/spec/integration/design/membership.design +1 -1
  22. data/spec/integration/design/user.design +12 -0
  23. data/spec/lib/core_extension/array_spec.rb +24 -0
  24. data/spec/lib/couch_model/active_model_spec.rb +51 -0
  25. data/spec/lib/couch_model/base_spec.rb +30 -1
  26. data/spec/lib/couch_model/collection_spec.rb +31 -7
  27. data/spec/lib/couch_model/configuration_spec.rb +2 -2
  28. data/spec/lib/couch_model/core/accessor_spec.rb +11 -3
  29. data/spec/lib/couch_model/core/association_spec.rb +26 -0
  30. data/spec/lib/couch_model/core/setup_spec.rb +8 -0
  31. data/spec/lib/couch_model/design_spec.rb +1 -5
  32. data/spec/lib/couch_model/row_spec.rb +71 -0
  33. data/spec/lib/couch_model/transport_spec.rb +18 -1
  34. data/spec/lib/couch_model/view_spec.rb +2 -2
  35. data/spec/spec_helper.rb +1 -1
  36. metadata +6 -3
@@ -1,70 +1,66 @@
1
1
 
2
2
  module CouchModel
3
3
 
4
- class Configuration
4
+ module Configuration
5
5
 
6
6
  CLASS_KEY = "model_class".freeze unless defined?(CLASS_KEY)
7
7
  CLASS_VIEW_NAME = "all".freeze unless defined?(CLASS_VIEW_NAME)
8
8
 
9
- class << self
9
+ @fake_transport = false
10
+ @databases = [ ]
11
+ @designs = [ ]
10
12
 
11
- @@fake_transport = false
12
- @@databases = [ ]
13
- @@designs = [ ]
14
-
15
- def fake_transport=(value)
16
- @@fake_transport = value
17
- end
13
+ def self.fake_transport=(value)
14
+ @fake_transport = value
15
+ end
18
16
 
19
- def fake_transport
20
- @@fake_transport
21
- end
17
+ def self.fake_transport
18
+ @fake_transport
19
+ end
22
20
 
23
- def design_directory=(value)
24
- @@design_directory = value
25
- end
21
+ def self.design_directory=(value)
22
+ @design_directory = value
23
+ end
26
24
 
27
- def design_directory
28
- class_variable_defined?(:@@design_directory) ? @@design_directory : ""
29
- end
25
+ def self.design_directory
26
+ instance_variable_defined?(:@design_directory) ? @design_directory : ""
27
+ end
30
28
 
31
- def register_database(database)
32
- result = @@databases.select{ |element| element == database }.first
33
- unless result
34
- @@databases << database
35
- result = database
36
- end
37
- result
29
+ def self.register_database(database)
30
+ result = @databases.select{ |element| element == database }.first
31
+ unless result
32
+ @databases << database
33
+ result = database
38
34
  end
35
+ result
36
+ end
39
37
 
40
- def databases
41
- @@databases
42
- end
38
+ def self.databases
39
+ @databases
40
+ end
43
41
 
44
- def setup_databases(options = { })
45
- delete_if_exists = options[:delete_if_exists] || false
46
- create_if_missing = options[:create_if_missing] || false
42
+ def self.setup_databases(options = { })
43
+ delete_if_exists = options[:delete_if_exists] || false
44
+ create_if_missing = options[:create_if_missing] || false
47
45
 
48
- @@databases.each do |database|
49
- database.delete_if_exists! if delete_if_exists
50
- database.create_if_missing! if create_if_missing
51
- end
46
+ @databases.each do |database|
47
+ database.delete_if_exists! if delete_if_exists
48
+ database.create_if_missing! if create_if_missing
52
49
  end
50
+ end
53
51
 
54
- def register_design(design)
55
- @@designs << design
56
- end
52
+ def self.register_design(design)
53
+ @designs << design
54
+ end
57
55
 
58
- def designs
59
- @@designs
60
- end
56
+ def self.designs
57
+ @designs
58
+ end
61
59
 
62
- def setup_designs
63
- @@designs.each do |design|
64
- design.push
65
- end
60
+ def self.setup_designs
61
+ @designs.each do |design|
62
+ design.push
66
63
  end
67
-
68
64
  end
69
65
 
70
66
  end
@@ -4,10 +4,10 @@ require File.join(File.dirname(__FILE__), "collection")
4
4
 
5
5
  module CouchModel
6
6
 
7
+ # The Database class provides methods create, delete and retrieve informations
8
+ # of a CouchDB database.
7
9
  class Database
8
10
 
9
- class Error < StandardError; end
10
-
11
11
  attr_reader :server
12
12
  attr_reader :name
13
13
 
@@ -6,6 +6,7 @@ require 'yaml'
6
6
 
7
7
  module CouchModel
8
8
 
9
+ # The Design class acts as a wrapper for CouchDB design documents.
9
10
  class Design
10
11
 
11
12
  attr_reader :database
@@ -22,9 +23,9 @@ module CouchModel
22
23
  @views = [ ]
23
24
 
24
25
  load_file
25
- self.id = attributes[:id] if attributes[:id]
26
- self.language = attributes[:language] if attributes[:language]
27
- self.views = attributes[:views] if attributes[:views]
26
+ self.id = attributes[:id] if attributes.has_key?(:id)
27
+ self.language = attributes[:language] if attributes.has_key?(:language)
28
+ self.views = attributes[:views] if attributes.has_key?(:views)
28
29
  end
29
30
 
30
31
  def filename
@@ -32,34 +33,33 @@ module CouchModel
32
33
  end
33
34
 
34
35
  def load_file
35
- return false unless File.exists?(filename)
36
- attributes = YAML::load_file filename
37
- self.id = attributes[:id]
38
- self.language = attributes[:language]
39
- self.views = attributes[:views]
36
+ self.id, self.language, self.views = YAML::load_file(self.filename).values_at(:id, :language, :views)
40
37
  true
38
+ rescue Errno::ENOENT
39
+ false
41
40
  end
42
41
 
43
42
  def views=(view_hash)
44
43
  @views = [ ]
45
44
  view_hash.each do |view_name, view|
46
- @views << View.new(self, view.merge(:name => view_name)) if view.is_a?(Hash)
45
+ @views << View.new(self, view.merge(:name => view_name.to_s)) if view.is_a?(Hash)
47
46
  end if view_hash.is_a?(Hash)
48
47
  end
49
48
 
50
49
  def generate_view(name, options = { })
51
- view = View.new self, options.merge(:name => name)
50
+ view = View.new self, options.merge(:name => name.to_s)
52
51
  @views.insert 0, view
53
52
  view
54
53
  end
55
54
 
56
55
  def to_hash
56
+ rev = self.rev
57
57
  hash = {
58
58
  "_id" => "_design/#{self.id}",
59
59
  "language" => self.language,
60
60
  "views" => { }
61
61
  }
62
- hash.merge! "_rev" => self.rev if self.rev
62
+ hash.merge! "_rev" => rev if rev
63
63
  @views.each { |view| hash["views"].merge! view.to_hash }
64
64
  hash
65
65
  end
@@ -72,10 +72,10 @@ module CouchModel
72
72
  end
73
73
 
74
74
  def push
75
- response = Transport.request :get, self.url
76
- self.rev = response["_rev"] if response["_rev"]
75
+ url = self.url
76
+ evaluate Transport.request(:get, url)
77
77
 
78
- Transport.request :put, self.url, :json => self.to_hash, :expected_status_code => 201
78
+ Transport.request :put, url, :body => self.to_hash, :expected_status_code => 201
79
79
  true
80
80
  end
81
81
 
@@ -87,6 +87,10 @@ module CouchModel
87
87
 
88
88
  attr_writer :rev
89
89
 
90
+ def evaluate(response)
91
+ self.rev = response["_rev"] if response.has_key?("_rev")
92
+ end
93
+
90
94
  end
91
95
 
92
96
  end
@@ -0,0 +1,34 @@
1
+ require File.join(File.dirname(__FILE__), "configuration")
2
+
3
+ module CouchModel
4
+
5
+ # The Row class acts as a wrapper for a CouchDB view result row.
6
+ class Row
7
+
8
+ attr_reader :id
9
+ attr_reader :key
10
+ attr_reader :value
11
+ attr_reader :document
12
+
13
+ def initialize(attributes = { })
14
+ @id, @key, @value, @document = attributes.values_at "id", "key", "value", "doc"
15
+ end
16
+
17
+ def model
18
+ return nil unless @document && @document.has_key?(Configuration::CLASS_KEY)
19
+
20
+ model_class_name = document[Configuration::CLASS_KEY]
21
+ raise StandardError, "no class defined with name [#{model_class_name}]" unless Object.const_defined?(model_class_name)
22
+ instanciate_model model_class_name
23
+ end
24
+
25
+ def instanciate_model(model_class_name)
26
+ model_class = Object.const_get model_class_name
27
+ model = model_class.new
28
+ model.instance_variable_set :@attributes, @document
29
+ model
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -2,10 +2,10 @@ require File.join(File.dirname(__FILE__), "transport")
2
2
 
3
3
  module CouchModel
4
4
 
5
+ # The Server class provides methods to retrieve informations and statistics
6
+ # of a CouchDB server.
5
7
  class Server
6
8
 
7
- class Error < StandardError; end
8
-
9
9
  attr_reader :host
10
10
  attr_reader :port
11
11
 
@@ -6,8 +6,8 @@ module CouchModel
6
6
 
7
7
  module Transport
8
8
 
9
- class Error < StandardError; end
10
-
9
+ # The UnexpectedStatusCodeError is raised if the :expected_status_code option is given to
10
+ # the :request method and the responded status code is different from the expected one.
11
11
  class UnexpectedStatusCodeError < StandardError
12
12
 
13
13
  attr_reader :status_code
@@ -22,47 +22,68 @@ module CouchModel
22
22
 
23
23
  end
24
24
 
25
- class << self
25
+ def self.request(http_method, url, options = { })
26
+ expected_status_code = options[:expected_status_code]
26
27
 
27
- def request(http_method, url, options = { })
28
- expected_status_code = options[:expected_status_code]
29
-
30
- uri = URI.parse @base_url ? @base_url + url : url
28
+ uri = URI.parse url
29
+ response = perform request_object(http_method, uri, options), uri
31
30
 
32
- request_class = request_class http_method
33
- request = request_object request_class, uri, options
31
+ check_status_code response, expected_status_code if expected_status_code
32
+ parse response
33
+ end
34
34
 
35
- response = Net::HTTP.start(uri.host, uri.port) { |connection| connection.request request }
35
+ def self.request_object(http_method, uri, options)
36
+ raise NotImplementedError, "the request method #{http_method} is not implemented" unless
37
+ self.respond_to?(:"#{http_method}_request_object")
36
38
 
37
- raise UnexpectedStatusCodeError, response.code.to_i if expected_status_code && expected_status_code.to_s != response.code
38
- JSON.parse response.body
39
- end
39
+ request_object = send :"#{http_method}_request_object", uri.path, (options[:parameters] || { }), (options[:headers] || { })
40
+ request_object.body = options[:body].to_json if options.has_key?(:body)
41
+ request_object
42
+ end
40
43
 
41
- private
44
+ def self.get_request_object(path, parameters, headers)
45
+ Net::HTTP::Get.new path + serialize_parameters(parameters),
46
+ headers
47
+ end
42
48
 
43
- def request_class(http_method)
44
- Net::HTTP.const_get http_method.capitalize
45
- end
49
+ def self.post_request_object(path, parameters, headers)
50
+ Net::HTTP::Post.new path + serialize_parameters(parameters),
51
+ { "Content-Type" => "application/json" }.merge(headers)
52
+ end
53
+
54
+ def self.put_request_object(path, parameters, headers)
55
+ Net::HTTP::Put.new path + serialize_parameters(parameters),
56
+ { "Content-Type" => "application/json" }.merge(headers)
57
+ end
46
58
 
47
- def request_object(request_class, uri, options)
48
- parameters = options[:parameters] || { }
49
- json = options[:json]
50
-
51
- case request_class.to_s
52
- when "Net::HTTP::Get", "Net::HTTP::Delete"
53
- request_class.new uri.path +
54
- (parameters.empty? ? "" : "?" + parameters.collect{ |key, value| "#{key}=#{URI.escape(value.to_s)}" }.reverse.join("&"))
55
- when "Net::HTTP::Post", "Net::HTTP::Put"
56
- request = request_class.new uri.path, { "Content-Type" => "application/json" }
57
- request.body = JSON.dump(json) if json
58
- request
59
- else
60
- request_class.new uri.path
61
- end
59
+ def self.delete_request_object(path, parameters, headers)
60
+ Net::HTTP::Delete.new path + serialize_parameters(parameters),
61
+ headers
62
+ end
63
+
64
+ def self.serialize_parameters(parameters)
65
+ return "" if parameters.empty?
66
+ "?" + parameters.collect do |key, value|
67
+ value = value.respond_to?(:to_json) ? value.to_json : value.to_s
68
+ "#{key}=#{URI.escape(value)}"
69
+ end.reverse.join("&")
70
+ end
71
+
72
+ def self.perform(request, uri)
73
+ Net::HTTP.start(uri.host, uri.port) do |connection|
74
+ connection.request request
62
75
  end
76
+ end
77
+
78
+ def self.check_status_code(response, expected_status_code)
79
+ response_code = response.code
80
+ raise UnexpectedStatusCodeError, response_code.to_i if expected_status_code.to_s != response_code
81
+ end
63
82
 
83
+ def self.parse(response)
84
+ JSON.parse response.body
64
85
  end
65
-
86
+
66
87
  end
67
88
 
68
- end
89
+ end
@@ -3,6 +3,8 @@ require File.join(File.dirname(__FILE__), "collection")
3
3
 
4
4
  module CouchModel
5
5
 
6
+ # The View class acts as a wrapper for the views that are in the CouchDB design document. It also
7
+ # provides methods to generate simple view javascript functions.
6
8
  class View
7
9
 
8
10
  attr_reader :design
@@ -15,8 +17,8 @@ module CouchModel
15
17
  @name = attributes[:name]
16
18
 
17
19
  generate_functions attributes
18
- @map = attributes[:map] if attributes[:map]
19
- @reduce = attributes[:reduce] if attributes[:reduce]
20
+ @map = attributes[:map] if attributes.has_key?(:map)
21
+ @reduce = attributes[:reduce] if attributes.has_key?(:reduce)
20
22
  end
21
23
 
22
24
  def collection(options = { })
@@ -33,18 +35,20 @@ module CouchModel
33
35
 
34
36
  def generate_functions(options = { })
35
37
  keys = [ (options[:keys] || "_id") ].flatten
38
+ @map = self.class.generate_map_function @design.model_class, keys
39
+ @reduce = nil
40
+ end
36
41
 
42
+ def self.generate_map_function(model_class, keys)
37
43
  emit_values = keys.map{ |key| "document['#{key}']" }
38
44
  check_values = emit_values.select{ |value| value != "document['_id']" }
39
45
 
40
- @map =
41
46
  """function(document) {
42
- if (document['#{Configuration::CLASS_KEY}'] == '#{@design.model_class.to_s}'#{check_values.empty? ? "" : " && " + check_values.join(" && ")}) {
47
+ if (document['#{Configuration::CLASS_KEY}'] == '#{model_class.to_s}'#{check_values.empty? ? "" : " && " + check_values.join(" && ")}) {
43
48
  emit(#{emit_values.size == 1 ? emit_values.first : "[ " + emit_values.join(", ") + " ]"}, null);
44
49
  }
45
50
  }
46
51
  """
47
- @reduce = nil
48
52
  end
49
53
 
50
54
  end
@@ -3,7 +3,7 @@
3
3
  :url: "http://localhost:5984/"
4
4
  :response:
5
5
  :code: "200"
6
- :json:
6
+ :body:
7
7
  "couchdb": "Welcome"
8
8
  "version": "0.10.0"
9
9
  -
@@ -11,7 +11,7 @@
11
11
  :url: "http://localhost:5984/_stats"
12
12
  :response:
13
13
  :code: "200"
14
- :json:
14
+ :body:
15
15
  "httpd_status_codes": "..."
16
16
  "httpd_request_methods": "..."
17
17
  -
@@ -19,7 +19,7 @@
19
19
  :url: "http://localhost:5984/_all_dbs"
20
20
  :response:
21
21
  :code: "200"
22
- :json: [ "development", "test" ]
22
+ :body: [ "development", "test" ]
23
23
  -
24
24
  :http_method: "get"
25
25
  :url: "http://localhost:5984/_uuids"
@@ -27,14 +27,14 @@
27
27
  :count: 3
28
28
  :response:
29
29
  :code: "200"
30
- :json:
30
+ :body:
31
31
  "uuids": [ "uuid_1", "uuid_2", "uuid_3" ]
32
32
  -
33
33
  :http_method: "get"
34
34
  :url: "http://localhost:5984/test"
35
35
  :response:
36
36
  :code: "200"
37
- :json:
37
+ :body:
38
38
  "db_name": "test"
39
39
  "doc_count": "0"
40
40
  -
@@ -42,13 +42,13 @@
42
42
  :url: "http://localhost:5984/new_database"
43
43
  :response:
44
44
  :code: "404"
45
- :json:
45
+ :body:
46
46
  -
47
47
  :http_method: "get"
48
48
  :url: "http://localhost:5984/test/test_model_1"
49
49
  :response:
50
50
  :code: "200"
51
- :json:
51
+ :body:
52
52
  "_id": "test_model_1"
53
53
  "_rev": "0"
54
54
  "model_class": "BaseTestModel"
@@ -59,7 +59,7 @@
59
59
  :url: "http://localhost:5984/test/test_model_2"
60
60
  :response:
61
61
  :code: "200"
62
- :json:
62
+ :body:
63
63
  "_id": "test_model_2"
64
64
  "_rev": "0"
65
65
  "model_class": "BaseTestModel"
@@ -74,7 +74,7 @@
74
74
  :url: "http://localhost:5984/test"
75
75
  :response:
76
76
  :code: "201"
77
- :json:
77
+ :body:
78
78
  "ok": true
79
79
  "id": "test_model_2"
80
80
  "rev": "0"
@@ -83,18 +83,18 @@
83
83
  :url: "http://localhost:5984/test/test_model_1"
84
84
  :response:
85
85
  :code: "201"
86
- :json:
86
+ :body:
87
87
  "ok": true
88
88
  "id": "test_model_1"
89
89
  "rev": "1"
90
90
  -
91
91
  :http_method: "delete"
92
92
  :url: "http://localhost:5984/test/test_model_1"
93
- :parameters:
94
- "rev": "0"
93
+ :headers:
94
+ "If-Match": "0"
95
95
  :response:
96
96
  :code: "200"
97
- :json:
97
+ :body:
98
98
  "ok": true
99
99
  "id": "test_model_1"
100
100
  "rev": "1"
@@ -103,7 +103,7 @@
103
103
  :url: "http://localhost:5984/test/_design/test_design"
104
104
  :response:
105
105
  :code: "200"
106
- :json:
106
+ :body:
107
107
  "_id": "_design/test_design"
108
108
  "_rev": "0"
109
109
  "language": "javascript"
@@ -116,7 +116,7 @@
116
116
  :url: "http://localhost:5984/test/_design/test_design"
117
117
  :response:
118
118
  :code: "201"
119
- :json:
119
+ :body:
120
120
  "ok": true
121
121
  "id": "_design/test_design"
122
122
  "rev": "1"
@@ -124,11 +124,11 @@
124
124
  :http_method: "get"
125
125
  :url: "http://localhost:5984/test/_all_docs"
126
126
  :parameters:
127
- "include_docs": "true"
128
- "limit": "1"
127
+ :include_docs: true
128
+ :limit: 1
129
129
  :response:
130
130
  :code: "200"
131
- :json:
131
+ :body:
132
132
  "total_rows": 1
133
133
  "offset": 0
134
134
  "rows":
@@ -146,24 +146,80 @@
146
146
  :http_method: "get"
147
147
  :url: "http://localhost:5984/test/_all_docs"
148
148
  :parameters:
149
- "include_docs": "true"
150
- "limit": "0"
149
+ :limit: 1
151
150
  :response:
152
151
  :code: "200"
153
- :json:
152
+ :body:
154
153
  "total_rows": 1
155
154
  "offset": 0
155
+ "rows":
156
+ -
157
+ "id": "test_model_1"
158
+ "key": "test_model_1"
159
+ "value":
160
+ "rev": "0"
161
+ "doc":
162
+ "_id": "test_model_1"
163
+ "_rev": "0"
164
+ "model_class": "CollectionTestModel"
165
+ "name": "phil"
166
+ -
167
+ :http_method: "get"
168
+ :url: "http://localhost:5984/test/_all_docs"
169
+ :parameters:
170
+ :include_docs: true
171
+ :limit: 0
172
+ :response:
173
+ :code: "200"
174
+ :body:
175
+ "total_rows": 1
176
+ "offset": 0
177
+ "rows": [ ]
178
+ -
179
+ :http_method: "get"
180
+ :url: "http://localhost:5984/test/_design/setup_test_model/_view/all"
181
+ :parameters:
182
+ :include_docs: true
183
+ :limit: 0
184
+ :response:
185
+ :code: "200"
186
+ :body:
187
+ "total_rows": 14
188
+ "offset": 0
156
189
  "rows": [ ]
157
190
  -
158
191
  :http_method: "get"
159
192
  :url: "http://localhost:5984/test/_design/association_test_model_one/_view/by_related_id_and_name"
160
193
  :parameters:
161
- "include_docs": "true"
162
- "startkey": "[\"test_model_2\",null]"
163
- "endkey": "[\"test_model_2\",{}]"
194
+ :include_docs: true
195
+ :startkey: [ "test_model_2", null ]
196
+ :endkey: [ "test_model_2", { } ]
197
+ :response:
198
+ :code: "200"
199
+ :body:
200
+ "total_rows": 1
201
+ "offset": 0
202
+ "rows":
203
+ -
204
+ "id": "test_model_1"
205
+ "key": "test_model_2"
206
+ "value":
207
+ "rev": "0"
208
+ "doc":
209
+ "_id": "test_model_1"
210
+ "_rev": "0"
211
+ "model_class": "AssociationTestModelOne"
212
+ "name": "phil"
213
+ -
214
+ :http_method: "get"
215
+ :url: "http://localhost:5984/test/_design/association_test_model_one/_view/by_related_id_and_name"
216
+ :parameters:
217
+ :include_docs: true
218
+ :startkey: [ "test_model_2", "phil" ]
219
+ :endkey: [ "test_model_2", "phil" ]
164
220
  :response:
165
221
  :code: "200"
166
- :json:
222
+ :body:
167
223
  "total_rows": 1
168
224
  "offset": 0
169
225
  "rows":
@@ -181,12 +237,11 @@
181
237
  :http_method: "get"
182
238
  :url: "http://localhost:5984/test/_design/association_test_model_one/_view/by_related_id_and_name"
183
239
  :parameters:
184
- "include_docs": "true"
185
- "startkey": "[\"test_model_2\",\"phil\"]"
186
- "endkey": "[\"test_model_2\",\"phil\"]"
240
+ :startkey: [ "test_model_2", "phil" ]
241
+ :endkey: [ "test_model_2", "phil" ]
187
242
  :response:
188
243
  :code: "200"
189
- :json:
244
+ :body:
190
245
  "total_rows": 1
191
246
  "offset": 0
192
247
  "rows":
@@ -9,16 +9,18 @@ module CouchModel
9
9
  self.stub!(:request).and_return do |http_method, url, options|
10
10
  options ||= { }
11
11
  parameters = options[:parameters]
12
+ headers = options[:headers]
12
13
  expected_status_code = options[:expected_status_code]
13
14
 
14
15
  request = @@fake.detect do |hash|
15
16
  hash[:http_method].to_s == http_method.to_s &&
16
17
  hash[:url].to_s == url.to_s &&
17
- hash[:parameters] == parameters
18
+ hash[:parameters] == parameters &&
19
+ hash[:headers] == headers
18
20
  end
19
- raise StandardError, "no fake request found for [#{http_method} #{url} #{parameters.inspect}]" unless request
21
+ raise StandardError, "no fake request found for [#{http_method} #{url} #{parameters.inspect} #{headers.inspect}]" unless request
20
22
  raise UnexpectedStatusCodeError, request[:response][:code].to_i if expected_status_code && expected_status_code.to_s != request[:response][:code]
21
- request[:response][:json].dup
23
+ request[:response][:body].dup
22
24
  end
23
25
  end
24
26