benjaminkrause-restful 0.2.8

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 (44) hide show
  1. data/CHANGES.markdown +8 -0
  2. data/LICENSE.markdown +22 -0
  3. data/README.markdown +123 -0
  4. data/Rakefile +22 -0
  5. data/TODO.markdown +9 -0
  6. data/init.rb +1 -0
  7. data/lib/restful.rb +65 -0
  8. data/lib/restful/apimodel/attribute.rb +17 -0
  9. data/lib/restful/apimodel/collection.rb +22 -0
  10. data/lib/restful/apimodel/link.rb +21 -0
  11. data/lib/restful/apimodel/map.rb +41 -0
  12. data/lib/restful/apimodel/resource.rb +23 -0
  13. data/lib/restful/converters/active_record.rb +131 -0
  14. data/lib/restful/rails.rb +22 -0
  15. data/lib/restful/rails/action_controller.rb +14 -0
  16. data/lib/restful/rails/active_record/configuration.rb +167 -0
  17. data/lib/restful/rails/active_record/metadata_tools.rb +106 -0
  18. data/lib/restful/serializers/atom_like_serializer.rb +51 -0
  19. data/lib/restful/serializers/base.rb +57 -0
  20. data/lib/restful/serializers/hash_serializer.rb +59 -0
  21. data/lib/restful/serializers/json_serializer.rb +20 -0
  22. data/lib/restful/serializers/params_serializer.rb +46 -0
  23. data/lib/restful/serializers/xml_serializer.rb +161 -0
  24. data/rails/init.rb +1 -0
  25. data/restful.gemspec +17 -0
  26. data/test/converters/active_record_converter_test.rb +122 -0
  27. data/test/converters/basic_types_converter_test.rb +48 -0
  28. data/test/fixtures/models/paginated_collection.rb +4 -0
  29. data/test/fixtures/models/person.rb +29 -0
  30. data/test/fixtures/models/pet.rb +5 -0
  31. data/test/fixtures/models/wallet.rb +5 -0
  32. data/test/fixtures/people.json.yaml +94 -0
  33. data/test/fixtures/people.xml.yaml +123 -0
  34. data/test/fixtures/pets.json.yaml +20 -0
  35. data/test/fixtures/pets.xml.yaml +31 -0
  36. data/test/rails/active_record_metadata_test.rb +23 -0
  37. data/test/rails/configuration_test.rb +40 -0
  38. data/test/rails/restful_publish_test.rb +52 -0
  39. data/test/serializers/atom_serializer_test.rb +33 -0
  40. data/test/serializers/json_serializer_test.rb +82 -0
  41. data/test/serializers/params_serializer_test.rb +76 -0
  42. data/test/serializers/xml_serializer_test.rb +51 -0
  43. data/test/test_helper.rb +147 -0
  44. metadata +98 -0
