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.
- data/.gitignore +1 -0
- data/Gemfile +8 -0
- data/README.textile +297 -0
- data/Rakefile +16 -0
- data/lib/roar/client/entity_proxy.rb +58 -0
- data/lib/roar/client/proxy.rb +14 -0
- data/lib/roar/client/transport.rb +29 -0
- data/lib/roar/model.rb +36 -0
- data/lib/roar/model/representable.rb +31 -0
- data/lib/roar/rails.rb +21 -0
- data/lib/roar/rails/controller_methods.rb +71 -0
- data/lib/roar/rails/representer_methods.rb +52 -0
- data/lib/roar/rails/test_case.rb +43 -0
- data/lib/roar/representer.rb +72 -0
- data/lib/roar/representer/feature/http_verbs.rb +63 -0
- data/lib/roar/representer/feature/hypermedia.rb +43 -0
- data/lib/roar/representer/feature/model_representing.rb +88 -0
- data/lib/roar/representer/json.rb +32 -0
- data/lib/roar/representer/xml.rb +43 -0
- data/lib/roar/version.rb +1 -1
- data/roar.gemspec +10 -1
- data/test/Gemfile +6 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/controllers/albums_controller.rb +27 -0
- data/test/dummy/app/controllers/application_controller.rb +4 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/album.rb +6 -0
- data/test/dummy/app/models/song.rb +2 -0
- data/test/dummy/app/representers/representer/xml/album.rb +19 -0
- data/test/dummy/app/representers/representer/xml/song.rb +9 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/app/views/musician/featured.html.erb +1 -0
- data/test/dummy/app/views/musician/featured_with_block.html.erb +4 -0
- data/test/dummy/app/views/musician/hamlet.html.haml +1 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +20 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +22 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +16 -0
- data/test/dummy/config/environments/production.rb +46 -0
- data/test/dummy/config/environments/test.rb +32 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +7 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/migrate/20110514114753_create_albums.rb +14 -0
- data/test/dummy/db/migrate/20110514121228_create_songs.rb +14 -0
- data/test/dummy/db/schema.rb +29 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/module - (2011-05-14 15:26:19) +5 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +26 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/dummy/tmp/app/cells/blog/post/latest.html.erb +7 -0
- data/test/dummy/tmp/app/cells/blog/post_cell.rb +7 -0
- data/test/fake_server.rb +80 -0
- data/test/http_verbs_test.rb +46 -0
- data/test/hypermedia_test.rb +35 -0
- data/test/integration_test.rb +122 -0
- data/test/json_representer_test.rb +101 -0
- data/test/model_representing_test.rb +121 -0
- data/test/model_test.rb +50 -0
- data/test/order_representers.rb +34 -0
- data/test/proxy_test.rb +89 -0
- data/test/rails/controller_methods_test.rb +147 -0
- data/test/rails/rails_representer_methods_test.rb +32 -0
- data/test/representable_test.rb +49 -0
- data/test/representer_test.rb +25 -0
- data/test/ruby_representation_test.rb +144 -0
- data/test/test_helper.rb +45 -0
- data/test/test_helper_test.rb +59 -0
- data/test/transport_test.rb +34 -0
- data/test/xml_hypermedia_test.rb +47 -0
- data/test/xml_representer_test.rb +238 -0
- 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
|
data/test/test_helper.rb
ADDED
@@ -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
|