resource_full 0.7.6
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/MIT-LICENSE +20 -0
- data/README.rdoc +100 -0
- data/Rakefile +19 -0
- data/lib/resource_full/base.rb +140 -0
- data/lib/resource_full/controllers/resources.rb +16 -0
- data/lib/resource_full/controllers/resources_controller.rb +26 -0
- data/lib/resource_full/controllers/routes_controller.rb +16 -0
- data/lib/resource_full/core_extensions/api.rb +26 -0
- data/lib/resource_full/core_extensions/exception.rb +25 -0
- data/lib/resource_full/core_extensions/from_json.rb +13 -0
- data/lib/resource_full/core_extensions/module.rb +13 -0
- data/lib/resource_full/dispatch.rb +196 -0
- data/lib/resource_full/models/resourced_route.rb +84 -0
- data/lib/resource_full/query.rb +337 -0
- data/lib/resource_full/render/html.rb +74 -0
- data/lib/resource_full/render/json.rb +107 -0
- data/lib/resource_full/render/xml.rb +106 -0
- data/lib/resource_full/render.rb +63 -0
- data/lib/resource_full/retrieve.rb +87 -0
- data/lib/resource_full/version.rb +9 -0
- data/lib/resource_full.rb +31 -0
- data/spec/resource_full/base_spec.rb +88 -0
- data/spec/resource_full/controllers/resources_spec.rb +30 -0
- data/spec/resource_full/dispatch_spec.rb +274 -0
- data/spec/resource_full/models/resourced_route_spec.rb +62 -0
- data/spec/resource_full/query/parameter_spec.rb +61 -0
- data/spec/resource_full/query_spec.rb +462 -0
- data/spec/resource_full/render/html_spec.rb +4 -0
- data/spec/resource_full/render/json_spec.rb +258 -0
- data/spec/resource_full/render/xml_spec.rb +378 -0
- data/spec/resource_full/render_spec.rb +5 -0
- data/spec/resource_full/retrieve_spec.rb +184 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +134 -0
- metadata +156 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
module ResourceFull
|
2
|
+
module Models
|
3
|
+
class RouteNotFound < Exception
|
4
|
+
end
|
5
|
+
|
6
|
+
class ResourcedRoute
|
7
|
+
attr_reader :verb, :name, :pattern, :action, :controller
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def find(what, opts={})
|
11
|
+
case what
|
12
|
+
when :all
|
13
|
+
find_all_routes(opts)
|
14
|
+
else
|
15
|
+
find_named_route(what)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def find_named_route(name)
|
20
|
+
all_named_routes.find {|route| route.name == name} or raise ResourceFull::Models::RouteNotFound, "Could not find route #{name}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def find_all_routes(opts={})
|
24
|
+
all_named_routes(opts).reject do |route|
|
25
|
+
opts.has_key?(:resource_id) && opts[:resource_id].to_s != route.resource.to_s
|
26
|
+
end.sort_by {|r| r.name.to_s}
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Translates an AR route into something a little more human-friendly, adding some extra
|
32
|
+
# relationships as it goes and cutting out the stuff we're not interested in--for example,
|
33
|
+
# formatted variants of regular routes.
|
34
|
+
def all_named_routes(opts={})
|
35
|
+
@all_named_routes ||= ActionController::Routing::Routes.named_routes.routes.collect do |name, route|
|
36
|
+
verb = route.conditions[:method].to_s.upcase
|
37
|
+
segs = route.segments.join
|
38
|
+
new(
|
39
|
+
:name => name,
|
40
|
+
:verb => verb,
|
41
|
+
:pattern => segs,
|
42
|
+
:action => route.requirements[:action],
|
43
|
+
:controller => route.requirements[:controller]
|
44
|
+
)
|
45
|
+
end.reject do |route|
|
46
|
+
route.formatted?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize(opts={})
|
53
|
+
@verb = opts[:verb]
|
54
|
+
@name = opts[:name]
|
55
|
+
@pattern = opts[:pattern]
|
56
|
+
@action = opts[:action]
|
57
|
+
@controller = ResourceFull::Base.controller_for(opts[:controller])
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_xml(opts={})
|
61
|
+
{
|
62
|
+
:resource => resource,
|
63
|
+
:verb => verb,
|
64
|
+
:name => name,
|
65
|
+
:pattern => pattern,
|
66
|
+
:action => action
|
67
|
+
}.to_xml(opts.merge(:root => "route"))
|
68
|
+
end
|
69
|
+
|
70
|
+
def formatted?
|
71
|
+
name.to_s =~ /^formatted/
|
72
|
+
end
|
73
|
+
|
74
|
+
def resource
|
75
|
+
controller.controller_name
|
76
|
+
end
|
77
|
+
|
78
|
+
def resourced?
|
79
|
+
controller.ancestors.include?(ResourceFull::Base)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,337 @@
|
|
1
|
+
module ResourceFull
|
2
|
+
module Query
|
3
|
+
class << self
|
4
|
+
def included(base)
|
5
|
+
super(base)
|
6
|
+
base.send :extend, ClassMethods
|
7
|
+
base.queryable_with :limit, :scope => lambda {|limit| { :limit => limit }}
|
8
|
+
base.queryable_with :offset, :scope => lambda {|offset| { :offset => offset }}
|
9
|
+
base.queryable_with_order
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# A Parameter represents the information necessary to describe a query relationship. It's inherently
|
14
|
+
# tied to ActiveRecord at the moment, unfortunately. Objects of this class should not be instantiated
|
15
|
+
# directly; instead, use the +queryable_with+ method.
|
16
|
+
class Parameter
|
17
|
+
attr_reader :name, :resource
|
18
|
+
|
19
|
+
def initialize(name, resource, opts={})
|
20
|
+
@name = name
|
21
|
+
@resource = resource
|
22
|
+
@fuzzy = opts[:fuzzy] || false
|
23
|
+
@allow_nil = opts[:allow_nil] || false
|
24
|
+
@default_value = opts[:default]
|
25
|
+
end
|
26
|
+
|
27
|
+
def fuzzy?; @fuzzy; end
|
28
|
+
def allow_nil?; @allow_nil; end
|
29
|
+
|
30
|
+
def to_xml(opts={})
|
31
|
+
{
|
32
|
+
:name => name.to_s,
|
33
|
+
:resource => resource.model_name.pluralize,
|
34
|
+
:fuzzy => fuzzy?,
|
35
|
+
}.to_xml(opts.merge(:root => "parameter"))
|
36
|
+
end
|
37
|
+
|
38
|
+
def applicable_to?(request_params)
|
39
|
+
if allow_nil?
|
40
|
+
request_params.has_key?(self.name.to_s) || request_params.has_key?(self.name.to_s.pluralize)
|
41
|
+
else
|
42
|
+
not param_values_for(request_params).blank?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def find(finder, request_params)
|
47
|
+
raise NotImplementedError, "Subclasses implement this behavior."
|
48
|
+
end
|
49
|
+
|
50
|
+
def subclass(new_resource)
|
51
|
+
raise NotImplementedError, "Subclasses implement this behavior."
|
52
|
+
end
|
53
|
+
|
54
|
+
protected
|
55
|
+
|
56
|
+
def param_values_for(params)
|
57
|
+
values = (params[self.name.to_s] || params[self.name.to_s.pluralize] || @default_value || '')
|
58
|
+
values = values.to_s.split(',') unless values.is_a?(Array)
|
59
|
+
values.map! {|value| "%#{value}%" } if fuzzy?
|
60
|
+
values
|
61
|
+
end
|
62
|
+
|
63
|
+
def table_for(opts={})
|
64
|
+
if opts.has_key?(:table)
|
65
|
+
opts[:table]
|
66
|
+
elsif opts.has_key?(:from)
|
67
|
+
infer_model_from(resource.model_class, opts[:from]).table_name
|
68
|
+
else
|
69
|
+
resource.model_class.table_name
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def infer_model_from(model, join)
|
74
|
+
if join.is_a? Symbol
|
75
|
+
model.reflect_on_association(join).klass
|
76
|
+
elsif join.is_a? Hash
|
77
|
+
new_model = model.reflect_on_association(join.keys.first).klass
|
78
|
+
infer_model_from new_model, join.values.first
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
class CustomParameter < Parameter
|
85
|
+
attr_reader :table, :columns, :include
|
86
|
+
|
87
|
+
def initialize(name, resource, opts={})
|
88
|
+
super(name, resource, opts)
|
89
|
+
|
90
|
+
@table = table_for(opts)
|
91
|
+
@columns = columns_for(name, resource, opts)
|
92
|
+
@negated = opts[:negated] || false
|
93
|
+
@include = opts[:from] || []
|
94
|
+
end
|
95
|
+
|
96
|
+
def allow_nil?; @allow_nil; end
|
97
|
+
def negated?; @negated; end
|
98
|
+
|
99
|
+
def find(finder, request_params)
|
100
|
+
return finder unless applicable_to?(request_params)
|
101
|
+
finder.scoped :conditions => conditions_for(request_params), :include => @include
|
102
|
+
end
|
103
|
+
|
104
|
+
def subclass(new_resource)
|
105
|
+
new_table = if @table == @resource.model_class.table_name
|
106
|
+
new_resource.model_class.table_name
|
107
|
+
else @table end
|
108
|
+
|
109
|
+
self.class.new(@name, new_resource,
|
110
|
+
:fuzzy => @fuzzy,
|
111
|
+
:allow_nil => @allow_nil,
|
112
|
+
:table => new_table,
|
113
|
+
:columns => @columns,
|
114
|
+
:from => @include,
|
115
|
+
:negated => @negated
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
def conditions_for(params)
|
122
|
+
values = param_values_for(params)
|
123
|
+
unless values.empty?
|
124
|
+
final_query_string = if negated?
|
125
|
+
values.collect { |value| negated_query_string(value) }.join(" AND ")
|
126
|
+
else
|
127
|
+
values.collect { |value| query_string(value) }.join(" OR ")
|
128
|
+
end
|
129
|
+
|
130
|
+
final_values = values.sum([]) { |value| Array.new(columns.size, value) }
|
131
|
+
|
132
|
+
[ final_query_string ] + final_values
|
133
|
+
else
|
134
|
+
if (allow_nil? && params.has_key?(self.name) && params[self.name].blank?)
|
135
|
+
[query_string(params[self.name])]
|
136
|
+
else
|
137
|
+
[]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def columns_for(name, resource, opts={})
|
143
|
+
if opts.has_key?(:columns)
|
144
|
+
opts[:columns]
|
145
|
+
elsif opts.has_key?(:column)
|
146
|
+
[ opts[:column] ]
|
147
|
+
elsif opts[:resource_identifier] && opts.has_key?(:from)
|
148
|
+
[ ResourceFull::Base.controller_for(infer_model_from(resource.model_class, opts[:from]).name.demodulize.pluralize).resource_identifier ]
|
149
|
+
else
|
150
|
+
[ name ]
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def query_string(value)
|
155
|
+
columns.collect do |column|
|
156
|
+
# Convert to a column name if column is a proc. TODO There must be a cleaner way to do this.
|
157
|
+
column = column.call(value) if column.is_a?(Proc)
|
158
|
+
if fuzzy?
|
159
|
+
"(#{table}.#{column} LIKE ?)"
|
160
|
+
elsif !value.blank?
|
161
|
+
"(#{table}.#{column} = ?)"
|
162
|
+
elsif allow_nil?
|
163
|
+
"(COALESCE(#{table}.#{column},'')='')"
|
164
|
+
end
|
165
|
+
end.join(" OR ")
|
166
|
+
end
|
167
|
+
|
168
|
+
def negated_query_string(value)
|
169
|
+
columns.collect do |column|
|
170
|
+
# Convert to a column name if column is a proc. TODO There must be a cleaner way to do this.
|
171
|
+
column = column.call(value) if column.is_a?(Proc)
|
172
|
+
if fuzzy?
|
173
|
+
"(#{table}.#{column} NOT LIKE ? OR #{table}.#{column} IS NULL)"
|
174
|
+
elsif !value.blank?
|
175
|
+
"(#{table}.#{column} != ? OR #{table}.#{column} IS NULL)"
|
176
|
+
end
|
177
|
+
end.join(" AND ")
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
class ScopedParameter < Parameter
|
182
|
+
attr_reader :scope
|
183
|
+
|
184
|
+
def initialize(name, resource, opts={})
|
185
|
+
super(name, resource, opts)
|
186
|
+
@scope = opts[:scope]
|
187
|
+
end
|
188
|
+
|
189
|
+
def method_scoped?; @scope.is_a?(Symbol); end
|
190
|
+
def proc_scoped?; @scope.is_a?(Proc); end
|
191
|
+
def hash_scoped?; @scope.is_a?(Hash); end
|
192
|
+
|
193
|
+
def find(finder, request_params)
|
194
|
+
return finder unless applicable_to?(request_params)
|
195
|
+
|
196
|
+
if proc_scoped?
|
197
|
+
finder.scoped scope.call(*param_values_for(request_params))
|
198
|
+
elsif hash_scoped?
|
199
|
+
finder.scoped scope
|
200
|
+
else
|
201
|
+
finder.send(scope, *param_values_for(request_params))
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def subclass(new_resource)
|
206
|
+
self.class.new @name, new_resource, :scope => @scope
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
class OrderParameter < Parameter
|
212
|
+
|
213
|
+
def applicable_to?(request_params)
|
214
|
+
request_params.has_key?(:order_by)
|
215
|
+
end
|
216
|
+
|
217
|
+
def natural_sort_for(opts)
|
218
|
+
if opts.has_key?(:natural_sort)
|
219
|
+
opts[:natural_sort]
|
220
|
+
else
|
221
|
+
false
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def find(finder, request_params)
|
226
|
+
return finder unless applicable_to?(request_params)
|
227
|
+
|
228
|
+
order_by = request_params[:order_by]
|
229
|
+
order_direction = request_params[:order_direction] || "asc"
|
230
|
+
sort_params = resource.orderables[order_by.to_sym] || {}
|
231
|
+
table = table_for(sort_params)
|
232
|
+
column = sort_params[:column] || order_by
|
233
|
+
|
234
|
+
order_params = returning({}) do |hash|
|
235
|
+
hash[:include] = sort_params[:from]
|
236
|
+
end
|
237
|
+
|
238
|
+
if natural_sort_for(sort_params)
|
239
|
+
# to use this natural sort you must follow these instructions: http://www.ciarpame.com/2008/06/28/true-mysql-natural-order-by-trick/
|
240
|
+
finder.scoped order_params.merge( :order => "natsort_canon(#{table}.#{column}, 'natural') #{order_direction}" )
|
241
|
+
else
|
242
|
+
finder.scoped order_params.merge( :order => "#{table}.#{column} #{order_direction}" )
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def subclass(new_resource)
|
247
|
+
self.class.new(@name, new_resource)
|
248
|
+
end
|
249
|
+
|
250
|
+
end
|
251
|
+
|
252
|
+
module ClassMethods
|
253
|
+
# Indicates that the resource should be queryable with the given parameters, which will be pulled from
|
254
|
+
# the params hash on an index or count call. Accepts the following options:
|
255
|
+
#
|
256
|
+
# * :fuzzy => true : Use a LIKE query instead of =.
|
257
|
+
# * :columns / :column => ... : Override the default column, or provide a list of columns to query for this value.
|
258
|
+
# * :from => :join_name : Indicate that this value should be queried by joining on another model. Should use
|
259
|
+
# a valid relationship from this controller's exposed model (e.g., :account if belongs_to :account is specified.)
|
260
|
+
# * :resource_identifier => true : Try to look up the resource controller for this value and honor its
|
261
|
+
# specified resource identifier. Useful for nesting relationships.
|
262
|
+
# * :allow_nils => true : Indicates that a nil value for a parameter should be taken to literally indicate
|
263
|
+
# that null values should be returned. This may be changed in the future to expect the literal string 'null'
|
264
|
+
# or some other reasonable standin.
|
265
|
+
#
|
266
|
+
# Examples:
|
267
|
+
#
|
268
|
+
# queryable_with :user_id
|
269
|
+
# queryable_with :description, :fuzzy => true
|
270
|
+
# queryable_with :name, :columns => [:first_name, :last_name]
|
271
|
+
# queryable_with :street_address, :from => :address, :column => :street
|
272
|
+
#
|
273
|
+
# TODO No full-text search support.
|
274
|
+
def queryable_with(*args)
|
275
|
+
opts = args.extract_options!
|
276
|
+
opts.assert_valid_keys :default, :fuzzy, :column, :columns, :from, :table, :resource_identifier, :allow_nil, :negated, :scope
|
277
|
+
args.each do |param|
|
278
|
+
self.queryable_params << if opts.has_key?(:scope)
|
279
|
+
ResourceFull::Query::ScopedParameter.new(param, self, opts)
|
280
|
+
else
|
281
|
+
ResourceFull::Query::CustomParameter.new(param, self, opts)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
# :nodoc:
|
287
|
+
def clear_queryable_params!
|
288
|
+
@queryable_params = []
|
289
|
+
end
|
290
|
+
|
291
|
+
# All queryable parameters. Objects are of type +ResourceFull::Query::Parameter+ or one of its subclasses.
|
292
|
+
def queryable_params
|
293
|
+
unless defined?(@queryable_params) && !@queryable_params.nil?
|
294
|
+
@queryable_params = []
|
295
|
+
if superclass.respond_to?(:queryable_params)
|
296
|
+
@queryable_params += superclass.queryable_params.collect {|param| param.subclass(self)}
|
297
|
+
end
|
298
|
+
end
|
299
|
+
@queryable_params
|
300
|
+
end
|
301
|
+
|
302
|
+
# Returns true if the controller is queryable with all of the named parameters.
|
303
|
+
def queryable_with?(*params)
|
304
|
+
(queryable_params.collect(&:name) & params.collect(&:to_sym)).size == params.size
|
305
|
+
end
|
306
|
+
|
307
|
+
# :nodoc:
|
308
|
+
def queryable_params=(params)
|
309
|
+
@queryable_params = params
|
310
|
+
end
|
311
|
+
|
312
|
+
def nests_within(*resources)
|
313
|
+
resources.each do |resource|
|
314
|
+
expected_nest_id = "#{resource.to_s.singularize}_id"
|
315
|
+
queryable_with expected_nest_id, :from => resource.to_sym, :resource_identifier => true
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def orderable_by(*params)
|
320
|
+
opts = params.extract_options!
|
321
|
+
params.each do |param|
|
322
|
+
orderables[param] = opts
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
def queryable_with_order
|
327
|
+
unless queryable_with?(:order_by)
|
328
|
+
queryable_params << ResourceFull::Query::OrderParameter.new(:order_by, self)
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
def orderables
|
333
|
+
read_inheritable_attribute(:orderables) || write_inheritable_hash(:orderables, {})
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module ResourceFull
|
2
|
+
module Render
|
3
|
+
module HTML
|
4
|
+
protected
|
5
|
+
|
6
|
+
def show_html
|
7
|
+
self.model_object = send("find_#{model_name}")
|
8
|
+
rescue ActiveRecord::RecordNotFound => e
|
9
|
+
flash[:error] = e.message
|
10
|
+
rescue => e
|
11
|
+
flash[:error] = e.message
|
12
|
+
redirect_to :back
|
13
|
+
end
|
14
|
+
|
15
|
+
def index_html
|
16
|
+
self.model_objects = send("find_all_#{model_name.pluralize}")
|
17
|
+
end
|
18
|
+
|
19
|
+
def count_html
|
20
|
+
send("count_all_#{model_name.pluralize}")
|
21
|
+
end
|
22
|
+
|
23
|
+
def new_html
|
24
|
+
self.model_object = send("new_#{model_name}")
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_html
|
28
|
+
self.model_object = transactional_create_model_object
|
29
|
+
if model_object.errors.empty?
|
30
|
+
flash[:info] = "Successfully created #{model_name.humanize} with ID of #{model_object.id}."
|
31
|
+
redirect_to :action => :index, :format => :html
|
32
|
+
else
|
33
|
+
render :action => "new"
|
34
|
+
end
|
35
|
+
rescue => e
|
36
|
+
flash[:error] = e.message
|
37
|
+
redirect_to :back
|
38
|
+
end
|
39
|
+
|
40
|
+
def edit_html
|
41
|
+
self.model_object = send("edit_#{model_name}")
|
42
|
+
end
|
43
|
+
|
44
|
+
def update_html
|
45
|
+
self.model_object = transactional_update_model_object
|
46
|
+
if model_object.errors.empty?
|
47
|
+
flash[:info] = "Successfully updated #{model_name.humanize} with ID of #{model_object.id}."
|
48
|
+
redirect_to :action => :index, :format => :html
|
49
|
+
else
|
50
|
+
render :action => "edit"
|
51
|
+
end
|
52
|
+
rescue => e
|
53
|
+
flash[:error] = e.message
|
54
|
+
redirect_to :back
|
55
|
+
end
|
56
|
+
|
57
|
+
def destroy_html
|
58
|
+
self.model_object = transactional_destroy_model_object
|
59
|
+
if model_object.errors.empty?
|
60
|
+
flash[:info] = "Successfully destroyed #{model_name.humanize} with ID of #{params[:id]}."
|
61
|
+
redirect_to :action => :index, :format => :html
|
62
|
+
else
|
63
|
+
flash[:error] = model_object.errors
|
64
|
+
end
|
65
|
+
rescue ActiveRecord::RecordNotFound => e
|
66
|
+
flash[:error] = e.message
|
67
|
+
redirect_to :back
|
68
|
+
rescue => e
|
69
|
+
flash[:error] = e.message
|
70
|
+
redirect_to :back
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module ResourceFull
|
2
|
+
module Render
|
3
|
+
module JSON
|
4
|
+
protected
|
5
|
+
|
6
|
+
def json_class_name(obj)
|
7
|
+
obj.class.name.demodulize.underscore
|
8
|
+
end
|
9
|
+
|
10
|
+
def show_json_options
|
11
|
+
{}
|
12
|
+
end
|
13
|
+
def show_json
|
14
|
+
self.model_object = send("find_#{model_name}")
|
15
|
+
render :json => model_object.to_json(show_json_options)
|
16
|
+
rescue ActiveRecord::RecordNotFound => e
|
17
|
+
render :json => e.to_json, :status => :not_found
|
18
|
+
rescue => e
|
19
|
+
handle_generic_error_in_json(e)
|
20
|
+
end
|
21
|
+
|
22
|
+
def index_json_options
|
23
|
+
{}
|
24
|
+
end
|
25
|
+
def index_json
|
26
|
+
self.model_objects = send("find_all_#{model_name.pluralize}")
|
27
|
+
render :json => model_objects.to_json(index_json_options)
|
28
|
+
end
|
29
|
+
|
30
|
+
def count_json
|
31
|
+
count = send("count_all_#{model_name.pluralize}")
|
32
|
+
render :json => {"count" => count}.to_json
|
33
|
+
end
|
34
|
+
|
35
|
+
def new_json_options
|
36
|
+
{}
|
37
|
+
end
|
38
|
+
def new_json
|
39
|
+
render :json => send("new_#{model_name}").to_json(new_json_options)
|
40
|
+
end
|
41
|
+
|
42
|
+
def create_json_options
|
43
|
+
{}
|
44
|
+
end
|
45
|
+
def create_json
|
46
|
+
self.model_object = transactional_create_model_object
|
47
|
+
if model_object.errors.empty?
|
48
|
+
render :json => model_object.to_json(create_json_options), :status => :created, :location => send("#{model_name}_url", model_object.id)
|
49
|
+
else
|
50
|
+
json_data = model_object.attributes
|
51
|
+
json_data[:errors] = {:list => model_object.errors,
|
52
|
+
:full_messages => model_object.errors.full_messages}
|
53
|
+
render :json => {json_class_name(model_object) => json_data}.to_json, :status => status_for(model_object.errors)
|
54
|
+
end
|
55
|
+
rescue => e
|
56
|
+
handle_generic_error_in_json(e)
|
57
|
+
end
|
58
|
+
|
59
|
+
def edit_json_options
|
60
|
+
{}
|
61
|
+
end
|
62
|
+
def edit_json
|
63
|
+
render :json => send("edit_#{model_name}").to_json(edit_json_options)
|
64
|
+
end
|
65
|
+
|
66
|
+
def update_json_options
|
67
|
+
{}
|
68
|
+
end
|
69
|
+
def update_json
|
70
|
+
self.model_object = transactional_update_model_object
|
71
|
+
if model_object.errors.empty?
|
72
|
+
render :json => model_object.to_json(update_json_options)
|
73
|
+
else
|
74
|
+
json_data = model_object.attributes
|
75
|
+
json_data[:errors] = {:list => model_object.errors,
|
76
|
+
:full_messages => model_object.errors.full_messages}
|
77
|
+
render :json => {json_class_name(model_object) => json_data}.to_json, :status => status_for(model_object.errors)
|
78
|
+
end
|
79
|
+
rescue ActiveRecord::RecordNotFound => e
|
80
|
+
render :json => e.to_json, :status => :not_found
|
81
|
+
rescue => e
|
82
|
+
handle_generic_error_in_json(e)
|
83
|
+
end
|
84
|
+
|
85
|
+
def destroy_json
|
86
|
+
self.model_object = transactional_destroy_model_object
|
87
|
+
if model_object.errors.empty?
|
88
|
+
head :ok
|
89
|
+
else
|
90
|
+
json_data = model_object.attributes
|
91
|
+
json_data[:errors] = {:list => model_object.errors,
|
92
|
+
:full_messages => model_object.errors.full_messages}
|
93
|
+
render :json => {json_class_name(model_object) => json_data}.to_json, :status => :unprocessable_entity
|
94
|
+
end
|
95
|
+
rescue ActiveRecord::RecordNotFound => e
|
96
|
+
render :json => e.to_json, :status => :not_found
|
97
|
+
rescue => e
|
98
|
+
handle_generic_error_in_json(e)
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
def handle_generic_error_in_json(exception)
|
103
|
+
render :json => exception, :status => :unprocessable_entity
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module ResourceFull
|
2
|
+
module Render
|
3
|
+
module XML
|
4
|
+
protected
|
5
|
+
|
6
|
+
def show_xml_options
|
7
|
+
{}
|
8
|
+
end
|
9
|
+
def show_xml
|
10
|
+
self.model_object = send("find_#{model_name}")
|
11
|
+
render :xml => model_object.to_xml({:root => model_name}.merge(show_xml_options))
|
12
|
+
rescue ActiveRecord::RecordNotFound => e
|
13
|
+
render :xml => e.to_xml, :status => :not_found
|
14
|
+
rescue => e
|
15
|
+
handle_generic_error_in_xml(e)
|
16
|
+
end
|
17
|
+
|
18
|
+
def index_xml_options
|
19
|
+
{}
|
20
|
+
end
|
21
|
+
def index_xml
|
22
|
+
self.model_objects = send("find_all_#{model_name.pluralize}")
|
23
|
+
root_tag = model_objects.all? { |e| e.is_a?(model_objects.first.class) && model_objects.first.class.to_s != "Hash" } ? model_objects.first.class.name.demodulize.underscore.pluralize : model_name.pluralize
|
24
|
+
render :xml => model_objects.to_xml({:root => root_tag}.merge(index_xml_options))
|
25
|
+
end
|
26
|
+
|
27
|
+
# Renders the number of objects in the database, in the following form:
|
28
|
+
#
|
29
|
+
# <count type="integer">34</count>
|
30
|
+
#
|
31
|
+
# This accepts the same queryable parameters as the index method.
|
32
|
+
#
|
33
|
+
# N.B. This may be highly specific to my previous experience and may go away
|
34
|
+
# in previous releases.
|
35
|
+
def count_xml
|
36
|
+
xml = Builder::XmlMarkup.new :indent => 2
|
37
|
+
xml.instruct!
|
38
|
+
render :xml => xml.count(send("count_all_#{model_name.pluralize}"))
|
39
|
+
ensure
|
40
|
+
xml = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def new_xml_options
|
44
|
+
{}
|
45
|
+
end
|
46
|
+
def new_xml
|
47
|
+
render :xml => send("new_#{model_name}").to_xml({:root => model_name}.merge(new_xml_options))
|
48
|
+
end
|
49
|
+
|
50
|
+
def create_xml_options
|
51
|
+
{}
|
52
|
+
end
|
53
|
+
def create_xml
|
54
|
+
self.model_object = transactional_create_model_object
|
55
|
+
if model_object.errors.empty?
|
56
|
+
render :xml => model_object.to_xml({:root => model_name}.merge(create_xml_options)), :status => :created, :location => send("#{model_name}_url", model_object.id)
|
57
|
+
else
|
58
|
+
render :xml => model_object.errors.to_xml, :status => status_for(model_object.errors)
|
59
|
+
end
|
60
|
+
rescue => e
|
61
|
+
handle_generic_error_in_xml(e)
|
62
|
+
end
|
63
|
+
|
64
|
+
def edit_xml_options
|
65
|
+
{}
|
66
|
+
end
|
67
|
+
def edit_xml
|
68
|
+
render :xml => send("edit_#{model_name}").to_xml({:root => model_name}.merge(edit_xml_options))
|
69
|
+
end
|
70
|
+
|
71
|
+
def update_xml_options
|
72
|
+
{}
|
73
|
+
end
|
74
|
+
def update_xml
|
75
|
+
self.model_object = transactional_update_model_object
|
76
|
+
if model_object.errors.empty?
|
77
|
+
render :xml => model_object.to_xml({:root => model_name}.merge(update_xml_options))
|
78
|
+
else
|
79
|
+
render :xml => model_object.errors.to_xml, :status => status_for(model_object.errors)
|
80
|
+
end
|
81
|
+
rescue ActiveRecord::RecordNotFound => e
|
82
|
+
render :xml => e.to_xml, :status => :not_found
|
83
|
+
rescue => e
|
84
|
+
handle_generic_error_in_xml(e)
|
85
|
+
end
|
86
|
+
|
87
|
+
def destroy_xml
|
88
|
+
self.model_object = transactional_destroy_model_object
|
89
|
+
if model_object.errors.empty?
|
90
|
+
head :ok
|
91
|
+
else
|
92
|
+
render :xml => model_object.errors, :status => :unprocessable_entity
|
93
|
+
end
|
94
|
+
rescue ActiveRecord::RecordNotFound => e
|
95
|
+
render :xml => e.to_xml, :status => :not_found
|
96
|
+
rescue => e
|
97
|
+
handle_generic_error_in_xml(e)
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
def handle_generic_error_in_xml(exception)
|
102
|
+
render :xml => exception, :status => :unprocessable_entity
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|