almodovar 0.5.6 → 0.6.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/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
|