toast 0.9.5 → 1.0.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.
- checksums.yaml +7 -0
- data/README.md +171 -86
- data/config/routes.rb +1 -27
- data/lib/generators/toast/USAGE +1 -0
- data/lib/generators/toast/templates/toast-api.rb.erb +10 -0
- data/lib/generators/toast/toast_generator.rb +9 -0
- data/lib/toast/canonical_request.rb +179 -0
- data/lib/toast/collection_request.rb +161 -0
- data/lib/toast/config_dsl/association.rb +72 -0
- data/lib/toast/config_dsl/base.rb +50 -0
- data/lib/toast/config_dsl/collection.rb +45 -0
- data/lib/toast/config_dsl/common.rb +38 -0
- data/lib/toast/config_dsl/default_handlers.rb +83 -0
- data/lib/toast/config_dsl/expose.rb +176 -0
- data/lib/toast/config_dsl/settings.rb +35 -0
- data/lib/toast/config_dsl/single.rb +15 -0
- data/lib/toast/config_dsl/via_verb.rb +49 -0
- data/lib/toast/config_dsl.rb +60 -225
- data/lib/toast/engine.rb +19 -30
- data/lib/toast/errors.rb +41 -0
- data/lib/toast/http_range.rb +17 -0
- data/lib/toast/plural_assoc_request.rb +285 -0
- data/lib/toast/rack_app.rb +133 -0
- data/lib/toast/request_helpers.rb +134 -0
- data/lib/toast/single_request.rb +66 -0
- data/lib/toast/singular_assoc_request.rb +207 -0
- data/lib/toast/version.rb +2 -2
- data/lib/toast.rb +100 -1
- metadata +83 -116
- data/app/controller/toast_controller.rb +0 -103
- data/lib/toast/active_record_extensions.rb +0 -85
- data/lib/toast/association.rb +0 -219
- data/lib/toast/collection.rb +0 -139
- data/lib/toast/record.rb +0 -123
- data/lib/toast/resource.rb +0 -175
- data/lib/toast/single.rb +0 -89
@@ -0,0 +1,207 @@
|
|
1
|
+
require 'toast/request_helpers'
|
2
|
+
require 'link_header'
|
3
|
+
|
4
|
+
class Toast::SingularAssocRequest
|
5
|
+
include Toast::RequestHelpers
|
6
|
+
include Toast::Errors
|
7
|
+
|
8
|
+
def initialize id, config, base_config, auth, request
|
9
|
+
@id = id
|
10
|
+
@config = config
|
11
|
+
@base_config = base_config
|
12
|
+
@selected_attributes = request.query_parameters.delete(:toast_select).try(:split,',')
|
13
|
+
@uri_params = request.query_parameters
|
14
|
+
@base_uri = base_uri(request)
|
15
|
+
@verb = request.request_method.downcase
|
16
|
+
@auth = auth
|
17
|
+
@request = request
|
18
|
+
end
|
19
|
+
|
20
|
+
def respond
|
21
|
+
if @verb.in? %w(get link unlink)
|
22
|
+
self.send(@verb)
|
23
|
+
else
|
24
|
+
response :method_not_allowed,
|
25
|
+
headers: {'Allow' => allowed_methods(@config)},
|
26
|
+
msg: "#{@verb.upcase} not supported for singular association requests"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def get
|
33
|
+
if @config.via_get.nil?
|
34
|
+
response :method_not_allowed,
|
35
|
+
headers: {'Allow' => allowed_methods(@config)},
|
36
|
+
msg: "GET not configured"
|
37
|
+
else
|
38
|
+
begin
|
39
|
+
source = @base_config.model_class.find(@id)
|
40
|
+
target_config = get_config(@config.target_model_class)
|
41
|
+
model_instance = @config.via_get.handler.call(source, @uri_params)
|
42
|
+
call_allow(@config.via_get.permissions, @auth, source, @uri_params)
|
43
|
+
|
44
|
+
unless model_instance.is_a? @config.target_model_class
|
45
|
+
# wrong class
|
46
|
+
response :internal_server_error,
|
47
|
+
msg: "singular association returned `#{model_instance.class}', expected `#{@config.target_model_class}'"
|
48
|
+
else
|
49
|
+
|
50
|
+
response :ok,
|
51
|
+
headers: {"Content-Type" => @config.media_type},
|
52
|
+
body: represent(model_instance, target_config),
|
53
|
+
msg: "sent ##{@config.model_class}##{@id}"
|
54
|
+
end
|
55
|
+
|
56
|
+
rescue NotAllowed => error
|
57
|
+
return response :unauthorized, msg: "not authorized by allow block in: #{error.source_location}"
|
58
|
+
|
59
|
+
|
60
|
+
rescue BadRequest => error
|
61
|
+
response :bad_request, msg: "`#{error.message}' in: #{error.source_location}"
|
62
|
+
|
63
|
+
rescue HandlerError => error
|
64
|
+
return response :internal_server_error,
|
65
|
+
msg: "exception raised in via_get handler: `#{error.orig_error.message}' in #{error.source_location}"
|
66
|
+
|
67
|
+
rescue ActiveRecord::RecordNotFound
|
68
|
+
response :not_found,
|
69
|
+
msg: "#{@config.model_class.name}##{@config.assoc_name} not found"
|
70
|
+
|
71
|
+
rescue ConfigNotFound => error
|
72
|
+
response :internal_server_error,
|
73
|
+
msg: "no API configuration found for model `#{@config.target_model_class.name}'"
|
74
|
+
|
75
|
+
rescue => error
|
76
|
+
response :internal_server_error,
|
77
|
+
msg: "exception from via_get handler: #{error.message}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def link
|
83
|
+
if @config.via_link.nil?
|
84
|
+
response :method_not_allowed,
|
85
|
+
headers: {'Allow' => allowed_methods(@config)},
|
86
|
+
msg: "LINK not configured"
|
87
|
+
else
|
88
|
+
|
89
|
+
begin
|
90
|
+
source = @base_config.model_class.find(@id)
|
91
|
+
begin
|
92
|
+
|
93
|
+
call_allow(@config.via_link.permissions, @auth, source, @uri_params)
|
94
|
+
|
95
|
+
|
96
|
+
link = LinkHeader.parse(@request.headers['Link']).find_link(['ref','related'])
|
97
|
+
|
98
|
+
if link.nil?
|
99
|
+
return response :bad_request, msg: "Link header missing or invalid"
|
100
|
+
end
|
101
|
+
|
102
|
+
name, target_id = URI(link.href).path.split('/')[1..-1]
|
103
|
+
target_model_class = name.singularize.classify.constantize
|
104
|
+
|
105
|
+
unless is_active_record? target_model_class
|
106
|
+
return response :not_found, msg: "target class `#{target_model_class.name}' is not an `ActiveRecord'"
|
107
|
+
end
|
108
|
+
|
109
|
+
if target_model_class != @config.target_model_class
|
110
|
+
return response :bad_request,
|
111
|
+
msg: "target class `#{target_model_class.name}' invalid, expect: `#{@config.target_model_class}'"
|
112
|
+
end
|
113
|
+
|
114
|
+
@config.via_link.handler.call(source, target_model_class.find(target_id), @uri_params)
|
115
|
+
response :ok,
|
116
|
+
msg: "linked #{source.class}##{source.id} with #{target_model_class.name}##{@id}"
|
117
|
+
|
118
|
+
rescue NotAllowed => error
|
119
|
+
return response :unauthorized, msg: "not authorized by allow block in: #{error.source_location}"
|
120
|
+
|
121
|
+
rescue BadRequest => error
|
122
|
+
response :bad_request, msg: "`#{error.message}' in: #{error.source_location}"
|
123
|
+
|
124
|
+
rescue HandlerError => error
|
125
|
+
return response :internal_server_error,
|
126
|
+
msg: "exception raised in via_link handler: `#{error.orig_error.message}' in #{error.source_location}"
|
127
|
+
|
128
|
+
rescue ActiveRecord::RecordNotFound # target not found
|
129
|
+
response :not_found, msg: "#{target_model_class.name}##{target_id} not found"
|
130
|
+
|
131
|
+
rescue => error
|
132
|
+
response :internal_server_error, msg: "exception from via_link handler: #{error.message}"
|
133
|
+
end
|
134
|
+
rescue ActiveRecord::RecordNotFound # source not found
|
135
|
+
response :not_found, msg: "#{@base_config.model_class.name}##{@id} not found"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
def unlink
|
142
|
+
if @config.via_unlink.nil?
|
143
|
+
response :method_not_allowed,
|
144
|
+
headers: {'Allow' => allowed_methods(@config)},
|
145
|
+
msg: "UNLINK not configured"
|
146
|
+
else
|
147
|
+
begin
|
148
|
+
source = @base_config.model_class.find(@id)
|
149
|
+
call_allow(@config.via_unlink.permissions, @auth, source, @uri_params)
|
150
|
+
|
151
|
+
link = LinkHeader.parse(@request.headers['Link']).find_link(['ref','related'])
|
152
|
+
|
153
|
+
if link.nil?
|
154
|
+
return response :bad_request, msg: "Link header missing or invalid"
|
155
|
+
end
|
156
|
+
|
157
|
+
name, target_id = URI(link.href).path.split('/')[1..-1]
|
158
|
+
target_model_class = name.singularize.classify.constantize
|
159
|
+
|
160
|
+
unless is_active_record? target_model_class
|
161
|
+
return response :not_found, msg: "target class `#{target_model_class.name}' is not an `ActiveRecord'"
|
162
|
+
end
|
163
|
+
|
164
|
+
if target_model_class != @config.target_model_class
|
165
|
+
return response :bad_request,
|
166
|
+
msg: "target class `#{target_model_class.name}' invalid, expect: `#{@config.target_model_class}'"
|
167
|
+
end
|
168
|
+
|
169
|
+
target = nil
|
170
|
+
begin
|
171
|
+
target = target_model_class.find(target_id)
|
172
|
+
rescue ActiveRecord::RecordNotFound # target
|
173
|
+
return response :not_found, msg: "#{target_model_class.name}##{target_id} not found"
|
174
|
+
end
|
175
|
+
|
176
|
+
current = source.send(@config.assoc_name)
|
177
|
+
|
178
|
+
if current != target
|
179
|
+
return response :conflict, msg: "target `#{current.class}##{current.id}' is not associated, cannot unlink `#{target.class}##{target.id}'"
|
180
|
+
end
|
181
|
+
|
182
|
+
call_handler(@config.via_unlink.handler, source, target, @uri_params)
|
183
|
+
|
184
|
+
response :ok,
|
185
|
+
msg: "unlinked #{source.class}##{source.id} from #{target_model_class.name}##{target_id}"
|
186
|
+
|
187
|
+
rescue NotAllowed => error
|
188
|
+
return response :unauthorized,
|
189
|
+
msg: "not authorized by allow block in: #{error.source_location}"
|
190
|
+
|
191
|
+
rescue BadRequest => error
|
192
|
+
response :bad_request, msg: "`#{error.message}' in: #{error.source_location}"
|
193
|
+
|
194
|
+
rescue HandlerError => error
|
195
|
+
return response :internal_server_error,
|
196
|
+
msg: "exception raised in via_unlink handler: `#{error.orig_error.message}' in #{error.source_location}"
|
197
|
+
|
198
|
+
rescue AllowError => error
|
199
|
+
return response :internal_server_error,
|
200
|
+
msg: "exception raised in allow block: `#{error.orig_error.message}' in #{error.source_location}"
|
201
|
+
|
202
|
+
rescue ActiveRecord::RecordNotFound # source
|
203
|
+
return response :not_found, msg: "#{@base_config.model_class.name}##{@id} not found"
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
data/lib/toast/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module Toast
|
2
|
-
VERSION = '0.
|
3
|
-
end
|
2
|
+
VERSION = '1.0.0'
|
3
|
+
end
|
data/lib/toast.rb
CHANGED
@@ -1 +1,100 @@
|
|
1
|
-
|
1
|
+
unless defined?(Rails)
|
2
|
+
puts 'The Toast gem is made for Ruby on Rails 5.'
|
3
|
+
exit 1
|
4
|
+
end
|
5
|
+
|
6
|
+
unless Rails::VERSION::MAJOR == 5
|
7
|
+
puts 'Toast v1 requires Ruby on Rails 5, try v0.9 for older rails.'
|
8
|
+
exit 1
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'ostruct'
|
12
|
+
require 'toast/engine'
|
13
|
+
require 'toast/config_dsl'
|
14
|
+
require 'toast/rack_app'
|
15
|
+
|
16
|
+
module Toast
|
17
|
+
Sym = "\xF0\x9F\x8D\x9E" # The BREAD
|
18
|
+
|
19
|
+
# config data of all expose blocks
|
20
|
+
@@expositions = []
|
21
|
+
|
22
|
+
# collects all configs of one expose block (DSL methods write to it)
|
23
|
+
@@current_expose = nil
|
24
|
+
|
25
|
+
cattr_accessor :expositions, :settings
|
26
|
+
|
27
|
+
class ConfigError < StandardError
|
28
|
+
end
|
29
|
+
|
30
|
+
# called once on boot via enigne.rb
|
31
|
+
def self.init config_path='config/toast-api/*', settings_path='config/toast-api.rb'
|
32
|
+
|
33
|
+
# clean up
|
34
|
+
Toast.expositions.clear
|
35
|
+
Toast.settings = nil
|
36
|
+
|
37
|
+
settings = ''
|
38
|
+
# read global settings
|
39
|
+
if File.exists? settings_path
|
40
|
+
open settings_path do |f|
|
41
|
+
settings = f.read
|
42
|
+
end
|
43
|
+
else
|
44
|
+
info "No global settings file found: `#{settings_path}', using defaults"
|
45
|
+
settings_path = '[defaults]'
|
46
|
+
end
|
47
|
+
|
48
|
+
Toast::ConfigDSL.get_settings settings, settings_path
|
49
|
+
|
50
|
+
# read configurations
|
51
|
+
config_files = Dir[config_path]
|
52
|
+
|
53
|
+
if config_files.empty?
|
54
|
+
Toast.raise_config_error "No config files found in `#{config_path}`"
|
55
|
+
else
|
56
|
+
|
57
|
+
config_files.each do |fname|
|
58
|
+
open fname do |f|
|
59
|
+
config = f.read
|
60
|
+
Toast::ConfigDSL.get_config(config, fname)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.info str
|
67
|
+
if Rails.const_defined?('Server') # only on console server
|
68
|
+
puts Toast::Sym+' Toast: '+str
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.disable message=''
|
73
|
+
info "Disabeling resource exposition due to config errors."
|
74
|
+
info message unless message.blank?
|
75
|
+
|
76
|
+
@@expositions.clear
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.raise_config_error message
|
80
|
+
raise ConfigError.new("CONFIG ERROR: #{message}")
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.logger
|
84
|
+
@@logger ||= Logger.new("#{Rails.root}/log/toast.log")
|
85
|
+
end
|
86
|
+
|
87
|
+
# get the representation (as Hash) by instance (w/o request)
|
88
|
+
# base_uri must be passed to be prepended in URIs
|
89
|
+
def self.represent instance, base_uri = ''
|
90
|
+
|
91
|
+
# using RequestHelper#represent_one method with a mocked up object
|
92
|
+
obj = Object.new
|
93
|
+
class << obj
|
94
|
+
include Toast::RequestHelpers
|
95
|
+
attr_accessor :base_uri
|
96
|
+
end
|
97
|
+
obj.base_uri = base_uri
|
98
|
+
obj.represent_one(instance, obj.get_config(instance.class) )
|
99
|
+
end
|
100
|
+
end
|
metadata
CHANGED
@@ -1,149 +1,116 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: toast
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease: false
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 9
|
9
|
-
- 5
|
10
|
-
version: 0.9.5
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
11
5
|
platform: ruby
|
12
|
-
authors:
|
6
|
+
authors:
|
13
7
|
- robokopp (Robert Annies)
|
14
8
|
autorequire:
|
15
9
|
bindir: bin
|
16
10
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
dependencies:
|
21
|
-
- !ruby/object:Gem::Dependency
|
11
|
+
date: 2017-09-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
22
14
|
name: rails
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
hash: 3
|
30
|
-
segments:
|
31
|
-
- 3
|
32
|
-
- 1
|
33
|
-
- 0
|
34
|
-
version: 3.1.0
|
35
|
-
- - <
|
36
|
-
- !ruby/object:Gem::Version
|
37
|
-
hash: 63
|
38
|
-
segments:
|
39
|
-
- 4
|
40
|
-
- 0
|
41
|
-
- 0
|
42
|
-
version: 4.0.0
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5'
|
43
20
|
type: :runtime
|
44
|
-
version_requirements: *id001
|
45
|
-
- !ruby/object:Gem::Dependency
|
46
|
-
name: blockenspiel
|
47
21
|
prerelease: false
|
48
|
-
|
49
|
-
|
50
|
-
requirements:
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
51
24
|
- - ~>
|
52
|
-
- !ruby/object:Gem::Version
|
53
|
-
|
54
|
-
|
55
|
-
- 0
|
56
|
-
- 4
|
57
|
-
- 2
|
58
|
-
version: 0.4.2
|
59
|
-
type: :runtime
|
60
|
-
version_requirements: *id002
|
61
|
-
- !ruby/object:Gem::Dependency
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
62
28
|
name: rack-accept-media-types
|
63
|
-
|
64
|
-
|
65
|
-
none: false
|
66
|
-
requirements:
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
67
31
|
- - ~>
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
|
70
|
-
segments:
|
71
|
-
- 0
|
72
|
-
- 9
|
73
|
-
version: "0.9"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.9'
|
74
34
|
type: :runtime
|
75
|
-
version_requirements: *id003
|
76
|
-
- !ruby/object:Gem::Dependency
|
77
|
-
name: rack-link_headers
|
78
35
|
prerelease: false
|
79
|
-
|
80
|
-
|
81
|
-
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.9'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: link_header
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
82
45
|
- - ~>
|
83
|
-
- !ruby/object:Gem::Version
|
84
|
-
|
85
|
-
segments:
|
86
|
-
- 2
|
87
|
-
- 2
|
88
|
-
- 2
|
89
|
-
version: 2.2.2
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.0.8
|
90
48
|
type: :runtime
|
91
|
-
|
92
|
-
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.0.8
|
55
|
+
description: "Toast is a Rack application that hooks into Ruby on Rails. It exposes
|
56
|
+
ActiveRecord models as \na web service (REST API). The main difference from doing
|
57
|
+
that with Ruby on Rails itself is \nit's DSL that covers all aspects of an API in
|
58
|
+
one single configuration.\n"
|
93
59
|
email: robokopp@fernwerk.net
|
94
60
|
executables: []
|
95
|
-
|
96
61
|
extensions: []
|
97
|
-
|
98
|
-
|
62
|
+
extra_rdoc_files:
|
63
|
+
- README.md
|
64
|
+
files:
|
99
65
|
- README.md
|
100
|
-
files:
|
101
|
-
- app/controller/toast_controller.rb
|
102
66
|
- config/routes.rb
|
67
|
+
- lib/generators/toast/USAGE
|
68
|
+
- lib/generators/toast/templates/toast-api.rb.erb
|
69
|
+
- lib/generators/toast/toast_generator.rb
|
103
70
|
- lib/toast.rb
|
104
|
-
- lib/toast/
|
105
|
-
- lib/toast/
|
106
|
-
- lib/toast/association.rb
|
107
|
-
- lib/toast/collection.rb
|
71
|
+
- lib/toast/canonical_request.rb
|
72
|
+
- lib/toast/collection_request.rb
|
108
73
|
- lib/toast/config_dsl.rb
|
74
|
+
- lib/toast/config_dsl/association.rb
|
75
|
+
- lib/toast/config_dsl/base.rb
|
76
|
+
- lib/toast/config_dsl/collection.rb
|
77
|
+
- lib/toast/config_dsl/common.rb
|
78
|
+
- lib/toast/config_dsl/default_handlers.rb
|
79
|
+
- lib/toast/config_dsl/expose.rb
|
80
|
+
- lib/toast/config_dsl/settings.rb
|
81
|
+
- lib/toast/config_dsl/single.rb
|
82
|
+
- lib/toast/config_dsl/via_verb.rb
|
109
83
|
- lib/toast/engine.rb
|
110
|
-
- lib/toast/
|
111
|
-
- lib/toast/
|
112
|
-
- lib/toast/
|
113
|
-
-
|
114
|
-
|
84
|
+
- lib/toast/errors.rb
|
85
|
+
- lib/toast/http_range.rb
|
86
|
+
- lib/toast/plural_assoc_request.rb
|
87
|
+
- lib/toast/rack_app.rb
|
88
|
+
- lib/toast/request_helpers.rb
|
89
|
+
- lib/toast/single_request.rb
|
90
|
+
- lib/toast/singular_assoc_request.rb
|
91
|
+
- lib/toast/version.rb
|
115
92
|
homepage: https://github.com/robokopp/toast
|
116
93
|
licenses: []
|
117
|
-
|
94
|
+
metadata: {}
|
118
95
|
post_install_message:
|
119
96
|
rdoc_options: []
|
120
|
-
|
121
|
-
require_paths:
|
97
|
+
require_paths:
|
122
98
|
- lib
|
123
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
none: false
|
134
|
-
requirements:
|
135
|
-
- - ">="
|
136
|
-
- !ruby/object:Gem::Version
|
137
|
-
hash: 3
|
138
|
-
segments:
|
139
|
-
- 0
|
140
|
-
version: "0"
|
99
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - '>='
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
141
109
|
requirements: []
|
142
|
-
|
143
110
|
rubyforge_project:
|
144
|
-
rubygems_version:
|
111
|
+
rubygems_version: 2.4.6
|
145
112
|
signing_key:
|
146
|
-
specification_version:
|
147
|
-
summary: Toast
|
113
|
+
specification_version: 4
|
114
|
+
summary: Toast exposes ActiveRecord models as a web service (REST API).
|
148
115
|
test_files: []
|
149
|
-
|
116
|
+
has_rdoc:
|
@@ -1,103 +0,0 @@
|
|
1
|
-
require 'rack-link_headers'
|
2
|
-
|
3
|
-
class ToastController < ApplicationController
|
4
|
-
|
5
|
-
def catch_all
|
6
|
-
|
7
|
-
begin
|
8
|
-
|
9
|
-
@resource = Toast::Resource.build( params, request )
|
10
|
-
|
11
|
-
unless request.headers["LINK"].nil?
|
12
|
-
# extract "path_info" from link header
|
13
|
-
port = if(request.port != 80 and request.port != 443)
|
14
|
-
":#{request.port}"
|
15
|
-
else
|
16
|
-
""
|
17
|
-
end
|
18
|
-
|
19
|
-
request.headers["LINK"] =~ /(#{request.protocol + request.host + port + request.script_name})(.*)/
|
20
|
-
end
|
21
|
-
|
22
|
-
toast_response = @resource.apply(request.method, request.body.read, request.content_type, $2)
|
23
|
-
|
24
|
-
# pagination
|
25
|
-
if pi = toast_response[:pagination_info]
|
26
|
-
# URL w/o parameters
|
27
|
-
|
28
|
-
url = request.url.split('?').first
|
29
|
-
qpar = request.query_parameters.clone
|
30
|
-
|
31
|
-
# change/add page parameter
|
32
|
-
link_header = []
|
33
|
-
|
34
|
-
if pi[:prev]
|
35
|
-
qpar[:page] = pi[:prev]
|
36
|
-
response.link "#{url}?#{qpar.to_query}", :rel => :prev
|
37
|
-
end
|
38
|
-
|
39
|
-
if pi[:next]
|
40
|
-
qpar[:page] = pi[:next]
|
41
|
-
response.link "#{url}?#{qpar.to_query}", :rel => :next
|
42
|
-
end
|
43
|
-
|
44
|
-
qpar[:page] = pi[:last]
|
45
|
-
response.link "#{url}?#{qpar.to_query}", :rel => :last
|
46
|
-
|
47
|
-
qpar[:page] = 1
|
48
|
-
response.link "#{url}?#{qpar.to_query}", :rel => :first
|
49
|
-
|
50
|
-
response.headers["X-Collection-Size"] = pi[:total].to_s
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
render toast_response
|
55
|
-
|
56
|
-
rescue Toast::ResourceNotFound => e
|
57
|
-
return head(:not_found)
|
58
|
-
|
59
|
-
rescue Toast::PayloadInvalid => e
|
60
|
-
return render :text => e.message, :status => :forbidden
|
61
|
-
|
62
|
-
rescue Toast::PayloadFormatError => e
|
63
|
-
return head(:bad_request)
|
64
|
-
|
65
|
-
rescue Toast::BadRequest => e
|
66
|
-
return head(:bad_request)
|
67
|
-
|
68
|
-
rescue Toast::MethodNotAllowed => e
|
69
|
-
return head(:method_not_allowed)
|
70
|
-
|
71
|
-
rescue Toast::UnsupportedMediaType => e
|
72
|
-
return head(:unsupported_media_type)
|
73
|
-
|
74
|
-
rescue Toast::ResourceNotAcceptable => e
|
75
|
-
return head(:not_acceptable)
|
76
|
-
|
77
|
-
rescue Toast::Conflict => e
|
78
|
-
return render :text => e.message, :status => :conflict
|
79
|
-
|
80
|
-
rescue Exception => e
|
81
|
-
log_exception e
|
82
|
-
puts e if Rails.env == "test"
|
83
|
-
return head(:internal_server_error)
|
84
|
-
end
|
85
|
-
|
86
|
-
end
|
87
|
-
|
88
|
-
def not_found
|
89
|
-
return head(:not_found)
|
90
|
-
end
|
91
|
-
|
92
|
-
|
93
|
-
if Rails.env == "test"
|
94
|
-
def log_exception e
|
95
|
-
puts "#{e.class}: '#{e.message}'\n\n#{e.backtrace[0..14].join("\n")}\n\n"
|
96
|
-
end
|
97
|
-
else
|
98
|
-
def log_exception e
|
99
|
-
logger.error("#{e.class}: '#{e.message}'\n\n#{e.backtrace.join("\n")}")
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
end
|