Class: Dupe
- Inherits:
-
Object
- Object
- Dupe
- Defined in:
- lib/superdupe/dupe.rb,
lib/superdupe/log.rb,
lib/superdupe/dupe.rb,
lib/superdupe/mock.rb,
lib/superdupe/mock.rb,
lib/superdupe/mock.rb,
lib/superdupe/mock.rb,
lib/superdupe/mock.rb,
lib/superdupe/model.rb,
lib/superdupe/record.rb,
lib/superdupe/schema.rb,
lib/superdupe/network.rb,
lib/superdupe/database.rb,
lib/superdupe/rest_validation.rb,
lib/superdupe/attribute_template.rb
Overview
- Author
-
Matt Parker ([email protected])
- License
-
Distributes under the same terms as Ruby
Defined Under Namespace
Classes: Database, Model, Network, UnprocessableEntity
Class Attribute Summary collapse
-
.database ⇒ Object
readonly
:nodoc:.
-
.debug ⇒ Object
set to true if you want to see mocked results spit out after each cucumber scenario.
-
.models ⇒ Object
readonly
:nodoc:.
-
.sequences ⇒ Object
readonly
:nodoc:.
Class Method Summary collapse
-
.create(model_name, records = {}) ⇒ Object
This method will cause Dupe to mock resources for the record(s) provided.
-
.define(*args, &block) ⇒ Object
Suppose we’re creating a ‘book’ resource.
- .delete(resource, &conditions) ⇒ Object
- .disable! ⇒ Object
- .disabled? ⇒ Boolean
- .enable! ⇒ Object
-
.find(model_name, &block) ⇒ Object
Dupe has a built-in querying system for finding resources you create.
-
.find_or_create(model_name, attributes = {}) ⇒ Object
This method will create a resource with the given specifications if one doesn’t already exist.
-
.network ⇒ Object
:nodoc:.
- .next(name) ⇒ Object
-
.reset ⇒ Object
clears out all model definitions, sequences, and database records / tables.
- .reset_database ⇒ Object
- .reset_models ⇒ Object
- .reset_network ⇒ Object
- .reset_sequences ⇒ Object
- .sequence(name, &block) ⇒ Object
-
.stub(count, model_name, options = {}) ⇒ Object
You can use this method to quickly stub out a large number of resources.
Class Attribute Details
.database ⇒ Object (readonly)
:nodoc:
9 10 11 |
# File 'lib/superdupe/dupe.rb', line 9 def database @database end |
.debug ⇒ Object
set to true if you want to see mocked results spit out after each cucumber scenario
13 14 15 |
# File 'lib/superdupe/dupe.rb', line 13 def debug @debug end |
.models ⇒ Object (readonly)
:nodoc:
7 8 9 |
# File 'lib/superdupe/dupe.rb', line 7 def models @models end |
.sequences ⇒ Object (readonly)
:nodoc:
8 9 10 |
# File 'lib/superdupe/dupe.rb', line 8 def sequences @sequences end |
Class Method Details
.create(model_name, records = {}) ⇒ Object
This method will cause Dupe to mock resources for the record(s) provided. The “records” value may be either a hash or an array of hashes. For example, suppose you’d like to mock a single author:
author = Dupe.create :author, :name => 'Arthur C. Clarke'
==> <#Duped::Author name="Arthur C. Clarke" id=1>
This will translate into the following two mocked resource calls:
# GET /authors.xml
<?xml version="1.0" encoding="UTF-8"?>
<authors>
<author>
<id type="integer">1</id>
<name>Arthur C. Clarke</name>
</author>
</authors>
# GET /authors/1.xml
<?xml version="1.0" encoding="UTF-8"?>
<author>
<id type="integer">1</id>
<name>Arthur C. Clarke</name>
</author>
However, suppose you wanted to mock two or more authors.
authors = Dupe.create :author, [{:name => 'Arthur C. Clarke'}, {:name => 'Robert Heinlein'}]
==> [<#Duped::Author name="Arthur C. Clarke" id=1>, <#Duped::Author name="Robert Heinlein" id=2>]
This will translate into the following three mocked resource calls:
# GET /authors.xml
<?xml version="1.0" encoding="UTF-8"?>
<authors>
<author>
<id type="integer">1</id>
<name>Arthur C. Clarke</name>
</author>
<author>
<id type="integer">2</id>
<name>Robert Heinlein</name>
</author>
</authors>
# GET /authors/1.xml
<?xml version="1.0" encoding="UTF-8"?>
<author>
<id type="integer">1</id>
<name>Arthur C. Clarke</name>
</author>
# GET /authors/2.xml
<?xml version="1.0" encoding="UTF-8"?>
<author>
<id type="integer">2</id>
<name>Robert Heinlein</name>
</author>
274 275 276 277 278 279 |
# File 'lib/superdupe/dupe.rb', line 274 def create(model_name, records={}) model_name = model_name.to_s.to_sym define model_name unless model_exists(model_name) records = records.kind_of?(Array) ? records.map {|r| r.symbolize_keys} : records.symbolize_keys! create_and_insert records, :into => model_name end |
.define(*args, &block) ⇒ Object
Suppose we’re creating a ‘book’ resource. Perhaps our app assumes every book has a title, so let’s define a book resource that specifies just that:
irb# Dupe.define :book do |attrs|
--# attrs.title 'Untitled'
--# attrs.author
--# end
==> #<Dupe::Model:0x17b2694 ...>
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.
irb# b = Dupe.create :book
==> <#Duped::Book author=nil title="Untitled" id=1>
If we provide our own title, it should allow us to override the default value:
irb# b = Dupe.create :book, :title => 'Monkeys!'
==> <#Duped::Book author=nil title="Monkeys!" id=2>
Attributes with procs as default values
Sometimes it might be convenient to procedurally define the default value for an attribute:
irb# Dupe.define :book do |attrs|
--# attrs.title 'Untitled'
--# attrs.author
--# attrs.isbn do
--# rand(1000000)
--# end
--# end
Now, every time we create a book, it will get assigned a random ISBN number:
irb# b = Dupe.create :book
==> <#Duped::Book author=nil title="Untitled" id=1 isbn=895825>
irb# b = Dupe.create :book
==> <#Duped::Book author=nil title="Untitled" id=2 isbn=606472>
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:
irb# Dupe.define :book do |attrs|
--# attrs.title 'Untitled'
--# attrs.author
--# attrs.isbn do
--# rand(1000000)
--# end
--# attrs.genre do
--# Dupe.find_or_create :genre
--# end
--# end
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.
First, let’s confirm that no genres currently exist:
irb# Dupe.find :genre
Dupe::Database::TableDoesNotExistError: The table ':genre' does not exist.
from /Library/Ruby/Gems/1.8/gems/dupe-0.4.0/lib/dupe/database.rb:30:in `select'
from /Library/Ruby/Gems/1.8/gems/dupe-0.4.0/lib/dupe/dupe.rb:295:in `find'
from (irb):135
Next, let’s create a book:
irb# b = Dupe.create :book
==> <#Duped::Book genre=<#Duped::Genre id=1> author=nil title="Untitled" id=1 isbn=62572>
Notice that it create a genre. If we tried to do another Dupe.find for the genre:
irb# Dupe.find :genre
==> <#Duped::Genre id=1>
Now, if create another book, it will associate with the genre that was just created:
irb# b = Dupe.create :book
==> <#Duped::Book genre=<#Duped::Genre id=1> author=nil title="Untitled" id=2 isbn=729317>
Attributes with transformers
Occasionally, you may find it useful to have attribute values transformed upon creation.
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.
irb# Dupe.define :book do |attrs|
--# attrs.title 'Untitled'
--# attrs.author
--# attrs.isbn do
--# rand(1000000)
--# end
--# attrs.publish_date do |publish_date|
--# Date.parse(publish_date)
--# end
--# end
Now, let’s create a book:
irb# b = Dupe.create :book, :publish_date => '2009-12-29'
==> <#Duped::Book author=nil title="Untitled" publish_date=Tue, 29 Dec 2009 id=1 isbn=826291>
irb# b.publish_date
==> Tue, 29 Dec 2009
irb# b.publish_date.class
==> Date
Uniquify attributes
If you’d just like to make sure that some attributes get a unique value, then you can use the uniquify method:
irb# Dupe.define :book do |attrs|
--# attrs.uniquify :title, :genre, :author
--# end
Now, Dupe will do its best to assign unique values to the :title, :genre, and :author attributes on any records it creates:
irb# b = Dupe.create :book
==> <#Duped::Book author="book 1 author" title="book 1 title" genre="book 1 genre" id=1>
irb# b2 = Dupe.create :book, :title => 'Rooby'
==> <#Duped::Book author="book 2 author" title="Rooby" genre="book 2 genre" id=2>
Callbacks
Suppose we’d like to make sure that our books get a unique label. We can accomplish that with an after_create callback:
irb# Dupe.define :book do |attrs|
--# attrs.title 'Untitled'
--# attrs.author
--# attrs.isbn do
--# rand(1000000)
--# end
--# attrs.publish_date do |publish_date|
--# Date.parse(publish_date)
--# end
--# attrs.after_create do |book|
--# book.label = book.title.downcase.gsub(/\ +/, '-') + "--#{book.id}"
--# end
--# end
irb# b = Dupe.create :book, :title => 'Rooby on Rails'
==> <#Duped::Book author=nil label="rooby-on-rails--1" title="Rooby on Rails" publish_date=nil id=1 isbn=842518>
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/superdupe/dupe.rb', line 178 def define(*args, &block) # yield: define model_name, model_object = create_model_if_definition_parameters_are_valid(args, block) model_object.tap do |m| models[model_name] = m database.create_table model_name mocks = %{ network.define_service_mock( :get, %r{^#{model_name.to_s.constantize.prefix rescue '/'}#{model_name.to_s.demodulize.downcase.pluralize}\\.xml$}, proc { Dupe.find(:"#{model_name.to_s.pluralize}") } ) network.define_service_mock( :get, %r{^#{model_name.to_s.constantize.prefix rescue '/'}#{model_name.to_s.demodulize.downcase.pluralize}/(\\d+)\\.xml$}, proc {|id| Dupe.find(:"#{model_name.to_s}") {|resource| resource.id == id.to_i}} ) network.define_service_mock( :post, %r{^#{model_name.to_s.constantize.prefix rescue '/'}#{model_name.to_s.demodulize.downcase.pluralize}\\.xml$}, proc { |post_body| Dupe.create(:"#{model_name.to_s}", post_body) } ) network.define_service_mock( :put, %r{^#{model_name.to_s.constantize.prefix rescue '/'}#{model_name.to_s.demodulize.downcase.pluralize}/(\\d+)\\.xml$}, proc { |id, put_data| Dupe.find(:"#{model_name.to_s}") {|resource| resource.id == id.to_i}.merge!(put_data) } ) network.define_service_mock( :delete, %r{^#{model_name.to_s.constantize.prefix rescue '/'}#{model_name.to_s.demodulize.downcase.pluralize}/(\\d+)\\.xml$}, proc { |id| Dupe.delete(:"#{model_name.to_s}") {|resource| resource.id == id.to_i} } ) } eval(mocks) end end |
.delete(resource, &conditions) ⇒ Object
423 424 425 |
# File 'lib/superdupe/dupe.rb', line 423 def delete(resource, &conditions) database.delete resource, conditions end |
.disable! ⇒ Object
19 20 21 |
# File 'lib/superdupe/dupe.rb', line 19 def disable! @dupe_disabled = true end |
.disabled? ⇒ Boolean
23 24 25 |
# File 'lib/superdupe/dupe.rb', line 23 def disabled? @dupe_disabled end |
.enable! ⇒ Object
15 16 17 |
# File 'lib/superdupe/dupe.rb', line 15 def enable! @dupe_disabled = false end |
.find(model_name, &block) ⇒ Object
Dupe has a built-in querying system for finding resources you create.
irb# a = Dupe.create :author, :name => 'Monkey'
==> <#Duped::Author name="Monkey" id=1>
irb# b = Dupe.create :book, :title => 'Bananas', :author => a
==> <#Duped::Book author=<#Duped::Author name="Monkey" id=1> title="Bananas" id=1>
irb# Dupe.find(:author) {|a| a.name == 'Monkey'}
==> <#Duped::Author name="Monkey" id=1>
irb# Dupe.find(:book) {|b| b.author.name == 'Monkey'}
==> <#Duped::Book author=<#Duped::Author name="Monkey" id=1> title="Bananas" id=1>
irb# Dupe.find(:author) {|a| a.id == 1}
==> <#Duped::Author name="Monkey" id=1>
irb# Dupe.find(:author) {|a| a.id == 2}
==> nil
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.
If we’d like to find several resources, we can use the plural form of the model name. For example:
irb# a = Dupe.create :author, :name => 'Monkey', :published => true
==> <#Duped::Author published=true name="Monkey" id=1>
irb# b = Dupe.create :book, :title => 'Bananas', :author => a
==> <#Duped::Book author=<#Duped::Author published=true name="Monkey" id=1> title="Bananas" id=1>
irb# Dupe.create :author, :name => 'Tiger', :published => false
==> <#Duped::Author published=false name="Tiger" id=2>
irb# Dupe.find(:authors)
==> [<#Duped::Author published=true name="Monkey" id=1>, <#Duped::Author published=false name="Tiger" id=2>]
irb# Dupe.find(:authors) {|a| a.published == true}
==> [<#Duped::Author published=true name="Monkey" id=1>]
irb# Dupe.find(:books)
==> [<#Duped::Book author=<#Duped::Author published=true name="Monkey" id=1> title="Bananas" id=1>]
irb# Dupe.find(:books) {|b| b.author.published == false}
==> []
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).
374 375 376 377 378 379 380 381 |
# File 'lib/superdupe/dupe.rb', line 374 def find(model_name, &block) # yield: record if model_name.plural? results = database.select model_name.to_s.singularize.to_sym, block else results = database.select model_name.to_s.to_sym, block end model_name.plural? ? results : results.first end |
.find_or_create(model_name, attributes = {}) ⇒ Object
This method will create a resource with the given specifications if one doesn’t already exist.
irb# Dupe.find :genre
Dupe::Database::TableDoesNotExistError: The table ':genre' does not exist.
from /Library/Ruby/Gems/1.8/gems/dupe-0.4.0/lib/dupe/database.rb:30:in `select'
from /Library/Ruby/Gems/1.8/gems/dupe-0.4.0/lib/dupe/dupe.rb:295:in `find'
from (irb):40
irb# Dupe.find_or_create :genre
==> <#Duped::Genre id=1>
irb# Dupe.find_or_create :genre
==> <#Duped::Genre id=1>
You can also pass conditions to find_or_create as a hash:
irb# Dupe.find_or_create :genre, :name => 'Science Fiction', :label => 'sci-fi'
==> <#Duped::Genre label="sci-fi" name="Science Fiction" id=2>
irb# Dupe.find_or_create :genre, :name => 'Science Fiction', :label => 'sci-fi'
==> <#Duped::Genre label="sci-fi" name="Science Fiction" id=2>
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 |
# File 'lib/superdupe/dupe.rb', line 404 def find_or_create(model_name, attributes={}) results = nil if model_exists(model_name) results = eval("find(:#{model_name}) #{build_conditions(attributes)}") end if !results if model_name.singular? create model_name, attributes else stub((rand(5)+1), model_name, :like => attributes) end elsif results.kind_of?(Array) && results.empty? stub((rand(5)+1), model_name, :like => attributes) else results end end |
.network ⇒ Object
:nodoc:
440 441 442 |
# File 'lib/superdupe/dupe.rb', line 440 def network #:nodoc: @network ||= Dupe::Network.new end |
.next(name) ⇒ Object
431 432 433 434 |
# File 'lib/superdupe/dupe.rb', line 431 def next(name) raise ArgumentError, "Unknown sequence \":#{name}\"" unless sequences.has_key?(name) sequences[name].next end |
.reset ⇒ Object
clears out all model definitions, sequences, and database records / tables.
453 454 455 456 457 458 |
# File 'lib/superdupe/dupe.rb', line 453 def reset reset_models reset_database reset_network reset_sequences end |
.reset_database ⇒ Object
468 469 470 |
# File 'lib/superdupe/dupe.rb', line 468 def reset_database @database = Dupe::Database.new end |
.reset_models ⇒ Object
464 465 466 |
# File 'lib/superdupe/dupe.rb', line 464 def reset_models @models = {} end |
.reset_network ⇒ Object
472 473 474 |
# File 'lib/superdupe/dupe.rb', line 472 def reset_network @network = Dupe::Network.new end |
.reset_sequences ⇒ Object
460 461 462 |
# File 'lib/superdupe/dupe.rb', line 460 def reset_sequences @sequences = {} end |
.sequence(name, &block) ⇒ Object
427 428 429 |
# File 'lib/superdupe/dupe.rb', line 427 def sequence(name, &block) sequences[name.to_sym] = Sequence.new 1, block end |
.stub(count, model_name, options = {}) ⇒ Object
You can use this method to quickly stub out a large number of resources. For example:
Dupe.stub 20, :authors
Assuming you had an :author resource definition like:
Dupe.define :author {|author| author.name('default')}
then stub would have generated 20 author records like:
<#Duped::Author name="default" id=1>
....
<#Duped::Author name="default" id=1>
and it would also have mocked find(id) and find(:all) responses for these records (along with any other custom mocks you’ve setup via Dupe.define_mocks). (Had you not defined an author resource, then the stub would have generated 20 author records where the only attribute is the id).
Of course, it’s more likely that you wanted to dupe 20 different authors. You can accomplish this by simply doing:
Dupe.stub 20, :authors, :like => {:name => proc {|n| "author #{n}"}}
which would generate 20 author records like:
<#Duped::Author name="author 1" id=1>
....
<#Duped::Author name="author 20" id=20>
Naturally, stub will consult the Dupe.define definitions for anything it’s attempting to stub and will honor those definitions (default values, transformations, callbacks) as you would expect.
313 314 315 316 317 318 319 320 321 322 323 324 |
# File 'lib/superdupe/dupe.rb', line 313 def stub(count, model_name, ={}) start_at = [:starting_with] || 1 record_template = [:like] || {} records = [] (start_at..(start_at + count - 1)).each do |i| records << record_template.map do |k,v| { k => (v.kind_of?(Proc) ? v.call(i) : v) } end.inject({}) {|h, v| h.merge(v)} end create model_name, records end |