restfulie 0.1.0.beta1 → 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +87 -99
- data/Rakefile +14 -109
- data/lib/restfulie.rb +183 -23
- metadata +12 -197
- data/Gemfile +0 -28
- data/Gemfile.lock +0 -128
- data/LICENSE +0 -17
- data/lib/restfulie/client.rb +0 -26
- data/lib/restfulie/client/base.rb +0 -36
- data/lib/restfulie/client/cache.rb +0 -11
- data/lib/restfulie/client/cache/basic.rb +0 -76
- data/lib/restfulie/client/cache/fake.rb +0 -15
- data/lib/restfulie/client/cache/http_ext.rb +0 -123
- data/lib/restfulie/client/cache/restrictions.rb +0 -13
- data/lib/restfulie/client/configuration.rb +0 -67
- data/lib/restfulie/client/dsl.rb +0 -66
- data/lib/restfulie/client/entry_point.rb +0 -61
- data/lib/restfulie/client/ext/atom_ext.rb +0 -14
- data/lib/restfulie/client/ext/http_ext.rb +0 -22
- data/lib/restfulie/client/ext/json_ext.rb +0 -16
- data/lib/restfulie/client/feature.rb +0 -5
- data/lib/restfulie/client/feature/base.rb +0 -75
- data/lib/restfulie/client/feature/base_request.rb +0 -35
- data/lib/restfulie/client/feature/cache.rb +0 -16
- data/lib/restfulie/client/feature/enhance_response.rb +0 -12
- data/lib/restfulie/client/feature/follow_request.rb +0 -41
- data/lib/restfulie/client/feature/history.rb +0 -26
- data/lib/restfulie/client/feature/history_request.rb +0 -19
- data/lib/restfulie/client/feature/open_search.rb +0 -21
- data/lib/restfulie/client/feature/open_search/pattern_matcher.rb +0 -25
- data/lib/restfulie/client/feature/serialize_body.rb +0 -32
- data/lib/restfulie/client/feature/setup_header.rb +0 -22
- data/lib/restfulie/client/feature/throw_error.rb +0 -41
- data/lib/restfulie/client/feature/verb.rb +0 -119
- data/lib/restfulie/client/http.rb +0 -7
- data/lib/restfulie/client/http/cache.rb +0 -28
- data/lib/restfulie/client/http/error.rb +0 -77
- data/lib/restfulie/client/http/response_holder.rb +0 -29
- data/lib/restfulie/client/master_delegator.rb +0 -31
- data/lib/restfulie/client/mikyung.rb +0 -15
- data/lib/restfulie/client/mikyung/concatenator.rb +0 -18
- data/lib/restfulie/client/mikyung/core.rb +0 -70
- data/lib/restfulie/client/mikyung/languages.rb +0 -11
- data/lib/restfulie/client/mikyung/languages/german.rb +0 -24
- data/lib/restfulie/client/mikyung/languages/portuguese.rb +0 -23
- data/lib/restfulie/client/mikyung/rest_process_model.rb +0 -191
- data/lib/restfulie/client/mikyung/steady_state_walker.rb +0 -38
- data/lib/restfulie/client/mikyung/then_condition.rb +0 -39
- data/lib/restfulie/client/mikyung/when_condition.rb +0 -57
- data/lib/restfulie/common.rb +0 -18
- data/lib/restfulie/common/converter.rb +0 -43
- data/lib/restfulie/common/converter/atom.rb +0 -12
- data/lib/restfulie/common/converter/atom/base.rb +0 -91
- data/lib/restfulie/common/converter/atom/builder.rb +0 -111
- data/lib/restfulie/common/converter/atom/helpers.rb +0 -17
- data/lib/restfulie/common/converter/json.rb +0 -12
- data/lib/restfulie/common/converter/json/base.rb +0 -87
- data/lib/restfulie/common/converter/json/builder.rb +0 -102
- data/lib/restfulie/common/converter/json/helpers.rb +0 -17
- data/lib/restfulie/common/converter/open_search.rb +0 -16
- data/lib/restfulie/common/converter/open_search/descriptor.rb +0 -32
- data/lib/restfulie/common/converter/values.rb +0 -33
- data/lib/restfulie/common/converter/xml.rb +0 -14
- data/lib/restfulie/common/converter/xml/base.rb +0 -63
- data/lib/restfulie/common/converter/xml/builder.rb +0 -113
- data/lib/restfulie/common/converter/xml/helpers.rb +0 -17
- data/lib/restfulie/common/converter/xml/link.rb +0 -30
- data/lib/restfulie/common/converter/xml/links.rb +0 -21
- data/lib/restfulie/common/core_ext.rb +0 -1
- data/lib/restfulie/common/core_ext/hash.rb +0 -18
- data/lib/restfulie/common/error.rb +0 -19
- data/lib/restfulie/common/links.rb +0 -9
- data/lib/restfulie/common/logger.rb +0 -19
- data/lib/restfulie/common/representation.rb +0 -3
- data/lib/restfulie/common/representation/atom.rb +0 -20
- data/lib/restfulie/common/representation/atom/atom.rng +0 -597
- data/lib/restfulie/common/representation/atom/base.rb +0 -142
- data/lib/restfulie/common/representation/atom/category.rb +0 -41
- data/lib/restfulie/common/representation/atom/entry.rb +0 -59
- data/lib/restfulie/common/representation/atom/factory.rb +0 -43
- data/lib/restfulie/common/representation/atom/feed.rb +0 -110
- data/lib/restfulie/common/representation/atom/link.rb +0 -68
- data/lib/restfulie/common/representation/atom/person.rb +0 -48
- data/lib/restfulie/common/representation/atom/source.rb +0 -59
- data/lib/restfulie/common/representation/atom/tag_collection.rb +0 -38
- data/lib/restfulie/common/representation/atom/xml.rb +0 -90
- data/lib/restfulie/common/representation/generic.rb +0 -22
- data/lib/restfulie/common/representation/json.rb +0 -13
- data/lib/restfulie/common/representation/json/base.rb +0 -27
- data/lib/restfulie/common/representation/json/keys_as_methods.rb +0 -74
- data/lib/restfulie/common/representation/json/link.rb +0 -29
- data/lib/restfulie/common/representation/json/link_collection.rb +0 -23
- data/lib/restfulie/common/representation/links.rb +0 -11
- data/lib/restfulie/server.rb +0 -25
- data/lib/restfulie/server/action_controller.rb +0 -11
- data/lib/restfulie/server/action_controller/base.rb +0 -48
- data/lib/restfulie/server/action_controller/params_parser.rb +0 -100
- data/lib/restfulie/server/action_controller/patch.rb +0 -6
- data/lib/restfulie/server/action_controller/restful_responder.rb +0 -12
- data/lib/restfulie/server/action_controller/trait.rb +0 -9
- data/lib/restfulie/server/action_controller/trait/cacheable.rb +0 -81
- data/lib/restfulie/server/action_controller/trait/created.rb +0 -17
- data/lib/restfulie/server/action_view.rb +0 -10
- data/lib/restfulie/server/action_view/helpers.rb +0 -50
- data/lib/restfulie/server/action_view/template_handlers.rb +0 -30
- data/lib/restfulie/server/action_view/template_handlers/tokamak.rb +0 -21
- data/lib/restfulie/server/configuration.rb +0 -24
- data/lib/restfulie/server/controller.rb +0 -74
- data/lib/restfulie/server/core_ext.rb +0 -1
- data/lib/restfulie/server/core_ext/array.rb +0 -61
- data/lib/restfulie/version.rb +0 -14
data/README.textile
CHANGED
@@ -1,138 +1,126 @@
|
|
1
|
-
h1. Web site
|
2
|
-
|
3
|
-
Restfulie's website can be found at "http://restfulie.caelumobjects.com":http://restfulie.caelumobjects.com
|
4
|
-
From there you can see some videos and read the "Restfulie from Scratch guide":http://restfulie.caelumobjects.com/caelumobjects-restful-rails.pdf.
|
5
|
-
|
6
1
|
h1. Quit pretending
|
7
2
|
|
8
|
-
CRUD through HTTP is a good step forward to using resources and becoming
|
9
|
-
|
10
|
-
You can read the "article on using the web for real":http://guilhermesilveira.wordpress.com/2009/11/03/quit-pretending-use-the-web-for-real-restfulie/ which gives an introduction to hypermedia/resources/services.
|
3
|
+
CRUD through HTTP is a good step forward to using resources and becoming RESTFul, another step further into is to make use of hypermedia based services and this gem allows you to do it really fast.
|
11
4
|
|
12
|
-
|
5
|
+
h1. Restfulie: client-side
|
13
6
|
|
14
|
-
|
15
|
-
# Small -> it's not a bloated solution with a huge list of APIs
|
16
|
-
# HATEOAS --> clients you are unaware of will not bother if you change
|
17
|
-
# HATEOAS --> consumed resources will not affect your software whenever they change their flow
|
18
|
-
# Adaptability --> clients are able to adapt to your changes
|
19
|
-
|
20
|
-
h2. Simple server example
|
21
|
-
|
22
|
-
In the server side, all you need to do is notify inherited_resources which media types you are able to represent your resource:
|
7
|
+
Here you can see how to access a resource and its services through the restfulie API:
|
23
8
|
|
24
9
|
<pre>
|
25
|
-
|
26
|
-
include Restfulie::Server::ActionController::Base
|
10
|
+
order = Order.from_web resource_uri # retrieves order resource (xml/atom/json support)
|
27
11
|
|
28
|
-
|
12
|
+
puts "Order price is #{order.price}"
|
29
13
|
|
30
|
-
|
31
|
-
respond_with @orders = Order.all
|
32
|
-
end
|
14
|
+
order.pay payment # sends a post request to pay this order
|
33
15
|
|
34
|
-
|
35
|
-
respond_with @order = Order.find(params[:id])
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
16
|
+
order.cancel # sends a delete request
|
39
17
|
</pre>
|
40
18
|
|
41
|
-
|
19
|
+
h1. Restfulie: server-side
|
20
|
+
|
21
|
+
This is a simple example how to make your state changes available to your resource consumers:
|
42
22
|
|
43
23
|
<pre>
|
44
|
-
|
45
|
-
|
46
|
-
|
24
|
+
class Order < ActiveRecord::Base
|
25
|
+
def following_states
|
26
|
+
states = [ {:controller => :orders, :action => :show } ]
|
27
|
+
states << {:controller => :orders, :action => :destroy} if can_cancel?
|
28
|
+
states << {:controller => :orders, :action => :pay, :id => id} if can_pay?
|
29
|
+
states << {:controller => :payments, :action => :show, :payment_id => payment.id } if paied?
|
30
|
+
states
|
47
31
|
end
|
48
|
-
|
49
|
-
collection.link("create", orders_url)
|
50
|
-
collection.members
|
51
|
-
end
|
52
32
|
</pre>
|
53
33
|
|
54
|
-
|
55
|
-
|
56
|
-
h2. Simple client example
|
34
|
+
h2. Installing
|
57
35
|
|
58
|
-
|
36
|
+
Just add in your environment.rb the following line:
|
59
37
|
|
60
38
|
<pre>
|
61
|
-
|
62
|
-
order.items.each { |item| puts items }
|
63
|
-
receipt = order.payment.post! { :amount => 500 }
|
64
|
-
puts receipt.id
|
39
|
+
config.gem "restfulie", :source => "http://gemcutter.org"
|
65
40
|
</pre>
|
66
41
|
|
67
|
-
|
42
|
+
Execute:
|
43
|
+
<pre>rake gems:install</pre>
|
68
44
|
|
69
|
-
|
45
|
+
or, if you prefer to install as a plugin:
|
70
46
|
|
71
|
-
|
47
|
+
script/plugin install git://github.com/caelum/restfulie.git
|
72
48
|
|
73
|
-
|
49
|
+
h2. Typical Restful Example
|
74
50
|
|
75
|
-
|
51
|
+
Trying to follow the definition of a restful application supporting resources with hypermedia content, a typical restful resource would be:
|
76
52
|
|
77
|
-
|
53
|
+
<pre>
|
54
|
+
<order>
|
55
|
+
<product>basic rails course</product>
|
56
|
+
<product>restful training</product>
|
57
|
+
<atom:link rel="refresh" href="http://www.caelum.com.br/orders/1" xmlns:atom="http://www.w3.org/2005/Atom"/>
|
58
|
+
<atom:link rel="update" href="http://www.caelum.com.br/orders/1" xmlns:atom="http://www.w3.org/2005/Atom"/>
|
59
|
+
<atom:link rel="pay" href="http://www.caelum.com.br/orders/1" xmlns:atom="http://www.w3.org/2005/Atom"/>
|
60
|
+
<atom:link rel="destroy" href="http://www.caelum.com.br/orders/1" xmlns:atom="http://www.w3.org/2005/Atom"/>
|
61
|
+
</order>
|
62
|
+
</pre>
|
78
63
|
|
79
|
-
|
80
|
-
* "Official website":http://restfulie.caelumobjects.com
|
81
|
-
* "How-tos":http://restfulie.caelumobjects.com/caelumobjects-restful-rails.pdf
|
82
|
-
* "Buying through Rest: Rest to the enterprise (video)":http://guilhermesilveira.wordpress.com/2010/04/13/buying-through-rest-applying-rest-to-the-enterprise/
|
64
|
+
h2. Client Usage
|
83
65
|
|
84
|
-
|
66
|
+
One should first acquire the representation from the server through your common GET process and process it through the usual from_* methods:
|
67
|
+
<pre>xml = Net::HTTP.get(URI.parse('http://www.caelum.com.br/orders/1'))
|
68
|
+
order = Order.from_xml(xml)</pre>
|
85
69
|
|
86
|
-
|
70
|
+
And now you can invoke all those actions in order to change your resource's state:
|
87
71
|
|
88
72
|
<pre>
|
89
|
-
|
73
|
+
order.refresh
|
74
|
+
order.update
|
75
|
+
order.destroy
|
76
|
+
order.pay(payment)
|
90
77
|
</pre>
|
91
78
|
|
92
|
-
|
79
|
+
Note that:
|
80
|
+
* refresh is get
|
81
|
+
* update is put (and you have to put everything back)
|
82
|
+
* destroy is delete
|
83
|
+
* pay (unknown methods) is post
|
84
|
+
|
85
|
+
h2. Resource format support
|
86
|
+
|
87
|
+
Restfulie currently supports xml+atom and will soon expand its support to xml+rel links and json+links supports. Those new formats will also support automatic http verb detection - if possible and reasonable.
|
88
|
+
|
89
|
+
|
90
|
+
h2. Help
|
91
|
+
|
92
|
+
If you are looking for or want to help, let us know at the mailing list: http://groups.google.com/group/restfulie
|
93
|
+
|
94
|
+
h2. How to customize your request
|
95
|
+
|
96
|
+
By default, restfulie uses the following table:
|
93
97
|
|
94
|
-
|
98
|
+
* destroy, cancel and delete send a DELETE request
|
99
|
+
* update sends a POST request
|
100
|
+
* refresh, reload, show sends a GET request
|
101
|
+
* other methods sends a POST request
|
95
102
|
|
96
|
-
If you want to
|
97
|
-
"Bundler":http://gembundler.com/ is required to easily manage dependencies
|
103
|
+
If you want to use a custom http verb in order to send your request, you can do it by setting the optional string 'method':
|
98
104
|
|
99
105
|
<pre>
|
100
|
-
|
101
|
-
rake test:spec test:integration
|
106
|
+
order.update(:method=>"post")
|
102
107
|
</pre>
|
103
108
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
George Guimaraes, abril and plataforma
|
123
|
-
Fabio Akita
|
124
|
-
Diego Carrion
|
125
|
-
Leandro Silva
|
126
|
-
Gavin-John Noonan
|
127
|
-
Antonio Marques
|
128
|
-
Luis Cipriani, abril
|
129
|
-
Everton Ribeiro, abril
|
130
|
-
Paulo Ahagon, abril
|
131
|
-
Elomar França
|
132
|
-
Thomas Stefano
|
133
|
-
"David Paniz":"http://www.caelum.com.br"
|
134
|
-
"Caike Souza":"http://www.caikesouza.com/blog"
|
135
|
-
|
136
|
-
h2. Rails 2
|
137
|
-
|
138
|
-
If you want to use Restfulie with Rails2, please use any release up to 0.9.2 and its minor releases.
|
109
|
+
h2. License
|
110
|
+
|
111
|
+
/***
|
112
|
+
* Copyright (c) 2009 Caelum - www.caelum.com.br/opensource
|
113
|
+
* All rights reserved.
|
114
|
+
*
|
115
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
116
|
+
* you may not use this file except in compliance with the License.
|
117
|
+
* You may obtain a copy of the License at
|
118
|
+
*
|
119
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
120
|
+
*
|
121
|
+
* Unless required by applicable law or agreed to in writing, software
|
122
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
123
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
124
|
+
* See the License for the specific language governing permissions and
|
125
|
+
* limitations under the License.
|
126
|
+
*/
|
data/Rakefile
CHANGED
@@ -2,18 +2,14 @@ require 'rubygems'
|
|
2
2
|
require 'rubygems/specification'
|
3
3
|
require 'rake'
|
4
4
|
require 'rake/gempackagetask'
|
5
|
-
require 'rake/
|
6
|
-
require 'rspec'
|
7
|
-
require 'rspec/core'
|
8
|
-
require 'rspec/core/rake_task'
|
9
|
-
require File.expand_path('lib/restfulie')
|
5
|
+
require 'spec/rake/spectask'
|
10
6
|
|
11
7
|
GEM = "restfulie"
|
12
|
-
GEM_VERSION =
|
13
|
-
SUMMARY
|
14
|
-
AUTHOR
|
15
|
-
EMAIL
|
16
|
-
HOMEPAGE = "http://
|
8
|
+
GEM_VERSION = "0.1"
|
9
|
+
SUMMARY = "This is a small cute plugin to show how to implement hypermedia based services in a easy way using rails."
|
10
|
+
AUTHOR = "Guilherme Silveira, Caue Guerra"
|
11
|
+
EMAIL = "guilherme.silveira@caelum.com.br"
|
12
|
+
HOMEPAGE = "http://github.com/caelum/restfulie"
|
17
13
|
|
18
14
|
spec = Gem::Specification.new do |s|
|
19
15
|
s.name = GEM
|
@@ -21,114 +17,27 @@ spec = Gem::Specification.new do |s|
|
|
21
17
|
s.platform = Gem::Platform::RUBY
|
22
18
|
s.summary = SUMMARY
|
23
19
|
s.require_paths = ['lib']
|
24
|
-
s.files = FileList['lib/**/*.rb', '[A-Z]*'
|
25
|
-
|
26
|
-
s.add_dependency(
|
27
|
-
s.add_dependency("activesupport", [">= 2.3.2"])
|
28
|
-
s.add_dependency("json_pure", [">= 1.2.4"])
|
20
|
+
s.files = FileList['lib/**/*.rb', '[A-Z]*'].to_a
|
21
|
+
|
22
|
+
# s.add_dependency(%q<rubigen>, [">= 1.3.4"])
|
29
23
|
|
30
24
|
s.author = AUTHOR
|
31
25
|
s.email = EMAIL
|
32
26
|
s.homepage = HOMEPAGE
|
33
27
|
end
|
34
28
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
begin
|
39
|
-
Net::HTTP.get(URI.parse("http://localhost:#{port}/"))
|
40
|
-
return
|
41
|
-
rescue
|
42
|
-
sleep 1
|
43
|
-
end
|
44
|
-
end
|
45
|
-
raise "Waited for the server but it did not finish"
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.start_sinatra
|
49
|
-
IO.popen("cd tests && ruby ./spec/requests/fake_server.rb") do |pipe|
|
50
|
-
wait_server 4567
|
51
|
-
yield
|
52
|
-
Process.kill 'INT', pipe.pid
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def self.run(setup, process)
|
57
|
-
success = IO.popen(setup) do |pipe|
|
58
|
-
wait_server
|
59
|
-
success = system "rake spec"
|
60
|
-
Process.kill 'INT', pipe.pid
|
61
|
-
success
|
62
|
-
end
|
63
|
-
if !success
|
64
|
-
raise "Some of the specs failed"
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def self.start_server_and_run_spec(target_dir)
|
69
|
-
success = Dir.chdir(File.join(File.dirname(__FILE__), target_dir)) do
|
70
|
-
system('rake db:drop db:create db:migrate')
|
71
|
-
self.run "rails server", "rake spec"
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
end
|
76
|
-
|
77
|
-
# optionally loads a task if the required gems exist
|
78
|
-
def optionally
|
79
|
-
begin
|
80
|
-
yield
|
81
|
-
rescue LoadError; end
|
82
|
-
end
|
83
|
-
|
84
|
-
namespace :test do
|
85
|
-
|
86
|
-
task :spec do
|
87
|
-
FakeServer.start_sinatra do
|
88
|
-
FakeServer.start_server_and_run_spec "tests"
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
task :integration do
|
93
|
-
FakeServer.start_server_and_run_spec "full-examples/rest_from_scratch/part_1"
|
94
|
-
FakeServer.start_server_and_run_spec "full-examples/rest_from_scratch/part_2"
|
95
|
-
FakeServer.start_server_and_run_spec "full-examples/rest_from_scratch/part_3"
|
96
|
-
end
|
97
|
-
|
98
|
-
task :sinatra do
|
99
|
-
FakeServer.start_sinatra do
|
100
|
-
puts "Press something to quit"
|
101
|
-
gets
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
task :all => ["spec","integration"]
|
106
|
-
|
107
|
-
end
|
108
|
-
|
109
|
-
RSpec::Core::RakeTask.new(:spec) do |t|
|
110
|
-
# t.spec_files = FileList['spec_*.rb']
|
111
|
-
t.spec_opts = ['--colour', '--format progress']
|
29
|
+
Spec::Rake::SpecTask.new do |t|
|
30
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
31
|
+
t.spec_opts = %w(-fs -fh:doc/specs.html --color)
|
112
32
|
end
|
113
33
|
|
114
34
|
Rake::GemPackageTask.new(spec) do |pkg|
|
115
35
|
pkg.gem_spec = spec
|
116
36
|
end
|
117
37
|
|
118
|
-
Rake::RDocTask.new("rdoc") do |rdoc|
|
119
|
-
rdoc.options << '--line-numbers' << '--inline-source'
|
120
|
-
end
|
121
|
-
|
122
|
-
optionally do
|
123
|
-
require 'yard'
|
124
|
-
YARD::Rake::YardocTask.new do |t|
|
125
|
-
t.files = ['lib/restfulie/**/*.rb', 'README.textile']
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
38
|
desc "Install the gem locally"
|
130
39
|
task :install => [:package] do
|
131
|
-
sh %{gem install pkg/#{GEM}-#{GEM_VERSION}
|
40
|
+
sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
|
132
41
|
end
|
133
42
|
|
134
43
|
desc "Create a gemspec file"
|
@@ -139,8 +48,4 @@ task :make_spec do
|
|
139
48
|
end
|
140
49
|
|
141
50
|
desc "Builds the project"
|
142
|
-
task :build =>
|
143
|
-
|
144
|
-
desc "Default build will run specs"
|
145
|
-
task :default => :build
|
146
|
-
|
51
|
+
task :build => :spec
|
data/lib/restfulie.rb
CHANGED
@@ -1,34 +1,194 @@
|
|
1
|
-
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
2
3
|
|
3
|
-
require 'restfulie/version'
|
4
|
-
require 'restfulie/common'
|
5
|
-
require 'restfulie/client'
|
6
|
-
require 'restfulie/server'
|
7
|
-
|
8
|
-
# Shortcut to RestfulieDsl
|
9
4
|
module Restfulie
|
5
|
+
def to_json
|
6
|
+
super :methods => :following_states
|
7
|
+
end
|
10
8
|
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
def to_xml(options = {})
|
10
|
+
controller = options[:controller]
|
11
|
+
return super if controller.nil?
|
12
|
+
|
13
|
+
options[:skip_types] = true
|
14
|
+
super options do |xml|
|
15
|
+
if respond_to?(:following_states)
|
16
|
+
states = following_states
|
17
|
+
states = [states] if states.class.to_s != 'Array'
|
18
|
+
states.each do |action|
|
19
|
+
rel = action[:action]
|
20
|
+
if action[:rel]
|
21
|
+
rel = action[:rel]
|
22
|
+
action[:rel] = nil
|
23
|
+
end
|
24
|
+
translate_href = controller.url_for(action)
|
25
|
+
if options[:use_name_based_link]
|
26
|
+
xml.tag!(rel, translate_href)
|
27
|
+
else
|
28
|
+
xml.tag!('atom:link', 'xmlns:atom' => 'http://www.w3.org/2005/Atom', :rel => rel, :href => translate_href)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
14
33
|
end
|
15
34
|
|
16
|
-
def
|
17
|
-
|
35
|
+
def create_method(name, &block)
|
36
|
+
self.class.send(:define_method, name, &block)
|
18
37
|
end
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
module ActiveRecord
|
42
|
+
class Base
|
43
|
+
|
44
|
+
include Restfulie
|
45
|
+
attr_accessor :_possible_states
|
46
|
+
attr_accessor :_came_from
|
47
|
+
|
48
|
+
def self.add_states(result, states)
|
49
|
+
result._possible_states = {}
|
50
|
+
states.each do |state|
|
51
|
+
result._possible_states[state["rel"]] = state
|
52
|
+
end
|
53
|
+
def result.respond_to?(sym)
|
54
|
+
has_state(sym.to_s) || super(sym)
|
55
|
+
end
|
56
|
+
|
57
|
+
def result.has_state(name)
|
58
|
+
!@_possible_states[name].nil?
|
59
|
+
end
|
60
|
+
|
61
|
+
states.each do |state|
|
62
|
+
name = state["rel"]
|
63
|
+
self.module_eval do
|
64
|
+
def current_method
|
65
|
+
caller[0]=~/`(.*?)'/
|
66
|
+
$1
|
67
|
+
end
|
68
|
+
def temp_method(options = {}, &block)
|
69
|
+
name = current_method
|
70
|
+
state = _possible_states[name]
|
71
|
+
data = options[:data] || {}
|
72
|
+
url = URI.parse(state["href"])
|
73
|
+
get = false
|
74
|
+
|
75
|
+
# gs: i dont know how to meta play here! i suck
|
76
|
+
if options[:method]=="delete"
|
77
|
+
req = Net::HTTP::Delete.new(url.path)
|
78
|
+
elsif options[:method]=="put"
|
79
|
+
req = Net::HTTP::Put.new(url.path)
|
80
|
+
elsif options[:method]=="get"
|
81
|
+
req = Net::HTTP::Get.new(url.path)
|
82
|
+
get = true
|
83
|
+
elsif options[:method]=="post"
|
84
|
+
req = Net::HTTP::Post.new(url.path)
|
85
|
+
elsif ['destroy','delete','cancel'].include? name
|
86
|
+
req = Net::HTTP::Delete.new(url.path)
|
87
|
+
elsif ['refresh', 'reload', 'show', 'latest'].include? name
|
88
|
+
req = Net::HTTP::Get.new(url.path)
|
89
|
+
get = true
|
90
|
+
else
|
91
|
+
req = Net::HTTP::Post.new(url.path)
|
92
|
+
end
|
93
|
+
|
94
|
+
req.set_form_data(data)
|
95
|
+
req.add_field("Accept", "text/xml") if _came_from == :xml
|
96
|
+
|
97
|
+
http = Net::HTTP.new(url.host, url.port)
|
98
|
+
response = http.request(req)
|
99
|
+
return yield(response) if !block.nil?
|
100
|
+
if get
|
101
|
+
case response.content_type
|
102
|
+
when "application/xml"
|
103
|
+
content = response.body
|
104
|
+
hash = Hash.from_xml content
|
105
|
+
return hash if hash.keys.length == 0
|
106
|
+
raise "unable to parse an xml with more than one root element" if hash.keys.length>1
|
107
|
+
key = hash.keys[0]
|
108
|
+
type = key.camelize.constantize
|
109
|
+
return type.from_xml content
|
110
|
+
else
|
111
|
+
raise :unknown_content_type
|
112
|
+
end
|
113
|
+
end
|
114
|
+
response
|
115
|
+
|
116
|
+
end
|
117
|
+
alias_method name, :temp_method
|
118
|
+
undef :temp_method
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
result
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.from_web(uri)
|
126
|
+
url = URI.parse(uri)
|
127
|
+
req = Net::HTTP::Get.new(url.path)
|
128
|
+
http = Net::HTTP.new(url.host, url.port)
|
129
|
+
res = http.request(req)
|
130
|
+
raise :invalid_request, res if res.code != "200"
|
131
|
+
case res.content_type
|
132
|
+
when "application/xml"
|
133
|
+
self.from_xml res.body
|
134
|
+
when "application/json"
|
135
|
+
self.from_json res.body
|
136
|
+
else
|
137
|
+
raise :unknown_content_type
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# basic code from Matt Pulver
|
142
|
+
# found at http://www.xcombinator.com/2008/08/11/activerecord-from_xml-and-from_json-part-2/
|
143
|
+
# addapted to support links
|
144
|
+
def self.from_hash( hash )
|
145
|
+
h = {}
|
146
|
+
h = hash.dup if hash
|
147
|
+
links = nil
|
148
|
+
h.each do |key,value|
|
149
|
+
case value.class.to_s
|
150
|
+
when 'Array'
|
151
|
+
if key=="link"
|
152
|
+
links = h[key]
|
153
|
+
h.delete("link")
|
154
|
+
else
|
155
|
+
h[key].map! { |e| reflect_on_association(key.to_sym ).klass.from_hash e }
|
156
|
+
end
|
157
|
+
when /\AHash(WithIndifferentAccess)?\Z/
|
158
|
+
if key=="link"
|
159
|
+
links = [h[key]]
|
160
|
+
h.delete("link")
|
161
|
+
else
|
162
|
+
h[key] = reflect_on_association(key.to_sym ).klass.from_hash value
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
result = self.new h
|
167
|
+
add_states(result, links) unless links.nil?
|
168
|
+
result
|
169
|
+
end
|
170
|
+
|
171
|
+
def self.from_json( json )
|
172
|
+
from_hash safe_json_decode( json )
|
173
|
+
end
|
174
|
+
|
175
|
+
# The xml has a surrounding class tag (e.g. ship-to),
|
176
|
+
# but the hash has no counterpart (e.g. 'ship_to' => {} )
|
177
|
+
def self.from_xml( xml )
|
178
|
+
hash = Hash.from_xml xml
|
179
|
+
head = hash[self.to_s.underscore]
|
180
|
+
result = self.from_hash head
|
181
|
+
return nil if result.nil?
|
182
|
+
result._came_from = :xml
|
183
|
+
result
|
25
184
|
end
|
26
185
|
end
|
27
|
-
|
28
186
|
end
|
29
187
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
188
|
+
def safe_json_decode( json )
|
189
|
+
return {} if !json
|
190
|
+
begin
|
191
|
+
ActiveSupport::JSON.decode json
|
192
|
+
rescue ; {} end
|
34
193
|
end
|
194
|
+
# end of code based on Matt Pulver's
|