roar 0.0.1.alpha1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/.gitignore +1 -0
  2. data/Gemfile +8 -0
  3. data/README.textile +297 -0
  4. data/Rakefile +16 -0
  5. data/lib/roar/client/entity_proxy.rb +58 -0
  6. data/lib/roar/client/proxy.rb +14 -0
  7. data/lib/roar/client/transport.rb +29 -0
  8. data/lib/roar/model.rb +36 -0
  9. data/lib/roar/model/representable.rb +31 -0
  10. data/lib/roar/rails.rb +21 -0
  11. data/lib/roar/rails/controller_methods.rb +71 -0
  12. data/lib/roar/rails/representer_methods.rb +52 -0
  13. data/lib/roar/rails/test_case.rb +43 -0
  14. data/lib/roar/representer.rb +72 -0
  15. data/lib/roar/representer/feature/http_verbs.rb +63 -0
  16. data/lib/roar/representer/feature/hypermedia.rb +43 -0
  17. data/lib/roar/representer/feature/model_representing.rb +88 -0
  18. data/lib/roar/representer/json.rb +32 -0
  19. data/lib/roar/representer/xml.rb +43 -0
  20. data/lib/roar/version.rb +1 -1
  21. data/roar.gemspec +10 -1
  22. data/test/Gemfile +6 -0
  23. data/test/dummy/Rakefile +7 -0
  24. data/test/dummy/app/controllers/albums_controller.rb +27 -0
  25. data/test/dummy/app/controllers/application_controller.rb +4 -0
  26. data/test/dummy/app/helpers/application_helper.rb +2 -0
  27. data/test/dummy/app/models/album.rb +6 -0
  28. data/test/dummy/app/models/song.rb +2 -0
  29. data/test/dummy/app/representers/representer/xml/album.rb +19 -0
  30. data/test/dummy/app/representers/representer/xml/song.rb +9 -0
  31. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  32. data/test/dummy/app/views/musician/featured.html.erb +1 -0
  33. data/test/dummy/app/views/musician/featured_with_block.html.erb +4 -0
  34. data/test/dummy/app/views/musician/hamlet.html.haml +1 -0
  35. data/test/dummy/config.ru +4 -0
  36. data/test/dummy/config/application.rb +20 -0
  37. data/test/dummy/config/boot.rb +10 -0
  38. data/test/dummy/config/database.yml +22 -0
  39. data/test/dummy/config/environment.rb +5 -0
  40. data/test/dummy/config/environments/development.rb +16 -0
  41. data/test/dummy/config/environments/production.rb +46 -0
  42. data/test/dummy/config/environments/test.rb +32 -0
  43. data/test/dummy/config/locales/en.yml +5 -0
  44. data/test/dummy/config/routes.rb +7 -0
  45. data/test/dummy/db/development.sqlite3 +0 -0
  46. data/test/dummy/db/migrate/20110514114753_create_albums.rb +14 -0
  47. data/test/dummy/db/migrate/20110514121228_create_songs.rb +14 -0
  48. data/test/dummy/db/schema.rb +29 -0
  49. data/test/dummy/db/test.sqlite3 +0 -0
  50. data/test/dummy/module - (2011-05-14 15:26:19) +5 -0
  51. data/test/dummy/public/404.html +26 -0
  52. data/test/dummy/public/422.html +26 -0
  53. data/test/dummy/public/500.html +26 -0
  54. data/test/dummy/public/favicon.ico +0 -0
  55. data/test/dummy/script/rails +6 -0
  56. data/test/dummy/tmp/app/cells/blog/post/latest.html.erb +7 -0
  57. data/test/dummy/tmp/app/cells/blog/post_cell.rb +7 -0
  58. data/test/fake_server.rb +80 -0
  59. data/test/http_verbs_test.rb +46 -0
  60. data/test/hypermedia_test.rb +35 -0
  61. data/test/integration_test.rb +122 -0
  62. data/test/json_representer_test.rb +101 -0
  63. data/test/model_representing_test.rb +121 -0
  64. data/test/model_test.rb +50 -0
  65. data/test/order_representers.rb +34 -0
  66. data/test/proxy_test.rb +89 -0
  67. data/test/rails/controller_methods_test.rb +147 -0
  68. data/test/rails/rails_representer_methods_test.rb +32 -0
  69. data/test/representable_test.rb +49 -0
  70. data/test/representer_test.rb +25 -0
  71. data/test/ruby_representation_test.rb +144 -0
  72. data/test/test_helper.rb +45 -0
  73. data/test/test_helper_test.rb +59 -0
  74. data/test/transport_test.rb +34 -0
  75. data/test/xml_hypermedia_test.rb +47 -0
  76. data/test/xml_representer_test.rb +238 -0
  77. metadata +181 -13
