dm-rest-adapter 0.9.11 → 0.10.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/{History.txt → History.rdoc} +5 -3
- data/Manifest.txt +5 -8
- data/{README.markdown → README.rdoc} +0 -0
- data/Rakefile +2 -3
- data/lib/rest_adapter.rb +4 -6
- data/lib/rest_adapter/adapter.rb +109 -232
- data/lib/rest_adapter/connection.rb +12 -5
- data/lib/rest_adapter/exceptions.rb +2 -2
- data/lib/rest_adapter/formats.rb +1 -1
- data/lib/rest_adapter/version.rb +1 -1
- data/spec/fixtures/book.rb +8 -0
- data/spec/{connection_spec.rb → semipublic/connection_spec.rb} +8 -10
- data/spec/semipublic/rest_adapter_spec.rb +224 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +25 -14
- data/tasks/install.rb +1 -1
- data/tasks/spec.rb +4 -4
- metadata +16 -26
- data/README.txt +0 -47
- data/config/database.rb.example +0 -8
- data/dm-rest-adapter.gemspec +0 -34
- data/spec/crud_spec.rb +0 -250
- data/spec/ruby_forker.rb +0 -13
data/README.txt
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
= dm-rest-adapter
|
2
|
-
|
3
|
-
A DataMapper adapter for REST Web Services
|
4
|
-
|
5
|
-
== Usage
|
6
|
-
|
7
|
-
DM Rest Adapter requires the use of a model which is the same name as the resource you are using. For example, if you have a resource named "posts" you will create a standard datamapper object called post.rb in app/models. The only difference in this model is you will need to define the rest adapter for the model. The following is an example of a post model, where the host settings point to the app you are running the resource on. In addition I have included a basic auth login which will be used if your resource requires auth:
|
8
|
-
|
9
|
-
DataMapper.setup(:default, {
|
10
|
-
:adapter => 'rest',
|
11
|
-
:format => 'xml',
|
12
|
-
:host => 'localhost',
|
13
|
-
:port => 4000,
|
14
|
-
:login => 'user',
|
15
|
-
:password => 'verys3crit'
|
16
|
-
})
|
17
|
-
|
18
|
-
class Post
|
19
|
-
|
20
|
-
include DataMapper::Resource
|
21
|
-
|
22
|
-
property :id, Serial
|
23
|
-
property :title, String
|
24
|
-
property :body, Text
|
25
|
-
|
26
|
-
end
|
27
|
-
|
28
|
-
|
29
|
-
If you notice this looks exactly like a normal datmapper model. Every property you define will map itself with the xml returned or posted from/to the resource.
|
30
|
-
|
31
|
-
== Code
|
32
|
-
|
33
|
-
Now for some code examples. DM Rest Adapter uses the same methods as datamapper including during creation.
|
34
|
-
|
35
|
-
Post.first => returns the object from the resouce
|
36
|
-
Post.get(1) => returns the object from the resource
|
37
|
-
p = Post.new(:title => "My awesome blog post", :body => "I really have nothing to say...")
|
38
|
-
p.save => saves the resource on the remote
|
39
|
-
|
40
|
-
== Caveat
|
41
|
-
|
42
|
-
Posts do not honor RESTful HTTP status codes. I might fix this...
|
43
|
-
|
44
|
-
== TODO:
|
45
|
-
|
46
|
-
Nested resources
|
47
|
-
Put verb actions
|
data/config/database.rb.example
DELETED
data/dm-rest-adapter.gemspec
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
|
-
Gem::Specification.new do |s|
|
4
|
-
s.name = %q{dm-rest-adapter}
|
5
|
-
s.version = "0.9.11"
|
6
|
-
|
7
|
-
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
-
s.authors = ["Scott Burton @ Joyent Inc"]
|
9
|
-
s.date = %q{2009-03-23}
|
10
|
-
s.description = %q{REST Adapter for DataMapper}
|
11
|
-
s.email = ["scott.burton [a] joyent [d] com"]
|
12
|
-
s.extra_rdoc_files = ["README.txt", "LICENSE", "TODO", "History.txt"]
|
13
|
-
s.files = ["History.txt", "LICENSE", "Manifest.txt", "README.markdown", "README.txt", "Rakefile", "TODO", "config/database.rb.example", "dm-rest-adapter.gemspec", "lib/rest_adapter.rb", "lib/rest_adapter/adapter.rb", "lib/rest_adapter/connection.rb", "lib/rest_adapter/exceptions.rb", "lib/rest_adapter/formats.rb", "lib/rest_adapter/version.rb", "spec/connection_spec.rb", "spec/crud_spec.rb", "spec/ruby_forker.rb", "spec/spec.opts", "spec/spec_helper.rb", "tasks/install.rb", "tasks/spec.rb"]
|
14
|
-
s.has_rdoc = true
|
15
|
-
s.homepage = %q{http://github.com/datamapper/dm-more/tree/master/adapters/dm-rest-adapter}
|
16
|
-
s.rdoc_options = ["--main", "README.txt"]
|
17
|
-
s.require_paths = ["lib"]
|
18
|
-
s.rubyforge_project = %q{datamapper}
|
19
|
-
s.rubygems_version = %q{1.3.1}
|
20
|
-
s.summary = %q{REST Adapter for DataMapper}
|
21
|
-
|
22
|
-
if s.respond_to? :specification_version then
|
23
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
-
s.specification_version = 2
|
25
|
-
|
26
|
-
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
27
|
-
s.add_runtime_dependency(%q<dm-core>, ["= 0.9.11"])
|
28
|
-
else
|
29
|
-
s.add_dependency(%q<dm-core>, ["= 0.9.11"])
|
30
|
-
end
|
31
|
-
else
|
32
|
-
s.add_dependency(%q<dm-core>, ["= 0.9.11"])
|
33
|
-
end
|
34
|
-
end
|
data/spec/crud_spec.rb
DELETED
@@ -1,250 +0,0 @@
|
|
1
|
-
$LOAD_PATH << File.dirname(__FILE__)
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe 'A REST adapter' do
|
5
|
-
|
6
|
-
before do
|
7
|
-
@book = Book.new(:title => 'Hello, World!', :author => 'Anonymous')
|
8
|
-
@adapter = DataMapper::Repository.adapters[:default]
|
9
|
-
end
|
10
|
-
|
11
|
-
describe 'when saving a new resource' do
|
12
|
-
|
13
|
-
before(:each) do
|
14
|
-
@mock_resp = mock("response")
|
15
|
-
|
16
|
-
@book.id = 1
|
17
|
-
@mock_resp.should_receive(:body).and_return @book.to_xml
|
18
|
-
end
|
19
|
-
|
20
|
-
it "should create a book" do
|
21
|
-
@mock_http = mock("http")
|
22
|
-
Net::HTTP.should_receive(:start).and_yield @mock_http
|
23
|
-
|
24
|
-
@mock_resp.should_receive(:code).and_return 200
|
25
|
-
|
26
|
-
@mock_http.should_receive(:request).and_return @mock_resp
|
27
|
-
|
28
|
-
@book.save.should eql(true)
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'should make an HTTP POST' do
|
32
|
-
@adapter.connection.should_receive(:http_post).with('books', @book.to_xml).and_return @mock_resp
|
33
|
-
@book.save
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'should call run_verb with POST' do
|
37
|
-
@adapter.connection.should_receive(:run_verb).with('post', @book.to_xml).and_return @mock_resp
|
38
|
-
@book.save
|
39
|
-
end
|
40
|
-
|
41
|
-
end
|
42
|
-
|
43
|
-
describe 'when returning incorrect xml from a save' do
|
44
|
-
before(:all) do
|
45
|
-
@mock_resp = mock("response")
|
46
|
-
end
|
47
|
-
|
48
|
-
it "should raise error on missing root element in xml" do
|
49
|
-
@mock_resp.should_receive(:body).and_return ""
|
50
|
-
@adapter.connection.should_receive(:run_verb).with('post', @book.to_xml).and_return @mock_resp
|
51
|
-
|
52
|
-
lambda {@book.save}.should raise_error(RuntimeError, "No root element matching book in xml")
|
53
|
-
end
|
54
|
-
|
55
|
-
it "should not raise an error if the root xml is empty" do
|
56
|
-
@mock_resp.should_receive(:body).and_return "<book></book>"
|
57
|
-
@adapter.connection.should_receive(:run_verb).with('post', @book.to_xml).and_return @mock_resp
|
58
|
-
|
59
|
-
lambda {@book.save}.should_not raise_error(RuntimeError)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
describe 'when deleting an existing resource' do
|
64
|
-
before do
|
65
|
-
@book.stub!(:new_record?).and_return(false)
|
66
|
-
end
|
67
|
-
|
68
|
-
it 'should do an HTTP DELETE' do
|
69
|
-
@adapter.connection.should_receive(:http_delete)
|
70
|
-
@book.destroy
|
71
|
-
end
|
72
|
-
|
73
|
-
it "should raise NotImplementedError if is not a single resource query" do
|
74
|
-
@adapter.should_receive(:is_single_resource_query?).and_return(false)
|
75
|
-
lambda {@book.destroy}.should raise_error(NotImplementedError)
|
76
|
-
end
|
77
|
-
|
78
|
-
it 'should call run_verb with DELETE and no data' do
|
79
|
-
@adapter.connection.should_receive(:run_verb).with('delete', nil)
|
80
|
-
@book.destroy
|
81
|
-
end
|
82
|
-
|
83
|
-
it "should return false if the record does not exist in the repository" do
|
84
|
-
@book.should_receive(:new_record?).and_return(true)
|
85
|
-
@book.destroy.should eql(false)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
describe 'when getting one resource' do
|
90
|
-
|
91
|
-
describe 'if the resource exists' do
|
92
|
-
|
93
|
-
before do
|
94
|
-
book_xml = <<-BOOK
|
95
|
-
<?xml version='1.0' encoding='UTF-8'?>
|
96
|
-
<book>
|
97
|
-
<author>Stephen King</author>
|
98
|
-
<created-at type='datetime'>2008-06-08T17:03:07Z</created-at>
|
99
|
-
<id type='integer'>1</id>
|
100
|
-
<title>The Shining</title>
|
101
|
-
<updated-at type='datetime'>2008-06-08T17:03:07Z</updated-at>
|
102
|
-
</book>
|
103
|
-
BOOK
|
104
|
-
@id = 1
|
105
|
-
@response = mock(Net::HTTPResponse)
|
106
|
-
@response.stub!(:body).and_return(book_xml)
|
107
|
-
@adapter.connection.stub!(:http_get).and_return(@response)
|
108
|
-
end
|
109
|
-
|
110
|
-
it 'should return the resource' do
|
111
|
-
book = Book.get(@id)
|
112
|
-
book.should_not be_nil
|
113
|
-
book.id.should be_an_instance_of(Fixnum)
|
114
|
-
book.id.should == 1
|
115
|
-
end
|
116
|
-
|
117
|
-
it "should have its attributes well formed" do
|
118
|
-
book = Book.get(@id)
|
119
|
-
book.author.should == 'Stephen King'
|
120
|
-
book.title.should == 'The Shining'
|
121
|
-
end
|
122
|
-
|
123
|
-
it 'should do an HTTP GET' do
|
124
|
-
@adapter.connection.should_receive(:http_get).with('books/1').and_return(@response)
|
125
|
-
Book.get(@id)
|
126
|
-
end
|
127
|
-
|
128
|
-
it "should be equal to itself" do
|
129
|
-
Book.get(@id).should == Book.get(@id)
|
130
|
-
end
|
131
|
-
|
132
|
-
it "should return its cached version when it was already fetched" do
|
133
|
-
book = mock(Book, :kind_of? => Book)
|
134
|
-
repo = mock(DataMapper::Repository)
|
135
|
-
ident_map = mock(DataMapper::IdentityMap)
|
136
|
-
|
137
|
-
Book.should_receive(:repository).and_return(repo)
|
138
|
-
repo.should_receive(:identity_map).and_return(ident_map)
|
139
|
-
ident_map.stub!(:get).with([@id]).and_return(book)
|
140
|
-
|
141
|
-
# The remote resource won't be called when a cached object exists
|
142
|
-
Book.should_receive(:first).never
|
143
|
-
Book.get(@id).should be_a_kind_of(Book)
|
144
|
-
end
|
145
|
-
|
146
|
-
it "should call read_one method" do
|
147
|
-
@adapter.should_receive(:read_one)
|
148
|
-
Book.get(@id)
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
describe 'if the resource does not exist' do
|
153
|
-
it 'should raise DataMapperRest::ResourceNotFound' do
|
154
|
-
@mock_resp = mock("response")
|
155
|
-
@mock_http = mock("http")
|
156
|
-
Net::HTTP.should_receive(:start).and_yield @mock_http
|
157
|
-
|
158
|
-
@mock_resp.should_receive(:code).and_return 404
|
159
|
-
@mock_http.should_receive(:request).and_return @mock_resp
|
160
|
-
|
161
|
-
lambda{ Book.get(5000) }.should raise_error(DataMapperRest::ResourceNotFound)
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
describe 'when getting all resource of a particular type' do
|
167
|
-
before do
|
168
|
-
books_xml = <<-BOOK
|
169
|
-
<?xml version='1.0' encoding='UTF-8'?>
|
170
|
-
<books type='array'>
|
171
|
-
<book>
|
172
|
-
<author>Ursula K LeGuin</author>
|
173
|
-
<created-at type='datetime'>2008-06-08T17:02:28Z</created-at>
|
174
|
-
<id type='integer'>1</id>
|
175
|
-
<title>The Dispossed</title>
|
176
|
-
<updated-at type='datetime'>2008-06-08T17:02:28Z</updated-at>
|
177
|
-
</book>
|
178
|
-
<book>
|
179
|
-
<author>Stephen King</author>
|
180
|
-
<created-at type='datetime'>2008-06-08T17:03:07Z</created-at>
|
181
|
-
<id type='integer'>2</id>
|
182
|
-
<title>The Shining</title>
|
183
|
-
<updated-at type='datetime'>2008-06-08T17:03:07Z</updated-at>
|
184
|
-
</book>
|
185
|
-
</books>
|
186
|
-
BOOK
|
187
|
-
@response = mock(Net::HTTPResponse)
|
188
|
-
@response.stub!(:body).and_return(books_xml)
|
189
|
-
end
|
190
|
-
|
191
|
-
it 'should get a non-empty list' do
|
192
|
-
@adapter.connection.stub!(:http_get).and_return(@response)
|
193
|
-
Book.all.should_not be_empty
|
194
|
-
end
|
195
|
-
|
196
|
-
it 'should receive one Resource for each entity in the XML' do
|
197
|
-
@adapter.connection.stub!(:http_get).and_return(@response)
|
198
|
-
Book.all.size.should == 2
|
199
|
-
end
|
200
|
-
|
201
|
-
it "should call read_many method" do
|
202
|
-
@adapter.connection.stub!(:http_get).and_return(@response)
|
203
|
-
@adapter.should_receive(:read_many)
|
204
|
-
Book.all
|
205
|
-
end
|
206
|
-
|
207
|
-
it "should raise NotImplementedError if conditions are specified" do
|
208
|
-
# Have to find a way to set an expectation for a method call inside a block
|
209
|
-
# Book.all(:title => "NonExistentTitle")
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
describe 'when updating an existing resource' do
|
214
|
-
before do
|
215
|
-
@books_xml = <<-XML
|
216
|
-
<book>
|
217
|
-
<id type='integer'>42</id>
|
218
|
-
<title>Starship Troopers</title>
|
219
|
-
<author>Robert Heinlein</author>
|
220
|
-
<created-at type='datetime'>2008-06-08T17:02:28Z</created-at>
|
221
|
-
</book>
|
222
|
-
XML
|
223
|
-
repository do |repo|
|
224
|
-
@repository = repo
|
225
|
-
@book = Book.new(:id => 42,
|
226
|
-
:title => 'Starship Troopers',
|
227
|
-
:author => 'Robert Heinlein',
|
228
|
-
:created_at => DateTime.parse('2008-06-08T17:02:28Z'))
|
229
|
-
@book.instance_eval { @new_record = false }
|
230
|
-
@repository.identity_map(Book)[@book.key] = @book
|
231
|
-
@book.title = "Mary Had a Little Lamb"
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
it 'should do an HTTP PUT' do
|
236
|
-
@adapter.connection.should_receive(:http_put).with('books/42', @book.to_xml)
|
237
|
-
@repository.scope do
|
238
|
-
@book.save
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
it "should not do an HTTP PUT for non-dirty resources" do
|
243
|
-
@book.should_receive(:dirty_attributes).and_return({})
|
244
|
-
@adapter.connection.should_receive(:http_put).never
|
245
|
-
@repository.scope do
|
246
|
-
@book.save
|
247
|
-
end
|
248
|
-
end
|
249
|
-
end
|
250
|
-
end
|
data/spec/ruby_forker.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
require 'rbconfig'
|
2
|
-
|
3
|
-
module RubyForker
|
4
|
-
# Forks a ruby interpreter with same type as ourself.
|
5
|
-
# juby will fork jruby, ruby will fork ruby etc.
|
6
|
-
def ruby(args, stderr=nil)
|
7
|
-
config = ::Config::CONFIG
|
8
|
-
interpreter = File::join(config['bindir'], config['ruby_install_name']) + config['EXEEXT']
|
9
|
-
cmd = "#{interpreter} #{args}"
|
10
|
-
cmd << " 2> #{stderr}" unless stderr.nil?
|
11
|
-
`#{cmd}`
|
12
|
-
end
|
13
|
-
end
|