restfulie 0.9.3 → 1.0.0.beta1

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 (78) hide show
  1. data/Gemfile +2 -0
  2. data/Gemfile.lock +2 -2
  3. data/README.textile +4 -2
  4. data/Rakefile +12 -13
  5. data/lib/restfulie/client/base.rb +9 -2
  6. data/lib/restfulie/client/cache/basic.rb +6 -5
  7. data/lib/restfulie/client/cache/http_ext.rb +10 -8
  8. data/lib/restfulie/client/cache/restrictions.rb +1 -6
  9. data/lib/restfulie/client/dsl.rb +66 -0
  10. data/lib/restfulie/client/entry_point.rb +34 -9
  11. data/lib/restfulie/client/ext/atom_ext.rb +4 -2
  12. data/lib/restfulie/client/ext/json_ext.rb +5 -1
  13. data/lib/restfulie/client/feature/base.rb +75 -0
  14. data/lib/restfulie/client/feature/base_request.rb +35 -0
  15. data/lib/restfulie/client/feature/cache.rb +16 -0
  16. data/lib/restfulie/client/feature/enhance_response.rb +12 -0
  17. data/lib/restfulie/client/feature/follow_request.rb +41 -0
  18. data/lib/restfulie/client/feature/history.rb +26 -0
  19. data/lib/restfulie/client/feature/history_request.rb +19 -0
  20. data/lib/restfulie/client/feature/open_search/pattern_matcher.rb +25 -0
  21. data/lib/restfulie/client/feature/open_search.rb +21 -0
  22. data/lib/restfulie/client/feature/serialize_body.rb +32 -0
  23. data/lib/restfulie/client/feature/setup_header.rb +22 -0
  24. data/lib/restfulie/client/feature/throw_error.rb +41 -0
  25. data/lib/restfulie/client/feature/verb.rb +119 -0
  26. data/lib/restfulie/client/feature.rb +5 -0
  27. data/lib/restfulie/client/http/response_holder.rb +26 -6
  28. data/lib/restfulie/client/http.rb +1 -21
  29. data/lib/restfulie/client/master_delegator.rb +31 -0
  30. data/lib/restfulie/client/mikyung/core.rb +5 -4
  31. data/lib/restfulie/client/mikyung/steady_state_walker.rb +1 -1
  32. data/lib/restfulie/client/mikyung.rb +1 -8
  33. data/lib/restfulie/client.rb +3 -1
  34. data/lib/restfulie/common/converter/atom/base.rb +2 -0
  35. data/lib/restfulie/common/converter/form_url_encoded.rb +16 -0
  36. data/lib/restfulie/common/converter/json/base.rb +5 -2
  37. data/lib/restfulie/common/converter/open_search/descriptor.rb +32 -0
  38. data/lib/restfulie/common/converter/open_search.rb +16 -0
  39. data/lib/restfulie/common/converter/xml/base.rb +3 -1
  40. data/lib/restfulie/common/converter/xml/builder.rb +3 -2
  41. data/lib/restfulie/common/converter/xml/helpers.rb +4 -4
  42. data/lib/restfulie/common/converter/xml/link.rb +5 -0
  43. data/lib/restfulie/common/converter/xml/links.rb +1 -5
  44. data/lib/restfulie/common/converter.rb +25 -4
  45. data/lib/restfulie/common/core_ext/hash.rb +6 -0
  46. data/lib/restfulie/common/links.rb +9 -0
  47. data/lib/restfulie/common/representation/atom/base.rb +34 -33
  48. data/lib/restfulie/common/representation/atom/xml.rb +5 -10
  49. data/lib/restfulie/common/representation/generic.rb +0 -12
  50. data/lib/restfulie/common/representation/json/keys_as_methods.rb +2 -0
  51. data/lib/restfulie/common/representation.rb +2 -9
  52. data/lib/restfulie/common.rb +2 -1
  53. data/lib/restfulie/server/action_controller/trait/cacheable.rb +81 -0
  54. data/lib/restfulie/server/action_controller/trait/created.rb +17 -0
  55. data/lib/restfulie/server/action_controller/trait/save_prior_to_create.rb +13 -0
  56. data/lib/restfulie/server/action_controller/trait.rb +9 -0
  57. data/lib/restfulie/server/action_controller.rb +1 -5
  58. data/lib/restfulie/server/action_view/template_handlers/tokamak.rb +1 -1
  59. data/lib/restfulie/server.rb +6 -0
  60. data/lib/restfulie/version.rb +4 -4
  61. data/lib/restfulie.rb +21 -3
  62. metadata +37 -26
  63. data/lib/restfulie/client/ext/xml_ext.rb +0 -4
  64. data/lib/restfulie/client/http/link_request_builder.rb +0 -16
  65. data/lib/restfulie/client/http/request_adapter.rb +0 -213
  66. data/lib/restfulie/client/http/request_builder.rb +0 -114
  67. data/lib/restfulie/client/http/request_builder_executor.rb +0 -24
  68. data/lib/restfulie/client/http/request_executor.rb +0 -17
  69. data/lib/restfulie/client/http/request_follow.rb +0 -42
  70. data/lib/restfulie/client/http/request_follow_executor.rb +0 -10
  71. data/lib/restfulie/client/http/request_history.rb +0 -71
  72. data/lib/restfulie/client/http/request_history_executor.rb +0 -10
  73. data/lib/restfulie/client/http/request_marshaller.rb +0 -129
  74. data/lib/restfulie/client/http/request_marshaller_executor.rb +0 -10
  75. data/lib/restfulie/client/http/response.rb +0 -23
  76. data/lib/restfulie/client/http/response_handler.rb +0 -67
  77. data/lib/restfulie/server/action_controller/cacheable_responder.rb +0 -77
  78. data/lib/restfulie/server/action_controller/created_responder.rb +0 -19
