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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c71a3fea7904490cd834aff65231d4a18804e8cd487a07314e475a73d9a6500e
4
- data.tar.gz: bde5421c92b4c960bc9b09b369a36b2d4df463b0722490c35cbaf00cdb6b6a66
3
+ metadata.gz: 0ae8173bd8209fe5250ec9fe3a749d41d46307f7948d1c7597921ab5b4d2cae2
4
+ data.tar.gz: 3ca1aa88dbde70ad327a65778bfa7e2a90499111dbf6e16a9d084aba2df42328
5
5
  SHA512:
6
- metadata.gz: 21db95368e8223a1b086e63b9724eb7cb35cb5eb42db9eedb5eca557662be24363f225ab69fb98d2996152c69d58218a5156fdaba1345330660c974313b3f912
7
- data.tar.gz: 2f32d14d77eb8f60b29019afd81f6b926e2bd97268b2cd6d4828698adf4b66debea2959f00403913a6e74a970f858d960a4d3c9268a1f6fcccd9549c1aca992e
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 on a sample app:
41
-
42
- $ rake test:app
112
+ To run integration tests:
43
113
 
44
- To run that sample app live:
114
+ $ rake test:integration
45
115
 
46
- $ rake test:app:run
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.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
- _restframework_attr_reader(:skip_actions, default: [])
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
- def extra_actions
36
- return (
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
- private def _get_model(from_internal_get_recordset: false)
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 from_get_recordset # prevent infinite recursion
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
- private def _get_recordset(from_internal_get_model: false)
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 from_get_model # prevent infinite recursion
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
- protected def _rest_resources(default_singular, name, **kwargs, &block)
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
- public_send(resource_method, name, except: controller_class.skip_actions, **kwargs) do
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.each do |action, methods|
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.each do |action, methods|
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.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-16 00:00:00.000000000 Z
11
+ date: 2020-09-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails