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.
Files changed (43) hide show
  1. data/README.rdoc +4 -4
  2. data/lib/almodovar.rb +15 -181
  3. data/lib/almodovar/digest_auth.rb +4 -0
  4. data/lib/almodovar/http_accessor.rb +26 -0
  5. data/lib/almodovar/resource.rb +56 -0
  6. data/lib/almodovar/resource_collection.rb +31 -0
  7. data/lib/almodovar/resource_presenter.rb +110 -0
  8. data/lib/almodovar/resource_presenter/collection.rb +24 -0
  9. data/lib/almodovar/resource_presenter/link.rb +103 -0
  10. data/lib/almodovar/single_resource.rb +79 -0
  11. data/lib/almodovar/to_xml.rb +21 -0
  12. metadata +47 -91
  13. data/lib/to_xml.rb +0 -24
  14. data/vendor/resourceful-0.5.3-patched/MIT-LICENSE +0 -21
  15. data/vendor/resourceful-0.5.3-patched/Manifest +0 -29
  16. data/vendor/resourceful-0.5.3-patched/README.markdown +0 -84
  17. data/vendor/resourceful-0.5.3-patched/Rakefile +0 -71
  18. data/vendor/resourceful-0.5.3-patched/lib/resourceful.rb +0 -18
  19. data/vendor/resourceful-0.5.3-patched/lib/resourceful/authentication_manager.rb +0 -108
  20. data/vendor/resourceful-0.5.3-patched/lib/resourceful/cache_manager.rb +0 -240
  21. data/vendor/resourceful-0.5.3-patched/lib/resourceful/exceptions.rb +0 -34
  22. data/vendor/resourceful-0.5.3-patched/lib/resourceful/header.rb +0 -126
  23. data/vendor/resourceful-0.5.3-patched/lib/resourceful/http_accessor.rb +0 -98
  24. data/vendor/resourceful-0.5.3-patched/lib/resourceful/memcache_cache_manager.rb +0 -75
  25. data/vendor/resourceful-0.5.3-patched/lib/resourceful/net_http_adapter.rb +0 -70
  26. data/vendor/resourceful-0.5.3-patched/lib/resourceful/options_interpreter.rb +0 -78
  27. data/vendor/resourceful-0.5.3-patched/lib/resourceful/request.rb +0 -230
  28. data/vendor/resourceful-0.5.3-patched/lib/resourceful/resource.rb +0 -165
  29. data/vendor/resourceful-0.5.3-patched/lib/resourceful/response.rb +0 -221
  30. data/vendor/resourceful-0.5.3-patched/lib/resourceful/stubbed_resource_proxy.rb +0 -47
  31. data/vendor/resourceful-0.5.3-patched/lib/resourceful/util.rb +0 -6
  32. data/vendor/resourceful-0.5.3-patched/resourceful.gemspec +0 -48
  33. data/vendor/resourceful-0.5.3-patched/spec/acceptance/authorization_spec.rb +0 -16
  34. data/vendor/resourceful-0.5.3-patched/spec/acceptance/caching_spec.rb +0 -192
  35. data/vendor/resourceful-0.5.3-patched/spec/acceptance/header_spec.rb +0 -24
  36. data/vendor/resourceful-0.5.3-patched/spec/acceptance/redirecting_spec.rb +0 -12
  37. data/vendor/resourceful-0.5.3-patched/spec/acceptance/resource_spec.rb +0 -84
  38. data/vendor/resourceful-0.5.3-patched/spec/acceptance_shared_specs.rb +0 -44
  39. data/vendor/resourceful-0.5.3-patched/spec/old_acceptance_specs.rb +0 -378
  40. data/vendor/resourceful-0.5.3-patched/spec/simple_sinatra_server.rb +0 -74
  41. data/vendor/resourceful-0.5.3-patched/spec/simple_sinatra_server_spec.rb +0 -98
  42. data/vendor/resourceful-0.5.3-patched/spec/spec.opts +0 -3
  43. 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 File.dirname(__FILE__) + '/../vendor/resourceful-0.5.3-patched/lib/resourceful'
1
+ require 'patron'
2
2
  require 'nokogiri'
3
- require 'active_support'
4
- require 'to_xml'
5
- require 'uri'
3
+ begin
4
+ require 'active_support/all'
5
+ rescue LoadError
6
+ require 'active_support'
7
+ end
6
8
 
7
- module Almodovar
8
-
9
- class DigestAuth < Resourceful::DigestAuthenticator
10
- end
11
-
12
- module HttpAccessor
13
-
14
- def xml
15
- @xml ||= begin
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,4 @@
1
+ module Almodovar
2
+ class DigestAuth < Struct.new(:realm, :username, :password)
3
+ end
4
+ end
@@ -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