toast 0.6.0 → 0.7.0
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/README.md +64 -80
- data/app/controller/toast_controller.rb +1 -1
- data/config/routes.rb +1 -1
- data/lib/toast/active_record_extensions.rb +14 -1
- data/lib/toast/association.rb +11 -3
- data/lib/toast/collection.rb +35 -11
- data/lib/toast/engine.rb +1 -0
- data/lib/toast/record.rb +9 -2
- data/lib/toast/resource.rb +14 -10
- data/lib/toast/scoped_association.rb +23 -0
- data/lib/toast/single.rb +12 -1
- data/lib/toast/version.rb +3 -0
- metadata +50 -10
data/README.md
CHANGED
@@ -1,32 +1,31 @@
|
|
1
1
|
Summary
|
2
2
|
=======
|
3
3
|
|
4
|
-
Toast is an extension to Ruby on Rails
|
5
|
-
|
6
|
-
|
4
|
+
Toast is an extension to Ruby on Rails to build web services with low
|
5
|
+
programming effort in a coherent way. Toast exends ActiveRecord such
|
6
|
+
that each model can be declared to be a web resource, exposing defined
|
7
|
+
attributes for reading and writing using HTTP.
|
7
8
|
|
8
|
-
|
9
|
-
toast takes a data centric approach: Tell the model to be a resource
|
10
|
-
and what attributes and associations are to be exposed. That's it. No
|
11
|
-
controller boiler plate code for every model, no routing setup.
|
9
|
+
Its main features are:
|
12
10
|
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
* declaration of web resources based on ActiveRecord models
|
12
|
+
* generic controller handles all actions
|
13
|
+
* automated routing
|
14
|
+
* exposing data values with JSON maps
|
15
|
+
* exposing associations by links (URLs)
|
16
16
|
|
17
|
-
|
18
|
-
response codes. It's on the toast user to invent media types that
|
19
|
-
control the application's state and introduce semantics. With toast you
|
20
|
-
can build REST services or tightly coupled server-client applications,
|
21
|
-
which ever suits the task best. That's why TOAST stands for:
|
17
|
+
Toast works with Ruby on Rails >= 3.1.0 (currently tested up to 3.2.6)
|
22
18
|
|
23
|
-
|
19
|
+
WARNING
|
20
|
+
=======
|
21
|
+
|
22
|
+
This version is experimental and probably not bullet
|
23
|
+
proof. As soon as the gem is loaded a controller with ready routing is
|
24
|
+
enabled serving the annotated model's data records.
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
updating and deleting. There are no measures to prevent XSS and CSFR
|
29
|
-
attacks.
|
26
|
+
Version 1.0.0 of Toast is planned to be a production-ready implementation,
|
27
|
+
which will be finished within 2012. Until then API/DSL changes must
|
28
|
+
be expected with each minor update.
|
30
29
|
|
31
30
|
Example
|
32
31
|
=======
|
@@ -40,89 +39,89 @@ Let the table `bananas` have the following schema:
|
|
40
39
|
t.integer "apple_id"
|
41
40
|
end
|
42
41
|
|
43
|
-
and let a corresponding model class have a *
|
42
|
+
and let a corresponding model class have a *acts_as_resource* annotation:
|
44
43
|
|
45
44
|
class Banana < ActiveRecord::Base
|
46
45
|
belongs_to :apple
|
47
46
|
has_many :coconuts
|
48
|
-
|
47
|
+
|
48
|
+
scope :less_than_100, where("number < 100")
|
49
49
|
|
50
|
-
|
51
|
-
# attributes or association names
|
50
|
+
acts_as_resource do
|
51
|
+
# exposed attributes or association names
|
52
52
|
readables :coconuts, :apple
|
53
|
-
|
53
|
+
writables :name, :number
|
54
54
|
|
55
|
-
# class methods of Banana returning an Array of Banana records
|
56
|
-
collections :
|
55
|
+
# exposed class methods of Banana returning an Array of Banana records
|
56
|
+
collections :less_than_100, :all
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
-
The above definition inside the `
|
60
|
+
The above definition inside the `acts_as_resource` block exposes the
|
61
61
|
records of the model Banana automatically via a generic controller to
|
62
62
|
the outside world, accepting and delivering JSON representations of
|
63
63
|
the records. Let the associated models Apple and Coconut be
|
64
|
-
exposed as a resource, too
|
64
|
+
exposed as a resource, too:
|
65
65
|
|
66
66
|
### Get a collection
|
67
67
|
GET /bananas
|
68
|
-
--> 200, '[{"
|
69
|
-
"name":
|
70
|
-
"number":
|
68
|
+
--> 200, '[{"self": "http://www.example.com/bananas/23",
|
69
|
+
"name": "Fred",
|
70
|
+
"number": 33,
|
71
71
|
"coconuts": "http://www.example.com/bananas/23/coconuts",
|
72
|
-
"apple":"http://www.example.com/bananas/23/apple,
|
73
|
-
{"
|
72
|
+
"apple": "http://www.example.com/bananas/23/apple,
|
73
|
+
{"self": "http://www.example.com/bananas/24",
|
74
74
|
... }, ... ]
|
75
75
|
### Get a customized collection (filtered, paging, etc.)
|
76
|
-
|
77
|
-
|
78
|
-
--> 200, '[SOME BANANAS]'
|
76
|
+
GET /bananas/less_than_100
|
77
|
+
--> 200, '[{BANANA}, {BANANA}, ...]'
|
79
78
|
|
80
79
|
### Get a single resource representation:
|
81
80
|
GET /bananas/23
|
82
|
-
--> 200, '{"
|
83
|
-
"name":
|
84
|
-
"number":
|
81
|
+
--> 200, '{"self": "http://www.example.com/bananas/23"
|
82
|
+
"name": "Fred",
|
83
|
+
"number": 33,
|
85
84
|
"coconuts": "http://www.example.com/bananas/23/coconuts",
|
86
|
-
"apple":
|
85
|
+
"apple": "http://www.example.com/bananas/23/apple" }'
|
87
86
|
|
88
|
-
### Get
|
87
|
+
### Get an associated collection
|
89
88
|
"GET" /bananas/23/coconuts
|
90
89
|
--> 200, '[{COCNUT},{COCONUT},...]',
|
91
90
|
|
92
91
|
### Update a single resource:
|
93
|
-
PUT /bananas/23, '{"
|
94
|
-
"name":
|
92
|
+
PUT /bananas/23, '{"self": "http://www.example.com/bananas/23"
|
93
|
+
"name": "Barney",
|
95
94
|
"number": 44}'
|
96
|
-
--> 200, '{"
|
97
|
-
"name":
|
98
|
-
"number":
|
95
|
+
--> 200, '{"self": "http://www.example.com/bananas/23"
|
96
|
+
"name": "Barney",
|
97
|
+
"number": 44,
|
99
98
|
"coconuts": "http://www.example.com/bananas/23/coconuts",
|
100
|
-
"apple":
|
99
|
+
"apple": "http://www.example.com/bananas/23/apple"}'
|
101
100
|
|
102
101
|
### Create a new record
|
103
102
|
"POST" /bananas, '{"name": "Johnny",
|
104
103
|
"number": 888}'
|
105
|
-
--> 201, {"
|
106
|
-
"name":
|
107
|
-
"number":
|
104
|
+
--> 201, {"self": "http://www.example.com/bananas/102"
|
105
|
+
"name": "Johnny",
|
106
|
+
"number": 888,
|
108
107
|
"coconuts": "http://www.example.com/bananas/102/coconuts" ,
|
109
|
-
"apple":
|
108
|
+
"apple": "http://www.example.com/bananas/102/apple }
|
110
109
|
|
111
110
|
### Create an associated record
|
112
111
|
"POST" /bananas/23/coconuts, '{COCONUT}'
|
113
|
-
--> 201, {"
|
112
|
+
--> 201, {"self":"http://www.example.com/coconuts/432,
|
114
113
|
...}
|
115
114
|
|
116
115
|
### Delete records
|
117
116
|
DELETE /bananas/23
|
118
117
|
--> 200
|
119
118
|
|
120
|
-
More details and configuration options are documented in the manual
|
119
|
+
More details and configuration options are documented in the manual.
|
121
120
|
|
122
121
|
Installation
|
123
122
|
============
|
124
123
|
|
125
|
-
With bundler
|
124
|
+
With bundler from (rubygems.org)
|
126
125
|
|
127
126
|
gem "toast"
|
128
127
|
|
@@ -130,29 +129,14 @@ the latest Git:
|
|
130
129
|
|
131
130
|
gem "toast", :git => "https://github.com/robokopp/toast.git"
|
132
131
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
In `test/rails_app` you can find a rails application with tests. To run
|
137
|
-
the tests you need to
|
138
|
-
|
139
|
-
0. Install the *jeweler* gem:
|
140
|
-
|
141
|
-
gem install jeweler
|
142
|
-
|
143
|
-
1. install the toast gem from this git clone:
|
144
|
-
|
145
|
-
rake install
|
146
|
-
|
147
|
-
2. initialize the test application
|
148
|
-
|
149
|
-
cd test/rails_app
|
150
|
-
bundle install
|
151
|
-
|
152
|
-
3. Now you can run the test suite from within the test application
|
132
|
+
Remarks
|
133
|
+
=======
|
153
134
|
|
154
|
-
|
135
|
+
REST is more than some pretty URIs, the use of the HTTP verbs and
|
136
|
+
response codes. It's on the Toast user to invent meaningful media
|
137
|
+
types that control the application's state and introduce
|
138
|
+
semantics. With toast you can build REST services or tightly coupled
|
139
|
+
server-client applications, which ever suits the task best. That's why
|
140
|
+
TOAST stands for:
|
155
141
|
|
156
|
-
|
157
|
-
copy. This will reinstall the toast gem before running tests
|
158
|
-
automatically.
|
142
|
+
> **TOast Ain't reST**
|
data/config/routes.rb
CHANGED
@@ -14,7 +14,7 @@ Rails.application.routes.draw do
|
|
14
14
|
|
15
15
|
namespaces << tc.namespace
|
16
16
|
|
17
|
-
match("#{tc.namespace}/#{resource_name}(/:id(/:subresource))" => 'toast#catch_all',
|
17
|
+
match("#{tc.namespace}/#{resource_name}(/:id(/:subresource(/:scope)))" => 'toast#catch_all',
|
18
18
|
:constraints => { :id => /\d+/ },
|
19
19
|
:resource => resource_name,
|
20
20
|
:as => resource_name,
|
@@ -44,15 +44,28 @@ module Toast
|
|
44
44
|
# Like ActiveRecord::Base.attributes, but result Hash includes
|
45
45
|
# only attributes from the list _attr_names_ plus the
|
46
46
|
# associations _assoc_names_ as links and the 'self' link
|
47
|
-
def represent attr_names, assoc_names, base_uri
|
47
|
+
def represent attr_names, assoc_names, base_uri, media_type
|
48
48
|
props = {}
|
49
49
|
|
50
50
|
attr_names.each do |name|
|
51
|
+
unless self.respond_to?(name) && self.method(name).arity == 0
|
52
|
+
raise "Toast Error: Connot find instance method '#{self.class}##{name}' of arity 0"
|
53
|
+
end
|
51
54
|
props[name] = self.send(name)
|
52
55
|
end
|
53
56
|
|
54
57
|
assoc_names.each do |name|
|
55
58
|
props[name] = "#{base_uri}#{self.uri_path}/#{name}"
|
59
|
+
|
60
|
+
# collections (scopes) actiing on associacions:
|
61
|
+
|
62
|
+
reflect = self.class.reflect_on_association(name.to_sym)
|
63
|
+
if reflect.collection?
|
64
|
+
reflect.klass.toast_config(media_type).collections.each do |collection_name|
|
65
|
+
next if collection_name == "all"
|
66
|
+
props[name+":"+collection_name] = "#{base_uri}#{self.uri_path}/#{name}/#{collection_name}"
|
67
|
+
end
|
68
|
+
end
|
56
69
|
end
|
57
70
|
|
58
71
|
props["self"] = base_uri + self.uri_path
|
data/lib/toast/association.rb
CHANGED
@@ -22,6 +22,11 @@ module Toast
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def get
|
25
|
+
|
26
|
+
unless @record.class.reflect_on_all_associations.detect{|a| a.name.to_s == @assoc}
|
27
|
+
raise "Toast Error: Association '#{@assoc}' not found in model '#{@record.class}'"
|
28
|
+
end
|
29
|
+
|
25
30
|
result = @record.send(@assoc)
|
26
31
|
|
27
32
|
raise ResourceNotFound if result.nil?
|
@@ -31,7 +36,8 @@ module Toast
|
|
31
36
|
:json => result.map{|r|
|
32
37
|
r.represent( @associate_config_out.in_collection.exposed_attributes,
|
33
38
|
@associate_config_out.in_collection.exposed_associations,
|
34
|
-
@base_uri
|
39
|
+
@base_uri,
|
40
|
+
@associate_config_out.media_type )
|
35
41
|
},
|
36
42
|
:status => :ok,
|
37
43
|
:content_type => @associate_config_out.in_collection.media_type
|
@@ -40,7 +46,8 @@ module Toast
|
|
40
46
|
{
|
41
47
|
:json => result.represent( @associate_config_out.exposed_attributes,
|
42
48
|
@associate_config_out.exposed_associations,
|
43
|
-
@base_uri
|
49
|
+
@base_uri,
|
50
|
+
@associate_config_out.media_type),
|
44
51
|
:status => :ok,
|
45
52
|
:content_type => @associate_config_out.media_type
|
46
53
|
}
|
@@ -81,7 +88,8 @@ module Toast
|
|
81
88
|
{
|
82
89
|
:json => record.represent( @associate_config_out.exposed_attributes,
|
83
90
|
@associate_config_out.exposed_associations,
|
84
|
-
@base_uri
|
91
|
+
@base_uri,
|
92
|
+
@associate_config_out.media_type),
|
85
93
|
:location => self.base_uri + record.uri_path,
|
86
94
|
:status => :created,
|
87
95
|
:content_type => @associate_config_out.media_type
|
data/lib/toast/collection.rb
CHANGED
@@ -3,7 +3,7 @@ module Toast
|
|
3
3
|
|
4
4
|
attr_reader :model
|
5
5
|
|
6
|
-
def initialize
|
6
|
+
def initialize model_or_relation, subresource_name, params, config_in, config_out
|
7
7
|
|
8
8
|
subresource_name ||= "all"
|
9
9
|
|
@@ -11,7 +11,11 @@ module Toast
|
|
11
11
|
raise ResourceNotFound
|
12
12
|
end
|
13
13
|
|
14
|
-
@
|
14
|
+
@target_model = (model_or_relation.klass rescue false) ?
|
15
|
+
model_or_relation.klass :
|
16
|
+
model_or_relation
|
17
|
+
|
18
|
+
@model_or_relation = model_or_relation
|
15
19
|
@collection = subresource_name
|
16
20
|
@params = params
|
17
21
|
@format = params[:format]
|
@@ -21,24 +25,43 @@ module Toast
|
|
21
25
|
|
22
26
|
def get
|
23
27
|
|
24
|
-
|
25
|
-
|
28
|
+
unless @model_or_relation.respond_to?(@collection)
|
29
|
+
raise "Toast Error: Cannot find class method '#{@collection}' of model '#{@model_or_relation}', which is configured in 'acts_as_resource > collections'."
|
30
|
+
end
|
31
|
+
|
32
|
+
# FIXME: This is a lot of hallooballoo to check if the #send
|
33
|
+
# will be successful, but if it's not checked the error
|
34
|
+
# message is not helpful to find the error.
|
35
|
+
|
36
|
+
records = if @config_out.pass_params_to.include?(@collection)
|
37
|
+
if @target_model.method(@collection).arity != 1
|
38
|
+
raise "Toast Error: Class method '#{@collection}' of model '#{@target_model}' must accept one parameter, as configured by 'acts_as_resource > pass_params_to'."
|
39
|
+
end
|
40
|
+
# fetch results
|
41
|
+
@model_or_relation.send(@collection, @params)
|
42
|
+
|
26
43
|
else
|
27
|
-
|
44
|
+
|
45
|
+
if @target_model.method(@collection).arity > 0
|
46
|
+
raise "Toast Error: Class method '#{@collection}' of model '#{@model_or_relation}' must not accept any parameter, as configured by 'acts_as_resource'"
|
47
|
+
end
|
48
|
+
# fetch results
|
49
|
+
@model_or_relation.send(@collection)
|
28
50
|
end
|
29
51
|
|
30
52
|
case @format
|
31
53
|
when "html"
|
32
54
|
{
|
33
|
-
:template => "resources/#{
|
34
|
-
:locals => {
|
55
|
+
:template => "resources/#{@target_model.to_s.pluralize.underscore}",
|
56
|
+
:locals => { @target_model.to_s.pluralize.underscore.to_sym => records }
|
35
57
|
}
|
36
58
|
when "json"
|
37
59
|
{
|
38
60
|
:json => records.map{|r|
|
39
61
|
r.represent( @config_out.in_collection.exposed_attributes,
|
40
62
|
@config_out.in_collection.exposed_associations,
|
41
|
-
|
63
|
+
self.base_uri,
|
64
|
+
@config_out.media_type)
|
42
65
|
},
|
43
66
|
:status => :ok,
|
44
67
|
:content_type => @config_out.in_collection.media_type
|
@@ -78,13 +101,14 @@ module Toast
|
|
78
101
|
end
|
79
102
|
|
80
103
|
begin
|
81
|
-
record = @
|
104
|
+
record = @model_or_relation.create! payload
|
82
105
|
|
83
106
|
{
|
84
107
|
:json => record.represent( @config_out.exposed_attributes,
|
85
108
|
@config_out.exposed_associations,
|
86
|
-
|
87
|
-
|
109
|
+
self.base_uri,
|
110
|
+
@config_out.media_type),
|
111
|
+
:location => self.base_uri + record.uri_path,
|
88
112
|
:status => :created,
|
89
113
|
:content_type => @config_out.media_type
|
90
114
|
}
|
data/lib/toast/engine.rb
CHANGED
data/lib/toast/record.rb
CHANGED
@@ -41,6 +41,11 @@ module Toast
|
|
41
41
|
|
42
42
|
# set the virtual attributes
|
43
43
|
(@config_in.writables - @record.attribute_names - @config_in.exposed_associations).each do |vattr|
|
44
|
+
|
45
|
+
unless (@record.respond_to?("#{vattr}=") && @record.method("#{vattr}=").arity == 1)
|
46
|
+
raise "Toast Error: Connot find setter '#{@record.class}##{vattr}='"
|
47
|
+
end
|
48
|
+
|
44
49
|
@record.send("#{vattr}=", payload.delete(vattr))
|
45
50
|
end
|
46
51
|
|
@@ -49,7 +54,8 @@ module Toast
|
|
49
54
|
{
|
50
55
|
:json => @record.represent( @config_out.exposed_attributes,
|
51
56
|
@config_out.exposed_associations,
|
52
|
-
@base_uri
|
57
|
+
@base_uri,
|
58
|
+
@config_out.media_type ),
|
53
59
|
:status => :ok,
|
54
60
|
:location => self.base_uri + @record.uri_path,
|
55
61
|
:content_type => @config_out.media_type
|
@@ -67,7 +73,8 @@ module Toast
|
|
67
73
|
{
|
68
74
|
:json => @record.represent( @config_out.exposed_attributes,
|
69
75
|
@config_out.exposed_associations,
|
70
|
-
@base_uri
|
76
|
+
@base_uri,
|
77
|
+
@config_out.media_type),
|
71
78
|
:status => :ok,
|
72
79
|
:content_type => @config_out.media_type
|
73
80
|
}
|
data/lib/toast/resource.rb
CHANGED
@@ -22,11 +22,7 @@ module Toast
|
|
22
22
|
id = params[:id]
|
23
23
|
subresource_name = params[:subresource]
|
24
24
|
format = params[:format]
|
25
|
-
|
26
|
-
#### Debugging stop
|
27
|
-
# binding.pry if $halt
|
28
|
-
###
|
29
|
-
|
25
|
+
scope = params[:scope]
|
30
26
|
begin
|
31
27
|
|
32
28
|
# determine model
|
@@ -39,7 +35,7 @@ module Toast
|
|
39
35
|
config_in = model.toast_config request.media_type
|
40
36
|
|
41
37
|
# ... or in case of an association request
|
42
|
-
config_assoc_src = model.toast_config request.headers["Assoc-source-type"]
|
38
|
+
config_assoc_src = model.toast_config request.headers["Assoc-source-type"] # ?
|
43
39
|
|
44
40
|
# base URI for returned object
|
45
41
|
base_uri = request.base_url + request.script_name +
|
@@ -53,9 +49,10 @@ module Toast
|
|
53
49
|
elsif subresource_name.nil?
|
54
50
|
Toast::Record.new(model, id, format, config_in, config_out)
|
55
51
|
elsif (config_assoc_src && config_assoc_src.exposed_associations.include?(subresource_name))
|
56
|
-
|
52
|
+
|
57
53
|
# determine associated model
|
58
|
-
assoc_model =
|
54
|
+
assoc_model =
|
55
|
+
model.reflect_on_all_associations.detect{|a| a.name.to_s == subresource_name}.klass
|
59
56
|
|
60
57
|
# determine config for representation of assoc. model
|
61
58
|
assoc_config_out = assoc_model.toast_config request.accept_media_types.prefered
|
@@ -65,8 +62,13 @@ module Toast
|
|
65
62
|
base_uri = request.base_url + request.script_name +
|
66
63
|
(assoc_config_out.namespace ? "/" + assoc_config_out.namespace : "")
|
67
64
|
|
68
|
-
|
69
|
-
|
65
|
+
if scope.nil?
|
66
|
+
Toast::Association.new(model, id, subresource_name, format, config_assoc_src,
|
67
|
+
assoc_model, assoc_config_in, assoc_config_out)
|
68
|
+
else
|
69
|
+
Toast::ScopedAssociation.new(model, id, subresource_name, scope, params.clone,
|
70
|
+
assoc_config_in, assoc_config_out)
|
71
|
+
end
|
70
72
|
else
|
71
73
|
raise ResourceNotFound
|
72
74
|
end
|
@@ -74,6 +76,8 @@ module Toast
|
|
74
76
|
# set base to be prepended to URIs
|
75
77
|
rsc.base_uri = base_uri
|
76
78
|
|
79
|
+
|
80
|
+
|
77
81
|
rsc
|
78
82
|
rescue NameError
|
79
83
|
raise ResourceNotFound
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Toast
|
2
|
+
class ScopedAssociation < Resource
|
3
|
+
# delegeates everything to Toast::Collection, where the model is replaced
|
4
|
+
# by the Relation this association creates
|
5
|
+
|
6
|
+
def initialize(model, id, association_name, scope_name, params,
|
7
|
+
config_in, config_out)
|
8
|
+
|
9
|
+
record = model.find(id)
|
10
|
+
assoc_model = record.class
|
11
|
+
|
12
|
+
@collection = Toast::Collection.new(record.send(association_name),
|
13
|
+
scope_name,
|
14
|
+
params,
|
15
|
+
config_in,
|
16
|
+
config_out)
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
delegate :get, :put, :post, :delete, :base_uri, :base_uri=, :to => :@collection
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
data/lib/toast/single.rb
CHANGED
@@ -25,10 +25,20 @@ module Toast
|
|
25
25
|
@params = params
|
26
26
|
@format = params[:format]
|
27
27
|
|
28
|
+
unless @model.respond_to?(subresource_name)
|
29
|
+
raise "Toast Error: Cannot find class method '#{@model}.#{subresource_name}', which is configured in 'acts_as_resource > singles'."
|
30
|
+
end
|
28
31
|
|
29
32
|
@record = if @config_out.pass_params_to.include?(subresource_name)
|
33
|
+
if @model.method(subresource_name).arity != 1
|
34
|
+
raise "Toast Error: Class method '#{@model}.#{subresource_name}' must accept one parameter, as configured by 'acts_as_resource > pass_params_to'."
|
35
|
+
end
|
30
36
|
@model.send(subresource_name, @params)
|
31
37
|
else
|
38
|
+
if(@model.method(subresource_name).arity < -1 or
|
39
|
+
@model.method(subresource_name).arity > 0)
|
40
|
+
raise "Toast Error: Class method '#{@model}.#{subresource_name}' must be callable w/o parameters"
|
41
|
+
end
|
32
42
|
@model.send(subresource_name)
|
33
43
|
end
|
34
44
|
|
@@ -46,7 +56,8 @@ module Toast
|
|
46
56
|
{
|
47
57
|
:json => @record.represent( @config_out.exposed_attributes,
|
48
58
|
@config_out.exposed_associations,
|
49
|
-
@base_uri
|
59
|
+
@base_uri,
|
60
|
+
@config_out.media_type),
|
50
61
|
:status => :ok
|
51
62
|
}
|
52
63
|
else
|
metadata
CHANGED
@@ -4,23 +4,49 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
7
|
+
- 7
|
8
8
|
- 0
|
9
|
-
version: 0.
|
9
|
+
version: 0.7.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
|
-
- Robert
|
12
|
+
- "robokopp (Robert Anni\xC3\xA9s)"
|
13
13
|
autorequire:
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2012-
|
17
|
+
date: 2012-07-10 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
|
-
name:
|
21
|
+
name: shoulda
|
22
22
|
prerelease: false
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :development
|
31
|
+
version_requirements: *id001
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: rails
|
34
|
+
prerelease: false
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
segments:
|
40
|
+
- 3
|
41
|
+
- 1
|
42
|
+
- 0
|
43
|
+
version: 3.1.0
|
44
|
+
type: :runtime
|
45
|
+
version_requirements: *id002
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: blockenspiel
|
48
|
+
prerelease: false
|
49
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
24
50
|
requirements:
|
25
51
|
- - ~>
|
26
52
|
- !ruby/object:Gem::Version
|
@@ -30,11 +56,11 @@ dependencies:
|
|
30
56
|
- 2
|
31
57
|
version: 0.4.2
|
32
58
|
type: :runtime
|
33
|
-
version_requirements: *
|
59
|
+
version_requirements: *id003
|
34
60
|
- !ruby/object:Gem::Dependency
|
35
61
|
name: rack-accept-media-types
|
36
62
|
prerelease: false
|
37
|
-
requirement: &
|
63
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
38
64
|
requirements:
|
39
65
|
- - ~>
|
40
66
|
- !ruby/object:Gem::Version
|
@@ -43,11 +69,23 @@ dependencies:
|
|
43
69
|
- 9
|
44
70
|
version: "0.9"
|
45
71
|
type: :runtime
|
46
|
-
version_requirements: *
|
72
|
+
version_requirements: *id004
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: ffaker
|
75
|
+
prerelease: false
|
76
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
segments:
|
81
|
+
- 0
|
82
|
+
version: "0"
|
83
|
+
type: :runtime
|
84
|
+
version_requirements: *id005
|
47
85
|
description: |-
|
48
86
|
Toast is an extension to Ruby on Rails that lets you expose any
|
49
|
-
ActiveRecord model as a resource according to the REST paradigm. The
|
50
|
-
representation format is JSON
|
87
|
+
ActiveRecord model as a resource according to the REST paradigm via a generic controller. The default
|
88
|
+
representation format is JSON or can be anything
|
51
89
|
email: robokopp@fernwerk.net
|
52
90
|
executables: []
|
53
91
|
|
@@ -59,6 +97,7 @@ files:
|
|
59
97
|
- app/controller/toast_controller.rb
|
60
98
|
- config/routes.rb
|
61
99
|
- lib/toast.rb
|
100
|
+
- lib/toast/version.rb
|
62
101
|
- lib/toast/active_record_extensions.rb
|
63
102
|
- lib/toast/association.rb
|
64
103
|
- lib/toast/collection.rb
|
@@ -67,6 +106,7 @@ files:
|
|
67
106
|
- lib/toast/record.rb
|
68
107
|
- lib/toast/resource.rb
|
69
108
|
- lib/toast/single.rb
|
109
|
+
- lib/toast/scoped_association.rb
|
70
110
|
- README.md
|
71
111
|
has_rdoc: true
|
72
112
|
homepage: https://github.com/robokopp/toast
|