ocean-rails 1.14.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/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
|