dm-rest-adapter 0.9.3 → 0.9.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +1 -1
- data/lib/rest_adapter.rb +29 -26
- data/lib/rest_adapter/version.rb +2 -2
- data/spec/create_spec.rb +5 -5
- data/spec/delete_spec.rb +5 -5
- data/spec/read_spec.rb +17 -17
- data/spec/ruby_forker.rb +1 -1
- data/spec/update_spec.rb +6 -6
- data/stories/crud/create +2 -2
- data/stories/crud/stories.rb +1 -1
- data/stories/helper.rb +1 -1
- data/stories/resources/helpers/story_helper.rb +1 -1
- data/stories/resources/steps/read.rb +5 -5
- data/stories/resources/steps/using_rest_adapter.rb +9 -9
- metadata +3 -3
data/Manifest.txt
CHANGED
data/lib/rest_adapter.rb
CHANGED
@@ -3,7 +3,10 @@ require 'pathname'
|
|
3
3
|
require Pathname(__FILE__).dirname + 'rest_adapter/version'
|
4
4
|
gem 'dm-core', DataMapper::More::RestAdapter::VERSION
|
5
5
|
require 'dm-core'
|
6
|
+
|
7
|
+
gem 'extlib', '=0.9.4'
|
6
8
|
require 'extlib'
|
9
|
+
|
7
10
|
require 'dm-serializer'
|
8
11
|
require 'net/http'
|
9
12
|
require 'rexml/document'
|
@@ -14,7 +17,7 @@ module DataMapper
|
|
14
17
|
module Adapters
|
15
18
|
class RestAdapter < AbstractAdapter
|
16
19
|
include Extlib
|
17
|
-
|
20
|
+
|
18
21
|
# Creates a new resource in the specified repository.
|
19
22
|
def create(resources)
|
20
23
|
count = 0
|
@@ -22,7 +25,7 @@ module DataMapper
|
|
22
25
|
resource_name = Inflection.underscore(resource.class.name)
|
23
26
|
result = http_post("/#{resource_name.pluralize}.xml", resource.to_xml)
|
24
27
|
# TODO: Raise error if cannot reach server
|
25
|
-
success = result.instance_of?(Net::HTTPCreated)
|
28
|
+
success = result.instance_of?(Net::HTTPCreated)
|
26
29
|
if success
|
27
30
|
count += 1
|
28
31
|
# TODO: Fix commented out code below to work through the identity_map of the repository
|
@@ -33,7 +36,7 @@ module DataMapper
|
|
33
36
|
end
|
34
37
|
count
|
35
38
|
end
|
36
|
-
|
39
|
+
|
37
40
|
# read_set
|
38
41
|
#
|
39
42
|
# Examples of query string:
|
@@ -62,7 +65,7 @@ module DataMapper
|
|
62
65
|
end
|
63
66
|
end
|
64
67
|
end
|
65
|
-
|
68
|
+
|
66
69
|
def read_one(query)
|
67
70
|
resource = nil
|
68
71
|
resource_name = resource_name_from_query(query)
|
@@ -73,16 +76,16 @@ module DataMapper
|
|
73
76
|
else
|
74
77
|
id = query.conditions.first[2]
|
75
78
|
# KLUGE: Again, we're assuming below that we're dealing with a pluralized resource mapping
|
76
|
-
|
79
|
+
|
77
80
|
response = http_get("/#{resource_name.pluralize}/#{id}.xml")
|
78
|
-
|
81
|
+
|
79
82
|
# KLUGE: Rails returns HTML if it can't find a resource. A properly RESTful app would return a 404, right?
|
80
83
|
return nil if response.is_a? Net::HTTPNotFound || response.content_type == "text/html"
|
81
|
-
|
84
|
+
|
82
85
|
data = response.body
|
83
86
|
resource_meta = parse_resource(data, query.model, query)
|
84
87
|
end
|
85
|
-
if resource_meta
|
88
|
+
if resource_meta
|
86
89
|
if resource_meta.has_key?(:associations)
|
87
90
|
load_nested_resources_from resource_meta[:associations], query
|
88
91
|
end
|
@@ -90,13 +93,13 @@ module DataMapper
|
|
90
93
|
end
|
91
94
|
resource
|
92
95
|
end
|
93
|
-
|
96
|
+
|
94
97
|
def update(attributes, query)
|
95
98
|
# TODO What if we have a compound key?
|
96
99
|
raise NotImplementedError.new unless is_single_resource_query? query
|
97
100
|
id = query.conditions.first[2]
|
98
101
|
resource = nil
|
99
|
-
query.repository.scope do
|
102
|
+
query.repository.scope do
|
100
103
|
resource = query.model.get(id)
|
101
104
|
end
|
102
105
|
attributes.each do |attr, val|
|
@@ -107,15 +110,15 @@ module DataMapper
|
|
107
110
|
# TODO: Raise error if cannot reach server
|
108
111
|
res.kind_of?(Net::HTTPSuccess) ? 1 : 0
|
109
112
|
end
|
110
|
-
|
113
|
+
|
111
114
|
def delete(query)
|
112
115
|
raise NotImplementedError.new unless is_single_resource_query? query
|
113
116
|
id = query.conditions.first[2]
|
114
117
|
res = http_delete("/#{resource_name_from_query(query).pluralize}/#{id}.xml")
|
115
118
|
res.kind_of?(Net::HTTPSuccess) ? 1 : 0
|
116
119
|
end
|
117
|
-
|
118
|
-
protected
|
120
|
+
|
121
|
+
protected
|
119
122
|
def load_nested_resources_from(nested_resources, query)
|
120
123
|
nested_resources.each do |resource_meta|
|
121
124
|
# TODO: Houston, we have a problem. Model#load expects a Query. When we're nested, we don't have a query yet...
|
@@ -125,7 +128,7 @@ module DataMapper
|
|
125
128
|
#end
|
126
129
|
end
|
127
130
|
end
|
128
|
-
|
131
|
+
|
129
132
|
def read_set_all(repository, query, resource_name)
|
130
133
|
# TODO: how do we know whether the resource we're talking to is singular or plural?
|
131
134
|
res = http_get("/#{resource_name.pluralize}.xml")
|
@@ -133,18 +136,18 @@ module DataMapper
|
|
133
136
|
parse_resources(data, query.model, query)
|
134
137
|
# TODO: Raise error if cannot reach server
|
135
138
|
end
|
136
|
-
|
139
|
+
|
137
140
|
# GET /books/4200
|
138
141
|
def read_set_for_condition(repository, query, resource_name)
|
139
142
|
# More complex conditions
|
140
143
|
raise NotImplementedError.new
|
141
|
-
end
|
142
|
-
|
144
|
+
end
|
145
|
+
|
143
146
|
# query.conditions like [[:eql, #<Property:Book:id>, 4200]]
|
144
147
|
def is_single_resource_query?(query)
|
145
148
|
query.conditions.length == 1 && query.conditions.first.first == :eql && query.conditions.first[1].name == :id
|
146
149
|
end
|
147
|
-
|
150
|
+
|
148
151
|
def http_put(uri, data = nil)
|
149
152
|
request { |http| http.put(uri, data, {"Content-Type", "application/xml"}) }
|
150
153
|
end
|
@@ -167,13 +170,13 @@ module DataMapper
|
|
167
170
|
res = yield(http)
|
168
171
|
end
|
169
172
|
res
|
170
|
-
end
|
173
|
+
end
|
171
174
|
|
172
175
|
def values_from_rexml(entity_element, dm_model_class)
|
173
176
|
resource = {}
|
174
177
|
resource[:values] = []
|
175
178
|
entity_element.elements.each do |field_element|
|
176
|
-
attribute = dm_model_class.properties(repository.name).find do |property|
|
179
|
+
attribute = dm_model_class.properties(repository.name).find do |property|
|
177
180
|
# *MUST* use Inflection.underscore on the XML as Rails converts '_' to '-' in the XML
|
178
181
|
property.name.to_s == Inflection.underscore(field_element.name.to_s)
|
179
182
|
end
|
@@ -188,7 +191,7 @@ module DataMapper
|
|
188
191
|
field_element.each_element do |associated_element|
|
189
192
|
model = association[1].child_model
|
190
193
|
(resource[:associations] ||= []) << {
|
191
|
-
:model => model,
|
194
|
+
:model => model,
|
192
195
|
:value => values_from_rexml(associated_element, association[1].child_model)
|
193
196
|
}
|
194
197
|
end
|
@@ -204,7 +207,7 @@ module DataMapper
|
|
204
207
|
return nil unless entity_element
|
205
208
|
values_from_rexml(entity_element, dm_model_class)
|
206
209
|
end
|
207
|
-
|
210
|
+
|
208
211
|
def parse_resources(xml, dm_model_class, query = nil)
|
209
212
|
doc = REXML::Document::new(xml)
|
210
213
|
# # TODO: handle singular resource case as well....
|
@@ -216,15 +219,15 @@ module DataMapper
|
|
216
219
|
doc.elements.collect("#{resource_name.pluralize}/#{resource_name}") do |entity_element|
|
217
220
|
values_from_rexml(entity_element, dm_model_class)
|
218
221
|
end
|
219
|
-
end
|
220
|
-
|
222
|
+
end
|
223
|
+
|
221
224
|
def resource_name_from_model(model)
|
222
225
|
Inflection.underscore(model.name)
|
223
226
|
end
|
224
|
-
|
227
|
+
|
225
228
|
def resource_name_from_query(query)
|
226
229
|
resource_name_from_model(query.model)
|
227
230
|
end
|
228
231
|
end
|
229
232
|
end
|
230
|
-
end
|
233
|
+
end
|
data/lib/rest_adapter/version.rb
CHANGED
data/spec/create_spec.rb
CHANGED
@@ -2,20 +2,20 @@ $LOAD_PATH << File.dirname(__FILE__)
|
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
4
|
describe 'A REST adapter' do
|
5
|
-
|
5
|
+
|
6
6
|
before do
|
7
7
|
@adapter = DataMapper::Repository.adapters[:default]
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
describe 'when saving a resource' do
|
11
|
-
|
11
|
+
|
12
12
|
before do
|
13
13
|
@book = Book.new(:title => 'Hello, World!', :author => 'Anonymous')
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
it 'should make an HTTP Post' do
|
17
17
|
@adapter.should_receive(:http_post).with('/books.xml', @book.to_xml)
|
18
18
|
@book.save
|
19
19
|
end
|
20
20
|
end
|
21
|
-
end
|
21
|
+
end
|
data/spec/delete_spec.rb
CHANGED
@@ -2,21 +2,21 @@ $LOAD_PATH << File.dirname(__FILE__)
|
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
4
|
describe 'A REST adapter' do
|
5
|
-
|
5
|
+
|
6
6
|
before do
|
7
7
|
@adapter = DataMapper::Repository.adapters[:default]
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
describe 'when deleting an existing resource' do
|
11
11
|
before do
|
12
12
|
@book = Book.new(:title => 'Hello, World!', :author => 'Anonymous')
|
13
13
|
@book.stub!(:new_record?).and_return(false)
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
it 'should do an HTTP DELETE' do
|
17
17
|
@adapter.should_receive(:http_delete)
|
18
18
|
@book.destroy
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
end
|
22
|
-
end
|
22
|
+
end
|
data/spec/read_spec.rb
CHANGED
@@ -2,15 +2,15 @@ $LOAD_PATH << File.dirname(__FILE__)
|
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
4
|
describe 'A REST adapter' do
|
5
|
-
|
5
|
+
|
6
6
|
before do
|
7
7
|
@adapter = DataMapper::Repository.adapters[:default]
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
describe 'when getting one resource' do
|
11
11
|
|
12
12
|
describe 'if the resource exists' do
|
13
|
-
|
13
|
+
|
14
14
|
before do
|
15
15
|
book_xml = <<-BOOK
|
16
16
|
<?xml version='1.0' encoding='UTF-8'?>
|
@@ -27,25 +27,25 @@ describe 'A REST adapter' do
|
|
27
27
|
@response.stub!(:body).and_return(book_xml)
|
28
28
|
@adapter.stub!(:http_get).and_return(@response)
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
it 'should return the resource' do
|
32
32
|
book = Book.get(@id)
|
33
33
|
book.should_not be_nil
|
34
34
|
book.id.should be_an_instance_of(Fixnum)
|
35
35
|
book.id.should == 1
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
it 'should do an HTTP GET' do
|
39
39
|
@adapter.should_receive(:http_get).with('/books/1.xml').and_return(@response)
|
40
40
|
Book.get(@id)
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
it "it be equal to itself" do
|
44
44
|
Book.get(@id).should == Book.get(@id)
|
45
|
-
end
|
45
|
+
end
|
46
46
|
end
|
47
|
-
|
48
|
-
|
47
|
+
|
48
|
+
|
49
49
|
describe 'if the resource does not exist' do
|
50
50
|
it 'should return nil' do
|
51
51
|
@id = 1
|
@@ -58,9 +58,9 @@ describe 'A REST adapter' do
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
61
|
-
|
61
|
+
|
62
62
|
describe 'when getting all resource of a particular type' do
|
63
|
-
before do
|
63
|
+
before do
|
64
64
|
books_xml = <<-BOOK
|
65
65
|
<?xml version='1.0' encoding='UTF-8'?>
|
66
66
|
<books type='array'>
|
@@ -70,7 +70,7 @@ describe 'A REST adapter' do
|
|
70
70
|
<id type='integer'>1</id>
|
71
71
|
<title>The Dispossed</title>
|
72
72
|
<updated-at type='datetime'>2008-06-08T17:02:28Z</updated-at>
|
73
|
-
</book>
|
73
|
+
</book>
|
74
74
|
<book>
|
75
75
|
<author>Stephen King</author>
|
76
76
|
<created-at type='datetime'>2008-06-08T17:03:07Z</created-at>
|
@@ -84,18 +84,18 @@ describe 'A REST adapter' do
|
|
84
84
|
@response.stub!(:body).and_return(books_xml)
|
85
85
|
@adapter.stub!(:http_get).and_return(@response)
|
86
86
|
end
|
87
|
-
|
87
|
+
|
88
88
|
it 'should get a non-empty list' do
|
89
89
|
Book.all.should_not be_empty
|
90
90
|
end
|
91
|
-
|
91
|
+
|
92
92
|
it 'should receive one Resource for each entity in the XML' do
|
93
93
|
Book.all.size.should == 2
|
94
94
|
end
|
95
|
-
|
95
|
+
|
96
96
|
it 'should do an HTTP GET' do
|
97
97
|
@adapter.should_receive(:http_get).and_return(@response)
|
98
98
|
Book.first
|
99
|
-
end
|
99
|
+
end
|
100
100
|
end
|
101
|
-
end
|
101
|
+
end
|
data/spec/ruby_forker.rb
CHANGED
data/spec/update_spec.rb
CHANGED
@@ -2,7 +2,7 @@ $LOAD_PATH << File.dirname(__FILE__)
|
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
4
|
describe 'A REST adapter' do
|
5
|
-
|
5
|
+
|
6
6
|
describe 'when updating an existing resource' do
|
7
7
|
before do
|
8
8
|
@books_xml = <<-XML
|
@@ -16,11 +16,11 @@ describe 'A REST adapter' do
|
|
16
16
|
repository do |repo|
|
17
17
|
@repository = repo
|
18
18
|
@book = Book.new(:id => 42,
|
19
|
-
:title => 'Starship Troopers',
|
20
|
-
:author => 'Robert Heinlein',
|
19
|
+
:title => 'Starship Troopers',
|
20
|
+
:author => 'Robert Heinlein',
|
21
21
|
:created_at => DateTime.parse('2008-06-08T17:02:28Z'))
|
22
22
|
@book.instance_eval { @new_record = false }
|
23
|
-
@repository.identity_map(Book).set(@book.key, @book)
|
23
|
+
@repository.identity_map(Book).set(@book.key, @book)
|
24
24
|
@book.title = "Mary Had a Little Lamb"
|
25
25
|
end
|
26
26
|
end
|
@@ -28,9 +28,9 @@ describe 'A REST adapter' do
|
|
28
28
|
it 'should do an HTTP PUT' do
|
29
29
|
adapter = @repository.adapter #DataMapper::Repository.adapters[:default]
|
30
30
|
adapter.should_receive(:http_put).with('/books/42.xml', @book.to_xml)
|
31
|
-
@repository.scope do
|
31
|
+
@repository.scope do
|
32
32
|
@book.save
|
33
33
|
end
|
34
34
|
end
|
35
35
|
end
|
36
|
-
end
|
36
|
+
end
|
data/stories/crud/create
CHANGED
@@ -28,12 +28,12 @@ Story: remote app wants to create a resource
|
|
28
28
|
|
29
29
|
Scenario: remote app supplies a new valid Resource associated with another new invalid Resource
|
30
30
|
Given a new Resource
|
31
|
-
And another new invalid Resource
|
31
|
+
And another new invalid Resource associated with the first
|
32
32
|
When I try to save the first Resource
|
33
33
|
Neither Resource should save
|
34
34
|
|
35
35
|
Scenario: remote app supplies a new invalid Resource associated with another new invalid Resource
|
36
36
|
Given a new invalid Resource
|
37
|
-
And another new invalid Resource
|
37
|
+
And another new invalid Resource associated with the first
|
38
38
|
When I try to save the first Resource
|
39
39
|
Neither Resource should save
|
data/stories/crud/stories.rb
CHANGED
data/stories/helper.rb
CHANGED
@@ -6,4 +6,4 @@ require 'dm-core'
|
|
6
6
|
#require 'pathname'
|
7
7
|
#require Pathname(__FILE__).dirname.parent.expand_path + '../../lib/rest_adapter'
|
8
8
|
require File.join(File.dirname(__FILE__), *%w[resources helpers story_helper])
|
9
|
-
require File.join(File.dirname(__FILE__), *%w[resources steps using_rest_adapter])
|
9
|
+
require File.join(File.dirname(__FILE__), *%w[resources steps using_rest_adapter])
|
@@ -1,2 +1,2 @@
|
|
1
1
|
require 'spec/story'
|
2
|
-
require File.dirname(__FILE__) + '/../../../spec/ruby_forker'
|
2
|
+
require File.dirname(__FILE__) + '/../../../spec/ruby_forker'
|
@@ -7,9 +7,9 @@ steps_for :read do
|
|
7
7
|
has n, :books
|
8
8
|
end
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
Given "the ID of an existing Resource that has associated Resources" do
|
12
|
-
# Assuming that resource 1 is there.
|
12
|
+
# Assuming that resource 1 is there.
|
13
13
|
# @type.first would do a GET; that's what we're testing
|
14
14
|
@resource_id = 1
|
15
15
|
end
|
@@ -21,15 +21,15 @@ steps_for :read do
|
|
21
21
|
When "I GET <nested resource>/<id>" do
|
22
22
|
@resource = Shelf.get(@resource_id)
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
Then "I should get the Resource" do
|
26
26
|
@resource.should_not be_nil
|
27
27
|
@resource.should be_an_instance_of(Shelf)
|
28
28
|
@resource.id.should == 1
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
Then "the Resource will have associated Resources" do
|
32
32
|
@resource.books.should_not be_empty
|
33
33
|
@resource.books.first.should be_an_instance_of(Book)
|
34
34
|
end
|
35
|
-
end
|
35
|
+
end
|
@@ -39,18 +39,18 @@ steps_for :using_rest_adapter do
|
|
39
39
|
@resource = Book.first
|
40
40
|
@resource_id = @resource.id
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
When("I try to save the Resource") do
|
44
44
|
@result = @resource.save
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
47
|
When("I request all of the Resources of that type") do
|
48
48
|
require File.join(File.dirname(__FILE__), '..', 'helpers', 'book')
|
49
49
|
@resources = Book.all
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
When("I request the Resource") do
|
53
|
-
require File.join(File.dirname(__FILE__), '..', 'helpers', 'book')
|
53
|
+
require File.join(File.dirname(__FILE__), '..', 'helpers', 'book')
|
54
54
|
@resource = Book.get(@resource_id)
|
55
55
|
end
|
56
56
|
|
@@ -61,19 +61,19 @@ steps_for :using_rest_adapter do
|
|
61
61
|
When("I make invalid changes to that Resource") do
|
62
62
|
@resource.title = nil
|
63
63
|
end
|
64
|
-
|
64
|
+
|
65
65
|
When("I destroy the Resource") do
|
66
66
|
@resource.destroy
|
67
67
|
end
|
68
|
-
|
68
|
+
|
69
69
|
Then("the Resource should save") do
|
70
70
|
@result.should be_true
|
71
71
|
end
|
72
|
-
|
72
|
+
|
73
73
|
Then("the Resource should not save") do
|
74
74
|
@result.should be_false
|
75
75
|
end
|
76
|
-
|
76
|
+
|
77
77
|
Then("I should not receive an empty list") do
|
78
78
|
@resources.should_not be_empty
|
79
79
|
end
|
@@ -86,7 +86,7 @@ steps_for :using_rest_adapter do
|
|
86
86
|
Then("I should get nothing in return") do
|
87
87
|
@resource.should be_nil
|
88
88
|
end
|
89
|
-
|
89
|
+
|
90
90
|
Then("the Resource will no longer be available") do
|
91
91
|
# TODO refactor
|
92
92
|
require File.join(File.dirname(__FILE__), '..', 'helpers', 'book')
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dm-rest-adapter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Potomac Ruby Hackers
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-08-21 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -20,7 +20,7 @@ dependencies:
|
|
20
20
|
requirements:
|
21
21
|
- - "="
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: 0.9.
|
23
|
+
version: 0.9.4
|
24
24
|
version:
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: hoe
|