roar 0.10.0 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,7 @@
1
+ ## 0.10.1
2
+
3
+ * Adding the Coercion feature.
4
+
1
5
  ## 0.10.0
2
6
 
3
7
  * Requiring representable-0.1.3.
data/Gemfile CHANGED
@@ -4,3 +4,9 @@ source "http://rubygems.org"
4
4
  gemspec
5
5
 
6
6
  #gem "representable", :path => "../representable"
7
+
8
+ gem 'faraday'
9
+
10
+ group :test do
11
+ gem 'turn'
12
+ end
@@ -134,7 +134,7 @@ module OrderRepresenter
134
134
  property :id
135
135
  property :client_id
136
136
 
137
- collection :articles, :as => Article
137
+ collection :articles, :class => Article
138
138
 
139
139
  link :self do
140
140
  order_url(represented)
@@ -279,11 +279,11 @@ order.post(order.links[:self])
279
279
 
280
280
  This was dead simple since representations can be composed of different documents in Roar.
281
281
 
282
+ h2. More Features
282
283
 
283
- h2. Current Status
284
+ Be sure to check out the bundled features.
284
285
 
285
- Please note that Roar is still in conception, the API might change as well as concepts do.
286
-
286
+ # *Coercion* transforms values to typed objects when parsing a document. Uses virtus.
287
287
 
288
288
  h2. What is REST about?
289
289
 
@@ -296,3 +296,4 @@ Making that system RESTful basically means
296
296
  h2. Support
297
297
 
298
298
  Questions? Need help? Free 1st Level Support on irc.freenode.org#roar !
299
+ We also have a "mailing list":https://groups.google.com/forum/?fromgroups#!forum/roar-talk, yiha!
@@ -0,0 +1,20 @@
1
+ require 'virtus'
2
+ require 'representable/coercion'
3
+
4
+ module Roar::Representer::Feature
5
+ # Use the +:type+ option to specify the conversion type.
6
+ # class ImmigrantSong
7
+ # include Roar::Representer::JSON
8
+ # include Roar::Representer::Feature::Coercion
9
+ #
10
+ # property :composed_at, :type => DateTime, :default => "May 12th, 2012"
11
+ # end
12
+ module Coercion
13
+ def self.included(base)
14
+ base.class_eval do
15
+ include Virtus
16
+ extend Representable::Coercion::ClassMethods
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,53 +1,75 @@
1
- require 'roar/representer/feature/transport'
1
+ require 'roar/representer/transport/net_http'
2
+ require 'roar/representer/transport/faraday' # Do not require here
2
3
 
3
4
  module Roar
4
5
  # Gives HTTP-power to representers. They can serialize, send, process and deserialize HTTP-requests.
5
6
  module Representer
6
7
  module Feature
7
8
  module HttpVerbs
8
- def self.included(base)
9
- base.extend ClassMethods
9
+
10
+ class << self
11
+ attr_accessor :transport_engine
12
+
13
+ def included(base)
14
+ base.extend ClassMethods
15
+ end
10
16
  end
17
+ self.transport_engine = ::Roar::Representer::Transport::NetHTTP
11
18
 
12
-
19
+
13
20
  module ClassMethods
14
- # GETs +url+ with +format+ and returns deserialized representer.
15
- def get(url, format)
16
- document = http.get_uri(url, format).body
17
- deserialize(document)
18
- end
19
-
20
- def http
21
- Transport
21
+ # GETs +url+ with +format+ and returns deserialized represented object.
22
+ def get(*args)
23
+ new.get(*args)
22
24
  end
23
25
  end
24
26
 
25
27
 
28
+ attr_writer :transport_engine
29
+ def transport_engine
30
+ @transport_engine || HttpVerbs.transport_engine
31
+ end
32
+
26
33
  # Serializes the object, POSTs it to +url+ with +format+, deserializes the returned document
27
34
  # and updates properties accordingly.
28
35
  def post(url, format)
29
- # DISCUSS: what if a redirect happens here?
30
- document = http.post_uri(url, serialize, format).body
31
- deserialize(document)
36
+ response = http.post_uri(url, serialize, format)
37
+ handle_response(response)
32
38
  end
33
39
 
34
40
  # GETs +url+ with +format+, deserializes the returned document and updates properties accordingly.
35
41
  def get(url, format)
36
- document = http.get_uri(url, format).body
37
- deserialize(document)
42
+ response = http.get_uri(url, format)
43
+ handle_response(response)
38
44
  end
39
45
 
40
46
  # Serializes the object, PUTs it to +url+ with +format+, deserializes the returned document
41
47
  # and updates properties accordingly.
42
48
  def put(url, format)
43
- document = http.put_uri(url, serialize, format).body
44
- deserialize(document)
49
+ response = http.put_uri(url, serialize, format)
50
+ handle_response(response)
51
+ self
45
52
  end
46
53
 
