dupe 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -38,6 +38,11 @@ It will also place example Dupe definitions in RAILS_ROOT/features/dupe/definiti
38
38
  Lastly, it will add a load_dupe.rb file (that loads up the definitions and custom mocks) to RAILS_ROOT/features/support/load_dupe.rb
39
39
 
40
40
 
41
+ = Tutorial
42
+
43
+ Checkout the {dupe example application}[http://github.com/moonmaster9000/dupe_example_app] for a tutorial on cuking an application with
44
+ ActiveResource and Dupe.
45
+
41
46
  = Features
42
47
 
43
48
  ==Creating resources
@@ -508,7 +513,7 @@ Now, when you try to create an Author without a name, it will respond with the a
508
513
  irb# a = a.new?
509
514
  ==> true
510
515
 
511
- Because the resource would invalidate, the Dupe object is not created.
516
+ Because our custom Post mock determined that the resource was invalid, Dupe did not mock the resource:
512
517
 
513
518
  irb# Dupe.find(:authors)
514
519
  ==> []
@@ -524,11 +529,127 @@ When we create the Author with the required attributes, it will now be considere
524
529
  irb# a.new?
525
530
  ==> false
526
531
 
527
- Now, Dupe will go ahead and create the resource.
532
+ Since our custom Post mock considered the resource valid, it went ahead and created the resource:
528
533
 
529
534
  irb# Dupe.find(:authors)
530
535
  ==> [<#Duped::Author name="CS Lewis" id=1>]
531
536
 
537
+
538
+ ===PUT requests
539
+
540
+ In ActiveResource, when you update a resource that already exists via the "save" method, it translates to a PUT request.
541
+ Dupe provides basic PUT intercept mocks out of the box, and like GET and POST mocks,
542
+ it allows you to override the default PUT intercept mock, and create new ones.
543
+
544
+ Let's again examine an "author" resource:
545
+
546
+ irb# Dupe.define :author
547
+
548
+ irb# class Author < ActiveResource::Base; self.site = ''; end
549
+ ==> ""
550
+
551
+ irb# Author.create :name => "CS Lewis" # --> Dupe intercepts this POST request
552
+ ==> #<Author:0x1a4ca58 @attributes={"name"=>"CS Lewis", "id"=>1}, @prefix_options={}>
553
+
554
+ irb# Dupe.find :authors
555
+ ==> [<#Duped::Author name="CS Lewis" id=1>]
556
+
557
+ irb# a = Author.find 1 # --> Dupe intercepts this GET request
558
+ ==> #<Author:0x1a4ca58 @attributes={"name"=>"CS Lewis", "id"=>1}, @prefix_options={}>
559
+
560
+ So far, we've created a resource (via dupe's POST intercept mocking), and we've also found the resource we create (via dupe's GET
561
+ intercept mocking). Now, let's attempt to update (PUT) the resource:
562
+
563
+ irb# a.name = "Frank Herbert"
564
+
565
+ irb# a.save # --> Dupe intercepts this PUT request
566
+ ==> true
567
+
568
+ Dupe intercepted the PUT request that ActiveResource attempted to send, and updated the Duped resource accordingly:
569
+
570
+ irb# a
571
+ ==> #<Author:0x1a4ca58 @attributes={"name"=>"Frank Herbert", "id"=>1}, @prefix_options={}>
572
+
573
+ irb# Dupe.find :authors
574
+ ==> [<#Duped::Author name="Frank Herbert" id=1>]
575
+
576
+ You can also overwrite the default PUT intercept mock for your resource by using the "Put" method:
577
+
578
+ irb# Put %r{/authors/(\d+)\.xml} do |id, put_data|
579
+ raise Dupe::UnprocessableEntity.new(:name => " must be present.") unless put_data[:name]
580
+ Dupe.find(:author) {|a| a.id == id.to_i}.merge! put_data
581
+ end
582
+ ==> #<Dupe::Network::PostMock:0x1a1afe4 @url_pattern=/\/authors\.xml/, @response=#<Proc:0x01a1b084@(irb):13>, @verb=:post>
583
+
584
+ Now, if we try to update our Author without a name, it will respond with the appropriate errors.
585
+
586
+ irb# Dupe.find :authors
587
+ ==> [<#Duped::Author name="Frank Herbert" id=1>]
588
+
589
+ irb# a = Author.find 1 # --> Dupe intercepts this GET request
590
+ ==> #<Author:0x1a4ca58 @attributes={"name"=>"Frank Herbert", "id"=>1}, @prefix_options={}>
591
+
592
+ irb# a.name = nil
593
+
594
+ irb# a.save
595
+ ==> false
596
+
597
+ irb# a.errors.on_base
598
+ ==> ["name must be present"]
599
+
600
+ Since our Put intercept mock raise the Dupe::UnprocessableEntity exception,
601
+ the underlying Duped record remains unchanged, just as we would expect the real service to have operated:
602
+
603
+ irb# Dupe.find :authors
604
+ ==> [<#Duped::Author name="Frank Herbert" id=1>]
605
+
606
+ We can, of course, at this point still update the name to a non-nil value and attempt to save it again:
607
+
608
+ irb# a.name = "Matt Parker"
609
+
610
+ irb# a.save
611
+ ==> true
612
+
613
+ irb# Dupe.find :authors
614
+ ==> [<#Duped::Author name="Matt Parker" id=1>]
615
+
616
+ ===DELETE requests
617
+
618
+ As you might have guessed, Dupe also supports DELETE intercept mocking (ActiveResource::Base#destroy):
619
+
620
+ irb# Dupe.define :author
621
+
622
+ irb# class Author < ActiveResource::Base; self.site = ''; end
623
+ ==> ""
624
+
625
+ irb# Author.create :name => "CS Lewis" # --> Dupe intercepts this POST request
626
+ ==> #<Author:0x1a4ca58 @attributes={"name"=>"CS Lewis", "id"=>1}, @prefix_options={}>
627
+
628
+ irb# Dupe.find :authors
629
+ ==> [<#Duped::Author name="CS Lewis" id=1>]
630
+
631
+ irb# a = Author.find 1 # --> Dupe intercepts this GET request
632
+ ==> #<Author:0x1a4ca58 @attributes={"name"=>"CS Lewis", "id"=>1}, @prefix_options={}>
633
+
634
+ irb# a.destroy
635
+ ==> #<ActiveResource::Response:0x181c1c0 @body="", @message="200", @code=200, @headers={"Content-Length"=>"0"}
636
+
637
+ irb# Dupe.find :authors
638
+ ==> []
639
+
640
+ And also, as you might have guessed, you can override the default DELETE intercept mock for a resource:
641
+
642
+ irb# Delete %r{/books/(\d+)\.xml} do |id|
643
+ puts "deleting the book with id #{id}"
644
+ Dupe.delete(:book) {|b| b.id == id.to_i}
645
+ end
646
+
647
+ irb# a = Author.create :name => "some author"
648
+
649
+ irb# a.destroy
650
+ ==> "deleting the book with id 1"
651
+
652
+
532
653
  == More
533
654
 
534
655
  Consult the API documentation at http://moonmaster9000.github.com/dupe/api/
@@ -50,5 +50,48 @@ module ActiveResource #:nodoc:
50
50
  end
51
51
  response
52
52
  end
53
+
54
+ def put(path, body = '', headers = {}) #:nodoc:
55
+ begin
56
+ response = request(:put, path, body.to_s, build_request_headers(headers, :put))
57
+
58
+ # if the request threw an exception
59
+ rescue
60
+ resource_hash = Hash.from_xml(body)
61
+ resource_hash = resource_hash[resource_hash.keys.first]
62
+ resource_hash = {} unless resource_hash.kind_of?(Hash)
63
+ resource_hash.symbolize_keys!
64
+
65
+ begin
66
+ error = false
67
+ mocked_response, path = Dupe.network.request(:put, path, resource_hash)
68
+ rescue Dupe::UnprocessableEntity => e
69
+ mocked_response = {:error => e.message.to_s}.to_xml(:root => 'errors')
70
+ error = true
71
+ end
72
+ ActiveResource::HttpMock.respond_to do |mock|
73
+ if error
74
+ mock.put(path, {}, mocked_response, 422, "Content-Type" => 'application/xml')
75
+ else
76
+ mock.put(path, {}, mocked_response, 204)
77
+ end
78
+ end
79
+ response = request(:put, path, body.to_s, build_request_headers(headers, :put))
80
+ ActiveResource::HttpMock.delete_mock(:put, path)
81
+ end
82
+ response
83
+ end
84
+
85
+ def delete(path, headers = {})
86
+ Dupe.network.request(:delete, path)
87
+
88
+ ActiveResource::HttpMock.respond_to do |mock|
89
+ mock.delete(path, {}, nil, 200)
90
+ end
91
+ response = request(:delete, path, build_request_headers(headers, :delete))
92
+
93
+ ActiveResource::HttpMock.delete_mock(:delete, path)
94
+ response
95
+ end
53
96
  end
54
97
  end
@@ -105,4 +105,12 @@ end
105
105
 
106
106
  def Post(url_pattern, &block)
107
107
  Dupe.network.define_service_mock :post, url_pattern, block
108
- end
108
+ end
109
+
110
+ def Put(url_pattern, &block)
111
+ Dupe.network.define_service_mock :put, url_pattern, block
112
+ end
113
+
114
+ def Delete(url_pattern, &block)
115
+ Dupe.network.define_service_mock :delete, url_pattern, block
116
+ end
data/lib/dupe/database.rb CHANGED
@@ -12,6 +12,23 @@ class Dupe
12
12
  def initialize
13
13
  @tables = {}
14
14
  end
15
+
16
+ # database.delete :books # --> would delete all books
17
+ # database.delete :book # --> would delete the first book record found
18
+ # database.delete :book, proc {|b| b.id < 10} # --> would delete all books found who's id is less than 10
19
+ # database.delete :books, proc {|b| b.id < 10} # --> would delete all books found who's id is less than 10
20
+ def delete(resource_name, conditions=nil)
21
+ model_name = resource_name.to_s.singularize.to_sym
22
+ raise StandardError, "Invalid DELETE operation: The resource #{model_name} has not been defined" unless @tables[model_name]
23
+
24
+ if conditions
25
+ @tables[model_name].reject!(&conditions)
26
+ elsif resource_name.singular?
27
+ @tables[model_name].shift
28
+ elsif resource_name.plural?
29
+ @tables[model_name] = []
30
+ end
31
+ end
15
32
 
16
33
  # pass in a Dupe::Database::Record object, and this method will store the record
17
34
  # in the appropriate table
@@ -49,4 +66,4 @@ class Dupe
49
66
  end
50
67
 
51
68
  end
52
- end
69
+ end
data/lib/dupe/dupe.rb CHANGED
@@ -8,7 +8,7 @@ class Dupe
8
8
  attr_reader :sequences #:nodoc:
9
9
  attr_reader :database #:nodoc:
10
10
 
11
- # set this to "true" if you Dupe to spit out mocked requests
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
 
@@ -184,6 +184,16 @@ class Dupe
184
184
  %r{^#{model_name.to_s.titleize.constantize.prefix rescue '/'}#{model_name.to_s.pluralize}\\.xml$},
185
185
  proc { |post_body| Dupe.create(:#{model_name.to_s}, post_body) }
186
186
  )
187
+ network.define_service_mock(
188
+ :put,
189
+ %r{^#{model_name.to_s.titleize.constantize.prefix rescue '/'}#{model_name.to_s.pluralize}/(\\d+)\\.xml$},
190
+ proc { |id, put_data| Dupe.find(:#{model_name.to_s}) {|resource| resource.id == id.to_i}.merge!(put_data) }
191
+ )
192
+ network.define_service_mock(
193
+ :delete,
194
+ %r{^#{model_name.to_s.titleize.constantize.prefix rescue '/'}#{model_name.to_s.pluralize}/(\\d+)\\.xml$},
195
+ proc { |id| Dupe.delete(:#{model_name.to_s}) {|resource| resource.id == id.to_i} }
196
+ )
187
197
  }
188
198
  eval(mocks)
189
199
  end
@@ -392,6 +402,10 @@ class Dupe
392
402
  end
393
403
  end
394
404
 
405
+ def delete(resource, &conditions)
406
+ database.delete resource, conditions
407
+ end
408
+
395
409
  def sequence(name, &block)
396
410
  sequences[name.to_sym] = Sequence.new 1, block
397
411
  end
@@ -517,4 +531,4 @@ end
517
531
 
518
532
  class Dupe
519
533
  class UnprocessableEntity < StandardError; end
520
- end
534
+ end
data/lib/dupe/mock.rb CHANGED
@@ -1,24 +1,18 @@
1
1
  class Dupe
2
2
  class Network #:nodoc:
3
3
  class Mock #:nodoc:
4
- include Dupe::Network::RestValidation
5
-
6
4
  class ResourceNotFoundError < StandardError; end
7
5
 
8
- attr_reader :verb
9
6
  attr_reader :url_pattern
10
7
  attr_reader :response
11
8
 
12
- def initialize(verb, url_pattern, response_proc=nil)
13
- validate_request_type verb
14
-
9
+ def initialize(url_pattern, response_proc=nil)
15
10
  raise(
16
11
  ArgumentError,
17
12
  "The URL pattern parameter must be a type of regular expression."
18
13
  ) unless url_pattern.kind_of?(Regexp)
19
14
 
20
15
  @response = response_proc || proc {}
21
- @verb = verb
22
16
  @url_pattern = url_pattern
23
17
  end
24
18
 
@@ -39,7 +33,7 @@ class Dupe
39
33
  end
40
34
 
41
35
  def process_response(resp, url)
42
- raise NotImplementedError, "Need to specify the appropriate mock class: GetMock, PostMock, PutMock or DeleteMock"
36
+ raise NotImplementedError, "When you extend Dupe::Network::Mock, you must implement the #process_response instance method."
43
37
  end
44
38
  end
45
39
  end
@@ -50,18 +44,21 @@ class Dupe
50
44
  class GetMock < Mock #:nodoc:
51
45
  def process_response(resp, url)
52
46
  case resp
53
- when NilClass
54
- raise ResourceNotFoundError, "Failed with 404: the request '#{url}' returned nil."
55
- when Dupe::Database::Record
56
- resp = resp.to_xml_safe(:root => resp.__model__.name.to_s)
57
- when Array
58
- if resp.empty?
59
- resp = [].to_xml :root => 'results'
60
- else
61
- resp = resp.map {|r| HashPruner.prune(r)}.to_xml(:root => resp.first.__model__.name.to_s.pluralize)
62
- end
47
+
48
+ when NilClass
49
+ raise ResourceNotFoundError, "Failed with 404: the request '#{url}' returned nil."
50
+
51
+ when Dupe::Database::Record
52
+ resp = resp.to_xml_safe(:root => resp.__model__.name.to_s)
53
+
54
+ when Array
55
+ if resp.empty?
56
+ resp = [].to_xml :root => 'results'
57
+ else
58
+ resp = resp.map {|r| HashPruner.prune(r)}.to_xml(:root => resp.first.__model__.name.to_s.pluralize)
59
+ end
63
60
  end
64
- Dupe.network.log.add_request @verb, url, resp
61
+ Dupe.network.log.add_request :get, url, resp
65
62
  resp
66
63
  end
67
64
  end
@@ -71,19 +68,60 @@ end
71
68
  class Dupe
72
69
  class Network
73
70
  class PostMock < Mock #:nodoc:
71
+
72
+ # returns a tuple representing the xml of the processed entity, plus the url to the entity.
74
73
  def process_response(resp, url)
75
74
  case resp
76
- when NilClass
77
- raise ResourceNotFoundError, "Failed with 404: the request '#{url}' returned nil."
78
- when Dupe::Database::Record
79
- new_path = "/#{resp.__model__.name.to_s.pluralize}/#{resp.id}.xml"
80
- resp = resp.to_xml_safe(:root => resp.__model__.name.to_s)
81
- Dupe.network.log.add_request @verb, url, resp
82
- return resp, new_path
83
- else
84
- raise StandardError, "Unknown PostMock Response. Your Post intercept mocks must return a Duped resource object or nil"
75
+
76
+ when NilClass
77
+ raise StandardError, "Failed with 500: the request '#{url}' returned nil."
78
+
79
+ 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)
82
+ Dupe.network.log.add_request :post, url, resp
83
+ return resp, new_path
84
+
85
+ else
86
+ raise StandardError, "Unknown PostMock Response. Your Post intercept mocks must return a Duped resource object or nil"
85
87
  end
86
88
  end
87
89
  end
88
90
  end
89
- end
91
+ end
92
+
93
+ class Dupe
94
+ class Network
95
+ class PutMock < Mock #:nodoc:
96
+
97
+ # returns a tuple representing the xml of the processed entity, plus the url to the entity.
98
+ def process_response(resp, url)
99
+ case resp
100
+
101
+ when NilClass
102
+ raise StandardError, "Failed with 500: the request '#{url}' returned nil."
103
+
104
+ when Dupe::Database::Record
105
+ resp = nil
106
+ Dupe.network.log.add_request :put, url, ""
107
+ return resp, url
108
+
109
+ else
110
+ raise StandardError, "Unknown PutMock Response. Your Post intercept mocks must return a Duped resource object or nil"
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+
117
+ class Dupe
118
+ class Network
119
+ class DeleteMock < Mock #:nodoc:
120
+ # logs the request
121
+ def process_response(resp, url)
122
+ Dupe.network.log.add_request :delete, url, ""
123
+ nil
124
+ end
125
+ end
126
+ end
127
+ end
data/lib/dupe/network.rb CHANGED
@@ -21,15 +21,23 @@ class Dupe
21
21
  validate_request_type verb
22
22
  case verb
23
23
  when :get
24
- GetMock.new(verb, url_pattern, response_proc).tap do |mock|
24
+ GetMock.new(url_pattern, response_proc).tap do |mock|
25
25
  @mocks[verb] << mock
26
26
  end
27
27
  when :post
28
- PostMock.new(verb, url_pattern, response_proc).tap do |mock|
28
+ PostMock.new(url_pattern, response_proc).tap do |mock|
29
+ @mocks[verb].unshift mock
30
+ end
31
+ when :put
32
+ PutMock.new(url_pattern, response_proc).tap do |mock|
33
+ @mocks[verb].unshift mock
34
+ end
35
+ when :delete
36
+ DeleteMock.new(url_pattern, response_proc).tap do |mock|
29
37
  @mocks[verb].unshift mock
30
38
  end
31
39
  else
32
- raise StandardError, "Dupe does not (yet) support mocking #{verb.to_s.upcase} requests."
40
+ raise StandardError, "Dupe does not support mocking #{verb.to_s.upcase} requests."
33
41
  end
34
42
  end
35
43
 
@@ -45,4 +53,4 @@ class Dupe
45
53
  end
46
54
 
47
55
  end
48
- end
56
+ end
@@ -62,4 +62,80 @@ describe ActiveResource::Connection do
62
62
  b.errors.should be_empty
63
63
  end
64
64
  end
65
- end
65
+
66
+ describe "#put" do
67
+ before do
68
+ @book = Dupe.create :book, :label => 'rooby', :title => 'Rooby'
69
+ class Book < ActiveResource::Base
70
+ self.site = ''
71
+ end
72
+ @ar_book = Book.find(1)
73
+ end
74
+
75
+ it "should pass a request off to the Dupe network if the original request failed" do
76
+ 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
77
+ @ar_book.title = 'Rails!'
78
+ @ar_book.save
79
+ end
80
+
81
+ it "should parse the xml and turn the result into active resource objects" do
82
+ @book.title.should == "Rooby"
83
+ @ar_book.title = "Rails!"
84
+ @ar_book.save
85
+ @ar_book.new?.should == false
86
+ @ar_book.valid?.should == true
87
+ @ar_book.id.should == 1
88
+ @ar_book.label.should == "rooby"
89
+ @book.title.should == "Rails!"
90
+ @book.id.should == 1
91
+ @book.label.should == 'rooby'
92
+ end
93
+
94
+ it "should make ActiveResource throw an unprocessable entity exception if our Put mock throws a Dupe::UnprocessableEntity exception" do
95
+ Put %r{/books/(\d+)\.xml} do |id, put_data|
96
+ raise Dupe::UnprocessableEntity.new(:title => " must be present.") unless put_data[:title]
97
+ Dupe.find(:book) {|b| b.id == id.to_i}.merge!(put_data)
98
+ end
99
+
100
+ @ar_book.title = nil
101
+ @ar_book.save.should == false
102
+ @ar_book.errors.on_base.should_not be_empty
103
+
104
+ @ar_book.title = "Rails!"
105
+ @ar_book.save.should == true
106
+ # the following line should be true, were it not for a bug in active_resource 2.3.3 - 2.3.5
107
+ # i reported the bug here: https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/4169-activeresourcebasesave-put-doesnt-clear-out-errors
108
+ # @ar_book.errors.should be_empty
109
+ end
110
+ end
111
+
112
+ describe "#delete" do
113
+ before do
114
+ @book = Dupe.create :book, :label => 'rooby', :title => 'Rooby'
115
+ class Book < ActiveResource::Base
116
+ self.site = ''
117
+ end
118
+ @ar_book = Book.find(1)
119
+ end
120
+
121
+ it "should pass a request off to the Dupe network if the original request failed" do
122
+ Dupe.network.should_receive(:request).with(:delete, '/books/1.xml').once
123
+ @ar_book.destroy
124
+ end
125
+
126
+ it "trigger a Dupe.delete to delete the mocked resource from the duped database" do
127
+ Dupe.find(:books).length.should == 1
128
+ @ar_book.destroy
129
+ Dupe.find(:books).length.should == 0
130
+ end
131
+
132
+ it "should allow you to override the default DELETE intercept mock" do
133
+ Delete %r{/books/(\d+)\.xml} do |id|
134
+ raise StandardError, "Testing Delete override"
135
+ end
136
+
137
+ proc {@ar_book.destroy}.should raise_error(StandardError, "Testing Delete override")
138
+ end
139
+ end
140
+
141
+ end
@@ -16,6 +16,36 @@ describe Dupe::Database do
16
16
  end
17
17
  end
18
18
 
19
+ describe "delete" do
20
+ before do
21
+ Dupe.stub 10, :books
22
+ end
23
+
24
+ it "should delete the first record found if the resource name is singular and there is no conditions proc" do
25
+ Dupe.find(:books).length.should == 10
26
+ Dupe.database.delete :book
27
+ Dupe.find(:books).length.should == 9
28
+ end
29
+
30
+ it "should delete all records if the resource name is plural and there is no conditions proc" do
31
+ Dupe.find(:books).length.should == 10
32
+ Dupe.database.delete :books
33
+ Dupe.find(:books).length.should == 0
34
+ end
35
+
36
+ it "should delete all matching records if there is a conditions proc and the resource name is singular" do
37
+ Dupe.find(:books).length.should == 10
38
+ Dupe.database.delete :book, proc {|b| b.id < 3}
39
+ Dupe.find(:books).length.should == 8
40
+ end
41
+
42
+ it "should delete all matching records if there is a conditions proc and the resource name is plural" do
43
+ Dupe.find(:books).length.should == 10
44
+ Dupe.database.delete :books, proc {|b| b.id < 3}
45
+ Dupe.find(:books).length.should == 8
46
+ end
47
+ end
48
+
19
49
  describe "insert" do
20
50
  before do
21
51
  Dupe.define :book
@@ -173,15 +173,56 @@ describe Dupe do
173
173
  Dupe.network.mocks[:get].length.should == 2
174
174
 
175
175
  find_all_mock = Dupe.network.mocks[:get].first
176
- find_all_mock.verb.should == :get
176
+ find_all_mock.class.should == Dupe::Network::GetMock
177
177
  find_all_mock.url_pattern.should == %r{^/books\.xml$}
178
178
  find_all_mock.mocked_response('/books.xml').should == Dupe.find(:books).to_xml(:root => 'books')
179
179
 
180
180
  find_one_mock = Dupe.network.mocks[:get].last
181
- find_one_mock.verb.should == :get
181
+ find_one_mock.class.should == Dupe::Network::GetMock
182
182
  find_one_mock.url_pattern.should == %r{^/books/(\d+)\.xml$}
183
183
  find_one_mock.mocked_response('/books/1.xml').should == Dupe.find(:book).to_xml(:root => 'book')
184
184
  end
185
+
186
+ it "should add a POST (create resource) mock to the network" do
187
+ Dupe.network.mocks[:post].should be_empty
188
+ Dupe.define :book
189
+ Dupe.network.mocks[:post].should_not be_empty
190
+ Dupe.network.mocks[:post].length.should == 1
191
+
192
+ post_mock = Dupe.network.mocks[:post].first
193
+ post_mock.class.should == Dupe::Network::PostMock
194
+ post_mock.url_pattern.should == %r{^/books\.xml$}
195
+ resp, url = post_mock.mocked_response('/books.xml', {:title => "Rooby"})
196
+ resp.should == Dupe.find(:book).to_xml(:root => 'book')
197
+ url.should == "/books/1.xml"
198
+ end
199
+
200
+ it "should add a PUT (update resource) mock to the network" do
201
+ Dupe.network.mocks[:put].should be_empty
202
+ book = Dupe.create :book, :title => 'Rooby!'
203
+ Dupe.network.mocks[:put].should_not be_empty
204
+ Dupe.network.mocks[:put].length.should == 1
205
+
206
+ put_mock = Dupe.network.mocks[:put].first
207
+ put_mock.class.should == Dupe::Network::PutMock
208
+ put_mock.url_pattern.should == %r{^/books/(\d+)\.xml$}
209
+ resp, url = put_mock.mocked_response('/books/1.xml', {:title => "Rails!"})
210
+ resp.should == nil
211
+ url.should == "/books/1.xml"
212
+ book.title.should == "Rails!"
213
+ end
214
+
215
+ it "should add a DELETE mock to the network" do
216
+ Dupe.network.mocks[:delete].should be_empty
217
+ book = Dupe.create :book, :title => 'Rooby!'
218
+ Dupe.network.mocks[:delete].should_not be_empty
219
+ Dupe.network.mocks[:delete].length.should == 1
220
+
221
+ delete_mock = Dupe.network.mocks[:delete].first
222
+ delete_mock.class.should == Dupe::Network::DeleteMock
223
+ delete_mock.url_pattern.should == %r{^/books/(\d+)\.xml$}
224
+ end
225
+
185
226
 
186
227
  it "should honor ActiveResource site prefix's for the find(:all) and find(<id>) mocks" do
187
228
  Dupe.network.mocks[:get].should be_empty
@@ -191,12 +232,12 @@ describe Dupe do
191
232
  Dupe.network.mocks[:get].length.should == 2
192
233
 
193
234
  find_all_mock = Dupe.network.mocks[:get].first
194
- find_all_mock.verb.should == :get
235
+ find_all_mock.class.should == Dupe::Network::GetMock
195
236
  find_all_mock.url_pattern.should == %r{^/book_services/authors\.xml$}
196
237
  find_all_mock.mocked_response('/book_services/authors.xml').should == Dupe.find(:authors).to_xml(:root => 'authors')
197
238
 
198
239
  find_one_mock = Dupe.network.mocks[:get].last
199
- find_one_mock.verb.should == :get
240
+ find_one_mock.class.should == Dupe::Network::GetMock
200
241
  find_one_mock.url_pattern.should == %r{^/book_services/authors/(\d+)\.xml$}
201
242
  find_one_mock.mocked_response('/book_services/authors/1.xml').should == Dupe.find(:author).to_xml(:root => 'author')
202
243
  end
@@ -461,4 +502,21 @@ describe Dupe do
461
502
  b2.genre.name.should == 'Science Fiction'
462
503
  end
463
504
  end
505
+
506
+ describe "##delete" do
507
+ it "should remove any resources that match the conditions provided in the block" do
508
+ Dupe.stub 10, :authors
509
+ Dupe.find(:authors).length.should == 10
510
+ Dupe.delete :author
511
+ Dupe.find(:authors).length.should == 9
512
+ Dupe.delete :authors
513
+ Dupe.find(:authors).length.should == 0
514
+ Dupe.create :author, :name => 'CS Lewis'
515
+ Dupe.create :author, :name => 'Robert Lewis Stevenson'
516
+ Dupe.create :author, :name => 'Frank Herbert'
517
+ Dupe.delete(:author) {|a| a.name.match /Lewis/}
518
+ Dupe.find(:authors).length.should == 1
519
+ Dupe.find(:authors).first.name.should == 'Frank Herbert'
520
+ end
521
+ end
464
522
  end
@@ -6,26 +6,17 @@ describe Dupe::Network::Mock do
6
6
  end
7
7
 
8
8
  describe "new" do
9
- it "should require a valid REST type" do
10
- proc { Dupe::Network::Mock.new :unknown, /\//, proc {} }.should raise_error(Dupe::Network::UnknownRestVerbError)
11
- proc { Dupe::Network::Mock.new :get, /\//, proc {} }.should_not raise_error
12
- proc { Dupe::Network::Mock.new :post, /\//, proc {} }.should_not raise_error
13
- proc { Dupe::Network::Mock.new :put, /\//, proc {} }.should_not raise_error
14
- proc { Dupe::Network::Mock.new :delete, /\//, proc {} }.should_not raise_error
15
- end
16
-
17
9
  it "should require the url be a kind of regular expression" do
18
- proc { Dupe::Network::Mock.new :get, '', proc {} }.should raise_error(
10
+ proc { Dupe::Network::Mock.new '', proc {} }.should raise_error(
19
11
  ArgumentError,
20
12
  "The URL pattern parameter must be a type of regular expression."
21
13
  )
22
14
  end
23
15
 
24
- it "should set the @verb, @url, and @response parameters accordingly" do
16
+ it "should set the, @url, and @response parameters accordingly" do
25
17
  url_pattern = /\//
26
18
  response = proc {}
27
- mock = Dupe::Network::Mock.new :get, url_pattern, response
28
- mock.verb.should == :get
19
+ mock = Dupe::Network::Mock.new url_pattern, response
29
20
  mock.url_pattern.should == url_pattern
30
21
  mock.response.should == response
31
22
  end
@@ -35,7 +26,7 @@ describe Dupe::Network::Mock do
35
26
  it "should determine if a given string matches the mock's url pattern" do
36
27
  url = %r{/blogs/(\d+).xml}
37
28
  response = proc {}
38
- mock = Dupe::Network::Mock.new :get, url, response
29
+ mock = Dupe::Network::Mock.new url, response
39
30
  mock.match?('/blogs/1.xml').should == true
40
31
  mock.match?('/bogs/1.xml').should == false
41
32
  end
@@ -53,13 +44,13 @@ describe Dupe::Network::GetMock do
53
44
  url_pattern = %r{/books/(\d+)\.xml}
54
45
  response = proc {|id| Dupe.find(:book) {|b| b.id == id.to_i}}
55
46
  book = Dupe.create :book
56
- mock = Dupe::Network::GetMock.new :get, url_pattern, response
47
+ mock = Dupe::Network::GetMock.new url_pattern, response
57
48
  mock.mocked_response('/books/1.xml').should == book.to_xml(:root => 'book')
58
49
 
59
50
  proc { mock.mocked_response('/books/2.xml') }.should raise_error(Dupe::Network::GetMock::ResourceNotFoundError)
60
51
 
61
52
  Dupe.define :author
62
- mock = Dupe::Network::GetMock.new :get, %r{/authors\.xml$}, proc {Dupe.find :authors}
53
+ mock = Dupe::Network::GetMock.new %r{/authors\.xml$}, proc {Dupe.find :authors}
63
54
  mock.mocked_response('/authors.xml').should == [].to_xml(:root => 'results')
64
55
  end
65
56
 
@@ -67,7 +58,7 @@ describe Dupe::Network::GetMock do
67
58
  url_pattern = %r{/books/([a-zA-Z0-9-]+)\.xml}
68
59
  response = proc {|label| Dupe.find(:book) {|b| b.label == label}}
69
60
  book = Dupe.create :book, :label => 'rooby'
70
- mock = Dupe::Network::GetMock.new :get, url_pattern, response
61
+ mock = Dupe::Network::GetMock.new url_pattern, response
71
62
  Dupe.network.log.requests.length.should == 0
72
63
  mock.mocked_response('/books/rooby.xml')
73
64
  Dupe.network.log.requests.length.should == 1
@@ -79,7 +70,7 @@ describe Dupe::Network::GetMock do
79
70
  url_pattern = %r{/authors/(\d+)\.xml}
80
71
  response = proc { |id| Dupe.find(:author) {|a| a.id == id.to_i}}
81
72
  Dupe.define :author
82
- mock = Dupe::Network::GetMock.new :get, url_pattern, response
73
+ mock = Dupe::Network::GetMock.new url_pattern, response
83
74
  proc {mock.mocked_response('/authors/1.xml')}.should raise_error(Dupe::Network::GetMock::ResourceNotFoundError)
84
75
  end
85
76
  end
@@ -87,13 +78,13 @@ describe Dupe::Network::GetMock do
87
78
  describe "on a mock object whose response returns an empty array" do
88
79
  it "should convert the empty array to an xml array record set with root 'results'" do
89
80
  Dupe.define :author
90
- mock = Dupe::Network::GetMock.new :get, %r{/authors\.xml$}, proc {Dupe.find :authors}
81
+ mock = Dupe::Network::GetMock.new %r{/authors\.xml$}, proc {Dupe.find :authors}
91
82
  mock.mocked_response('/authors.xml').should == [].to_xml(:root => 'results')
92
83
  end
93
84
 
94
85
  it "should add a request to the Dupe::Network#log" do
95
86
  Dupe.define :author
96
- mock = Dupe::Network::GetMock.new :get, %r{/authors\.xml$}, proc {Dupe.find :authors}
87
+ mock = Dupe::Network::GetMock.new %r{/authors\.xml$}, proc {Dupe.find :authors}
97
88
  Dupe.network.log.requests.length.should == 0
98
89
  mock.mocked_response('/authors.xml')
99
90
  Dupe.network.log.requests.length.should == 1
@@ -103,13 +94,13 @@ describe Dupe::Network::GetMock do
103
94
  describe "on a mock object whose response returns an array of duped records" do
104
95
  it "should convert the array to xml" do
105
96
  Dupe.create :author
106
- mock = Dupe::Network::GetMock.new :get, %r{/authors\.xml$}, proc {Dupe.find :authors}
97
+ mock = Dupe::Network::GetMock.new %r{/authors\.xml$}, proc {Dupe.find :authors}
107
98
  mock.mocked_response('/authors.xml').should == Dupe.find(:authors).to_xml(:root => 'authors')
108
99
  end
109
100
 
110
101
  it "should add a request to the Dupe::Network#log" do
111
102
  Dupe.create :author
112
- mock = Dupe::Network::GetMock.new :get, %r{/authors\.xml$}, proc {Dupe.find :authors}
103
+ mock = Dupe::Network::GetMock.new %r{/authors\.xml$}, proc {Dupe.find :authors}
113
104
  Dupe.network.log.requests.length.should == 0
114
105
  mock.mocked_response('/authors.xml')
115
106
  Dupe.network.log.requests.length.should == 1
@@ -127,7 +118,7 @@ describe Dupe::Network::PostMock do
127
118
  describe "on a mock object whose response returns a location of a new record" do
128
119
  it "should convert the new post to xml" do
129
120
  Dupe.define :author
130
- mock = Dupe::Network::PostMock.new :post, %r{/authors\.xml$}, proc {|post_data| Dupe.create(:author, post_data)}
121
+ mock = Dupe::Network::PostMock.new %r{/authors\.xml$}, proc {|post_data| Dupe.create(:author, post_data)}
131
122
  resp, url = mock.mocked_response('/authors.xml', {:name => "Rachel"})
132
123
  resp.should == Dupe.find(:authors).first.to_xml_safe(:root => 'author')
133
124
  url.should == "/authors/1.xml"
@@ -135,11 +126,69 @@ describe Dupe::Network::PostMock do
135
126
 
136
127
  it "should add a request to the Dupe::Network#log" do
137
128
  Dupe.define :author
138
- mock = Dupe::Network::PostMock.new :post, %r{/authors\.xml$}, proc {|post_data| Dupe.create(:author, post_data)}
129
+ mock = Dupe::Network::PostMock.new %r{/authors\.xml$}, proc {|post_data| Dupe.create(:author, post_data)}
139
130
  Dupe.network.log.requests.length.should == 0
140
131
  mock.mocked_response('/authors.xml', {:name => "Rachel"})
141
132
  Dupe.network.log.requests.length.should == 1
142
133
  end
143
134
  end
144
135
  end
145
- end
136
+ end
137
+
138
+ describe Dupe::Network::PutMock do
139
+ before do
140
+ Dupe.reset
141
+ end
142
+
143
+ describe "mocked_response" do
144
+ describe "on a mock object whose response returns a location of a new record" do
145
+ before do
146
+ Dupe.define :author
147
+ @a = Dupe.create :author, :name => "Matt"
148
+ @mock = Dupe::Network::PutMock.new %r{/authors/(\d+)\.xml$}, proc {|id, put_data| Dupe.find(:author) {|a| a.id == id.to_i}.merge!(put_data)}
149
+ end
150
+
151
+ it "should convert the put to xml" do
152
+ resp, url = @mock.mocked_response('/authors/1.xml', {:name => "Rachel"})
153
+ resp.should == nil
154
+ @a.name.should == "Rachel"
155
+ url.should == "/authors/1.xml"
156
+ end
157
+
158
+ it "should add a request to the Dupe::Network#log" do
159
+ Dupe.network.log.requests.length.should == 0
160
+ @mock.mocked_response('/authors/1.xml', {:name => "Rachel"})
161
+ Dupe.network.log.requests.length.should == 1
162
+ end
163
+ end
164
+ end
165
+ end
166
+
167
+ describe Dupe::Network::DeleteMock do
168
+ before do
169
+ Dupe.reset
170
+ end
171
+
172
+ describe "mocked_response" do
173
+ describe "on a mock object whose response returns a location of a new record" do
174
+ before do
175
+ Dupe.define :author
176
+ @a = Dupe.create :author, :name => "Matt"
177
+ @mock = Dupe::Network::DeleteMock.new %r{/authors/(\d+)\.xml$}, proc {|id| Dupe.delete(:author) {|a| a.id == id.to_i}}
178
+ end
179
+
180
+ it "should convert the put to xml" do
181
+ Dupe.find(:authors).length.should == 1
182
+ resp = @mock.mocked_response('/authors/1.xml')
183
+ resp.should == nil
184
+ Dupe.find(:authors).length.should == 0
185
+ end
186
+
187
+ it "should add a request to the Dupe::Network#log" do
188
+ Dupe.network.log.requests.length.should == 0
189
+ @mock.mocked_response('/authors/1.xml')
190
+ Dupe.network.log.requests.length.should == 1
191
+ end
192
+ end
193
+ end
194
+ end
@@ -68,7 +68,7 @@ describe Dupe::Network do
68
68
  proc { @network.define_service_mock :invalid_rest_verb, // }.should raise_error(Dupe::Network::UnknownRestVerbError)
69
69
  proc { @network.define_service_mock :get, // }.should_not raise_error(Dupe::Network::UnknownRestVerbError)
70
70
  proc { @network.define_service_mock :post, // }.should_not raise_error(Dupe::Network::UnknownRestVerbError)
71
-
71
+ proc { @network.define_service_mock :put, // }.should_not raise_error(Dupe::Network::UnknownRestVerbError)
72
72
  end
73
73
 
74
74
  it "should require a valid Regexp url pattern" do
@@ -76,7 +76,6 @@ describe Dupe::Network do
76
76
  proc { @network.define_service_mock :post, 'not a regular expression' }.should raise_error(ArgumentError)
77
77
  proc { @network.define_service_mock :get, // }.should_not raise_error
78
78
  proc { @network.define_service_mock :post, // }.should_not raise_error
79
-
80
79
  end
81
80
 
82
81
  it "should create and return a new get service mock when given valid parameters" do
@@ -86,7 +85,7 @@ describe Dupe::Network do
86
85
  @network.mocks[:get].should be_empty
87
86
  mock = @network.define_service_mock verb, pattern, response
88
87
  @network.mocks[:get].should_not be_empty
89
- @network.mocks[:post].first.class == "GetMock"
88
+ @network.mocks[:get].first.class.should == Dupe::Network::GetMock
90
89
  @network.mocks[:get].length.should == 1
91
90
  @network.mocks[:get].first.should == mock
92
91
  end
@@ -98,12 +97,34 @@ describe Dupe::Network do
98
97
  @network.mocks[:post].should be_empty
99
98
  mock = @network.define_service_mock verb, pattern, response
100
99
  @network.mocks[:post].should_not be_empty
101
- @network.mocks[:post].first.class == "PostMock"
100
+ @network.mocks[:post].first.class.should == Dupe::Network::PostMock
102
101
  @network.mocks[:post].length.should == 1
103
102
  @network.mocks[:post].first.should == mock
104
103
  end
105
-
106
104
 
105
+ it "should create and return a new put service mock when given valid parameters" do
106
+ verb = :put
107
+ pattern = //
108
+ response = proc { 'test' }
109
+ @network.mocks[:put].should be_empty
110
+ mock = @network.define_service_mock verb, pattern, response
111
+ @network.mocks[:put].should_not be_empty
112
+ @network.mocks[:put].first.class.should == Dupe::Network::PutMock
113
+ @network.mocks[:put].length.should == 1
114
+ @network.mocks[:put].first.should == mock
115
+ end
116
+
117
+ it "should create and return a new delete service mock when given valid parameters" do
118
+ verb = :delete
119
+ pattern = //
120
+ response = proc { 'test' }
121
+ @network.mocks[:delete].should be_empty
122
+ mock = @network.define_service_mock verb, pattern, response
123
+ @network.mocks[:delete].should_not be_empty
124
+ @network.mocks[:delete].first.class.should == Dupe::Network::DeleteMock
125
+ @network.mocks[:delete].length.should == 1
126
+ @network.mocks[:delete].first.should == mock
127
+ end
107
128
  end
108
129
 
109
- end
130
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dupe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Parker
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-03-12 00:00:00 -05:00
12
+ date: 2010-03-14 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency