toast 0.8.13 → 0.8.14
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +11 -6
- data/app/controller/toast_controller.rb +36 -1
- data/lib/toast/association.rb +26 -6
- data/lib/toast/collection.rb +25 -17
- data/lib/toast/config_dsl.rb +12 -1
- data/lib/toast/resource.rb +36 -0
- data/lib/toast/version.rb +1 -1
- metadata +109 -66
- checksums.yaml +0 -7
data/README.md
CHANGED
@@ -23,16 +23,21 @@ Toast works with
|
|
23
23
|
|
24
24
|
See the [User Manual](https://github.com/robokopp/toast/wiki/User-Manual) for a detailed description.
|
25
25
|
|
26
|
+
Status
|
27
|
+
=====
|
28
|
+
|
29
|
+
Toast is ready for production and is being used in productive
|
30
|
+
applications since 2012. However, misconfigurations can have undesired
|
31
|
+
effects.
|
32
|
+
|
26
33
|
WARNING
|
27
34
|
=======
|
28
35
|
|
29
|
-
|
30
|
-
|
31
|
-
|
36
|
+
A soon the gem is loaded a controller with ready routing is enabled
|
37
|
+
serving the annotated model's data records at least for reading for
|
38
|
+
everybody.
|
32
39
|
|
33
|
-
|
34
|
-
which will be finished within 2012. Until then API/DSL changes must
|
35
|
-
be expected with each minor update.
|
40
|
+
You need to implement authorization yourself.
|
36
41
|
|
37
42
|
Example
|
38
43
|
=======
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'rack-link_headers'
|
2
|
+
|
1
3
|
class ToastController < ApplicationController
|
2
4
|
|
3
5
|
def catch_all
|
@@ -11,7 +13,37 @@ class ToastController < ApplicationController
|
|
11
13
|
request.headers["LINK"] =~ /(#{request.protocol + request.host + request.script_name})(.*)/
|
12
14
|
end
|
13
15
|
|
14
|
-
|
16
|
+
toast_response = @resource.apply(request.method, request.body.read, request.content_type, $2)
|
17
|
+
|
18
|
+
# pagination
|
19
|
+
if pi = toast_response[:pagination_info]
|
20
|
+
# URL w/o parameters
|
21
|
+
|
22
|
+
url = request.url.split('?').first
|
23
|
+
qpar = request.query_parameters.clone
|
24
|
+
|
25
|
+
# change/add page parameter
|
26
|
+
link_header = []
|
27
|
+
|
28
|
+
if pi[:prev]
|
29
|
+
qpar[:page] = pi[:prev]
|
30
|
+
response.link "#{url}?#{qpar.to_query}", :rel => :prev
|
31
|
+
end
|
32
|
+
|
33
|
+
if pi[:next]
|
34
|
+
qpar[:page] = pi[:next]
|
35
|
+
response.link "#{url}?#{qpar.to_query}", :rel => :next
|
36
|
+
end
|
37
|
+
|
38
|
+
qpar[:page] = pi[:last]
|
39
|
+
response.link "#{url}?#{qpar.to_query}", :rel => :last
|
40
|
+
|
41
|
+
qpar[:page] = 1
|
42
|
+
response.link "#{url}?#{qpar.to_query}", :rel => :first
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
render toast_response
|
15
47
|
|
16
48
|
rescue Toast::ResourceNotFound => e
|
17
49
|
return head(:not_found)
|
@@ -22,6 +54,9 @@ class ToastController < ApplicationController
|
|
22
54
|
rescue Toast::PayloadFormatError => e
|
23
55
|
return head(:bad_request)
|
24
56
|
|
57
|
+
rescue Toast::BadRequest => e
|
58
|
+
return head(:bad_request)
|
59
|
+
|
25
60
|
rescue Toast::MethodNotAllowed => e
|
26
61
|
return head(:method_not_allowed)
|
27
62
|
|
data/lib/toast/association.rb
CHANGED
@@ -29,18 +29,24 @@ module Toast
|
|
29
29
|
|
30
30
|
reflection = @model.reflect_on_association(@assoc.to_sym)
|
31
31
|
|
32
|
-
|
32
|
+
if reflection.collection?
|
33
|
+
|
34
|
+
|
33
35
|
if(@config.pass_params_to.include?(@assoc) and
|
34
36
|
reflection.options[:extend] and
|
35
37
|
reflection.options[:extend].detect{|e| e.method_defined? :find_by_params} )
|
36
|
-
|
38
|
+
|
39
|
+
result, pagination_info = paginate_query( @config, @assoc,
|
40
|
+
@record.send(@assoc).find_by_params(@params), @params)
|
37
41
|
else
|
38
|
-
|
42
|
+
|
43
|
+
result, pagination_info = paginate_query( @config, @assoc,
|
44
|
+
@record.send(@assoc), @params)
|
45
|
+
|
39
46
|
end
|
40
47
|
|
41
|
-
|
48
|
+
raise ResourceNotFound if result.nil?
|
42
49
|
|
43
|
-
if result.is_a? ActiveRecord::Relation or result.is_a? Array
|
44
50
|
{
|
45
51
|
:json => result.map{|r|
|
46
52
|
r.represent( @associate_config_out.in_collection.exposed_attributes,
|
@@ -49,9 +55,23 @@ module Toast
|
|
49
55
|
@associate_config_out.media_type )
|
50
56
|
},
|
51
57
|
:status => :ok,
|
52
|
-
:content_type => @associate_config_out.in_collection.media_type
|
58
|
+
:content_type => @associate_config_out.in_collection.media_type,
|
59
|
+
:pagination_info => pagination_info
|
53
60
|
}
|
61
|
+
|
54
62
|
else
|
63
|
+
|
64
|
+
result =
|
65
|
+
if(@config.pass_params_to.include?(@assoc) and
|
66
|
+
reflection.options[:extend] and
|
67
|
+
reflectin.options[:extend].detect{|e| e.method_defined? :find_by_params} )
|
68
|
+
@record.send(@assoc).find_by_params(@params)
|
69
|
+
else
|
70
|
+
@record.send(@assoc)
|
71
|
+
end
|
72
|
+
|
73
|
+
raise ResourceNotFound if result.nil?
|
74
|
+
|
55
75
|
{
|
56
76
|
:json => result.represent( @associate_config_out.exposed_attributes,
|
57
77
|
@associate_config_out.exposed_associations,
|
data/lib/toast/collection.rb
CHANGED
@@ -32,27 +32,33 @@ module Toast
|
|
32
32
|
# will be successful, but if it's not checked the error
|
33
33
|
# message is not helpful to find the error.
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
35
|
+
if @config_out.pass_params_to.include?(@collection)
|
36
|
+
if @model.method(@collection).arity**2 != 1
|
37
|
+
raise "Toast Error: Class method '#{@collection}' of model '#{@model}' must accept one parameter, as configured by 'acts_as_resource > pass_params_to'."
|
38
|
+
end
|
39
|
+
|
40
|
+
# fetch results
|
41
|
+
#binding.pry if $halt
|
42
|
+
records, pagination_info = paginate_query( @config_out, @collection,
|
43
|
+
@model.send(@collection, @params),
|
44
|
+
@params )
|
45
|
+
else
|
46
|
+
|
47
|
+
if @model.method(@collection).arity > 0
|
48
|
+
raise "Toast Error: Class method '#{@collection}' of model '#{@model}' must not accept any parameter, as configured by 'acts_as_resource'"
|
49
|
+
end
|
50
|
+
|
51
|
+
records, pagination_info = paginate_query( @config_out, @collection,
|
52
|
+
@model.send(@collection=='all'? 'scoped': @collection), # #scoped ?: #all would trigger query too early
|
53
|
+
@params )
|
54
|
+
end
|
50
55
|
|
51
56
|
case @format
|
52
57
|
when "html"
|
53
58
|
{
|
54
59
|
:template => "resources/#{@model.to_s.pluralize.underscore}",
|
55
|
-
:locals => { @model.to_s.pluralize.underscore.to_sym => records
|
60
|
+
:locals => { @model.to_s.pluralize.underscore.to_sym => records,
|
61
|
+
:pagination_info => pagination_info }
|
56
62
|
}
|
57
63
|
when "json"
|
58
64
|
{
|
@@ -63,7 +69,8 @@ module Toast
|
|
63
69
|
@config_out.media_type)
|
64
70
|
},
|
65
71
|
:status => :ok,
|
66
|
-
:content_type => @config_out.in_collection.media_type
|
72
|
+
:content_type => @config_out.in_collection.media_type,
|
73
|
+
:pagination_info => pagination_info
|
67
74
|
}
|
68
75
|
else
|
69
76
|
raise ResourceNotFound
|
@@ -127,5 +134,6 @@ module Toast
|
|
127
134
|
def unlink l
|
128
135
|
raise MethodNotAllowed
|
129
136
|
end
|
137
|
+
|
130
138
|
end
|
131
139
|
end
|
data/lib/toast/config_dsl.rb
CHANGED
@@ -18,7 +18,7 @@ module Toast
|
|
18
18
|
@in_collection = ConfigDSL::InCollection.new model, self
|
19
19
|
@media_type = "application/json"
|
20
20
|
@apidoc = {}
|
21
|
-
|
21
|
+
@paginations = {}
|
22
22
|
@model.attr_accessible []
|
23
23
|
end
|
24
24
|
|
@@ -112,12 +112,23 @@ module Toast
|
|
112
112
|
@apidoc = arg
|
113
113
|
end
|
114
114
|
|
115
|
+
def paginate collection_name, options={}
|
116
|
+
options.reverse_merge! :page_size => 30
|
117
|
+
|
118
|
+
@paginations[collection_name.to_s] = {:page_size => options[:page_size]}
|
119
|
+
end
|
120
|
+
|
115
121
|
# non DSL methods
|
116
122
|
dsl_methods false
|
117
123
|
|
118
124
|
def field_comments
|
119
125
|
@field_comments
|
120
126
|
end
|
127
|
+
|
128
|
+
def paginations
|
129
|
+
@paginations
|
130
|
+
end
|
131
|
+
|
121
132
|
end
|
122
133
|
|
123
134
|
class InCollection
|
data/lib/toast/resource.rb
CHANGED
@@ -8,6 +8,7 @@ module Toast
|
|
8
8
|
class UnsupportedMediaType < Exception; end
|
9
9
|
class RequestedVersionNotDefined < Exception; end
|
10
10
|
class ResourceNotAcceptable < Exception; end
|
11
|
+
class BadRequest < Exception; end
|
11
12
|
|
12
13
|
# Represents a resource. There are following resource types as sub classes:
|
13
14
|
# Record, Collection, Association, Single
|
@@ -135,5 +136,40 @@ module Toast
|
|
135
136
|
|
136
137
|
out
|
137
138
|
end
|
139
|
+
|
140
|
+
|
141
|
+
def paginate_query config, relation_name, relation, params
|
142
|
+
|
143
|
+
if pagination = config.paginations[relation_name] and params[:page] =~ /\d+/
|
144
|
+
|
145
|
+
raise Toast::BadRequest.new("Page 0 not defined. Paging starts with 1") if params[:page].to_i == 0
|
146
|
+
|
147
|
+
ps = pagination[:page_size].to_i
|
148
|
+
total = relation.count
|
149
|
+
|
150
|
+
page = params[:page].to_i
|
151
|
+
total_pages = (total/ps) + (total%ps == 0 ? 0 : 1)
|
152
|
+
|
153
|
+
pagination_info = {}
|
154
|
+
|
155
|
+
if total_pages > 0
|
156
|
+
|
157
|
+
if page > total_pages
|
158
|
+
pagination_info[:prev] = total_pages
|
159
|
+
else
|
160
|
+
pagination_info[:prev] = page - 1 unless page == 1
|
161
|
+
pagination_info[:next] = page + 1 if page < total_pages
|
162
|
+
end
|
163
|
+
|
164
|
+
pagination_info[:last] = total_pages
|
165
|
+
else
|
166
|
+
pagination_info[:last] = 1
|
167
|
+
end
|
168
|
+
|
169
|
+
[relation.limit(ps).offset((page-1) * ps), pagination_info]
|
170
|
+
else
|
171
|
+
[relation, nil]
|
172
|
+
end
|
173
|
+
end
|
138
174
|
end
|
139
175
|
end
|
data/lib/toast/version.rb
CHANGED
metadata
CHANGED
@@ -1,81 +1,112 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: toast
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 35
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 8
|
9
|
+
- 14
|
10
|
+
version: 0.8.14
|
5
11
|
platform: ruby
|
6
|
-
authors:
|
7
|
-
- robokopp (Robert
|
12
|
+
authors:
|
13
|
+
- "robokopp (Robert Anni\xC3\xA9s)"
|
8
14
|
autorequire:
|
9
15
|
bindir: bin
|
10
16
|
cert_chain: []
|
11
|
-
|
12
|
-
|
13
|
-
|
17
|
+
|
18
|
+
date: 2014-04-14 00:00:00 +02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
14
22
|
name: rails
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - '>='
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 3.1.0
|
20
|
-
type: :runtime
|
21
23
|
prerelease: false
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 3
|
32
|
+
- 1
|
33
|
+
- 0
|
26
34
|
version: 3.1.0
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: blockenspiel
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ~>
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: 0.4.2
|
34
35
|
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: blockenspiel
|
35
39
|
prerelease: false
|
36
|
-
|
37
|
-
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
38
43
|
- - ~>
|
39
|
-
- !ruby/object:Gem::Version
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 11
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
- 4
|
49
|
+
- 2
|
40
50
|
version: 0.4.2
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rack-accept-media-types
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ~>
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0.9'
|
48
51
|
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: rack-accept-media-types
|
49
55
|
prerelease: false
|
50
|
-
|
51
|
-
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
52
59
|
- - ~>
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
|
55
|
-
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 25
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
- 9
|
65
|
+
version: "0.9"
|
66
|
+
type: :runtime
|
67
|
+
version_requirements: *id003
|
68
|
+
- !ruby/object:Gem::Dependency
|
56
69
|
name: ffaker
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
70
|
+
prerelease: false
|
71
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 3
|
77
|
+
segments:
|
78
|
+
- 0
|
79
|
+
version: "0"
|
62
80
|
type: :runtime
|
81
|
+
version_requirements: *id004
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: rack-link_headers
|
63
84
|
prerelease: false
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
85
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ~>
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
hash: 3
|
91
|
+
segments:
|
92
|
+
- 2
|
93
|
+
- 2
|
94
|
+
- 2
|
95
|
+
version: 2.2.2
|
96
|
+
type: :runtime
|
97
|
+
version_requirements: *id005
|
69
98
|
description: |-
|
70
99
|
Toast is an extension to Ruby on Rails that lets you expose any
|
71
100
|
ActiveRecord model as a resource according to the REST paradigm via a generic controller. The default
|
72
101
|
representation format is JSON or can be anything
|
73
102
|
email: robokopp@fernwerk.net
|
74
103
|
executables: []
|
104
|
+
|
75
105
|
extensions: []
|
76
|
-
|
106
|
+
|
107
|
+
extra_rdoc_files:
|
77
108
|
- README.md
|
78
|
-
files:
|
109
|
+
files:
|
79
110
|
- app/controller/toast_controller.rb
|
80
111
|
- config/routes.rb
|
81
112
|
- lib/toast.rb
|
@@ -89,27 +120,39 @@ files:
|
|
89
120
|
- lib/toast/resource.rb
|
90
121
|
- lib/toast/single.rb
|
91
122
|
- README.md
|
123
|
+
has_rdoc: true
|
92
124
|
homepage: https://github.com/robokopp/toast
|
93
125
|
licenses: []
|
94
|
-
|
126
|
+
|
95
127
|
post_install_message:
|
96
128
|
rdoc_options: []
|
97
|
-
|
129
|
+
|
130
|
+
require_paths:
|
98
131
|
- lib
|
99
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
132
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
133
|
+
none: false
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
hash: 3
|
138
|
+
segments:
|
139
|
+
- 0
|
140
|
+
version: "0"
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
+
none: false
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
hash: 3
|
147
|
+
segments:
|
148
|
+
- 0
|
149
|
+
version: "0"
|
109
150
|
requirements: []
|
151
|
+
|
110
152
|
rubyforge_project:
|
111
|
-
rubygems_version:
|
153
|
+
rubygems_version: 1.6.2
|
112
154
|
signing_key:
|
113
|
-
specification_version:
|
155
|
+
specification_version: 3
|
114
156
|
summary: Toast adds a RESTful interface to ActiveRecord models in Ruby on Rails.
|
115
157
|
test_files: []
|
158
|
+
|
checksums.yaml
DELETED
@@ -1,7 +0,0 @@
|
|
1
|
-
---
|
2
|
-
SHA1:
|
3
|
-
metadata.gz: b312ad41453a5e1c9d8c38fb0ebfc38d0d1c26fe
|
4
|
-
data.tar.gz: c09def11305a4ec61d570f98ef71de7982da41af
|
5
|
-
SHA512:
|
6
|
-
metadata.gz: 6aad28550213a443d1d8b3d0a8b4f4dbf282f83a1295b90c40f7e3cc1155900aaab227276455f24b5343f6bf6b670cc56d2dabe13bcb3799652a4d799692694d
|
7
|
-
data.tar.gz: 42421ba7a9854ce1b8ce0aceaf3c5716fe069ab3e396c90167a895a8a7b84cce0ddcaf4d6b68d38dfbbb86ee61acda3ac9b4f455d8ce8984108f8b93f8eaa819
|