rest_framework 0.3.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1583903193d7a8c88b82eea0f34b7bd65ad4c59cc4f1112c082dc1be0c88100f
4
- data.tar.gz: 0b6fd3b17bb8255069af4f4b747c68b05c58cc07baa9a924f93284f1eba93528
3
+ metadata.gz: bd9117c7f8ec6e09424fd35946ede97fe1173cc010d77ef5da97328f1f5a555a
4
+ data.tar.gz: cd79a9d89862fdb60f14d6a17c03f9447d639ff794b0869e4cb8149c84755d4d
5
5
  SHA512:
6
- metadata.gz: fc14438bd01e40cc8d971743ec1fbc1c39f3acc5d571fb218d819c293e01c6d2f9ccf4a5665ea34986e47a343c42ed065ac114cdcbcac9aafff2d9e86ec30033
7
- data.tar.gz: 94b67818844fb8d2e4ca414f431ff85e7276216d1844f9da8413a1c135391fec26d6885c6fafc672c7068a579ec6d12ffd9f148d450ed8a0857820141f793966
6
+ metadata.gz: d3958f44f171649a7f2d4a92aa8d21b8375bb18532ca53af723cb64cc6f3bdbe0842784fc4dcb5a3ac715327731f030437c37cec7cb10604a8f44b5f260f49c7
7
+ data.tar.gz: 7fda99d8ef74d689091b0ab971d1256bcef1c745f696a7a2af0a93c37aaebfd05abbfd111ec676d6d2c9ee104764d83cf3c994b7fa33f5a2411d57f181c5a121
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2020 Gregory N. Schmit
3
+ Copyright (c) 2021 Gregory N. Schmit
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Rails REST Framework
2
2
 
