rest_framework 0.0.1 → 0.0.7

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: 0ae8173bd8209fe5250ec9fe3a749d41d46307f7948d1c7597921ab5b4d2cae2
4
- data.tar.gz: 3ca1aa88dbde70ad327a65778bfa7e2a90499111dbf6e16a9d084aba2df42328
3
+ metadata.gz: 5a70372b29a6a1f71c62ecfe54f87836c185986e0786be23c73d8da52c73e3d3
4
+ data.tar.gz: fa48c80ce2d183f31c3e9791a454462fd157f803bd3f77f620ebad13bb9b86a2
5
5
  SHA512:
6
- metadata.gz: bd56096773684dc55324b6a8d3fed2a7d214201fd9ac516862a24a111ecedcf70cebf20b61ff264f11a921d67e9a057da4a8b1c2d4c498d07bb042401993e5e3
7
- data.tar.gz: 2f9f5b01fa4a956f0047307ed0f9b7523607ad6487fa5f8fa15d9895b7ff814703c048e5a7a0261fad35f11b96f3078641e8b9dd9e897bdbbc15b36c775c084e
6
+ metadata.gz: 4c7819ea220f5a41890320d5f7c4667d9989d9a23a46c6d9e53b4c8fe558f187cbfa094616ade0013b2ed8a28732024403d416449589167d759eaa8f0df432b0
7
+ data.tar.gz: cf92ba584de58531b31ed33a1855270324559a4484c29a4ea953e4b2093080db3923e1ded3be6fedac867985febf11dbdfaf07600879b02ff6a17f5b87cef473
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
@@ -65,13 +68,13 @@ end
65
68
  ```
66
69
 
67
70
  Note that you can also override `get_model` and `get_recordset` instance methods to override the API
68
- behavior based on each request.
71
+ behavior dynamically per-request.
69
72
 
70
73
  ### Routing
71
74
 
72
75
  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
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
75
78
  the root of your API:
76
79
 
77
80
  ```
@@ -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)) unless @serialized_payload.blank? %></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>
@@ -2,5 +2,6 @@ module RESTFramework
2
2
  end
3
3
 
4
4
  require_relative "rest_framework/controllers"
5
+ require_relative "rest_framework/engine"
5
6
  require_relative "rest_framework/routers"
6
7
  require_relative "rest_framework/version"
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.0.7
@@ -2,7 +2,6 @@ module RESTFramework
2
2
 
3
3
  # This module provides helpers for mixin `ClassMethods` submodules.
4
4
  module ClassMethodHelpers
5
-
6
5
  # This helper assists in providing reader interfaces for mixin properties.
7
6
  def _restframework_attr_reader(property, default: nil)
8
7
  method = <<~RUBY
@@ -22,9 +21,8 @@ module RESTFramework
22
21
  # is defined.
23
22
  module BaseControllerMixin
24
23
  # Default action for API root.
25
- # TODO: use api_response and show sub-routes.
26
24
  def root
27
- render inline: "This is the root of your awesome API!"
25
+ api_response({message: "This is the root of your awesome API!"})
28
26
  end
29
27
 
30
28
  protected
@@ -32,27 +30,25 @@ module RESTFramework
32
30
  module ClassMethods
33
31
  extend ClassMethodHelpers
34
32
 
35
- # 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.
36
35
  private def _restframework_try_class_level_variable_get(name, default: nil)
37
- begin
38
- v = instance_variable_get("@#{name}")
36
+ instance_variable = "@#{name}"
37
+ if self.instance_variable_defined?(instance_variable)
38
+ v = self.instance_variable_get(instance_variable)
39
39
  return v unless v.nil?
40
- rescue NameError
41
40
  end
42
- begin
43
- v = class_variable_get("@@#{name}")
41
+ class_variable = "@@#{name}"
42
+ if self.class_variable_defined?(class_variable)
43
+ v = self.class_variable_get(class_variable)
44
44
  return v unless v.nil?
45
- rescue NameError
46
45
  end
47
46
  return default
48
47
  end
49
48
 
50
- # Interface for registering exceptions handlers.
51
- # private def _restframework_register_exception_handlers
52
- # rescue_from
53
- # end
54
-
49
+ _restframework_attr_reader(:singleton_controller)
55
50
  _restframework_attr_reader(:extra_actions, default: {})
51
+ _restframework_attr_reader(:template_logo_text, default: 'Rails REST Framework')
56
52
 
57
53
  def skip_actions(skip_undefined: true)
58
54
  # first, skip explicitly skipped actions
@@ -73,11 +69,54 @@ module RESTFramework
73
69
  base.extend ClassMethods
74
70
  end
75
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
+
76
85
  # Helper alias for `respond_to`/`render`, and replace nil responses with blank ones.
