cathode 0.0.1 → 0.1.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 +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
|