restfulie 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -91,6 +91,7 @@ If you want to build the project and run its tests, remember to install all (cli
91
91
  <pre>
92
92
  gem install rack-conneg
93
93
  gem install responders_backport
94
+ gem install json_pure
94
95
  </pre>
95
96
 
96
97
  <script type="text/javascript">
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'spec/rake/spectask'
6
6
  require 'rake/rdoctask'
7
7
 
8
8
  GEM = "restfulie"
9
- GEM_VERSION = "0.7.0"
9
+ GEM_VERSION = "0.7.1"
10
10
  SUMMARY = "Hypermedia aware resource based library in ruby (client side) and ruby on rails (server side)."
11
11
  AUTHOR = "Guilherme Silveira, Caue Guerra"
12
12
  EMAIL = "guilherme.silveira@caelum.com.br"
@@ -22,6 +22,7 @@ spec = Gem::Specification.new do |s|
22
22
  s.add_dependency("actionpack", [">= 2.3.2"])
23
23
  s.add_dependency("activesupport", [">= 2.3.2"])
24
24
  s.add_dependency("responders_backport", ["~> 0.1.0"])
25
+ s.add_dependency("json_pure", [">= 1.2.4"])
25
26
 
26
27
  s.author = AUTHOR
27
28
  s.email = EMAIL
@@ -22,6 +22,10 @@ module Restfulie::Client#:nodoc
22
22
  end
23
23
 
24
24
  end
25
+ class NilEntryPoint
26
+ include Restfulie::Client::EntryPoint
27
+ # extend self
28
+ end
25
29
 
26
30
  module Base
27
31
  include HTTP::RequestMarshaller
@@ -52,9 +56,11 @@ module Restfulie::Client#:nodoc
52
56
 
53
57
  end
54
58
 
59
+
55
60
  # Shortcut to Restfulie::Client::EntryPoint
56
61
  module Restfulie
57
- include Client::EntryPoint
58
- extend self
62
+ def self.at(uri)
63
+ Client::NilEntryPoint.new.at(uri)
64
+ end
59
65
  end
60
66
 
@@ -1,22 +1,4 @@
1
1
  module Restfulie::Client::HTTP#:nodoc:
2
-
3
- # Offer easy access to Atom link relationships, such as <tt>post.next</tt> for
4
- # <tt>&lt;link rel="next" href="http://resource.entrypoint.com/post/12" type="application/atom+xml" /&gt;</tt> relationships.
5
- module AtomLinkShortcut
6
- def method_missing(method_sym,*args)#:nodoc:
7
- selected_links = links.select{ |l| l.rel == method_sym.to_s }
8
- super if selected_links.empty?
9
- link = (selected_links.size == 1) ? selected_links.first : selected_links
10
-
11
- return link unless link.instance_variable_defined?(:@type)
12
- link.accepts(link.type)
13
-
14
- representation = Restfulie::Client::HTTP::RequestMarshaller.content_type_for(link.type)
15
- return representation.prepare_link_for(link) if representation
16
- link
17
-
18
- end
19
- end
20
2
 
21
3
  # Offers a way to access Atom entries element's in namespaced extensions.
22
4
  module AtomElementShortcut
@@ -64,24 +46,24 @@ module Restfulie::Client::HTTP#:nodoc:
64
46
  end
65
47
  end
66
48
 
67
- # Gives to Atom::Link capabilities to fetch related resources.
68
- module LinkRequestBuilder
69
- include RequestMarshaller
70
- def path#:nodoc:
71
- at(href)
72
- super
73
- end
74
- end
75
-
76
49
  # inject new behavior in rAtom instances to enable easily access to link relationships.
77
50
  ::Atom::Feed.instance_eval {
78
- include AtomLinkShortcut
51
+ include Restfulie::Client::LinkShortcut
79
52
  include AtomElementShortcut
80
53
  }
81
54
  ::Atom::Entry.instance_eval {
82
- include AtomLinkShortcut
55
+ include Restfulie::Client::LinkShortcut
83
56
  include AtomElementShortcut
84
57
  }
85
- ::Atom::Link.instance_eval { include LinkRequestBuilder }
58
+ ::Atom::Link.instance_eval {
59
+ include Restfulie::Client::HTTP::LinkRequestBuilder
60
+ }
86
61
 
87
62
  end
63
+
64
+
65
+ class Atom::Link
66
+ def content_type
67
+ type
68
+ end
69
+ end
@@ -0,0 +1,39 @@
1
+ module Restfulie::Client
2
+
3
+ # Offer easy access to link relationships, such as <tt>post.next</tt> for
4
+ # <tt>&lt;link rel="next" href="http://resource.entrypoint.com/post/12" type="application/atom+xml" /&gt;</tt> relationships.
5
+ module LinkShortcut
6
+ def method_missing(method_sym,*args)#:nodoc:
7
+ selected_links = links.select{ |l| l.rel == method_sym.to_s }
8
+ super if selected_links.empty?
9
+ link = (selected_links.size == 1) ? selected_links.first : selected_links
10
+
11
+ return link unless link.instance_variable_defined?(:@type)
12
+ link.accepts(link.type)
13
+
14
+ representation = Restfulie::Client::HTTP::RequestMarshaller.content_type_for(link.type)
15
+ return representation.prepare_link_for(link) if representation
16
+ link
17
+
18
+ end
19
+ end
20
+
21
+ class Link
22
+
23
+ include Restfulie::Client::HTTP::LinkRequestBuilder
24
+
25
+ def initialize(options = {})
26
+ @options = options
27
+ end
28
+ def href
29
+ @options["href"]
30
+ end
31
+ def rel
32
+ @options["rel"]
33
+ end
34
+ def content_type
35
+ @options["type"]
36
+ end
37
+ end
38
+
39
+ end
@@ -5,7 +5,7 @@ module Restfulie::Client::HTTP
5
5
  attr_accessor :response
6
6
 
7
7
  def respond_to?(symbol)
8
- respond_to_rel?(symbol.to_s) || super(symbol)
8
+ super(symbol) || (super(:links) && respond_to_rel?(symbol.to_s))
9
9
  end
10
10
 
11
11
  private
@@ -21,18 +21,21 @@ module Restfulie::Client::HTTP
21
21
 
22
22
  # accepts a series of media types by default
23
23
  def initialize
24
- @acceptable_mediatypes = "application/atom+xml"
25
24
  end
26
25
 
27
26
  @@representations = {
28
- 'application/atom+xml' => ::Restfulie::Common::Representation::Atom,
29
- 'application/xml' => ::Restfulie::Common::Representation::XmlD
27
+ 'application/atom+xml' => ::Restfulie::Common::Representation::Atom
30
28
  }
31
29
  def self.register_representation(media_type,representation)
32
30
  @@representations[media_type] = representation
33
31
  end
34
32
 
33
+ RequestMarshaller.register_representation('application/xml', ::Restfulie::Common::Representation::XmlD)
34
+ RequestMarshaller.register_representation('text/xml', ::Restfulie::Common::Representation::XmlD)
35
+ RequestMarshaller.register_representation('application/json', ::Restfulie::Common::Representation::Json)
36
+
35
37
  def self.content_type_for(media_type)
38
+ return nil unless media_type
36
39
  content_type = media_type.split(';')[0] # [/(.*?);/, 1]
37
40
  type = @@representations[content_type]
38
41
  type ? type.new : nil
@@ -52,30 +55,39 @@ module Restfulie::Client::HTTP
52
55
 
53
56
  #Executes super and unmarshalls it
54
57
  def request!(method, path, *args)
55
- representation = do_conneg_and_choose_representation(method, path, *args)
56
- if representation
57
- if has_payload?(method, path, *args)
58
- payload = get_payload(method, path, *args)
59
- rel = self.respond_to?(:rel) ? self.rel : ""
60
- payload = representation.marshal(payload, rel)
61
- args = set_marshalled_payload(method, path, payload, *args)
62
- end
63
- args = add_representation_headers(method, path, representation, *args)
58
+
59
+ if has_payload?(method, path, *args)
60
+ payload = get_payload(method, path, *args)
61
+ rel = self.respond_to?(:rel) ? self.rel : ""
62
+ type = headers['Content-Type']
63
+ raise Restfulie::Common::Error::RestfulieError, "Missing content type related to the data to be submitted" unless type
64
+ marshaller = RequestMarshaller.content_type_for(type)
65
+ payload = marshaller.marshal(payload, rel)
66
+ args = set_marshalled_payload(method, path, payload, *args)
67
+ args = add_representation_headers(method, path, marshaller, *args)
68
+ end
69
+
70
+ if @acceptable_mediatypes
71
+ unmarshaller = RequestMarshaller.content_type_for(@acceptable_mediatypes)
72
+ args = add_representation_headers(method, path, unmarshaller, *args)
64
73
  end
74
+
65
75
  response = super(method, path, *args)
66
- parse_response(response, representation)
76
+ parse_response(response)
67
77
  end
68
78
 
69
79
  private
70
80
 
81
+
71
82
  # parses the http response.
72
83
  # first checks if its a 201, redirecting to the resource location.
73
84
  # otherwise check if its a raw request, returning the content itself.
74
85
  # finally, tries to parse the content with a mediatype handler or returns the response itself.
75
- def parse_response(response, representation)
86
+ def parse_response(response)
76
87
  if response.code == 201
77
- location = response.headers['location']
78
- Restfulie.at(location).accepts(@acceptable_mediatypes).get!
88
+ request = Restfulie.at(response.headers['location'])
89
+ request.accepts(@acceptable_mediatypes) if @acceptable_mediatypes
90
+ request.get!
79
91
  elsif @raw
80
92
  response
81
93
  elsif !response.body.empty?
@@ -89,16 +101,6 @@ module Restfulie::Client::HTTP
89
101
  end
90
102
  end
91
103
 
92
- def content_type_for(media_type)
93
- @@representations[media_type.split(';')[0]].new # [/(.*?);/, 1]
94
- end
95
-
96
- def do_conneg_and_choose_representation(method, path, *args)
97
- #TODO make a request to server (conneg)
98
- representation = @default_representation || @default_representation = @@representations['application/atom+xml']
99
- representation.new
100
- end
101
-
102
104
  def has_payload?(method, path, *args)
103
105
  [:put,:post].include?(method)
104
106
  end
@@ -117,12 +119,23 @@ module Restfulie::Client::HTTP
117
119
 
118
120
  def add_representation_headers(method, path, representation, *args)
119
121
  headers = args.extract_options!
120
- args << representation.headers[method]
122
+ headers = headers.merge(representation.headers[method] || {})
123
+ args << headers
121
124
  args
122
125
  end
123
126
 
124
127
  end
125
128
 
129
+ # Gives to Link capabilities to fetch related resources.
130
+ module LinkRequestBuilder
131
+ include Restfulie::Client::HTTP::RequestMarshaller
132
+ def path#:nodoc:
133
+ at(href)
134
+ as(content_type) if respond_to?(:content_type) && content_type
135
+ super
136
+ end
137
+ end
138
+
126
139
  #=This class includes RequestBuilder module.
127
140
  class RequestMarshallerExecutor
128
141
  include RequestMarshaller
@@ -144,4 +157,3 @@ module Restfulie::Client::HTTP
144
157
  end
145
158
 
146
159
  end
147
-
@@ -0,0 +1,4 @@
1
+ ::Hash.instance_eval {
2
+ include Restfulie::Client::LinkShortcut
3
+ }
4
+
@@ -6,7 +6,9 @@ end
6
6
  adapter
7
7
  cache
8
8
  marshal
9
+ link
9
10
  atom_ext
11
+ xml
10
12
  ).each do |file|
