dupe 0.5.0 → 0.5.1

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.
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