restful 0.2.20

Sign up to get free protection for your applications and to get access to all the features.
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