47
- # TODO: implement delete, patch.
54
+ def patch(url, format)
55
+ response = http.patch_uri(url, serialize, format)
56
+ handle_response(response)
57
+ self
58
+ end
59
+
60
+ def delete(url, format)
61
+ http.delete_uri(url, format)
62
+ self
63
+ end
64
+
48
65
  private
66
+ def handle_response(response)
67
+ document = response.body
68
+ deserialize(document)
69
+ end
70
+
49
71
  def http
50
- Transport # DISCUSS: might be refering to separate http object soon.
72
+ transport_engine.new
51
73
  end
52
74
  end
53
75
  end
@@ -108,9 +108,15 @@ module Roar
108
108
 
109
109
  class LinksDefinition < Representable::Definition
110
110
  # TODO: hide rel2block in interface.
111
+ attr_writer :rel2block
112
+
111
113
  def rel2block
112
114
  @rel2block ||= []
113
115
  end
116
+
117
+ def clone
118
+ super.tap { |d| d.rel2block = rel2block.clone }
119
+ end
114
120
  end
115
121
 
116
122
 
@@ -42,7 +42,7 @@ module Roar::Representer
42
42
 
43
43
  def uncompile_fragment(bin, doc)
44
44
  return super unless bin.definition.options[:embedded]
45
- super(bin, doc["_embedded"])
45
+ super(bin, doc["_embedded"] || {})
46
46
  end
47
47
  end
48
48
 
@@ -90,6 +90,7 @@ module Roar::Representer
90
90
  end
91
91
 
92
92
  def from_hash(json, *)
93
+ json ||= {} # since we override #from_hash we're responsible for this.
93
94
  json.each do |k, v|
94
95
  self << Feature::Hypermedia::Hyperlink.new(v.merge :rel => k)
95
96
  end
@@ -0,0 +1,56 @@
1
+ begin
2
+ require 'faraday'
3
+ rescue LoadError
4
+ puts 'You must add faraday as a dependency to use the FaradayTransport'
5
+ end
6
+ require 'logger'
7
+
8
+ module Roar
9
+ module Representer
10
+ module Transport
11
+ # Advanced implementation of the HTTP verbs with the Faraday HTTP library
12
+ # (which can, in turn, use adapters based on Net::HTTP or libcurl)
13
+ #
14
+ # Depending on how the Faraday middleware stack is configured, this
15
+ # Transport can support features such as HTTP error code handling,
16
+ # redirects, etc.
17
+ #
18
+ # @see http://rubydoc.info/gems/faraday/file/README.md Faraday README
19
+ class Faraday
20
+
21
+ def get_uri(uri, as)
22
+ build_connection(uri, as).get
23
+ end
24
+
25
+ def post_uri(uri, body, as)
26
+ build_connection(uri, as).post
27
+ end
28
+
29
+ def put_uri(uri, body, as)
30
+ build_connection(uri, as).put
31
+ end
32
+
33
+ def patch_uri(uri, body, as)
34
+ build_connection(uri, as).patch
35
+ end
36
+
37
+ def delete_uri(uri, as)
38
+ build_connection(uri, as).delete
39
+ end
40
+
41
+ private
42
+
43
+ def build_connection(uri, as)
44
+ ::Faraday::Connection.new(
45
+ :url => uri,
46
+ :headers => { :accept => as }
47
+ ) do |builder|
48
+ # builder.use Faraday::Response::Logger, Logger.new('faraday.log')
49
+ builder.use ::Faraday::Response::RaiseError
50
+ builder.adapter ::Faraday.default_adapter
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,44 @@
1
+ require "net/http"
2
+ require "uri"
3
+
4
+ module Roar
5
+ module Representer
6
+ # Implements the (HTTP) transport interface with Net::HTTP.
7
+ module Transport
8
+ # Definitions: every call returns a Response object responding to #body.
9
+ class NetHTTP
10
+ # TODO: generically handle return codes.
11
+ def get_uri(uri, as)
12
+ do_request(Net::HTTP::Get, uri, as)
13
+ end
14
+
15
+ def post_uri(uri, body, as)
16
+ do_request(Net::HTTP::Post, uri, as)
17
+ end
18
+
19
+ def put_uri(uri, body, as)
20
+ do_request(Net::HTTP::Put, uri, as)
21
+ end
22
+
23
+ def patch_uri(uri, body, as)
24
+ do_request(Net::HTTP::Patch, uri, as)
25
+ end
26
+
27
+ def delete_uri(uri, as)
28
+ do_request(Net::HTTP::Delete, uri, as)
29
+ end
30
+
31
+ private
32
+ def do_request(what, uri, as, body="")
33
+ # DISCUSS: can this be made easier?
34
+ uri = URI.parse(uri)
35
+ http = Net::HTTP.new(uri.host, uri.port)
36
+ req = what.new(uri.request_uri)
37
+ req.content_type = as
38
+ req.body = body if body
39
+ http.request(req)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,3 +1,3 @@
1
1
  module Roar
