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
data/CHANGES.markdown
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
17. Aug 2009 - 0.2.6
|
2
|
+
|
3
|
+
* added :include option in to_restful.
|
4
|
+
|
5
|
+
18. Aug 2009 - 0.2.7
|
6
|
+
|
7
|
+
* fixed issue where configurations where overwriting each other.
|
8
|
+
* added hash#to_restful
|
9
|
+
|
10
|
+
19. Aug 2009 - 0.2.12
|
11
|
+
|
12
|
+
* added ability to publish :wallet-restful-url (explicitly collapsed)
|
13
|
+
|
14
|
+
20. Aug 2009
|
15
|
+
|
16
|
+
- 0.2.13
|
17
|
+
|
18
|
+
* hash serializer no longer dereferences ids
|
19
|
+
|
20
|
+
- 0.2.14
|
21
|
+
|
22
|
+
* arrays names now use base_class of content models
|
23
|
+
* restful_path defaults to using base_class in path
|
24
|
+
* if array responds to name, use this as collection name
|
25
|
+
|
26
|
+
- 0.2.15
|
27
|
+
|
28
|
+
* :includes like active record to_xml to_json
|
29
|
+
|
30
|
+
21. Aug 2009
|
31
|
+
|
32
|
+
* refactored HashSerializer to be much clearer
|
33
|
+
|
34
|
+
26. Aug 2009
|
35
|
+
|
36
|
+
* fixed issue with link collapsing
|
37
|
+
|
38
|
+
28. Aug 2009
|
39
|
+
|
40
|
+
* fixed cascading of :includes
|
data/LICENSE.markdown
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2009 SalesKing GmbH
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
22
|
+
|
data/README.markdown
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
# Disclaimer
|
2
|
+
|
3
|
+
!!! Refactor this shice. Seriously, this has devolved into some nasty-ass code.
|
4
|
+
|
5
|
+
# Why?
|
6
|
+
|
7
|
+
Aims to provide a production quality Rest API to your Rails app, with the following features:
|
8
|
+
|
9
|
+
* whitelisting
|
10
|
+
* flexible xml formats with good defaults
|
11
|
+
* all resources are referred to by url and not by id; expose a "web of resources"
|
12
|
+
|
13
|
+
# Serializers
|
14
|
+
|
15
|
+
Getting started
|
16
|
+
============================
|
17
|
+
In order to make your models apiable add
|
18
|
+
|
19
|
+
`apiable`
|
20
|
+
|
21
|
+
to your model. Next, define which properties you want to export, so within the model write something like:
|
22
|
+
|
23
|
+
`self.restful_publish(:name, :current-location, :pets)`
|
24
|
+
|
25
|
+
Configuration
|
26
|
+
=============
|
27
|
+
|
28
|
+
Some example configurations:
|
29
|
+
|
30
|
+
# Person
|
31
|
+
restful_publish :name, :pets, :restful_options => { :expansion => :expanded } # default on level 1-2: expanded. default above: collapsed.
|
32
|
+
restful_publish :name, :pets, :wallet => :contents, :restful_options => { :expansion => :expanded } # combined options and expansion rules
|
33
|
+
restful_publish :name, :pets, :restful_options => { :collapsed => :pets } # collapsed pets, even though they are on the second level.
|
34
|
+
restful_publish :name, :pets, :restful_options => { :force_expanded => [:pets, :wallet] }
|
35
|
+
|
36
|
+
## explicitly collapsed
|
37
|
+
restful_options :wallet-restful-url
|
38
|
+
|
39
|
+
# Pet
|
40
|
+
restful_publish :name, :person # expands person per default because it is on the second level. Does not expand person.pets.first.person, since this is higher than second level.
|
41
|
+
|
42
|
+
Options
|
43
|
+
=======
|
44
|
+
|
45
|
+
You can add includes to your call like this:
|
46
|
+
|
47
|
+
pet.to_restful_json :include => :owner.
|
48
|
+
|
49
|
+
Rails-like
|
50
|
+
==========
|
51
|
+
|
52
|
+
This format sticks to xml_simple, adding links as `<association-name-restful-url>` nodes of type "link".
|
53
|
+
|
54
|
+
|
55
|
+
`Person.last.to_restful.serialize(:xml)` OR
|
56
|
+
`Person.last.to_restful_xml` results in something like...
|
57
|
+
|
58
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
59
|
+
<person>
|
60
|
+
<restful-url type="link">http://example.com:3000/people/1</restful-url>
|
61
|
+
<name>Joe Bloggs</name>
|
62
|
+
<current-location>Under a tree</current-location>
|
63
|
+
<pets type="array">
|
64
|
+
<pet>
|
65
|
+
<restful-url type="link">http://example.com:3000/pets/1</restful-url>
|
66
|
+
<person-restful-url type="link">http://example.com:3000/people/1</person-restful-url>
|
67
|
+
<name nil="true"></name>
|
68
|
+
</pet>
|
69
|
+
</pets>
|
70
|
+
<sex>
|
71
|
+
<restful-url type="link">http://example.com:3000/sexes/1</restful-url>
|
72
|
+
<sex>male</sex>
|
73
|
+
</sex>
|
74
|
+
</person>
|
75
|
+
|
76
|
+
|
77
|
+
Atom-like
|
78
|
+
=========
|
79
|
+
|
80
|
+
`Person.last.to_restful.serialize(:atom_like)` OR
|
81
|
+
`Person.last.to_restful_atom_like` results in something like...
|
82
|
+
|
83
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
84
|
+
<person xml:base="http://example.com:3000">
|
85
|
+
<link rel="self" href="/people/1"/>
|
86
|
+
<name>Joe Bloggs</name>
|
87
|
+
<current-location>Under a tree</current-location>
|
88
|
+
<pets>
|
89
|
+
<pet>
|
90
|
+
<link rel="self" href="/pets/1"/>
|
91
|
+
<link rel="person_id" href="/people/1"/>
|
92
|
+
<name></name>
|
93
|
+
</pet>
|
94
|
+
</pets>
|
95
|
+
<sex>
|
96
|
+
<link rel="self" href="/sexes/1"/>
|
97
|
+
<sex>male</sex>
|
98
|
+
</sex>
|
99
|
+
</person>
|
100
|
+
|
101
|
+
Params-like
|
102
|
+
===========
|
103
|
+
|
104
|
+
`Person.last.to_restful.serialize(:params)` OR
|
105
|
+
`Person.last.to_restful_params` results in something like...
|
106
|
+
|
107
|
+
{:sex_attributes => {:sex=>"male"},
|
108
|
+
:current_location=>"Under a tree",
|
109
|
+
:name=>"Joe Bloggs",
|
110
|
+
:pets_attributes=> [ {:person_id=>1, :name=>nil} ]
|
111
|
+
}
|
112
|
+
|
113
|
+
Other Serializers
|
114
|
+
=================
|
115
|
+
|
116
|
+
Hash. Spits out a plain ole hash, no nested attributes or such like. Useful for further conversions.
|
117
|
+
|
118
|
+
Deserializing
|
119
|
+
=============
|
120
|
+
|
121
|
+
Use `Restful.from_atom_like(xml).serialize(:hash)` to convert from an atom-like formatted xml create to a params hash. Takes care of dereferencing the urls back to ids. Generally, use `Restful.from_<serializer name>(xml)` to get a Resource.
|
122
|
+
|
123
|
+
Nested Attributes
|
124
|
+
=================
|
125
|
+
Serializing uses Rails 2.3 notation of nested attributes. For deserializing you will need Rails 2.3 for having nested attributes support and the respective model must have the
|
126
|
+
`accepts_nested_attributes_for :<table name>` set accordingly.
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the restful plugin.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Generate documentation for the restful plugin.'
|
16
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
18
|
+
rdoc.title = 'Restful'
|
19
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
20
|
+
rdoc.rdoc_files.include('README.markdown')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
data/TODO.markdown
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
Refactor this shice. Seriously, this has devolved into some nasty-ass code.
|
2
|
+
|
3
|
+
* the metamodel is kind of weird. make a better metamodel - how about just using activemodel?
|
4
|
+
* remove requirement to call apiable in model classes; replace with restful_publish with no args (or with args.)
|
5
|
+
* move configuration object out of rails folder - this is general stuff.
|
6
|
+
* remove xml serialization here and test resource directly (in active_record_converter_test)
|
7
|
+
* get rid of to_a warning
|
8
|
+
* convert underscores to dashes (or not) in serializers instead of converter
|
9
|
+
* implement xml serialization of hashes
|
10
|
+
* write tests to show that [Person.new].to_restful_xml(:include => :wallet) fails to preserve Person whitelist.
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/rails/init"
|
data/lib/restful.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'restful/rails'
|
2
|
+
require 'restful/rails/active_record/configuration'
|
3
|
+
require 'restful/rails/active_record/metadata_tools'
|
4
|
+
require 'restful/rails/action_controller'
|
5
|
+
require 'restful/converters/active_record'
|
6
|
+
require 'restful/apimodel/map'
|
7
|
+
require 'restful/apimodel/resource'
|
8
|
+
require 'restful/apimodel/attribute'
|
9
|
+
require 'restful/apimodel/collection'
|
10
|
+
require 'restful/apimodel/link'
|
11
|
+
require 'restful/serializers/xml_serializer'
|
12
|
+
require 'restful/serializers/json_serializer'
|
13
|
+
require 'restful/serializers/atom_like_serializer'
|
14
|
+
require 'restful/serializers/params_serializer'
|
15
|
+
require 'restful/serializers/hash_serializer'
|
16
|
+
|
17
|
+
module Restful
|
18
|
+
|
19
|
+
MAJOR = 0
|
20
|
+
MINOR = 1
|
21
|
+
REVISION = 2
|
22
|
+
VERSION = [MAJOR, MINOR, REVISION].join(".")
|
23
|
+
|
24
|
+
#
|
25
|
+
# Restful.from_xml, #from_atom_like. Methods correspond with
|
26
|
+
# resgistered serializers.
|
27
|
+
#
|
28
|
+
def self.method_missing(method, *args, &block)
|
29
|
+
if method.to_s.match(/^from_(.*)$/)
|
30
|
+
if serializer_clazz = Restful::Serializers::Base.serializers[type = $1.to_sym]
|
31
|
+
s = serializer_clazz.new
|
32
|
+
s.deserialize(args.first)
|
33
|
+
else
|
34
|
+
super
|
35
|
+
end
|
36
|
+
else
|
37
|
+
super
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Shortcuts past the namespaces
|
43
|
+
#
|
44
|
+
|
45
|
+
def self.cfg(*options)
|
46
|
+
Restful::Rails::ActiveRecord::Configuration::Config.new(*options)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.attr(*options)
|
50
|
+
Restful::ApiModel::Attribute.new(*options)
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.link(*options)
|
54
|
+
Restful::ApiModel::Link.new(*options)
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.collection(*options)
|
58
|
+
Restful::ApiModel::Collection.new(*options)
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.resource(*options)
|
62
|
+
Restful::ApiModel::Resource.new(*options)
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#
|
2
|
+
# Attribute model.
|
3
|
+
#
|
4
|
+
module Restful
|
5
|
+
module ApiModel
|
6
|
+
class Attribute
|
7
|
+
attr_accessor :name, :value, :type, :extended_type
|
8
|
+
|
9
|
+
def initialize(name, value, extended_type)
|
10
|
+
self.name = name
|
11
|
+
self.value = value
|
12
|
+
self.extended_type = extended_type
|
13
|
+
self.type = :simple_attribute
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#
|
2
|
+
# Collection model. A collection is a named array of Resources.
|
3
|
+
#
|
4
|
+
module Restful
|
5
|
+
module ApiModel
|
6
|
+
class Collection < Attribute
|
7
|
+
attr_accessor :total_entries
|
8
|
+
|
9
|
+
def initialize(name, resources, extended_type)
|
10
|
+
super
|
11
|
+
|
12
|
+
self.type = :collection
|
13
|
+
end
|
14
|
+
|
15
|
+
# invoke serialization
|
16
|
+
def serialize(type)
|
17
|
+
serializer = Restful::Serializers::Base.serializer(type)
|
18
|
+
serializer.serialize(self)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#
|
2
|
+
# Link model.
|
3
|
+
#
|
4
|
+
module Restful
|
5
|
+
module ApiModel
|
6
|
+
class Link < Attribute
|
7
|
+
attr_accessor :base, :path
|
8
|
+
|
9
|
+
def initialize(name, base, path, extended_type)
|
10
|
+
self.base = base
|
11
|
+
self.path = path
|
12
|
+
super(name, self.full_url, extended_type)
|
13
|
+
self.type = :link
|
14
|
+
end
|
15
|
+
|
16
|
+
def full_url
|
17
|
+
base.blank? ? path : "#{ base }#{ path }"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
#
|
2
|
+
# Resource model. Something like a DOM model for the api.
|
3
|
+
#
|
4
|
+
module Restful
|
5
|
+
module ApiModel
|
6
|
+
class Map
|
7
|
+
attr_accessor :values, :name, :type
|
8
|
+
|
9
|
+
def initialize(name)
|
10
|
+
self.name = name
|
11
|
+
self.type = :map
|
12
|
+
self.values = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def links
|
16
|
+
self.values.select { |attribute| attribute.type == :link }
|
17
|
+
end
|
18
|
+
|
19
|
+
def simple_attributes
|
20
|
+
self.values.select { |attribute| attribute.type == :simple_attribute }
|
21
|
+
end
|
22
|
+
|
23
|
+
def collections
|
24
|
+
self.values.select { |attribute| attribute.type == :collection }
|
25
|
+
end
|
26
|
+
|
27
|
+
# invoke serialization
|
28
|
+
def serialize(type)
|
29
|
+
serializer = Restful::Serializers::Base.serializer(type)
|
30
|
+
serializer.serialize(self)
|
31
|
+
end
|
32
|
+
|
33
|
+
# invoke deserialization
|
34
|
+
def deserialize_from(type)
|
35
|
+
serializer = Restful::Serializers::Base.serializer(type)
|
36
|
+
serializer.deserialize(self)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#
|
2
|
+
# Resource model. Something like a DOM model for the api.
|
3
|
+
#
|
4
|
+
module Restful
|
5
|
+
module ApiModel
|
6
|
+
class Resource < Map
|
7
|
+
attr_accessor :base, :path, :url
|
8
|
+
|
9
|
+
def initialize(name, url)
|
10
|
+
super(name)
|
11
|
+
|
12
|
+
self.url = url[:url]
|
13
|
+
self.path = url[:path]
|
14
|
+
self.base = url[:base]
|
15
|
+
self.type = :resource
|
16
|
+
end
|
17
|
+
|
18
|
+
def full_url
|
19
|
+
base.blank? ? url : "#{ base }#{ path }"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
#
|
2
|
+
# Converts an ActiveRecord model into an ApiModel
|
3
|
+
#
|
4
|
+
module Restful
|
5
|
+
module Converters
|
6
|
+
class ActiveRecord
|
7
|
+
|
8
|
+
def self.convert(model, config, options = {})
|
9
|
+
published = []
|
10
|
+
nested = config.nested?
|
11
|
+
|
12
|
+
resource = Restful.resource(
|
13
|
+
model.class.to_s.tableize.demodulize.singularize, {
|
14
|
+
:base => Restful::Rails.api_hostname,
|
15
|
+
:path => model.restful_path,
|
16
|
+
:url => model.restful_url
|
17
|
+
})
|
18
|
+
|
19
|
+
explicit_links = config.whitelisted.select { |x| x.class == Symbol && x.to_s.ends_with?("_restful_url") }
|
20
|
+
explicit_links.each { |link| config.whitelisted.delete(link) }
|
21
|
+
explicit_links.map! { |link| link.to_s.chomp("_restful_url").to_sym }
|
22
|
+
config.whitelisted += explicit_links
|
23
|
+
|
24
|
+
# simple attributes
|
25
|
+
resource.values += Restful::Rails.tools.simple_attributes_on(model).map do |key, value|
|
26
|
+
convert_to_simple_attribute(key, value, config, published, model)
|
27
|
+
end.compact
|
28
|
+
|
29
|
+
# has_many, has_one, belongs_to
|
30
|
+
resource.values += model.class.reflections.keys.map do |key|
|
31
|
+
explicit_link = !!explicit_links.include?(key)
|
32
|
+
|
33
|
+
if config.published?(key.to_sym) || explicit_link
|
34
|
+
nested_config = config.nested(key.to_sym)
|
35
|
+
published << key.to_sym
|
36
|
+
|
37
|
+
if has_many?(model, key) && config.expanded?(key, nested)
|
38
|
+
convert_to_collection(model, key, nested_config, published) do |key, resources, extended_type|
|
39
|
+
Restful.collection(key, resources, extended_type)
|
40
|
+
end
|
41
|
+
elsif has_one?(model, key) or belongs_to?(model, key)
|
42
|
+
if config.expanded?(key, nested) && !explicit_link
|
43
|
+
convert_to_collection(model, key, nested_config, published) do |key, resources, extended_type|
|
44
|
+
returning(resources.first) do |res|
|
45
|
+
res.name = key
|
46
|
+
end
|
47
|
+
end
|
48
|
+
else
|
49
|
+
link_to(model, key)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end.compact
|
54
|
+
|
55
|
+
# Links
|
56
|
+
if model.class.apiable_association_table
|
57
|
+
|
58
|
+
resource.values += model.class.apiable_association_table.keys.map do |key|
|
59
|
+
|
60
|
+
if config.published?(key.to_sym)
|
61
|
+
published << key.to_sym
|
62
|
+
base, path = model.resolve_association_restful_url(key)
|
63
|
+
Restful.link(key.to_sym, base, path, compute_extended_type(model, key))
|
64
|
+
end
|
65
|
+
end.compact
|
66
|
+
end
|
67
|
+
|
68
|
+
# public methods
|
69
|
+
resource.values += (model.public_methods - Restful::Rails.tools.simple_attributes_on(model).keys.map(&:to_s)).map do |method_name|
|
70
|
+
|
71
|
+
explicit_link = !!explicit_links.include?(method_name.to_sym)
|
72
|
+
|
73
|
+
if !published.include?(method_name.to_sym) && (config.published?(method_name.to_sym) || explicit_link)
|
74
|
+
value = model.send(method_name.to_sym)
|
75
|
+
sanitized_method_name = method_name.tr("!?", "").tr("_", "-").to_sym
|
76
|
+
|
77
|
+
if value.is_a? ::ActiveRecord::Base
|
78
|
+
if config.expanded?(method_name.to_sym, nested) && !explicit_link
|
79
|
+
returning Restful::Rails.tools.expand(value, config.nested(method_name.to_sym)) do |expanded|
|
80
|
+
expanded.name = sanitized_method_name
|
81
|
+
end
|
82
|
+
else
|
83
|
+
Restful.link("#{ sanitized_method_name }-restful-url", Restful::Rails.api_hostname, value ? value.restful_path : "", compute_extended_type(model, method_name.to_sym))
|
84
|
+
end
|
85
|
+
else
|
86
|
+
Restful.attr(sanitized_method_name, value, compute_extended_type(model, method_name))
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end.compact
|
90
|
+
|
91
|
+
resource
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.has_one?(model, key)
|
95
|
+
macro(model, key) == :has_one
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.has_many?(model, key)
|
99
|
+
macro(model, key) == :has_many
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.belongs_to?(model, key)
|
103
|
+
macro(model, key) == :belongs_to
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.link_to(model, key)
|
107
|
+
value = model.send(key)
|
108
|
+
restful_path = value ? value.restful_path : nil
|
109
|
+
basename = value ? Restful::Rails.api_hostname : nil
|
110
|
+
|
111
|
+
Restful.link("#{ key }-restful-url", basename, restful_path, compute_extended_type(model, key))
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.convert_to_simple_attribute(key, value, config, published, model = nil)
|
115
|
+
if config.published?(key.to_sym)
|
116
|
+
published << key.to_sym
|
117
|
+
ext_type = (model ? compute_extended_type(model, key) : value.class.to_s.underscore.to_sym)
|
118
|
+
Restful.attr(key.to_sym, value, ext_type)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def self.macro(model, key)
|
125
|
+
model.class.reflections[key].macro
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.convert_to_collection(model, key, nested_config, published)
|
129
|
+
if resources = Restful::Rails.tools.convert_collection_to_resources(model, key, nested_config)
|
130
|
+
yield key.to_sym, resources, compute_extended_type(model, key)
|
131
|
+
else
|
132
|
+
published << key.to_sym
|
133
|
+
Restful.attr(key.to_sym, nil, :notype)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.compute_extended_type(record, attribute_name)
|
138
|
+
type_symbol = :yaml if record.class.serialized_attributes.has_key?(attribute_name)
|
139
|
+
|
140
|
+
if column = record.class.columns_hash[attribute_name]
|
141
|
+
type_symbol = column.send(:simplified_type, column.sql_type)
|
142
|
+
else
|
143
|
+
|
144
|
+
type_symbol = record.send(attribute_name).class.to_s.underscore.to_sym
|
145
|
+
end
|
146
|
+
|
147
|
+
case type_symbol
|
148
|
+
when :text
|
149
|
+
:string
|
150
|
+
when :time
|
151
|
+
:datetime
|
152
|
+
when :date
|
153
|
+
:date
|
154
|
+
else
|
155
|
+
type_symbol
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|