roar 0.0.1.alpha1 → 0.8.0

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.
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