2
- VERSION = "0.10.0"
2
+ VERSION = "0.10.1"
3
3
  end
@@ -19,11 +19,13 @@ Gem::Specification.new do |s|
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
20
  s.require_paths = ["lib"]
21
21
 
22
- s.add_runtime_dependency "representable", ">= 1.1.3"
22
+ s.add_runtime_dependency "representable", ">= 1.1.7"
23
23
 
24
24
  s.add_development_dependency "rake"
25
25
  s.add_development_dependency "test_xml"
26
26
  s.add_development_dependency "minitest", ">= 2.8.1"
27
- s.add_development_dependency "sinatra", "~> 1.2.6"
27
+ s.add_development_dependency "sinatra", "~> 1.3.2"
28
28
  s.add_development_dependency "sham_rack", "~> 1.3.0"
29
+ s.add_development_dependency "turn"
30
+ s.add_development_dependency "virtus"
29
31
  end
@@ -0,0 +1,18 @@
1
+ require 'test_helper'
2
+ require 'roar/representer/feature/coercion'
3
+
4
+ class CoercionFeatureTest < MiniTest::Spec
5
+ describe "Coercion" do
6
+ class ImmigrantSong
7
+ include Roar::Representer::JSON
8
+ include Roar::Representer::Feature::Coercion
9
+
10
+ property :composed_at, :type => DateTime, :default => "May 12th, 2012"
11
+ end
12
+
13
+ it "coerces into the provided type" do
14
+ song = ImmigrantSong.new.from_json("{\"composed_at\":\"November 18th, 1983\"}")
15
+ assert_equal DateTime.parse("Fri, 18 Nov 1983 00:00:00 +0000"), song.composed_at
16
+ end
17
+ end
18
+ end
@@ -1,41 +1,60 @@
1
1
  require "bundler/setup"
2
2
  require 'sinatra/base'
3
-
4
- class FakeServer < Sinatra::Base
5
- get "/method" do
6
- "<method>get</method>"
3
+
4
+ class FakeServer < Sinatra::Base
5
+
6
+ set :raise_errors, false
7
+
8
+ get "/method" do
9
+ "<method>get</method>"
7
10
  end
8
-
9
- post "/method" do
10
- "<method>post</method>"
11
+
12
+ post "/method" do
13
+ "<method>post</method>"
11
14
  end
12
15
 
13
- put "/method" do
14
- "<method>put</method>"
16
+ put "/method" do
17
+ "<method>put</method>"
15
18
  end
16
19
 
17
- delete "/method" do
18
- "<method>delete</method>"
20
+ delete "/method" do
21
+ "<method>delete</method>"
19
22
  end
20
23
 
21
- #patch "/method" do
22
- # "<method>patch</method>"
23
- #end
24
-
24
+ patch "/method" do
25
+ "<method>patch</method>"
26
+ end
27
+
28
+ get '/deliberate-error' do
29
+ raise 'this error was deliberate'
30
+ end
31
+
25
32
  post "/bands" do
26
33
  #if request.content_type =~ /xml/
27
- '{"label": "n/a", "name": "Strung Out", "links": [{"href":"http://search", "rel": "search"}, {"href":"http://band/strungout", "rel": "self"}]}'
34
+ body '{"label": "n/a", "name": "Strung Out", "links": [{"href":"http://search", "rel": "search"}, {"href":"http://band/strungout", "rel": "self"}]}'
35
+ status 201
28
36
  end
29
37
 
30
38
  put "/bands/strungout" do
39
+ # DISCUSS: as long as we don't agree on what to return in PUT/PATCH, let's return an updated document.
31
40
  {:name => "Strung Out", :label => "Fat Wreck"}.to_json
41
+ #status 204
32
42
  end
33
-
43
+
44
+ patch '/bands/strungout' do
45
+ # DISCUSS: as long as we don't agree on what to return in PUT/PATCH, let's return an updated document.
46
+ {:name => 'Strung Out', :label => 'Fat Mike'}.to_json
47
+ #status 204
48
+ end
49
+
34
50
  get "/bands/slayer" do
35
51
  {:name => "Slayer", :label => "Canadian Maple"}.to_json
36
52
  end
37
-
38
-
53
+
54
+ delete '/banks/metallica' do
55
+ status 204
56
+ end
57
+
39
58
  require './test/order_representers'
40
59
  JSON::Order.class_eval do
41
60
  def items_url
@@ -69,6 +88,6 @@ class FakeServer < Sinatra::Base
69
88
  get "/orders/1" do
70
89
  JSON::Order.new(:client_id => 1, :items => [JSON::Item.new(:article_id => "666-S", :amount => 1)]).serialize
71
90
  end
