ocean-rails 1.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +72 -0
- data/Rakefile +38 -0
- data/lib/generators/ocean_scaffold/USAGE +8 -0
- data/lib/generators/ocean_scaffold/ocean_scaffold_generator.rb +76 -0
- data/lib/generators/ocean_scaffold/templates/controller_specs/create_spec.rb +71 -0
- data/lib/generators/ocean_scaffold/templates/controller_specs/delete_spec.rb +47 -0
- data/lib/generators/ocean_scaffold/templates/controller_specs/index_spec.rb +45 -0
- data/lib/generators/ocean_scaffold/templates/controller_specs/show_spec.rb +43 -0
- data/lib/generators/ocean_scaffold/templates/controller_specs/update_spec.rb +85 -0
- data/lib/generators/ocean_scaffold/templates/model_spec.rb +76 -0
- data/lib/generators/ocean_scaffold/templates/resource_routing_spec.rb +27 -0
- data/lib/generators/ocean_scaffold/templates/view_specs/_resource_spec.rb +55 -0
- data/lib/generators/ocean_scaffold/templates/views/_resource.json.jbuilder +8 -0
- data/lib/generators/ocean_setup/USAGE +8 -0
- data/lib/generators/ocean_setup/ocean_setup_generator.rb +93 -0
- data/lib/generators/ocean_setup/templates/Gemfile +19 -0
- data/lib/generators/ocean_setup/templates/alive_controller.rb +18 -0
- data/lib/generators/ocean_setup/templates/alive_routing_spec.rb +11 -0
- data/lib/generators/ocean_setup/templates/alive_spec.rb +12 -0
- data/lib/generators/ocean_setup/templates/api_constants.rb +19 -0
- data/lib/generators/ocean_setup/templates/application_controller.rb +8 -0
- data/lib/generators/ocean_setup/templates/application_helper.rb +34 -0
- data/lib/generators/ocean_setup/templates/config.yml.example +57 -0
- data/lib/generators/ocean_setup/templates/errors_controller.rb +14 -0
- data/lib/generators/ocean_setup/templates/gitignore +37 -0
- data/lib/generators/ocean_setup/templates/hyperlinks.rb +22 -0
- data/lib/generators/ocean_setup/templates/ocean_constants.rb +36 -0
- data/lib/generators/ocean_setup/templates/routes.rb +8 -0
- data/lib/generators/ocean_setup/templates/spec_helper.rb +47 -0
- data/lib/generators/ocean_setup/templates/zeromq_logger.rb +15 -0
- data/lib/ocean-rails.rb +38 -0
- data/lib/ocean/api.rb +263 -0
- data/lib/ocean/api_resource.rb +135 -0
- data/lib/ocean/flooding.rb +29 -0
- data/lib/ocean/ocean_application_controller.rb +214 -0
- data/lib/ocean/ocean_resource_controller.rb +76 -0
- data/lib/ocean/ocean_resource_model.rb +61 -0
- data/lib/ocean/selective_rack_logger.rb +33 -0
- data/lib/ocean/version.rb +3 -0
- data/lib/ocean/zero_log.rb +184 -0
- data/lib/ocean/zeromq_logger.rb +42 -0
- data/lib/tasks/ocean_tasks.rake +4 -0
- data/lib/template.rb +31 -0
- data/lib/templates/rails/scaffold_controller/controller.rb +91 -0
- metadata +267 -0
@@ -0,0 +1,135 @@
|
|
1
|
+
module ApiResource
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
|
10
|
+
#
|
11
|
+
# This method implements the common behaviour in Ocean for requesting collections
|
12
|
+
# of resources, including conditions, +GROUP+ and substring searches. It can be used
|
13
|
+
# directly on a class:
|
14
|
+
#
|
15
|
+
# @collection = ApiUser.collection(params)
|
16
|
+
#
|
17
|
+
# or on any Relation:
|
18
|
+
#
|
19
|
+
# @collection = @api_user.groups.collection(params)
|
20
|
+
#
|
21
|
+
# Since a Relation is returned, further chaining is possible:
|
22
|
+
#
|
23
|
+
# @collection = @api_user.groups.collection(params).active.order("email ASC")
|
24
|
+
#
|
25
|
+
# The whole params hash can safely be passed as the input arg: keys are filtered so
|
26
|
+
# that matches only are done against the attributes declared in the controller using
|
27
|
+
# +ocean_resource_model+.
|
28
|
+
#
|
29
|
+
# The +group:+ keyword arg, if present, adds a +GROUP+ clause to the generated SQL.
|
30
|
+
#
|
31
|
+
# The +search:+ keyword arg, if present, searches for the value in the database string or
|
32
|
+
# text column declared in the controller's +ocean_resource_model+ declaration.
|
33
|
+
# The search is done using an SQL +LIKE+ clause, with the substring framed by
|
34
|
+
# wildcard characters. It's self-evident that this is not an efficient search method
|
35
|
+
# for larger datasets; in such cases, other search methods should be employed.
|
36
|
+
#
|
37
|
+
# If +page:+ is present, pagination will be added. If +page+ is less than zero, an
|
38
|
+
# empty Relation will be returned. Otherwise, +page_size:+ (default 25) will be used
|
39
|
+
# to calculate OFFSET and LIMIT. The default +page_size+ for a resource class can
|
40
|
+
# also be declared using +ocean_resource_model+.
|
41
|
+
#
|
42
|
+
def collection(bag={})
|
43
|
+
collection_internal bag, bag[:group], bag[:search], bag[:page], bag[:page_size]
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def collection_internal(conds={}, group, search, page, page_size)
|
48
|
+
if index_only != []
|
49
|
+
new_conds = {}
|
50
|
+
index_only.each { |key| new_conds[key] = conds[key] if conds[key].present? }
|
51
|
+
conds = new_conds
|
52
|
+
end
|
53
|
+
# Fold in the conditions
|
54
|
+
query = all.where(conds)
|
55
|
+
# Take care of grouping
|
56
|
+
query = query.group(group) if group.present? && index_only.include?(group.to_sym)
|
57
|
+
# Searching
|
58
|
+
if search.present?
|
59
|
+
return query.none if index_search_property.blank?
|
60
|
+
query = query.where("#{index_search_property} LIKE ?", "%#{search}%")
|
61
|
+
end
|
62
|
+
# Pagination
|
63
|
+
if page.present?
|
64
|
+
return query.none if page < 0
|
65
|
+
query = query.limit(page_size || collection_page_size).offset(page_size * page)
|
66
|
+
end
|
67
|
+
# Finally, return the accumulated Relation
|
68
|
+
query
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
#
|
73
|
+
# Returns the latest version for the resource class. E.g.:
|
74
|
+
#
|
75
|
+
# > ApiUser.latest_version
|
76
|
+
# "v1"
|
77
|
+
#
|
78
|
+
def latest_api_version
|
79
|
+
Api.version_for(self.class.name.pluralize.underscore)
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# Invalidate all members of this class in Varnish using a +BAN+ requests to all
|
84
|
+
# caches in the Chef environment. The +BAN+ requests are done in parallel.
|
85
|
+
# The number of +BAN+ requests, and the exact +URI+ composition in each request,
|
86
|
+
# is determined by the +invalidate_collection:+ arg to the +ocean_resource_model+
|
87
|
+
# declaration in the model.
|
88
|
+
#
|
89
|
+
def invalidate
|
90
|
+
resource_name = name.pluralize.underscore
|
91
|
+
varnish_invalidate_collection.each do |suffix|
|
92
|
+
Api.ban "/v[0-9]+/#{resource_name}#{suffix}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
# Instance methods
|
100
|
+
|
101
|
+
#
|
102
|
+
# Convenience function used to touch two resources in one call, e.g:
|
103
|
+
#
|
104
|
+
# @api_user.touch_both(@connectee)
|
105
|
+
#
|
106
|
+
def touch_both(other)
|
107
|
+
touch
|
108
|
+
other.touch
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
#
|
113
|
+
# Invalidate the member and all its collections in Varnish using a +BAN+ requests to all
|
114
|
+
# caches in the Chef environment. The +BAN+ request are done in parallel.
|
115
|
+
# The number of +BAN+ requests, and the exact +URI+ composition in each request,
|
116
|
+
# is determined by the +invalidate_member:+ arg to the +ocean_resource_model+
|
117
|
+
# declaration in the model.
|
118
|
+
#
|
119
|
+
# The optional arg +avoid_self+, if true (the default is false), avoids invalidating
|
120
|
+
# the basic resource itself: only its derived collections are invalidated. This is useful
|
121
|
+
# when instantiating a new resource.
|
122
|
+
#
|
123
|
+
def invalidate(avoid_self=false)
|
124
|
+
self.class.invalidate
|
125
|
+
resource_name = self.class.name.pluralize.underscore
|
126
|
+
varnish_invalidate_member.each do |thing|
|
127
|
+
if thing.is_a?(String)
|
128
|
+
Api.ban "/v[0-9]+/#{resource_name}/#{self.id}#{thing}" if !avoid_self
|
129
|
+
else
|
130
|
+
Api.ban "/v[0-9]+/#{thing.call(self)}"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# # Rack::Attack.blacklist('block 1.2.3.4') do |req|
|
2
|
+
# # true
|
3
|
+
# # end
|
4
|
+
|
5
|
+
# Rack::Attack.blacklisted_response = lambda do |env|
|
6
|
+
# [ 403, {}, ['Blacklisted']]
|
7
|
+
# end
|
8
|
+
|
9
|
+
|
10
|
+
# Rack::Attack.throttle('req/ip', :limit => 1000, :period => 1.second) do |req|
|
11
|
+
# # If the return value is truthy, the cache key for the return value
|
12
|
+
# # is incremented and compared with the limit. In this case:
|
13
|
+
# # "rack::attack:#{Time.now.to_i/1.second}:req/ip:#{req.ip}"
|
14
|
+
# # We might want to use the token value instead of the #{req.ip} value.
|
15
|
+
# # (IPs may be shared, tokens never are.)
|
16
|
+
# #
|
17
|
+
# # If falsy, the cache key is neither incremented nor checked.
|
18
|
+
# req.ip
|
19
|
+
# end
|
20
|
+
|
21
|
+
# Rack::Attack.throttled_response = lambda do |env|
|
22
|
+
# allowed = env['rack.attack.match_data'][:limit]
|
23
|
+
# t = env['rack.attack.match_data'][:period].inspect
|
24
|
+
# made = env['rack.attack.match_data'][:count]
|
25
|
+
# retry_after = env['rack.attack.match_data'][:period] rescue nil
|
26
|
+
# [ 429,
|
27
|
+
# {'Retry-After' => retry_after.to_s},
|
28
|
+
# ["Too Many Requests. You have exceeded your quota of #{allowed} request(s)/#{t} by #{made - allowed}."]]
|
29
|
+
# end
|
@@ -0,0 +1,214 @@
|
|
1
|
+
module OceanApplicationController
|
2
|
+
|
3
|
+
#
|
4
|
+
# Sets the default URL generation options to the HTTPS protocol, and
|
5
|
+
# the host to the OCEAN_API_HOST, that is, to the external URL of the
|
6
|
+
# Ocean API. We always generate external URIs, even for internal calls.
|
7
|
+
# It's the responsibility of the other service to rewrite external
|
8
|
+
# to internal URIs when calling the internal API point.
|
9
|
+
#
|
10
|
+
def default_url_options(options = nil)
|
11
|
+
{ :protocol => "https", :host => OCEAN_API_HOST }
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
#
|
16
|
+
# Ensures that there is an +X-API-Token+ HTTP header in the request.
|
17
|
+
# Stores the token in @x_api_token for use during authorisation of the
|
18
|
+
# current controller action. If there's no +X-API-Token+ header, the
|
19
|
+
# request is aborted and an API error with status 400 is returned.
|
20
|
+
#
|
21
|
+
# 400 error responses will always contain a body with error information
|
22
|
+
# explaining the API error:
|
23
|
+
#
|
24
|
+
# {"_api_error": ["X-API-Token missing"]}
|
25
|
+
#
|
26
|
+
# or
|
27
|
+
#
|
28
|
+
# {"_api_error": ["Authentication expired"]}
|
29
|
+
#
|
30
|
+
def require_x_api_token
|
31
|
+
return true if ENV['NO_OCEAN_AUTH']
|
32
|
+
@x_api_token = request.headers['X-API-Token']
|
33
|
+
return true if @x_api_token.present?
|
34
|
+
logger.info "X-API-Token missing"
|
35
|
+
render_api_error 400, "X-API-Token missing"
|
36
|
+
false
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
#
|
42
|
+
# Class variable to hold any extra controller actions defined in the
|
43
|
+
# +ocean_resource_controller+ declaration in the resource controller.
|
44
|
+
#
|
45
|
+
@@extra_actions = {}
|
46
|
+
|
47
|
+
|
48
|
+
#
|
49
|
+
# Performs authorisation of the current action. Returns true if allowed,
|
50
|
+
# false if not. Calls the Auth service using a +GET+, which means previous
|
51
|
+
# authorisations using the same token and args will be cached in Varnish.
|
52
|
+
#
|
53
|
+
def authorize_action
|
54
|
+
return true if ENV['NO_OCEAN_AUTH']
|
55
|
+
# Obtain any nonstandard actions
|
56
|
+
@@extra_actions[controller_name] ||= begin
|
57
|
+
extra_actions
|
58
|
+
rescue NameError => e
|
59
|
+
{}
|
60
|
+
end
|
61
|
+
# Create a query string and call Auth
|
62
|
+
qs = Api.authorization_string(@@extra_actions, controller_name, action_name)
|
63
|
+
response = Api.permitted?(@x_api_token, query: qs)
|
64
|
+
if response.status == 200
|
65
|
+
@auth_api_user_id = response.body['authentication']['user_id'] # Deprecate and remove
|
66
|
+
@auth_api_user_uri = response.body['authentication']['_links']['creator']['href'] # Keep
|
67
|
+
return true
|
68
|
+
end
|
69
|
+
error_messages = response.body['_api_error']
|
70
|
+
render_api_error response.status, *error_messages
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
#
|
76
|
+
# Updates +created_by+ and +updated_by+ to the ApiUser for which the current request
|
77
|
+
# is authorised. The attributes can be declared either String (recommended) or
|
78
|
+
# Integer (deprecated). If String, they will be set to the URI of the ApiUser. (If
|
79
|
+
# Integer, to their internal SQL ID.)
|
80
|
+
#
|
81
|
+
def set_updater(obj)
|
82
|
+
id_or_uri = obj.created_by.is_a?(Integer) ? @auth_api_user_id : @auth_api_user_uri
|
83
|
+
obj.created_by = id_or_uri if obj.created_by.blank? || obj.created_by == 0
|
84
|
+
obj.updated_by = id_or_uri
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
#
|
89
|
+
# Renders an API level error. The body will be a JSON hash with a single key,
|
90
|
+
# +_api_error+. The value is an array containing the +messages+.
|
91
|
+
#
|
92
|
+
# render_api_error(500, "An unforeseen error occurred")
|
93
|
+
#
|
94
|
+
# results in a response with HTTP status 500 and the following body:
|
95
|
+
#
|
96
|
+
# {"_api_error": ["An unforeseen error occurred"]}
|
97
|
+
#
|
98
|
+
# Resource consumers should always examine the body when an error is returned,
|
99
|
+
# as +_api_error+ always will give additional information which may be required
|
100
|
+
# to process the error properly.
|
101
|
+
#
|
102
|
+
def render_api_error(status_code, *messages)
|
103
|
+
render json: {_api_error: messages}, status: status_code
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
# Renders a +HEAD+ response with HTTP status 204 No Content.
|
108
|
+
#
|
109
|
+
def render_head_204
|
110
|
+
render text: '', status: 204, content_type: 'application/json'
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
# Renders a HTTP 422 Unprocessable Entity response with a body enumerating
|
115
|
+
# each invalid Rails resource attribute and all their errors. This is usually
|
116
|
+
# done in response to detecting a resource is invalid during +POST+ (create) and
|
117
|
+
# +PUT/PATCH+ (update). E.g.:
|
118
|
+
#
|
119
|
+
# {"name": ["must be specified"],
|
120
|
+
# "email": ["must be specified", "must contain a @ character"]}
|
121
|
+
#
|
122
|
+
# The messages are intended for presentation to an end user.
|
123
|
+
#
|
124
|
+
def render_validation_errors(r)
|
125
|
+
render json: r.errors, :status => 422
|
126
|
+
end
|
127
|
+
|
128
|
+
#
|
129
|
+
# This is the main rendering function in Ocean. The argument +x+ can be a resource
|
130
|
+
# or a collection of resources (which need not be of the same type).
|
131
|
+
#
|
132
|
+
# The keyword arg +new+, if true, sets the response HTTP status to 201 and also adds
|
133
|
+
# a +Location+ HTTP header with the URI of the resource.
|
134
|
+
#
|
135
|
+
# Rendering is done using partials only. These should by convention be located in
|
136
|
+
# their standard position, begin with an underscore, etc. The +ocean+ gem generator
|
137
|
+
# for resources creates a partial in the proper location.
|
138
|
+
#
|
139
|
+
def api_render(x, new: false)
|
140
|
+
if !x.is_a?(Array) && !x.is_a?(ActiveRecord::Relation)
|
141
|
+
partial = x.to_partial_path
|
142
|
+
if new
|
143
|
+
render partial: partial, object: x, status: 201, location: x
|
144
|
+
else
|
145
|
+
render partial: partial, object: x
|
146
|
+
end
|
147
|
+
return
|
148
|
+
elsif x == []
|
149
|
+
render text: '[]'
|
150
|
+
return
|
151
|
+
else
|
152
|
+
partials = x.collect { |m| render_to_string(partial: m.to_partial_path,
|
153
|
+
locals: {m.class.model_name.i18n_key => m}) }
|
154
|
+
render text: '[' + partials.join(',') + ']'
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
#
|
160
|
+
# Filters away all non-accessible attributes from params. Thus, we still are
|
161
|
+
# using pre-Rails 4.0 protected attributes. This will eventually be replaced
|
162
|
+
# by strong parameters. Takes a class and returns a new hash containing only
|
163
|
+
# the model attributes which may be modified.
|
164
|
+
#
|
165
|
+
def filtered_params(klass)
|
166
|
+
result = {}
|
167
|
+
params.each do |k, v|
|
168
|
+
result[k] = v if klass.accessible_attributes.include?(k)
|
169
|
+
end
|
170
|
+
result
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
#
|
175
|
+
# Cache values for collections. Accepts a class or a scope. The cache value
|
176
|
+
# is based on three components: (1) the name of the class, (2) the number of
|
177
|
+
# members in the collection, and (3) the modification time of the last updated
|
178
|
+
# member.
|
179
|
+
#
|
180
|
+
def collection_etag(coll)
|
181
|
+
coll.name.constantize # Force a load of the class (for secondary collections)
|
182
|
+
last_updated = coll.order(:updated_at).last.updated_at.utc rescue 0
|
183
|
+
# We could also, in the absence of an updated_at attribute, use created_at.
|
184
|
+
{ etag: "#{coll.name}:#{coll.count}:#{last_updated}"
|
185
|
+
}
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
#
|
190
|
+
# This method finds the other resource for connect/disconnect, given the
|
191
|
+
# value of the param +href+, which should be a complete resource URI.
|
192
|
+
#
|
193
|
+
# Renders API errors if the +href+ arg is missing, can't be parsed, or
|
194
|
+
# the resource can't be found.
|
195
|
+
#
|
196
|
+
# Sets @connectee_class to the class of the resource pointed to by +href+,
|
197
|
+
# and @connectee to the resource itself.
|
198
|
+
#
|
199
|
+
def find_connectee
|
200
|
+
href = params[:href]
|
201
|
+
render_api_error(422, "href query arg is missing") and return if href.blank?
|
202
|
+
begin
|
203
|
+
routing = Rails.application.routes.recognize_path(href)
|
204
|
+
rescue ActionController::RoutingError
|
205
|
+
render_api_error(422, "href query arg isn't parseable")
|
206
|
+
return
|
207
|
+
end
|
208
|
+
@connectee_class = routing[:controller].classify.constantize
|
209
|
+
@connectee = @connectee_class.find_by_id(routing[:id])
|
210
|
+
render_api_error(404, "Resource to connect not found") and return unless @connectee
|
211
|
+
true
|
212
|
+
end
|
213
|
+
|
214
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
#
|
2
|
+
# This is an "acts_as" type method to be used in ActiveRecord model
|
3
|
+
# definitions: "ocean_resource_controller".
|
4
|
+
#
|
5
|
+
|
6
|
+
module Ocean
|
7
|
+
module OceanResourceController
|
8
|
+
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
included do
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
|
16
|
+
#
|
17
|
+
# The presence of +ocean_resource_controller+ in a Rails controller declares
|
18
|
+
# that the controller is an Ocean controller handling an Ocean resource. It takes
|
19
|
+
# two keyword parameters:
|
20
|
+
#
|
21
|
+
# +required_attributes+: a list of keywords naming model attributes which must be
|
22
|
+
# present in every update operation. If an API consumer submits data where any
|
23
|
+
# of these attributes isn't present, an API error will be generated.
|
24
|
+
#
|
25
|
+
# ocean_resource_controller required_attributes: [:lock_version, :title]
|
26
|
+
#
|
27
|
+
# +extra_actions+: a hash containing information about extra controller actions
|
28
|
+
# apart from the standard Rails ones of +index+, +show+, +create+, +update+, and
|
29
|
+
# +destroy+. One entry per extra action is required in order to process authentication
|
30
|
+
# requests. Here's an example:
|
31
|
+
#
|
32
|
+
# ocean_resource_controller extra_actions: {'comments' => ['comments', "GET"],
|
33
|
+
# 'comment_create' => ['comments', "POST"]}
|
34
|
+
#
|
35
|
+
# The above example declares that the controller has two non-standard actions called
|
36
|
+
# +comments+ and +comments_create+, respectively. Their respective values indicate that
|
37
|
+
# +comments+ will be called as the result of a +GET+ to the +comments+ hyperlink, and
|
38
|
+
# that +comment_create+ will be called as the result of a +POST+ to the same hyperlink.
|
39
|
+
# Thus, +extra_actions+ maps actions to hyperlink names and HTTP methods.
|
40
|
+
#
|
41
|
+
def ocean_resource_controller(required_attributes: [:lock_version, :name, :description],
|
42
|
+
extra_actions: {}
|
43
|
+
)
|
44
|
+
cattr_accessor :ocean_resource_controller_extra_actions
|
45
|
+
cattr_accessor :ocean_resource_controller_required_attributes
|
46
|
+
self.ocean_resource_controller_extra_actions = extra_actions
|
47
|
+
self.ocean_resource_controller_required_attributes = required_attributes
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
#
|
53
|
+
# Used in controller code internals to obtain the extra actions declared using
|
54
|
+
# +ocean_resource_controller+.
|
55
|
+
#
|
56
|
+
def extra_actions
|
57
|
+
self.class.ocean_resource_controller_extra_actions
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
#
|
62
|
+
# Returns true if the params hash lacks a required attribute declared using
|
63
|
+
# +ocean_resource_controller+.
|
64
|
+
#
|
65
|
+
def missing_attributes?
|
66
|
+
self.class.ocean_resource_controller_required_attributes.each do |attr|
|
67
|
+
return true unless params[attr]
|
68
|
+
end
|
69
|
+
return false
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
ActionController::Base.send :include, Ocean::OceanResourceController
|