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.
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: []