shaf 0.2.1 → 0.3.0
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
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/shaf/extensions.rb +4 -0
- data/lib/shaf/extensions/authorize.rb +1 -1
- data/lib/shaf/extensions/current_user.rb +44 -0
- data/lib/shaf/extensions/resource_uris.rb +47 -13
- data/lib/shaf/extensions/symbolic_routes.rb +27 -0
- data/lib/shaf/formable.rb +19 -3
- data/lib/shaf/generator/policy.rb +1 -0
- data/lib/shaf/generator/templates/api/controller.rb.erb +37 -26
- data/lib/shaf/generator/templates/api/policy.rb.erb +4 -1
- data/lib/shaf/helpers.rb +0 -2
- data/lib/shaf/helpers/payload.rb +7 -3
- data/lib/shaf/spec/http_method_utils.rb +3 -4
- data/lib/shaf/spec/integration_spec.rb +10 -1
- data/lib/shaf/spec/payload_utils.rb +11 -38
- data/lib/shaf/tasks/test.rb +5 -0
- data/lib/shaf/version.rb +1 -1
- data/templates/api/controllers/base_controller.rb +10 -3
- data/templates/api/controllers/docs_controller.rb +4 -3
- data/templates/api/controllers/root_controller.rb +1 -1
- data/templates/api/serializers/form_serializer.rb +6 -2
- data/templates/config/constants.rb +2 -0
- data/templates/config/directories.rb +13 -1
- metadata +4 -3
- metadata.gz.sig +0 -0
- data/lib/shaf/helpers/session.rb +0 -53
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 45e90f32663688a63993bcf1aa79c6c9b8667d11dbfc9a5fdba50141e983ea53
|
4
|
+
data.tar.gz: 919292b331cc09ce95031e4d1e723371114fef9e86b6d97c2e90e816befb0d77
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 788b01fd3f933f9e86ef99c9b51c14768208f73c5dc3cf4fe0a39fcb19078f82eb7eaaa2a1dcfc33d4d1a44980dc66254693a240b2e2b28d4a6e6b0d0383b676
|
7
|
+
data.tar.gz: a07510311e28e5008991d5461f9c2e97485640cd146a8304874bc216888eb722c941c266dc74b59b184ca3ea302b6a508a315421ed040dd64c2d111ea80548c7
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/lib/shaf/extensions.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
require 'shaf/extensions/resource_uris'
|
2
|
+
require 'shaf/extensions/current_user'
|
2
3
|
require 'shaf/extensions/authorize'
|
4
|
+
require 'shaf/extensions/symbolic_routes'
|
3
5
|
|
4
6
|
module Shaf
|
5
7
|
def self.extensions
|
6
8
|
[
|
7
9
|
ResourceUris,
|
10
|
+
CurrentUser,
|
8
11
|
Authorize,
|
12
|
+
SymbolicRoutes
|
9
13
|
]
|
10
14
|
end
|
11
15
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'digest'
|
2
|
+
|
3
|
+
module Shaf
|
4
|
+
module CurrentUser
|
5
|
+
def self.registered(app)
|
6
|
+
return unless app.respond_to?(:current_user) && app.current_user
|
7
|
+
|
8
|
+
app.log.info 'Using Shaf::CurrentUser'
|
9
|
+
app.helpers Helpers
|
10
|
+
end
|
11
|
+
|
12
|
+
def lookup_user_with(&block)
|
13
|
+
unless block_given? && block.respond_to?(:call)
|
14
|
+
raise ArgumentError, '::lookup_user_with requires a block argument'
|
15
|
+
end
|
16
|
+
log.info 'Using custom current_user lookup'
|
17
|
+
Helpers.lookup_proc = block
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Helpers
|
22
|
+
class << self
|
23
|
+
attr_accessor :lookup_proc
|
24
|
+
|
25
|
+
def user_lookup(request, token)
|
26
|
+
return lookup_proc.call(token, request) if lookup_proc
|
27
|
+
return unless token
|
28
|
+
digest = Digest::SHA256.hexdigest(token)
|
29
|
+
User.where(auth_token_digest: digest).first
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def current_user
|
34
|
+
return @current_user if defined?(@current_user)
|
35
|
+
header = settings.auth_token_header
|
36
|
+
token = request.env[header]
|
37
|
+
@current_user = Helpers.user_lookup(request, token)
|
38
|
+
end
|
39
|
+
|
40
|
+
def authenticated?
|
41
|
+
!current_user.nil?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -4,7 +4,8 @@ module Shaf
|
|
4
4
|
module ResourceUris
|
5
5
|
def resource_uris_for(*args)
|
6
6
|
CreateUriMethods.new(*args).call
|
7
|
-
|
7
|
+
|
8
|
+
include UriHelper unless self < UriHelper
|
8
9
|
end
|
9
10
|
|
10
11
|
def register_uri(name, uri)
|
@@ -13,6 +14,9 @@ module Shaf
|
|
13
14
|
end
|
14
15
|
method_string = MethodBuilder.as_string(name, uri)
|
15
16
|
UriHelperMethods.eval_method(method_string)
|
17
|
+
UriHelperMethods.register(MethodBuilder.template_method_name(name)) { uri.dup.freeze }
|
18
|
+
|
19
|
+
include UriHelper unless self < UriHelper
|
16
20
|
end
|
17
21
|
end
|
18
22
|
|
@@ -31,6 +35,10 @@ module Shaf
|
|
31
35
|
module UriHelper
|
32
36
|
extend UriHelperMethods
|
33
37
|
include UriHelperMethods
|
38
|
+
|
39
|
+
def self.included(mod)
|
40
|
+
mod.extend self
|
41
|
+
end
|
34
42
|
end
|
35
43
|
|
36
44
|
# This class register uri helper methods like:
|
@@ -39,6 +47,12 @@ module Shaf
|
|
39
47
|
# new_book_uri => /books/form
|
40
48
|
# edit_book_uri(book) => /books/5/edit
|
41
49
|
#
|
50
|
+
# And uri template methods:
|
51
|
+
# books_uri_template => /books
|
52
|
+
# book_uri_template => /books/:id
|
53
|
+
# new_book_uri_template => /books/form
|
54
|
+
# edit_book_uri_template => /books/:id/edit
|
55
|
+
#
|
42
56
|
class CreateUriMethods
|
43
57
|
def initialize(name, base: nil, plural_name: nil)
|
44
58
|
@name = name.to_s
|
@@ -62,17 +76,23 @@ module Shaf
|
|
62
76
|
attr_reader :name, :base, :plural_name
|
63
77
|
|
64
78
|
def register_resources_uri
|
65
|
-
uri = "#{base}/#{plural_name}"
|
66
|
-
|
67
|
-
|
68
|
-
|
79
|
+
uri = "#{base}/#{plural_name}".freeze
|
80
|
+
|
81
|
+
UriHelperMethods.register("#{plural_name}_uri") { uri }
|
82
|
+
UriHelperMethods.register("#{plural_name}_uri_template") { uri }
|
69
83
|
end
|
70
84
|
|
71
85
|
def register_resource_uri
|
72
86
|
uri = "#{base}/#{plural_name}"
|
87
|
+
|
73
88
|
UriHelperMethods.register "#{name}_uri" do |resrc|
|
74
89
|
id = resrc.is_a?(Integer) ? resrc : resrc&.id
|
75
|
-
"#{
|
90
|
+
raise ArgumentError, "id must be an integer! was #{id.class}" unless id.is_a?(Integer)
|
91
|
+
"#{uri}/#{id}".freeze
|
92
|
+
end
|
93
|
+
|
94
|
+
UriHelperMethods.register "#{name}_uri_template" do
|
95
|
+
"#{uri}/:id".freeze
|
76
96
|
end
|
77
97
|
end
|
78
98
|
|
@@ -83,27 +103,37 @@ module Shaf
|
|
83
103
|
uri = "#{base}/#{plural_name}"
|
84
104
|
UriHelperMethods.register "#{plural_name}_uri" do |resrc = nil|
|
85
105
|
if resrc.nil?
|
86
|
-
uri.
|
106
|
+
uri.freeze
|
87
107
|
else
|
88
|
-
id = resrc.is_a?(Integer) ? resrc : resrc
|
89
|
-
"#{
|
108
|
+
id = (resrc.is_a?(Integer) ? resrc : resrc.id).to_i
|
109
|
+
raise ArgumentError, "id must be an integer! was #{id.class}" unless id.is_a?(Integer)
|
110
|
+
"#{uri}/#{id}".freeze
|
90
111
|
end
|
91
112
|
end
|
113
|
+
|
114
|
+
UriHelperMethods.register "#{plural_name}_uri_template" do |collection = false|
|
115
|
+
(collection ? uri : "#{uri}/:id").freeze
|
116
|
+
end
|
92
117
|
end
|
93
118
|
|
94
119
|
def register_new_resource_uri
|
95
|
-
uri = "#{base}/#{plural_name}/form"
|
96
|
-
|
97
|
-
|
98
|
-
|
120
|
+
uri = "#{base}/#{plural_name}/form".freeze
|
121
|
+
|
122
|
+
UriHelperMethods.register("new_#{name}_uri") { uri }
|
123
|
+
UriHelperMethods.register("new_#{name}_uri_template") { uri }
|
99
124
|
end
|
100
125
|
|
101
126
|
def register_edit_resource_uri
|
102
127
|
uri = "#{base}/#{plural_name}"
|
128
|
+
|
103
129
|
UriHelperMethods.register "edit_#{name}_uri" do |resrc|
|
104
130
|
id = resrc.is_a?(Integer) ? resrc : resrc&.id
|
105
131
|
"#{uri}/#{id}/edit".freeze unless id.nil?
|
106
132
|
end
|
133
|
+
|
134
|
+
UriHelperMethods.register "edit_#{name}_uri_template" do
|
135
|
+
"#{uri}/:id/edit".freeze
|
136
|
+
end
|
107
137
|
end
|
108
138
|
end
|
109
139
|
|
@@ -113,6 +143,10 @@ module Shaf
|
|
113
143
|
"#{name}_uri"
|
114
144
|
end
|
115
145
|
|
146
|
+
def template_method_name(name)
|
147
|
+
"#{method_name(name)}_template"
|
148
|
+
end
|
149
|
+
|
116
150
|
def signature(name, uri)
|
117
151
|
args = extract_symbols(uri)
|
118
152
|
s = method_name(name)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Shaf
|
2
|
+
module SymbolicRoutes
|
3
|
+
[
|
4
|
+
:get,
|
5
|
+
:put,
|
6
|
+
:post,
|
7
|
+
:patch,
|
8
|
+
:delete,
|
9
|
+
:head,
|
10
|
+
:options,
|
11
|
+
:link,
|
12
|
+
:unlink
|
13
|
+
].each do |m|
|
14
|
+
define_method m do |path, &block|
|
15
|
+
super(rewrite_path(path), &block)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def rewrite_path(path)
|
20
|
+
return path unless path.is_a? Symbol
|
21
|
+
|
22
|
+
m = "#{path}_template"
|
23
|
+
raise "Don't know how to 'get #{path}'" unless respond_to? m
|
24
|
+
send m
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/shaf/formable.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
module Shaf
|
2
2
|
module Formable
|
3
3
|
class Field
|
4
|
-
attr_reader :name, :type, :value, :label
|
4
|
+
attr_reader :name, :type, :value, :label, :required
|
5
|
+
|
6
|
+
HTML_TYPE_MAPPINGS = {
|
7
|
+
'string' => 'text',
|
8
|
+
'boolean' => 'checkbox',
|
9
|
+
}.freeze
|
5
10
|
|
6
11
|
def initialize(name, params = {})
|
7
12
|
@name = name
|
@@ -9,6 +14,7 @@ module Shaf
|
|
9
14
|
@label = params[:label]
|
10
15
|
@has_value = params.key? :value
|
11
16
|
@value = params[:value]
|
17
|
+
@required = params[:required] || false
|
12
18
|
end
|
13
19
|
|
14
20
|
def has_value?
|
@@ -32,8 +38,18 @@ module Shaf
|
|
32
38
|
end
|
33
39
|
|
34
40
|
def input_element
|
35
|
-
_value = value ? %Q(
|
36
|
-
|
41
|
+
_value = value ? %Q(value="#{value.to_s}") : nil
|
42
|
+
_required = required ? "required" : nil
|
43
|
+
attributes = [
|
44
|
+
%Q(type="#{HTML_TYPE_MAPPINGS[type.to_s]}"),
|
45
|
+
'class="form--input"',
|
46
|
+
%Q(id="#{name.to_s}"),
|
47
|
+
%Q(name="#{name.to_s}"),
|
48
|
+
]
|
49
|
+
attributes << %Q(value="#{value.to_s}") if value
|
50
|
+
attributes << "required" if required
|
51
|
+
|
52
|
+
"<input #{attributes.join(" ")}>"
|
37
53
|
end
|
38
54
|
end
|
39
55
|
|
@@ -2,52 +2,50 @@ require '<%= policy_file %>'
|
|
2
2
|
|
3
3
|
class <%= controller_class_name %> < BaseController
|
4
4
|
|
5
|
+
authorize_with <%= policy_class_name %>
|
6
|
+
|
5
7
|
resource_uris_for :<%= name %>
|
6
8
|
|
7
|
-
|
9
|
+
get :<%= plural_name %>_uri do
|
10
|
+
authorize! :read
|
11
|
+
collection = paginate(<%= model_class_name %>.order(:created_at).reverse)
|
12
|
+
respond_with_collection collection, serializer: <%= serializer_class_name %>
|
13
|
+
end
|
8
14
|
|
9
|
-
get
|
10
|
-
|
11
|
-
|
12
|
-
form.href = <%= plural_name %>_uri
|
13
|
-
respond_with form
|
15
|
+
get :new_<%= name %>_uri do
|
16
|
+
authorize! :read
|
17
|
+
respond_with create_form
|
14
18
|
end
|
15
19
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
respond_with
|
20
|
+
post :<%= plural_name %>_uri do
|
21
|
+
authorize! :write
|
22
|
+
<%= name %> = <%= model_class_name %>.create(<%= name %>_params)
|
23
|
+
headers({ "Location" => <%= name %>_uri(<%= name %>) })
|
24
|
+
respond_with <%= name %>, status: 201
|
21
25
|
end
|
22
26
|
|
23
|
-
get
|
27
|
+
get :<%= name %>_uri do
|
28
|
+
authorize! :read
|
24
29
|
respond_with <%= name %>
|
25
30
|
end
|
26
31
|
|
27
|
-
|
32
|
+
get :edit_<%= name %>_uri do
|
33
|
+
authorize! :read
|
34
|
+
respond_with edit_form
|
35
|
+
end
|
36
|
+
|
37
|
+
put :<%= name %>_uri do
|
28
38
|
authorize! :write
|
29
39
|
<%= name %>.update(<%= name %>_params)
|
30
40
|
respond_with <%= name %>
|
31
41
|
end
|
32
42
|
|
33
|
-
delete
|
43
|
+
delete :<%= name %>_uri do
|
34
44
|
authorize! :write
|
35
45
|
<%= name %>.destroy
|
36
46
|
status 204
|
37
47
|
end
|
38
48
|
|
39
|
-
get '/<%= plural_name %>' do
|
40
|
-
collection = paginate(<%= model_class_name %>.order(:created_at).reverse)
|
41
|
-
respond_with_collection collection, serializer: <%= serializer_class_name %>
|
42
|
-
end
|
43
|
-
|
44
|
-
post '/<%= plural_name %>' do
|
45
|
-
authorize! :write
|
46
|
-
<%= name %> = <%= model_class_name %>.create(<%= name %>_params)
|
47
|
-
headers({ "Location" => <%= name %>_uri(<%= name %>) })
|
48
|
-
respond_with <%= name %>, status: 201
|
49
|
-
end
|
50
|
-
|
51
49
|
def <%= name %>_params
|
52
50
|
# Generated method - TODO: Remove any params that should not be allowed!
|
53
51
|
safe_params(<%= params.map { |p| ":#{p[0]}" }.join(', ') %>)
|
@@ -59,4 +57,17 @@ class <%= controller_class_name %> < BaseController
|
|
59
57
|
end
|
60
58
|
end
|
61
59
|
|
60
|
+
def create_form
|
61
|
+
<%= model_class_name %>.create_form.tap do |form|
|
62
|
+
form.self_link = new_<%= name %>_uri
|
63
|
+
form.href = <%= plural_name %>_uri
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def edit_form
|
68
|
+
<%= name %>.edit_form.tap do |form|
|
69
|
+
form.self_link = edit_<%= name %>_uri(<%= name %>)
|
70
|
+
form.href = <%= name %>_uri(<%= name %>)
|
71
|
+
end
|
72
|
+
end
|
62
73
|
end
|
@@ -3,6 +3,8 @@ class <%= policy_class_name %>
|
|
3
3
|
|
4
4
|
# Auto generated policy: Update this file to suite your API!
|
5
5
|
|
6
|
+
alias :<%= name %> :resource
|
7
|
+
|
6
8
|
<%= attributes.join("\n ") %>
|
7
9
|
|
8
10
|
link :up
|
@@ -16,11 +18,12 @@ class <%= policy_class_name %>
|
|
16
18
|
end
|
17
19
|
|
18
20
|
def read?
|
21
|
+
# FIXME!!
|
19
22
|
true
|
20
23
|
end
|
21
24
|
|
22
25
|
def write?
|
23
|
-
# !!
|
26
|
+
# FIXME!!
|
24
27
|
true
|
25
28
|
end
|
26
29
|
end
|
data/lib/shaf/helpers.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'shaf/helpers/payload'
|
2
2
|
require 'shaf/helpers/json_html'
|
3
3
|
require 'shaf/helpers/paginate'
|
4
|
-
require 'shaf/helpers/session'
|
5
4
|
|
6
5
|
module Shaf
|
7
6
|
def self.helpers
|
@@ -9,7 +8,6 @@ module Shaf
|
|
9
8
|
Payload,
|
10
9
|
JsonHtml,
|
11
10
|
Paginate,
|
12
|
-
Session,
|
13
11
|
]
|
14
12
|
end
|
15
13
|
end
|
data/lib/shaf/helpers/payload.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module Shaf
|
2
2
|
module Payload
|
3
|
+
EXCLUDED_FORM_PARAMS = ['captures', 'splat'].freeze
|
4
|
+
|
3
5
|
def supported_response_types(resource)
|
4
6
|
[
|
5
7
|
mime_type(:hal),
|
@@ -39,7 +41,7 @@ module Shaf
|
|
39
41
|
|
40
42
|
def parse_payload
|
41
43
|
if request.env['CONTENT_TYPE'] == 'application/x-www-form-urlencoded'
|
42
|
-
return params.reject { |key,_|
|
44
|
+
return params.reject { |key,_| EXCLUDED_FORM_PARAMS.include? key }
|
43
45
|
end
|
44
46
|
|
45
47
|
input = read_input
|
@@ -56,9 +58,11 @@ module Shaf
|
|
56
58
|
|
57
59
|
def safe_params(*fields)
|
58
60
|
return {} unless payload
|
61
|
+
field_strings = fields.map { |f| f.to_s.downcase }
|
62
|
+
field_strings << 'id' unless field_strings.include? 'id'
|
63
|
+
|
59
64
|
{}.tap do |allowed|
|
60
|
-
|
61
|
-
f = field.to_s.downcase
|
65
|
+
field_strings.each do |f|
|
62
66
|
allowed[f.to_sym] = payload[f] if payload[f]
|
63
67
|
end
|
64
68
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module Shaf
|
2
2
|
module Spec
|
3
|
-
module
|
3
|
+
module HttpUtils
|
4
4
|
include ::Rack::Test::Methods
|
5
5
|
|
6
|
-
[:get, :put, :post, :delete].each do |m|
|
6
|
+
[:get, :put, :patch, :post, :delete, :options, :head, :link, :unlink].each do |m|
|
7
7
|
define_method m do |*args|
|
8
8
|
set_headers
|
9
9
|
super(*args)
|
@@ -16,9 +16,8 @@ module Shaf
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def headers
|
19
|
-
last_response&.headers
|
19
|
+
last_response&.headers || {}
|
20
20
|
end
|
21
|
-
|
22
21
|
end
|
23
22
|
end
|
24
23
|
end
|
@@ -2,7 +2,7 @@ module Shaf
|
|
2
2
|
module Spec
|
3
3
|
class IntegrationSpec < Minitest::Spec
|
4
4
|
include Minitest::Hooks
|
5
|
-
include
|
5
|
+
include HttpUtils
|
6
6
|
include PayloadUtils
|
7
7
|
include UriHelper
|
8
8
|
|
@@ -30,6 +30,8 @@ module Shaf
|
|
30
30
|
def parse_response(body)
|
31
31
|
return nil if body.empty?
|
32
32
|
JSON.parse(body, symbolize_names: true)
|
33
|
+
rescue JSON::ParserError => e
|
34
|
+
assert e.nil?, "Could not parse reponse as json"
|
33
35
|
end
|
34
36
|
|
35
37
|
def app
|
@@ -46,6 +48,13 @@ module Shaf
|
|
46
48
|
end
|
47
49
|
end
|
48
50
|
|
51
|
+
def auth_token(token)
|
52
|
+
@__integration_test_auth_token = token
|
53
|
+
end
|
54
|
+
|
55
|
+
def clear_auth_token
|
56
|
+
@__integration_test_auth_token = nil
|
57
|
+
end
|
49
58
|
|
50
59
|
# def login(email, pass)
|
51
60
|
# params = {email: email, password: pass}
|
@@ -3,42 +3,6 @@ require 'minitest/assertions'
|
|
3
3
|
module Shaf
|
4
4
|
module Spec
|
5
5
|
module PayloadUtils
|
6
|
-
|
7
|
-
class Embedded
|
8
|
-
include HttpMethodUtils
|
9
|
-
include PayloadUtils
|
10
|
-
include Minitest::Assertions
|
11
|
-
|
12
|
-
# This is needed by Minitest::Assertions
|
13
|
-
# And we need that module to have all assert_*/refute_* methods
|
14
|
-
# available in this class
|
15
|
-
attr_accessor :assertions
|
16
|
-
|
17
|
-
def initialize(payload, context, block)
|
18
|
-
@payload = payload
|
19
|
-
@context = context
|
20
|
-
@block = block
|
21
|
-
@assertions = 0
|
22
|
-
end
|
23
|
-
|
24
|
-
def call(*args)
|
25
|
-
instance_exec(*args, &@block)
|
26
|
-
end
|
27
|
-
|
28
|
-
def method_missing(method, *args, &block)
|
29
|
-
if @context&.respond_to? method
|
30
|
-
define_singleton_method(method) { |*a, &b| @context.public_send(method, *a, &b) }
|
31
|
-
return public_send(method, *args, &block)
|
32
|
-
end
|
33
|
-
super
|
34
|
-
end
|
35
|
-
|
36
|
-
def respond_to_missing?(method, include_private = false)
|
37
|
-
return true if @context&.respond_to? method
|
38
|
-
super
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
6
|
def set_payload(payload)
|
43
7
|
@payload = payload
|
44
8
|
@payload = JSON.parse(payload, symbolize_names: true) if payload.is_a?(String)
|
@@ -69,7 +33,7 @@ module Shaf
|
|
69
33
|
assert_has_embedded name unless name.nil?
|
70
34
|
keys = [:_embedded, name&.to_sym].compact
|
71
35
|
return last_payload.dig(*keys) unless block_given?
|
72
|
-
|
36
|
+
exec_embed_block(last_payload.dig(*keys), Proc.new)
|
73
37
|
end
|
74
38
|
|
75
39
|
def each_embedded(name, &block)
|
@@ -80,7 +44,7 @@ module Shaf
|
|
80
44
|
"Embedded '#{name}' is not an instance of Array. Actual: #{list.class}"
|
81
45
|
|
82
46
|
list.each_with_index do |resource, i|
|
83
|
-
|
47
|
+
exec_embed_block(resource, block, i)
|
84
48
|
end
|
85
49
|
end
|
86
50
|
|
@@ -172,6 +136,15 @@ module Shaf
|
|
172
136
|
"Response contains disallowed embedded resource with name '#{name}': #{last_payload}"
|
173
137
|
end
|
174
138
|
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def exec_embed_block(payload, block, *args)
|
143
|
+
prev_payload = last_payload
|
144
|
+
set_payload(payload)
|
145
|
+
instance_exec(*args, &block)
|
146
|
+
set_payload(prev_payload)
|
147
|
+
end
|
175
148
|
end
|
176
149
|
end
|
177
150
|
end
|
data/lib/shaf/tasks/test.rb
CHANGED
@@ -5,24 +5,28 @@ namespace :test do |ns|
|
|
5
5
|
t.libs = %w(. api spec)
|
6
6
|
t.pattern = "spec/integration/**/*_spec.rb"
|
7
7
|
t.verbose = true
|
8
|
+
t.warning = false
|
8
9
|
end
|
9
10
|
|
10
11
|
Rake::TestTask.new(:models) do |t|
|
11
12
|
t.libs = %w(. api spec)
|
12
13
|
t.pattern = "spec/models/**/*_spec.rb"
|
13
14
|
t.verbose = true
|
15
|
+
t.warning = false
|
14
16
|
end
|
15
17
|
|
16
18
|
Rake::TestTask.new(:serializers) do |t|
|
17
19
|
t.libs = %w(. api spec)
|
18
20
|
t.pattern = "spec/serializers/**/*_spec.rb"
|
19
21
|
t.verbose = true
|
22
|
+
t.warning = false
|
20
23
|
end
|
21
24
|
|
22
25
|
Rake::TestTask.new(:lib) do |t|
|
23
26
|
t.libs = %w(. api spec)
|
24
27
|
t.pattern = "spec/lib/**/*_spec.rb"
|
25
28
|
t.verbose = true
|
29
|
+
t.warning = false
|
26
30
|
end
|
27
31
|
|
28
32
|
Rake::TestTask.new(:all) do |t|
|
@@ -34,6 +38,7 @@ namespace :test do |ns|
|
|
34
38
|
"spec/integration/**/*_spec.rb"
|
35
39
|
]
|
36
40
|
t.verbose = true
|
41
|
+
t.warning = false
|
37
42
|
end
|
38
43
|
|
39
44
|
end
|
data/lib/shaf/version.rb
CHANGED
@@ -12,21 +12,28 @@ class BaseController < Sinatra::Base
|
|
12
12
|
set :public_folder, ASSETS_DIR
|
13
13
|
disable :dump_errors
|
14
14
|
set :show_exceptions, :after_handler
|
15
|
+
enable :current_user
|
16
|
+
set :auth_token_header, AUTH_TOKEN_HEADER
|
15
17
|
end
|
16
18
|
|
17
19
|
use Rack::Deflater
|
18
|
-
register(*Shaf.extensions)
|
19
|
-
helpers(*Shaf.helpers)
|
20
20
|
|
21
21
|
def self.inherited(controller)
|
22
22
|
super
|
23
23
|
Shaf::App.use controller
|
24
24
|
end
|
25
25
|
|
26
|
-
def log
|
26
|
+
def self.log
|
27
27
|
$logger
|
28
28
|
end
|
29
29
|
|
30
|
+
def log
|
31
|
+
self.class.log
|
32
|
+
end
|
33
|
+
|
34
|
+
register(*Shaf.extensions)
|
35
|
+
helpers(*Shaf.helpers)
|
36
|
+
|
30
37
|
before do
|
31
38
|
log.info "Processing: #{request.request_method} #{request.path_info}"
|
32
39
|
log.debug "Payload: #{payload || 'empty'}"
|
@@ -1,12 +1,13 @@
|
|
1
1
|
class DocsController < BaseController
|
2
2
|
|
3
|
-
register_uri :doc_curie,
|
3
|
+
register_uri :doc_curie, '/doc/:resource/rels/{rel}'
|
4
|
+
register_uri :documentation, '/doc/:resource'
|
4
5
|
|
5
|
-
get
|
6
|
+
get doc_curie_uri_template do
|
6
7
|
doc.link(params[:rel])
|
7
8
|
end
|
8
9
|
|
9
|
-
get
|
10
|
+
get documentation_uri_template do
|
10
11
|
doc.to_s
|
11
12
|
end
|
12
13
|
|
@@ -33,9 +33,13 @@ class FormSerializer
|
|
33
33
|
fields = resource&.fields
|
34
34
|
break if fields.nil? || fields.empty?
|
35
35
|
hash[:fields] = fields.map do |field|
|
36
|
-
{
|
36
|
+
{
|
37
|
+
name: field.name,
|
38
|
+
type: field.type,
|
39
|
+
label: field.label,
|
40
|
+
}.tap do |f|
|
37
41
|
f[:value] = field.value if field.has_value?
|
38
|
-
f[:
|
42
|
+
f[:required] = true if field.required
|
39
43
|
end
|
40
44
|
end
|
41
45
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
APP_ROOT = File.expand_path('../', __dir__)
|
2
2
|
APP_DIR = File.expand_path('api', APP_ROOT)
|
3
|
+
SRC_DIR = File.expand_path('src', APP_ROOT)
|
3
4
|
VIEWS_DIR = File.join(APP_ROOT, Shaf::Settings.views_folder)
|
4
5
|
ASSETS_DIR = File.join(APP_ROOT, Shaf::Settings.public_folder)
|
6
|
+
AUTH_TOKEN_HEADER = 'HTTP_X_AUTH_TOKEN'
|
5
7
|
PAGINATION_PER_PAGE = Shaf::Settings.paginate_per_page || 25
|
@@ -21,7 +21,7 @@ def sort_files(files)
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
|
24
|
+
def require_ruby_files
|
25
25
|
files = Dir[File.join('**', '*.rb')]
|
26
26
|
sort_files(files).each do |file|
|
27
27
|
# load all files with .rb extension in subfolders of api
|
@@ -30,3 +30,15 @@ Dir.chdir(APP_DIR) do
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
+
if Dir.exist? SRC_DIR
|
34
|
+
$:.unshift SRC_DIR
|
35
|
+
|
36
|
+
Dir.chdir(SRC_DIR) do
|
37
|
+
require_ruby_files
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
Dir.chdir(APP_DIR) do
|
42
|
+
require_ruby_files
|
43
|
+
end
|
44
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shaf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sammy Henningsson
|
@@ -31,7 +31,7 @@ cert_chain:
|
|
31
31
|
CNZdF8Vavp6xMQbPHZwqjaeZz2WRXYS7jyYSvCunjwa3OtvXtfbIEGEWE6IM+t9k
|
32
32
|
H1g6Q+B6qk9O6g==
|
33
33
|
-----END CERTIFICATE-----
|
34
|
-
date: 2018-03-
|
34
|
+
date: 2018-03-26 00:00:00.000000000 Z
|
35
35
|
dependencies:
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rake
|
@@ -252,7 +252,9 @@ files:
|
|
252
252
|
- lib/shaf/errors.rb
|
253
253
|
- lib/shaf/extensions.rb
|
254
254
|
- lib/shaf/extensions/authorize.rb
|
255
|
+
- lib/shaf/extensions/current_user.rb
|
255
256
|
- lib/shaf/extensions/resource_uris.rb
|
257
|
+
- lib/shaf/extensions/symbolic_routes.rb
|
256
258
|
- lib/shaf/formable.rb
|
257
259
|
- lib/shaf/generator.rb
|
258
260
|
- lib/shaf/generator/controller.rb
|
@@ -277,7 +279,6 @@ files:
|
|
277
279
|
- lib/shaf/helpers/json_html.rb
|
278
280
|
- lib/shaf/helpers/paginate.rb
|
279
281
|
- lib/shaf/helpers/payload.rb
|
280
|
-
- lib/shaf/helpers/session.rb
|
281
282
|
- lib/shaf/middleware.rb
|
282
283
|
- lib/shaf/middleware/request_id.rb
|
283
284
|
- lib/shaf/registrable_factory.rb
|
metadata.gz.sig
CHANGED
Binary file
|
data/lib/shaf/helpers/session.rb
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
require 'digest'
|
2
|
-
|
3
|
-
module Shaf
|
4
|
-
module Session
|
5
|
-
|
6
|
-
SESSION_TTL = 60 * 60 * 24 * 2 # 2 days
|
7
|
-
|
8
|
-
def login(email, password)
|
9
|
-
return unless email && password
|
10
|
-
user = User.first(email: email) or return
|
11
|
-
bcrypt = BCrypt::Password.new(user.password_digest)
|
12
|
-
return unless bcrypt == password
|
13
|
-
@current_user = user
|
14
|
-
|
15
|
-
Session.where(user_id: user.id).delete
|
16
|
-
params = {
|
17
|
-
user_id: user.id,
|
18
|
-
expire_at: Time.now + SESSION_TTL,
|
19
|
-
}
|
20
|
-
Session.create(params)
|
21
|
-
end
|
22
|
-
|
23
|
-
def extend_session(session)
|
24
|
-
return unless session
|
25
|
-
session.update(expire_at: Time.now + SESSION_TTL)
|
26
|
-
session.auth_token = request.env['HTTP_X_AUTH_TOKEN']
|
27
|
-
session
|
28
|
-
end
|
29
|
-
|
30
|
-
def logout
|
31
|
-
current_session&.destroy
|
32
|
-
end
|
33
|
-
|
34
|
-
def current_user
|
35
|
-
unless defined?(@current_user) && @current_user
|
36
|
-
return unless request.env.key? 'HTTP_X_AUTH_TOKEN'
|
37
|
-
digest = Digest::SHA256.hexdigest(request.env['HTTP_X_AUTH_TOKEN'])
|
38
|
-
session = Session.where(auth_token_digest: digest).first
|
39
|
-
@current_user = User[session.user_id] if session&.valid?
|
40
|
-
end
|
41
|
-
@current_user
|
42
|
-
end
|
43
|
-
|
44
|
-
def current_session
|
45
|
-
unless @current_session
|
46
|
-
return unless current_user
|
47
|
-
@current_session = Session.where(user_id: current_user.id).first
|
48
|
-
end
|
49
|
-
@current_session
|
50
|
-
end
|
51
|
-
|
52
|
-
end
|
53
|
-
end
|