11
13
  require "restfulie/client/http/#{file}"
12
14
  end
@@ -34,14 +34,6 @@ module Restfulie::Common::Representation
34
34
  def prepare_link_for(link)
35
35
  link
36
36
  end
37
-
38
- def prepare_link_for(link)
39
- link
40
- end
41
-
42
- def prepare_link_for(link)
43
- link
44
- end
45
37
  end
46
38
 
47
39
  end
@@ -0,0 +1,25 @@
1
+ module Restfulie::Common::Representation
2
+
3
+ # Implements the interface for marshal Xml media type requests (application/xml)
4
+ class Json
5
+
6
+ cattr_reader :media_type_name
7
+ @@media_type_name = 'application/json'
8
+
9
+ cattr_reader :headers
10
+ @@headers = {
11
+ :post => { 'Content-Type' => media_type_name }
12
+ }
13
+
14
+ def unmarshal(string)
15
+ JSON.parse(string)
16
+ end
17
+
18
+ def marshal(entity, rel)
19
+ return entity if entity.kind_of? String
20
+ entity.to_json
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -1,7 +1,32 @@
1
+ class Hash
2
+ def links(*args)
3
+ links = fetch("link", [])
4
+ links = [links] unless links.kind_of? Array
5
+ links = [] unless links
6
+ links = links.map do |l|
7
+ Restfulie::Client::Link.new(l)
8
+ end
9
+ if args.empty?
10
+ links
11
+ else
12
+ found = links.find do |link|
13
+ link.rel == args[0].to_s
14
+ end
15
+ found
16
+ end
17
+ end
18
+ def method_missing(sym, *args)
19
+ self[sym.to_s].nil? ? super(sym, args) : self[sym.to_s]
20
+ end
21
+ def respond_to?(sym)
22
+ include?(sym.to_s) || super(sym)
23
+ end
24
+ end
25
+
1
26
  module Restfulie::Common::Representation