@@ -0,0 +1,22 @@
1
+ module Restfulie::Client::Feature
2
+
3
+ class SetupHeader
4
+
5
+ def execute(flow, request, response, env)
6
+ headers = request.default_headers.dup.merge(request.headers)
7
+ host = request.host
8
+ if host.user || host.password
9
+ headers["Authorization"] = "Basic " + ["#{host.user}:#{host.password}"].pack("m").delete("\r\n")
10
+ end
11
+ headers.delete :recipe
12
+ headers['cookie'] = request.cookies if request.cookies
13
+
14
+ # gs: this should not be overriden, do it in some other way
15
+ request.headers = headers
16
+
17
+ flow.continue(request, response, env)
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,41 @@
1
+ class Restfulie::Client::Feature::ThrowError
2
+ def execute(flow, request, result_so_far, env)
3
+ result = flow.continue(request, result_so_far, env)
4
+ if result.kind_of? Exception
5
+ Restfulie::Common::Logger.logger.error(result)
6
+ raise Restfulie::Client::HTTP::Error::ServerNotAvailableError.new(request, Restfulie::Client::HTTP::Response.new(request.verb, request.path, 503, nil, {}), result )
7
+ end
8
+ case result.response.code.to_i
9
+ when 100..299
10
+ result
11
+ when 300..399
12
+ raise Restfulie::Client::HTTP::Error::Redirection.new(request, result)
13
+ when 400
14
+ raise Restfulie::Client::HTTP::Error::BadRequest.new(request, result)
15
+ when 401
16
+ raise Restfulie::Client::HTTP::Error::Unauthorized.new(request, result)
17
+ when 403
18
+ raise Restfulie::Client::HTTP::Error::Forbidden.new(request, result)
19
+ when 404
20
+ raise Restfulie::Client::HTTP::Error::NotFound.new(request, result)
21
+ when 405
22
+ raise Restfulie::Client::HTTP::Error::MethodNotAllowed.new(request, result)
23
+ when 407
24
+ raise Restfulie::Client::HTTP::Error::ProxyAuthenticationRequired.new(request, result)
25
+ when 409
26
+ raise Restfulie::Client::HTTP::Error::Conflict.new(request, result)
27
+ when 410
28
+ raise Restfulie::Client::HTTP::Error::Gone.new(request, result)
29
+ when 412
30
+ raise Restfulie::Client::HTTP::Error::PreconditionFailed.new(request, result)
31
+ when 402, 406, 408, 411, 413..499
32
+ raise Restfulie::Client::HTTP::Error::ClientError.new(request, result)
33
+ when 501
34
+ raise Restfulie::Client::HTTP::Error::NotImplemented.new(request, result)
35
+ when 500, 502..599
36
+ raise Restfulie::Client::HTTP::Error::ServerError.new(request, result)
37
+ else
38
+ raise Restfulie::Client::HTTP::Error::UnknownError.new(request, result)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,119 @@
1
+ module Restfulie::Client::Feature::Verb
2
+
3
+ # GET HTTP verb without {Error}
4
+ # * <tt>path: '/posts'</tt>
5
+ # * <tt>headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'}</tt>
6
+ def get(params = {})
7
+ @verb = :get
8
+ at query_string(params)
9
+ request_flow
10
+ end
11
+
12
+ # HEAD HTTP verb without {Error}
13
+ # * <tt>path: '/posts'</tt>
14
+ # * <tt>headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'}</tt>
15
+ def head
16
+ @verb = :head
17
+ request_flow
18
+ end
19
+
20
+ # POST HTTP verb without {Error}
21
+ # * <tt>path: '/posts'</tt>
22
+ # * <tt>payload: 'some text'</tt>
23
+ # * <tt>headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'}</tt>
24
+ def post(payload, options = {:recipe => nil})
25
+ @verb = :post
26
+ request_flow :body => payload
27
+ end
28
+
29
+ # PATCH HTTP verb without {Error}
30
+ # * <tt>path: '/posts'</tt>
31
+ # * <tt>payload: 'some text'</tt>
32
+ # * <tt>headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'}</tt>
33
+ def patch(payload)
34
+ @verb = :patch
35
+ request_flow :body => payload
36
+ end
37
+
38
+ # PUT HTTP verb without {Error}
39
+ # * <tt>path: '/posts'</tt>
40
+ # * <tt>payload: 'some text'</tt>
41
+ # * <tt>headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'}</tt>
42
+ def put(payload)
43
+ @verb = :put
44
+ request_flow :body => payload
45
+ end
46
+
47
+ # DELETE HTTP verb without {Error}
48
+ # * <tt>path: '/posts'</tt>
49
+ # * <tt>headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'}</tt>
50
+ def delete
51
+ @verb = :delete
52
+ request_flow
53
+ end
54
+
55
+ # GET HTTP verb {Error}
56
+ # * <tt>path: '/posts'</tt>
57
+ # * <tt>headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'}</tt>
58
+ def get!(params = {})
59
+ @verb = :get
60
+ at query_string(params)
61
+ request :throw_error
62
+ request_flow
63
+ end
64
+
65
+ # HEAD HTTP verb {Error}
66
+ # * <tt>path: '/posts'</tt>
67
+ # * <tt>headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'}</tt>
68
+ def head!
69
+ @verb = :head
70
+ request :throw_error
71
+ request_flow
72
+ end
73
+
74
+ # POST HTTP verb {Error}
75
+ # * <tt>path: '/posts'</tt>
76
+ # * <tt>payload: 'some text'</tt>
77
+ # * <tt>headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'}</tt>
78
+ def post!(payload, options = {:recipe => nil})
79
+ @verb = :post
80
+ request :throw_error
81
+ request_flow :body => payload
82
+ end
83
+
84
+ # PATCH HTTP verb {Error}
85
+ # * <tt>path: '/posts'</tt>
86
+ # * <tt>payload: 'some text'</tt>
87
+ # * <tt>headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'}</tt>
88
+ def patch!(payload)
89
+ @verb = :patch
90
+ request :throw_error
91
+ request_flow :body => payload
92
+ end
93
+
94
+ # PUT HTTP verb {Error}
95
+ # * <tt>path: '/posts'</tt>
96
+ # * <tt>payload: 'some text'</tt>
97
+ # * <tt>headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'}</tt>
98
+ def put!(payload)
99
+ @verb = :put
100
+ request :throw_error
101
+ request_flow :body => payload
102
+ end
103
+
104
+ # DELETE HTTP verb {Error}
105
+ # * <tt>path: '/posts'</tt>
106
+ # * <tt>headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'}</tt>
107
+ def delete!
108
+ @verb = :delete
109
+ request :throw_error
110
+ request_flow
111
+ end
112
+
113
+ protected
114
+
115
+ def query_string(params)
116
+ params = params.map { |param, value| "#{param}=#{value}"}.join("&")
117
+ params.blank? ? "" : URI.escape("?#{params}")
118
+ end
119
+ end
@@ -0,0 +1,5 @@
1
+ module Restfulie::Client
2
+ module Feature
3
+ Dir["#{File.dirname(__FILE__)}/feature/*.rb"].each {|f| autoload File.basename(f)[0..-4].camelize.to_sym, f }
4
+ end
5
+ end
@@ -1,9 +1,29 @@
1
- module Restfulie
2
- module Client
3
- module HTTP
4
- module ResponseHolder
5
- attr_accessor :response
6
- end
1
+ module Restfulie::Client::HTTP
2
+ module ResponseHolder
3
+ attr_reader :response, :request
4
+
5
+ def resource
6
+ type = headers['content-type'] || response['Content-Type']
7
+ representation = Restfulie::Common::Converter.content_type_for(type[0]) || Restfulie::Common::Representation::Generic.new
8
+ representation.unmarshal(response.body)
7
9
  end