@@ -0,0 +1,5 @@
1
+ class Pet < ActiveRecord::Base
2
+ belongs_to :owner, :class_name => "Person", :foreign_key => "person_id"
3
+
4
+ apiable
5
+ end
@@ -0,0 +1,5 @@
1
+ class Wallet < ActiveRecord::Base
2
+ belongs_to :person
3
+
4
+ apiable
5
+ end
@@ -0,0 +1,94 @@
1
+ bloggs:
2
+ |
3
+ {
4
+ "restful_url": "http://example.com:3000/people/<%= @person.to_param %>",
5
+ "wallet": {
6
+ "restful_url": "http://example.com:3000/wallets/<%= @person.wallet.to_param %>",
7
+ "contents": "an old photo, 5 euros in coins"
8
+ },
9
+ "current_location": "Under a tree",
10
+ "name": "Joe Bloggs",
11
+ "pets": [ {
12
+ "restful_url": "http://example.com:3000/pets/<%= @person.pets.first.to_param %>",
13
+ "name": "mietze"
14
+ }],
15
+ "created_at": "<%= @person.created_at.xmlschema %>"
16
+ }
17
+
18
+ bloggs_with_oldest_pet:
19
+ |
20
+ {
21
+ "restful_url": "http://example.com:3000/people/<%= @person.to_param %>",
22
+ "oldest_pet": {
23
+ "restful_url": "http://example.com:3000/pets/<%= @person.pets.first.to_param %>",
24
+ "name": "mietze"
25
+ }
26
+ }
27
+
28
+ bloggs_with_birthday:
29
+ |
30
+ {
31
+ "restful_url": "http://example.com:3000/people/<%= @person.to_param %>",
32
+ "birthday": "<%= @person.birthday.to_s(:db) %>"
33
+ }
34
+
35
+ bloggs_with_pets_ages_hash:
36
+ |
37
+ {
38
+ "restful_url": "http://example.com:3000/people/<%= @person.to_param %>",
39
+ "pets_ages_hash": {
40
+ "mietze": 200,
41
+ "motze": 100
42
+ }
43
+ }
44
+ bloggs_with_has_pets:
45
+ |
46
+ {
47
+ "restful_url": "http://example.com:3000/people/<%= @person.to_param %>",
48
+ "has_pets": true
49
+ }
50
+ bloggs_with_hasno_pets:
51
+ |
52
+ {
53
+ "restful_url": "http://example.com:3000/people/<%= @person.to_param %>",
54
+ "has_pets": false
55
+ }
56
+
57
+
58
+ bloggs_da_pet_hater:
59
+ |
60
+ {
61
+ "restful_url": "http://example.com:3000/people/<%= @person.to_param %>",
62
+ "wallet": {
63
+ "restful_url": "http://example.com:3000/wallets/<%= @person.wallet.to_param %>",
64
+ "contents": "an old photo, 5 euros in coins"
65
+ },
66
+ "current_location": "Under a tree",
67
+ "name": "Joe Bloggs",
68
+ "pets": [],
69
+ "created_at": "<%= @person.created_at.xmlschema %>"
70
+ }
71
+
72
+ hash_with_person:
73
+ |
74
+ {
75
+ "total_hits": 1,
76
+ "a_person": {
77
+ "restful_url": "http://example.com:3000/people/<%= @person.to_param %>",
78
+ "name": "Joe Bloggs"
79
+ }
80
+ }
81
+
82
+ hash_with_people:
83
+ |
84
+ {
85
+ "total_hits": 2,
86
+ "people": [{
87
+ "restful_url": "http://example.com:3000/people/<%= @person.to_param %>",
88
+ "name": "Joe Bloggs"
89
+ },
90
+ {
91
+ "restful_url": "http://example.com:3000/people/<%= @person.to_param %>",
92
+ "name": "Joe Bloggs"
93
+ }]
94
+ }
@@ -0,0 +1,123 @@
1
+ joe_bloggs:
2
+ |
3
+ <?xml version="1.0" encoding="UTF-8"?>
4
+ <person>
5
+ <restful-url type="link">http://example.com:3000/people/<%= @person.id %></restful-url>
6
+ <name>Joe Bloggs</name>
7
+ <wallet-restful-url type="link">http://example.com:3000/wallets/<%= @wallet.id %></wallet-restful-url>
8
+ </person>
9
+
10
+ no_wallet:
11
+ |
12
+ <?xml version="1.0" encoding="UTF-8"?>
13
+ <person>
14
+ <restful-url type="link">http://example.com:3000/people/<%= @person.id %></restful-url>
15
+ <location-sentence><%= @person.location_sentence %></location-sentence>
16
+ </person>
17
+
18
+ with_oldest_pet:
19
+ |
20
+ <?xml version="1.0" encoding="UTF-8"?>
21
+ <person>
22
+ <restful-url type="link">http://example.com:3000/people/<%= @person.id %></restful-url>
23
+ <oldest-pet>
24
+ <restful-url type="link">http://example.com:3000/pets/<%= @person.oldest_pet.id %></restful-url>
25
+ <name><%= @person.oldest_pet.name %></name>
26
+ <person-restful-url type="link">http://example.com:3000/people/<%= @person.id %></person-restful-url>
27
+ </oldest-pet>
28
+ </person>
29
+
30
+ with_oldest_pet_species:
31
+ |
32
+ <?xml version="1.0" encoding="UTF-8"?>
33
+ <person>
34
+ <restful-url type="link">http://example.com:3000/people/<%= @person.id %></restful-url>
35
+ <oldest-pet>
36
+ <restful-url type="link">http://example.com:3000/pets/<%= @person.oldest_pet.id %></restful-url>
37
+ <species type="integer"><%= @person.oldest_pet.species %></species>
38
+ </oldest-pet>
39
+ </person>
40
+
41
+ joe_with_birthday:
42
+ |
43
+ <?xml version="1.0" encoding="UTF-8"?>
44
+ <person>
45
+ <restful-url type="link">http://example.com:3000/people/<%= @person.id %></restful-url>
46
+ <birthday type="date"><%= @person.birthday.to_s(:db) %></birthday>
47
+ </person>
48
+
49
+ joe_with_zwiebelleder:
50
+ |
51
+ <?xml version="1.0" encoding="UTF-8"?>
52
+ <person>
53
+ <restful-url type="link">http://example.com:3000/people/<%= @person.id %></restful-url>
54
+ <wallet nil="true"></wallet>
55
+ </person>
56
+
57
+ atom_person:
58
+ |
59
+ <?xml version="1.0" encoding="UTF-8"?>
60
+ <person xml:base="http://example.com:3000">
61
+ <created-at><%= @person.created_at.xmlschema %></created-at>
62
+ <link rel="self" href="/people/<%= @person.id %>"/>
63
+ <name>Joe Bloggs</name>
64
+ <current-location>Under a tree</current-location>
65
+ <pets>
66
+ <pet>
67
+ <link rel="self" href="/pets/<%= @pet.id %>"/>
68
+ <name>mietze</name>
69
+ </pet>
70
+ </pets>
71
+ <wallet>
72
+ <link rel="self" href="/wallets/<%= @wallet.id %>"/>
73
+ <contents>an old photo, 5 euros in coins</contents>
74
+ </wallet>
75
+ </person>
76
+
77
+ verbose_with_pets:
78
+ |
79
+ <?xml version="1.0" encoding="UTF-8"?>
80
+ <person>
81
+ <restful-url type="link">http://example.com:3000/people/<%= @person.id %></restful-url>
82
+ <name>Joe Bloggs</name>
83
+ <created-at type="datetime"><%= @person.created_at.xmlschema %></created-at>
84
+ <current-location>Under a tree</current-location>
85
+ <pets type="array">
86
+ <pet>
87
+ <restful-url type="link">http://example.com:3000/pets/<%= @pet.id %></restful-url>
88
+ <name>mietze</name>
89
+ </pet>
90
+ </pets>
91
+ <wallet-restful-url type="link" nil="true"></wallet-restful-url>
92
+ </person>
93
+
94
+ with_pets_and_expanded_wallet:
95
+ |
96
+ <?xml version="1.0" encoding="UTF-8"?>
97
+ <person>
98
+ <restful-url type="link">http://example.com:3000/people/<%= @person.id %></restful-url>
99
+ <name>Joe Bloggs</name>
100
+ <created-at type="datetime"><%= @person.created_at.xmlschema %></created-at>
101
+ <current-location>Under a tree</current-location>
102
+ <pets type="array">
103
+ <pet>
104
+ <restful-url type="link">http://example.com:3000/pets/<%= @pet.id %></restful-url>
105
+ <name>mietze</name>
106
+ </pet>
107
+ </pets>
108
+ <wallet>
109
+ <restful-url type="link">http://example.com:3000/wallets/<%= @wallet.id %></restful-url>
110
+ <contents>an old photo, 5 euros in coins</contents>
111
+ </wallet>
112
+ </person>
113
+
114
+ hashy_person:
115
+ |
116
+ <?xml version="1.0" encoding="UTF-8"?>
117
+ <person>
118
+ <restful-url type="link">http://example.com:3000/people/<%= @person.id %></restful-url>
119
+ <pets-ages-hash>
120
+ <mietze>200</mietze>
121
+ <motze>100</motze>
122
+ </pets-ages-hash>
123
+ </person>
@@ -0,0 +1,20 @@
1
+ pets_array:
2
+ |
3
+ [
4
+ {
5
+ "restful_url": "http://example.com:3000/pets/<%= @person.pets.first.to_param %>",
6
+ "name": "mietze"
7
+ }
8
+ ]
9
+
10
+ pets_array_with_total_entries:
11
+ |
12
+ {
13
+ "total_entries": 1001,
14
+ "pets": [
15
+ {
16
+ "restful_url": "http://example.com:3000/pets/<%= @person.pets.first.to_param %>",
17
+ "name": "mietze"
18
+ }
19
+ ]
20
+ }
@@ -0,0 +1,31 @@
1
+ nameless_pet:
2
+ |
3
+ <?xml version="1.0" encoding="UTF-8"?>
4
+ <pet>
5
+ <restful-url type="link">http://example.com:3000/pets/<%= @pet.id %></restful-url>
6
+ <owner>
7
+ <restful-url type="link">http://example.com:3000/people/<%= @person.id %></restful-url>
8
+ <name>Joe Bloggs</name>
9
+ <current-location>Under a tree</current-location>
10
+ <wallet-restful-url type="link">http://example.com:3000/wallets/<%= @wallet.id %></wallet-restful-url>
11
+ </owner>
12
+ </pet>
13
+
14
+ gracie:
15
+ |
16
+ <?xml version="1.0" encoding="UTF-8"?>
17
+ <pet>
18
+ <person-restful-url type="link">http://example.com:3000/people/<%= @person.id %></person-restful-url>
19
+ <species>123</species>
20
+ <name>Gracie</name>
21
+ </pet>
22
+
23
+ pets_array:
24
+ |
25
+ <?xml version="1.0" encoding="UTF-8"?>
26
+ <pets>
27
+ <pet>
28
+ <restful-url type="link">http://example.com:3000/pets/<%= @pet.id %></restful-url>
29
+ <name>mietze</name>
30
+ </pet>
31
+ </pets>
@@ -0,0 +1,23 @@
1
+ require File.dirname(__FILE__) + '/../test_helper.rb'
2
+
3
+ context "active record metadata" do
4
+ setup do
5
+ Person.restful_publish(:name, :pets => [:name, :species])
6
+ Pet.restful_publish(:person_id, :name) # person_id gets converted to a link automagically.
7
+
8
+ @person = Person.create(:name => "Jimmy Jones", :current_location => "Under a tree")
9
+ @pet = @person.pets.create(:name => "Mietze", :species => "cat")
10
+ end
11
+
12
+ teardown do
13
+ reset_config
14
+ end
15
+
16
+ specify "should be able to convert a collection to an array of resources" do
17
+ resources = Restful::Rails.tools.convert_collection_to_resources(@person, :pets, Restful.cfg)
18
+ pet = resources.first
19
+
20
+ resources.size.should.equal 1
21
+ pet.url.should.equal @pet.to_restful.url
22
+ end
23
+ end
@@ -0,0 +1,40 @@
1
+ require File.dirname(__FILE__) + '/../test_helper.rb'
2
+
3
+ context "Configuration" do
4
+ specify "should have an empty whitelist if only restful options are passed in" do
5
+ config = Restful.cfg
6
+ config.restful_options[:expansion] = :expanded
7
+ config.whitelisted.should.empty
8
+ end
9
+
10
+ specify "should return fields without restful_options via whitelisted" do
11
+ config = Restful.cfg([:one, :two])
12
+ config.restful_options[:expansion] = :expanded
13
+ config.whitelisted.should.not.include :restful_options
14
+ end
15
+
16
+ specify "should have 0 whitelisted fields if none were specified" do
17
+ end
18
+
19
+ specify "should know if it has restful_options" do
20
+ config = Restful.cfg([:one, :two])
21
+ config.restful_options[:expansion] = :expanded
22
+ config.restful_options.should.not.empty
23
+ end
24
+
25
+ specify "should be able to handle nested whitelist attributes" do
26
+ config = Restful.cfg(:one, :two => [:a, :b])
27
+ config.nested(:two).whitelisted.should.== [:a,:b]
28
+ end
29
+
30
+ specify "should know which attributes are published" do
31
+ config = Restful.cfg(:one, :two => [:a, :b])
32
+ config.published?(:two).should.== true
33
+ config.published?(:one).should.== true
34
+ end
35
+
36
+ specify "should know if it is nested" do
37
+ config = Restful.cfg(:one, :restful_options => {:nested => true})
38
+ config.nested?.should.== true
39
+ end
40
+ end
@@ -0,0 +1,52 @@
1
+ require File.dirname(__FILE__) + '/../test_helper.rb'
2
+
3
+ context "restful publish" do
4
+ teardown do
5
+ reset_config
6
+ end
7
+
8
+ specify "should result in a method .published?(:attr_key) return true for published attributes" do
9
+ Pet.restful_publish(:person_id, :name) # person_id gets converted to a link automagically.
10
+
11
+ Pet.restful_config.published?(:name).should.equal true
12
+ Pet.restful_config.published?(:pets).should.equal false
13
+ Pet.restful_config.published?(:species).should.equal false
14
+ end
15
+
16
+ specify "should have restful_options as an empty hash after calling restful_publish" do
17
+ Person.restful_publish(:name, :pets => [:name, :species])
18
+ Person.restful_config.restful_options.should.==({})
19
+ end
20
+
21
+ specify "should include attributes when publishe parameter is passed to to_restful" do
22
+ Person.restful_publish(:name)
23
+ Pet.restful_publish(:name)
24
+
25
+ @person = Person.create
26
+ @pet = @person.pets.create(:name => "Mietze")
27
+
28
+ @pet.to_restful(:include => :owner).values.map(&:name).should.include :owner
29
+ Pet.restful_config.whitelisted.should.not.include? :owner
30
+ end
31
+ end
32
+
33
+ context "api publishing with nesting" do
34
+ teardown do
35
+ reset_config
36
+ end
37
+
38
+ specify "should result in a method .published?(:attr_key) return true for nested attributes" do
39
+ Person.restful_publish(:name, :pets => [:name, :species])
40
+ Person.restful_config.published?(:pets).should.equal true
41
+ end
42
+
43
+ specify "should be invoke to_restful on the nested model with the specified nested attributes" do
44
+ Person.restful_publish(:name, :pets => [:name, :species])
45
+ @person = Person.create(:name => "Joe Bloggs", :current_location => "Under a tree")
46
+ @pet = @person.pets.create(:name => "Mietze", :species => "cat")
47
+
48
+ Pet.any_instance.expects(:to_restful).with { |arg| arg.whitelisted == [:name, :species] }
49
+ @person.to_restful
50
+ end
51
+ end
52
+
@@ -0,0 +1,33 @@
1
+ require File.dirname(__FILE__) + '/../test_helper.rb'
2
+
3
+ context "params serializer" do
4
+
5
+ setup do
6
+ Person.restful_publish(:name, :current_location, :pets, :wallet, :created_at)
7
+ Pet.restful_publish(:name)
8
+ Wallet.restful_publish(:contents)
9
+
10
+ @person = Person.create(:name => "Joe Bloggs", :current_location => "Under a tree")
11
+ @pet = @person.pets.create(:species => "cat", :age => 200, :name => "mietze")
12
+ @wallet = @person.wallet = Wallet.new(:contents => "an old photo, 5 euros in coins")
13
+ @person.save
14
+ end
15
+
16
+ teardown do
17
+ reset_config
18
+ end
19
+
20
+ specify "serialize to xml, atom style" do
21
+ xml_should_eql_fixture(@person.to_restful_atom_like, "people", :atom_person)
22
+ end
23
+
24
+ specify "deserialize from atom style xml" do
25
+ restful = @pet.to_restful
26
+ expected = restful.serialize(:atom_like)
27
+ serializer = Restful::Serializers::AtomLikeSerializer.new
28
+ resource = serializer.deserialize(expected)
29
+ actual = serializer.serialize(resource)
30
+
31
+ xml_should_eql(expected, actual)
32
+ end
33
+ end
@@ -0,0 +1,82 @@
1
+ require File.dirname(__FILE__) + '/../test_helper.rb'
2
+
3
+ context "json serializer" do
4
+
5
+ setup do
6
+ Person.restful_publish(:name, :current_location, :pets, :wallet, :created_at)
7
+ Pet.restful_publish(:name)
8
+ Wallet.restful_publish(:contents)
9
+
10
+ @person = Person.create(:name => "Joe Bloggs", :current_location => "Under a tree", :birthday => "2009-09-19")
11
+ @pet = @person.pets.create(:species => "cat", :age => 200, :name => "mietze")
12
+ @wallet = @person.wallet = Wallet.new(:contents => "an old photo, 5 euros in coins")
13
+ @person.save
14
+ end
15
+
16
+ teardown { reset_config }
17
+
18
+ specify "serialize to json" do
19
+ json_should_eql_fixture(@person.to_restful_json, "people", :bloggs)
20
+ end
21
+
22
+ specify "should be able to serialize objects with empty collections" do
23
+ @person.pets = []
24
+
25
+ assert_nothing_raised do
26
+ json_should_eql_fixture(@person.to_restful_json, "people", :bloggs_da_pet_hater)
27
+ end
28
+ end
29
+
30
+ specify "should serialize date type correctly" do
31
+ json_should_eql_fixture(@person.to_restful_json(:birthday), "people", :bloggs_with_birthday)
32
+ end
33
+
34
+ specify "should not bug out on nil values in date fields" do
35
+ person = Person.create :created_at => nil, :birthday => nil, :last_login => nil
36
+ person.created_at = nil
37
+ expected = "{\"birthday\":null,\"restful_url\":\"http://example.com:3000/people/#{person.to_param}\",\"last_login\":null,\"created_at\":null}"
38
+ assert_nothing_raised do
39
+ actual = person.to_restful_json([:created_at, :birthday, :last_login])
40
+ json_should_eql(actual, expected)
41
+ end
42
+ end
43
+
44
+ specify "should serialize hashes correctly" do
45
+ @person.pets.create(:species => "cat", :age => 100, :name => "motze")
46
+ json_should_eql_fixture(@person.to_restful_json(:pets_ages_hash), "people", :bloggs_with_pets_ages_hash)
47
+ end
48
+
49
+ specify "should render boolean values correctly" do
50
+ json_should_eql_fixture(@person.to_restful_json(:has_pets), "people", :bloggs_with_has_pets)
51
+ @person.pets = []
52
+ @person.save!
53
+ json_should_eql_fixture(@person.to_restful_json(:has_pets), "people", :bloggs_with_hasno_pets)
54
+ end
55
+
56
+ specify "should not ever use dashes as hash keys but underscores" do
57
+ assert_nothing_raised do
58
+ json_should_eql_fixture(@person.to_restful_json(:oldest_pet), "people", :bloggs_with_oldest_pet)
59
+ end
60
+ end
61
+
62
+ specify "should serialize collections correctly" do
63
+ json_should_eql_fixture(@person.pets.to_restful_json, "pets", :pets_array)
64
+ end
65
+
66
+ specify "should be able to serialize collections with total entries info" do
67
+ pets = PaginatedCollection.new(@person.pets)
68
+ pets.total_entries = 1001
69
+
70
+ json_should_eql_fixture(pets.to_restful_json, "pets", :pets_array_with_total_entries)
71
+ end
72
+
73
+ specify "should be able to serialize a map" do
74
+ Person.restful_publish(:name)
75
+ json_should_eql_fixture({ "total_hits" => 1, "a_person" => @person }.to_restful_json, "people", :hash_with_person)
76
+ end
77
+
78
+ specify "should be able to serialize a map with arrays as values" do
79
+ Person.restful_publish(:name)
80
+ json_should_eql_fixture({ "total_hits" => 2, "people" => [ @person, @person ] }.to_restful_json, "people", :hash_with_people)
81
+ end
82
+ end