dupe 0.6.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -19,7 +19,12 @@ module ActiveResource #:nodoc:
19
19
  response = request(:get, path, build_request_headers(headers, :get, self.site.merge(path)))
20
20
  ActiveResource::HttpMock.delete_mock(:get, path)
21
21
  end
22
- format.decode(response.body)
22
+
23
+ if ActiveResource::VERSION::MAJOR == 3 && ActiveResource::VERSION::MINOR >= 1
24
+ response
25
+ else
26
+ format.decode(response.body)
27
+ end
23
28
  end
24
29
 
25
30
  def post(path, body = '', headers = {}) #:nodoc:
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe ActiveResource::Connection do
4
4
  before do
@@ -9,7 +9,8 @@ describe ActiveResource::Connection do
9
9
  before do
10
10
  @book = Dupe.create :book, :title => 'Rooby', :label => 'rooby'
11
11
  class Book < ActiveResource::Base
12
- self.site = 'http://www.example.com'
12
+ self.site = 'http://www.example.com'
13
+ self.format = :xml
13
14
  end
14
15
  end
15
16
 
@@ -81,7 +82,8 @@ describe ActiveResource::Connection do
81
82
  context "put methods that return HTTP 204" do
82
83
  before(:each) do
83
84
  class ExpirableBook < ActiveResource::Base
84
- self.site = 'http://www.example.com'
85
+ self.site = 'http://www.example.com'
86
+ self.format = :xml
85
87
  attr_accessor :state
86
88
 
87
89
  def expire_copyrights!
@@ -140,7 +142,8 @@ describe ActiveResource::Connection do
140
142
  before do
141
143
  @book = Dupe.create :book, :label => 'rooby', :title => 'Rooby'
142
144
  class Book < ActiveResource::Base
143
- self.site = 'http://www.example.com'
145
+ self.site = 'http://www.example.com'
146
+ self.format = :xml
144
147
  end
145
148
  @ar_book = Book.find(1)
146
149
  end
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe Dupe::Model::Schema::AttributeTemplate do
4
4
  describe "new" do
@@ -170,4 +170,4 @@ describe Dupe::Model::Schema::AttributeTemplate do
170
170
  end
171
171
  end
172
172
  end
173
- end
173
+ end
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe Dupe::Database do
4
4
  before do
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe Dupe do
4
4
  before do
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe HashPruner do
4
4
  describe "#prune" do
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe Dupe::Network::Log do
4
4
  before do
@@ -75,4 +75,4 @@ describe Dupe::Network::Log do
75
75
  @log.requests.should be_empty
76
76
  end
77
77
  end
78
- end
78
+ end
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe Dupe::Network::Log::Request do
4
4
  describe "##new" do
@@ -19,4 +19,4 @@ describe Dupe::Network::Log::Request do
19
19
  " who's there?"
20
20
  end
21
21
  end
22
- end
22
+ end
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe "Mock Definition Methods" do
4
4
  before do
@@ -55,4 +55,4 @@ describe "Mock Definition Methods" do
55
55
  Dupe.network.request(:post, '/books.xml', book_post).should == [Dupe.find(:book) {|b| b.id == 4}.to_xml_safe(:root => 'book'), "/books/4.xml"]
56
56
  end
57
57
  end
58
- end
58
+ end
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe Dupe::Network::Mock do
4
4
  before do
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe Dupe::Model do
4
4
  describe "new" do
@@ -92,4 +92,4 @@ describe Dupe::Model do
92
92
  book.author.should == 'Author: Matt Parker'
93
93
  end
94
94
  end
95
- end
95
+ end
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe Dupe::Network do
4
4
 
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe Dupe::Database::Record do
4
4
  describe "new" do
@@ -67,4 +67,4 @@ describe Dupe::Database::Record do
67
67
  end
68
68
  end
69
69
 
70
- end
70
+ end
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe Dupe::Network::RestValidation do
4
4
  before do
@@ -14,4 +14,4 @@ describe Dupe::Network::RestValidation do
14
14
  }.should raise_error(Dupe::Network::UnknownRestVerbError)
15
15
  end
16
16
  end
17
- end
17
+ end
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe Dupe::Model::Schema do
4
4
  describe "new" do
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe Sequence do
4
4
  describe "new" do
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe String do
4
4
  describe "plural?" do
@@ -28,4 +28,4 @@ describe String do
28
28
  "apples\noranges".indent(2).should == " apples\n oranges"
29
29
  end
30
30
  end
31
- end
31
+ end
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe Symbol do
4
4
  describe "plural?" do
@@ -14,4 +14,4 @@ describe Symbol do
14
14
  :apple.singular?.should == true
15
15
  end
16
16
  end
17
- end
17
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,8 +1,4 @@
1
1
  $LOAD_PATH.unshift './lib'
2
2
  require 'rubygems'
3
- gem 'activeresource'
4
- gem 'rspec'
5
- gem 'cucumber'
6
- require 'cucumber'
7
3
  require 'active_resource'
