rest_framework 0.0.0 → 0.0.1
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 +77 -5
- data/lib/rest_framework/VERSION_STAMP +1 -1
- data/lib/rest_framework/controllers/base.rb +20 -1
- data/lib/rest_framework/controllers/models.rb +9 -6
- data/lib/rest_framework/routers.rb +12 -7
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ae8173bd8209fe5250ec9fe3a749d41d46307f7948d1c7597921ab5b4d2cae2
|
4
|
+
data.tar.gz: 3ca1aa88dbde70ad327a65778bfa7e2a90499111dbf6e16a9d084aba2df42328
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bd56096773684dc55324b6a8d3fed2a7d214201fd9ac516862a24a111ecedcf70cebf20b61ff264f11a921d67e9a057da4a8b1c2d4c498d07bb042401993e5e3
|
7
|
+
data.tar.gz: 2f9f5b01fa4a956f0047307ed0f9b7523607ad6487fa5f8fa15d9895b7ff814703c048e5a7a0261fad35f11b96f3078641e8b9dd9e897bdbbc15b36c775c084e
|
data/README.md
CHANGED
@@ -24,6 +24,78 @@ Or install it yourself with:
|
|
24
24
|
|
25
25
|
$ gem install rest_framework
|
26
26
|
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
### Controller Mixins
|
30
|
+
|
31
|
+
To transform a controller into a RESTful controller, you can either include `BaseControllerMixin`,
|
32
|
+
`ReadOnlyModelControllerMixin`, or `ModelControllerMixin`. `BaseControllerMixin` provides a `root`
|
33
|
+
action and a simple interface for routing arbitrary additional actions:
|
34
|
+
|
35
|
+
```
|
36
|
+
class ApiController < ApplicationController
|
37
|
+
include RESTFramework::BaseControllerMixin
|
38
|
+
@extra_actions = {test: [:get]}
|
39
|
+
|
40
|
+
def test
|
41
|
+
render inline: "Test successful!"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
`ModelControllerMixin` assists with providing the standard CRUD for your controller.
|
47
|
+
|
48
|
+
```
|
49
|
+
class Api::MoviesController < ApiController
|
50
|
+
include RESTFramework::ModelControllerMixin
|
51
|
+
|
52
|
+
@recordset = Movie.where(enabled: true) # by default, @recordset would include all movies
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
`ReadOnlyModelControllerMixin` only enables list/show actions, but since we're naming this
|
57
|
+
controller in a way that doesn't make the model obvious, we can set that explicitly:
|
58
|
+
|
59
|
+
```
|
60
|
+
class Api::ReadOnlyMoviesController < ApiController
|
61
|
+
include RESTFramework::ReadOnlyModelControllerMixin
|
62
|
+
|
63
|
+
@model = Movie
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
Note that you can also override `get_model` and `get_recordset` instance methods to override the API
|
68
|
+
behavior based on each request.
|
69
|
+
|
70
|
+
### Routing
|
71
|
+
|
72
|
+
You can use Rails' `resource`/`resources` routers to route your API, however if you want
|
73
|
+
`@extra_actions`/`@extra_member_actions` to be routed automatically, then you can use the
|
74
|
+
`rest_resource`/`rest_resources` routers provided by this gem. You can also use `rest_root` to route
|
75
|
+
the root of your API:
|
76
|
+
|
77
|
+
```
|
78
|
+
Rails.application.routes.draw do
|
79
|
+
rest_root :api # will find `api_controller` and route the `root` action to '/api'
|
80
|
+
namespace :api do
|
81
|
+
rest_resources :movies
|
82
|
+
rest_resources :read_only_movies
|
83
|
+
end
|
84
|
+
end
|
85
|
+
```
|
86
|
+
|
87
|
+
Or if you want the API root to be routed to `Api::RootController#root`:
|
88
|
+
|
89
|
+
```
|
90
|
+
Rails.application.routes.draw do
|
91
|
+
namespace :api do
|
92
|
+
rest_root # will route `Api::RootController#root` to '/' in this namespace ('/api')
|
93
|
+
rest_resources :movies
|
94
|
+
rest_resources :read_only_movies
|
95
|
+
end
|
96
|
+
end
|
97
|
+
```
|
98
|
+
|
27
99
|
## Development/Testing
|
28
100
|
|
29
101
|
After you clone the repository, cd'ing into the directory should create a new gemset if you are
|
@@ -37,10 +109,10 @@ To run unit tests:
|
|
37
109
|
|
38
110
|
$ rake test:unit
|
39
111
|
|
40
|
-
To run integration tests
|
41
|
-
|
42
|
-
$ rake test:app
|
112
|
+
To run integration tests:
|
43
113
|
|
44
|
-
|
114
|
+
$ rake test:integration
|
45
115
|
|
46
|
-
|
116
|
+
To interact with the integration app, you can `cd test/integration` and operate it via the normal
|
117
|
+
Rails interfaces. Ensure you run `rake db:schema:load` before running `rails server` or
|
118
|
+
`rails console`.
|
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.1
|
@@ -1,6 +1,9 @@
|
|
1
1
|
module RESTFramework
|
2
2
|
|
3
|
+
# This module provides helpers for mixin `ClassMethods` submodules.
|
3
4
|
module ClassMethodHelpers
|
5
|
+
|
6
|
+
# This helper assists in providing reader interfaces for mixin properties.
|
4
7
|
def _restframework_attr_reader(property, default: nil)
|
5
8
|
method = <<~RUBY
|
6
9
|
def #{property}
|
@@ -14,6 +17,9 @@ module RESTFramework
|
|
14
17
|
end
|
15
18
|
end
|
16
19
|
|
20
|
+
# This module provides the common functionality for any controller mixins, a `root` action, and
|
21
|
+
# the ability to route arbitrary actions with `@extra_actions`. This is also where `api_response`
|
22
|
+
# is defined.
|
17
23
|
module BaseControllerMixin
|
18
24
|
# Default action for API root.
|
19
25
|
# TODO: use api_response and show sub-routes.
|
@@ -47,7 +53,20 @@ module RESTFramework
|
|
47
53
|
# end
|
48
54
|
|
49
55
|
_restframework_attr_reader(:extra_actions, default: {})
|
50
|
-
|
56
|
+
|
57
|
+
def skip_actions(skip_undefined: true)
|
58
|
+
# first, skip explicitly skipped actions
|
59
|
+
skip = _restframework_try_class_level_variable_get(:skip_actions, default: [])
|
60
|
+
|
61
|
+
# now add methods which don't exist, since we don't want to route those
|
62
|
+
if skip_undefined
|
63
|
+
[:index, :new, :create, :show, :edit, :update, :destroy].each do |a|
|
64
|
+
skip << a unless self.method_defined?(a)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
return skip
|
69
|
+
end
|
51
70
|
end
|
52
71
|
|
53
72
|
def self.included(base)
|
@@ -32,11 +32,14 @@ module RESTFramework
|
|
32
32
|
_restframework_attr_reader(:extra_member_actions, default: {})
|
33
33
|
|
34
34
|
# For model-based mixins, `@extra_collection_actions` is synonymous with `@extra_actions`.
|
35
|
-
|
36
|
-
|
35
|
+
# @param skip_undefined [Boolean] whether we should skip routing undefined actions
|
36
|
+
def extra_actions(skip_undefined: true)
|
37
|
+
actions = (
|
37
38
|
_restframework_try_class_level_variable_get(:extra_collection_actions) ||
|
38
39
|
_restframework_try_class_level_variable_get(:extra_actions, default: {})
|
39
40
|
)
|
41
|
+
actions = actions.select { |a| self.method_defined?(a) } if skip_undefined
|
42
|
+
return actions
|
40
43
|
end
|
41
44
|
end
|
42
45
|
|
@@ -109,10 +112,10 @@ module RESTFramework
|
|
109
112
|
end
|
110
113
|
|
111
114
|
# Internal interface for get_model, protecting against infinite recursion with get_recordset.
|
112
|
-
|
115
|
+
def _get_model(from_internal_get_recordset: false)
|
113
116
|
return @model if @model
|
114
117
|
return self.class.model if self.class.model
|
115
|
-
unless
|
118
|
+
unless from_internal_get_recordset # prevent infinite recursion
|
116
119
|
recordset = self._get_recordset(from_internal_get_model: true)
|
117
120
|
return (@model = recordset.klass) if recordset
|
118
121
|
end
|
@@ -124,10 +127,10 @@ module RESTFramework
|
|
124
127
|
end
|
125
128
|
|
126
129
|
# Internal interface for get_recordset, protecting against infinite recursion with get_model.
|
127
|
-
|
130
|
+
def _get_recordset(from_internal_get_model: false)
|
128
131
|
return @recordset if @recordset
|
129
132
|
return self.class.recordset if self.class.recordset
|
130
|
-
unless
|
133
|
+
unless from_internal_get_model # prevent infinite recursion
|
131
134
|
model = self._get_model(from_internal_get_recordset: true)
|
132
135
|
return (@recordset = model.all) if model
|
133
136
|
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.1
|
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-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|