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.
- 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
|