72
-
73
- end
74
-
91
+
92
+ end
93
+
@@ -0,0 +1,69 @@
1
+ require 'test_helper'
2
+ require 'roar/representer/transport/faraday'
3
+
4
+ class FaradayHttpTransportTest < MiniTest::Spec
5
+ describe 'FaradayHttpTransport' do
6
+ before do
7
+ @transport = Roar::Representer::Transport::Faraday.new
8
+ end
9
+
10
+ it "#get_uri returns response" do
11
+ assert_equal "<method>get</method>", @transport.get_uri("http://roar.example.com/method", "application/xml").body
12
+ end
13
+
14
+ it "#post_uri returns response" do
15
+ assert_equal "<method>post</method>", @transport.post_uri("http://roar.example.com/method", "booty", "application/xml").body
16
+ end
17
+
18
+ it "#put_uri returns response" do
19
+ assert_equal "<method>put</method>", @transport.put_uri("http://roar.example.com/method", "booty", "application/xml").body
20
+ end
21
+
22
+ it "#delete_uri returns response" do
23
+ assert_equal "<method>delete</method>", @transport.delete_uri("http://roar.example.com/method", "application/xml").body
24
+ end
25
+
26
+ it "#patch_uri returns response" do
27
+ assert_equal "<method>patch</method>", @transport.patch_uri("http://roar.example.com/method", "booty", "application/xml").body
28
+ end
29
+
30
+ describe 'non-existent resource' do
31
+ before do
32
+ @not_found_url = 'http://roar.example.com/missing-resource'
33
+ end
34
+
35
+ it '#get_uri raises a ResourceNotFound error' do
36
+ assert_raises(Faraday::Error::ResourceNotFound) do
37
+ @transport.get_uri(@not_found_url, "application/xml").body
38
+ end
39
+ end
40
+
41
+ it '#post_uri raises a ResourceNotFound error' do
42
+ assert_raises(Faraday::Error::ResourceNotFound) do
43
+ @transport.post_uri(@not_found_url, 'crisis', "application/xml").body
44
+ end
45
+ end
46
+
47
+ it '#post_uri raises a ResourceNotFound error' do
48
+ assert_raises(Faraday::Error::ResourceNotFound) do
49
+ @transport.post_uri(@not_found_url, 'crisis', "application/xml").body
50
+ end
51
+ end
52
+
53
+ it '#delete_uri raises a ResourceNotFound error' do
54
+ assert_raises(Faraday::Error::ResourceNotFound) do
55
+ @transport.delete_uri(@not_found_url, "application/xml").body
56
+ end
57
+ end
58
+ end
59
+
60
+ describe 'server errors (500 Internal Server Error)' do
61
+ it '#get_uri raises a ClientError' do
62
+ assert_raises(Faraday::Error::ClientError) do
63
+ @transport.get_uri('http://roar.example.com/deliberate-error', "application/xml").body
64
+ end
65
+ end
66
+ end
67
+
68
+ end
69
+ end
@@ -65,5 +65,12 @@ class HalJsonTest < MiniTest::Spec
65
65
  assert_equal "http://items/Coffee", @order.items.first.links[:self].href
66
66
  assert_equal "http://orders/2", @order.links[:self].href
67
67
  end
68
+
69
+ it "doesn't require _links and _embedded to be present" do
70
+ @order.from_json("{\"id\":2}")
71
+ assert_equal 2, @order.id
72
+ assert_equal [], @order.items
73
+ assert_equal [], @order.links
74
+ end
68
75
  end
69
76
  end
@@ -23,21 +23,49 @@ class HttpVerbsTest < MiniTest::Spec
23
23
  @band.extend(Roar::Representer::Feature::HttpVerbs)
24
24
  end
25
25
 
26
+ describe "transport_engine" do
27
+ before do
28
+ @http_verbs = Roar::Representer::Feature::HttpVerbs
29
+ @net_http = Roar::Representer::Transport::NetHTTP
30
+ end
31
+
32
+ it "has a default set in the transport module level" do
33
+ assert_equal @net_http, @band.transport_engine
34
+ end
35
+
36
+ it "allows changing on instance level" do
37
+ @band.transport_engine = :soap
38
+ assert_equal @net_http, @http_verbs.transport_engine
39
+ assert_equal :soap, @band.transport_engine
40
+ end
41
+ end
42
+
43
+
26
44
  describe "HttpVerbs.get" do
27
45
  it "returns instance from incoming representation" do
