dupe 1.0.1 → 1.1.0.rc1

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.
@@ -23,7 +23,7 @@ module ActiveResource #:nodoc:
23
23
  if ActiveResource::VERSION::MAJOR == 3 && ActiveResource::VERSION::MINOR >= 1
24
24
  response
25
25
  else
26
- format.decode(response.body)
26
+ Dupe.format.decode(response.body)
27
27
  end
28
28
  end
29
29
 
@@ -34,20 +34,19 @@ module ActiveResource #:nodoc:
34
34
  # if the request threw an exception
35
35
  rescue
36
36
  unless body.blank?
37
- resource_hash = Hash.from_xml(body)
38
- resource_hash = resource_hash[resource_hash.keys.first]
37
+ resource_hash = Dupe.format.decode(body)
39
38
  end
40
39
  resource_hash = {} unless resource_hash.kind_of?(Hash)
41
40
  begin
42
41
  mocked_response, new_path = Dupe.network.request(:post, path, resource_hash)
43
42
  error = false
44
43
  rescue Dupe::UnprocessableEntity => e
45
- mocked_response = {:error => e.message.to_s}.to_xml(:root => 'errors')
44
+ mocked_response = Dupe.format.encode( {:error => e.message.to_s}, :root => 'errors')
46
45
  error = true
47
46
  end
48
47
  ActiveResource::HttpMock.respond_to do |mock|
49
48
  if error
50
- mock.post(path, {}, mocked_response, 422, "Content-Type" => 'application/xml')
49
+ mock.post(path, {}, mocked_response, 422, "Content-Type" => Dupe.format.mime_type)
51
50
  else
52
51
  mock.post(path, {}, mocked_response, 201, "Location" => new_path)
53
52
  end
@@ -65,8 +64,7 @@ module ActiveResource #:nodoc:
65
64
  # if the request threw an exception
66
65
  rescue
67
66
  unless body.blank?
68
- resource_hash = Hash.from_xml(body)
69
- resource_hash = resource_hash[resource_hash.keys.first]
67
+ resource_hash = Dupe.format.decode(body)
70
68
  end
71
69
  resource_hash = {} unless resource_hash.kind_of?(Hash)
72
70
  resource_hash.symbolize_keys!
@@ -75,12 +73,12 @@ module ActiveResource #:nodoc:
75
73
  error = false
76
74
  mocked_response, path = Dupe.network.request(:put, path, resource_hash)
77
75
  rescue Dupe::UnprocessableEntity => e
78
- mocked_response = {:error => e.message.to_s}.to_xml(:root => 'errors')
76
+ mocked_response = Dupe.format.encode( {:error => e.message.to_s}, :root => 'errors' )
79
77
  error = true
80
78
  end
81
79
  ActiveResource::HttpMock.respond_to do |mock|
82
80
  if error
83
- mock.put(path, {}, mocked_response, 422, "Content-Type" => 'application/xml')
81
+ mock.put(path, {}, mocked_response, 422, "Content-Type" => Dupe.format.mime_type)
84
82
  else
85
83
  mock.put(path, {}, mocked_response, 204)
86
84
  end
@@ -11,7 +11,10 @@ class Dupe
11
11
  # set this to "true" if you want Dupe to spit out mocked requests
12
12
  # after each of your cucumber scenario's run
13
13
  attr_accessor :debug
14
-
14
+
15
+ # Get the format to use (default is ActiveResource::Base.format)
16
+ attr_accessor :format
17
+
15
18
  # Suppose we're creating a 'book' resource. Perhaps our app assumes every book has a title, so let's define a book resource
16
19
  # that specifies just that:
17
20
  #