27
+
2
28
  # Implements the interface for marshal Xml media type requests (application/xml)
3
29
  class XmlD
4
-
5
30
  cattr_reader :media_type_name
6
31
  @@media_type_name = 'application/xml'
7
32
 
@@ -11,7 +36,7 @@ module Restfulie::Common::Representation
11
36
  }
12
37
 
13
38
  def unmarshal(string)
14
- raise "should never be invoked, xml to ruby objects should be handled by rails itself"
39
+ Hash.from_xml(string).values.first
15
40
  end
16
41
 
17
42
  def marshal(string, rel)
@@ -21,4 +46,3 @@ module Restfulie::Common::Representation
21
46
  end
22
47
 
23
48
  end
24
-
@@ -3,6 +3,8 @@ module Restfulie::Common::Representation; end
3
3
 
4
4
  %w(
5
5
  atom
6
+ generic
7
+ json
6
8
  xml
7
9
  ).each do |file|
8
10
  require File.join(File.dirname(__FILE__), 'representation', file)
@@ -42,21 +42,21 @@ end
42
42
  # extensions are handled by the Rack stack
43
43
  #
44
44
  # TODO Change this when porting this code to Rails 3
45
- ::ActionController::ParamsParser.class_eval do
46
- def call(env)
47
- begin
48
- if params = parse_formatted_parameters(env)
49
- env["action_controller.request.request_parameters"] = params
50
- else
51
- if env["CONTENT_LENGTH"] && (env["CONTENT_LENGTH"] != "0")
52
- return [415, {'Content-Type' => 'text/html'}, "<html><body><h1>415 Unsupported Media Type</h1></body></html>"]
53
- end
54
- end
55
- rescue
56
- return [400, {'Content-Type' => 'text/html'}, "<html><body><h1>400 Bad Request</h1></body></html>"]
57
- end
45
+ #::ActionController::ParamsParser.class_eval do
46
+ #def call(env)
47
+ #begin
48
+ #if params = parse_formatted_parameters(env)
49
+ #env["action_controller.request.request_parameters"] = params
50
+ #else
51
+ #if env["CONTENT_LENGTH"] && (env["CONTENT_LENGTH"] != "0")
52
+ #return [415, {'Content-Type' => 'text/html'}, "<html><body><h1>415 Unsupported Media Type</h1></body></html>"]
53
+ #end
54
+ #end
55
+ #rescue
56
+ #return [400, {'Content-Type' => 'text/html'}, "<html><body><h1>400 Bad Request</h1></body></html>"]
57
+ #end
58
58
 
