restful 0.2.20
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.markdown +40 -0
- data/LICENSE.markdown +22 -0
- data/README.markdown +126 -0
- data/Rakefile +22 -0
- data/TODO.markdown +10 -0
- data/init.rb +1 -0
- data/lib/restful.rb +65 -0
- data/lib/restful/apimodel/attribute.rb +17 -0
- data/lib/restful/apimodel/collection.rb +22 -0
- data/lib/restful/apimodel/link.rb +21 -0
- data/lib/restful/apimodel/map.rb +41 -0
- data/lib/restful/apimodel/resource.rb +23 -0
- data/lib/restful/converters/active_record.rb +160 -0
- data/lib/restful/rails.rb +22 -0
- data/lib/restful/rails/action_controller.rb +14 -0
- data/lib/restful/rails/active_record/configuration.rb +219 -0
- data/lib/restful/rails/active_record/metadata_tools.rb +102 -0
- data/lib/restful/serializers/atom_like_serializer.rb +51 -0
- data/lib/restful/serializers/base.rb +58 -0
- data/lib/restful/serializers/hash_serializer.rb +46 -0
- data/lib/restful/serializers/json_serializer.rb +18 -0
- data/lib/restful/serializers/params_serializer.rb +46 -0
- data/lib/restful/serializers/xml_serializer.rb +160 -0
- data/rails/init.rb +1 -0
- data/restful.gemspec +17 -0
- data/test/converters/active_record_converter_test.rb +147 -0
- data/test/converters/basic_types_converter_test.rb +99 -0
- data/test/fixtures/models/paginated_collection.rb +3 -0
- data/test/fixtures/models/person.rb +29 -0
- data/test/fixtures/models/pet.rb +5 -0
- data/test/fixtures/models/wallet.rb +5 -0
- data/test/fixtures/people.json.yaml +107 -0
- data/test/fixtures/people.xml.yaml +117 -0
- data/test/fixtures/pets.json.yaml +20 -0
- data/test/fixtures/pets.xml.yaml +31 -0
- data/test/rails/active_record_metadata_test.rb +23 -0
- data/test/rails/configuration_test.rb +47 -0
- data/test/rails/restful_publish_test.rb +54 -0
- data/test/serializers/atom_serializer_test.rb +33 -0
- data/test/serializers/json_serializer_test.rb +90 -0
- data/test/serializers/params_serializer_test.rb +76 -0
- data/test/serializers/xml_serializer_test.rb +51 -0
- data/test/test_helper.rb +154 -0
- metadata +106 -0
@@ -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,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
|