rest_framework 0.0.0 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +80 -5
- data/app/views/layouts/rest_framework.html.erb +38 -0
- data/app/views/rest_framework/_head.html.erb +19 -0
- data/app/views/rest_framework/_routes.html.erb +18 -0
- data/app/views/rest_framework/default.html.erb +0 -0
- data/lib/rest_framework.rb +1 -0
- data/lib/rest_framework/VERSION_STAMP +1 -1
- data/lib/rest_framework/controllers/base.rb +73 -18
- data/lib/rest_framework/controllers/models.rb +17 -16
- data/lib/rest_framework/engine.rb +2 -0
- data/lib/rest_framework/routers.rb +14 -9
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3e577365f00c98803ebf7f4548c10e5e0cde9208d47de71ac5d5193d0e0c776
|
4
|
+
data.tar.gz: b0527bc192535edcd85a91587e11df09c30dff460701c826fe6574ec487bc003
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 805f9fe5d4fcd15f5e360459af7246e52392115f3cf6ab3fe51a2128f16c4092e10ac0797767769992416818cdf5dc429790e128ad7a889119cdb134d739ea61
|
7
|
+
data.tar.gz: d0079b9e2cb005938d95aa83cccc3971849183be3209e2c08a2ebae5c357d6c9c571663cd496f68c4a5f8855ea2b804a46fbfcb73d839693b41ce781563599c0
|
data/README.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# REST Framework
|
2
2
|
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/rest_framework.svg)](https://badge.fury.io/rb/rest_framework)
|
4
|
+
[![Build Status](https://travis-ci.org/gregschmit/rails-rest-framework.svg?branch=master)](https://travis-ci.org/gregschmit/rails-rest-framework)
|
5
|
+
|
3
6
|
REST Framework helps you build awesome APIs in Ruby on Rails.
|
4
7
|
|
5
8
|
**The Problem**: Building controllers for APIs usually involves writing a lot of redundant CRUD
|
@@ -24,6 +27,78 @@ Or install it yourself with:
|
|
24
27
|
|
25
28
|
$ gem install rest_framework
|
26
29
|
|
30
|
+
## Usage
|
31
|
+
|
32
|
+
### Controller Mixins
|
33
|
+
|
34
|
+
To transform a controller into a RESTful controller, you can either include `BaseControllerMixin`,
|
35
|
+
`ReadOnlyModelControllerMixin`, or `ModelControllerMixin`. `BaseControllerMixin` provides a `root`
|
36
|
+
action and a simple interface for routing arbitrary additional actions:
|
37
|
+
|
38
|
+
```
|
39
|
+
class ApiController < ApplicationController
|
40
|
+
include RESTFramework::BaseControllerMixin
|
41
|
+
@extra_actions = {test: [:get]}
|
42
|
+
|
43
|
+
def test
|
44
|
+
render inline: "Test successful!"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
`ModelControllerMixin` assists with providing the standard CRUD for your controller.
|
50
|
+
|
51
|
+
```
|
52
|
+
class Api::MoviesController < ApiController
|
53
|
+
include RESTFramework::ModelControllerMixin
|
54
|
+
|
55
|
+
@recordset = Movie.where(enabled: true) # by default, @recordset would include all movies
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
`ReadOnlyModelControllerMixin` only enables list/show actions, but since we're naming this
|
60
|
+
controller in a way that doesn't make the model obvious, we can set that explicitly:
|
61
|
+
|
62
|
+
```
|
63
|
+
class Api::ReadOnlyMoviesController < ApiController
|
64
|
+
include RESTFramework::ReadOnlyModelControllerMixin
|
65
|
+
|
66
|
+
@model = Movie
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
Note that you can also override `get_model` and `get_recordset` instance methods to override the API
|
71
|
+
behavior dynamically per-request.
|
72
|
+
|
73
|
+
### Routing
|
74
|
+
|
75
|
+
You can use Rails' `resource`/`resources` routers to route your API, however if you want
|
76
|
+
`@extra_actions` / `@extra_member_actions` to be routed automatically, then you can use the
|
77
|
+
`rest_resource` / `rest_resources` routers provided by this gem. You can also use `rest_root` to route
|
78
|
+
the root of your API:
|
79
|
+
|
80
|
+
```
|
81
|
+
Rails.application.routes.draw do
|
82
|
+
rest_root :api # will find `api_controller` and route the `root` action to '/api'
|
83
|
+
namespace :api do
|
84
|
+
rest_resources :movies
|
85
|
+
rest_resources :read_only_movies
|
86
|
+
end
|
87
|
+
end
|
88
|
+
```
|
89
|
+
|
90
|
+
Or if you want the API root to be routed to `Api::RootController#root`:
|
91
|
+
|
92
|
+
```
|
93
|
+
Rails.application.routes.draw do
|
94
|
+
namespace :api do
|
95
|
+
rest_root # will route `Api::RootController#root` to '/' in this namespace ('/api')
|
96
|
+
rest_resources :movies
|
97
|
+
rest_resources :read_only_movies
|
98
|
+
end
|
99
|
+
end
|
100
|
+
```
|
101
|
+
|
27
102
|
## Development/Testing
|
28
103
|
|
29
104
|
After you clone the repository, cd'ing into the directory should create a new gemset if you are
|
@@ -37,10 +112,10 @@ To run unit tests:
|
|
37
112
|
|
38
113
|
$ rake test:unit
|
39
114
|
|
40
|
-
To run integration tests
|
41
|
-
|
42
|
-
$ rake test:app
|
115
|
+
To run integration tests:
|
43
116
|
|
44
|
-
|
117
|
+
$ rake test:integration
|
45
118
|
|
46
|
-
|
119
|
+
To interact with the integration app, you can `cd test/integration` and operate it via the normal
|
120
|
+
Rails interfaces. Ensure you run `rake db:schema:load` before running `rails server` or
|
121
|
+
`rails console`.
|
@@ -0,0 +1,38 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title><%= @title %></title>
|
5
|
+
<%= render partial: 'rest_framework/head' %>
|
6
|
+
</head>
|
7
|
+
|
8
|
+
<body>
|
9
|
+
<div class="bg-dark">
|
10
|
+
<div class="w-100 m-0 p-0" style="height: .3em; background-color: #a00;"></div>
|
11
|
+
<nav class="navbar navbar-dark bg-dark">
|
12
|
+
<div class="container">
|
13
|
+
<span class="navbar-brand p-0">
|
14
|
+
<h1 class="text-light font-weight-light m-0 p-0" style="font-size: 1em"><%= @template_logo_text %></h1>
|
15
|
+
</span>
|
16
|
+
</div>
|
17
|
+
</nav>
|
18
|
+
</div>
|
19
|
+
<div class="container py-3">
|
20
|
+
<div class="container">
|
21
|
+
<div class="row">
|
22
|
+
<h1><%= @title %></h1>
|
23
|
+
</div>
|
24
|
+
<hr/>
|
25
|
+
<div class="row">
|
26
|
+
<h2>Payload</h2><br>
|
27
|
+
<div><pre><%= JSON.pretty_generate(JSON.parse(@serialized_payload)) %></pre></div>
|
28
|
+
</div>
|
29
|
+
<% unless @routes.blank? %>
|
30
|
+
<div class="row">
|
31
|
+
<h2>Routes</h2>
|
32
|
+
<%= render partial: 'rest_framework/routes' %>
|
33
|
+
</div>
|
34
|
+
<% end %>
|
35
|
+
</div>
|
36
|
+
</div>
|
37
|
+
</body>
|
38
|
+
</html>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<meta charset="utf-8">
|
2
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
3
|
+
<%= csrf_meta_tags %>
|
4
|
+
<%= csp_meta_tag rescue nil %>
|
5
|
+
|
6
|
+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
|
7
|
+
<style>
|
8
|
+
h1,h2,h3,h4,h5,h6 { width: 100%; }
|
9
|
+
h1 { font-size: 2rem; }
|
10
|
+
h2 { font-size: 1.7rem; }
|
11
|
+
h3 { font-size: 1.5rem; }
|
12
|
+
h4 { font-size: 1.3rem; }
|
13
|
+
h5 { font-size: 1.1rem; }
|
14
|
+
h6 { font-size: 1rem; }
|
15
|
+
</style>
|
16
|
+
|
17
|
+
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
|
18
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
|
19
|
+
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<table class="table">
|
2
|
+
<thead>
|
3
|
+
<tr>
|
4
|
+
<th scope="col">Verb</th>
|
5
|
+
<th scope="col">Path</th>
|
6
|
+
<th scope="col">Action</th>
|
7
|
+
</tr>
|
8
|
+
</thead>
|
9
|
+
<tbody>
|
10
|
+
<% @routes.each do |r| %>
|
11
|
+
<tr>
|
12
|
+
<td><%= r[:verb] %></td>
|
13
|
+
<td><%= r[:path] %></td>
|
14
|
+
<td><%= r[:action] %></td>
|
15
|
+
</tr>
|
16
|
+
<% end %>
|
17
|
+
</tbody>
|
18
|
+
</table>
|
File without changes
|
data/lib/rest_framework.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.6
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module RESTFramework
|
2
2
|
|
3
|
+
# This module provides helpers for mixin `ClassMethods` submodules.
|
3
4
|
module ClassMethodHelpers
|
5
|
+
# This helper assists in providing reader interfaces for mixin properties.
|
4
6
|
def _restframework_attr_reader(property, default: nil)
|
5
7
|
method = <<~RUBY
|
6
8
|
def #{property}
|
@@ -14,11 +16,13 @@ module RESTFramework
|
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
19
|
+
# This module provides the common functionality for any controller mixins, a `root` action, and
|
20
|
+
# the ability to route arbitrary actions with `@extra_actions`. This is also where `api_response`
|
21
|
+
# is defined.
|
17
22
|
module BaseControllerMixin
|
18
23
|
# Default action for API root.
|
19
|
-
# TODO: use api_response and show sub-routes.
|
20
24
|
def root
|
21
|
-
|
25
|
+
api_response({message: "This is the root of your awesome API!"})
|
22
26
|
end
|
23
27
|
|
24
28
|
protected
|
@@ -26,39 +30,90 @@ module RESTFramework
|
|
26
30
|
module ClassMethods
|
27
31
|
extend ClassMethodHelpers
|
28
32
|
|
29
|
-
# Interface for getting class-level instance/class variables.
|
33
|
+
# Interface for getting class-level instance/class variables. Note: we check if they are
|
34
|
+
# defined first rather than rescuing NameError to prevent uninitialized variable warnings.
|
30
35
|
private def _restframework_try_class_level_variable_get(name, default: nil)
|
31
|
-
|
32
|
-
|
36
|
+
instance_variable = "@#{name}"
|
37
|
+
if self.instance_variable_defined?(instance_variable)
|
38
|
+
v = self.instance_variable_get(instance_variable)
|
33
39
|
return v unless v.nil?
|
34
|
-
rescue NameError
|
35
40
|
end
|
36
|
-
|
37
|
-
|
41
|
+
class_variable = "@@#{name}"
|
42
|
+
if self.class_variable_defined?(class_variable)
|
43
|
+
v = self.class_variable_get(class_variable)
|
38
44
|
return v unless v.nil?
|
39
|
-
rescue NameError
|
40
45
|
end
|
41
46
|
return default
|
42
47
|
end
|
43
48
|
|
44
|
-
|
45
|
-
# private def _restframework_register_exception_handlers
|
46
|
-
# rescue_from
|
47
|
-
# end
|
48
|
-
|
49
|
+
_restframework_attr_reader(:singleton_controller)
|
49
50
|
_restframework_attr_reader(:extra_actions, default: {})
|
50
|
-
_restframework_attr_reader(:
|
51
|
+
_restframework_attr_reader(:template_logo_text, default: 'Rails REST Framework')
|
52
|
+
|
53
|
+
def skip_actions(skip_undefined: true)
|
54
|
+
# first, skip explicitly skipped actions
|
55
|
+
skip = _restframework_try_class_level_variable_get(:skip_actions, default: [])
|
56
|
+
|
57
|
+
# now add methods which don't exist, since we don't want to route those
|
58
|
+
if skip_undefined
|
59
|
+
[:index, :new, :create, :show, :edit, :update, :destroy].each do |a|
|
60
|
+
skip << a unless self.method_defined?(a)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
return skip
|
65
|
+
end
|
51
66
|
end
|
52
67
|
|
53
68
|
def self.included(base)
|
54
69
|
base.extend ClassMethods
|
55
70
|
end
|
56
71
|
|
72
|
+
def _get_routes
|
73
|
+
begin
|
74
|
+
formatter = ActionDispatch::Routing::ConsoleFormatter::Sheet
|
75
|
+
rescue NameError
|
76
|
+
formatter = ActionDispatch::Routing::ConsoleFormatter
|
77
|
+
end
|
78
|
+
return ActionDispatch::Routing::RoutesInspector.new(Rails.application.routes.routes).format(
|
79
|
+
formatter.new
|
80
|
+
).lines[1..].map { |r| r.split.last(3) }.map { |r|
|
81
|
+
{verb: r[0], path: r[1], action: r[2]}
|
82
|
+
}.select { |r| r[:path].start_with?(request.path) }
|
83
|
+
end
|
84
|
+
|
57
85
|
# Helper alias for `respond_to`/`render`, and replace nil responses with blank ones.
|
58
|
-
def api_response(
|
86
|
+
def api_response(payload, html_kwargs: nil, json_kwargs: nil, **kwargs)
|
87
|
+
payload ||= '' # replace nil with ''
|
88
|
+
html_kwargs ||= {}
|
89
|
+
json_kwargs ||= {}
|
90
|
+
|
91
|
+
# serialize
|
92
|
+
if self.respond_to?(:get_model_serializer_config, true)
|
93
|
+
serialized_payload = payload.to_json(self.get_model_serializer_config)
|
94
|
+
else
|
95
|
+
serialized_payload = payload.to_json
|
96
|
+
end
|
97
|
+
|
59
98
|
respond_to do |format|
|
60
|
-
format.html
|
61
|
-
|
99
|
+
format.html {
|
100
|
+
kwargs = kwargs.merge(html_kwargs)
|
101
|
+
@template_logo_text ||= self.class.template_logo_text
|
102
|
+
@title ||= self.controller_name.camelize
|
103
|
+
@routes ||= self._get_routes
|
104
|
+
@payload = payload
|
105
|
+
@serialized_payload = serialized_payload
|
106
|
+
begin
|
107
|
+
render(**kwargs)
|
108
|
+
rescue ActionView::MissingTemplate # fallback to rest_framework default view
|
109
|
+
kwargs[:template] = "rest_framework/default"
|
110
|
+
end
|
111
|
+
render(**kwargs)
|
112
|
+
}
|
113
|
+
format.json {
|
114
|
+
kwargs = kwargs.merge(json_kwargs)
|
115
|
+
render(json: serialized_payload || '', **kwargs)
|
116
|
+
}
|
62
117
|
end
|
63
118
|
end
|
64
119
|
end
|
@@ -21,7 +21,6 @@ module RESTFramework
|
|
21
21
|
|
22
22
|
_restframework_attr_reader(:model)
|
23
23
|
_restframework_attr_reader(:recordset)
|
24
|
-
_restframework_attr_reader(:singleton_controller)
|
25
24
|
|
26
25
|
_restframework_attr_reader(:fields)
|
27
26
|
_restframework_attr_reader(:list_fields)
|
@@ -32,11 +31,14 @@ module RESTFramework
|
|
32
31
|
_restframework_attr_reader(:extra_member_actions, default: {})
|
33
32
|
|
34
33
|
# For model-based mixins, `@extra_collection_actions` is synonymous with `@extra_actions`.
|
35
|
-
|
36
|
-
|
34
|
+
# @param skip_undefined [Boolean] whether we should skip routing undefined actions
|
35
|
+
def extra_actions(skip_undefined: true)
|
36
|
+
actions = (
|
37
37
|
_restframework_try_class_level_variable_get(:extra_collection_actions) ||
|
38
38
|
_restframework_try_class_level_variable_get(:extra_actions, default: {})
|
39
39
|
)
|
40
|
+
actions = actions.select { |a| self.method_defined?(a) } if skip_undefined
|
41
|
+
return actions
|
40
42
|
end
|
41
43
|
end
|
42
44
|
|
@@ -48,7 +50,7 @@ module RESTFramework
|
|
48
50
|
|
49
51
|
# Get a list of fields for the current action.
|
50
52
|
def get_fields
|
51
|
-
return @fields if @fields
|
53
|
+
return @fields if instance_variable_defined?(:@fields) && @fields
|
52
54
|
|
53
55
|
# index action should use list_fields
|
54
56
|
name = (action_name == 'index') ? 'list' : action_name
|
@@ -57,9 +59,8 @@ module RESTFramework
|
|
57
59
|
@fields = self.class.send("#{name}_fields")
|
58
60
|
rescue NameError
|
59
61
|
end
|
60
|
-
@fields ||= self.class.fields || []
|
61
62
|
|
62
|
-
return @fields
|
63
|
+
return @fields ||= self.class.fields || []
|
63
64
|
end
|
64
65
|
|
65
66
|
# Get a configuration passable to `as_json` for the model.
|
@@ -109,10 +110,10 @@ module RESTFramework
|
|
109
110
|
end
|
110
111
|
|
111
112
|
# Internal interface for get_model, protecting against infinite recursion with get_recordset.
|
112
|
-
|
113
|
-
return @model if @model
|
113
|
+
def _get_model(from_internal_get_recordset: false)
|
114
|
+
return @model if instance_variable_defined?(:@model) && @model
|
114
115
|
return self.class.model if self.class.model
|
115
|
-
unless
|
116
|
+
unless from_internal_get_recordset # prevent infinite recursion
|
116
117
|
recordset = self._get_recordset(from_internal_get_model: true)
|
117
118
|
return (@model = recordset.klass) if recordset
|
118
119
|
end
|
@@ -124,10 +125,10 @@ module RESTFramework
|
|
124
125
|
end
|
125
126
|
|
126
127
|
# Internal interface for get_recordset, protecting against infinite recursion with get_model.
|
127
|
-
|
128
|
-
return @recordset if @recordset
|
128
|
+
def _get_recordset(from_internal_get_model: false)
|
129
|
+
return @recordset if instance_variable_defined?(:@recordset) && @recordset
|
129
130
|
return self.class.recordset if self.class.recordset
|
130
|
-
unless
|
131
|
+
unless from_internal_get_model # prevent infinite recursion
|
131
132
|
model = self._get_model(from_internal_get_recordset: true)
|
132
133
|
return (@recordset = model.all) if model
|
133
134
|
end
|
@@ -149,14 +150,14 @@ module RESTFramework
|
|
149
150
|
# TODO: pagination classes like Django
|
150
151
|
def index
|
151
152
|
@records = self.get_filtered_recordset
|
152
|
-
api_response(@records
|
153
|
+
api_response(@records)
|
153
154
|
end
|
154
155
|
end
|
155
156
|
|
156
157
|
module ShowModelMixin
|
157
158
|
def show
|
158
159
|
@record = self.get_record
|
159
|
-
api_response(@record
|
160
|
+
api_response(@record)
|
160
161
|
end
|
161
162
|
end
|
162
163
|
|
@@ -167,7 +168,7 @@ module RESTFramework
|
|
167
168
|
rescue ActiveRecord::RecordInvalid => e
|
168
169
|
api_response(e.record.messages, status: 400)
|
169
170
|
end
|
170
|
-
api_response(@record
|
171
|
+
api_response(@record)
|
171
172
|
end
|
172
173
|
end
|
173
174
|
|
@@ -177,7 +178,7 @@ module RESTFramework
|
|
177
178
|
if @record
|
178
179
|
@record.attributes(self.get_update_params)
|
179
180
|
@record.save!
|
180
|
-
api_response(@record
|
181
|
+
api_response(@record)
|
181
182
|
else
|
182
183
|
api_response({detail: "Record not found."}, status: 404)
|
183
184
|
end
|
@@ -25,10 +25,10 @@ module ActionDispatch::Routing
|
|
25
25
|
|
26
26
|
# convert class name to class
|
27
27
|
begin
|
28
|
-
controller = mod.const_get(name
|
28
|
+
controller = mod.const_get(name)
|
29
29
|
rescue NameError
|
30
30
|
if fallback_reverse_pluralization
|
31
|
-
controller = mod.const_get(name_reverse
|
31
|
+
controller = mod.const_get(name_reverse)
|
32
32
|
else
|
33
33
|
raise
|
34
34
|
end
|
@@ -41,7 +41,8 @@ module ActionDispatch::Routing
|
|
41
41
|
# @param default_singular [Boolean] the default plurality of the resource if the plurality is
|
42
42
|
# not otherwise defined by the controller
|
43
43
|
# @param name [Symbol] the resource name, from which path and controller are deduced by default
|
44
|
-
|
44
|
+
# @param skip_undefined [Boolean] whether we should skip routing undefined actions
|
45
|
+
protected def _rest_resources(default_singular, name, skip_undefined: true, **kwargs, &block)
|
45
46
|
controller = kwargs[:controller] || name
|
46
47
|
if controller.is_a?(Class)
|
47
48
|
controller_class = controller
|
@@ -50,9 +51,7 @@ module ActionDispatch::Routing
|
|
50
51
|
end
|
51
52
|
|
52
53
|
# set controller if it's not explicitly set
|
53
|
-
unless kwargs[:controller]
|
54
|
-
kwargs[:controller] = name
|
55
|
-
end
|
54
|
+
kwargs[:controller] = name unless kwargs[:controller]
|
56
55
|
|
57
56
|
# determine plural/singular resource
|
58
57
|
if kwargs.delete(:force_singular)
|
@@ -67,10 +66,14 @@ module ActionDispatch::Routing
|
|
67
66
|
resource_method = singular ? :resource : :resources
|
68
67
|
|
69
68
|
# call either `resource` or `resources`, passing appropriate modifiers
|
70
|
-
|
69
|
+
skip_undefined = kwargs.delete(:skip_undefined) || true
|
70
|
+
skip = controller_class.skip_actions(skip_undefined: skip_undefined)
|
71
|
+
public_send(resource_method, name, except: skip, **kwargs) do
|
71
72
|
if controller_class.respond_to?(:extra_member_actions)
|
72
73
|
member do
|
73
|
-
controller_class.extra_member_actions
|
74
|
+
actions = controller_class.extra_member_actions
|
75
|
+
actions = actions.select { |k,v| controller_class.method_defined?(k) } if skip_undefined
|
76
|
+
actions.each do |action, methods|
|
74
77
|
methods = [methods] if methods.is_a?(Symbol) || methods.is_a?(String)
|
75
78
|
methods.each do |m|
|
76
79
|
public_send(m, action)
|
@@ -80,7 +83,9 @@ module ActionDispatch::Routing
|
|
80
83
|
end
|
81
84
|
|
82
85
|
collection do
|
83
|
-
controller_class.extra_actions
|
86
|
+
actions = controller_class.extra_actions
|
87
|
+
actions = actions.select { |k,v| controller_class.method_defined?(k) } if skip_undefined
|
88
|
+
actions.each do |action, methods|
|
84
89
|
methods = [methods] if methods.is_a?(Symbol) || methods.is_a?(String)
|
85
90
|
methods.each do |m|
|
86
91
|
public_send(m, action)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rest_framework
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gregory N. Schmit
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-09-
|
11
|
+
date: 2020-09-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -33,11 +33,16 @@ extra_rdoc_files: []
|
|
33
33
|
files:
|
34
34
|
- LICENSE
|
35
35
|
- README.md
|
36
|
+
- app/views/layouts/rest_framework.html.erb
|
37
|
+
- app/views/rest_framework/_head.html.erb
|
38
|
+
- app/views/rest_framework/_routes.html.erb
|
39
|
+
- app/views/rest_framework/default.html.erb
|
36
40
|
- lib/rest_framework.rb
|
37
41
|
- lib/rest_framework/VERSION_STAMP
|
38
42
|
- lib/rest_framework/controllers.rb
|
39
43
|
- lib/rest_framework/controllers/base.rb
|
40
44
|
- lib/rest_framework/controllers/models.rb
|
45
|
+
- lib/rest_framework/engine.rb
|
41
46
|
- lib/rest_framework/routers.rb
|
42
47
|
- lib/rest_framework/version.rb
|
43
48
|
homepage: https://github.com/gregschmit/rails-rest-framework
|
@@ -50,6 +55,7 @@ post_install_message:
|
|
50
55
|
rdoc_options: []
|
51
56
|
require_paths:
|
52
57
|
- lib
|
58
|
+
- app
|
53
59
|
required_ruby_version: !ruby/object:Gem::Requirement
|
54
60
|
requirements:
|
55
61
|
- - ">="
|