10
+
11
+ def at(uri)
12
+ request.clone.at(uri)
13
+ end
14
+
15
+ def headers
16
+ response.to_hash
17
+ end
18
+
19
+ def results_from(request, response)
20
+ @request = request
21
+ @response = response
22
+ end
23
+
24
+ def verb
25
+ @request.verb
26
+ end
27
+
8
28
  end
9
29
  end
@@ -1,27 +1,7 @@
1
1
  module Restfulie
2
2
  module Client
3
3
  module HTTP#:nodoc:
4
- autoload :Error, 'restfulie/client/http/error'
5
- autoload :Response, 'restfulie/client/http/response'
6
- autoload :ResponseHandler, 'restfulie/client/http/response_handler'
7
- autoload :RequestAdapter, 'restfulie/client/http/request_adapter'
8
- autoload :RequestBuilder, 'restfulie/client/http/request_builder'
9
- autoload :RequestFollow, 'restfulie/client/http/request_follow'
10
- autoload :RequestHistory, 'restfulie/client/http/request_history'
11
- autoload :RequestExecutor, 'restfulie/client/http/request_executor'
12
- autoload :RequestBuilderExecutor, 'restfulie/client/http/request_builder_executor'
13
- autoload :RequestFollowExecutor, 'restfulie/client/http/request_follow_executor'
14
- autoload :RequestHistoryExecutor, 'restfulie/client/http/request_history_executor'
15
- autoload :Cache, 'restfulie/client/http/cache'
16
- autoload :ResponseHolder, 'restfulie/client/http/response_holder'
17
- autoload :RequestMarshaller, 'restfulie/client/http/request_marshaller'
18
- autoload :LinkRequestBuilder, 'restfulie/client/http/link_request_builder'
19
- autoload :RequestMarshallerExecutor, 'restfulie/client/http/request_marshaller_executor'
4
+ Dir["#{File.dirname(__FILE__)}/http/*.rb"].each {|f| autoload File.basename(f)[0..-4].camelize.to_sym, f }
20
5
  end
