superdupe 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +10 -0
- data/lib/superdupe/active_resource_extensions.rb +161 -0
- data/lib/superdupe/attribute_template.rb +71 -0
- data/lib/superdupe/cucumber_hooks.rb +16 -0
- data/lib/superdupe/custom_mocks.rb +116 -0
- data/lib/superdupe/database.rb +69 -0
- data/lib/superdupe/dupe.rb +534 -0
- data/lib/superdupe/hash_pruner.rb +37 -0
- data/lib/superdupe/log.rb +38 -0
- data/lib/superdupe/mock.rb +127 -0
- data/lib/superdupe/model.rb +59 -0
- data/lib/superdupe/network.rb +56 -0
- data/lib/superdupe/record.rb +42 -0
- data/lib/superdupe/rest_validation.rb +16 -0
- data/lib/superdupe/schema.rb +52 -0
- data/lib/superdupe/sequence.rb +20 -0
- data/lib/superdupe/singular_plural_detection.rb +9 -0
- data/lib/superdupe/string.rb +7 -0
- data/lib/superdupe/symbol.rb +3 -0
- data/lib/superdupe.rb +20 -0
- data/rails_generators/dupe/dupe_generator.rb +20 -0
- data/rails_generators/dupe/templates/custom_mocks.rb +4 -0
- data/rails_generators/dupe/templates/definitions.rb +9 -0
- data/rails_generators/dupe/templates/load_dupe.rb +9 -0
- data/spec/lib_specs/active_resource_extensions_spec.rb +141 -0
- data/spec/lib_specs/attribute_template_spec.rb +173 -0
- data/spec/lib_specs/database_spec.rb +163 -0
- data/spec/lib_specs/dupe_spec.rb +522 -0
- data/spec/lib_specs/hash_pruner_spec.rb +73 -0
- data/spec/lib_specs/log_spec.rb +78 -0
- data/spec/lib_specs/logged_request_spec.rb +22 -0
- data/spec/lib_specs/mock_definitions_spec.rb +58 -0
- data/spec/lib_specs/mock_spec.rb +194 -0
- data/spec/lib_specs/model_spec.rb +95 -0
- data/spec/lib_specs/network_spec.rb +130 -0
- data/spec/lib_specs/record_spec.rb +70 -0
- data/spec/lib_specs/rest_validation_spec.rb +17 -0
- data/spec/lib_specs/schema_spec.rb +109 -0
- data/spec/lib_specs/sequence_spec.rb +39 -0
- data/spec/lib_specs/string_spec.rb +31 -0
- data/spec/lib_specs/symbol_spec.rb +17 -0
- data/spec/spec_helper.rb +8 -0
- metadata +142 -0
data/README.markdown
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
## SuperDupe
|
2
|
+
SuperDupe provides two things:
|
3
|
+
|
4
|
+
* Mock ActiveResource objects like the originally gem dupe
|
5
|
+
* Superdupe send no requests to external services registerd by the ARes. It has an extra parameter to send explicitly extrnal requests.
|
6
|
+
|
7
|
+
SuperDupe is a fork of the originally gem dupe 0.5.1 (Matt Parker). At first, the gem try to use only the available mocked resources. If you have the requirement to send external requests without mocking, take an extra parameter for this situation.
|
8
|
+
|
9
|
+
# Install the gem
|
10
|
+
gem install superdupe
|
@@ -0,0 +1,161 @@
|
|
1
|
+
ActiveResource::HttpMock.instance_eval do #:nodoc:
|
2
|
+
def delete_mock(http_method, path) #:nodoc:
|
3
|
+
responses.reject! {|r| r[0].path == path && r[0].method == http_method}
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
module ActiveResource #:nodoc:
|
8
|
+
class Base
|
9
|
+
class << self
|
10
|
+
|
11
|
+
# Overwrite the configuration of ActiveResource authentication (self.user). Don't need it for mocking.
|
12
|
+
def user=(user)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Overwrite the configuration of ActiveResource authentication (self.password). Don't need it for mocking.
|
16
|
+
def password=(password)
|
17
|
+
end
|
18
|
+
|
19
|
+
def find(*arguments)
|
20
|
+
scope = arguments.slice!(0)
|
21
|
+
options = arguments.slice!(0) || {}
|
22
|
+
|
23
|
+
case scope
|
24
|
+
when :all then find_every(options)
|
25
|
+
when :first then find_every(options).first
|
26
|
+
when :last then find_every(options).last
|
27
|
+
when :one then find_one(options)
|
28
|
+
else find_single(scope, options)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def find_every(options)
|
34
|
+
external_request = options[:external_request] if options.has_key?(:external_request)
|
35
|
+
|
36
|
+
case from = options[:from]
|
37
|
+
when Symbol
|
38
|
+
instantiate_collection(get(from, options[:params]))
|
39
|
+
when String
|
40
|
+
path = "#{from}#{query_string(options[:params])}"
|
41
|
+
instantiate_collection(connection.get(path, headers, external_request) || [])
|
42
|
+
else
|
43
|
+
prefix_options, query_options = split_options(options[:params])
|
44
|
+
path = collection_path(prefix_options, query_options)
|
45
|
+
instantiate_collection( (connection.get(path, headers, external_request) || []), prefix_options )
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def find_one(options)
|
50
|
+
external_request = options[:external_request] if options.has_key?(:external_request)
|
51
|
+
|
52
|
+
case from = options[:from]
|
53
|
+
when Symbol
|
54
|
+
instantiate_record(get(from, options[:params]))
|
55
|
+
when String
|
56
|
+
path = "#{from}#{query_string(options[:params])}"
|
57
|
+
instantiate_record(connection.get(path, headers, external_request))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def find_single(scope, options)
|
62
|
+
external_request = options[:external_request] if options.has_key?(:external_request)
|
63
|
+
|
64
|
+
prefix_options, query_options = split_options(options[:params])
|
65
|
+
path = element_path(scope, prefix_options, query_options)
|
66
|
+
instantiate_record(connection.get(path, headers, external_request), prefix_options)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class Connection #:nodoc:
|
72
|
+
def get(path, headers = {}, external_request = false) #:nodoc:
|
73
|
+
if external_request
|
74
|
+
# Without mocking - standard ActiveResource
|
75
|
+
response = request(:get, path, build_request_headers(headers, :get))
|
76
|
+
else
|
77
|
+
mocked_response = Dupe.network.request(:get, path)
|
78
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
79
|
+
mock.get(path, {}, mocked_response)
|
80
|
+
end
|
81
|
+
response = request(:get, path, build_request_headers(headers, :get))
|
82
|
+
ActiveResource::HttpMock.delete_mock(:get, path)
|
83
|
+
end
|
84
|
+
format.decode(response.body)
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
def post(path, body = '', headers = {}, external_request = false) #:nodoc:
|
89
|
+
if external_request
|
90
|
+
# external request - standard ActiveResource
|
91
|
+
response = request(:post, path, body.to_s, build_request_headers(headers, :post))
|
92
|
+
else
|
93
|
+
resource_hash = Hash.from_xml(body)
|
94
|
+
resource_hash = resource_hash[resource_hash.keys.first]
|
95
|
+
resource_hash = {} unless resource_hash.kind_of?(Hash)
|
96
|
+
begin
|
97
|
+
mocked_response, new_path = Dupe.network.request(:post, path, resource_hash)
|
98
|
+
error = false
|
99
|
+
rescue Dupe::UnprocessableEntity => e
|
100
|
+
mocked_response = {:error => e.message.to_s}.to_xml(:root => 'errors')
|
101
|
+
error = true
|
102
|
+
end
|
103
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
104
|
+
if error
|
105
|
+
mock.post(path, {}, mocked_response, 422, "Content-Type" => 'application/xml')
|
106
|
+
else
|
107
|
+
mock.post(path, {}, mocked_response, 201, "Location" => new_path)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
response = request(:post, path, body.to_s, build_request_headers(headers, :post))
|
111
|
+
ActiveResource::HttpMock.delete_mock(:post, path)
|
112
|
+
end
|
113
|
+
response
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
def put(path, body = '', headers = {}, external_request = false) #:nodoc:
|
118
|
+
if external_request
|
119
|
+
response = request(:put, path, body.to_s, build_request_headers(headers, :put))
|
120
|
+
else
|
121
|
+
resource_hash = Hash.from_xml(body)
|
122
|
+
resource_hash = resource_hash[resource_hash.keys.first]
|
123
|
+
resource_hash = {} unless resource_hash.kind_of?(Hash)
|
124
|
+
resource_hash.symbolize_keys!
|
125
|
+
|
126
|
+
begin
|
127
|
+
error = false
|
128
|
+
mocked_response, path = Dupe.network.request(:put, path, resource_hash)
|
129
|
+
rescue Dupe::UnprocessableEntity => e
|
130
|
+
mocked_response = {:error => e.message.to_s}.to_xml(:root => 'errors')
|
131
|
+
error = true
|
132
|
+
end
|
133
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
134
|
+
if error
|
135
|
+
mock.put(path, {}, mocked_response, 422, "Content-Type" => 'application/xml')
|
136
|
+
else
|
137
|
+
mock.put(path, {}, mocked_response, 204)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
response = request(:put, path, body.to_s, build_request_headers(headers, :put))
|
141
|
+
ActiveResource::HttpMock.delete_mock(:put, path)
|
142
|
+
end
|
143
|
+
|
144
|
+
response
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
def delete(path, headers = {}, external_request = false)
|
149
|
+
# External requests are not allowed!
|
150
|
+
Dupe.network.request(:delete, path)
|
151
|
+
|
152
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
153
|
+
mock.delete(path, {}, nil, 200)
|
154
|
+
end
|
155
|
+
response = request(:delete, path, build_request_headers(headers, :delete))
|
156
|
+
|
157
|
+
ActiveResource::HttpMock.delete_mock(:delete, path)
|
158
|
+
response
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
class Dupe
|
2
|
+
class Model #:nodoc:
|
3
|
+
class Schema #:nodoc:
|
4
|
+
# This class represents an attribute template.
|
5
|
+
# An attribute template consists of an attribute name (a symbol),
|
6
|
+
# a potential default value (nil if not specified),
|
7
|
+
# and a potential transformer proc.
|
8
|
+
class AttributeTemplate #:nodoc:
|
9
|
+
|
10
|
+
class NilValue; end
|
11
|
+
|
12
|
+
attr_reader :name
|
13
|
+
attr_reader :transformer
|
14
|
+
attr_reader :default
|
15
|
+
|
16
|
+
def initialize(name, options={})
|
17
|
+
default = options[:default]
|
18
|
+
transformer = options[:transformer]
|
19
|
+
|
20
|
+
if transformer
|
21
|
+
raise ArgumentError, "Your transformer must be a kind of proc." if !transformer.kind_of?(Proc)
|
22
|
+
raise ArgumentError, "Your transformer must accept a parameter." if transformer.arity != 1
|
23
|
+
end
|
24
|
+
|
25
|
+
@name = name
|
26
|
+
@default = default
|
27
|
+
@transformer = transformer
|
28
|
+
end
|
29
|
+
|
30
|
+
# Suppose we have the following attribute template:
|
31
|
+
#
|
32
|
+
# console> a = Dupe::Model::Schema::AttributeTemplate.new(:title)
|
33
|
+
#
|
34
|
+
# If we call generate with no parameter, we'll get back the following:
|
35
|
+
#
|
36
|
+
# console> a.generate
|
37
|
+
# ===> :title, nil
|
38
|
+
#
|
39
|
+
# If we call generate with a parameter, we'll get back the following:
|
40
|
+
#
|
41
|
+
# console> a.generate 'my value'
|
42
|
+
# ===> :title, 'my value'
|
43
|
+
#
|
44
|
+
# If we setup an attribute template with a transformer:
|
45
|
+
#
|
46
|
+
# console> a =
|
47
|
+
# Dupe::Model::Schema::AttributeTemplate.new(
|
48
|
+
# :title,
|
49
|
+
# :default => 'default value',
|
50
|
+
# :transformer => proc {|dont_care| 'test'}
|
51
|
+
# )
|
52
|
+
# Then we'll get back the following when we call generate:
|
53
|
+
#
|
54
|
+
# console> a.generate
|
55
|
+
# ===> :title, 'default value'
|
56
|
+
#
|
57
|
+
# console> a.generate 'my value'
|
58
|
+
# ===> :title, 'test'
|
59
|
+
def generate(value=NilValue)
|
60
|
+
if value == NilValue
|
61
|
+
v = @default.respond_to?(:call) ? @default.call : (@default.dup rescue @default)
|
62
|
+
else
|
63
|
+
v = (@transformer ? @transformer.call(value) : value)
|
64
|
+
end
|
65
|
+
|
66
|
+
return @name, v
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
begin
|
2
|
+
After do |scenario|
|
3
|
+
# print the requests logged during the scenario if in Dupe debug mode
|
4
|
+
if Dupe.debug
|
5
|
+
log = Dupe.network.log.pretty_print
|
6
|
+
puts "\n\n" + log.indent(4) + "\n\n" if log
|
7
|
+
end
|
8
|
+
|
9
|
+
# remove any data created during the scenario from the dupe database
|
10
|
+
Dupe.database.truncate_tables
|
11
|
+
|
12
|
+
# clear out the network log
|
13
|
+
Dupe.network.log.reset
|
14
|
+
end
|
15
|
+
rescue
|
16
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# Dupe knows how to handle simple find by id and find :all lookups from ActiveResource. But what about other requests we might potentially make?
|
2
|
+
#
|
3
|
+
# irb# Dupe.create :author, :name => 'Monkey', :published => true
|
4
|
+
# ==> <#Duped::Author name="Monkey" published=true id=1>
|
5
|
+
#
|
6
|
+
# irb# Dupe.create :author, :name => 'Tiger', :published => false
|
7
|
+
# ==> <#Duped::Author name="Tiger" published=false id=2>
|
8
|
+
#
|
9
|
+
# irb# class Author < ActiveResource::Base; self.site = ''; end
|
10
|
+
# ==> ""
|
11
|
+
#
|
12
|
+
# irb# Author.find :all, :from => :published
|
13
|
+
# Dupe::Network::RequestNotFoundError: No mocked service response found for '/authors/published.xml'
|
14
|
+
# from /Library/Ruby/Gems/1.8/gems/dupe-0.4.0/lib/dupe/network.rb:32:in `match'
|
15
|
+
# from /Library/Ruby/Gems/1.8/gems/dupe-0.4.0/lib/dupe/network.rb:17:in `request'
|
16
|
+
# from /Library/Ruby/Gems/1.8/gems/dupe-0.4.0/lib/dupe/active_resource_extensions.rb:15:in `get'
|
17
|
+
# from /Library/Ruby/Gems/1.8/gems/activeresource-2.3.5/lib/active_resource/custom_methods.rb:57:in `get'
|
18
|
+
# from /Library/Ruby/Gems/1.8/gems/activeresource-2.3.5/lib/active_resource/base.rb:632:in `find_every'
|
19
|
+
# from /Library/Ruby/Gems/1.8/gems/activeresource-2.3.5/lib/active_resource/base.rb:582:in `find'
|
20
|
+
# from (irb):12
|
21
|
+
#
|
22
|
+
# Obviously, Dupe had no way of anticipating this possibility. However, you can create your own custom intercept mock for this:
|
23
|
+
#
|
24
|
+
# irb# Get %r{/authors/published.xml} do
|
25
|
+
# --# Dupe.find(:authors) {|a| a.published == true}
|
26
|
+
# --# end
|
27
|
+
# ==> #<Dupe::Network::Mock:0x1833e88 @url_pattern=/\/authors\/published.xml/, @verb=:get, @response=#<Proc:0x01833f14@(irb):13>
|
28
|
+
#
|
29
|
+
# irb# Author.find :all, :from => :published
|
30
|
+
# ==> [#<Author:0x1821d3c @attributes={"name"=>"Monkey", "published"=>true, "id"=>1}, prefix_options{}]
|
31
|
+
#
|
32
|
+
# irb# puts Dupe.network.log.pretty_print
|
33
|
+
#
|
34
|
+
# Logged Requests:
|
35
|
+
# Request: GET /authors/published.xml
|
36
|
+
# Response:
|
37
|
+
# <?xml version="1.0" encoding="UTF-8"?>
|
38
|
+
# <authors type="array">
|
39
|
+
# <author>
|
40
|
+
# <name>Monkey</name>
|
41
|
+
# <published type="boolean">true</published>
|
42
|
+
# <id type="integer">1</id>
|
43
|
+
# </author>
|
44
|
+
# </authors>
|
45
|
+
#
|
46
|
+
#
|
47
|
+
# 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.
|
48
|
+
#
|
49
|
+
# Suppose instead the service expected us to pass published as a query string parameter:
|
50
|
+
#
|
51
|
+
# irb# Author.find :all, :params => {:published => true}
|
52
|
+
# Dupe::Network::RequestNotFoundError: No mocked service response found for '/authors.xml?published=true'
|
53
|
+
# from /Library/Ruby/Gems/1.8/gems/dupe-0.4.0/lib/dupe/network.rb:32:in `match'
|
54
|
+
# from /Library/Ruby/Gems/1.8/gems/dupe-0.4.0/lib/dupe/network.rb:17:in `request'
|
55
|
+
# from /Library/Ruby/Gems/1.8/gems/dupe-0.4.0/lib/dupe/active_resource_extensions.rb:15:in `get'
|
56
|
+
# from /Library/Ruby/Gems/1.8/gems/activeresource-2.3.5/lib/active_resource/base.rb:639:in `find_every'
|
57
|
+
# from /Library/Ruby/Gems/1.8/gems/activeresource-2.3.5/lib/active_resource/base.rb:582:in `find'
|
58
|
+
# from (irb):18
|
59
|
+
#
|
60
|
+
# We can mock this with the following:
|
61
|
+
#
|
62
|
+
# irb# Get %r{/authors\.xml\?published=(true|false)$} do |published|
|
63
|
+
# --# if published == 'true'
|
64
|
+
# --# Dupe.find(:authors) {|a| a.published == true}
|
65
|
+
# --# else
|
66
|
+
# --# Dupe.find(:authors) {|a| a.published == false}
|
67
|
+
# --# end
|
68
|
+
# --# end
|
69
|
+
#
|
70
|
+
# irb# Author.find :all, :params => {:published => true}
|
71
|
+
# ==> [#<Author:0x17db094 @attributes={"name"=>"Monkey", "published"=>true, "id"=>1}, prefix_options{}]
|
72
|
+
#
|
73
|
+
# irb# Author.find :all, :params => {:published => false}
|
74
|
+
# ==> [#<Author:0x17c68c4 @attributes={"name"=>"Tiger", "published"=>false, "id"=>2}, prefix_options{}]
|
75
|
+
#
|
76
|
+
# irb# puts Dupe.network.log.pretty_print
|
77
|
+
#
|
78
|
+
# Logged Requests:
|
79
|
+
# Request: GET /authors.xml?published=true
|
80
|
+
# Response:
|
81
|
+
# <?xml version="1.0" encoding="UTF-8"?>
|
82
|
+
# <authors type="array">
|
83
|
+
# <author>
|
84
|
+
# <name>Monkey</name>
|
85
|
+
# <published type="boolean">true</published>
|
86
|
+
# <id type="integer">1</id>
|
87
|
+
# </author>
|
88
|
+
# </authors>
|
89
|
+
#
|
90
|
+
# Request: GET /authors.xml?published=false
|
91
|
+
# Response:
|
92
|
+
# <?xml version="1.0" encoding="UTF-8"?>
|
93
|
+
# <authors type="array">
|
94
|
+
# <author>
|
95
|
+
# <name>Tiger</name>
|
96
|
+
# <published type="boolean">false</published>
|
97
|
+
# <id type="integer">2</id>
|
98
|
+
# </author>
|
99
|
+
# </authors>
|
100
|
+
|
101
|
+
|
102
|
+
def Get(url_pattern, &block)
|
103
|
+
Dupe.network.define_service_mock :get, url_pattern, block
|
104
|
+
end
|
105
|
+
|
106
|
+
def Post(url_pattern, &block)
|
107
|
+
Dupe.network.define_service_mock :post, url_pattern, block
|
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
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class Dupe
|
2
|
+
class Database #:nodoc:
|
3
|
+
attr_reader :tables
|
4
|
+
|
5
|
+
#:nodoc:
|
6
|
+
class TableDoesNotExistError < StandardError; end
|
7
|
+
|
8
|
+
#:nodoc:
|
9
|
+
class InvalidQueryError < StandardError; end
|
10
|
+
|
11
|
+
# by default, there are not tables in the database
|
12
|
+
def initialize
|
13
|
+
@tables = {}
|
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
|
32
|
+
|
33
|
+
# pass in a Dupe::Database::Record object, and this method will store the record
|
34
|
+
# in the appropriate table
|
35
|
+
def insert(record)
|
36
|
+
if !record.kind_of?(Dupe::Database::Record) || !record.__model__ || !record[:id]
|
37
|
+
raise ArgumentError, "You may only insert well-defined Dupe::Database::Record objects"
|
38
|
+
end
|
39
|
+
@tables[record.__model__.name] ||= []
|
40
|
+
@tables[record.__model__.name] << record
|
41
|
+
record.__model__.run_after_create_callbacks(record)
|
42
|
+
end
|
43
|
+
|
44
|
+
# pass in a model_name (e.g., :book) and optionally a proc with
|
45
|
+
# conditions (e.g., {|b| b.genre == 'Science Fiction'})
|
46
|
+
# and recieve a (possibly empty) array of results
|
47
|
+
def select(model_name, conditions=nil)
|
48
|
+
raise TableDoesNotExistError, "The table ':#{model_name}' does not exist." unless @tables[model_name]
|
49
|
+
raise(
|
50
|
+
InvalidQueryError,
|
51
|
+
"There was a problem with your select conditions. Please consult the API."
|
52
|
+
) if conditions and (!conditions.kind_of?(Proc) || conditions.arity != 1)
|
53
|
+
|
54
|
+
return @tables[model_name] if !conditions
|
55
|
+
@tables[model_name].select {|r| conditions.call(r)}
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_table(model_name)
|
59
|
+
@tables[model_name.to_sym] ||= []
|
60
|
+
end
|
61
|
+
|
62
|
+
def truncate_tables
|
63
|
+
@tables.each do |table_name, table_records|
|
64
|
+
@tables[table_name] = []
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|