dupe 1.0.1 → 1.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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