restfulie 0.8.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. data/Gemfile +10 -5
  2. data/Rakefile +5 -2
  3. data/lib/restfulie.rb +10 -5
  4. data/lib/restfulie/client.rb +13 -9
  5. data/lib/restfulie/client/base.rb +24 -65
  6. data/lib/restfulie/client/configuration.rb +62 -64
  7. data/lib/restfulie/client/entry_point.rb +36 -0
  8. data/lib/restfulie/client/ext/atom_ext.rb +12 -0
  9. data/lib/restfulie/client/ext/http_ext.rb +22 -0
  10. data/lib/restfulie/client/ext/json_ext.rb +12 -0
  11. data/lib/restfulie/client/ext/xml_ext.rb +4 -0
  12. data/lib/restfulie/client/http.rb +25 -13
  13. data/lib/restfulie/client/http/cache.rb +22 -22
  14. data/lib/restfulie/client/http/error.rb +70 -70
  15. data/lib/restfulie/client/http/link_request_builder.rb +15 -0
  16. data/lib/restfulie/client/http/request_adapter.rb +209 -0
  17. data/lib/restfulie/client/http/request_builder.rb +107 -0
  18. data/lib/restfulie/client/http/request_builder_executor.rb +24 -0
  19. data/lib/restfulie/client/http/request_executor.rb +17 -0
  20. data/lib/restfulie/client/http/request_follow.rb +42 -0
  21. data/lib/restfulie/client/http/request_follow_executor.rb +10 -0
  22. data/lib/restfulie/client/http/request_history.rb +69 -0
  23. data/lib/restfulie/client/http/request_history_executor.rb +10 -0
  24. data/lib/restfulie/client/http/request_marshaller.rb +127 -0
  25. data/lib/restfulie/client/http/request_marshaller_executor.rb +10 -0
  26. data/lib/restfulie/client/http/response.rb +23 -0
  27. data/lib/restfulie/client/http/response_handler.rb +67 -0
  28. data/lib/restfulie/client/http/response_holder.rb +9 -0
  29. data/lib/restfulie/client/mikyung.rb +17 -14
  30. data/lib/restfulie/client/mikyung/concatenator.rb +15 -12
  31. data/lib/restfulie/client/mikyung/core.rb +65 -39
  32. data/lib/restfulie/client/mikyung/languages.rb +8 -26
  33. data/lib/restfulie/client/mikyung/languages/german.rb +24 -0
  34. data/lib/restfulie/client/mikyung/languages/portuguese.rb +23 -0
  35. data/lib/restfulie/client/mikyung/rest_process_model.rb +184 -107
  36. data/lib/restfulie/client/mikyung/steady_state_walker.rb +34 -28
  37. data/lib/restfulie/client/mikyung/then_condition.rb +33 -27
  38. data/lib/restfulie/client/mikyung/when_condition.rb +53 -49
  39. data/lib/restfulie/common.rb +7 -12
  40. data/lib/restfulie/common/converter.rb +20 -9
  41. data/lib/restfulie/common/converter/atom.rb +8 -83
  42. data/lib/restfulie/common/converter/atom/base.rb +89 -0
  43. data/lib/restfulie/common/converter/atom/builder.rb +101 -99
  44. data/lib/restfulie/common/converter/atom/helpers.rb +16 -8
  45. data/lib/restfulie/common/converter/json.rb +12 -0
  46. data/lib/restfulie/common/converter/json/base.rb +84 -0
  47. data/lib/restfulie/common/converter/json/builder.rb +102 -0
  48. data/lib/restfulie/common/converter/json/helpers.rb +17 -0
  49. data/lib/restfulie/common/converter/values.rb +30 -26
  50. data/lib/restfulie/common/converter/xml.rb +14 -0
  51. data/lib/restfulie/common/converter/xml/base.rb +61 -0
  52. data/lib/restfulie/common/converter/xml/builder.rb +112 -0
  53. data/lib/restfulie/common/converter/xml/helpers.rb +17 -0
  54. data/lib/restfulie/common/converter/xml/link.rb +25 -0
  55. data/lib/restfulie/common/converter/xml/links.rb +25 -0
  56. data/lib/restfulie/common/core_ext.rb +1 -5
  57. data/lib/restfulie/common/core_ext/hash.rb +12 -0
  58. data/lib/restfulie/common/error.rb +19 -0
  59. data/lib/restfulie/common/logger.rb +17 -9
  60. data/lib/restfulie/common/representation.rb +9 -10
  61. data/lib/restfulie/common/representation/atom.rb +15 -47
  62. data/lib/restfulie/common/representation/atom/base.rb +122 -365
  63. data/lib/restfulie/common/representation/atom/category.rb +41 -0
  64. data/lib/restfulie/common/representation/atom/entry.rb +52 -100
  65. data/lib/restfulie/common/representation/atom/factory.rb +43 -0
  66. data/lib/restfulie/common/representation/atom/feed.rb +103 -99
  67. data/lib/restfulie/common/representation/atom/link.rb +68 -0
  68. data/lib/restfulie/common/representation/atom/person.rb +48 -0
  69. data/lib/restfulie/common/representation/atom/source.rb +59 -0
  70. data/lib/restfulie/common/representation/atom/tag_collection.rb +38 -0
  71. data/lib/restfulie/common/representation/atom/xml.rb +95 -0
  72. data/lib/restfulie/common/representation/generic.rb +30 -29
  73. data/lib/restfulie/common/representation/json.rb +10 -22
  74. data/lib/restfulie/common/representation/json/base.rb +27 -0
  75. data/lib/restfulie/common/representation/json/keys_as_methods.rb +72 -0
  76. data/lib/restfulie/common/representation/json/link.rb +29 -0
  77. data/lib/restfulie/common/representation/json/link_collection.rb +23 -0
  78. data/lib/restfulie/common/representation/xml.rb +18 -227
  79. data/lib/restfulie/server.rb +9 -10
  80. data/lib/restfulie/server/action_controller.rb +10 -12
  81. data/lib/restfulie/server/action_controller/base.rb +18 -15
  82. data/lib/restfulie/server/action_controller/{routing/patch.rb → patch.rb} +0 -0
  83. data/lib/restfulie/server/action_controller/restful_responder.rb +43 -35
  84. data/lib/restfulie/server/action_view.rb +8 -6
  85. data/lib/restfulie/server/action_view/helpers.rb +47 -41
  86. data/lib/restfulie/server/action_view/template_handlers.rb +24 -12
  87. data/lib/restfulie/server/action_view/template_handlers/tokamak.rb +17 -12
  88. data/lib/restfulie/server/configuration.rb +22 -19
  89. data/lib/restfulie/server/{restfulie_controller.rb → controller.rb} +1 -10
  90. data/lib/restfulie/server/core_ext.rb +1 -1
  91. data/lib/restfulie/version.rb +14 -0
  92. metadata +52 -16
  93. data/lib/restfulie/client/http/adapter.rb +0 -502
  94. data/lib/restfulie/client/http/atom_ext.rb +0 -4
  95. data/lib/restfulie/client/http/core_ext.rb +0 -6
  96. data/lib/restfulie/client/http/core_ext/http.rb +0 -19
  97. data/lib/restfulie/client/http/marshal.rb +0 -145
  98. data/lib/restfulie/client/http/xml_ext.rb +0 -7
  99. data/lib/restfulie/common/core_ext/proc.rb +0 -48
  100. data/lib/restfulie/common/errors.rb +0 -15
  101. data/lib/restfulie/server/action_controller/routing.rb +0 -12
  102. data/lib/restfulie/server/action_controller/routing/restful_route.rb +0 -14