59
- @app.call(env)
60
- end
61
- end
59
+ #@app.call(env)
60
+ #end
61
+ #end
62
62
 
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 7
8
- - 0
9
- version: 0.7.0
8
+ - 1
9
+ version: 0.7.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Guilherme Silveira, Caue Guerra
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-04-12 00:00:00 -03:00
17
+ date: 2010-04-21 00:00:00 -03:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -59,6 +59,20 @@ dependencies:
59
59
  version: 0.1.0
60
60
  type: :runtime
61
61
  version_requirements: *id003
62
+ - !ruby/object:Gem::Dependency
63
+ name: json_pure
64
+ prerelease: false
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ segments:
70
+ - 1
71
+ - 2
72
+ - 4
73
+ version: 1.2.4
74
+ type: :runtime
75
+ version_requirements: *id004
62
76
  description:
63
77
  email: guilherme.silveira@caelum.com.br
64
78
  executables: []
@@ -74,7 +88,9 @@ files:
74
88
  - lib/restfulie/client/http/atom_ext.rb
75
89
  - lib/restfulie/client/http/cache.rb
76
90
  - lib/restfulie/client/http/error.rb
91
+ - lib/restfulie/client/http/link.rb
77
92
  - lib/restfulie/client/http/marshal.rb
93
+ - lib/restfulie/client/http/xml.rb
78
94
  - lib/restfulie/client/http.rb
79
95
  - lib/restfulie/client.rb
80
96
  - lib/restfulie/common/builder/builder_base.rb
@@ -94,6 +110,7 @@ files:
94
110
  - lib/restfulie/common/logger.rb
95
111
  - lib/restfulie/common/representation/atom.rb
96
112
  - lib/restfulie/common/representation/generic.rb
113
+ - lib/restfulie/common/representation/json.rb
97
114
  - lib/restfulie/common/representation/xml.rb
98
115
  - lib/restfulie/common/representation.rb
99
116
  - lib/restfulie/common.rb