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,121 @@
1
+ require 'test_helper'
2
+ require 'roar/representer/feature/model_representing'
3
+
4
+ class ModelRepresentingTest < MiniTest::Spec
5
+ describe "ModelRepresenting" do
6
+ class ItemRepresenter < Roar::Representer::XML
7
+ include Roar::Representer::Feature::ModelRepresenting # TODO: move to abstract!
8
+ self.representation_name= :item
9
+ representable_property :value
10
+ end
11
+
12
+ class PositionRepresenter < Roar::Representer::XML
13
+ include Roar::Representer::Feature::ModelRepresenting # TODO: move to abstract!
14
+ self.representation_name= :position
15
+ representable_property :id
16
+ representable_property :item, :as => ItemRepresenter
17
+ end
18
+
19
+ class OrderRepresenter < Roar::Representer::XML
20
+ include Roar::Representer::Feature::ModelRepresenting # TODO: move to abstract!
21
+ self.representation_name= :order
22
+ representable_property :id
23
+ representable_property :items, :as => [ItemRepresenter]
24
+ end
25
+
26
+ describe "#definition_class" do
27
+ it "returns ModelDefinition" do
28
+ assert_equal Roar::Representer::Feature::ModelRepresenting::ModelDefinition, OrderRepresenter.send(:definition_class)
29
+ end
30
+
31
+ end
32
+
33
+ describe "#for_model" do
34
+ it "copies represented model attributes, nothing more" do
35
+ @o = Position.new("id" => 1, "item" => Item.new("value" => "Beer"))
36
+
37
+ @r = PositionRepresenter.for_model(@o)
38
+ assert_kind_of PositionRepresenter, @r
39
+ assert_equal 1, @r.id
40
+
41
+ @i = @r.item
42
+ assert_kind_of ItemRepresenter, @i
43
+ assert_equal "Beer", @i.value
44
+ end
45
+
46
+ it "copies the model to @represented" do
47
+ @o = Position.new("id" => 1, "item" => @i = Item.new("value" => "Beer"))
48
+
49
+ @r = PositionRepresenter.for_model(@o)
50
+ assert_equal @o, @r.represented
51
+ assert_equal @i, @r.item.represented
52
+ end
53
+
54
+
55
+ it "works with Hyperlink attributes" do
56
+ @c = Class.new(ItemRepresenter) do
57
+ link :self do "http://self" end
58
+ end
59
+
60
+ assert_equal({"value"=>"Beer", "links"=>[{"rel"=>:self, "href"=>"http://self"}]}, @c.for_model(Item.new("value" => "Beer")).to_attributes)
61
+ end
62
+
63
+ end
64
+
65
+ describe "#serialize_model" do
66
+ it "skips empty :item" do
67
+ @o = Position.new("id" => 1)
68
+ assert_xml_equal "<position><id>1</id></position>", PositionRepresenter.serialize_model(@o)
69
+ end
70
+
71
+ it "skips empty [:items]" do
72
+ assert_xml_equal "<order><id>1</id></order>", OrderRepresenter.serialize_model(Order.new("id" => 1))
73
+ end
74
+
75
+ it "serializes the model" do
76
+ @o = Order.new("id" => 1, "items" => [Item.new("value" => "Beer")])
77
+ assert_xml_equal %{
78
+ <order>
79
+ <id>1</id>
80
+ <item>
81
+ <value>Beer</value>
82
+ </item>
83
+ </order>}"", OrderRepresenter.serialize_model(@o)
84
+ end
85
+
86
+ end
87
+
88
+ describe "#to_nested_attributes" do
89
+ it "provides a AR-compatible hash" do
90
+ @o = Order.new("id" => 1, "items" => [Item.new("value" => "Beer")])
91
+ @r = OrderRepresenter.for_model(@o)
92
+
93
+ OrderRepresenter.class_eval do
94
+ include Roar::Representer::Feature::ActiveRecordMethods
95
+ end
96
+ assert_equal({"id" => 1, "items_attributes" => [{"value" => "Beer"}]}, @r.to_nested_attributes) # DISCUSS: overwrite #to_attributes.
97
+ end
98
+
99
+ it "doesn't include :links" do
100
+ @o = Order.new("id" => 1, "items" => [Item.new("value" => "Beer")])
101
+
102
+
103
+
104
+ OrderRepresenter.class_eval do
105
+ include Roar::Representer::Feature::ActiveRecordMethods
106
+ link :self do
107
+ # "bla"
108
+ end
109
+ end
110
+ ItemRepresenter.class_eval do
111
+ link :self do
112
+
113
+ end
114
+ end
115
+ @r = OrderRepresenter.for_model(@o)
116
+
117
+ assert_equal({"id" => 1, "items_attributes" => [{"value" => "Beer"}]}, @r.to_nested_attributes) # DISCUSS: overwrite #to_attributes.
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,50 @@
1
+ require 'test_helper'
2
+
3
+ class ModelTest < MiniTest::Spec
4
+ describe "HttpVerbs" do
5
+ before do
6
+ @klass = Class.new(TestModel) do
7
+ include Roar::Model::HttpVerbs
8
+
9
+ self.resource_base = "http://localhost:9999/test/"
10
+ end
11
+ @o = @klass.new
12
+ end
13
+
14
+ it "has resource_base* accessors for setting the uri base path" do
15
+ assert_equal "http://localhost:9999/test/", @klass.resource_base
16
+ end
17
+
18
+ it ".get returns deserialized object from " do
19
+ assert_equal({"id" => "4711"}, @klass.get(4711).attributes)
20
+ end
21
+ end
22
+
23
+
24
+ describe "The Model API" do
25
+ class Dog
26
+ include Roar::Model
27
+
28
+ accessors :name
29
+ end
30
+
31
+ before do
32
+ @klass = Dog
33
+ end
34
+
35
+ it "the constructor accepts attributes" do
36
+ assert_equal({"id" => "4711"}, @klass.new({"id" => "4711"}).attributes)
37
+ end
38
+
39
+ it "responds to .model_name" do
40
+ assert_equal "model_test/dog", @klass.model_name
41
+ end
42
+
43
+ it "lets .accessors create accessors" do
44
+ @o = @klass.new({"name" => "Joe"})
45
+ assert_equal "Joe", @o.name
46
+ @o.name= "Noe"
47
+ assert_equal "Noe", @o.name
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,34 @@
1
+ require 'roar/representer/json'
2
+ require 'roar/representer/feature/http_verbs'
3
+ require 'roar/representer/feature/hypermedia'
4
+
5
+ module JSON
6
+
7
+ class Item < Roar::Representer::JSON
8
+ property :article_id
9
+ property :amount
10
+
11
+ include Roar::Representer::Feature::HttpVerbs
12
+ include Roar::Representer::Feature::Hypermedia
13
+ end
14
+
15
+
16
+ class Order < Roar::Representer::JSON
17
+ property :client_id
18
+ collection :items, :as => Item
19
+
20
+
21
+ include Roar::Representer::Feature::HttpVerbs
22
+ include Roar::Representer::Feature::Hypermedia
23
+
24
+
25
+ link :items do
26
+ items_url
27
+ end
28
+
29
+ link :self do
30
+ order_url(represented)
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,89 @@
1
+ require 'test_helper'
2
+
3
+ class ProxyTest < MiniTest::Spec
4
+ describe "Transport" do
5
+ before do
6
+ @klass = Class.new(Object) do
7
+ include Roar::Client::Transport
8
+ end
9
+ @o = @klass.new
10
+ end
11
+
12
+ it "#get_uri returns Restfulie response" do
13
+ assert_equal "<test><id>4711</id></test>", @o.get_uri("http://localhost:9999/test/4711").body
14
+ end
15
+ end
16
+
17
+
18
+ describe "The public Proxy API" do
19
+ before do
20
+ @klass = Class.new(TestModel) do
21
+ extend Roar::Client::Proxy
22
+ end
23
+ end
24
+
25
+ it "responds to .get_model" do
26
+ @o = @klass.get_model("http://localhost:9999/test/1", TestModel)
27
+ assert_kind_of TestModel, @o
28
+ assert_equal({"id" => "1"}, @o.attributes)
29
+ end
30
+ end
31
+
32
+ describe "EntityProxy" do
33
+ before do
34
+ Proxy = Roar::Client::EntityProxy
35
+ @proxy_class = Proxy.class_for(:class => TestModel)
36
+ end
37
+
38
+ it ".class_for returns an EntityProxy subclass" do
39
+ @proxy = @proxy_class.new
40
+ assert_kind_of Proxy, @proxy
41
+ end
42
+
43
+ it "responds to .options" do
44
+ assert_equal({:class => TestModel}, @proxy_class.send(:options))
45
+ end
46
+
47
+ it "doesn't override superclass options" do
48
+ assert_equal nil, Proxy.options
49
+ end
50
+
51
+ it "responds to .from_attributes and responds to #original_attributes" do
52
+ assert_equal({:urn => "urn:item"}, @proxy_class.from_attributes(:urn => "urn:item").send(:original_attributes))
53
+ end
54
+
55
+ it "responds to .model_name with the proxied name" do
56
+ assert_equal "test", @proxy_class.model_name
57
+ end
58
+
59
+ # finalize!
60
+ describe "#finalize!" do
61
+ before do
62
+ @o = @proxy_class.from_attributes("uri" => "http://localhost:9999/test/1")
63
+ end
64
+
65
+ it "responds to #proxied_resource" do
66
+ assert_nil @o.send(:proxied_resource)
67
+ end
68
+
69
+ it "#finalize! retrieves proxied resource" do
70
+ @o.finalize!
71
+ assert_kind_of TestModel, @o.send(:proxied_resource)
72
+ end
73
+
74
+ # delegation:
75
+ it "#attributes are delegated" do
76
+ @o.finalize!
77
+ assert_equal({"id" => "1"}, @o.attributes)
78
+ end
79
+
80
+ it "#to_xml renders the unproxied entity" do
81
+ assert_equal "<test>\n <uri>http://localhost:9999/test/1</uri>\n</test>\n", @o.to_xml
82
+ end
83
+
84
+ it "#attributes_for_xml returns the unfinalized EntityProxy#attributes hash" do
85
+ assert_equal({"uri"=>"http://localhost:9999/test/1"}, @o.attributes_for_xml)
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,147 @@
1
+ require 'test_helper'
2
+ require 'roar/rails/test_case'
3
+ require "dummy/config/environment"
4
+ require "rails/test_help" # adds stuff like @routes, etc.
5
+
6
+ module Representer
7
+ module BMP
8
+ class Album; end
9
+ end
10
+ end
11
+
12
+ class ControllerMethodsTest < ActionController::TestCase
13
+ tests AlbumsController
14
+
15
+ test "responds to #responder" do
16
+ assert_equal Roar::Rails::ControllerMethods::Responder, @controller.class.responder
17
+ end
18
+
19
+ test "responds to #represents" do
20
+ @controller = Class.new(AlbumsController)
21
+ assert_equal Album, @controller.represented_class
22
+ @controller.represents Song
23
+ assert_equal Song, @controller.represented_class
24
+ end
25
+
26
+ test "responds to #representer_class_for" do
27
+ assert_equal Representer::BMP::Album, @controller.representer_class_for(Album, :bmp)
28
+ end
29
+
30
+ test "responds to #representation" do
31
+ post :create, %{<album>
32
+ <year>2011</year>
33
+ <song>
34
+ <title>Walking In Your Footsteps</title>
35
+ </song>
36
+ </album>}, :format => :xml
37
+
38
+
39
+ assert_equal({"id"=>"", "year"=>"2011",
40
+ "songs_attributes"=>[{"title"=>"Walking In Your Footsteps"}]}, @controller.representation)
41
+ end
42
+
43
+ test "responds to #incoming" do
44
+ post :create, %{<album>
45
+ <year>2011</year>
46
+ <song>
47
+ <title>Walking In Your Footsteps</title>
48
+ </song>
49
+ </album>}, :format => :xml
50
+
51
+
52
+ assert_equal({"id"=>"", "year"=>"2011",
53
+ "songs"=>[{"title"=>"Walking In Your Footsteps"}], "links"=>[]}, @controller.incoming.to_attributes)
54
+ end
55
+ end
56
+
57
+
58
+ class ControllerFunctionalTest < ActionController::TestCase
59
+ tests AlbumsController
60
+
61
+ test "GET: returns a xml representation" do
62
+ get :show, :id => 1, :format => :xml
63
+
64
+ assert_response 200
65
+ assert_body %{
66
+ <album>
67
+ <id>1</id>
68
+ <year>2011</year>
69
+ <song>
70
+ <title>Alltax</title>
71
+ </song>
72
+ <song>
73
+ <title>Bali</title>
74
+ </song>
75
+
76
+ <link rel="self" href="http://test.host/albums/1" />
77
+ <link rel="album-search" href="http://test.host/articles/starts_with/{query}" />
78
+ </album>}, :format => :xml
79
+ end
80
+
81
+ test "POST: creates a new album and returns the xml representation" do
82
+ post :create, %{<album>
83
+ <year>1997</year>
84
+ <song>
85
+ <title>Cooler Than You</title>
86
+ </song>
87
+ </album>}, :format => :xml
88
+
89
+ assert @album = Album.find(:last)
90
+ assert_equal "1997", @album.year
91
+ assert_equal "Cooler Than You", @album.songs.first.title
92
+
93
+ assert_response 201, "Location" => album_url(@album) # Created
94
+ assert_body %{
95
+ <album>
96
+ <id>2</id>
97
+ <year>1997</year>
98
+ <song>
99
+ <title>Cooler Than You</title>
100
+ </song>
101
+
102
+ <link rel="self" href="http://test.host/albums/2" />
103
+ <link rel="album-search" href="http://test.host/articles/starts_with/{query}" />
104
+ </album>}, :format => :xml
105
+ end
106
+
107
+ test "POST: invalid incoming representations yields to 422" do
108
+ post :create, :format => :xml
109
+
110
+ assert_response 422 # Unprocessable Entity
111
+ end
112
+
113
+ test "PUT: updates album and returns the xml representation" do
114
+ put :update, %{
115
+ <album>
116
+ <year>1997</year>
117
+ <song>
118
+ <title>Cooler Than You</title>
119
+ </song>
120
+ <song>
121
+ <title>Rubbing The Elf</title>
122
+ </song>
123
+ </album>}, :id => 1, :format => :xml
124
+
125
+ assert @album = Album.find(1)
126
+ assert_equal "1997", @album.year
127
+ assert_equal 2, @album.songs.size
128
+ assert_equal "Cooler Than You", @album.songs.first.title
129
+ assert_equal "Rubbing The Elf", @album.songs.last.title
130
+
131
+ assert_response 200
132
+ assert_body %{
133
+ <album>
134
+ <id>1</id>
135
+ <year>1997</year>
136
+ <song>
137
+ <title>Cooler Than You</title>
138
+ </song>
139
+ <song>
140
+ <title>Rubbing The Elf</title>
141
+ </song>
142
+
143
+ <link rel="self" href="http://test.host/albums/1" />
144
+ <link rel="album-search" href="http://test.host/articles/starts_with/{query}" />
145
+ </album>}, :format => :xml
146
+ end
147
+ end
@@ -0,0 +1,32 @@
1
+ require 'test_helper'
2
+ require "dummy/config/environment"
3
+ require 'roar/rails'
4
+
5
+ module Representer
6
+ module JPG
7
+ class Song; end
8
+ class Album < Roar::Representer::XML; end
9
+ end
10
+ end
11
+
12
+
13
+
14
+ class RailsRepresenterMethodsTest < MiniTest::Spec
15
+ describe "Rails::RepresenterMethods" do
16
+ before do
17
+ @c = Representer::JPG::Album # TODO: mix Rails into Base, not XML only.
18
+ end
19
+
20
+ it "provides conventions for #collection" do
21
+ @c.collection :songs
22
+
23
+ @d = @c.representable_attrs.first
24
+ assert_equal Representer::JPG::Song, @d.sought_type
25
+ assert @d.array?
26
+ end
27
+
28
+ it "provides conventions for #representation_name" do
29
+ assert_equal "album", @c.representation_name
30
+ end
31
+ end
32
+ end