77
- def api_response(value, **kwargs)
86
+ def api_response(payload, html_kwargs: nil, json_kwargs: nil, **kwargs)
87
+ html_kwargs ||= {}
88
+ json_kwargs ||= {}
89
+
90
+ # serialize the payload if it's not blank
91
+ if payload.blank?
92
+ serialized_payload = ''
93
+ else
94
+ if self.respond_to?(:get_model_serializer_config, true)
95
+ serialized_payload = payload.to_json(self.get_model_serializer_config)
96
+ else
97
+ serialized_payload = payload.to_json
98
+ end
99
+ end
100
+
78
101
  respond_to do |format|
79
- format.html
80
- format.json { render json: value || '', **kwargs }
102
+ format.html {
103
+ kwargs = kwargs.merge(html_kwargs)
104
+ @template_logo_text ||= self.class.template_logo_text
105
+ @title ||= self.controller_name.camelize
106
+ @routes ||= self._get_routes
107
+ @payload = payload
108
+ @serialized_payload = serialized_payload
109
+ begin
110
+ render(**kwargs)
111
+ rescue ActionView::MissingTemplate # fallback to rest_framework default view
112
+ kwargs[:template] = "rest_framework/default"
113
+ end
114
+ render(**kwargs)
115
+ }
116
+ format.json {
117
+ kwargs = kwargs.merge(json_kwargs)
118
+ render(json: serialized_payload || '', **kwargs)
119
+ }
81
120
  end
82
121
  end
83
122
  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)
@@ -51,7 +50,7 @@ module RESTFramework
51
50
 
52
51
  # Get a list of fields for the current action.
53
52
  def get_fields
54
- return @fields if @fields
53
+ return @fields if instance_variable_defined?(:@fields) && @fields
55
54
 
56
55
  # index action should use list_fields
57
56
  name = (action_name == 'index') ? 'list' : action_name
@@ -60,9 +59,8 @@ module RESTFramework
60
59
  @fields = self.class.send("#{name}_fields")
61
60
  rescue NameError
62
61
  end
63
- @fields ||= self.class.fields || []
64
62
 
65
- return @fields
63
+ return @fields ||= self.class.fields || []
66
64
  end
67
65
 
68
66
  # Get a configuration passable to `as_json` for the model.
@@ -113,7 +111,7 @@ module RESTFramework
113
111
 
114
112
  # Internal interface for get_model, protecting against infinite recursion with get_recordset.
115
113
  def _get_model(from_internal_get_recordset: false)
116
- return @model if @model
114
+ return @model if instance_variable_defined?(:@model) && @model
117
115
  return self.class.model if self.class.model
118
116
  unless from_internal_get_recordset # prevent infinite recursion
119
117
  recordset = self._get_recordset(from_internal_get_model: true)
@@ -128,7 +126,7 @@ module RESTFramework
128
126
 
129
127
  # Internal interface for get_recordset, protecting against infinite recursion with get_model.
130
128
  def _get_recordset(from_internal_get_model: false)
131
- return @recordset if @recordset
129
+ return @recordset if instance_variable_defined?(:@recordset) && @recordset
132
130
  return self.class.recordset if self.class.recordset
133
131
  unless from_internal_get_model # prevent infinite recursion
134
132
  model = self._get_model(from_internal_get_recordset: true)
@@ -152,14 +150,14 @@ module RESTFramework
152
150
  # TODO: pagination classes like Django
153
151
  def index
154
152
  @records = self.get_filtered_recordset
155
- api_response(@records, **self.get_model_serializer_config)
153
+ api_response(@records)
156
154
  end
157
155
  end
158
156
 
159
157
  module ShowModelMixin
160
158
  def show
161
159
  @record = self.get_record
162
- api_response(@record, **self.get_model_serializer_config)
160
+ api_response(@record)
163
161
  end
164
162
  end
165
163
 
@@ -170,7 +168,7 @@ module RESTFramework
170
168
  rescue ActiveRecord::RecordInvalid => e
171
169
  api_response(e.record.messages, status: 400)
172
170
  end
173
- api_response(@record, **self.get_model_serializer_config)
171
+ api_response(@record)
174
172
  end
175
173
  end
176
174
 
@@ -180,7 +178,7 @@ module RESTFramework
180
178
  if @record
181
179
  @record.attributes(self.get_update_params)
182
180
  @record.save!
183
- api_response(@record, **self.get_model_serializer_config)
181
+ api_response(@record)
184
182
  else
185
183
  api_response({detail: "Record not found."}, status: 404)
186
184
  end
@@ -0,0 +1,2 @@
1
+ class RESTFramework::Engine < ::Rails::Engine
2
+ 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, false)
28
+ controller = mod.const_get(name)
29
29
  rescue NameError
30
30
  if fallback_reverse_pluralization
31
- controller = mod.const_get(name_reverse, false)
31
+ controller = mod.const_get(name_reverse)
32
32
  else
33
33
  raise
34
34
  end
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.1
4
+ version: 0.0.7
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-17 00:00:00.000000000 Z
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
  - - ">="