28
- # let's pretend the user wants Roar class methods.
29
- @Band = Class.new do
30
- include Roar::Representer::JSON
31
- include BandRepresenter
32
- include Roar::Representer::Feature::HttpVerbs
33
- attr_accessor :name, :label
46
+ band = @band.get("http://roar.example.com/bands/slayer", "application/json")
47
+ assert_equal "Slayer", band.name
48
+ assert_equal "Canadian Maple", band.label
49
+ end
50
+
51
+ # FIXME: move to faraday test.
52
+ describe 'a non-existent resource' do
53
+ it 'handles HTTP errors and raises a ResourceNotFound error with FaradayHttpTransport' do
54
+ @band.transport_engine = Roar::Representer::Transport::Faraday
55
+ assert_raises(::Faraday::Error::ResourceNotFound) do
56
+ @band.get('http://roar.example.com/bands/anthrax', "application/json")
57
+ end
58
+ end
59
+
60
+ it 'performs no HTTP error handling with NetHttpTransport' do
61
+ @band.transport_engine = Roar::Representer::Transport::NetHTTP
62
+ assert_raises(JSON::ParserError) do
63
+ @band.get('http://roar.example.com/bands/anthrax', "application/json")
64
+ end
34
65
  end
35
- @band = @Band.get("http://roar.example.com/bands/slayer", "application/json")
36
- assert_equal "Slayer", @band.name
37
- assert_equal "Canadian Maple", @band.label
38
66
  end
39
67
  end
40
-
68
+
41
69
  describe "#get" do
42
70
  it "updates instance with incoming representation" do
43
71
  @band.get("http://roar.example.com/bands/slayer", "application/json")
@@ -67,12 +95,21 @@ class HttpVerbsTest < MiniTest::Spec
67
95
  end
68
96
  end
69
97
 
70
- describe "#delete" do
71
-
72
- end
73
-
74
98
  describe "#patch" do
75
-
99
+ it 'does something' do
100
+ @band.label = 'Fat Mike'
101
+ @band.patch("http://roar.example.com/bands/strungout", "application/xml")
102
+ assert_equal 'Fat Mike', @band.label
103
+ end
76
104
  end
105
+
106
+ describe "#delete" do
107
+ it 'does something' do
108
+ @band.delete("http://roar.example.com/bands/metallica", "application/xml")
109
+ end
110
+ end
111
+
112
+ # HEAD, OPTIONs?
113
+
77
114
  end
78
115
  end
@@ -11,51 +11,51 @@ class HypermediaTest
11
11
  include Roar::Representer::Feature::Hypermedia
12
12
  end
13
13
  end
14
-
14
+
15
15
  it "accepts rel symbol, only" do
16
16
  @mod.class_eval do
17
17
  link :self do
18
18
  "http://self"
19
19
  end
20
20
  end
21
-
21
+
22
22
  assert_equal "{\"links\":[{\"rel\":\"self\",\"href\":\"http://self\"}]}", Object.new.extend(@mod).to_json
23
23
  end
24
-
24
+
25
25
  it "accepts any options" do
26
26
  @mod.class_eval do
27
27
  link :rel => :self, :title => "Hey, @myabc" do
28
28
  "http://self"
29
29
  end
30
30
  end
31
-
31
+
32
32
  assert_equal "{\"links\":[{\"rel\":\"self\",\"href\":\"http://self\",\"title\":\"Hey, @myabc\"}]}", Object.new.extend(@mod).to_json
33
33
  end
34
34
  end
35
-
36
-
35
+
36
+
37
37
  before do
38
38
  @bookmarks = Class.new do
39
39
  include AttributesContructor
40
40
  include Roar::Representer::XML
41
41
  include Roar::Representer::Feature::Hypermedia
42
-
42
+
43
43
  self.representation_wrap = "bookmarks"
44
44
  end
45
-
45
+
46
46
  @bookmarks_with_links = Class.new(@bookmarks)
47
47
  @bookmarks_with_links.class_eval do
48
48
  self.representation_wrap = "bookmarks"
49
-
49
+
50
50
  property :id
51
51
  link :self do "http://bookmarks" end
52
52
  link :all do "http://bookmarks/all" end
53
-
53
+
54
54
  attr_accessor :id, :self, :all
55
55
  end
56
56
  end
57
-
58
-
57
+
58
+
59
59
  describe "#to_xml" do
60
60
  it "sets up links and renders <link> correctly in XML" do
61
61
  assert_xml_equal '<bookmarks>
@@ -64,31 +64,31 @@ class HypermediaTest
64
64
  <link rel="all" href="http://bookmarks/all"/>
65
65
  </bookmarks>', @bookmarks_with_links.new(:id => 1).to_xml
66
66
  end
67
-
67
+
68
68
  it "still works even if there are no links defined" do
69
69
  assert_xml_equal '<bookmarks/>', @bookmarks.new.to_xml
70
70
  end
71
-
71
+
72
72
  it "doesn't render links with :links => false" do
73
73
  assert_xml_equal '<bookmarks>
74
74
  <id>1</id>
75
- </bookmarks>',
75
+ </bookmarks>',
76
76
  @bookmarks_with_links.new(:id => 1).to_xml(:links => false)
77
77
  end
78
78
  end
79
-
79
+
80
80
  describe "#to_json" do
81
81
  class Note
82
82
  include Roar::Representer::JSON
83
83
  include Roar::Representer::Feature::Hypermedia
84
84
  link(:self) { "http://me" }
