almodovar 0.5.6 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +4 -4
- data/lib/almodovar.rb +15 -181
- data/lib/almodovar/digest_auth.rb +4 -0
- data/lib/almodovar/http_accessor.rb +26 -0
- data/lib/almodovar/resource.rb +56 -0
- data/lib/almodovar/resource_collection.rb +31 -0
- data/lib/almodovar/resource_presenter.rb +110 -0
- data/lib/almodovar/resource_presenter/collection.rb +24 -0
- data/lib/almodovar/resource_presenter/link.rb +103 -0
- data/lib/almodovar/single_resource.rb +79 -0
- data/lib/almodovar/to_xml.rb +21 -0
- metadata +47 -91
- data/lib/to_xml.rb +0 -24
- data/vendor/resourceful-0.5.3-patched/MIT-LICENSE +0 -21
- data/vendor/resourceful-0.5.3-patched/Manifest +0 -29
- data/vendor/resourceful-0.5.3-patched/README.markdown +0 -84
- data/vendor/resourceful-0.5.3-patched/Rakefile +0 -71
- data/vendor/resourceful-0.5.3-patched/lib/resourceful.rb +0 -18
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/authentication_manager.rb +0 -108
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/cache_manager.rb +0 -240
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/exceptions.rb +0 -34
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/header.rb +0 -126
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/http_accessor.rb +0 -98
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/memcache_cache_manager.rb +0 -75
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/net_http_adapter.rb +0 -70
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/options_interpreter.rb +0 -78
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/request.rb +0 -230
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/resource.rb +0 -165
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/response.rb +0 -221
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/stubbed_resource_proxy.rb +0 -47
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/util.rb +0 -6
- data/vendor/resourceful-0.5.3-patched/resourceful.gemspec +0 -48
- data/vendor/resourceful-0.5.3-patched/spec/acceptance/authorization_spec.rb +0 -16
- data/vendor/resourceful-0.5.3-patched/spec/acceptance/caching_spec.rb +0 -192
- data/vendor/resourceful-0.5.3-patched/spec/acceptance/header_spec.rb +0 -24
- data/vendor/resourceful-0.5.3-patched/spec/acceptance/redirecting_spec.rb +0 -12
- data/vendor/resourceful-0.5.3-patched/spec/acceptance/resource_spec.rb +0 -84
- data/vendor/resourceful-0.5.3-patched/spec/acceptance_shared_specs.rb +0 -44
- data/vendor/resourceful-0.5.3-patched/spec/old_acceptance_specs.rb +0 -378
- data/vendor/resourceful-0.5.3-patched/spec/simple_sinatra_server.rb +0 -74
- data/vendor/resourceful-0.5.3-patched/spec/simple_sinatra_server_spec.rb +0 -98
- data/vendor/resourceful-0.5.3-patched/spec/spec.opts +0 -3
- data/vendor/resourceful-0.5.3-patched/spec/spec_helper.rb +0 -28
data/README.rdoc
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
=Almodovar
|
1
|
+
=Almodovar {<img src="http://travis-ci.org/bebanjo/almodovar.png" />}[http://travis-ci.org/bebanjo/almodovar]
|
2
2
|
|
3
3
|
Almodovar is a client for BeBanjo's Sequence & Movida API written in Ruby (it's actually a generic client which plays nice with any RESTful API following some conventions).
|
4
4
|
|
@@ -85,7 +85,7 @@ Resource collections have the _create_ method. Just call it with the attributes
|
|
85
85
|
|
86
86
|
>> jobs = Almodovar::Resource("http://sequence.example.com/api/work_areas/52/jobs", auth)
|
87
87
|
=> [<job> ... </job>, <job> ... </job>]
|
88
|
-
>> job = jobs.create(:name => "Wadus")
|
88
|
+
>> job = jobs.create(:job => {:name => "Wadus"})
|
89
89
|
=> <job> ... </job>
|
90
90
|
>> job.name
|
91
91
|
=> "Wadus"
|
@@ -96,7 +96,7 @@ You can use the _update_ method:
|
|
96
96
|
|
97
97
|
>> job = Almodovar::Resource("http://sequence.example.com/api/work_areas/52/jobs", auth).first
|
98
98
|
=> <job> ... </job>
|
99
|
-
>> job.update(:name => "Wadus wadus")
|
99
|
+
>> job.update(:job => {:name => "Wadus wadus"})
|
100
100
|
=> ...
|
101
101
|
>> job.name
|
102
102
|
=> "Wadus wadus"
|
@@ -111,8 +111,8 @@ And exactly the same with the _delete_ method:
|
|
111
111
|
|
112
112
|
==To-do
|
113
113
|
|
114
|
-
* See what is preventing latest version of Resourceful from working and fix it (Now we're using a vendorized, patched version based on resourceful 0.5.3)
|
115
114
|
* Better error management
|
116
115
|
* Write the conventions Almodovar expects in an API
|
116
|
+
* Other authentication methods than digest
|
117
117
|
|
118
118
|
Copyright (c) 2010 BeBanjo S.L., released under the MIT license
|
data/lib/almodovar.rb
CHANGED
@@ -1,183 +1,17 @@
|
|
1
|
-
require
|
1
|
+
require 'patron'
|
2
2
|
require 'nokogiri'
|
3
|
-
|
4
|
-
require '
|
5
|
-
|
3
|
+
begin
|
4
|
+
require 'active_support/all'
|
5
|
+
rescue LoadError
|
6
|
+
require 'active_support'
|
7
|
+
end
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
response = http.resource(url_with_params).get
|
17
|
-
Nokogiri.parse(response.body).root
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def url_with_params
|
24
|
-
@options[:expand] = @options[:expand].join(",") if @options[:expand].is_a?(Array)
|
25
|
-
params = @options.map { |k, v| "#{k}=#{v}" }.join("&")
|
26
|
-
params = "?#{params}" unless params.empty?
|
27
|
-
@url + params
|
28
|
-
end
|
29
|
-
|
30
|
-
def http
|
31
|
-
Resourceful::HttpAccessor.new(:authenticator => @auth)
|
32
|
-
end
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
class SingleResource
|
37
|
-
|
38
|
-
include HttpAccessor
|
39
|
-
|
40
|
-
undef id, type
|
41
|
-
|
42
|
-
def initialize(url, auth, xml = nil, options = {})
|
43
|
-
@url = url
|
44
|
-
@auth = auth
|
45
|
-
@xml = xml
|
46
|
-
@options = options
|
47
|
-
end
|
48
|
-
|
49
|
-
def update(attrs = {})
|
50
|
-
response = http.resource(@url).put(attrs.to_xml(:root => object_type), :content_type => "application/xml")
|
51
|
-
@xml = Nokogiri.parse(response.body).root
|
52
|
-
end
|
53
|
-
|
54
|
-
def delete
|
55
|
-
http.resource(@url).delete
|
56
|
-
end
|
57
|
-
|
58
|
-
def url
|
59
|
-
@url ||= xml.at_xpath("./link[@rel='self']").try(:[], "href")
|
60
|
-
end
|
61
|
-
|
62
|
-
delegate :to_xml, :to => :xml
|
63
|
-
alias_method :inspect, :to_xml
|
64
|
-
|
65
|
-
private
|
66
|
-
|
67
|
-
def object_type
|
68
|
-
uri_parts = URI.parse(@url).path.split("/")
|
69
|
-
@object_type ||= (uri_parts.last =~ /^\d$/ ? uri_parts[-2].singularize : uri_parts.last.singularize)
|
70
|
-
end
|
71
|
-
|
72
|
-
def [](key) # for resources with type "document"
|
73
|
-
return super unless xml.at_xpath("/*[@type='document']")
|
74
|
-
Hash.from_xml(xml.to_xml).values.first[key]
|
75
|
-
end
|
76
|
-
|
77
|
-
def method_missing(meth, *args, &blk)
|
78
|
-
document = xml.at_xpath("./*[(name()='#{meth}' or name()='#{attribute_name(meth)}') and @type='document']")
|
79
|
-
return Resource.from_xml(document.to_xml) if document
|
80
|
-
|
81
|
-
attribute = xml.at_xpath("./*[name()='#{meth}' or name()='#{attribute_name(meth)}']")
|
82
|
-
return node_text(attribute) if attribute
|
83
|
-
|
84
|
-
link = xml.at_xpath("./link[@rel='#{meth}' or @rel='#{attribute_name(meth)}']")
|
85
|
-
return Resource.new(link["href"], @auth, link.at_xpath("./*"), *args) if link
|
86
|
-
|
87
|
-
super
|
88
|
-
end
|
89
|
-
|
90
|
-
def node_text(node)
|
91
|
-
case node['type']
|
92
|
-
when "integer": node.text.to_i
|
93
|
-
when "datetime": Time.parse(node.text)
|
94
|
-
else
|
95
|
-
node.text
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
def attribute_name(attribute)
|
100
|
-
attribute.to_s.gsub('_', '-')
|
101
|
-
end
|
102
|
-
|
103
|
-
end
|
104
|
-
|
105
|
-
class ResourceCollection
|
106
|
-
|
107
|
-
include HttpAccessor
|
108
|
-
|
109
|
-
delegate :inspect, :to => :resources
|
110
|
-
|
111
|
-
def initialize(url, auth, xml = nil, options = {})
|
112
|
-
@url = url
|
113
|
-
@auth = auth
|
114
|
-
@xml = xml if options.empty?
|
115
|
-
@options = options
|
116
|
-
end
|
117
|
-
|
118
|
-
def create(attrs = {})
|
119
|
-
response = http.resource(url_with_params).post(attrs.to_xml(:root => object_type, :convert_links => true), :content_type => "application/xml")
|
120
|
-
Resource.new(nil, @auth, Nokogiri.parse(response.body).root)
|
121
|
-
end
|
122
|
-
|
123
|
-
private
|
124
|
-
|
125
|
-
def object_type
|
126
|
-
@object_type ||= URI.parse(@url).path.split("/").last.singularize
|
127
|
-
end
|
128
|
-
|
129
|
-
def resources
|
130
|
-
@resources ||= xml.xpath("./*").map { |subnode| Resource.new(subnode.at_xpath("./link[@rel='self']").try(:[], "href"), @auth, subnode, @options) }
|
131
|
-
end
|
132
|
-
|
133
|
-
def method_missing(meth, *args, &blk)
|
134
|
-
resources.send(meth, *args, &blk)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
class Resource
|
139
|
-
|
140
|
-
include HttpAccessor
|
141
|
-
|
142
|
-
undef id, type
|
143
|
-
|
144
|
-
delegate :inspect, :to => :get!
|
145
|
-
|
146
|
-
def self.from_xml(xml, auth = nil)
|
147
|
-
new(nil, auth, Nokogiri.parse(xml).root)
|
148
|
-
end
|
149
|
-
|
150
|
-
def initialize(url, auth, xml = nil, options = {})
|
151
|
-
@url = url
|
152
|
-
@auth = auth
|
153
|
-
@xml = xml
|
154
|
-
@options = options
|
155
|
-
end
|
156
|
-
|
157
|
-
def method_missing(meth, *args, &blk)
|
158
|
-
@resource_object ||= resource_class(meth).new(@url, @auth, @xml, @options)
|
159
|
-
@resource_object.send(meth, *args, &blk)
|
160
|
-
end
|
161
|
-
|
162
|
-
def resource_class(meth, *args)
|
163
|
-
@resource_class ||= collection_call?(meth, *args) ? ResourceCollection : SingleResource
|
164
|
-
end
|
165
|
-
|
166
|
-
def get!
|
167
|
-
klass = xml['type'] == 'array' ? ResourceCollection : SingleResource
|
168
|
-
@resource_object = klass.new(@url, @auth, @xml, @options)
|
169
|
-
end
|
170
|
-
|
171
|
-
private
|
172
|
-
|
173
|
-
def collection_call?(meth, *args)
|
174
|
-
(Array.instance_methods + ["create"] - ["delete", "id", "[]"]).include?(meth.to_s) ||
|
175
|
-
(meth.to_s == "[]" && args.size == 1 && args.first.is_a?(Fixnum))
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
def self.Resource(url, auth, params = {})
|
180
|
-
Resource.new(url, auth, nil, params)
|
181
|
-
end
|
182
|
-
|
183
|
-
end
|
9
|
+
require 'almodovar/digest_auth'
|
10
|
+
require 'almodovar/http_accessor'
|
11
|
+
require 'almodovar/resource'
|
12
|
+
require 'almodovar/resource_collection'
|
13
|
+
require 'almodovar/single_resource'
|
14
|
+
require 'almodovar/to_xml'
|
15
|
+
require 'almodovar/resource_presenter'
|
16
|
+
require 'almodovar/resource_presenter/collection'
|
17
|
+
require 'almodovar/resource_presenter/link'
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Almodovar
|
2
|
+
module HttpAccessor
|
3
|
+
def xml
|
4
|
+
@xml ||= begin
|
5
|
+
response = http.get(url_with_params)
|
6
|
+
Nokogiri::XML.parse(response.body).root
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def url_with_params
|
11
|
+
@options[:expand] = @options[:expand].join(",") if @options[:expand].is_a?(Array)
|
12
|
+
params = @options.map { |k, v| "#{k}=#{v}" }.join("&")
|
13
|
+
params = "?#{params}" unless params.empty?
|
14
|
+
@url + params
|
15
|
+
end
|
16
|
+
|
17
|
+
def http
|
18
|
+
@http ||= Patron::Session.new.tap do |session|
|
19
|
+
session.username = @auth.username
|
20
|
+
session.password = @auth.password
|
21
|
+
session.auth_type = :digest
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Almodovar
|
2
|
+
class Resource
|
3
|
+
include HttpAccessor
|
4
|
+
|
5
|
+
undef_method :id if instance_methods.include?("id")
|
6
|
+
undef_method :type if instance_methods.include?("type")
|
7
|
+
|
8
|
+
delegate :inspect, :to => :get!
|
9
|
+
|
10
|
+
def self.from_xml(xml, auth = nil)
|
11
|
+
new(nil, auth, Nokogiri::XML.parse(xml).root)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(url, auth, xml = nil, options = {})
|
15
|
+
@url = url
|
16
|
+
@auth = auth
|
17
|
+
@xml = xml
|
18
|
+
@options = options
|
19
|
+
end
|
20
|
+
|
21
|
+
def method_missing(meth, *args, &blk)
|
22
|
+
resource_object(meth).send(meth, *args, &blk)
|
23
|
+
end
|
24
|
+
|
25
|
+
def get!
|
26
|
+
klass = xml['type'] == 'array' ? ResourceCollection : SingleResource
|
27
|
+
@resource_object = klass.new(@url, @auth, @xml, @options)
|
28
|
+
end
|
29
|
+
|
30
|
+
def respond_to?(meth)
|
31
|
+
super || resource_object(meth).respond_to?(meth)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def collection_call?(meth, *args)
|
37
|
+
([].respond_to?(meth) && !["delete", "id", "[]"].include?(meth.to_s)) ||
|
38
|
+
["create"].include?(meth.to_s) ||
|
39
|
+
(meth.to_s == "[]" && args.size == 1 && args.first.is_a?(Fixnum))
|
40
|
+
end
|
41
|
+
|
42
|
+
def resource_object(meth)
|
43
|
+
@resource_object ||= resource_class(meth).new(@url, @auth, @xml, @options)
|
44
|
+
end
|
45
|
+
|
46
|
+
def resource_class(meth, *args)
|
47
|
+
@resource_class ||= collection_call?(meth, *args) ? ResourceCollection : SingleResource
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.Resource(url, auth = nil, params = {})
|
53
|
+
Resource.new(url, auth, nil, params)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Almodovar
|
2
|
+
class ResourceCollection
|
3
|
+
include HttpAccessor
|
4
|
+
|
5
|
+
delegate :inspect, :to => :resources
|
6
|
+
|
7
|
+
def initialize(url, auth, xml = nil, options = {})
|
8
|
+
@url = url
|
9
|
+
@auth = auth
|
10
|
+
@xml = xml if options.empty?
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def create(attrs = {})
|
15
|
+
raise ArgumentError.new("You must specify one only root element which is the type of resource (e.g. `:project => { :name => 'Wadus' }` instead of just `:name => 'Wadus'`)") if attrs.size > 1
|
16
|
+
root, body = attrs.first
|
17
|
+
response = http.post(url_with_params, body.to_xml(:root => root, :convert_links => true), :content_type => "application/xml")
|
18
|
+
Resource.new(nil, @auth, Nokogiri::XML.parse(response.body).root)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def resources
|
24
|
+
@resources ||= xml.xpath("./*").map { |subnode| Resource.new(subnode.at_xpath("./link[@rel='self']").try(:[], "href"), @auth, subnode, @options) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def method_missing(meth, *args, &blk)
|
28
|
+
resources.send(meth, *args, &blk)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module Almodovar
|
2
|
+
|
3
|
+
class ResourcePresenter
|
4
|
+
|
5
|
+
attr_accessor :url
|
6
|
+
|
7
|
+
def attributes
|
8
|
+
@attributes ||= ActiveSupport::OrderedHash.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def links
|
12
|
+
@links ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.resource_type
|
16
|
+
name.gsub(/Resource$/, '').underscore
|
17
|
+
end
|
18
|
+
|
19
|
+
def resource_type
|
20
|
+
self.class.resource_type
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_xml(options = {})
|
24
|
+
XmlSerializer.new(self, options).to_xml
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_json(options = {})
|
28
|
+
JsonSerializer.new(self, options).to_json
|
29
|
+
end
|
30
|
+
|
31
|
+
def as_json(options = {})
|
32
|
+
JsonSerializer.new(self, options).as_json
|
33
|
+
end
|
34
|
+
|
35
|
+
def all_links
|
36
|
+
([link_to_self] + links).compact
|
37
|
+
end
|
38
|
+
|
39
|
+
def link_to_self
|
40
|
+
Link.new(:self, @url) if @url
|
41
|
+
end
|
42
|
+
|
43
|
+
class Serializer
|
44
|
+
|
45
|
+
attr_reader :resource, :options
|
46
|
+
|
47
|
+
def initialize(resource, options)
|
48
|
+
@resource = resource
|
49
|
+
@options = options
|
50
|
+
end
|
51
|
+
|
52
|
+
def options_for_link
|
53
|
+
options.merge(:dont_expand => Array(options[:dont_expand]) << resource.url)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
class XmlSerializer < Serializer
|
59
|
+
|
60
|
+
def to_xml
|
61
|
+
attributes_to_xml do |builder|
|
62
|
+
links_to_xml builder
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def attributes_to_xml(&block)
|
69
|
+
resource.attributes.to_xml(options.merge(:root => resource.resource_type), &block)
|
70
|
+
end
|
71
|
+
|
72
|
+
def links_to_xml(builder)
|
73
|
+
resource.all_links.each do |link|
|
74
|
+
link.to_xml(options_for_link.merge(:builder => builder))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
class JsonSerializer < Serializer
|
81
|
+
|
82
|
+
def to_json
|
83
|
+
require 'yajl'
|
84
|
+
Yajl::Encoder.encode(as_json, :pretty => true) + "\n"
|
85
|
+
end
|
86
|
+
|
87
|
+
def as_json
|
88
|
+
ActiveSupport::OrderedHash[:resource_type, resource.resource_type].tap do |message|
|
89
|
+
message.merge! attributes_as_json
|
90
|
+
message.merge! links_as_json
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def attributes_as_json
|
97
|
+
resource.attributes
|
98
|
+
end
|
99
|
+
|
100
|
+
def links_as_json
|
101
|
+
resource.all_links.inject(ActiveSupport::OrderedHash.new) do |message, link|
|
102
|
+
message.merge! link.as_json(options_for_link)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|