21
6
  end
22
7
  end
23
-
24
- require 'restfulie/client/ext/atom_ext'
25
- require 'restfulie/client/ext/xml_ext'
26
- require 'restfulie/client/ext/http_ext'
27
- require 'restfulie/client/ext/json_ext'
@@ -0,0 +1,31 @@
1
+ class MasterDelegator
2
+
3
+ alias_method :original_respond_to?, :respond_to?
4
+
5
+ def respond_to?(sym)
6
+ original_respond_to?(sym) || @requester.respond_to?(sym)
7
+ end
8
+
9
+ def method_missing(sym, *args, &block)
10
+ if original_respond_to?(sym)
11
+ result = super(sym, *args, &block)
12
+ elsif @requester.respond_to?(sym)
13
+ result = @requester.send(sym, *args, &block)
14
+ else
15
+ # let it go
16
+ return super(sym, *args, &block)
17
+ end
18
+ delegate_parse result
19
+ end
20
+
21
+ protected
22
+
23
+ def delegate(what, *args, &block)
24
+ delegate_parse @requester.send(what, *args, &block)
25
+ end
26
+
27
+ def delegate_parse(result)
28
+ (result == @requester) ? self : result
29
+ end
30
+
31
+ end
@@ -42,6 +42,11 @@ module Restfulie
42
42
 