85
85
  end
86
-
86
+
87
87
  it "works twice" do
88
88
  note = Note.new
89
89
  assert_equal note.to_json, note.to_json
90
90
  end
91
-
91
+
92
92
  it "sets up links even when nested" do
93
93
  class Page
94
94
  include AttributesContructor
@@ -96,13 +96,13 @@ class HypermediaTest
96
96
  property :note, :class => Note
97
97
  attr_accessor :note
98
98
  end
99
-
99
+
100
100
  assert_equal "{\"note\":{\"links\":[{\"rel\":\"self\",\"href\":\"http://me\"}]}}", Page.new(:note => Note.new).to_json
101
101
  end
102
102
  end
103
-
104
-
105
-
103
+
104
+
105
+
106
106
  describe "#from_xml" do
107
107
  it "extracts links from document" do
108
108
  doc = @bookmarks_with_links.from_xml(%{
@@ -110,25 +110,25 @@ class HypermediaTest
110
110
  <link rel="self" href="http://bookmarks">
111
111
  </bookmarks>
112
112
  })
113
-
113
+
114
114
  assert_kind_of Roar::Representer::Feature::Hypermedia::LinkCollection, doc.links
115
115
  assert_equal 1, doc.links.size
116
- assert_equal(["self", "http://bookmarks"], [doc.links.first.rel, doc.links.first.href])
116
+ assert_equal(["self", "http://bookmarks"], [doc.links.first.rel, doc.links.first.href])
117
117
  end
118
-
118
+
119
119
  it "sets up an empty link list if no links found in the document" do
120
120
  assert_equal [], @bookmarks_with_links.from_xml(%{<bookmarks/>}).links
121
121
  end
122
122
  end
123
-
124
-
123
+
124
+
125
125
  describe "#links" do
126
126
  before do
127
127
  @set = @bookmarks.new
128
128
  hyper = Roar::Representer::Feature::Hypermedia::Hyperlink
129
-
129
+
130
130
  @set.links = [
131
- {:rel => "self", :href => "http://self"},
131
+ {:rel => "self", :href => "http://self"},
132
132
  {:rel => "next", :href => "http://next"}].collect do |config|
133
133
  link = hyper.new
134
134
  link.rel = config[:rel]
@@ -136,14 +136,14 @@ class HypermediaTest
136
136
  link
137
137
  end
138
138
  end
139
-
139
+
140
140
  describe "#links=" do
141
141
  it "wraps links in a LinkCollection" do
142
142
  assert_kind_of Roar::Representer::Feature::Hypermedia::LinkCollection, @set.links
143
143
  assert_equal 2, @set.links.size
144
144
  end
145
145
  end
146
-
146
+
147
147
  describe "#link[]" do
148
148
  it "returns link object" do
149
149
  assert_equal "http://self", @set.links["self"].href
@@ -152,23 +152,23 @@ class HypermediaTest
152
152
  assert_equal nil, @set.links[:prev]
153
153
  end
154
154
  end
155
-
155
+
156
156
  it "returns an empty list when no links present" do
157
157
  assert_equal Roar::Representer::Feature::Hypermedia::LinkCollection.new, @bookmarks.new.links
158
158
  end
159
159
  end
160
-
161
-
160
+
161
+
162
162
  describe "#find_links_definition" do
163
163
  it "returns Definition if links are present" do
164
164
  @bookmarks.class_eval do
165
165
  property :id
166
166
  link :self
167
167
  end
168
-
168
+
169
169
  assert_equal "links", @bookmarks.find_links_definition.name
170
170
  end
171
-
171
+
172
172
  it "returns nil if no links defined" do
173
173
  assert_equal nil, @bookmarks.find_links_definition
174
174
  end
@@ -181,19 +181,25 @@ class LinksDefinitionTest < MiniTest::Spec
181
181
  before do
182
182
  @d = Roar::Representer::Feature::Hypermedia::LinksDefinition.new(:links)
183
183
  end
184
-
184
+
185
185
  it "accepts options in constructor" do
186
186
  assert_equal [], @d.rel2block
187
187
  end
188
-
188
+
189
189
  it "accepts configuration" do
190
190
  @d.rel2block << {:rel => :self}
191
191
  assert_equal [{:rel=>:self}], @d.rel2block
192
192
  end
193
193
 
194
- it "responds to #each to iterate rel2block" do
194
+ it "responds to #clone" do
195
+ @d.rel2block << {:rel => :self}
196
+ assert @d.clone.rel2block.object_id != @d.rel2block.object_id
195
197
  end
196
198
 
199
+
200
+ it "responds to #each to iterate rel2block" do
201
+ end
202
+
197
203
  end
198
204
  end
199
205
 
@@ -204,10 +210,10 @@ class LinkCollectionTest < MiniTest::Spec
204
210
  link = Roar::Representer::Feature::Hypermedia::Hyperlink.new
