restfulie 0.8.0 → 0.8.1

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 (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