dm-rest-adapter 0.9.3 → 0.9.4
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/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
|