205
211
  link.rel = "self"
206
212
  link.href = "http://self"
207
-
213
+
208
214
  collection.update_link(link)
209
215
  assert_equal 1, collection.size
210
-
216
+
211
217
  collection.update_link(link)
212
218
  assert_equal 1, collection.size
213
219
  end
@@ -220,21 +226,57 @@ class HyperlinkTest < MiniTest::Spec
220
226
  before do
221
227
  @link = Hyperlink.new(:rel => "self", :href => "http://self")
222
228
  end
223
-
229
+
224
230
  it "accepts string keys in constructor" do
225
231
  assert_equal "Hey, @myabc", Hyperlink.new("title" => "Hey, @myabc").title
226
232
  end
227
-
233
+
228
234
  it "responds to #media" do
229
235
  assert_equal nil, @link.media
230
236
  end
231
-
237
+
232
238
  it "responds to #rel" do
233
239
  assert_equal "self", @link.rel
234
240
  end
235
-
241
+
236
242
  it "responds to #href" do
237
243
  assert_equal "http://self", @link.href
238
244
  end
239
245
  end
240
246
  end
247
+
248
+ class HyperlinkInheritanceTest < MiniTest::Spec
249
+ describe "when the base representer has a link" do
250
+ before do
251
+ module BaseRepresenter
252
+ include Roar::Representer::JSON
253
+ include Roar::Representer::Feature::Hypermedia
254
+
255
+ link(:base) { "http://base" }
256
+ end
257
+
258
+ module Foo
259
+ include Roar::Representer::JSON
260
+ include Roar::Representer::Feature::Hypermedia
261
+ include BaseRepresenter
262
+
263
+ link(:foo) { "http://foo" }
264
+ end
265
+
266
+ module Bar
267
+ include Roar::Representer::JSON
268
+ include Roar::Representer::Feature::Hypermedia
269
+ include BaseRepresenter
270
+
271
+ link(:bar) { "http://bar" }
272
+ end
273
+ end
274
+
275
+ it "should inherit parent links" do
276
+ foo = Object.new.extend(Foo)
277
+
278
+ assert_equal "{\"links\":[{\"rel\":\"base\",\"href\":\"http://base\"},{\"rel\":\"foo\",\"href\":\"http://foo\"}]}", foo.to_json
279
+ end
280
+
281
+ end
282
+ end
@@ -1,10 +1,10 @@
1
1
  require 'test_helper'
2
- require 'roar/representer/feature/transport'
2
+ require 'roar/representer/transport/net_http'
3
3
 
4
- class TransportTest < MiniTest::Spec
4
+ class NetHTTPTransportTest < MiniTest::Spec
5
5
  describe "Transport" do
6
6
  before do
7
- @transport = Roar::Representer::Feature::Transport
7
+ @transport = Roar::Representer::Transport::NetHTTP.new
8
8
  end
9
9
 
10
10
  it "#get_uri returns response" do
@@ -23,9 +23,8 @@ class TransportTest < MiniTest::Spec
23
23
  assert_equal "<method>delete</method>", @transport.delete_uri("http://roar.example.com/method", "application/xml").body
24
24
  end
25
25
 
26
- # TODO: how to get PATCH into Sinatra?
27
- #it "#patch_uri returns Restfulie response" do
28
- # assert_equal "<method>patch</method>", @o.patch_uri("http://roar.example.com/method", "booty", "application/xml").body
29
- #end
26
+ it "#patch_uri returns response" do
27
+ assert_equal "<method>patch</method>", @transport.patch_uri("http://roar.example.com/method", "booty", "application/xml").body
28
+ end
30
29
  end
31
30
  end
@@ -35,6 +35,5 @@ class RepresenterTest < MiniTest::Spec
35
35
 
36
36
  assert_equal "{\"name\":\"Paulo\"}", Person.new(:name => "Paulo").to_json
37
37
  end
38
-
39
38
  end
40
39
  end
@@ -7,6 +7,15 @@ require 'minitest/spec'
7
7
  require 'roar/representer'
8
8
  require 'roar/representer/feature/http_verbs'
9
9
 
10
+ begin
11
+ require 'turn'
12
+
13
+ Turn.config do |config|
14
+ config.format = :dotted
15
+ # config.trace = true
16
+ end
17
+ rescue LoadError
18
+ end
10
19
 
11
20
  module AttributesContructor
12
21
  def initialize(attrs={})
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roar
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.10.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,22 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-22 00:00:00.000000000 Z
12
+ date: 2012-05-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: representable
16
- requirement: &82244490 !ruby/object:Gem::Requirement
16
+ requirement: &75807040 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
20
20
  - !ruby/object:Gem::Version
