almodovar 0.1.2 → 0.2.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 +43 -4
- data/lib/almodovar.rb +121 -32
- metadata +18 -6
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
=Almodovar
|
2
2
|
|
3
|
-
Almodovar is a client for BeBanjo's Movida API written in Ruby
|
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
|
|
5
5
|
==Getting started
|
6
6
|
|
@@ -16,9 +16,9 @@ Now, let's play with irb:
|
|
16
16
|
First you need an authentication token:
|
17
17
|
|
18
18
|
>> auth = Almodovar::DigestAuth.new("realm", "user", "password")
|
19
|
-
=> #<Almodovar::DigestAuth:0x101f846c8
|
19
|
+
=> #<Almodovar::DigestAuth:0x101f846c8 ... >
|
20
20
|
|
21
|
-
Now you have to instantiate a resource given its URL. Let's try with the root of the
|
21
|
+
Now you have to instantiate a resource given its URL. Let's try with the root of the Movida API:
|
22
22
|
|
23
23
|
>> movida = Almodovar::Resource("http://movida.example.com/api", auth)
|
24
24
|
=> <movida>
|
@@ -67,13 +67,52 @@ Of course, once you've got the URL of a resource, the next time you don't need t
|
|
67
67
|
<link href="http://staging.schedule.bebanjo.net/api/title_groups/129" rel="title_group"/>
|
68
68
|
<link href="http://staging.schedule.bebanjo.net/api/titles/498" rel="title"/>
|
69
69
|
</scheduling>]
|
70
|
+
|
71
|
+
What if I want to access a specific node? Just do it:
|
72
|
+
|
73
|
+
>> schedulings.first.id
|
74
|
+
=> 112
|
75
|
+
>> schedulings.first.scheduling_type
|
76
|
+
=> "archive"
|
70
77
|
|
78
|
+
Note that fields with a hyphen are accessed with an underscore instead, otherwise ruby will think you are trying to substract ('-')
|
71
79
|
|
72
80
|
Next, explore the {API docs}[http://wiki.github.com/bebanjo/almodovar/] to learn about other resources.
|
81
|
+
|
82
|
+
==Creating resources
|
83
|
+
|
84
|
+
Resource collections have the _create_ method. Just call it with the attributes you want your new resource to have!
|
85
|
+
|
86
|
+
>> jobs = Almodovar::Resource("http://sequence.example.com/api/work_areas/52/jobs", auth)
|
87
|
+
=> [<job> ... </job>, <job> ... </job>]
|
88
|
+
>> job = jobs.create(:name => "Wadus")
|
89
|
+
=> <job> ... </job>
|
90
|
+
>> job.name
|
91
|
+
=> "Wadus"
|
73
92
|
|
93
|
+
==Modifying resources
|
94
|
+
|
95
|
+
You can use the _update_ method:
|
96
|
+
|
97
|
+
>> job = Almodovar::Resource("http://sequence.example.com/api/work_areas/52/jobs", auth).first
|
98
|
+
=> <job> ... </job>
|
99
|
+
>> job.update(:name => "Wadus wadus")
|
100
|
+
=> ...
|
101
|
+
>> job.name
|
102
|
+
=> "Wadus wadus"
|
103
|
+
|
104
|
+
== Deleting resources
|
105
|
+
|
106
|
+
And exactly the same with the _delete_ method:
|
107
|
+
|
108
|
+
>> job = Almodovar::Resource("http://sequence.example.com/api/work_areas/52/jobs", auth).first
|
109
|
+
=> <job> ... </job>
|
110
|
+
>> job.delete
|
111
|
+
|
74
112
|
==To-do
|
75
113
|
|
76
|
-
* Memoize linked resource calls so that the same request isn't made several times
|
77
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
|
+
* Better error management
|
116
|
+
* Write the conventions Almodovar expects in an API
|
78
117
|
|
79
118
|
Copyright (c) 2010 BeBanjo S.L., released under the MIT license
|
data/lib/almodovar.rb
CHANGED
@@ -1,41 +1,82 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../vendor/resourceful-0.5.3-patched/lib/resourceful'
|
2
2
|
require 'nokogiri'
|
3
|
+
require 'active_support'
|
4
|
+
require 'uri'
|
3
5
|
|
4
6
|
module Almodovar
|
5
7
|
|
6
8
|
class DigestAuth < Resourceful::DigestAuthenticator
|
7
9
|
end
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
+
module HttpAccessor
|
12
|
+
|
13
|
+
def xml
|
14
|
+
@xml ||= begin
|
15
|
+
response = http.resource(url_with_params).get
|
16
|
+
Nokogiri.parse(response.body).root
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def url_with_params
|
23
|
+
@options[:expand] = @options[:expand].join(",") if @options[:expand].is_a?(Array)
|
24
|
+
params = @options.map { |k, v| "#{k}=#{v}" }.join("&")
|
25
|
+
params = "?#{params}" unless params.empty?
|
26
|
+
@url + params
|
27
|
+
end
|
28
|
+
|
29
|
+
def http
|
30
|
+
Resourceful::HttpAccessor.new(:authenticator => @auth)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
class SingleResource
|
36
|
+
|
37
|
+
include HttpAccessor
|
38
|
+
|
39
|
+
undef id
|
40
|
+
|
41
|
+
def initialize(url, auth, xml = nil, options = {})
|
42
|
+
@url = url
|
11
43
|
@auth = auth
|
12
44
|
@xml = xml
|
45
|
+
@options = options
|
46
|
+
end
|
47
|
+
|
48
|
+
def update(attrs = {})
|
49
|
+
response = http.resource(@url).put(attrs.to_xml(:root => object_type), :content_type => "application/xml")
|
50
|
+
@xml = Nokogiri.parse(response.body).root
|
51
|
+
end
|
52
|
+
|
53
|
+
def delete
|
54
|
+
http.resource(@url).delete
|
13
55
|
end
|
14
56
|
|
15
|
-
def
|
16
|
-
@xml.
|
57
|
+
def url
|
58
|
+
@url ||= xml.at_xpath("./link[@rel='self']").try(:[], "href")
|
17
59
|
end
|
18
60
|
|
61
|
+
delegate :to_xml, :to => :xml
|
62
|
+
alias_method :inspect, :to_xml
|
63
|
+
|
19
64
|
private
|
20
65
|
|
21
|
-
|
66
|
+
def object_type
|
67
|
+
@object_type ||= URI.parse(@url).path.split("/")[-2].singularize
|
68
|
+
end
|
22
69
|
|
23
70
|
def method_missing(meth, *args, &blk)
|
24
|
-
attribute =
|
71
|
+
attribute = xml.at_xpath("./*[name()='#{meth}' or name()='#{attribute_name(meth)}']")
|
25
72
|
return node_text(attribute) if attribute
|
26
73
|
|
27
|
-
link =
|
28
|
-
return
|
74
|
+
link = xml.at_xpath("./link[@rel='#{meth}' or @rel='#{attribute_name(meth)}']")
|
75
|
+
return Resource.new(link["href"], @auth, link.at_xpath("./*"), *args) if link
|
29
76
|
|
30
77
|
super
|
31
78
|
end
|
32
79
|
|
33
|
-
def get_linked_resource(link, options = {})
|
34
|
-
options ||= {}
|
35
|
-
expansion = link.at_xpath("./*")
|
36
|
-
options.empty? && expansion ? Almodovar.instantiate(expansion, @auth) : Almodovar::Resource(link['href'], @auth, options)
|
37
|
-
end
|
38
|
-
|
39
80
|
def node_text(node)
|
40
81
|
case node['type']
|
41
82
|
when "integer": node.text.to_i
|
@@ -48,31 +89,79 @@ module Almodovar
|
|
48
89
|
def attribute_name(attribute)
|
49
90
|
attribute.to_s.gsub('_', '-')
|
50
91
|
end
|
92
|
+
|
51
93
|
end
|
52
94
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
95
|
+
class ResourceCollection
|
96
|
+
|
97
|
+
include HttpAccessor
|
98
|
+
|
99
|
+
delegate :inspect, :to => :resources
|
100
|
+
|
101
|
+
def initialize(url, auth, xml = nil, options = {})
|
102
|
+
@url = url
|
103
|
+
@auth = auth
|
104
|
+
@xml = xml if options.empty?
|
105
|
+
@options = options
|
106
|
+
end
|
107
|
+
|
108
|
+
def create(attrs = {})
|
109
|
+
response = http.resource(@url).post(attrs.to_xml(:root => object_type), :content_type => "application/xml")
|
110
|
+
Resource.new(nil, @auth, Nokogiri.parse(response.body).root)
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def object_type
|
116
|
+
@object_type ||= URI.parse(@url).path.split("/").last.singularize
|
117
|
+
end
|
118
|
+
|
119
|
+
def resources
|
120
|
+
@resources ||= xml.xpath("./*").map { |subnode| Resource.new(subnode.at_xpath("./link[@rel='self']").try(:[], "href"), @auth, subnode, @options) }
|
121
|
+
end
|
122
|
+
|
123
|
+
def method_missing(meth, *args, &blk)
|
124
|
+
resources.send(meth, *args, &blk)
|
64
125
|
end
|
65
126
|
end
|
66
127
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
128
|
+
class Resource
|
129
|
+
|
130
|
+
include HttpAccessor
|
131
|
+
|
132
|
+
undef id
|
133
|
+
|
134
|
+
delegate :inspect, :to => :get!
|
135
|
+
|
136
|
+
def initialize(url, auth, xml = nil, options = {})
|
137
|
+
@url = url
|
138
|
+
@auth = auth
|
139
|
+
@xml = xml
|
140
|
+
@options = options
|
141
|
+
end
|
142
|
+
|
143
|
+
def method_missing(meth, *args, &blk)
|
144
|
+
@resource_object ||= resource_class(meth).new(@url, @auth, @xml, @options)
|
145
|
+
@resource_object.send(meth, *args, &blk)
|
146
|
+
end
|
147
|
+
|
148
|
+
def resource_class(meth)
|
149
|
+
@resource_class ||= if [:create, :[], :first, :last, :size].include?(meth)
|
150
|
+
ResourceCollection
|
151
|
+
else
|
152
|
+
SingleResource
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def get!
|
157
|
+
klass = xml['type'] == 'array' ? ResourceCollection : SingleResource
|
158
|
+
@resource_object = klass.new(@url, @auth, @xml, @options)
|
159
|
+
end
|
160
|
+
|
72
161
|
end
|
73
162
|
|
74
|
-
def self.
|
75
|
-
|
163
|
+
def self.Resource(url, auth, params = {})
|
164
|
+
Resource.new(url, auth, nil, params)
|
76
165
|
end
|
77
166
|
|
78
167
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
- 1
|
8
7
|
- 2
|
9
|
-
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- BeBanjo S.L.
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-05-17 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -44,7 +44,7 @@ dependencies:
|
|
44
44
|
type: :runtime
|
45
45
|
version_requirements: *id002
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
|
-
name:
|
47
|
+
name: activesupport
|
48
48
|
prerelease: false
|
49
49
|
requirement: &id003 !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
@@ -53,10 +53,10 @@ dependencies:
|
|
53
53
|
segments:
|
54
54
|
- 0
|
55
55
|
version: "0"
|
56
|
-
type: :
|
56
|
+
type: :runtime
|
57
57
|
version_requirements: *id003
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
|
-
name:
|
59
|
+
name: steak
|
60
60
|
prerelease: false
|
61
61
|
requirement: &id004 !ruby/object:Gem::Requirement
|
62
62
|
requirements:
|
@@ -67,6 +67,18 @@ dependencies:
|
|
67
67
|
version: "0"
|
68
68
|
type: :development
|
69
69
|
version_requirements: *id004
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: webmock
|
72
|
+
prerelease: false
|
73
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
segments:
|
78
|
+
- 0
|
79
|
+
version: "0"
|
80
|
+
type: :development
|
81
|
+
version_requirements: *id005
|
70
82
|
description:
|
71
83
|
email: ballsbreaking@bebanjo.com
|
72
84
|
executables: []
|