@@ -0,0 +1,24 @@
1
+ module Restfulie
2
+ module Client
3
+ module HTTP #:nodoc:
4
+ #=This class includes RequestBuilder module.
5
+ class RequestBuilderExecutor < RequestExecutor
6
+ include RequestBuilder
7
+
8
+ def host=(host)
9
+ super
10
+ at(self.host.path)
11
+ end
12
+
13
+ def at(path)
14
+ @path = path
15
+ self
16
+ end
17
+
18
+ def path
19
+ @path
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,17 @@
1
+ module Restfulie
2
+ module Client
3
+ module HTTP #:nodoc:
4
+ #=This class includes RequestAdapter module.
5
+ class RequestExecutor
6
+ include RequestAdapter
7
+
8
+ # * <tt> host (e.g. 'http://restfulie.com') </tt>
9
+ # * <tt> default_headers (e.g. {'Cache-control' => 'no-cache'} ) </tt>
10
+ def initialize(host, default_headers = {})
11
+ self.host=host
12
+ self.default_headers=default_headers
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,42 @@
1
+ module Restfulie
2
+ module Client
3
+ module HTTP #:nodoc:
4
+ # ==== RequestFollow follow new location of a document usually with response codes 201,301,302,303 and 307. You can also configure other codes.
5
+ #
6
+ # ==== Example:
7
+ # @executor = ::Restfulie::Client::HTTP::RequestFollowExecutor.new("http://restfulie.com") #this class includes RequestFollow module.
8
+ # @executor.at('/custom/songs').accepts('application/atom+xml').follow(201).post!("custom").code
9
+ module RequestFollow
10
+ include RequestBuilder
11
+
12
+ def follow(code = nil)
13
+ @follow ||= true # turn on follow redirection
14
+ follow_codes << code unless code.nil? or follow_codes.include?(code)
15
+ self
16
+ end
17
+
18
+ def request!(method, path, *args)#:nodoc:
19
+ begin
20
+ response = super
21
+ rescue Error::Redirection => e
22
+ raise e unless @follow # normal behavior for bang methods is to raise the exception
23
+ response = e.response
24
+ if follow_codes.include?(response.code)
25
+ location = response.headers['location'] || response.headers['Location']
26
+ raise Error::AutoFollowWithoutLocationError.new(self, response) unless location
27
+ self.host = location
28
+ response = super(:get, location, headers)
29
+ end
30
+ response
31
+ end
32
+ end
33
+
34
+ protected
35
+
36
+ def follow_codes
37
+ @follow_codes ||= [201,301,302,303,307]
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,10 @@
1
+ module Restfulie
2
+ module Client
3
+ module HTTP #:nodoc:
4
+ #=This class inherits RequestBuilderExecutor and include RequestFollow module.
5
+ class RequestFollowExecutor < RequestBuilderExecutor
6
+ include RequestFollow
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,69 @@
1
+ module Restfulie
2
+ module Client
3
+ module HTTP #:nodoc:
4
+ # ==== RequestHistory
5
+ # Uses RequestBuilder and remind previous requests
6
+ #
7
+ # ==== Example:
8
+ #
9
+ # @executor = ::Restfulie::Client::HTTP::RequestHistoryExecutor.new("http://restfulie.com") #this class includes RequestHistory module.
10
+ # @executor.at('/posts').as('application/xml').accepts('application/atom+xml').with('Accept-Language' => 'en').get.code #=> 200 #first request
11
+ # @executor.at('/blogs').as('application/xml').accepts('application/atom+xml').with('Accept-Language' => 'en').get.code #=> 200 #second request
12
+ # @executor.request_history!(0) #doing first request
13
+ #
14
+ module RequestHistory
15
+ include RequestBuilder
16
+
17
+ attr_accessor_with_default :max_to_remind, 10
18
+
19
+ def snapshots
20
+ @snapshots ||= []
21
+ end
22
+
23
+ def request!(method=nil, path=nil, *args)#:nodoc:
24
+ if method == nil || path == nil
25
+ raise 'History not selected' unless @snapshot
26
+ super( @snapshot[:method], @snapshot[:path], *@snapshot[:args] )
27
+ else
28
+ @snapshot = make_snapshot(method, path, *args)
29
+ unless snapshots.include?(@snapshot)
30
+ snapshots.shift if snapshots.size >= max_to_remind
31
+ snapshots << @snapshot
32
+ end
33
+ super
34
+ end
35
+ end
36
+
37
+ def request(method=nil, path=nil, *args)#:nodoc:
38
+ request!(method, path, *args)
39
+ rescue Error::RESTError => se
40
+ se.response
41
+ end
42
+
43
+ def history(number)
44
+ @snapshot = snapshots[number]
45
+ raise "Undefined snapshot for #{number}" unless @snapshot
46
+ self.host = @snapshot[:host]
47
+ self.cookies = @snapshot[:cookies]
48
+ self.headers = @snapshot[:headers]
49
+ self.default_headers = @snapshot[:default_headers]
50
+ at(@snapshot[:path])
51
+ end
52
+
53
+ private
54
+
55
+ def make_snapshot(method, path, *args)
56
+ arguments = args.dup
57
+ cutom_headers = arguments.extract_options!
58
+ { :host => self.host.dup,
59
+ :default_headers => self.default_headers.dup,
60
+ :headers => self.headers.dup,
61
+ :cookies => self.cookies,
62
+ :method => method,
63
+ :path => path,
64
+ :args => arguments << self.headers.merge(cutom_headers) }
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,10 @@
1
+ module Restfulie
2
+ module Client
3
+ module HTTP #:nodoc:
4
+ #=This class inherits RequestFollowExecutor and include RequestHistory module.
5
+ class RequestHistoryExecutor < RequestBuilderExecutor
6
+ include RequestHistory
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,127 @@
1
+ module Restfulie
2
+ module Client
3
+ module HTTP
4
+ module RequestMarshaller
5
+ include RequestHistory
6
+
7
+ @@representations = {
8
+ 'application/atom+xml' => ::Restfulie::Common::Converter::Atom,
9
+ 'application/xml' => ::Restfulie::Common::Converter::Xml,
10
+ 'text/xml' => ::Restfulie::Common::Converter::Xml,
11
+ 'application/json' => ::Restfulie::Common::Converter::Json
12
+ }
13
+
14
+ def self.register_representation(media_type,representation)
15
+ @@representations[media_type] = representation
16
+ end
17
+
18
+ def self.content_type_for(media_type)
19
+ return nil unless media_type
20
+ content_type = media_type.split(';')[0] # [/(.*?);/, 1]
21
+ @@representations[content_type]
22
+ end
23
+
24
+ def accepts(media_types)
25
+ @default_representation = @@representations[media_types]
26
+ super
27
+ end
28
+
29
+ def raw
30
+ @raw = true
31
+ self
32
+ end
33
+
34
+ def post(payload, options = { :recipe => nil })
35
+ request(:post, path, payload, options.merge(headers))
36
+ end
37
+
38
+ def post!(payload, options = { :recipe => nil })
39
+ request!(:post, path, payload, options.merge(headers))
40
+ end
41
+
42
+ # Executes super if its a raw request, returning the content itself.
43
+ # otherwise tries to parse the content with a mediatype handler or returns the response itself.
44
+ def request!(method, path, *args)
45
+ if has_payload?(method, path, *args)
46
+ recipe = get_recipe(*args)
47
+
48
+ payload = get_payload(method, path, *args)
49
+ rel = self.respond_to?(:rel) ? self.rel : ""
50
+ type = headers['Content-Type']
51
+ raise Restfulie::Common::Error::RestfulieError, "Missing content type related to the data to be submitted" unless type
52
+ marshaller = RequestMarshaller.content_type_for(type)
53
+ payload = marshaller.marshal(payload, { :rel => rel, :recipe => recipe }) unless payload.nil? || (payload.kind_of?(String) && payload.empty?)
54
+ args = set_marshalled_payload(method, path, payload, *args)
55
+ args = add_representation_headers(method, path, marshaller, *args)
56
+ end
57
+
58
+ if @acceptable_mediatypes
59
+ unmarshaller = RequestMarshaller.content_type_for(@acceptable_mediatypes)
60
+ args = add_representation_headers(method, path, unmarshaller, *args)
61
+ end
62
+
63
+ response = super(method, path, *args)
64
+ parse_response(response)
65
+ end
66
+
67
+ private
68
+
69
+ # parses the http response.
70
+ # first checks if its a 201, redirecting to the resource location.
71
+ # otherwise check if its a raw request, returning the content itself.
72
+ # finally, tries to parse the content with a mediatype handler or returns the response itself.
73
+ def parse_response(response)
74
+ if response.code == 201
75
+ request = Restfulie.at(response.headers['location'])
76
+ request.accepts(@acceptable_mediatypes) if @acceptable_mediatypes
77
+ request.get!
78
+ elsif @raw
79
+ response
80
+ elsif !response.body.empty?
81
+ representation = RequestMarshaller.content_type_for(response.headers['content-type']) || Restfulie::Common::Representation::Generic.new
82
+ representation.unmarshal(response.body).tap do |u|
83
+ u.extend(ResponseHolder)
84
+ u.response = response
85
+ end
86
+ else
87
+ response.tap do |resp|
88
+ resp.extend(ResponseHolder)
89
+ resp.response = response
90
+ end
91
+ end
92
+ end
93
+
94
+ def get_recipe(*args)
95
+ headers_and_recipe = args.extract_options!
96
+ recipe = headers_and_recipe.delete(:recipe)
97
+ args << headers_and_recipe
98
+ recipe
99
+ end
100
+
101
+ def has_payload?(method, path, *args)
102
+ [:put,:post,:patch].include?(method)
103
+ end
104
+
105
+ def get_payload(method, path, *args)
106
+ args.extract_options! #remove header
107
+ args.shift #payload
108
+ end
109
+
110
+ def set_marshalled_payload(method, path, payload, *args)
111
+ headers = args.extract_options!
112
+ args.tap do |a|
113
+ a.shift #old payload
114
+ a << payload << headers
115
+ end
116
+ end
117
+
118
+ def add_representation_headers(method, path, representation, *args)
119
+ headers = args.extract_options!
120
+ headers = headers.merge(representation.headers[method] || {})
121
+ args << headers
122
+ args
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,10 @@
1
+ module Restfulie
2
+ module Client
3
+ module HTTP
4
+ #=This class includes RequestBuilder module.
5
+ class RequestMarshallerExecutor < RequestHistoryExecutor
6
+ include RequestMarshaller
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,23 @@
1
+ module Restfulie
2
+ module Client
3
+ module HTTP #:nodoc:
4
+ #=Response
5
+ # Default response class
6
+ class Response
7
+ attr_reader :method
8
+ attr_reader :path
9
+ attr_reader :code
10
+ attr_reader :body
11
+ attr_reader :headers
12
+
13
+ def initialize(method, path, code, body, headers)
14
+ @method = method
15
+ @path = path
16
+ @code = code
17
+ @body = body
18
+ @headers = headers
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,67 @@
1
+ module Restfulie
2
+ module Client
3
+ module HTTP #:nodoc:
4
+ #=ResponseHandler
5
+ # You can change instance registering a class according to the code.
6
+ #
7
+ #==Example
8
+ #
9
+ # class RequestExecutor
10
+ # include RequestAdapter
11
+ # def initialize(host)
12
+ # self.host=host
13
+ # end
14
+ # end
15
+ #
16
+ # class FakeResponse < Restfulie::Client::HTTP::Response
17
+ # end
18
+ #
19
+ # Restfulie::Client::HTTP::ResponseHandler.register(201,FakeResponse)
20
+ # @re = Restfulie::Client::HTTP::RequestExecutor.new('http://restfulie.com')
21
+ # puts @re.as('application/atom+xml').get!('/posts').class.to_i #=> FakeResponse
22
+ #
23
+ module ResponseHandler
24
+ @@response_handlers = {}
25
+ ##
26
+ # :singleton-method:
27
+ # Response handlers attribute reader
28
+ # * code: HTTP status code
29
+ def self.handlers(code)
30
+ @@response_handlers[code]
31
+ end
32
+
33
+ ##
34
+ # :singleton-method:
35
+ # Use to register response handlers
36
+ #
37
+ # * <tt>code: HTTP status code</tt>
38
+ # * <tt>response_class: Response class</tt>
39
+ #
40
+ #==Example:
41
+ # class FakeResponse < ::Restfulie::Client::HTTP::Response
42
+ # end
43
+ #
44
+ # Restfulie::Client::HTTP::ResponseHandler.register(200,FakeResponse)
45
+ #
46
+ def self.register(code,response_class)
47
+ @@response_handlers[code] = response_class
48
+ end
49
+
50
+ ##
51
+ # :singleton-method:
52
+ # Request Adapter uses this method to choose response instance
53
+ #
54
+ # *<tt>method: :get,:post,:delete,:head,:put</tt>
55
+ # *<tt>path: '/posts'</tt>
56
+ # *<tt>http_response</tt>
57
+ #
58
+ def self.handle(method, path, http_response)
59
+ response_class = @@response_handlers[http_response.code.to_i] || Response
60
+ headers = {}
61
+ http_response.header.each { |k, v| headers[k] = v }
62
+ response_class.new( method, path, http_response.code.to_i, http_response.body, headers)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,9 @@
1
+ module Restfulie
2
+ module Client
3
+ module HTTP
4
+ module ResponseHolder
5
+ attr_accessor :response
6
+ end
7
+ end
8
+ end
9
+ end
@@ -1,19 +1,22 @@
1
- module Restfulie::Client::Mikyung
2
- end
3
-
4
- %w(
5
- when_condition
6
- then_condition
7
- rest_process_model
8
- concatenator
9
- core
10
- steady_state_walker
11
- languages
12
- ).each do |file|
13
- require "restfulie/client/mikyung/#{file}"
1
+ module Restfulie
2
+ module Client
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'
11
+ end
12
+ end
14
13
  end
15
14
 
16
15
  # Restfulie::Mikyung entry point is based on its core
17
16
  # implementation.
18
- class Restfulie::Mikyung < Restfulie::Client::Mikyung::Core
17
+ module Restfulie
18
+ class Mikyung < Restfulie::Client::Mikyung::Core
19
+ Restfulie::Common::Logger.logger.level = Logger::INFO
20
+ # empty class
21
+ end
19
22
  end