restful 0.2.20

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 +40 -0
  2. data/LICENSE.markdown +22 -0
  3. data/README.markdown +126 -0
  4. data/Rakefile +22 -0
  5. data/TODO.markdown +10 -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 +160 -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 +219 -0
  17. data/lib/restful/rails/active_record/metadata_tools.rb +102 -0
  18. data/lib/restful/serializers/atom_like_serializer.rb +51 -0
  19. data/lib/restful/serializers/base.rb +58 -0
  20. data/lib/restful/serializers/hash_serializer.rb +46 -0
  21. data/lib/restful/serializers/json_serializer.rb +18 -0
  22. data/lib/restful/serializers/params_serializer.rb +46 -0
  23. data/lib/restful/serializers/xml_serializer.rb +160 -0
  24. data/rails/init.rb +1 -0
  25. data/restful.gemspec +17 -0
  26. data/test/converters/active_record_converter_test.rb +147 -0
  27. data/test/converters/basic_types_converter_test.rb +99 -0
  28. data/test/fixtures/models/paginated_collection.rb +3 -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 +107 -0
  33. data/test/fixtures/people.xml.yaml +117 -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 +47 -0
  38. data/test/rails/restful_publish_test.rb +54 -0
  39. data/test/serializers/atom_serializer_test.rb +33 -0
  40. data/test/serializers/json_serializer_test.rb +90 -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 +154 -0
  44. metadata +106 -0
@@ -0,0 +1,3 @@
1
+ class PaginatedCollection < Array
2
+ attr_accessor :total_entries, :name
3
+ end
@@ -0,0 +1,29 @@
1
+ class Person < ActiveRecord::Base
2
+ has_many :pets
3
+ has_one :wallet
4
+
5
+ accepts_nested_attributes_for :pets
6
+ accepts_nested_attributes_for :wallet
7
+
8
+ def oldest_pet
9
+ pets.first :order => "age DESC"
10
+ end
11
+
12
+ def location_sentence
13
+ "Hi. I'm currently in #{ current_location }"
14
+ end
15
+
16
+ def has_pets
17
+ pets.size > 0
18
+ end
19
+
20
+ def pets_ages_hash
21
+ returning pets_hash = {} do
22
+ pets.each do |pet|
23
+ pets_hash[pet.name] = pet.age
24
+ end
25
+ end
26
+ end
27
+
28
+ apiable
29
+ end
@@ -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,107 @@
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
+ }
95
+
96
+ hash_with_rich_person:
97
+ |
98
+ {
99
+ "person": {
100
+ "restful_url": "http://example.com:3000/people/<%= @person.to_param %>",
101
+ "name": "Joe Bloggs",
102
+ "wallet": {
103
+ "restful_url": "http://example.com:3000/wallets/<%= @person.wallet.to_param %>",
104
+ "contents": "<%= @person.wallet.contents %>"
105
+ }
106
+ }
107
+ }
@@ -0,0 +1,117 @@
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_no_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
+ <wallet-restful-url type="link" nil="true"></wallet-restful-url>
86
+ </person>
87
+
88
+ with_pets_and_expanded_wallet:
89
+ |
90
+ <?xml version="1.0" encoding="UTF-8"?>
91
+ <person>
92
+ <restful-url type="link">http://example.com:3000/people/<%= @person.id %></restful-url>
93
+ <name>Joe Bloggs</name>
94
+ <created-at type="datetime"><%= @person.created_at.xmlschema %></created-at>
95
+ <current-location>Under a tree</current-location>
96
+ <pets type="array">
97
+ <pet>
98
+ <restful-url type="link">http://example.com:3000/pets/<%= @pet.id %></restful-url>
99
+ <name>mietze</name>
100
+ </pet>
101
+ </pets>
102
+ <wallet>
103
+ <restful-url type="link">http://example.com:3000/wallets/<%= @wallet.id %></restful-url>
104
+ <contents>an old photo, 5 euros in coins</contents>
105
+ </wallet>
106
+ </person>
107
+
108
+ hashy_person:
109
+ |
110
+ <?xml version="1.0" encoding="UTF-8"?>
111
+ <person>
112
+ <restful-url type="link">http://example.com:3000/people/<%= @person.id %></restful-url>
113
+ <pets-ages-hash>
114
+ <mietze>200</mietze>
115
+ <motze>100</motze>
116
+ </pets-ages-hash>
117
+ </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,47 @@
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 know if it has restful_options" do
17
+ config = Restful.cfg([:one, :two])
18
+ config.restful_options[:expansion] = :expanded
19
+ config.restful_options.should.not.empty
20
+ end
21
+
22
+ specify "should be able to handle nested whitelist attributes" do
23
+ config = Restful.cfg(:one, :two => [:a, :b])
24
+ config.nested(:two).whitelisted.should.== [:a,:b]
25
+ end
26
+
27
+ specify "should know which attributes are published" do
28
+ config = Restful.cfg(:one, :two => [:a, :b])
29
+ config.published?(:two).should.== true
30
+ config.published?(:one).should.== true
31
+ end
32
+
33
+ specify "should know if it is nested" do
34
+ config = Restful.cfg(:one, :restful_options => {:nested => true})
35
+ config.nested?.should.== true
36
+ end
37
+
38
+ specify "should preserve :include when :restful_options are present" do
39
+ Person.restful_publish :pets
40
+ p = Person.create
41
+ p.pets.create
42
+
43
+ restful = p.to_restful :include => :pets, :restful_options => {}
44
+ restful.collections.size.should.== 1
45
+ end
46
+
47
+ end
@@ -0,0 +1,54 @@
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
+ end
21
+
22
+ context "api publishing with nesting" do
23
+
24
+ teardown do
25
+ reset_config
26
+ end
27
+
28
+ specify "should result in a method .published?(:attr_key) return true for nested attributes" do
29
+ Person.restful_publish(:name, :pets => [:name, :species])
30
+ Person.restful_config.published?(:pets).should.equal true
31
+ end
32
+
33
+ specify "should be invoke to_restful on the nested model with the specified nested attributes" do
34
+ Person.restful_publish(:name, :pets => [:name, :species])
35
+ @person = Person.create(:name => "Joe Bloggs", :current_location => "Under a tree")
36
+ @pet = @person.pets.create(:name => "Mietze", :species => "cat")
37
+
38
+ Pet.any_instance.expects(:to_restful).with { |arg| arg.whitelisted == [:name, :species] }
39
+ @person.to_restful
40
+ end
41
+
42
+ specify "should pass config over to nested objects. " do
43
+ Pet.restful_publish :name
44
+ House.restful_publish :people
45
+
46
+ house = House.create
47
+ person = house.people.create :name => "johannes"
48
+ person.pets.create :name => "puenktchen"
49
+
50
+ restful = [house].to_restful(:include => :pets)
51
+
52
+ restful.value.first.collections.first.value.first.collections.size.should.== 1
53
+ end
54
+ end