8
4
  require 'dupe'
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dupe
3
3
  version: !ruby/object:Gem::Version
4
- hash: 7
5
- prerelease: false
4
+ hash: 23
5
+ prerelease:
6
6
  segments:
7
+ - 1
7
8
  - 0
8
- - 6
9
9
  - 0
10
- version: 0.6.0
10
+ version: 1.0.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Matt Parker
@@ -15,8 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-01-08 00:00:00 -05:00
19
- default_executable:
18
+ date: 2011-08-04 00:00:00 Z
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
22
21
  name: activeresource
@@ -24,31 +23,38 @@ dependencies:
24
23
  requirement: &id001 !ruby/object:Gem::Requirement
25
24
  none: false
26
25
  requirements:
27
- - - ">="
26
+ - - ~>
28
27
  - !ruby/object:Gem::Version
29
- hash: -1848230022
28
+ hash: 7
30
29
  segments:
31
30
  - 3
32
31
  - 0
33
- - 0
34
- - beta2
35
- version: 3.0.0.beta2
32
+ version: "3.0"
36
33
  type: :runtime
37
34
  version_requirements: *id001
38
- description: |-
39
- Dupe rides on top of ActiveResource to allow you to cuke the client side of
40
- a service-oriented app without having to worry about whether or not the service
41
- is live or available while cuking.
35
+ - !ruby/object:Gem::Dependency
36
+ name: rspec
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :development
48
+ version_requirements: *id002
49
+ description: TDD your services outside in by starting at the client, then working your way back to the server.
42
50
  email: moonmaster9000@gmail.com
43
51
  executables: []
44
52
 
45
53
  extensions: []
46
54
 
47
- extra_rdoc_files:
48
- - README.rdoc
55
+ extra_rdoc_files: []
56
+
49
57
  files:
50
- - README.rdoc
51
- - lib/dupe.rb
52
58
  - lib/dupe/active_resource_extensions.rb
53
59
  - lib/dupe/attribute_template.rb
54
60
  - lib/dupe/cucumber_hooks.rb
@@ -67,10 +73,7 @@ files:
67
73
  - lib/dupe/singular_plural_detection.rb
68
74
  - lib/dupe/string.rb
69
75
  - lib/dupe/symbol.rb
70
- - rails_generators/dupe/dupe_generator.rb
71
- - rails_generators/dupe/templates/custom_mocks.rb
72
- - rails_generators/dupe/templates/definitions.rb
73
- - rails_generators/dupe/templates/load_dupe.rb
76
+ - lib/dupe.rb
74
77
  - spec/lib_specs/active_resource_extensions_spec.rb
75
78
  - spec/lib_specs/attribute_template_spec.rb
76
79
  - spec/lib_specs/database_spec.rb
@@ -89,7 +92,6 @@ files:
89
92
  - spec/lib_specs/string_spec.rb
90
93
  - spec/lib_specs/symbol_spec.rb
91
94
  - spec/spec_helper.rb
92
- has_rdoc: true
93
95
  homepage: http://github.com/moonmaster9000/dupe
94
96
  licenses: []
95
97
 
@@ -119,10 +121,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
119
121
  requirements: []
120
122
 
121
123
  rubyforge_project:
122
- rubygems_version: 1.3.7
124
+ rubygems_version: 1.8.5
123
125
  signing_key:
124
126
  specification_version: 3
125
- summary: A tool that helps you mock services while cuking.
127
+ summary: Dupe - a testing library for ActiveResource
126
128
  test_files:
127
129
  - spec/lib_specs/active_resource_extensions_spec.rb
128
130
  - spec/lib_specs/attribute_template_spec.rb
