almodovar 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/README.rdoc +43 -4
  2. data/lib/almodovar.rb +121 -32
  3. 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 @password="secret", @username="robot_five", @realm="Staging Movida", @domain=nil>
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 api:
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
- class Resource
10
- def initialize(auth, xml)
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 inspect
16
- @xml.to_s
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
- undef id
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 = @xml.at_xpath("./*[name()='#{meth}' or name()='#{attribute_name(meth)}']")
71
+ attribute = xml.at_xpath("./*[name()='#{meth}' or name()='#{attribute_name(meth)}']")
25
72
  return node_text(attribute) if attribute
26
73
 
27
- link = @xml.at_xpath("./link[@rel='#{meth}' or @rel='#{attribute_name(meth)}']")
28
- return get_linked_resource(link, args.first) if link
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
- def self.Resource(url, auth, params = {})
54
- http = Resourceful::HttpAccessor.new(:authenticator => auth)
55
- response = http.resource(add_params(url, params)).get
56
- instantiate Nokogiri.parse(response.body).root, auth
57
- end
58
-
59
- def self.instantiate(node, auth)
60
- if node['type'] == 'array'
61
- node.xpath("./*").map { |subnode| Resource.new(auth, subnode) }
62
- else
63
- Resource.new(auth, node)
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
- def self.add_params(url, options)
68
- options[:expand] = options[:expand].join(",") if options[:expand].is_a?(Array)
69
- params = options.map { |k, v| "#{k}=#{v}" }.join("&")
70
- params = "?#{params}" unless params.empty?
71
- url + params
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.ResourceCollection(*args)
75
- ResourceCollection.new(*args)
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
- version: 0.1.2
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-04-12 00:00:00 +02:00
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: steak
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: :development
56
+ type: :runtime
57
57
  version_requirements: *id003
58
58
  - !ruby/object:Gem::Dependency
59
- name: webmock
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: []