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