almodovar 0.5.6 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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