43
43
  # keeps changing from a steady state to another until its goal has been achieved
44
44
  def run
45
+
46
+ # load the steps and scenario
47
+ @goal.steps
48
+ @goal.scenario
49
+
45
50
  if @start.kind_of? String
46
51
  client = Restfulie.at(@start)
47
52
  client = client.follow if @follow
@@ -52,10 +57,6 @@ module Restfulie
52
57
  @start = current = @goal.class.get_restfulie.get
53
58
  end
54
59
 
55
- # load the steps and scenario
56
- @goal.steps
57
- @goal.scenario
58
-
59
60
  while(!@goal.completed?(current))
60
61
  current = @walker.move(@goal, current, self)
61
62
  end
@@ -14,7 +14,7 @@ module Restfulie
14
14
  private
15
15
 
16
16
  def try_to_execute(step, current, max_attempts, mikyung)
17
- raise "Unable to proceed when trying to #{step}" if max_attempts == 0
17
+ raise "Unable to proceed when trying to analyze #{step.body}" if max_attempts == 0
18
18
 
19
19
  resource = step
20
20
  raise "Step returned 'give up'" if resource.nil?
@@ -1,13 +1,7 @@
1
1
  module Restfulie
2
2
  module Client
3
3
  module Mikyung
4
- autoload :WhenCondition, 'restfulie/client/mikyung/when_condition'
5
- autoload :ThenCondition, 'restfulie/client/mikyung/then_condition'
6
- autoload :RestProcessModel, 'restfulie/client/mikyung/rest_process_model'
7
- autoload :Concatenator, 'restfulie/client/mikyung/concatenator'
8
- autoload :Core, 'restfulie/client/mikyung/core'
9
- autoload :SteadyStateWalker, 'restfulie/client/mikyung/steady_state_walker'
10
- autoload :Languages, 'restfulie/client/mikyung/languages'
4
+ Dir["#{File.dirname(__FILE__)}/mikyung/*.rb"].each {|f| autoload File.basename(f)[0..-4].camelize.to_sym, f }
11
5
  end
12
6
  end
13
7
  end
@@ -17,6 +11,5 @@ end
17
11
  module Restfulie
18
12
  class Mikyung < Restfulie::Client::Mikyung::Core
19
13
  Restfulie::Common::Logger.logger.level = Logger::INFO
20
- # empty class
21
14
  end
22
15
  end
@@ -2,12 +2,15 @@ require 'restfulie/common'
2
2
 
3
3
  module Restfulie
4
4
  module Client
5
+ autoload :MasterDelegator, 'restfulie/client/master_delegator'
5
6
  autoload :HTTP, 'restfulie/client/http'
6
7
  autoload :Configuration, 'restfulie/client/configuration'
7
8
  autoload :EntryPoint, 'restfulie/client/entry_point'
8
9
  autoload :Base, 'restfulie/client/base'
9
10
  autoload :Mikyung, 'restfulie/client/mikyung'
10
11
  autoload :Cache, 'restfulie/client/cache'
12
+ autoload :Feature, 'restfulie/client/feature'
13
+ autoload :Dsl, 'restfulie/client/dsl'
11
14
 
12
15
  mattr_accessor :cache_provider, :cache_store
13
16
 
@@ -20,5 +23,4 @@ end
20
23
  require 'restfulie/client/ext/http_ext'
21
24
  require 'restfulie/client/ext/atom_ext'
22
25
  require 'restfulie/client/ext/json_ext'
23
- require 'restfulie/client/ext/xml_ext'
24
26
 
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/hash/conversions'
2
+
1
3
  module Restfulie
2
4
  module Common
3
5
  module Converter
@@ -0,0 +1,16 @@
1
+ class Restfulie::Common::Converter::FormUrlEncoded < Restfulie::Common::Representation::Generic
2
+ def self.marshal(content, rel)
3
+ if content.kind_of? Hash
4
+ content.map { |key, value| "#{key}=#{value}" }.join("&")
5
+ else
6
+ content
7
+ end
8
+ end
9
+
10
+ def self.unmarshal(content)
11
+ def content.links
12
+ []
13
+ end
14
+ content
15
+ end
16
+ end
@@ -34,8 +34,11 @@ module Restfulie
34
34
 
35
35
  # Check if the object is already an json
36
36
  unless recipe
37
- return Restfulie::Common::Representation::Json.create(obj) if obj.kind_of?(Hash) || obj.kind_of?(Array)
38
- raise Restfulie::Common::Error::ConverterError.new("Recipe required")
37
+ if obj.kind_of?(Hash) || obj.kind_of?(Array)
38
+ return Restfulie::Common::Representation::Json.create(obj)
39
+ else
40
+ raise Restfulie::Common::Error::ConverterError.new("Recipe required")
41
+ end
39
42
  end
40
43
 
41
44
  # Get recipe already described
@@ -0,0 +1,32 @@
1
+ class Restfulie::Common::Converter::OpenSearch::Descriptor
2
+
3
+ def initialize(hash)
4
+ @hash = hash["OpenSearchDescription"]
5
+ end
6
+
7
+ def urls
8
+ uris = @hash["Url"]
9
+ uris.kind_of?(Array) ? uris : [uris]
10
+ end
11
+
12
+ def use(content_type)
13
+ uri = urls.find do |url|
14
+ url["type"]==content_type
15
+ end
16
+ return nil if uri.nil?
17
+
18
+ base_uri, params_pattern = extract_uri(uri)
19
+ Restfulie.at(base_uri).accepts(content_type).open_search.with_pattern(params_pattern)
20
+ end
21
+
22
+ private
23
+ def extract_uri(uri)
24
+ uri = uri["template"]
25
+ interrogation = uri.index("?")
26
+ params = uri[interrogation+1..uri.size]
27
+ base_uri = uri[0..interrogation-1]
28
+ [base_uri, params]
29
+ end
30
+
31
+ end
32
+
@@ -0,0 +1,16 @@
1
+ module Restfulie
2
+ module Common
3
+ class Converter::OpenSearch#:nodoc:
4
+ autoload :Descriptor, 'restfulie/common/converter/open_search/descriptor'
5
+ end
6
+ end
7
+ end
8
+
9
+ class Restfulie::Common::Converter::OpenSearch
10
+
11
+ def self.unmarshal(xml)
12
+ hash = Hash.from_xml(xml)
13
+ descriptor = Descriptor.new(hash)
14
+ end
15
+
16
+ end
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/hash/conversions'
2
+
1
3
  module Restfulie
2
4
  module Common
3
5
  module Converter
@@ -43,7 +45,7 @@ module Restfulie
43
45
  end
44
46
 
45
47
  # Create representation and proxy
46
- builder = Builder.new(obj)
48
+ builder = Builder.new(obj, options)
47
49
 
48
50
  # Check recipe arity size before calling it
49
51
  recipe.call(*[builder, obj, options][0,recipe.arity])
@@ -42,11 +42,12 @@ module Restfulie
42
42
  insert_value("link", nil, options)
43
43
  end
44
44
 
45
- def members(a_collection = nil, &block)
45
+ def members(a_collection = nil, options = {}, &block)
46
46
  collection = a_collection || @obj
47
47
  raise Error::BuilderError("Members method require a collection to execute") unless collection.respond_to?(:each)
48
48
  collection.each do |member|
49
- entry = @doc.create_element(Restfulie::Common::Converter.root_element_for(member))
49
+ root = options[:root] || Restfulie::Common::Converter.root_element_for(member)
50
+ entry = @doc.create_element(root)
50
51
  entry.parent = @parent
51
52
  @parent = entry
52
53
  block.call(self, member)
@@ -3,12 +3,12 @@ module Restfulie
3
3
  module Converter
4
4
  module Xml
5
5
  module Helpers
6
- def collection(obj, *args, &block)
7
- Xml.to_xml(obj, {}, &block)
6
+ def collection(obj, opts = {}, &block)
7
+ Xml.to_xml(obj, opts, &block)
8
8
  end
9
9
 
10
- def member(obj, *args, &block)
11
- Xml.to_xml(obj, {}, &block)
10
+ def member(obj, opts = {}, &block)
11
+ Xml.to_xml(obj, opts, &block)
12
12
  end
13
13
  end
14
14
  end
@@ -18,6 +18,11 @@ module Restfulie
18
18
  def type
19
19
  content_type
20
20
  end
21
+ def follow
22
+ r = Restfulie.at(href)
23
+ r = r.as(content_type) if content_type
24
+ r
25
+ end
21
26
  end
22
27
  end
23
28
  end
@@ -6,11 +6,7 @@ module Restfulie
6
6
  def initialize(links)
7
7
  links = [links] unless links.kind_of? Array
8
8
  links = [] unless links
9
- @links = links.map do |l|
10
- link = Restfulie::Common::Converter::Xml::Link.new(l)
11
- link.instance_eval { self.class.send :include, ::Restfulie::Client::HTTP::LinkRequestBuilder }
12
- link
13
- end
9
+ @links = links.map { |l| Restfulie::Common::Converter::Xml::Link.new(l) }
14
10
  end
15
11
 
16
12
  def method_missing(sym, *args)
@@ -1,10 +1,7 @@
1
1
  module Restfulie
2
2
  module Common
3
3
  module Converter
4
- autoload :Values, 'restfulie/common/converter/values'
5
- autoload :Atom, 'restfulie/common/converter/atom'
6
- autoload :Json, 'restfulie/common/converter/json'
7
- autoload :Xml, 'restfulie/common/converter/xml'
4
+ Dir["#{File.dirname(__FILE__)}/converter/*.rb"].each {|f| autoload File.basename(f)[0..-4].camelize.to_sym, f }
8
5
 
9
6
  # Returns the default root element name for an item or collection
10
7
  def self.root_element_for(obj)
@@ -16,6 +13,30 @@ module Restfulie
16
13
  obj.class.to_s.underscore
17
14
  end
18
15
  end
16
+
17
+ def self.register(media_type,representation)
18
+ representations[media_type] = representation
19
+ end
20
+
21
+ def self.content_type_for(media_type)
22
+ return nil unless media_type
23
+ content_type = media_type.split(';')[0] # [/(.*?);/, 1]
24
+ representations[content_type]
25
+ end
26
+
27
+ private
28
+
29
+ def self.representations
30
+ @representations ||= {}
31
+ end
32
+
33
+ register 'application/atom+xml' , ::Restfulie::Common::Converter::Atom
34
+ register 'application/xml' , ::Restfulie::Common::Converter::Xml
35
+ register 'text/xml' , ::Restfulie::Common::Converter::Xml
36
+ register 'application/json' , ::Restfulie::Common::Converter::Json
37
+ register 'application/opensearchdescription+xml' , ::Restfulie::Common::Converter::OpenSearch
38
+ register 'application/x-www-form-urlencoded', Restfulie::Common::Converter::FormUrlEncoded
39
+
19
40
  end
20
41
  end
21
42
  end
@@ -1,12 +1,18 @@
1
1
  class Hash
2
+
2
3
  def links(*args)
3
4
  links = fetch("link", [])
4
5
  Restfulie::Common::Converter::Xml::Links.new(links)
5
6
  end
7
+
8
+ include Restfulie::Common::Links
9
+
6
10
  def method_missing(sym, *args)
7
11
  self[sym.to_s].nil? ? super(sym, args) : self[sym.to_s]
8
12
  end
13
+
9
14
  def respond_to?(sym)
10
15
  include?(sym.to_s) || super(sym)
11
16
  end
17
+
12
18
  end
@@ -0,0 +1,9 @@
1
+ module Restfulie
2
+ module Common
3
+ module Links
4
+ def refresh
5
+ links.self.follow.get
6
+ end
7
+ end
8
+ end
9
+ end