data/README.rdoc DELETED
@@ -1,642 +0,0 @@
1
- = Dupe
2
-
3
- There are lots of great tools out there to ease the burden of prototyping ActiveRecord objects while cuking your application (e.g., thoughtbot's {"Factory Girl"}[http://www.thoughtbot.com/projects/factory_girl]).
4
-
5
- But what about prototyping ActiveResource records? That's where Dupe steps in.
6
-
7
- == Motivation
8
-
9
- If you're going to create a service-oriented rails app with ActiveResource, why not cuke the front end first?
10
- Let the behavior of the front-end drive the services you build on the backend. That's exactly what Dupe makes possible.
11
-
12
- == Installation
13
-
14
- If you want to install this for use in something other than a rails project, simply:
15
-
16
- # gem install dupe
17
-
18
- == Rails 3
19
-
20
- Dupe versions 0.6.0 and greater only work with Rails 3 / ActiveResource 3. If you're working on a Rails 2.* project, use Dupe version 0.5.3.
21
-
22
- = Tutorial
23
-
24
- Checkout the {dupe example application}[http://github.com/moonmaster9000/dupe_example_app] for a tutorial on cuking an application with
25
- ActiveResource and Dupe.
26
-
27
- = Features
28
-
29
- ==Creating resources
30
-
31
- Dupe allows you to quickly create resources, even if you have yet to define them. For example:
32
-
33
- irb# require 'dupe'
34
- ==> true
35
-
36
- irb# b = Dupe.create :book, :title => '2001'
37
- ==> <#Duped::Book title="2001" id=1>
38
-
39
- irb# a = Dupe.create :author, :name => 'Arthur C. Clarke'
40
- ==> <#Duped::Author name="Arthur C. Clarke" id=1>
41
-
42
- irb# b.author
43
- ==> nil
44
-
45
- irb# b.author = a
46
- ==> <#Duped::Author name="Arthur C. Clarke" id=1>
47
-
48
- irb# b
49
- ==> <#Duped::Book author=<#Duped::Author name="Arthur C. Clarke" id=1> title="2001" id=1>
50
-
51
-
52
- Dupe also provides a way for us to quickly to generate a large number of resources. For example, suppose we have a cucumber scenario that tests paginating through lists of books. To easily create 50 unique books, we could use the Dupe.stub method:
53
-
54
- irb# Dupe.stub 50, :books, :like => {:title => proc {|n| "book ##{n} title"}}
55
- ==> [<#Duped::Book title="book #1 title" id=1>, <#Duped::Book title="book #2 title" id=2>, ...]
56
-
57
- Notice that each book has a unique title, achieved by passing the "proc {|n| "book ##{n} title"}" as the value for the title.
58
-
59
-
60
- ==Finding Resources
61
-
62
- Dupe also has a built-in querying system for finding resources you create. In your tests / cucumber step definitions, you'll most likely be using this approach for finding resources. If you're wondering how your app (i.e., ActiveResource) can find resources you create, skip down to the section on ActiveResource.
63
-
64
- irb# a = Dupe.create :author, :name => 'Monkey'
65
- ==> <#Duped::Author name="Monkey" id=1>
66
-
67
- irb# b = Dupe.create :book, :title => 'Bananas', :author => a
68
- ==> <#Duped::Book author=<#Duped::Author name="Monkey" id=1> title="Bananas" id=1>
69
-
70
- irb# Dupe.find(:author) {|a| a.name == 'Monkey'}
71
- ==> <#Duped::Author name="Monkey" id=1>
72
-
73
- irb# Dupe.find(:book) {|b| b.author.name == 'Monkey'}
74
- ==> <#Duped::Book author=<#Duped::Author name="Monkey" id=1> title="Bananas" id=1>
75
-
76
- irb# Dupe.find(:author) {|a| a.id == 1}
77
- ==> <#Duped::Author name="Monkey" id=1>
78
-
79
- irb# Dupe.find(:author) {|a| a.id == 2}
80
- ==> nil
81
-
82
- In all cases, notice that we provided the singular form of a model name to Dupe.find. This ensures that we either get back either a single resource (if the query was successful), or _nil_.
83
-
84
- If we'd like to find several resources, we can use the plural form of the model name. For example:
85
-
86
- irb# a = Dupe.create :author, :name => 'Monkey', :published => true
87
- ==> <#Duped::Author published=true name="Monkey" id=1>
88
-
89
- irb# b = Dupe.create :book, :title => 'Bananas', :author => a
90
- ==> <#Duped::Book author=<#Duped::Author published=true name="Monkey" id=1> title="Bananas" id=1>
91
-
92
- irb# Dupe.create :author, :name => 'Tiger', :published => false
93
- ==> <#Duped::Author published=false name="Tiger" id=2>
94
-
95
- irb# Dupe.find(:authors)
96
- ==> [<#Duped::Author published=true name="Monkey" id=1>, <#Duped::Author published=false name="Tiger" id=2>]
97
-
98
- irb# Dupe.find(:authors) {|a| a.published == true}
99
- ==> [<#Duped::Author published=true name="Monkey" id=1>]
100
-
101
- irb# Dupe.find(:books)
102
- ==> [<#Duped::Book author=<#Duped::Author published=true name="Monkey" id=1> title="Bananas" id=1>]
103
-
104
- irb# Dupe.find(:books) {|b| b.author.published == false}
105
- ==> []
106
-
107
- Notice that by using the plural form of the model name, we ensure that we receive back an array - even in the case that the query did not find any results (it simply returns an empty array).
108
-
109
-
110
- ==Finding or Creating Resources
111
-
112
- You might have seen this one coming.
113
-
114
- Let's assume no genres currently exist. If we call the "find_or_create" method, it will create a new :genre.
115
-
116
- irb# Dupe.find_or_create :genre
117
- ==> <#Duped::Genre id=1>
118
-
119
- If we call it again, it will find the :genre we already created:
120
-
121
- irb# Dupe.find_or_create :genre
122
- ==> <#Duped::Genre id=1>
123
-
124
- You can also pass conditions to find_or_create as a hash:
125
-
126
- irb# Dupe.find_or_create :genre, :name => 'Science Fiction', :label => 'sci-fi'
127
- ==> <#Duped::Genre label="sci-fi" name="Science Fiction" id=2>
128
-
129
- irb# Dupe.find_or_create :genre, :name => 'Science Fiction', :label => 'sci-fi'
130
- ==> <#Duped::Genre label="sci-fi" name="Science Fiction" id=2>
131
-
132
- == Defining a resource
133
-
134
- Though often we may get away with creating resources willy-nilly, it's sometimes quite handy to define a resource, giving it default attributes and callbacks.
135
-
136
- === Attributes with default values
137
-
138
- Suppose we're creating a 'book' resource. Perhaps our app assumes every book has a title, so let's define a book resource
139
- that specifies just that:
140
-
141
- irb# Dupe.define :book do |attrs|
142
- --# attrs.title 'Untitled'
143
- --# attrs.author
144
- --# end
145
- ==> #<Dupe::Model:0x17b2694 ...>
146
-
147
- Basically, this reads like "A book resource has a title attribute with a default value of 'Untitled'. It also has an author attribute." Thus, if we create a book and we don't specify a "title" attribute, it should create a "title" for us, as well as provide a nil "author" attribute.
148
-
149
- irb# b = Dupe.create :book
150
- ==> <#Duped::Book author=nil title="Untitled" id=1>
151
-
152
-
153
- If we provide our own title, it should allow us to override the default value:
154
-
155
- irb# b = Dupe.create :book, :title => 'Monkeys!'
156
- ==> <#Duped::Book author=nil title="Monkeys!" id=2>
157
-
158
- === Attributes with procs as default values
159
-
160
- Sometimes it might be convenient to procedurally define the default value for an attribute:
161
-
162
- irb# Dupe.define :book do |attrs|
163
- --# attrs.title 'Untitled'
164
- --# attrs.author
165
- --# attrs.isbn do
166
- --# rand(1000000)
167
- --# end
168
- --# end
169
-
170
- Now, every time we create a book, it will get assigned a random ISBN number:
171
-
172
- irb# b = Dupe.create :book
173
- ==> <#Duped::Book author=nil title="Untitled" id=1 isbn=895825>
174
-
175
- irb# b = Dupe.create :book
176
- ==> <#Duped::Book author=nil title="Untitled" id=2 isbn=606472>
177
-
178
- Another common use of this feature is for associations. Lets suppose we'd like to make sure that a book always has a genre, but a genre should be its own resource. We can accomplish that by taking advantage of Dupe's "find_or_create" method:
179
-
180
- irb# Dupe.define :book do |attrs|
181
- --# attrs.title 'Untitled'
182
- --# attrs.author
183
- --# attrs.isbn do
184
- --# rand(1000000)
185
- --# end
186
- --# attrs.genre do
187
- --# Dupe.find_or_create :genre
188
- --# end
189
- --# end
190
-
191
- Now when we create books, Dupe will associate them with an existing genre (the first one it finds), or if none yet exist, it will create one.
192
-
193
- First, let's confirm that no genres currently exist:
194
-
195
- irb# Dupe.find :genre
196
- Dupe::Database::TableDoesNotExistError: The table ':genre' does not exist.
197
- from /Library/Ruby/Gems/1.8/gems/dupe-0.4.0/lib/dupe/database.rb:30:in `select'
198
- from /Library/Ruby/Gems/1.8/gems/dupe-0.4.0/lib/dupe/dupe.rb:295:in `find'
199
- from (irb):135
200
-
201
- Next, let's create a book:
202
-
203
- irb# b = Dupe.create :book
204
- ==> <#Duped::Book genre=<#Duped::Genre id=1> author=nil title="Untitled" id=1 isbn=62572>
205
-
206
- Notice that it create a genre. If we tried to do another Dupe.find for the genre:
207
-
208
- irb# Dupe.find :genre
209
- ==> <#Duped::Genre id=1>
210
-
211
- Now, if create another book, it will associate with the genre that was just created:
212
-
213
- irb# b = Dupe.create :book
214
- ==> <#Duped::Book genre=<#Duped::Genre id=1> author=nil title="Untitled" id=2 isbn=729317>
215
-
216
-
217
-
218
- === Attributes with transformers
219
-
220
- Occasionally, you may find it useful to have attribute values transformed upon creation.
221
-
222
- For example, suppose we want to create books with publish dates. In our cucumber scenario's, we may prefer to simply specify a date like '2009-12-29', and have that automatically transformed into an ruby Date object.
223
-
224
- irb# Dupe.define :book do |attrs|
225
- --# attrs.title 'Untitled'
226
- --# attrs.author
227
- --# attrs.isbn do
228
- --# rand(1000000)
229
- --# end
230
- --# attrs.publish_date do |publish_date|
231
- --# Date.parse(publish_date)
232
- --# end
233
- --# end
234
-
235
- Now, let's create a book:
236
-
237
- irb# b = Dupe.create :book, :publish_date => '2009-12-29'
238
- ==> <#Duped::Book author=nil title="Untitled" publish_date=Tue, 29 Dec 2009 id=1 isbn=826291>
239
-
240
- irb# b.publish_date
241
- ==> Tue, 29 Dec 2009
242
-
243
- irb# b.publish_date.class
244
- ==> Date
245
-
246
-
247
- === Uniquify attributes
248
-
249
- If you'd just like to make sure that some attributes get a unique value, then you can use the uniquify
250
- method:
251
-
252
- irb# Dupe.define :book do |attrs|
253
- --# attrs.uniquify :title, :genre, :author
254
- --# end
255
-
256
- Now, Dupe will do its best to assign unique values to the :title, :genre, and :author attributes on
257
- any records it creates:
258
-
259
- irb# b = Dupe.create :book
260
- ==> <#Duped::Book author="book 1 author" title="book 1 title" genre="book 1 genre" id=1>
261
-
262
- irb# b2 = Dupe.create :book, :title => 'Rooby'
263
- ==> <#Duped::Book author="book 2 author" title="Rooby" genre="book 2 genre" id=2>
264
-
265
-
266
- === Sequences
267
-
268
- The "uniquify" method is great if don't care too much about the format of the values it creates. But what if you'd like to ensure
269
- that the value of an attribute conforms to a specific format?
270
-
271
- irb# Dupe.sequence :email do |n|
272
- --# "email-#{n}@somewhere.com"
273
- --# end
274
-
275
- irb# Dupe.define :user do |user|
276
- --# user.uniquify :name
277
- --# user.email do
278
- --# Dupe.next :email
279
- --# end
280
- --# end
281
-
282
- irb# Dupe.create :user
283
- ==> <#Duped::User name="user 1 name" id=1 email="email-1@somewhere.com">
284
-
285
- irb# Dupe.create :user
286
- ==> <#Duped::User name="user 2 name" id=2 email="email-2@somewhere.com">
287
-
288
-
289
- === Callbacks
290
-
291
- Suppose we'd like to make sure that our books get a unique label. We can accomplish that with an after_create callback:
292
-
293
- irb# Dupe.define :book do |attrs|
294
- --# attrs.title 'Untitled'
295
- --# attrs.author
296
- --# attrs.isbn do
297
- --# rand(1000000)
298
- --# end
299
- --# attrs.publish_date do |publish_date|
300
- --# Date.parse(publish_date)
301
- --# end
302
- --# attrs.after_create do |book|
303
- --# book.label = book.title.downcase.gsub(/\ +/, '-') + "--#{book.id}"
304
- --# end
305
- --# end
306
-
307
- irb# b = Dupe.create :book, :title => 'Rooby on Rails'
308
- ==> <#Duped::Book author=nil label="rooby-on-rails--1" title="Rooby on Rails" publish_date=nil id=1 isbn=842518>
309
-
310
-
311
-
312
-
313
-
314
- = ActiveResource
315
-
316
- So how does Dupe actually help us to spec/test ActiveResource-based applications? It uses a simple, yet sophisticated "intercept-mocking" technique, whereby failed network requests sent by ActiveResource fallback to the "Duped" network. Consider the following:
317
-
318
- irb# Dupe.create :book, :title => 'Monkeys!'
319
- ==> <#Duped::Book title="Monkeys!" id=1>
320
-
321
- irb# class Book < ActiveResource::Base; self.site = ''; end
322
- ==> ""
323
-
324
- irb# Book.find(1)
325
- ==> #<Book:0x1868a20 @attributes={"title"=>"Monkeys!", "id"=>1}, prefix_options{}
326
-
327
- Voila! When the _Book_ class was unable to find the book with id 1, it asked Dupe if it knew about any book resources with id 1. Check out the Dupe network log for a clue as to what happened behind the scenes:
328
-
329
- irb# puts Dupe.network.log.pretty_print
330
-
331
- Logged Requests:
332
- Request: GET /books/1.xml
333
- Response:
334
- <?xml version="1.0" encoding="UTF-8"?>
335
- <book>
336
- <title>Monkeys!</title>
337
- <id type="integer">1</id>
338
- </book>
339
-
340
- Similarly:
341
-
342
- irb# Book.find(:all)
343
- ==> [#<Book:0x185608c @attributes={"title"=>"Monkeys!", "id"=>1}, prefix_options{}]
344
-
345
- irb# puts Dupe.network.log.pretty_print
346
-
347
- Logged Requests:
348
- Request: GET /books.xml
349
- Response:
350
- <?xml version="1.0" encoding="UTF-8"?>
351
- <books type="array">
352
- <book>
353
- <title>Monkeys!</title>
354
- <id type="integer">1</id>
355
- </book>
356
- </books>
357
-
358
-
359
- ==Intercept Mocking
360
-
361
- Dupe knew how to handle simple find by id and find :all lookups from ActiveResource. But what about other requests we might potentially make?
362
-
363
- ===GET requests
364
-
365
- In this section, you'll learn how to mock custom GET requests.
366
-
367
- irb# Dupe.create :author, :name => 'Monkey', :published => true
368
- ==> <#Duped::Author name="Monkey" published=true id=1>
369
-
370
- irb# Dupe.create :author, :name => 'Tiger', :published => false
371
- ==> <#Duped::Author name="Tiger" published=false id=2>
372
-
373
- irb# class Author < ActiveResource::Base; self.site = ''; end
374
- ==> ""
375
-
376
- irb# Author.find :all, :from => :published
377
- ==> Dupe::Network::RequestNotFoundError: No mocked service response found for '/authors/published.xml'
378
-
379
- Obviously, Dupe had no way of anticipating this possibility. However, you can create your own custom intercept mock for this:
380
-
381
- irb# Get %r{/authors/published.xml} do
382
- --# Dupe.find(:authors) {|a| a.published == true}
383
- --# end
384
- ==> #<Dupe::Network::Mock:0x1833e88 @url_pattern=/\/authors\/published.xml/, @verb=:get, @response=#<Proc:0x01833f14@(irb):13>
385
-
386
- irb# Author.find :all, :from => :published
387
- ==> [#<Author:0x1821d3c @attributes={"name"=>"Monkey", "published"=>true, "id"=>1}, prefix_options{}]
388
-
389
- irb# puts Dupe.network.log.pretty_print
390
-
391
- Logged Requests:
392
- Request: GET /authors/published.xml
393
- Response:
394
- <?xml version="1.0" encoding="UTF-8"?>
395
- <authors type="array">
396
- <author>
397
- <name>Monkey</name>
398
- <published type="boolean">true</published>
399
- <id type="integer">1</id>
400
- </author>
401
- </authors>
402
-
403
-
404
- The "Get" method requires a url pattern and a block. In most cases, your block will return a Dupe.find result. Internally, Dupe will transform that into XML. However, if your "Get" block returns a string, Dupe will use that as the response body and not attempt to do any transformations on it.
405
-
406
- Suppose instead the service expected us to pass published as a query string parameter:
407
-
408
- irb# Author.find :all, :params => {:published => true}
409
- Dupe::Network::RequestNotFoundError: No mocked service response found for '/authors.xml?published=true'
410
- from /Library/Ruby/Gems/1.8/gems/dupe-0.4.0/lib/dupe/network.rb:32:in `match'
411
- from /Library/Ruby/Gems/1.8/gems/dupe-0.4.0/lib/dupe/network.rb:17:in `request'
412
- from /Library/Ruby/Gems/1.8/gems/dupe-0.4.0/lib/dupe/active_resource_extensions.rb:15:in `get'
413
- from /Library/Ruby/Gems/1.8/gems/activeresource-2.3.5/lib/active_resource/base.rb:639:in `find_every'
414
- from /Library/Ruby/Gems/1.8/gems/activeresource-2.3.5/lib/active_resource/base.rb:582:in `find'
415
- from (irb):18
416
-
417
- We can mock this with the following:
418
-
419
- irb# Get %r{/authors\.xml\?published=(true|false)$} do |published|
420
- --# if published == 'true'
421
- --# Dupe.find(:authors) {|a| a.published == true}
422
- --# else
423
- --# Dupe.find(:authors) {|a| a.published == false}
424
- --# end
425
- --# end
426
-
427
- irb# Author.find :all, :params => {:published => true}
428
- ==> [#<Author:0x17db094 @attributes={"name"=>"Monkey", "published"=>true, "id"=>1}, prefix_options{}]
429
-
430
- irb# Author.find :all, :params => {:published => false}
431
- ==> [#<Author:0x17c68c4 @attributes={"name"=>"Tiger", "published"=>false, "id"=>2}, prefix_options{}]
432
-
433
- irb# puts Dupe.network.log.pretty_print
434
-
435
- Logged Requests:
436
- Request: GET /authors.xml?published=true
437
- Response:
438
- <?xml version="1.0" encoding="UTF-8"?>
439
- <authors type="array">
440
- <author>
441
- <name>Monkey</name>
442
- <published type="boolean">true</published>
443
- <id type="integer">1</id>
444
- </author>
445
- </authors>
446
-
447
- Request: GET /authors.xml?published=false
448
- Response:
449
- <?xml version="1.0" encoding="UTF-8"?>
450
- <authors type="array">
451
- <author>
452
- <name>Tiger</name>
453
- <published type="boolean">false</published>
454
- <id type="integer">2</id>
455
- </author>
456
- </authors>
457
-
458
- ===POST requests
459
-
460
- Out of the box you get a POST intercept mock:
461
-
462
- irb# Dupe.define :author
463
-
464
- irb# class Author < ActiveResource::Base; self.site = ''; end
465
- ==> ""
466
-
467
- irb# Author.create :name => "CS Lewis"
468
- ==> #<Author:0x1a4ca58 @attributes={"name"=>"CS Lewis", "id"=>1}, @prefix_options={}>
469
-
470
- Author.create sent a network POST to /authors.xml and Dupe responded by creating the resource with the requested parameters:
471
-
472
- irb# Dupe.find :authors
473
- ==> [<#Duped::Author name="CS Lewis" id=1>]
474
-
475
- You can also overwrite the default POST intercept mock for your resource by using the Post method:
476
-
477
- irb# Post %r{/authors\.xml} do |post_data|
478
- raise Dupe::UnprocessableEntity.new(:name => " must be present.") unless post_data["name"]
479
- Dupe.create :author, post_data
480
- end
481
- ==> #<Dupe::Network::PostMock:0x1a1afe4 @url_pattern=/\/authors\.xml/, @response=#<Proc:0x01a1b084@(irb):13>, @verb=:post>
482
-
483
- Now, when you try to create an Author without a name, it will respond with the appropriate mocked errors.
484
-
485
- irb# Dupe.find(:authors)
486
- ==> []
487
-
488
- irb# a = Author.create
489
- ==> a = #<Author:0x1a19fb8 @attributes={}, @errors=#<ActiveResource::Errors:0x1a10bc0 @errors={"base"=>["name must be present."]}, @base=#<Author:0x1a19fb8 ...>>, @prefix_options={}>
490
-
491
- irb# a = a.valid?
492
- ==> false
493
-
494
- irb# a = a.new?
495
- ==> true
496
-
497
- Because our custom Post mock determined that the resource was invalid, Dupe did not mock the resource:
498
-
499
- irb# Dupe.find(:authors)
500
- ==> []
501
-
502
- When we create the Author with the required attributes, it will now be considered valid.
503
-
504
- irb# a = Author.create :name => "CS Lewis"
505
- ==> #<Author:0x19f1edc @attributes={"name"=>"CS Lewis", "id"=>1}, @prefix_options={}>
506
-
507
- irb# a.valid?
508
- ==> true
509
-
510
- irb# a.new?
511
- ==> false
512
-
513
- Since our custom Post mock considered the resource valid, it went ahead and created the resource:
514
-
515
- irb# Dupe.find(:authors)
516
- ==> [<#Duped::Author name="CS Lewis" id=1>]
517
-
518
-
519
- ===PUT requests
520
-
521
- In ActiveResource, when you update a resource that already exists via the "save" method, it translates to a PUT request.
522
- Dupe provides basic PUT intercept mocks out of the box, and like GET and POST mocks,
523
- it allows you to override the default PUT intercept mock, and create new ones.
524
-
525
- Let's again examine an "author" resource:
526
-
527
- irb# Dupe.define :author
528
-
529
- irb# class Author < ActiveResource::Base; self.site = ''; end
530
- ==> ""
531
-
532
- irb# Author.create :name => "CS Lewis" # --> Dupe intercepts this POST request
533
- ==> #<Author:0x1a4ca58 @attributes={"name"=>"CS Lewis", "id"=>1}, @prefix_options={}>
534
-
535
- irb# Dupe.find :authors
536
- ==> [<#Duped::Author name="CS Lewis" id=1>]
537
-
538
- irb# a = Author.find 1 # --> Dupe intercepts this GET request
539
- ==> #<Author:0x1a4ca58 @attributes={"name"=>"CS Lewis", "id"=>1}, @prefix_options={}>
540
-
541
- 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
542
- intercept mocking). Now, let's attempt to update (PUT) the resource:
543
-
544
- irb# a.name = "Frank Herbert"
545
-
546
- irb# a.save # --> Dupe intercepts this PUT request
547
- ==> true
548
-
549
- Dupe intercepted the PUT request that ActiveResource attempted to send, and updated the Duped resource accordingly:
550
-
551
- irb# a
552
- ==> #<Author:0x1a4ca58 @attributes={"name"=>"Frank Herbert", "id"=>1}, @prefix_options={}>
553
-
554
- irb# Dupe.find :authors
555
- ==> [<#Duped::Author name="Frank Herbert" id=1>]
556
-
557
- You can also overwrite the default PUT intercept mock for your resource by using the "Put" method:
558
-
559
- irb# Put %r{/authors/(\d+)\.xml} do |id, put_data|
560
- raise Dupe::UnprocessableEntity.new(:name => " must be present.") unless put_data[:name]
561
- Dupe.find(:author) {|a| a.id == id.to_i}.merge! put_data
562
- end
563
- ==> #<Dupe::Network::PostMock:0x1a1afe4 @url_pattern=/\/authors\.xml/, @response=#<Proc:0x01a1b084@(irb):13>, @verb=:post>
564
-
565
- Now, if we try to update our Author without a name, it will respond with the appropriate errors.
566
-
567
- irb# Dupe.find :authors
568
- ==> [<#Duped::Author name="Frank Herbert" id=1>]
569
-
570
- irb# a = Author.find 1 # --> Dupe intercepts this GET request
571
- ==> #<Author:0x1a4ca58 @attributes={"name"=>"Frank Herbert", "id"=>1}, @prefix_options={}>
572
-
573
- irb# a.name = nil
574
-
575
- irb# a.save
576
- ==> false
577
-
578
- irb# a.errors.on_base
579
- ==> ["name must be present"]
580
-
581
- Since our Put intercept mock raise the Dupe::UnprocessableEntity exception,
582
- the underlying Duped record remains unchanged, just as we would expect the real service to have operated:
583
-
584
- irb# Dupe.find :authors
585
- ==> [<#Duped::Author name="Frank Herbert" id=1>]
586
-
587
- We can, of course, at this point still update the name to a non-nil value and attempt to save it again:
588
-
589
- irb# a.name = "Matt Parker"
590
-
591
- irb# a.save
592
- ==> true
593
-
594
- irb# Dupe.find :authors
595
- ==> [<#Duped::Author name="Matt Parker" id=1>]
596
-
597
- ===DELETE requests
598
-
599
- As you might have guessed, Dupe also supports DELETE intercept mocking (ActiveResource::Base#destroy):
600
-
601
- irb# Dupe.define :author
602
-
603
- irb# class Author < ActiveResource::Base; self.site = ''; end
604
- ==> ""
605
-
606
- irb# Author.create :name => "CS Lewis" # --> Dupe intercepts this POST request
607
- ==> #<Author:0x1a4ca58 @attributes={"name"=>"CS Lewis", "id"=>1}, @prefix_options={}>
608
-
609
- irb# Dupe.find :authors
610
- ==> [<#Duped::Author name="CS Lewis" id=1>]
611
-
612
- irb# a = Author.find 1 # --> Dupe intercepts this GET request
613
- ==> #<Author:0x1a4ca58 @attributes={"name"=>"CS Lewis", "id"=>1}, @prefix_options={}>
614
-
615
- irb# a.destroy
616
- ==> #<ActiveResource::Response:0x181c1c0 @body="", @message="200", @code=200, @headers={"Content-Length"=>"0"}
617
-
618
- irb# Dupe.find :authors
619
- ==> []
620
-
621
- And also, as you might have guessed, you can override the default DELETE intercept mock for a resource:
622
-
623
- irb# Delete %r{/books/(\d+)\.xml} do |id|
624
- puts "deleting the book with id #{id}"
625
- Dupe.delete(:book) {|b| b.id == id.to_i}
626
- end
627
-
628
- irb# a = Author.create :name => "some author"
629
-
630
- irb# a.destroy
631
- ==> "deleting the book with id 1"
632
-
633
-
634
- == More
635
-
636
- Consult the API documentation at http://moonmaster9000.github.com/dupe/api/
637
-
638
-
639
- == TODO List
640
-
641
- * We need "Put", "Post", and "Delete", and "Head" intercept mocking methods. Currently we only have "Get".
642
- * We need a rake task that will run your cucumber scenarios and create service documentation based on the dupe log output (i.e., example requests and example responses) that the programmers implementing the service can use as a reference.
@@ -1,20 +0,0 @@
1
- class DupeGenerator < Rails::Generator::Base
2
- def manifest
3
- record do |m|
4
- # make sure the features and features/support directories exist
5
- m.directory 'features/support'
6
- m.directory 'features/dupe'
7
- m.directory 'features/dupe/custom_mocks'
8
- m.directory 'features/dupe/definitions'
9
-
10
- # copy the custom_mocks.rb example file into features/dupe/custom_mocks
11
- m.template 'custom_mocks.rb', 'features/dupe/custom_mocks/custom_mocks.rb'
12
-
13
- # copy the definitions.rb example file into features/dupe/definitions
14
- m.template 'definitions.rb', 'features/dupe/definitions/definitions.rb'
15
-
16
- # copy the load_dupe.rb into the features/support directory
17
- m.template 'load_dupe.rb', 'features/support/load_dupe.rb'
18
- end
19
- end
20
- end
@@ -1,4 +0,0 @@
1
- # Example:
2
- # Get %r{/books/([\d\w-]+)\.xml} do |label|
3
- # Dupe.find(:book) {|b| b.label == label}
4
- # end
@@ -1,9 +0,0 @@
1
- # set this to false if you don't care to see the requests mocked
2
- # for each cucumber scenario.
3
- Dupe.debug = true
4
-
5
-
6
- # Define your models
7
- # Dupe.define :example_model do |attrs|
8
- # attrs.example_attribute 'Example Default Value'
9
- # end
@@ -1,9 +0,0 @@
1
- # First, load the definitions
2
- Dir[File.join(File.dirname(__FILE__), '../dupe/definitions/*.rb')].each do |file|
3
- require file
4
- end
5
-
6
- # next, load the custom mocks
7
- Dir[File.join(File.dirname(__FILE__), '../dupe/custom_mocks/*.rb')].each do |file|
8
- require file
9
- end