@@ -0,0 +1,49 @@
1
+ require 'test_helper'
2
+ require 'roar/model/representable'
3
+
4
+ class RepresentableTest < MiniTest::Spec
5
+
6
+ class ThisAsAppXml < Roar::Representer::Base
7
+ def self.deserialize(represented_class, mime_type, data)
8
+ "#{represented_class.name}->#{mime_type}: #{data}"
9
+ end
10
+
11
+ def serialize(represented, mime_type)
12
+ "#{represented.class.name}->#{mime_type}"
13
+ end
14
+ end
15
+
16
+ describe "Representable" do
17
+ before do
18
+ @c = Class.new do
19
+ include Roar::Model::Representable
20
+
21
+ represents "application/xml", :with => ThisAsAppXml
22
+ end
23
+ end
24
+
25
+
26
+ describe ".representer_class_for" do
27
+ it "returns the class" do
28
+ assert_equal ThisAsAppXml, @c.representer_class_for("application/xml")
29
+ end
30
+
31
+ it "returns nil if unknown" do
32
+ assert_equal nil, @c.representer_class_for("text/html")
33
+ end
34
+ end
35
+
36
+
37
+ describe ".from" do
38
+ it "receives mime_type and content" do
39
+ assert_equal "->application/xml: <xml/>", @c.from("application/xml", "<xml/>")
40
+ end
41
+ end
42
+
43
+ describe "#to" do
44
+ it "receives mime_type" do
45
+ assert_equal "->application/xml", @c.new.to("application/xml")
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,25 @@
1
+ require 'test_helper'
2
+
3
+ class RepresenterTest < MiniTest::Spec
4
+ describe "Representer" do
5
+ before do
6
+ @c = Class.new(Roar::Representer::Base)
7
+ end
8
+
9
+ it "aliases #representable_property to #property" do
10
+ @c.property :title
11
+ assert_equal "title", @c.representable_attrs.first.name
12
+ end
13
+
14
+ it "aliases #representable_collection to #collection" do
15
+ @c.collection :songs
16
+ assert_equal "songs", @c.representable_attrs.first.name
17
+ end
18
+
19
+ it "accepts property hash in #new" do
20
+ @c.property :title
21
+ assert_equal "Counting Down", @c.new(:title => "Counting Down").title
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,144 @@
1
+ require 'test_helper'
2
+
3
+ module Roar
4
+ module Representer
5
+ class Ruby < Base
6
+ def serialize(attributes)
7
+ serializable_attributes = attributes.dup
8
+
9
+ serialize_typed_attributes(serializable_attributes)
10
+ Marshal.dump serializable_attributes
11
+ end
12
+
13
+ def deserialize(body)
14
+ deserialized_hash = Marshal.load body
15
+ deserialize_typed_attributes(deserialized_hash)
16
+ end
17
+
18
+
19
+
20
+ module ConfigurationDsl # FIXME: move to Representer::BaseDsl or so.
21
+ def has_many(name, options={})
22
+ collections[name] = options
23
+ end
24
+ alias_method :collection, :has_many
25
+
26
+ def has_one(name, options={})
27
+ typed_entities[name] = options
28
+ end
29
+
30
+ def has_proxied(name, options={})
31
+ has_one(name, {:class => EntityProxy.class_for(options)})
32
+ end
33
+
34
+ def has_many_proxied(name, options={})
35
+ has_many(name, {:class => EntityProxy.class_for(options)})
36
+ end
37
+ end
38
+
39
+ self.extend ConfigurationDsl
40
+ # FIXME: use one variable?
41
+ extend Hooks::InheritableAttribute
42
+ inheritable_attr :collections
43
+ self.collections = {}
44
+ inheritable_attr :typed_entities
45
+ self.typed_entities = {}
46
+
47
+
48
+
49
+ def serialize_typed_attributes(attributes)
50
+ filter_attributes_for(attributes, self.class.typed_entities) do |name, options|
51
+ next unless entity = attributes[name]
52
+ attributes[name] = entity.to(mime_type)
53
+ end
54
+ end
55
+
56
+ # Attributes can be typecasted with +has_one+.
57
+ def deserialize_typed_attributes(attributes)
58
+ filter_attributes_for(attributes, self.class.typed_entities) do |name, options|
59
+ item = attributes.delete(name) # attributes[:sum]
60
+ item = options[:class].from(mime_type, item) # Sum.from_xml_attributes
61
+ attributes[name] = item
62
+ end
63
+ end
64
+
65
+ def filter_attributes_for(attributes, config)
66
+ config.each do |name, options|
67
+ name = name.to_s
68
+ yield name, options
69
+ end
70
+ attributes
71
+ end
72
+
73
+ end
74
+
75
+
76
+ end
77
+
78
+ end
79
+
80
+
81
+ class RubyRepresenterFunctionalTest < MiniTest::Spec
82
+ class Item
83
+ attr_reader :value
84
+
85
+ def initialize(value)
86
+ @value = value
87
+ end
88
+
89
+ def to(mime_type)
90
+ raise unless mime_type == "ruby/serialized"
91
+ Marshal.dump(@value)
92
+ end
93
+
94
+ def self.from(mime_type, string)
95
+ raise unless mime_type == "ruby/serialized"
96
+ new Marshal.load(string)
97
+ end
98
+
99
+ def ==(b)
100
+ value == b.value
101
+ end
102
+ end
103
+
104
+ describe "RubyRepresenter" do
105
+ before do
106
+ @r = Class.new(Roar::Representer::Ruby).new("ruby/serialized")
107
+ @m = {:id => 1}
108
+ end
109
+
110
+ describe "without options" do
111
+ it "#serialize returns the serialized model" do
112
+ assert_equal "\x04\b{\x06:\aidi\x06", @r.serialize(@m)
113
+ end
114
+
115
+ it "#deserialize returns the attributes" do
116
+ assert_equal @m, @r.deserialize("\x04\b{\x06:\aidi\x06")
117
+ end
118
+ end
119
+
120
+
121
+ describe "has_one" do
122
+ before do
123
+ @r.class.instance_eval do
124
+ has_one :item, :class => Item
125
+ end
126
+ end
127
+
128
+ it "#serialize skips empty :item" do
129
+ assert_equal "\x04\b{\x06:\aidi\x06", @r.serialize(@m)
130
+ end
131
+
132
+ it "#serialize delegates to item#to" do
133
+ @m = {:id => 1, "item" => Item.new("beer")}
134
+ assert_equal "\x04\b{\a:\aidi\x06I\"\titem\x06:\x06EF\"\x13\x04\bI\"\tbeer\x06:\x06EF", @r.serialize(@m)
135
+ end
136
+
137
+ it "#deserialize typecasts :item" do
138
+ m = @r.deserialize("\x04\b{\a:\aidi\x06I\"\titem\x06:\x06EF\"\x13\x04\bI\"\tbeer\x06:\x06EF")
139
+ assert_equal({:id => 1, "item" => Item.new("beer")}, m)
140
+ end
141
+
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,45 @@
1
+ require 'bundler'
2
+ Bundler.setup
3
+
4
+ require 'test/unit'
5
+ require 'minitest/spec'
6
+
7
+ require 'active_model'
8
+ #require 'roar/client/entity_proxy'
9
+ require 'roar/representer'
10
+ require 'roar/model'
11
+ require 'roar/representer/feature/http_verbs'
12
+
13
+ require 'stringio' # FIXME. remove for 3.0.4.
14
+ require 'builder'
15
+
16
+
17
+ class Item
18
+ include Roar::Model
19
+ accessors :value
20
+
21
+ def self.model_name
22
+ "item"
23
+ end
24
+ end
25
+
26
+ class Position
27
+ include Roar::Model
28
+ accessors :id, :item
29
+
30
+ def self.model_name
31
+ :order
32
+ end
33
+ end
34
+
35
+ class Order
36
+ include Roar::Model
37
+ accessors :id, :items
38
+
39
+ def self.model_name
40
+ :order
41
+ end
42
+ end
43
+
44
+ require "test_xml/mini_test"
45
+ require "roar/representer/xml"
@@ -0,0 +1,59 @@
1
+ require 'test_helper'
2
+ require 'roar/model'
3
+
4
+ class AssertModelTest < MiniTest::Spec
5
+ class Song
6
+ include Roar::Model
7
+ accessors :title
8
+ end
9
+
10
+ class Album
11
+ include Roar::Model
12
+ accessors :songs
13
+ end
14
+
15
+ class Play
16
+ include Roar::Model
17
+ accessors :song
18
+ end
19
+
20
+
21
+ describe "#assert_model" do
22
+ before do
23
+ @jerk = Song.new(:title => "Jerk")
24
+ @sellout = Song.new(:title => "Sellout")
25
+ @saleout = Song.new(:title => "Sellout")
26
+ end
27
+
28
+ it "compares to plain models" do
29
+ assert_model @jerk, @jerk
30
+ assert_model @sellout, @saleout
31
+
32
+ assert_raises MiniTest::Assertion do
33
+ assert_model @jerk, @sellout
34
+ end
35
+ end
36
+
37
+ it "compares nested models" do
38
+ @play = Play.new(:song => @sellout)
39
+ @play_2 = Play.new(:song => @saleout)
40
+ assert_model @play, @play_2
41
+
42
+ assert_raises MiniTest::Assertion do
43
+ assert_model @play, Play.new(:song => @jerk)
44
+ end
45
+ end
46
+
47
+ it "compares nested models in collections" do
48
+ @stay_asleep = Album.new(:songs => [@jerk, @sellout])
49
+ @stay_asleep_ripped = Album.new(:songs => [@jerk, @saleout])
50
+ assert_model @stay_asleep, @stay_asleep_ripped
51
+
52
+ assert_raises MiniTest::Assertion do
53
+ assert_model assert_model @stay_asleep, Album.new(:songs => [@jerk])
54
+ end
55
+ end
56
+
57
+
58
+ end
59
+ end
@@ -0,0 +1,34 @@
1
+ require 'test_helper'
2
+ require 'roar/client/transport'
3
+
4
+ class TransportTest < MiniTest::Spec
5
+ describe "Transport" do
6
+ before do
7
+ @klass = Class.new(Object) do
8
+ include Roar::Client::Transport
9
+ end
10
+ @o = @klass.new
11
+ end
12
+
13
+ it "#get_uri returns Restfulie response" do
14
+ assert_equal "<method>get</method>", @o.get_uri("http://localhost:9999/method", "application/xml").body
15
+ end
16
+
17
+ it "#post_uri returns Restfulie response" do
18
+ assert_equal "<method>post</method>", @o.post_uri("http://localhost:9999/method", "booty", "application/xml").body
19
+ end
20
+
21
+ it "#put_uri returns Restfulie response" do
22
+ assert_equal "<method>put</method>", @o.put_uri("http://localhost:9999/method", "booty", "application/xml").body
23
+ end
24
+
25
+ it "#delete_uri returns Restfulie response" do
26
+ assert_equal "<method>delete</method>", @o.delete_uri("http://localhost:9999/method", "application/xml").body
27
+ end
28
+
29
+ # TODO: how to get PATCH into Sinatra?
30
+ #it "#patch_uri returns Restfulie response" do
31
+ # assert_equal "<method>patch</method>", @o.patch_uri("http://localhost:9999/method", "booty", "application/xml").body
32
+ #end
33
+ end
34
+ end
@@ -0,0 +1,47 @@
1
+ require 'test_helper'
2
+
3
+ class XmlHypermediaTest < MiniTest::Spec
4
+ describe "Hypermedia API" do
5
+ before do
6
+ @c = Class.new(Roar::Representer::XML) do
7
+ self.representation_name= :wuff
8
+ representable_property :id
9
+ link :self do "http://self" end
10
+ link :next do "http://next/#{id}" end
11
+ end
12
+ @r = @c.new
13
+ end
14
+
15
+ it "responds to #links" do
16
+ assert_equal nil, @r.links
17
+ end
18
+
19
+ it "computes links in #from_attributes" do
20
+ @r = @c.from_attributes({"id" => 1})
21
+ assert_equal 2, @r.links.size
22
+ assert_equal({"rel"=>:self, "href"=>"http://self"}, @r.links.first.to_attributes)
23
+ assert_equal({"rel"=>:next, "href"=>"http://next/1"}, @r.links.last.to_attributes)
24
+ end
25
+
26
+ it "extracts links from XML" do
27
+ @r = @c.deserialize(%{
28
+ <order>
29
+ <link rel="self" href="http://self">
30
+ </order>
31
+ })
32
+
33
+ assert_kind_of Roar::Representer::Feature::Hypermedia::LinkCollection, @r.links
34
+ assert_equal 1, @r.links.size
35
+ assert_equal({"rel"=>"self", "href"=>"http://self"}, @r.links.first.to_attributes)
36
+ end
37
+
38
+ it "renders <link> correctly in XML" do
39
+ assert_xml_equal %{<wuff>
40
+ <id>1</id>
41
+ <link rel="self" href="http://self"/>
42
+ <link rel="next" href="http://next/1"/>
43
+ </wuff><expected />}, @c.from_attributes({"id" => 1}).serialize
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,238 @@
1
+ require 'test_helper'
2
+
3
+ class Order
4
+ include Roar::Model
5
+ accessors :id, :item
6
+
7
+ def self.model_name
8
+ :order
9
+ end
10
+ end
11
+
12
+
13
+ class ItemRepresenter < Roar::Representer::XML
14
+ self.representation_name= :item
15
+ representable_property :value
16
+ end
17
+
18
+ class PositionRepresenter < Roar::Representer::XML
19
+ self.representation_name= :position
20
+ representable_property :id
21
+ representable_property :item, :as => ItemRepresenter
22
+ end
23
+
24
+
25
+
26
+
27
+ class XMLRepresenterUnitTest < MiniTest::Spec
28
+ describe "XmlRepresenter" do
29
+ describe "#link" do
30
+ class Rapper < Roar::Representer::XML
31
+ link :self
32
+ link :next
33
+ end
34
+
35
+ it "creates a LinksDefinition" do
36
+ assert_equal 1, Rapper.representable_attrs.size
37
+ assert_equal [{:rel=>:self, :block=>nil}, {:rel=>:next, :block=>nil}], Rapper.representable_attrs.first.rel2block
38
+ end
39
+ end
40
+
41
+ describe "#from_attributes" do
42
+ it "accepts a block" do
43
+ @c = Class.new(Roar::Representer::XML) do
44
+ attr_accessor :name
45
+ end
46
+
47
+ assert_equal("Conan", @c.from_attributes({}) { |rep| rep.name = "Conan" }.name)
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ class LinksDefinitionTest < MiniTest::Spec
54
+ describe "LinksDefinition" do
55
+ before do
56
+ @d = Roar::Representer::LinksDefinition.new(:links)
57
+ end
58
+
59
+ it "accepts options in constructor" do
60
+ assert_equal [], @d.rel2block
61
+ end
62
+
63
+ it "accepts configuration" do
64
+ @d.rel2block << {:rel => :self}
65
+ assert_equal [{:rel=>:self}], @d.rel2block
66
+ end
67
+ end
68
+ end
69
+
70
+ class XMLDefinitionTest < MiniTest::Spec
71
+ class Rapper
72
+ attr_accessor :name
73
+ end
74
+
75
+ describe "ROXML::Definition" do
76
+ it "responds to #populate" do
77
+ @r = Rapper.new
78
+ Representable::Definition.new(:name).populate(@r, "name" => "Eugen")
79
+ assert_equal "Eugen", @r.name
80
+ end
81
+ end
82
+ end
83
+
84
+
85
+
86
+
87
+
88
+ class XMLRepresenterFunctionalTest < MiniTest::Spec
89
+ class GreedyOrder
90
+ include Roar::Model
91
+ accessors :id, :items
92
+
93
+ def self.model_name
94
+ :order
95
+ end
96
+ end
97
+
98
+ class TestXmlRepresenter < Roar::Representer::XML
99
+ self.representation_name= :order # FIXME: get from represented?
100
+ representable_property :id
101
+ end
102
+
103
+
104
+ describe "XMLRepresenter" do
105
+ before do
106
+ @m = {"id" => "1"}
107
+ @o = Order.new(@m)
108
+ @r = TestXmlRepresenter.new
109
+ @i = ItemRepresenter.new
110
+ @i.value = "Beer"
111
+ end
112
+
113
+ describe "#from_attributes" do
114
+ it "copies represented attributes, only" do
115
+ @r = PositionRepresenter.from_attributes("id" => 1, "item" => @i, "unknown" => 1)
116
+ assert_kind_of PositionRepresenter, @r
117
+ assert_equal 1, @r.id
118
+
119
+ assert_kind_of ItemRepresenter, @r.item
120
+ assert_equal @r.item.value, "Beer"
121
+ end
122
+ end
123
+
124
+
125
+ describe "#to_attributes" do
126
+ it "returns a nested attributes hash" do
127
+ @r = PositionRepresenter.from_attributes("id" => 1, "item" => @i)
128
+ assert_equal({"id" => 1, "item" => {"value" => "Beer"}}, @r.to_attributes)
129
+ end
130
+ end
131
+
132
+
133
+ describe "#to_xml" do
134
+ it "serializes the current model" do
135
+ assert_xml_equal "<order/>", @r.to_xml.serialize
136
+
137
+ @r.id = 2
138
+ assert_xml_equal "<rap><id>2</id></rap>", @r.to_xml(:name => :rap).serialize
139
+ end
140
+ end
141
+
142
+
143
+ describe "without options" do
144
+ it "#serialize returns the serialized model" do
145
+ @r.id = 1
146
+ assert_xml_equal "<order><id>1</id></order>", @r.serialize
147
+ end
148
+
149
+
150
+ it ".from_xml returns the deserialized model" do
151
+ @m = TestXmlRepresenter.deserialize("<order><id>1</id></order>")
152
+ assert_equal "1", @m.id
153
+ end
154
+
155
+ it ".from_xml still works with nil" do
156
+ assert TestXmlRepresenter.deserialize(nil)
157
+ end
158
+
159
+ end
160
+
161
+
162
+ describe "with a typed attribute" do
163
+ before do
164
+ @r = PositionRepresenter.from_attributes("id" => "1")
165
+ end
166
+
167
+ it "#serialize skips empty :item" do
168
+ assert_xml_equal "<position><id>1</id></position>", @r.serialize
169
+ end
170
+
171
+ it "#to_xml delegates to ItemRepresenter#to_xml" do
172
+ @r.item = @i
173
+ assert_xml_equal "<position><id>1</id><item><value>Beer</value></item></position>", @r.serialize
174
+ end
175
+
176
+ it ".from_xml typecasts :item" do
177
+ @m = PositionRepresenter.deserialize("<position><id>1</id><item><value>beer</value></item>\n</position>")
178
+
179
+ assert_equal "1", @m.id
180
+ assert_equal "beer", @m.item.value
181
+ end
182
+ end
183
+
184
+
185
+ describe "with a typed list" do
186
+ before do
187
+ @c = Class.new(Roar::Representer::XML) do
188
+ self.representation_name= :order
189
+ representable_property :id
190
+ representable_collection :items, :as => ItemRepresenter, :tag => :item
191
+ end
192
+
193
+ @r = @c.from_attributes("id" => 1)
194
+ end
195
+
196
+ it "#serialize_model skips empty :item" do
197
+ assert_xml_equal "<order><id>1</id></order>", @r.serialize
198
+ end
199
+
200
+ it "#serialize delegates to ItemXmlRepresenter#to_xml in list" do
201
+ @r.items = [ItemRepresenter.from_attributes("value" => "Bier")]
202
+
203
+ assert_xml_equal "<order><id>1</id><item><value>Bier</value></item></order>",
204
+ @r.serialize
205
+ end
206
+
207
+ it ".from_xml typecasts list" do
208
+ @m = @c.deserialize("<order><id>1</id><item><value>beer</value></item>\n</order>")
209
+
210
+ assert_equal "1", @m.id
211
+ assert_equal 1, @m.items.size
212
+ assert_equal "beer", @m.items.first.value
213
+ end
214
+ end
215
+
216
+ end
217
+ end
218
+
219
+ class XmlHyperlinkRepresenterTest < MiniTest::Spec
220
+ describe "API" do
221
+ before do
222
+ @l = Roar::Representer::XML::Hyperlink.from_xml(%{<link rel="self" href="http://roar.apotomo.de"/>})
223
+ end
224
+
225
+ it "responds to #representation_name" do
226
+ assert_equal :link, @l.class.representation_name
227
+ end
228
+
229
+
230
+ it "responds to #rel" do
231
+ assert_equal "self", @l.rel
232
+ end
233
+
234
+ it "responds to #href" do
235
+ assert_equal "http://roar.apotomo.de", @l.href
236
+ end
237
+ end
238
+ end