roar 0.10.0 → 0.10.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.
@@ -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