21
- version: 1.1.3
21
+ version: 1.1.7
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *82244490
24
+ version_requirements: *75807040
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &82244190 !ruby/object:Gem::Requirement
27
+ requirement: &75805090 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *82244190
35
+ version_requirements: *75805090
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: test_xml
38
- requirement: &82243710 !ruby/object:Gem::Requirement
38
+ requirement: &75802000 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *82243710
46
+ version_requirements: *75802000
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: minitest
49
- requirement: &82243040 !ruby/object:Gem::Requirement
49
+ requirement: &75801380 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,21 +54,21 @@ dependencies:
54
54
  version: 2.8.1
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *82243040
57
+ version_requirements: *75801380
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: sinatra
60
- requirement: &82242340 !ruby/object:Gem::Requirement
60
+ requirement: &75799950 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
64
64
  - !ruby/object:Gem::Version
65
- version: 1.2.6
65
+ version: 1.3.2
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *82242340
68
+ version_requirements: *75799950
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: sham_rack
71
- requirement: &82241770 !ruby/object:Gem::Requirement
71
+ requirement: &75799310 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ~>
@@ -76,7 +76,29 @@ dependencies:
76
76
  version: 1.3.0
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *82241770
79
+ version_requirements: *75799310
80
+ - !ruby/object:Gem::Dependency
81
+ name: turn
82
+ requirement: &75798790 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *75798790
91
+ - !ruby/object:Gem::Dependency
92
+ name: virtus
93
+ requirement: &75797000 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *75797000
80
102
  description: Streamlines the development of RESTful, resource-oriented architectures
81
103
  in Ruby.
82
104
  email:
@@ -96,16 +118,19 @@ files:
96
118
  - lib/roar/rails.rb
97
119
  - lib/roar/representer.rb
98
120
  - lib/roar/representer/feature/client.rb
121
+ - lib/roar/representer/feature/coercion.rb
99
122
  - lib/roar/representer/feature/http_verbs.rb
100
123
  - lib/roar/representer/feature/hypermedia.rb
101
- - lib/roar/representer/feature/transport.rb
102
124
  - lib/roar/representer/json.rb
103
125
  - lib/roar/representer/json/hal.rb
126
+ - lib/roar/representer/transport/faraday.rb
127
+ - lib/roar/representer/transport/net_http.rb
104
128
  - lib/roar/representer/xml.rb
105
129
  - lib/roar/version.rb
106
130
  - roar.gemspec
107
131
  - test/Gemfile
108
132
  - test/client_test.rb
133
+ - test/coercion_feature_test.rb
109
134
  - test/dummy/Rakefile
110
135
  - test/dummy/app/controllers/albums_controller.rb
111
136
  - test/dummy/app/controllers/application_controller.rb
@@ -142,17 +167,18 @@ files:
142
167
  - test/dummy/tmp/app/cells/blog/post/latest.html.erb
143
168
  - test/dummy/tmp/app/cells/blog/post_cell.rb
144
169
  - test/fake_server.rb
170
+ - test/faraday_http_transport_test.rb
145
171
  - test/hal_json_test.rb
146
172
  - test/http_verbs_feature_test.rb
147
173
  - test/hypermedia_feature_test.rb
148
174
  - test/integration_test.rb
149
175
  - test/json_representer_test.rb
176
+ - test/net_http_transport_test.rb
150
177
  - test/order_representers.rb
151
178
  - test/rails/controller_methods_test.rb
152
179
  - test/rails/rails_representer_methods_test.rb
153
180
  - test/representer_test.rb
154
181
  - test/test_helper.rb
155
- - test/transport_test.rb
156
182
  - test/xml_representer_test.rb
157
183
  homepage: http://rubygems.org/gems/roar
158
184
  licenses: []
@@ -1,45 +0,0 @@
1
- require "net/http"
2
- require "uri"
3
-
4
- module Roar
5
- module Representer
6
- module Feature
7
- # Implements the HTTP verbs with Net::HTTP.
8
- module Transport
9
- class << self
10
- # TODO: generically handle return codes/let Restfulie do it.
11
- def get_uri(uri, as)
12
- do_request(Net::HTTP::Get, uri, as)
13
- end
14
-
15
- def post_uri(uri, body, as)
16
- do_request(Net::HTTP::Post, uri, as)
17
- end
18
-
19
- def put_uri(uri, body, as)
20
- do_request(Net::HTTP::Put, uri, as)
21
- end
22
-
23
- def patch_uri(uri, body, as)
24
- do_request(Net::HTTP::Patch, uri, as)
25
- end
26
-
27
- def delete_uri(uri, as)
28
- do_request(Net::HTTP::Delete, uri, as)
29
- end
30
-
31
- private
32
- def do_request(what, uri, as, body="")
33
- # DISCUSS: can this be made easier?
34
- uri = URI.parse(uri)
35
- http = Net::HTTP.new(uri.host, uri.port)
36
- req = what.new(uri.request_uri)
37
- req.content_type = as
38
- req.body = body if body
39
- http.request(req)
40
- end
41
- end
42
- end
43
- end
44
- end
45
- end