3
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)
4
+ [![Build Status](https://travis-ci.com/gregschmit/rails-rest-framework.svg?branch=master)](https://travis-ci.com/gregschmit/rails-rest-framework)
5
5
  [![Coverage Status](https://coveralls.io/repos/github/gregschmit/rails-rest-framework/badge.svg?branch=master)](https://coveralls.io/github/gregschmit/rails-rest-framework?branch=master)
6
6
  [![Maintainability](https://api.codeclimate.com/v1/badges/ba5df7706cb544d78555/maintainability)](https://codeclimate.com/github/gregschmit/rails-rest-framework/maintainability)
7
7
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.4
1
+ 0.4.0
@@ -29,14 +29,14 @@
29
29
  <ul class="nav nav-tabs">
30
30
  <% if @json_payload %>
31
31
  <li class="nav-item">
32
- <a class="nav-link active" href="#tab-json" data-toggle="tab" role="tab">
32
+ <a class="nav-link active" href="#tab-json" data-bs-toggle="tab" role="tab">
33
33
  .json
34
34
  </a>
35
35
  </li>
36
36
  <% end %>
37
37
  <% if @xml_payload %>
38
38
  <li class="nav-item">
39
- <a class="nav-link" href="#tab-xml" data-toggle="tab" role="tab">
39
+ <a class="nav-link" href="#tab-xml" data-bs-toggle="tab" role="tab">
40
40
  .xml
41
41
  </a>
42
42
  </li>
@@ -57,7 +57,7 @@
57
57
  </div>
58
58
  </div>
59
59
  <% end %>
60
- <% unless @routes.blank? %>
60
+ <% unless @route_groups.blank? %>
61
61
  <div class="row">
62
62
  <h2>Routes</h2>
63
63
  <%= render partial: 'rest_framework/routes' %>
@@ -1,11 +1,12 @@
1
1
  <meta charset="utf-8">
2
- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
2
+ <meta name="viewport" content="width=device-width, initial-scale=1">
3
3
  <%= csrf_meta_tags %>
4
4
  <%= csp_meta_tag rescue nil %>
5
5
 
6
- <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
7
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.2.0/styles/vs.min.css" integrity="sha512-aWjgJTbdG4imzxTxistV5TVNffcYGtIQQm2NBNahV6LmX14Xq9WwZTL1wPjaSglUuVzYgwrq+0EuI4+vKvQHHw==" crossorigin="anonymous" />
6
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous">
7
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.2.0/styles/vs.min.css" integrity="sha512-aWjgJTbdG4imzxTxistV5TVNffcYGtIQQm2NBNahV6LmX14Xq9WwZTL1wPjaSglUuVzYgwrq+0EuI4+vKvQHHw==" crossorigin="anonymous">
8
8
  <style>
9
+ /* Adjust headers to always take up their entire row, and tweak the sizing. */
9
10
  h1,h2,h3,h4,h5,h6 { width: 100%; font-weight: normal; }
10
11
  h1 { font-size: 2rem; }
11
12
  h2 { font-size: 1.7rem; }
@@ -13,11 +14,27 @@
13
14
  h4 { font-size: 1.3rem; }
14
15
  h5 { font-size: 1.1rem; }
15
16
  h6 { font-size: 1rem; }
17
+
18
+ /* Make route group expansion obvious to the user. */
19
+ .rrf-routes .rrf-route-group-header {
20
+ background-color: #f8f8f8;
21
+ }
22
+ .rrf-routes .rrf-route-group-header:hover {
23
+ background-color: #f0f0f0;
24
+ }
25
+ .rrf-routes .rrf-route-group-header td {
26
+ cursor: pointer;
27
+ }
28
+
29
+ /* Disable bootstrap's collapsing animation because in tables it causes delayed jerkiness. */
30
+ .rrf-routes .collapsing {
31
+ -webkit-transition: none;
32
+ transition: none;
33
+ display: none;
34
+ }
16
35
  </style>
17
36
 
18
- <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
19
- <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
20
- <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
37
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/bQdsTh/da6pkI1MST/rWKFNjaCP5gBSY4sEBT38Q/9RBh9AH40zEOg7Hlq2THRZ" crossorigin="anonymous"></script>
21
38
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.2.0/highlight.min.js" integrity="sha512-TDKKr+IvoqZnPzc3l35hdjpHD0m+b2EC2SrLEgKDRWpxf2rFCxemkgvJ5kfU48ip+Y+m2XVKyOCD85ybtlZDmw==" crossorigin="anonymous"></script>
22
39
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.2.0/languages/json.min.js" integrity="sha512-FoN8JE+WWCdIGXAIT8KQXwpiavz0Mvjtfk7Rku3MDUNO0BDCiRMXAsSX+e+COFyZTcDb9HDgP+pM2RX12d4j+A==" crossorigin="anonymous"></script>
23
40
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.2.0/languages/xml.min.js" integrity="sha512-dICltIgnUP+QSJrnYGCV8943p3qSDgvcg2NU4W8IcOZP4tdrvxlXjbhIznhtVQEcXow0mOjLM0Q6/NvZsmUH4g==" crossorigin="anonymous"></script>
@@ -1,18 +1,36 @@
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>
1
+ <div class="table-responsive">
2
+ <table class="table table-responsive rrf-routes">
3
+ <thead>
4
+ <tr>
5
+ <th scope="col">Path</th>
6
+ <th scope="col">Verb</th>
7
+ <th scope="col">Controller#Action</th>
8
+ </tr>
9
+ </thead>
10
+ <%# Render first group of routes directly. %>
11
+ <tbody>
12
+ <% @route_groups.values[0].each do |route| %>
13
+ <tr>
14
+ <td><%= route[:path] %></td>
15
+ <td><%= route[:verb] %></td>
16
+ <td><%= route[:controller] %>#<%= route[:action] %></td>
17
+ </tr>
18
+ <% end %>
19
+ </tbody>
20
+ <%# Render any other groups under dropdowns. %>
21
+ <% @route_groups.drop(1).each_with_index do |(name, route_group), index| %>
22
+ <tr data-bs-toggle="collapse" data-bs-target="#route-group-<%= index %>" class="rrf-route-group-header">
23
+ <td colspan="3" class="text-center user-select-none"><%= name %></td>
24
+ </tr>
25
+ <tbody id="route-group-<%= index %>" class="collapse" style="padding: 0">
26
+ <% route_group.each do |route| %>
27
+ <tr>
28
+ <td><%= route[:path] %></td>
29
+ <td><%= route[:verb] %></td>
30
+ <td><%= route[:controller] %>#<%= route[:action] %></td>
31
+ </tr>
32
+ <% end %>
33
+ </tbody>
16
34
  <% end %>
17
- </tbody>
18
- </table>
35
+ </table>
36
+ </div>
@@ -1,5 +1,6 @@
1
1
  require_relative '../errors'
2
2
  require_relative '../serializers'
3
+ require_relative '../utils'
3
4
 
4
5
 
5
6
  # This module provides the common functionality for any controller mixins, a `root` action, and
@@ -121,22 +122,6 @@ module RESTFramework::BaseControllerMixin
121
122
  }, status: 406)
122
123
  end
123
124
 
124
- # Helper for showing routes under a controller action, used for the browsable API.
125
- def _get_routes
126
- begin
127
- formatter = ActionDispatch::Routing::ConsoleFormatter::Sheet
128
- rescue NameError
129
- # :nocov:
130
- formatter = ActionDispatch::Routing::ConsoleFormatter
131
- # :nocov:
132
- end
133
- return ActionDispatch::Routing::RoutesInspector.new(Rails.application.routes.routes).format(
134
- formatter.new
135
- ).lines.drop(1).map { |r| r.split.last(3) }.map { |r|
136
- {verb: r[0], path: r[1], action: r[2]}
137
- }.select { |r| r[:path].start_with?(request.path) }
138
- end
139
-
140
125
  # Helper to render a browsable API for `html` format, along with basic `json`/`xml` formats, and
141
126
  # with support or passing custom `kwargs` to the underlying `render` calls.
142
127
  def api_response(payload, html_kwargs: nil, **kwargs)
@@ -178,7 +163,7 @@ module RESTFramework::BaseControllerMixin
178
163
  end
179
164
  @template_logo_text ||= "Rails REST Framework"
180
165
  @title ||= self.controller_name.camelize
181
- @routes ||= self._get_routes
166
+ @route_groups ||= RESTFramework::Utils::get_routes(Rails.application.routes, request)
182
167
  hkwargs = kwargs.merge(html_kwargs)
183
168
  begin
184
169
  render(**hkwargs)
@@ -14,15 +14,3 @@ class RESTFramework::NilPassedToAPIResponseError < RESTFramework::Error
14
14
  MSG
15
15
  end
16
16
  end
17
-
18
-
19
- class RESTFramework::UnserializableError < RESTFramework::Error
20
- def initialize(object)
21
- @object = object
22
- return super
23
- end
24
-
25
- def message
26
- return "Unable to serialize `#{@object.inspect}` (of type `#{@object.class}`)."
27
- end
28
- end
@@ -84,7 +84,7 @@ class RESTFramework::ModelSearchFilter < RESTFramework::BaseFilter
84
84
  # Ensure we use array conditions to prevent SQL injection.
85
85
  unless search.blank?
86
86
  return data.where(fields.map { |f|
87
- "CAST(#{f} AS text) #{@controller.send(:search_ilike) ? "ILIKE" : "LIKE"} ?"
87
+ "CAST(#{f} AS CHAR) #{@controller.send(:search_ilike) ? "ILIKE" : "LIKE"} ?"
88
88
  }.join(' OR '), *(["%#{search}%"] * fields.length))
89
89
  end
90
90
 
@@ -1,43 +1,8 @@
1
1
  require 'action_dispatch/routing/mapper'
2
-
2
+ require_relative 'utils'
3
3
 
4
4
  module ActionDispatch::Routing
5
5
  class Mapper
6
- # Internal helper to take extra_actions hash and convert to a consistent format.
7
- protected def _parse_extra_actions(extra_actions)
8
- return (extra_actions || {}).map do |k,v|
9
- kwargs = {action: k}
10
- path = k
11
-
12
- # Convert structure to path/methods/kwargs.
13
- if v.is_a?(Hash) # allow kwargs
14
- v = v.symbolize_keys
15
-
16
- # Ensure methods is an array.
17
- if v[:methods].is_a?(String) || v[:methods].is_a?(Symbol)
18
- methods = [v.delete(:methods)]
19
- else
20
- methods = v.delete(:methods)
21
- end
22
-
23
- # Override path if it's provided.
24
- if v.key?(:path)
25
- path = v.delete(:path)
26
- end
27
-
28
- # Pass any further kwargs to the underlying Rails interface.
29
- kwargs = kwargs.merge(v)
30
- elsif v.is_a?(Symbol) || v.is_a?(String)
31
- methods = [v]
32
- else
33
- methods = v
34
- end
35
-
36
- # Return a hash with keys: :path, :methods, :kwargs.
37
- {path: path, methods: methods, kwargs: kwargs}
38
- end
39
- end
40
-
41
6
  # Internal interface to get the controller class from the name and current scope.
42
7
  protected def _get_controller_class(name, pluralize: true, fallback_reverse_pluralization: true)
43
8
  # get class name
@@ -92,13 +57,13 @@ module ActionDispatch::Routing
92
57
  if controller.is_a?(Class)
93
58
  controller_class = controller
94
59
  else
95
- controller_class = _get_controller_class(controller, pluralize: !default_singular)
60
+ controller_class = self._get_controller_class(controller, pluralize: !default_singular)
96
61
  end
97
62
 
98
63
  # Set controller if it's not explicitly set.
99
64
  kwargs[:controller] = name unless kwargs[:controller]
100
65
 
101
- # determine plural/singular resource
66
+ # Determine plural/singular resource.
102
67
  force_singular = kwargs.delete(:force_singular)
103
68
  force_plural = kwargs.delete(:force_plural)
104
69
  if force_singular
@@ -118,14 +83,16 @@ module ActionDispatch::Routing
118
83
  public_send(resource_method, name, except: skip, **kwargs) do
119
84
  if controller_class.respond_to?(:extra_member_actions)
120
85
  member do
121
- actions = self._parse_extra_actions(controller_class.extra_member_actions)
122
- _route_extra_actions(actions)
86
+ actions = RESTFramework::Utils::parse_extra_actions(
87
+ controller_class.extra_member_actions
88
+ )
89
+ self._route_extra_actions(actions)
123
90
  end
124
91
  end
125
92
 
126
93
  collection do
127
- actions = self._parse_extra_actions(controller_class.extra_actions)
128
- _route_extra_actions(actions)
94
+ actions = RESTFramework::Utils::parse_extra_actions(controller_class.extra_actions)
95
+ self._route_extra_actions(actions)
129
96
  end
130
97
 
131
98
  yield if block_given?
@@ -160,14 +127,14 @@ module ActionDispatch::Routing
160
127
  kwargs[:controller] = name unless kwargs[:controller]
161
128
 
162
129
  # Route actions using the resourceful router, but skip all builtin actions.
163
- actions = self._parse_extra_actions(controller_class.extra_actions)
130
+ actions = RESTFramework::Utils::parse_extra_actions(controller_class.extra_actions)
164
131
  public_send(:resource, name, only: [], **kwargs) do
165
132
  # Route a root for this resource.
166
133
  if route_root_to
167
134
  get '', action: route_root_to
168
135
  end
169
136
 
170
- _route_extra_actions(actions, &block)
137
+ self._route_extra_actions(actions, &block)
171
138
  end
172
139
  end
173
140
 
@@ -105,18 +105,12 @@ class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
105
105
 
106
106
  # Convert the object (record or recordset) to Ruby primitives.
107
107
  def serialize
108
- if @object
109
- begin
110
- if @object.is_a?(Enumerable)
111
- return @object.map { |r| r.serializable_hash(self.get_serializer_config) }
112
- end
113
- return @object.serializable_hash(self.get_serializer_config)
114
- rescue NoMethodError
115
- end
116
- end
108
+ raise "No object available to serialize!" unless @object
117
109
 
118
- # Raise an error if we cannot serialize the object.
119
- raise RESTFramework::UnserializableError.new(@object)
110
+ if @object.is_a?(Enumerable)
111
+ return @object.map { |r| r.serializable_hash(self.get_serializer_config) }
112
+ end
113
+ return @object.serializable_hash(self.get_serializer_config)
120
114
  end
121
115
 
122
116
  # Allow a serializer instance to be used as a hash directly in a nested serializer config.
@@ -0,0 +1,63 @@
1
+ module RESTFramework::Utils
2
+ # Helper to take extra_actions hash and convert to a consistent format:
3
+ # `{paths:, methods:, kwargs:}`.
4
+ def self.parse_extra_actions(extra_actions)
5
+ return (extra_actions || {}).map do |k,v|
6
+ kwargs = {action: k}
7
+ path = k
8
+
9
+ # Convert structure to path/methods/kwargs.
10
+ if v.is_a?(Hash) # allow kwargs
11
+ v = v.symbolize_keys
12
+
13
+ # Ensure methods is an array.
14
+ if v[:methods].is_a?(String) || v[:methods].is_a?(Symbol)
15
+ methods = [v.delete(:methods)]
16
+ else
17
+ methods = v.delete(:methods)
18
+ end
19
+
20
+ # Override path if it's provided.
21
+ if v.key?(:path)
22
+ path = v.delete(:path)
23
+ end
24
+
25
+ # Pass any further kwargs to the underlying Rails interface.
26
+ kwargs = kwargs.merge(v)
27
+ elsif v.is_a?(Symbol) || v.is_a?(String)
28
+ methods = [v]
29
+ else
30
+ methods = v
31
+ end
32
+
33
+ # Return a hash with keys: :path, :methods, :kwargs.
34
+ {path: path, methods: methods, kwargs: kwargs}
35
+ end
36
+ end
37
+
38
+ # Helper to get the current route pattern, stripped of the `(:format)` segment.
39
+ def self.get_route_pattern(application_routes, request)
40
+ application_routes.router.recognize(request) do |route, _, _|
41
+ return route.path.spec.to_s.gsub(/\(\.:format\)$/, '')
42
+ end
43
+ end
44
+
45
+ # Helper for showing routes under a controller action, used for the browsable API.
46
+ def self.get_routes(application_routes, request)
47
+ current_pattern = self.get_route_pattern(application_routes, request)
48
+ current_subdomain = request.subdomain.presence
49
+
50
+ # Return routes that match our current route subdomain/pattern, grouped by controller.
51
+ return application_routes.routes.map { |r|
52
+ {
53
+ verb: r.verb,
54
+ path: r.path.spec.to_s,
55
+ action: r.defaults[:action],
56
+ controller: r.defaults[:controller],
57
+ subdomain: r.defaults[:subdomain],
58
+ }
59
+ }.select { |r|
60
+ r[:subdomain] == current_subdomain && r[:path].start_with?(current_pattern)
61
+ }.group_by { |r| r[:controller] }
62
+ end
63
+ 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.3.4
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gregory N. Schmit
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-07 00:00:00.000000000 Z
11
+ date: 2021-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -49,6 +49,7 @@ files:
49
49
  - lib/rest_framework/paginators.rb
50
50
  - lib/rest_framework/routers.rb
51
51
  - lib/rest_framework/serializers.rb
52
+ - lib/rest_framework/utils.rb
52
53
  - lib/rest_framework/version.rb
53
54
  homepage: https://rails-rest-framework.com
54
55
  licenses:
@@ -56,7 +57,7 @@ licenses:
56
57
  metadata:
57
58
  homepage_uri: https://rails-rest-framework.com
58
59
  source_code_uri: https://github.com/gregschmit/rails-rest-framework
59
- post_install_message:
60
+ post_install_message:
60
61
  rdoc_options: []
61
62
  require_paths:
62
63
  - lib
@@ -73,7 +74,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
73
74
  version: '0'
74
75
  requirements: []
75
76
  rubygems_version: 3.1.4
76
- signing_key:
77
+ signing_key:
77
78
  specification_version: 4
78
79
  summary: A framework for DRY RESTful APIs in Ruby on Rails.
79
80
  test_files: []