@@ -171,27 +174,27 @@ class Dupe
171
174
  mocks = %{
172
175
  network.define_service_mock(
173
176
  :get,
174
- %r{^#{model_name.to_s.titleize.constantize.prefix rescue '/'}#{model_name.to_s.pluralize}\\.xml$},
177
+ %r{^#{model_name.to_s.titleize.constantize.prefix rescue '/'}#{model_name.to_s.pluralize}\\.(?:xml|json)$},
175
178
  proc { Dupe.find(:#{model_name.to_s.pluralize}) }
176
179
  )
177
180
  network.define_service_mock(
178
181
  :get,
179
- %r{^#{model_name.to_s.titleize.constantize.prefix rescue '/'}#{model_name.to_s.pluralize}/(\\d+)\\.xml$},
182
+ %r{^#{model_name.to_s.titleize.constantize.prefix rescue '/'}#{model_name.to_s.pluralize}/(\\d+)\\.(?:xml|json)$},
180
183
  proc {|id| Dupe.find(:#{model_name}) {|resource| resource.id == id.to_i}}
181
184
  )
182
185
  network.define_service_mock(
183
186
  :post,
184
- %r{^#{model_name.to_s.titleize.constantize.prefix rescue '/'}#{model_name.to_s.pluralize}\\.xml$},
187
+ %r{^#{model_name.to_s.titleize.constantize.prefix rescue '/'}#{model_name.to_s.pluralize}\\.(?:xml|json)$},
185
188
  proc { |post_body| Dupe.create(:#{model_name.to_s}, post_body) }
186
189
  )
187
190
  network.define_service_mock(
188
191
  :put,
189
- %r{^#{model_name.to_s.titleize.constantize.prefix rescue '/'}#{model_name.to_s.pluralize}/(\\d+)\\.xml$},
192
+ %r{^#{model_name.to_s.titleize.constantize.prefix rescue '/'}#{model_name.to_s.pluralize}/(\\d+)\\.(?:xml|json)$},
190
193
  proc { |id, put_data| Dupe.find(:#{model_name.to_s}) {|resource| resource.id == id.to_i}.merge!(put_data) }
191
194
  )
192
195
  network.define_service_mock(
193
196
  :delete,
194
- %r{^#{model_name.to_s.titleize.constantize.prefix rescue '/'}#{model_name.to_s.pluralize}/(\\d+)\\.xml$},
197
+ %r{^#{model_name.to_s.titleize.constantize.prefix rescue '/'}#{model_name.to_s.pluralize}/(\\d+)\\.(?:xml|json)$},
195
198
  proc { |id| Dupe.delete(:#{model_name.to_s}) {|resource| resource.id == id.to_i} }
196
199
  )
197
200
  }
@@ -460,7 +463,10 @@ class Dupe
460
463
  @debug ||= false
461
464
  end
462
465
 
463
-
466
+ # Get the current format, defaulting to ActiveResource's configured formatter
467
+ def format
468
+ @format ||= ActiveResource::Base.format
469
+ end
464
470
 
465
471
  private
466
472
  def build_conditions(conditions)
@@ -31,7 +31,11 @@ class HashPruner
31
31
  end
32
32
 
33
33
  class Hash
34
- def to_xml_safe(options={})
35
- HashPruner.prune(self).to_xml(options)
34
+ def make_safe
35
+ HashPruner.prune(self)
36
+ end
37
+
38
+ def self.from_json(s)
39
+ ActiveSupport::JSON.decode(s)
36
40
  end
37
41
  end
@@ -28,6 +28,7 @@ class Dupe
28
28
 
29
29
  grouped_results = url_pattern.match(url)[1..-1]
30
30
  grouped_results << body if body
31
+
31
32
  resp = @response.call *grouped_results
32
33
  process_response(resp, url)
33
34
  end
@@ -49,13 +50,15 @@ class Dupe
49
50
  raise ResourceNotFoundError, "Failed with 404: the request '#{url}' returned nil."
50
51
 
51
52
  when Dupe::Database::Record
52
- resp = resp.to_xml_safe(:root => resp.__model__.name.to_s)
53
+ resp = Dupe.format.encode( resp.make_safe, :root => resp.__model__.name.to_s )
53
54
 
54
55
  when Array
55
56
  if resp.empty?
56
- resp = [].to_xml :root => 'results'
57
+ resp = Dupe.format.encode( [], :root => 'results' )
57
58
  else
58
- resp = resp.map {|r| HashPruner.prune(r)}.to_xml(:root => resp.first.__model__.name.to_s.pluralize)
59
+ resp = Dupe.format.encode(
60
+ resp.map {|r| HashPruner.prune(r)},
61
+ :root => resp.first.__model__.name.to_s.pluralize )
59
62
  end
60
63
  end
61
64
  Dupe.network.log.add_request :get, url, resp
@@ -69,7 +72,7 @@ class Dupe
69
72
  class Network
70
73
  class PostMock < Mock #:nodoc:
71
74
 
72
- # returns a tuple representing the xml of the processed entity, plus the url to the entity.
75
+ # returns a tuple representing the encoded form of the processed entity, plus the url to the entity.
73
76
  def process_response(resp, url)
74
77
  case resp
75
78
 
@@ -77,8 +80,8 @@ class Dupe
77
80
  raise StandardError, "Failed with 500: the request '#{url}' returned nil."
78
81
 
79
82
  when Dupe::Database::Record
80
- new_path = "/#{resp.__model__.name.to_s.pluralize}/#{resp.id}.xml"
81
- resp = resp.to_xml_safe(:root => resp.__model__.name.to_s)
83
+ new_path = "/#{resp.__model__.name.to_s.pluralize}/#{resp.id}.#{Dupe.format.extension}"
84
+ resp = Dupe.format.encode( resp.make_safe, :root => resp.__model__.name.to_s)
82
85
  Dupe.network.log.add_request :post, url, resp
83
86
  return resp, new_path
84
87
 
@@ -94,7 +97,7 @@ class Dupe
94
97
  class Network
95
98
  class PutMock < Mock #:nodoc:
96
99
 
97
- # returns a tuple representing the xml of the processed entity, plus the url to the entity.
100
+ # returns a tuple representing the encoded form of the processed entity, plus the url to the entity.
98
101
  def process_response(resp, url)
99
102
  case resp
100
103
 
@@ -1,189 +1,376 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe ActiveResource::Connection do
4
- before do
5
- Dupe.reset
6
- end
7
-
8
- describe "#get" do
4
+ describe "using xml " do
9
5
  before do
10
- @book = Dupe.create :book, :title => 'Rooby', :label => 'rooby'
11
- class Book < ActiveResource::Base
12
- self.site = 'http://www.example.com'
13
- self.format = :xml
14
- end
6
+ Dupe.reset
7
+ Dupe.format = ActiveResource::Formats::XmlFormat
15
8
  end
16
9
 
17
- it "should pass a request off to the Dupe network if the original request failed" do
18
- Dupe.network.should_receive(:request).with(:get, '/books.xml').once.and_return(Dupe.find(:books).to_xml(:root => 'books'))
19
- books = Book.find(:all)
20
- end
10
+ describe "#get" do
11
+ before do
12
+ @book = Dupe.create :book, :title => 'Rooby', :label => 'rooby'
13
+ class Book < ActiveResource::Base
14
+ self.site = 'http://www.example.com'
15
+ self.format = :xml
16
+ end
17
+ end
21
18
 
22
- it "should parse the xml and turn the result into active resource objects" do
23
- books = Book.find(:all)
24
- books.length.should == 1
25
- books.first.id.should == 1
26
- books.first.title.should == 'Rooby'
27
- books.first.label.should == 'rooby'
28
- end
29
- end
19
+ it "should pass a request off to the Dupe network if the original request failed" do
20
+ Dupe.network.should_receive(:request).with(:get, '/books.xml').once.and_return(Dupe.find(:books).to_xml(:root => 'books'))
21
+ books = Book.find(:all)
22
+ end
30
23
 
31
- describe "#post" do
32
- before do
33
- @book = Dupe.create :book, :label => 'rooby', :title => 'Rooby'
34
- @book.delete(:id)
35
- class Book < ActiveResource::Base
36
- self.site = 'http://www.example.com'
24
+ it "should parse the xml and turn the result into active resource objects" do
25
+ books = Book.find(:all)
26
+ books.length.should == 1
27
+ books.first.id.should == 1
28
+ books.first.title.should == 'Rooby'
29
+ books.first.label.should == 'rooby'
37
30
  end
38
31
  end
39
32
 
40
- it "should pass a request off to the Dupe network if the original request failed" do
41
- Dupe.network.should_receive(:request).with(:post, '/books.xml', Hash.from_xml(@book.to_xml(:root => 'book'))["book"] ).once
42
- book = Book.create({:label => 'rooby', :title => 'Rooby'})
43
- end
33
+ describe "#post" do
34
+ before do
35
+ @book = Dupe.create :book, :label => 'rooby', :title => 'Rooby'
36
+ @book.delete(:id)
37
+ class Book < ActiveResource::Base
38
+ self.site = 'http://www.example.com'
39
+ end
40
+ end
44
41
 
45
- it "should parse the xml and turn the result into active resource objects" do
46
- book = Book.create({:label => 'rooby', :title => 'Rooby'})
47
- book.id.should == 2
48
- book.title.should == 'Rooby'
49
- book.label.should == 'rooby'
50
- end
42
+ it "should pass a request off to the Dupe network if the original request failed" do
43
+ Dupe.network.should_receive(:request).with(:post, '/books.xml', Hash.from_xml(@book.to_xml(:root => 'book'))["book"] ).once
44
+ book = Book.create({:label => 'rooby', :title => 'Rooby'})
45
+ end
51
46
 
52
- it "should make ActiveResource throw an unprocessable entity exception if our Post mock throws a Dupe::UnprocessableEntity exception" do
53
- Post %r{/books\.xml} do |post_data|
54
- raise Dupe::UnprocessableEntity.new(:title => "must be present.") unless post_data["title"]
55
- Dupe.create :book, post_data
47
+ it "should parse the xml and turn the result into active resource objects" do
48
+ book = Book.create({:label => 'rooby', :title => 'Rooby'})
49
+ book.id.should == 2
50
+ book.title.should == 'Rooby'
51
+ book.label.should == 'rooby'
56
52
  end
57
53
 
58
- b = Book.create
59
- b.new?.should be_true
60
- b.errors.should_not be_empty
61
- b = Book.create(:title => "hello")
62
- b.new?.should be_false
63
- b.errors.should be_empty
64
- end
54
+ it "should make ActiveResource throw an unprocessable entity exception if our Post mock throws a Dupe::UnprocessableEntity exception" do
55
+ Post %r{/books\.xml} do |post_data|
56
+ raise Dupe::UnprocessableEntity.new(:title => "must be present.") unless post_data["title"]
57
+ Dupe.create :book, post_data
58
+ end
65
59
 
66
- it "should handle request with blank body" do
67
- class SubscribableBook < ActiveResource::Base
68
- self.site = 'http://www.example.com'
69
- self.format = :xml
60
+ b = Book.create
61
+ b.new?.should be_true
62
+ b.errors.should_not be_empty
63
+ b = Book.create(:title => "hello")
64
+ b.new?.should be_false
65
+ b.errors.should be_empty
66
+ end
70
67
 
71
- def self.send_update_emails
72
- post(:send_update_emails)
68
+ it "should handle request with blank body" do
69
+ class SubscribableBook < ActiveResource::Base
70
+ self.site = 'http://www.example.com'
71
+ self.format = :xml
72
+
73
+ def self.send_update_emails
74
+ post(:send_update_emails)
75
+ end
73
76
  end
74
- end
75
77
 
76
- Post %r{/subscribable_books/send_update_emails\.xml} do |post_data|
77
- Dupe.create :email, post_data
78
- end
78
+ Post %r{/subscribable_books/send_update_emails\.xml} do |post_data|
79
+ Dupe.create :email, post_data
80
+ end
79
81
 
80
- response = SubscribableBook.send_update_emails
81
- response.code.should == 201
82
+ response = SubscribableBook.send_update_emails
83
+ response.code.should == 201
84
+ end
82
85
  end
83
- end
84
86
 
85
- describe "#put" do
86
- before do
87
- @book = Dupe.create :book, :label => 'rooby', :title => 'Rooby'
88
- class Book < ActiveResource::Base
89
- self.site = 'http://www.example.com'
87
+ describe "#put" do
88
+ before do
89
+ @book = Dupe.create :book, :label => 'rooby', :title => 'Rooby'
90
+ class Book < ActiveResource::Base
91
+ self.site = 'http://www.example.com'
92
+ end
93
+ @ar_book = Book.find(1)
90
94
  end
91
- @ar_book = Book.find(1)
92
- end
93
95
 
94
- it "should pass a request off to the Dupe network if the original request failed" do
95
- Dupe.network.should_receive(:request).with(:put, '/books/1.xml', Hash.from_xml(@book.merge(:title => "Rails!").to_xml(:root => 'book'))["book"].symbolize_keys!).once.and_return([nil, '/books/1.xml'])
96
- @ar_book.title = 'Rails!'
97
- @ar_book.save
98
- end
96
+ it "should pass a request off to the Dupe network if the original request failed" do
97
+ Dupe.network.should_receive(:request).with(:put, '/books/1.xml', Hash.from_xml(@book.merge(:title => "Rails!").to_xml(:root => 'book'))["book"].symbolize_keys!).once.and_return([nil, '/books/1.xml'])
98
+ @ar_book.title = 'Rails!'
99
+ @ar_book.save
100
+ end
99
101
 
100
- context "put methods that return HTTP 204" do
101
- before(:each) do
102
- class ExpirableBook < ActiveResource::Base
103
- self.site = 'http://www.example.com'
104
- self.format = :xml
105
- attr_accessor :state
102
+ context "put methods that return HTTP 204" do
103
+ before(:each) do
104
+ class ExpirableBook < ActiveResource::Base
105
+ self.site = 'http://www.example.com'
106
+ self.format = :xml
107
+ attr_accessor :state
108
+
109
+ def expire_copyrights!
110
+ put(:expire)
111
+ end
112
+ end
106
113
 
107
- def expire_copyrights!
108
- put(:expire)
114
+ Put %r{/expirable_books/(\d)+/expire.xml} do |id, body|
115
+ Dupe.find(:expirable_book) { |eb| eb.id == id.to_i }.tap { |book|
116
+ book.state = 'expired'
117
+ }
109
118
  end
119
+
120
+ @e = Dupe.create :expirable_book, :title => 'Impermanence', :state => 'active'
110
121
  end
111
122
 
112
- Put %r{/expirable_books/(\d)+/expire.xml} do |id, body|
113
- Dupe.find(:expirable_book) { |eb| eb.id == id.to_i }.tap { |book|
114
- book.state = 'expired'
115
- }
123
+ it "should handle no-content responses" do
124
+ response = ExpirableBook.find(@e.id).expire_copyrights!
125
+ response.body.should be_blank
126
+ response.code.to_s.should == "204"
116
127
  end
128
+ end
117
129
 
118
- @e = Dupe.create :expirable_book, :title => 'Impermanence', :state => 'active'
130
+ it "should parse the xml and turn the result into active resource objects" do
131
+ @book.title.should == "Rooby"
132
+ @ar_book.title = "Rails!"
133
+ @ar_book.save
134
+ @ar_book.new?.should == false
135
+ @ar_book.valid?.should == true
136
+ @ar_book.id.should == 1
137
+ @ar_book.label.should == "rooby"
138
+ @book.title.should == "Rails!"
139
+ @book.id.should == 1
140
+ @book.label.should == 'rooby'
119
141
  end
120
142
 
121
- it "should handle no-content responses" do
122
- response = ExpirableBook.find(@e.id).expire_copyrights!
123
- response.body.should be_blank
124
- response.code.to_s.should == "204"
143
+ it "should make ActiveResource throw an unprocessable entity exception if our Put mock throws a Dupe::UnprocessableEntity exception" do
144
+ Put %r{/books/(\d+)\.xml} do |id, put_data|
145
+ raise Dupe::UnprocessableEntity.new(:title => " must be present.") unless put_data[:title]
146
+ Dupe.find(:book) {|b| b.id == id.to_i}.merge!(put_data)
147
+ end
148
+
149
+ @ar_book.title = nil
150
+ @ar_book.save.should == false
151
+ @ar_book.errors[:base].should_not be_empty
152
+
153
+ @ar_book.title = "Rails!"
154
+ @ar_book.save.should == true
155
+ # the following line should be true, were it not for a bug in active_resource 2.3.3 - 2.3.5
156
+ # i reported the bug here: https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/4169-activeresourcebasesave-put-doesnt-clear-out-errors
157
+ # @ar_book.errors.should be_empty
125
158
  end
126
159
  end
127
160
 
128
- it "should parse the xml and turn the result into active resource objects" do
129
- @book.title.should == "Rooby"
130
- @ar_book.title = "Rails!"
131
- @ar_book.save
132
- @ar_book.new?.should == false
133
- @ar_book.valid?.should == true
134
- @ar_book.id.should == 1
135
- @ar_book.label.should == "rooby"
136
- @book.title.should == "Rails!"
137
- @book.id.should == 1
138
- @book.label.should == 'rooby'
139
- end
161
+ describe "#delete" do
162
+ before do
163
+ @book = Dupe.create :book, :label => 'rooby', :title => 'Rooby'
164
+ class Book < ActiveResource::Base
165
+ self.site = 'http://www.example.com'
166
+ self.format = :xml
167
+ end
168
+ @ar_book = Book.find(1)
169
+ end
140
170
 
141
- it "should make ActiveResource throw an unprocessable entity exception if our Put mock throws a Dupe::UnprocessableEntity exception" do
142
- Put %r{/books/(\d+)\.xml} do |id, put_data|
143
- raise Dupe::UnprocessableEntity.new(:title => " must be present.") unless put_data[:title]
144
- Dupe.find(:book) {|b| b.id == id.to_i}.merge!(put_data)
171
+ it "should pass a request off to the Dupe network if the original request failed" do
172
+ Dupe.network.should_receive(:request).with(:delete, '/books/1.xml').once
173
+ @ar_book.destroy
145
174
  end
146
175
 
147
- @ar_book.title = nil
148
- @ar_book.save.should == false
149
- @ar_book.errors[:base].should_not be_empty
176
+ it "trigger a Dupe.delete to delete the mocked resource from the duped database" do
177
+ Dupe.find(:books).length.should == 1
178
+ @ar_book.destroy
179
+ Dupe.find(:books).length.should == 0
180
+ end
150
181
 
151
- @ar_book.title = "Rails!"
152
- @ar_book.save.should == true
153
- # the following line should be true, were it not for a bug in active_resource 2.3.3 - 2.3.5
154
- # i reported the bug here: https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/4169-activeresourcebasesave-put-doesnt-clear-out-errors
155
- # @ar_book.errors.should be_empty
182
+ it "should allow you to override the default DELETE intercept mock" do
183
+ Delete %r{/books/(\d+)\.xml} do |id|
184
+ raise StandardError, "Testing Delete override"
185
+ end
186
+
187
+ proc {@ar_book.destroy}.should raise_error(StandardError, "Testing Delete override")
188
+ end
156
189
  end
157
190
  end
158
191
 
159
- describe "#delete" do
192
+ describe "using json " do
160
193
  before do
161
- @book = Dupe.create :book, :label => 'rooby', :title => 'Rooby'
162
- class Book < ActiveResource::Base
163
- self.site = 'http://www.example.com'
164
- self.format = :xml
194
+ Dupe.reset
195
+ Dupe.format = ActiveResource::Formats::JsonFormat
196
+ end
197
+
198
+ describe "#get" do
199
+ before do
200
+ @book = Dupe.create :book, :title => 'Rooby', :label => 'rooby'
201
+ class Book < ActiveResource::Base
202
+ self.site = 'http://www.example.com'
203
+ self.format = :json
204
+ end
205
+ end
206
+
207
+ it "should pass a request off to the Dupe network if the original request failed" do
208
+ Dupe.network.should_receive(:request).with(:get, '/books.json').once.and_return(Dupe.find(:books).to_json(:root => 'books'))
209
+ books = Book.find(:all)
210
+ end
211
+
212
+ it "should parse the json and turn the result into active resource objects" do
213
+ books = Book.find(:all)
214
+ books.length.should == 1
215
+ books.first.id.should == 1
216
+ books.first.title.should == 'Rooby'
217
+ books.first.label.should == 'rooby'
165
218
  end
166
- @ar_book = Book.find(1)
167
219
  end
168
220
 
169
- it "should pass a request off to the Dupe network if the original request failed" do
170
- Dupe.network.should_receive(:request).with(:delete, '/books/1.xml').once
171
- @ar_book.destroy
221
+ describe "#post" do
222
+ before do
223
+ @book = Dupe.create :book, :label => 'rooby', :title => 'Rooby'
224
+ @book.delete(:id)
225
+ class Book < ActiveResource::Base
226
+ self.site = 'http://www.example.com'
227
+ end
228
+ end
229
+
230
+ it "should pass a request off to the Dupe network if the original request failed" do
231
+ Dupe.network.should_receive(:request).with(:post, '/books.json', Hash.from_json(@book.to_json(:root => 'book')) ).once
232
+ book = Book.create({:label => 'rooby', :title => 'Rooby'})
233
+ end
234
+
235
+ it "should parse the json and turn the result into active resource objects" do
236
+ book = Book.create({:label => 'rooby', :title => 'Rooby'})
237
+ book.id.should == 2
238
+ book.title.should == 'Rooby'
239
+ book.label.should == 'rooby'
240
+ end
241
+
242
+ it "should make ActiveResource throw an unprocessable entity exception if our Post mock throws a Dupe::UnprocessableEntity exception" do
243
+ Post %r{/books\.json} do |post_data|
244
+ raise Dupe::UnprocessableEntity.new(:title => "must be present.") unless post_data["title"]
245
+ Dupe.create :book, post_data
246
+ end
247
+
248
+ b = Book.create
249
+ b.new?.should be_true
250
+ b.should_not be_empty
251
+ end
252
+
253
+ it "should handle request with blank body" do
254
+ class SubscribableBook < ActiveResource::Base
255
+ self.site = 'http://www.example.com'
256
+ self.format = :json
257
+
258
+ def self.send_update_emails
259
+ post(:send_update_emails)
260
+ end
261
+ end
262
+
263
+ Post %r{/subscribable_books/send_update_emails\.json} do |post_data|
264
+ Dupe.create :email, post_data
265
+ end
266
+
267
+ response = SubscribableBook.send_update_emails
268
+ response.code.should == 201
269
+ end
172
270
  end
173
271
 
174
- it "trigger a Dupe.delete to delete the mocked resource from the duped database" do
175
- Dupe.find(:books).length.should == 1
176
- @ar_book.destroy
177
- Dupe.find(:books).length.should == 0
272
+ describe "#put" do
273
+ before do
274
+ @book = Dupe.create :book, :label => 'rooby', :title => 'Rooby'
275
+ class Book < ActiveResource::Base
276
+ self.site = 'http://www.example.com'
277
+ end
278
+ @ar_book = Book.find(1)
279
+ end
280
+
281
+ it "should pass a request off to the Dupe network if the original request failed" do
282
+ Dupe.network.should_receive(:request).with(:put, '/books/1.json', Hash.from_json(@book.merge(:title => "Rails!").to_json(:root => 'book')).symbolize_keys!).once.and_return([nil, '/books/1.json'])
283
+ @ar_book.title = 'Rails!'
284
+ @ar_book.save
285
+ end
286
+
287
+ context "put methods that return HTTP 204" do
288
+ before(:each) do
289
+ class ExpirableBook < ActiveResource::Base
290
+ self.site = 'http://www.example.com'
291
+ self.format = :json
292
+ attr_accessor :state
293
+
294
+ def expire_copyrights!
295
+ put(:expire)
296
+ end
297
+ end
298
+
299
+ Put %r{/expirable_books/(\d)+/expire.json} do |id, body|
300
+ Dupe.find(:expirable_book) { |eb| eb.id == id.to_i }.tap { |book|
301
+ book.state = 'expired'
302
+ }
303
+ end
304
+
305
+ @e = Dupe.create :expirable_book, :title => 'Impermanence', :state => 'active'
306
+ end
307
+
308
+ it "should handle no-content responses" do
309
+ response = ExpirableBook.find(@e.id).expire_copyrights!
310
+ response.body.should be_blank
311
+ response.code.to_s.should == "204"
312
+ end
313
+ end
314
+
315
+ it "should parse the json and turn the result into active resource objects" do
316
+ @book.title.should == "Rooby"
317
+ @ar_book.title = "Rails!"
318
+ @ar_book.save
319
+ @ar_book.new?.should == false
320
+ @ar_book.valid?.should == true
321
+ @ar_book.id.should == 1
322
+ @ar_book.label.should == "rooby"
323
+ @book.title.should == "Rails!"
324
+ @book.id.should == 1
325
+ @book.label.should == 'rooby'
326
+ end
327
+
328
+ it "should make ActiveResource throw an unprocessable entity exception if our Put mock throws a Dupe::UnprocessableEntity exception" do
329
+ Put %r{/books/(\d+)\.json} do |id, put_data|
330
+ raise Dupe::UnprocessableEntity.new(:title => " must be present.") unless put_data[:title]
331
+ Dupe.find(:book) {|b| b.id == id.to_i}.merge!(put_data)
332
+ end
333
+
334
+ @ar_book.title = nil
335
+ @ar_book.save.should == false
336
+ @ar_book.should_not be_empty
337
+
338
+ @ar_book.title = "Rails!"
339
+ @ar_book.save.should == true
340
+ # the following line should be true, were it not for a bug in active_resource 2.3.3 - 2.3.5
341
+ # i reported the bug here: https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/4169-activeresourcebasesave-put-doesnt-clear-out-errors
342
+ # @ar_book.errors.should be_empty
343
+ end
178
344
  end
179
345
 
180
- it "should allow you to override the default DELETE intercept mock" do
181
- Delete %r{/books/(\d+)\.xml} do |id|
182
- raise StandardError, "Testing Delete override"
346
+ describe "#delete" do
347
+ before do
348
+ @book = Dupe.create :book, :label => 'rooby', :title => 'Rooby'
349
+ class Book < ActiveResource::Base
350
+ self.site = 'http://www.example.com'
351
+ self.format = :json
352
+ end
353
+ @ar_book = Book.find(1)
354
+ end
355
+
356
+ it "should pass a request off to the Dupe network if the original request failed" do
357
+ Dupe.network.should_receive(:request).with(:delete, '/books/1.json').once
358
+ @ar_book.destroy
183
359
  end
184
360
 
185
- proc {@ar_book.destroy}.should raise_error(StandardError, "Testing Delete override")
361
+ it "trigger a Dupe.delete to delete the mocked resource from the duped database" do
362
+ Dupe.find(:books).length.should == 1
363
+ @ar_book.destroy
364
+ Dupe.find(:books).length.should == 0
365
+ end
366
+
367
+ it "should allow you to override the default DELETE intercept mock" do
368
+ Delete %r{/books/(\d+)\.json} do |id|
369
+ raise StandardError, "Testing Delete override"
370
+ end
371
+
372
+ proc {@ar_book.destroy}.should raise_error(StandardError, "Testing Delete override")
373
+ end
186
374
  end
187
375
  end
188
-
189
376
  end