cathode 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +346 -125
- data/Rakefile +1 -0
- data/app/controllers/cathode/base_controller.rb +28 -45
- data/app/models/cathode/token.rb +21 -0
- data/config/routes.rb +16 -0
- data/db/migrate/20140425164100_create_cathode_tokens.rb +11 -0
- data/lib/cathode.rb +0 -13
- data/lib/cathode/_version.rb +2 -1
- data/lib/cathode/action.rb +197 -39
- data/lib/cathode/action_dsl.rb +60 -0
- data/lib/cathode/base.rb +81 -12
- data/lib/cathode/create_request.rb +21 -0
- data/lib/cathode/custom_request.rb +5 -0
- data/lib/cathode/debug.rb +25 -0
- data/lib/cathode/destroy_request.rb +13 -0
- data/lib/cathode/engine.rb +9 -0
- data/lib/cathode/exceptions.rb +20 -0
- data/lib/cathode/index_request.rb +40 -0
- data/lib/cathode/object_collection.rb +49 -0
- data/lib/cathode/query.rb +24 -0
- data/lib/cathode/railtie.rb +21 -0
- data/lib/cathode/request.rb +139 -7
- data/lib/cathode/resource.rb +50 -19
- data/lib/cathode/resource_dsl.rb +46 -0
- data/lib/cathode/show_request.rb +13 -0
- data/lib/cathode/update_request.rb +26 -0
- data/lib/cathode/version.rb +112 -23
- data/lib/tasks/cathode_tasks.rake +5 -4
- data/spec/dummy/app/api/api.rb +0 -0
- data/spec/dummy/app/models/payment.rb +3 -0
- data/spec/dummy/app/models/product.rb +1 -0
- data/spec/dummy/app/models/sale.rb +5 -0
- data/spec/dummy/app/models/salesperson.rb +3 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20140409183635_create_sales.rb +11 -0
- data/spec/dummy/db/migrate/20140423172419_create_salespeople.rb +11 -0
- data/spec/dummy/db/migrate/20140424181343_create_payments.rb +10 -0
- data/spec/dummy/db/schema.rb +31 -1
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +1167 -0
- data/spec/dummy/log/test.log +180602 -0
- data/spec/dummy/spec/factories/payments.rb +8 -0
- data/spec/dummy/spec/factories/products.rb +1 -1
- data/spec/dummy/spec/factories/sales.rb +9 -0
- data/spec/dummy/spec/factories/salespeople.rb +7 -0
- data/spec/dummy/spec/requests/requests_spec.rb +434 -0
- data/spec/lib/cathode/action_spec.rb +136 -0
- data/spec/lib/cathode/base_spec.rb +34 -0
- data/spec/lib/cathode/create_request_spec.rb +40 -0
- data/spec/lib/cathode/custom_request_spec.rb +31 -0
- data/spec/lib/cathode/debug_spec.rb +25 -0
- data/spec/lib/cathode/destroy_request_spec.rb +28 -0
- data/spec/lib/cathode/index_request_spec.rb +62 -0
- data/spec/lib/cathode/object_collection_spec.rb +66 -0
- data/spec/lib/cathode/query_spec.rb +28 -0
- data/spec/lib/cathode/request_spec.rb +58 -0
- data/spec/lib/cathode/resource_spec.rb +482 -0
- data/spec/lib/cathode/show_request_spec.rb +23 -0
- data/spec/lib/cathode/update_request_spec.rb +41 -0
- data/spec/lib/cathode/version_spec.rb +416 -0
- data/spec/models/cathode/token_spec.rb +62 -0
- data/spec/spec_helper.rb +8 -2
- data/spec/support/factories/payments.rb +3 -0
- data/spec/support/factories/sale.rb +3 -0
- data/spec/support/factories/salespeople.rb +3 -0
- data/spec/support/factories/token.rb +3 -0
- data/spec/support/helpers.rb +13 -2
- metadata +192 -47
- data/app/helpers/cathode/application_helper.rb +0 -4
- data/spec/dummy/app/api/dummy_api.rb +0 -4
- data/spec/integration/api_spec.rb +0 -88
- data/spec/lib/action_spec.rb +0 -140
- data/spec/lib/base_spec.rb +0 -28
- data/spec/lib/request_spec.rb +0 -5
- data/spec/lib/resources_spec.rb +0 -78
- data/spec/lib/versioning_spec.rb +0 -104
data/lib/cathode/base.rb
CHANGED
@@ -1,27 +1,96 @@
|
|
1
|
-
require '
|
2
|
-
require 'cathode/
|
3
|
-
require 'cathode/
|
4
|
-
require 'cathode/action'
|
5
|
-
require 'cathode/version'
|
1
|
+
require 'cathode/engine'
|
2
|
+
require 'cathode/railtie'
|
3
|
+
require 'cathode/exceptions'
|
6
4
|
|
5
|
+
# Cathode is a gem for creating API boilerplate for resourceful Rails
|
6
|
+
# applications. It has first-class support for versions, model-backed resources,
|
7
|
+
# default actions like `create` and `destroy`, and custom actions.
|
7
8
|
module Cathode
|
8
|
-
|
9
|
-
|
9
|
+
autoload :Action, 'cathode/action'
|
10
|
+
autoload :ActionDsl, 'cathode/action_dsl'
|
11
|
+
autoload :CreateRequest, 'cathode/create_request'
|
12
|
+
autoload :CustomRequest, 'cathode/custom_request'
|
13
|
+
autoload :Debug, 'cathode/debug'
|
14
|
+
autoload :DeepClone, 'deep_clone'
|
15
|
+
autoload :DestroyRequest, 'cathode/destroy_request'
|
16
|
+
autoload :IndexRequest, 'cathode/index_request'
|
17
|
+
autoload :ObjectCollection, 'cathode/object_collection'
|
18
|
+
autoload :Query, 'cathode/query'
|
19
|
+
autoload :Request, 'cathode/request'
|
20
|
+
autoload :Resource, 'cathode/resource'
|
21
|
+
autoload :ResourceDsl, 'cathode/resource_dsl'
|
22
|
+
autoload :Semantic, 'semantic'
|
23
|
+
autoload :ShowRequest, 'cathode/show_request'
|
24
|
+
autoload :UpdateRequest, 'cathode/update_request'
|
25
|
+
autoload :Version, 'cathode/version'
|
26
|
+
|
27
|
+
# The actions whose default behavior is defined by Cathode.
|
28
|
+
DEFAULT_ACTIONS = [:index, :show, :create, :update, :destroy]
|
10
29
|
|
30
|
+
# Holds the top-level Cathode accessors for defining an API.
|
31
|
+
class Base
|
11
32
|
class << self
|
33
|
+
attr_reader :tokens_required
|
34
|
+
|
35
|
+
# Defines an API
|
36
|
+
# @param block The API's versions and resources, defined using this
|
37
|
+
# class's `version`, `resources`, and `resource` methods
|
38
|
+
def define(&block)
|
39
|
+
instance_eval(&block)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Lists the collection of versions associated with this API
|
43
|
+
# @return [Cathode::ObjectCollection] the collection of versions
|
44
|
+
def versions
|
45
|
+
Version.all
|
46
|
+
end
|
47
|
+
|
48
|
+
# Defines a new version
|
49
|
+
# @param version_number [String, Fixnum, Float] A number or string
|
50
|
+
# representing a SemVer-compliant version number. If a `Fixnum` or
|
51
|
+
# `Float` is passed, it will be converted to a string before being
|
52
|
+
# evaluated for SemVer compliance, so passing `1.5` is equivalent to
|
53
|
+
# passing `'1.5.0'`.
|
54
|
+
# @param block A block defining the version's resources and actions, and
|
55
|
+
# has access to the methods in the {Cathode::ActionDsl} and {Cathode::ResourceDsl}
|
56
|
+
def version(version_number, &block)
|
57
|
+
Version.define(version_number, &block)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Defines a singular resource on version 1.0.0 of the API
|
61
|
+
# @param resource_name [Symbol] The resource's name
|
62
|
+
# @param params [Hash] Optional params, e.g. `{ actions: :all }`
|
63
|
+
# @param block A block defining the resource's actions and properties, run
|
64
|
+
# inside the context of version 1.0.0 with access to the methods in the
|
65
|
+
# {Cathode::ActionDsl} and {Cathode::ResourceDsl}
|
12
66
|
def resource(resource_name, params = nil, &block)
|
13
67
|
version 1 do
|
14
68
|
resource resource_name, params, &block
|
15
69
|
end
|
16
70
|
end
|
17
71
|
|
18
|
-
|
19
|
-
|
72
|
+
# Defines a plural resource on version 1.0.0 of the API
|
73
|
+
# @param resource_name [Symbol] The resource's name
|
74
|
+
# @param params [Hash] Optional params, e.g. `{ actions: :all }`
|
75
|
+
# @param block A block defining the resource's actions and properties, run
|
76
|
+
# inside the context of version 1.0.0 with access to the methods in the
|
77
|
+
# {Cathode::ActionDsl} and {Cathode::ResourceDsl}
|
78
|
+
def resources(resource_name, params = nil, &block)
|
79
|
+
version 1 do
|
80
|
+
resources resource_name, params, &block
|
81
|
+
end
|
20
82
|
end
|
21
83
|
|
22
|
-
|
23
|
-
|
24
|
-
|
84
|
+
# Configures this API to require incoming requests to have a valid token
|
85
|
+
def require_tokens
|
86
|
+
@tokens_required = true
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def reset!
|
92
|
+
versions.clear
|
93
|
+
@tokens_required = false
|
25
94
|
end
|
26
95
|
end
|
27
96
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Cathode
|
2
|
+
# Defines the default behavior for a create request.
|
3
|
+
class CreateRequest < Request
|
4
|
+
# Sets the default action to create a new resource. If the resource is
|
5
|
+
# singular, sets the parent resource `id` as well.
|
6
|
+
def default_action_block
|
7
|
+
proc do
|
8
|
+
begin
|
9
|
+
create_params = instance_eval(&@strong_params)
|
10
|
+
if resource.singular
|
11
|
+
create_params["#{parent_resource_name}_id"] = parent_resource_id
|
12
|
+
end
|
13
|
+
body model.create(create_params)
|
14
|
+
rescue ActionController::ParameterMissing => error
|
15
|
+
body error.message
|
16
|
+
status :bad_request
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Cathode
|
2
|
+
# Provides information about the Cathode API.
|
3
|
+
class Debug
|
4
|
+
class << self
|
5
|
+
# Gathers information about the API's versions, properties, resources, and
|
6
|
+
# actions.
|
7
|
+
# @return [String] A string listing the versions, resources, and actions
|
8
|
+
def info
|
9
|
+
output = ''
|
10
|
+
Cathode::Base.versions.each do |version|
|
11
|
+
output += "\nVersion #{version.version}"
|
12
|
+
|
13
|
+
version._resources.each do |resource|
|
14
|
+
output += "\n #{resource.name}/"
|
15
|
+
resource.actions.each do |action|
|
16
|
+
output += "\n #{action.name}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
output
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Cathode
|
2
|
+
# Defines the default behavior for a destroy request.
|
3
|
+
class DestroyRequest < Request
|
4
|
+
# Sets the default action to destroy a resource. If the resource is
|
5
|
+
# singular, destroys the parent's associated resource. Otherwise, destroys
|
6
|
+
# the resource directly.
|
7
|
+
def default_action_block
|
8
|
+
proc do
|
9
|
+
record.destroy
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/cathode/engine.rb
CHANGED
@@ -1,5 +1,14 @@
|
|
1
|
+
require 'rails'
|
1
2
|
module Cathode
|
3
|
+
# Define a Rails engine with an isolated `Cathode` namespace.
|
2
4
|
class Engine < ::Rails::Engine
|
5
|
+
config.generators do |g|
|
6
|
+
g.test_framework :rspec, fixture: false
|
7
|
+
g.fixture_replacement :factory_girl, dir: 'spec/factories'
|
8
|
+
g.assets false
|
9
|
+
g.helper false
|
10
|
+
end
|
11
|
+
|
3
12
|
isolate_namespace Cathode
|
4
13
|
end
|
5
14
|
end
|
data/lib/cathode/exceptions.rb
CHANGED
@@ -1,3 +1,23 @@
|
|
1
1
|
module Cathode
|
2
|
+
# Raised when a resource is initialized but there is no constant (ActiveRecord
|
3
|
+
# model) that matches it.
|
2
4
|
class UnknownResourceError < NameError; end
|
5
|
+
|
6
|
+
# Raised when a nonexistent action is referred to.
|
7
|
+
class UnknownActionError < NameError; end
|
8
|
+
|
9
|
+
# Raised when an `attributes` block is not passed in a context that requires
|
10
|
+
# one.
|
11
|
+
class UnknownAttributesError < NameError; end
|
12
|
+
|
13
|
+
# Raised when a custom action is defined without an HTTP method.
|
14
|
+
class RequestMethodMissingError < NameError; end
|
15
|
+
|
16
|
+
# Raised when an ActiveModel association is not present in a context that
|
17
|
+
# requires one.
|
18
|
+
class MissingAssociationError < NameError; end
|
19
|
+
|
20
|
+
# Raised when an action's behavior has not been defined in a context that
|
21
|
+
# requires it.
|
22
|
+
class ActionBehaviorMissingError < NameError; end
|
3
23
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Cathode
|
2
|
+
# Defines the default behavior for an index request.
|
3
|
+
class IndexRequest < Request
|
4
|
+
# Determine the model to use depending on the request. If a sub-resource
|
5
|
+
# was requested, use the parent model to get the association. Otherwise, use
|
6
|
+
# all the resource's model's records.
|
7
|
+
def model
|
8
|
+
if @resource_tree.size > 1
|
9
|
+
parent_model_id = params["#{@resource_tree.first.name.to_s.singularize}_id"]
|
10
|
+
model = @resource_tree.first.model.find(parent_model_id)
|
11
|
+
@resource_tree.drop(1).each do |resource|
|
12
|
+
model = model.send resource.name
|
13
|
+
end
|
14
|
+
model
|
15
|
+
else
|
16
|
+
super.all
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Determine the default action to use depending on the request. If the
|
21
|
+
# `page` param was passed and the action allows paging, page the results.
|
22
|
+
# Otherwise, set the request body to all records.
|
23
|
+
def default_action_block
|
24
|
+
proc do
|
25
|
+
all_records = model
|
26
|
+
|
27
|
+
if allowed?(:paging) && params[:page]
|
28
|
+
page = params[:page]
|
29
|
+
per_page = params[:per_page] || 10
|
30
|
+
lower_bound = (per_page - 1) * page
|
31
|
+
upper_bound = lower_bound + per_page - 1
|
32
|
+
|
33
|
+
body all_records[lower_bound..upper_bound]
|
34
|
+
else
|
35
|
+
body all_records
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Cathode
|
2
|
+
# Provides an enumerable interface for arrays of objects.
|
3
|
+
class ObjectCollection
|
4
|
+
attr_accessor :objects
|
5
|
+
|
6
|
+
delegate :each, to: :objects
|
7
|
+
delegate :select, to: :objects
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@objects = []
|
11
|
+
end
|
12
|
+
|
13
|
+
# Look up an object by its `name` property.
|
14
|
+
# @param name [Symbol] The object's name
|
15
|
+
# @return [Object]
|
16
|
+
def find(name)
|
17
|
+
objects.detect { |o| o.name == name }
|
18
|
+
end
|
19
|
+
|
20
|
+
# An array of all the `name` properties in this object.
|
21
|
+
# @return [Array]
|
22
|
+
def names
|
23
|
+
objects.map(&:name)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Adds a new object to the collection.
|
27
|
+
# @param items [Object, Array] A single object or an array of objects to add
|
28
|
+
# @return [ObjectCollection] self
|
29
|
+
def add(items)
|
30
|
+
items = [items] unless items.is_a?(Array)
|
31
|
+
self.objects += items
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
# Delets an object from the collection by name.
|
36
|
+
# @param name [Symbol] The name of the object to remove
|
37
|
+
# @return [ObjectCollection] self
|
38
|
+
def delete(name)
|
39
|
+
objects.delete find(name)
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
# Forwards all missing methods to the `objects` array stored in this
|
44
|
+
# collection.
|
45
|
+
def method_missing(method, args)
|
46
|
+
objects.send method, args
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Cathode
|
2
|
+
# Holds the Cathode Query DSL interface.
|
3
|
+
class Query
|
4
|
+
attr_reader :results
|
5
|
+
|
6
|
+
# Initialize and parse a query.
|
7
|
+
# @param model [Class] A subclass of `ActiveRecord::Base`.
|
8
|
+
# @param query [String] The query to be executed
|
9
|
+
def initialize(model, query)
|
10
|
+
clauses = query.split ','
|
11
|
+
results = model
|
12
|
+
clauses.each do |clause|
|
13
|
+
words = clause.split
|
14
|
+
results = case words.first
|
15
|
+
when 'where'
|
16
|
+
results.where(words.drop(1).join ' ')
|
17
|
+
else
|
18
|
+
results.where(words.join ' ')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
@results = results
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rack/cors'
|
2
|
+
|
3
|
+
module Cathode
|
4
|
+
# Defines a Railtie to hook into the Rails initialization process. Autoloads
|
5
|
+
# API code from the `app/api` directory and adds `Rack::Cors` to the
|
6
|
+
# application's middleware stack.
|
7
|
+
class Railtie < Rails::Railtie
|
8
|
+
initializer 'cathode.add_api_to_autoload_paths' do |app|
|
9
|
+
Dir[File.join(Rails.root, 'app', 'api', '**', '*.rb')].each { |f| require f }
|
10
|
+
end
|
11
|
+
|
12
|
+
initializer 'cathode.enable_cors' do |app|
|
13
|
+
app.config.middleware.use Rack::Cors do
|
14
|
+
allow do
|
15
|
+
origins '*'
|
16
|
+
resource '*', headers: :any, methods: [:get, :post, :put, :delete, :options]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/cathode/request.rb
CHANGED
@@ -1,15 +1,147 @@
|
|
1
1
|
module Cathode
|
2
|
+
# A `Request` object is created when a Rails request is intercepted by
|
3
|
+
# Cathode. This object is responsible for enforcing version headers as well as
|
4
|
+
# token authorization headers. After header enforcement, the object figures
|
5
|
+
# out which version and resource/action to use to process the request.
|
6
|
+
# Finally, the request's `_body` (HTTP response body) and `_status` (HTTP
|
7
|
+
# status code) are set and rendered from the controller that initiated the
|
8
|
+
# request.
|
2
9
|
class Request
|
3
|
-
attr_reader :
|
4
|
-
:
|
10
|
+
attr_reader :_body,
|
11
|
+
:_status,
|
12
|
+
:action,
|
13
|
+
:context,
|
14
|
+
:custom_logic,
|
15
|
+
:model,
|
16
|
+
:resource
|
5
17
|
|
6
|
-
|
7
|
-
|
18
|
+
delegate :allowed?, to: :action
|
19
|
+
delegate :params, to: :context
|
8
20
|
|
9
|
-
|
10
|
-
|
21
|
+
class << self
|
22
|
+
# Creates a request by initializing the appropriate subclass.
|
23
|
+
# @param context [ActionController] The controller responding to the
|
24
|
+
# request
|
25
|
+
# @return [IndexRequest, ShowRequest, CreateRequest, UpdateRequest,
|
26
|
+
# DestroyRequest, CustomRequest] The subclassed request
|
27
|
+
def create(context)
|
28
|
+
klass = case context.params[:action].to_sym
|
29
|
+
when :index
|
30
|
+
IndexRequest
|
31
|
+
when :show
|
32
|
+
ShowRequest
|
33
|
+
when :create
|
34
|
+
CreateRequest
|
35
|
+
when :update
|
36
|
+
UpdateRequest
|
37
|
+
when :destroy
|
38
|
+
DestroyRequest
|
39
|
+
else
|
40
|
+
CustomRequest
|
41
|
+
end
|
42
|
+
klass.new(context)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Initializes the request
|
47
|
+
# @param context [ActionController] The controller responding to the request
|
48
|
+
def initialize(context)
|
49
|
+
@context = context
|
50
|
+
|
51
|
+
version_number = context.request.headers['HTTP_ACCEPT_VERSION']
|
52
|
+
|
53
|
+
if version_number.nil?
|
54
|
+
@_status, @_body = :bad_request, 'A version number must be passed in the Accept-Version header'
|
55
|
+
return self
|
56
|
+
end
|
57
|
+
|
58
|
+
version = Version.find(version_number)
|
59
|
+
unless version.present?
|
60
|
+
@_status, @_body = :bad_request, "Unknown API version: #{version_number}"
|
61
|
+
return self
|
62
|
+
end
|
63
|
+
|
64
|
+
action_name = params[:action]
|
65
|
+
if action_name == 'custom'
|
66
|
+
action_name = context.request.path.split('/').last
|
67
|
+
end
|
68
|
+
|
69
|
+
params[:controller].slice! 'cathode/'
|
70
|
+
resources = params[:controller].split('_').map(&:to_sym)
|
71
|
+
resource = version._resources.find(resources.first)
|
72
|
+
@resource_tree = [resource]
|
73
|
+
subresources = resources.drop(1).collect do |r|
|
74
|
+
resource = resource._resources.find(r)
|
75
|
+
end
|
76
|
+
@resource_tree += subresources
|
77
|
+
resource = @resource_tree.last
|
78
|
+
|
79
|
+
@resource = resource
|
80
|
+
|
81
|
+
if @resource_tree.size > 1
|
82
|
+
@action = resource.actions.find(action_name.to_sym)
|
83
|
+
else
|
84
|
+
unless version.action?(resource.try(:name) || '', action_name)
|
85
|
+
@_status = :not_found
|
86
|
+
return self
|
87
|
+
end
|
88
|
+
@action = version._resources.find(resource.name).actions.find(action_name.to_sym)
|
89
|
+
end
|
90
|
+
|
91
|
+
@strong_params = @action.strong_params
|
92
|
+
@_status = :ok
|
93
|
+
|
94
|
+
if action.override_block
|
95
|
+
context.instance_eval(&action.override_block)
|
96
|
+
else
|
97
|
+
action_block = action.action_block
|
98
|
+
if action_block.nil? && respond_to?(:default_action_block)
|
99
|
+
action_block = default_action_block
|
100
|
+
end
|
101
|
+
|
102
|
+
instance_eval(&action_block)
|
103
|
+
end
|
104
|
+
|
105
|
+
body if @_body.nil?
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def attributes(&block)
|
111
|
+
block.call(params)
|
112
|
+
rescue ActionController::ParameterMissing => error
|
113
|
+
body error.message
|
114
|
+
status :bad_request
|
115
|
+
end
|
116
|
+
|
117
|
+
def model
|
118
|
+
resource.model
|
119
|
+
end
|
120
|
+
|
121
|
+
def parent_resource_id
|
122
|
+
params["#{parent_resource_name}_id"]
|
123
|
+
end
|
124
|
+
|
125
|
+
def parent_resource_name
|
126
|
+
resource.parent.name.to_s.singularize
|
127
|
+
end
|
128
|
+
|
129
|
+
def record
|
130
|
+
if resource.singular
|
131
|
+
parent_model = resource.parent.model.find(parent_resource_id)
|
132
|
+
parent_model.send resource.name
|
133
|
+
else
|
134
|
+
model.find params[:id]
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def body(value = Hash.new, &block)
|
139
|
+
return if _body.present?
|
140
|
+
@_body = block_given? ? block.call : value
|
141
|
+
end
|
11
142
|
|
12
|
-
|
143
|
+
def status(value = nil, &block)
|
144
|
+
@_status = block_given? ? block.call : value
|
13
145
|
end
|
14
146
|
end
|
15
147
|
end
|