superdupe 0.4.0
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.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
@@ -0,0 +1,127 @@
|
|
1
|
+
class Dupe
|
2
|
+
class Network #:nodoc:
|
3
|
+
class Mock #:nodoc:
|
4
|
+
class ResourceNotFoundError < StandardError; end
|
5
|
+
|
6
|
+
attr_reader :url_pattern
|
7
|
+
attr_reader :response
|
8
|
+
|
9
|
+
def initialize(url_pattern, response_proc=nil)
|
10
|
+
raise(
|
11
|
+
ArgumentError,
|
12
|
+
"The URL pattern parameter must be a type of regular expression."
|
13
|
+
) unless url_pattern.kind_of?(Regexp)
|
14
|
+
|
15
|
+
@response = response_proc || proc {}
|
16
|
+
@url_pattern = url_pattern
|
17
|
+
end
|
18
|
+
|
19
|
+
def match?(url)
|
20
|
+
url_pattern =~ url ? true : false
|
21
|
+
end
|
22
|
+
|
23
|
+
def mocked_response(url, body = nil)
|
24
|
+
raise(
|
25
|
+
StandardError,
|
26
|
+
"Tried to mock a response to a non-matched url! This should never occur. My pattern: #{@url_pattern}. Url: #{url}"
|
27
|
+
) unless match?(url)
|
28
|
+
|
29
|
+
grouped_results = url_pattern.match(url)[1..-1]
|
30
|
+
grouped_results << body if body
|
31
|
+
resp = @response.call *grouped_results
|
32
|
+
process_response(resp, url)
|
33
|
+
end
|
34
|
+
|
35
|
+
def process_response(resp, url)
|
36
|
+
raise NotImplementedError, "When you extend Dupe::Network::Mock, you must implement the #process_response instance method."
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class Dupe
|
43
|
+
class Network
|
44
|
+
class GetMock < Mock #:nodoc:
|
45
|
+
def process_response(resp, url)
|
46
|
+
case resp
|
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
|
60
|
+
end
|
61
|
+
Dupe.network.log.add_request :get, url, resp
|
62
|
+
resp
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Dupe
|
69
|
+
class Network
|
70
|
+
class PostMock < Mock #:nodoc:
|
71
|
+
|
72
|
+
# returns a tuple representing the xml of the processed entity, plus the url to the entity.
|
73
|
+
def process_response(resp, url)
|
74
|
+
case resp
|
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"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
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
|
@@ -0,0 +1,59 @@
|
|
1
|
+
class Dupe
|
2
|
+
class Model #:nodoc:
|
3
|
+
attr_reader :schema
|
4
|
+
attr_reader :name
|
5
|
+
attr_reader :id_sequence
|
6
|
+
|
7
|
+
def initialize(name)
|
8
|
+
@schema = Dupe::Model::Schema.new
|
9
|
+
@name = name.to_sym
|
10
|
+
@id_sequence = Sequence.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def define(definition_proc)
|
14
|
+
definition_proc.call @schema
|
15
|
+
end
|
16
|
+
|
17
|
+
def create(attributes={})
|
18
|
+
Database::Record.new.tap do |record|
|
19
|
+
record.__model__ = self
|
20
|
+
record.id = @id_sequence.next
|
21
|
+
record.merge! default_record
|
22
|
+
record.merge! transform(attributes)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# called by the Dupe::Database#insert method
|
27
|
+
def run_after_create_callbacks(record)
|
28
|
+
@schema.after_create_callbacks.each do |callback|
|
29
|
+
callback.call record
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def default_record
|
35
|
+
Database::Record.new.tap do |record|
|
36
|
+
# setup all the required attributes
|
37
|
+
@schema.attribute_templates.each do |attribute_template_name, attribute_template|
|
38
|
+
required_attribute_name, required_attribute_value =
|
39
|
+
attribute_template.generate
|
40
|
+
record[required_attribute_name] = required_attribute_value
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def transform(attributes)
|
46
|
+
Database::Record.new.tap do |record|
|
47
|
+
# override the required attributes or create new attributes
|
48
|
+
attributes.each do |attribute_name, attribute_value|
|
49
|
+
if @schema.attribute_templates[attribute_name]
|
50
|
+
k, v = @schema.attribute_templates[attribute_name].generate attribute_value
|
51
|
+
record[attribute_name] = v
|
52
|
+
else
|
53
|
+
record[attribute_name] = attribute_value
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
class Dupe
|
2
|
+
class Network #:nodoc:
|
3
|
+
include RestValidation #:nodoc:
|
4
|
+
|
5
|
+
class RequestNotFoundError < StandardError; end #:nodoc:
|
6
|
+
|
7
|
+
attr_reader :mocks, :log
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@mocks = {}
|
11
|
+
@log = Dupe::Network::Log.new
|
12
|
+
VERBS.each { |verb| @mocks[verb] = [] }
|
13
|
+
end
|
14
|
+
|
15
|
+
def request(verb, url, body=nil)
|
16
|
+
validate_request_type verb
|
17
|
+
match(verb, url).mocked_response(url, body)
|
18
|
+
end
|
19
|
+
|
20
|
+
def define_service_mock(verb, url_pattern, response_proc=nil)
|
21
|
+
validate_request_type verb
|
22
|
+
case verb
|
23
|
+
when :get
|
24
|
+
GetMock.new(url_pattern, response_proc).tap do |mock|
|
25
|
+
@mocks[verb] << mock
|
26
|
+
end
|
27
|
+
when :post
|
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|
|
37
|
+
@mocks[verb].unshift mock
|
38
|
+
end
|
39
|
+
else
|
40
|
+
raise StandardError, "Dupe does not support mocking #{verb.to_s.upcase} requests."
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def match(verb, url)
|
46
|
+
@mocks[verb].each do |mock|
|
47
|
+
return mock if mock.match?(url)
|
48
|
+
end
|
49
|
+
raise(
|
50
|
+
RequestNotFoundError,
|
51
|
+
"No mocked service response found for '#{url}'"
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class Dupe
|
2
|
+
class Database #:nodoc:
|
3
|
+
class Record < Hash #:nodoc:
|
4
|
+
attr_accessor :__model__
|
5
|
+
|
6
|
+
def id
|
7
|
+
self[:id]
|
8
|
+
end
|
9
|
+
|
10
|
+
def id=(value)
|
11
|
+
self[:id] = value
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(method_name, *args, &block)
|
15
|
+
if attempting_to_assign(method_name)
|
16
|
+
method_name = method_name.to_s[0..-2].to_sym
|
17
|
+
self[method_name] = args.first
|
18
|
+
else
|
19
|
+
self[method_name.to_sym]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def record_inspect
|
24
|
+
class_name = __model__ ? "Duped::#{__model__.name.to_s.titleize}" : self.class.to_s
|
25
|
+
"<##{class_name}".tap do |inspection|
|
26
|
+
keys.each do |key|
|
27
|
+
inspection << " #{key}=#{self[key].inspect}"
|
28
|
+
end
|
29
|
+
inspection << ">"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
alias_method :hash_inspect, :inspect
|
34
|
+
alias_method :inspect, :record_inspect
|
35
|
+
|
36
|
+
private
|
37
|
+
def attempting_to_assign(method_name)
|
38
|
+
method_name.to_s[-1..-1] == '='
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Dupe
|
2
|
+
class Network #:nodoc:
|
3
|
+
#:nodoc:
|
4
|
+
class UnknownRestVerbError < StandardError; end #:nodoc:
|
5
|
+
VERBS = [:get, :post, :put, :delete] #:nodoc:
|
6
|
+
|
7
|
+
module RestValidation #:nodoc:
|
8
|
+
def validate_request_type(verb)
|
9
|
+
raise(
|
10
|
+
Dupe::Network::UnknownRestVerbError,
|
11
|
+
"Unknown REST verb ':#{verb}'. Valid REST verbs are :get, :post, :put, and :delete."
|
12
|
+
) unless Dupe::Network::VERBS.include?(verb)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class Dupe
|
2
|
+
class Model #:nodoc:
|
3
|
+
class Schema #:nodoc:
|
4
|
+
attr_reader :attribute_templates
|
5
|
+
attr_reader :after_create_callbacks
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@attribute_templates = {}
|
9
|
+
@after_create_callbacks = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(method_name, *args, &block)
|
13
|
+
attribute_name = method_name.to_s[-1..-1] == '=' ? method_name.to_s[0..-2].to_sym : method_name
|
14
|
+
if block && block.arity < 1
|
15
|
+
default_value = block
|
16
|
+
transformer = nil
|
17
|
+
else
|
18
|
+
default_value = args[0]
|
19
|
+
transformer = block
|
20
|
+
end
|
21
|
+
|
22
|
+
@attribute_templates[method_name.to_sym] =
|
23
|
+
AttributeTemplate.new method_name.to_sym, :default => default_value, :transformer => transformer
|
24
|
+
end
|
25
|
+
|
26
|
+
def after_create(&block)
|
27
|
+
raise(
|
28
|
+
ArgumentError,
|
29
|
+
"You must pass a block that accepts a single parameter to 'after_create'"
|
30
|
+
) if !block || block.arity != 1
|
31
|
+
|
32
|
+
@after_create_callbacks << block
|
33
|
+
end
|
34
|
+
|
35
|
+
def uniquify(*args)
|
36
|
+
raise ArgumentError, "You must pass at least one attribute to uniquify." if args.empty?
|
37
|
+
raise ArgumentError, "You may only pass symbols to uniquify." unless all_members_of_class(args, Symbol)
|
38
|
+
|
39
|
+
args.each do |attribute|
|
40
|
+
after_create do |record|
|
41
|
+
record[attribute] = "#{record.__model__.name} #{record.id} #{attribute}" unless record[attribute]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
def all_members_of_class(ary, klass)
|
48
|
+
ary.inject(true) {|bool, v| bool && v.kind_of?(klass)}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Sequence #:nodoc:
|
2
|
+
attr_reader :current_value
|
3
|
+
|
4
|
+
def initialize(starting_at=1, sequencer=nil)
|
5
|
+
@current_value = starting_at
|
6
|
+
if sequencer && sequencer.arity != 1
|
7
|
+
raise ArgumentError, "Your block must accept a single parameter"
|
8
|
+
end
|
9
|
+
@transformer = sequencer
|
10
|
+
end
|
11
|
+
|
12
|
+
def next
|
13
|
+
@current_value += 1
|
14
|
+
if @transformer
|
15
|
+
@transformer.call(@current_value - 1)
|
16
|
+
else
|
17
|
+
@current_value - 1
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/superdupe.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'superdupe/hash_pruner'
|
2
|
+
require 'active_resource'
|
3
|
+
require 'active_resource/http_mock'
|
4
|
+
require 'superdupe/singular_plural_detection'
|
5
|
+
require 'superdupe/symbol'
|
6
|
+
require 'superdupe/string'
|
7
|
+
require 'superdupe/rest_validation'
|
8
|
+
require 'superdupe/mock'
|
9
|
+
require 'superdupe/log'
|
10
|
+
require 'superdupe/network'
|
11
|
+
require 'superdupe/dupe'
|
12
|
+
require 'superdupe/database'
|
13
|
+
require 'superdupe/sequence'
|
14
|
+
require 'superdupe/record'
|
15
|
+
require 'superdupe/attribute_template'
|
16
|
+
require 'superdupe/schema'
|
17
|
+
require 'superdupe/model'
|
18
|
+
require 'superdupe/custom_mocks'
|
19
|
+
require 'superdupe/active_resource_extensions'
|
20
|
+
require 'superdupe/cucumber_hooks'
|
@@ -0,0 +1,20 @@
|
|
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
|
@@ -0,0 +1,9 @@
|
|
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
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe ActiveResource::Connection do
|
4
|
+
before do
|
5
|
+
Dupe.reset
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "#get" do
|
9
|
+
before do
|
10
|
+
@book = Dupe.create :book, :title => 'Rooby', :label => 'rooby'
|
11
|
+
class Book < ActiveResource::Base
|
12
|
+
self.site = ''
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should pass a request off to the Dupe network if the original request failed" do
|
17
|
+
Dupe.network.should_receive(:request).with(:get, '/books.xml').once.and_return(Dupe.find(:books).to_xml(:root => 'books'))
|
18
|
+
books = Book.find(:all)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should parse the xml and turn the result into active resource objects" do
|
22
|
+
books = Book.find(:all)
|
23
|
+
books.length.should == 1
|
24
|
+
books.first.id.should == 1
|
25
|
+
books.first.title.should == 'Rooby'
|
26
|
+
books.first.label.should == 'rooby'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#post" do
|
31
|
+
before do
|
32
|
+
@book = Dupe.create :book, :label => 'rooby', :title => 'Rooby'
|
33
|
+
@book.delete(:id)
|
34
|
+
class Book < ActiveResource::Base
|
35
|
+
self.site = ''
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should pass a request off to the Dupe network if the original request failed" do
|
40
|
+
Dupe.network.should_receive(:request).with(:post, '/books.xml', Hash.from_xml(@book.to_xml(:root => 'book'))["book"] ).once
|
41
|
+
book = Book.create({:label => 'rooby', :title => 'Rooby'})
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should parse the xml and turn the result into active resource objects" do
|
45
|
+
book = Book.create({:label => 'rooby', :title => 'Rooby'})
|
46
|
+
book.id.should == 2
|
47
|
+
book.title.should == 'Rooby'
|
48
|
+
book.label.should == 'rooby'
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should make ActiveResource throw an unprocessable entity exception if our Post mock throws a Dupe::UnprocessableEntity exception" do
|
52
|
+
Post %r{/books\.xml} do |post_data|
|
53
|
+
raise Dupe::UnprocessableEntity.new(:title => "must be present.") unless post_data["title"]
|
54
|
+
Dupe.create :book, post_data
|
55
|
+
end
|
56
|
+
|
57
|
+
b = Book.create
|
58
|
+
b.new?.should be_true
|
59
|
+
b.errors.errors.should_not be_empty
|
60
|
+
b = Book.create(:title => "hello")
|
61
|
+
b.new?.should be_false
|
62
|
+
b.errors.should be_empty
|
63
|
+
end
|
64
|
+
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
|