shaf 0.8.0 → 1.0.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/bin/shaf +19 -4
- data/lib/shaf.rb +1 -0
- data/lib/shaf/app.rb +7 -10
- data/lib/shaf/command/server.rb +4 -2
- data/lib/shaf/errors.rb +22 -2
- data/lib/shaf/extensions/controller_hooks.rb +14 -0
- data/lib/shaf/extensions/resource_uris.rb +1 -1
- data/lib/shaf/formable/field.rb +10 -6
- data/lib/shaf/formable/form.rb +4 -0
- data/lib/shaf/generator/base.rb +2 -2
- data/lib/shaf/generator/controller.rb +11 -11
- data/lib/shaf/generator/helper.rb +33 -0
- data/lib/shaf/generator/migration.rb +6 -1
- data/lib/shaf/generator/migration/empty.rb +2 -2
- data/lib/shaf/generator/serializer.rb +4 -4
- data/lib/shaf/generator/templates/api/controller.rb.erb +4 -3
- data/lib/shaf/generator/templates/api/serializer.rb.erb +10 -12
- data/lib/shaf/generator/templates/spec/serializer_spec.rb.erb +4 -2
- data/lib/shaf/helpers.rb +2 -0
- data/lib/shaf/helpers/http_header.rb +26 -0
- data/lib/shaf/helpers/paginate.rb +4 -2
- data/lib/shaf/helpers/payload.rb +33 -8
- data/lib/shaf/middleware/request_id.rb +3 -1
- data/lib/shaf/rake/db.rb +5 -5
- data/lib/shaf/rake/test.rb +0 -27
- data/lib/shaf/router.rb +133 -0
- data/lib/shaf/settings.rb +36 -11
- data/lib/shaf/spec/http_method_utils.rb +11 -3
- data/lib/shaf/spec/integration_spec.rb +4 -4
- data/lib/shaf/spec/serializer_spec.rb +1 -1
- data/lib/shaf/upgrade/manifest.rb +34 -9
- data/lib/shaf/upgrade/package.rb +24 -15
- data/lib/shaf/utils.rb +31 -21
- data/lib/shaf/version.rb +1 -1
- data/templates/Rakefile +27 -0
- data/templates/api/controllers/base_controller.rb +13 -8
- data/templates/api/serializers/error_serializer.rb +3 -1
- data/templates/api/serializers/form_serializer.rb +11 -16
- data/templates/api/serializers/validation_error_serializer.rb +13 -0
- data/templates/config.ru +1 -1
- data/templates/config/bootstrap.rb +3 -1
- data/templates/config/database.rb +6 -7
- data/templates/config/directories.rb +3 -2
- data/templates/config/helpers.rb +1 -1
- data/templates/config/initializers/db_migrations.rb +14 -8
- data/templates/config/initializers/logging.rb +2 -2
- data/templates/config/initializers/sequel.rb +1 -0
- data/templates/config/paths.rb +7 -0
- data/templates/config/settings.yml +5 -0
- data/upgrades/1.0.0.tar.gz +0 -0
- metadata +38 -19
- metadata.gz.sig +0 -0
- data/templates/config/constants.rb +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5e022f8371c152548e409bfdf7711fa7dcd91e54b93cecfc341c09d9beee963
|
4
|
+
data.tar.gz: ac93080f87f7663402225692f402789fdae5b8605ec5bbba44b8a6fd8fb9bd4b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5607ab31104b40ab08cbec25ae47a382e616ddaf8239b411b82509afd7bc995cd867d070fd86a8bb3f8596534f83d2e97338c8d72c9727e410196dfd8c237fa5
|
7
|
+
data.tar.gz: c5bd80f13128e66384a0a01a5a24137a6507509bca7a2ae5a01b2b2c36c2eb2515c2b3e082ed0ba2e55238aaa982441300c7121fa5b7d63176033ab839b92846
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/bin/shaf
CHANGED
@@ -16,17 +16,23 @@ module Shaf
|
|
16
16
|
check_customizations
|
17
17
|
return show_help if show_help?
|
18
18
|
Command::Factory.create(*ARGV).call
|
19
|
-
rescue RegistrableFactory::NotFoundError, Command::ArgumentError =>
|
20
|
-
STDERR.puts
|
19
|
+
rescue RegistrableFactory::NotFoundError, Command::ArgumentError => err
|
20
|
+
STDERR.puts err.message, "\n"
|
21
|
+
show_backtrace(err)
|
21
22
|
show_help
|
22
23
|
exit 1
|
23
24
|
rescue Utils::ProjectRootNotFound
|
24
25
|
STDERR.puts "This command can only be executed inside a Shaf project directory. " \
|
25
26
|
"Please change directory and try again!"
|
26
27
|
exit 2
|
27
|
-
rescue Command::CommandError =>
|
28
|
-
STDERR.puts "Command failed: #{
|
28
|
+
rescue Command::CommandError => err
|
29
|
+
STDERR.puts "Command failed: #{err.message}\n"
|
30
|
+
show_backtrace(err)
|
29
31
|
exit 3
|
32
|
+
rescue StandardError => err
|
33
|
+
STDERR.puts err.message, "\n"
|
34
|
+
show_backtrace(err, force: true)
|
35
|
+
exit 4
|
30
36
|
end
|
31
37
|
|
32
38
|
def show_help
|
@@ -45,6 +51,15 @@ module Shaf
|
|
45
51
|
ARGV.first =~ /-h/
|
46
52
|
end
|
47
53
|
|
54
|
+
def show_backtrace(err, force: false)
|
55
|
+
if force || ARGV.include?('--trace')
|
56
|
+
err = err.cause while err.cause
|
57
|
+
STDERR.puts err.backtrace
|
58
|
+
else
|
59
|
+
STDERR.puts 'run the command with "--trace" to see a stack trace'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
48
63
|
def check_customizations
|
49
64
|
return unless project_root
|
50
65
|
|
data/lib/shaf.rb
CHANGED
data/lib/shaf/app.rb
CHANGED
@@ -3,20 +3,17 @@ require 'shaf/middleware'
|
|
3
3
|
module Shaf
|
4
4
|
class App
|
5
5
|
class << self
|
6
|
-
def
|
7
|
-
|
6
|
+
def run!
|
7
|
+
app.run!
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
11
|
-
Sinatra.new.tap do |
|
12
|
-
|
13
|
-
|
10
|
+
def app
|
11
|
+
Sinatra.new.tap do |app|
|
12
|
+
app.set :port, Settings.port || 3000
|
13
|
+
app.use Middleware::RequestId
|
14
|
+
app.use Router
|
14
15
|
end
|
15
16
|
end
|
16
|
-
|
17
|
-
def use(middleware)
|
18
|
-
instance.use middleware
|
19
|
-
end
|
20
17
|
end
|
21
18
|
end
|
22
19
|
end
|
data/lib/shaf/command/server.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Shaf
|
2
4
|
module Command
|
3
5
|
class Server < Base
|
@@ -6,7 +8,7 @@ module Shaf
|
|
6
8
|
usage 'server'
|
7
9
|
|
8
10
|
def self.options(parser, options)
|
9
|
-
parser.on(
|
11
|
+
parser.on('-p', '--port PORT', Integer, 'Listen port') do |p|
|
10
12
|
options[:port] = p
|
11
13
|
end
|
12
14
|
end
|
@@ -14,7 +16,7 @@ module Shaf
|
|
14
16
|
def call
|
15
17
|
Settings.port = options[:port] if options[:port]
|
16
18
|
bootstrap
|
17
|
-
App.
|
19
|
+
App.run!
|
18
20
|
end
|
19
21
|
end
|
20
22
|
end
|
data/lib/shaf/errors.rb
CHANGED
@@ -64,7 +64,7 @@ module Shaf
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
-
class ConflictError
|
67
|
+
class ConflictError < ServerError
|
68
68
|
def http_status
|
69
69
|
409
|
70
70
|
end
|
@@ -87,7 +87,7 @@ module Shaf
|
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
-
class UnprocessableEntityError
|
90
|
+
class UnprocessableEntityError < ServerError
|
91
91
|
def http_status
|
92
92
|
422
|
93
93
|
end
|
@@ -97,5 +97,25 @@ module Shaf
|
|
97
97
|
super(msg, code: "UNPROCESSABLE_ENTITY", title: "Request can not be processed")
|
98
98
|
end
|
99
99
|
end
|
100
|
+
|
101
|
+
class ValidationError < ServerError
|
102
|
+
attr_reader :fields
|
103
|
+
|
104
|
+
def self.from_sequel(validation_failed)
|
105
|
+
new(validation_failed.message, validation_failed.errors).tap do |err|
|
106
|
+
err.set_backtrace(validation_failed.backtrace)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def http_status
|
111
|
+
422
|
112
|
+
end
|
113
|
+
|
114
|
+
def initialize(msg, fields)
|
115
|
+
msg ||= "The entity being created/updated is invalid"
|
116
|
+
super(msg, code: "VALIDATION_ERROR", title: "Invalid entity")
|
117
|
+
@fields = fields || {}
|
118
|
+
end
|
119
|
+
end
|
100
120
|
end
|
101
121
|
end
|
@@ -15,6 +15,7 @@ module Shaf
|
|
15
15
|
def __action_hook(hook, method_name, block, **options)
|
16
16
|
only = Array(options[:only]) if options.key? :only
|
17
17
|
except = Array(options[:except]) if options.key? :except
|
18
|
+
validate_symbols(hook: hook, only: only, except: except)
|
18
19
|
|
19
20
|
path_helpers.each do |helper|
|
20
21
|
next if only && !only.include?(helper)
|
@@ -41,6 +42,19 @@ module Shaf
|
|
41
42
|
str = template.gsub(%r{:[^/]*}, '\w+')
|
42
43
|
Regexp.new("#{str}/?")
|
43
44
|
end
|
45
|
+
|
46
|
+
def validate_symbols(hook:, only: [], except: [])
|
47
|
+
s = "\n\n\"%s\" given as :%s keyword arg to %s hook. Must be a symbol!\n"
|
48
|
+
Array(only).each do |value|
|
49
|
+
next if value.is_a? Symbol
|
50
|
+
log.warn format(s, value, 'only', hook)
|
51
|
+
end
|
52
|
+
|
53
|
+
Array(except).each do |value|
|
54
|
+
next if value.is_a? Symbol
|
55
|
+
log.warn format(s, value, 'except', hook)
|
56
|
+
end
|
57
|
+
end
|
44
58
|
end
|
45
59
|
|
46
60
|
Sinatra.register ControllerHooks
|
data/lib/shaf/formable/field.rb
CHANGED
@@ -5,7 +5,9 @@ module Shaf
|
|
5
5
|
class Field
|
6
6
|
extend Shaf::ImmutableAttr
|
7
7
|
|
8
|
-
immutable_reader :name, :type, :value, :
|
8
|
+
immutable_reader :name, :type, :value, :title, :required, :hidden, :accessor_name
|
9
|
+
|
10
|
+
alias label title
|
9
11
|
|
10
12
|
HTML_TYPE_MAPPINGS = {
|
11
13
|
string: 'text',
|
@@ -15,11 +17,12 @@ module Shaf
|
|
15
17
|
def initialize(name, params = {})
|
16
18
|
@name = name
|
17
19
|
@type = params[:type]&.to_sym
|
18
|
-
@
|
20
|
+
@title = params[:title] || params[:label]
|
21
|
+
@hidden = params.fetch(:hidden, false)
|
19
22
|
@has_value = params.key? :value
|
20
23
|
@value = params[:value]
|
21
|
-
@required = params
|
22
|
-
@accessor_name = (
|
24
|
+
@required = params.fetch(:required, false)
|
25
|
+
@accessor_name = params.fetch(:accessor_name, name).to_sym
|
23
26
|
end
|
24
27
|
|
25
28
|
def has_value?
|
@@ -43,15 +46,16 @@ module Shaf
|
|
43
46
|
private
|
44
47
|
|
45
48
|
def label_element
|
46
|
-
str = (
|
49
|
+
str = (title || name || "").to_s
|
47
50
|
%Q(<label for="#{name}" class="form--label">#{str}</label>)
|
48
51
|
end
|
49
52
|
|
50
53
|
def input_element
|
51
54
|
_value = value ? %Q(value="#{value.to_s}") : nil
|
52
55
|
_required = required ? "required" : nil
|
56
|
+
_type = hidden ? "hidden" : HTML_TYPE_MAPPINGS.fetch(type.to_s, 'text')
|
53
57
|
attributes = [
|
54
|
-
%Q(type="#{
|
58
|
+
%Q(type="#{_type}"),
|
55
59
|
'class="form--input"',
|
56
60
|
%Q(id="#{name.to_s}"),
|
57
61
|
%Q(name="#{name.to_s}"),
|
data/lib/shaf/formable/form.rb
CHANGED
data/lib/shaf/generator/base.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
require 'erb'
|
3
|
-
require '
|
3
|
+
require 'shaf/generator/helper'
|
4
4
|
|
5
5
|
module Shaf
|
6
6
|
module Generator
|
@@ -44,7 +44,7 @@ module Shaf
|
|
44
44
|
def render(template, locals = {})
|
45
45
|
str = read_template(template)
|
46
46
|
locals[:changes] ||= []
|
47
|
-
b =
|
47
|
+
b = Helper.new(locals).binding
|
48
48
|
|
49
49
|
return ERB.new(str, 0, '%-<>').result(b) if RUBY_VERSION < "2.6.0"
|
50
50
|
ERB.new(str,trim_mode: '-<>').result(b)
|
@@ -16,22 +16,22 @@ module Shaf
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def name
|
19
|
-
n = args.first ||
|
19
|
+
n = args.first || ''
|
20
20
|
return n unless n.empty?
|
21
21
|
raise Command::ArgumentError,
|
22
|
-
|
22
|
+
'Please provide a controller name when using the controller generator!'
|
23
23
|
end
|
24
24
|
|
25
25
|
def model_class_name
|
26
|
-
Utils
|
26
|
+
Utils.model_name(name)
|
27
27
|
end
|
28
28
|
|
29
29
|
def plural_name
|
30
|
-
Utils
|
30
|
+
Utils.pluralize(name)
|
31
31
|
end
|
32
32
|
|
33
33
|
def pluralized_model_name
|
34
|
-
Utils
|
34
|
+
Utils.pluralize(model_class_name)
|
35
35
|
end
|
36
36
|
|
37
37
|
def template
|
@@ -74,16 +74,16 @@ module Shaf
|
|
74
74
|
end
|
75
75
|
|
76
76
|
def add_link_to_root
|
77
|
-
file =
|
77
|
+
file = 'api/serializers/root_serializer.rb'
|
78
78
|
unless File.exist? file
|
79
79
|
puts "Warning: file '#{file}' does not exist. "\
|
80
80
|
"Skip adding link to the #{plural_name} collection"
|
81
81
|
end
|
82
82
|
added = false
|
83
83
|
content = []
|
84
|
-
File.readlines(file).
|
84
|
+
File.readlines(file).reverse_each do |line|
|
85
85
|
if match = !added && line.match(/^(\s*)link /)
|
86
|
-
content.unshift link_content(
|
86
|
+
content.unshift link_content(match[1].to_s)
|
87
87
|
added = true
|
88
88
|
end
|
89
89
|
content.unshift(line)
|
@@ -92,8 +92,8 @@ module Shaf
|
|
92
92
|
puts "Modified: #{file}"
|
93
93
|
end
|
94
94
|
|
95
|
-
def link_content(indentation =
|
96
|
-
<<~
|
95
|
+
def link_content(indentation = '')
|
96
|
+
<<~DOC.split("\n").map { |line| "#{indentation}#{line}" }
|
97
97
|
|
98
98
|
# Auto generated doc:
|
99
99
|
# Link to the collection of #{plural_name}.
|
@@ -105,7 +105,7 @@ module Shaf
|
|
105
105
|
# /#{plural_name}
|
106
106
|
#```
|
107
107
|
link :#{plural_name}, #{plural_name}_uri
|
108
|
-
|
108
|
+
DOC
|
109
109
|
end
|
110
110
|
end
|
111
111
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Shaf
|
4
|
+
module Generator
|
5
|
+
class Helper < OpenStruct
|
6
|
+
# public method mapped to Kernel's private #binding
|
7
|
+
def binding
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def print(lines, indent_level = 2)
|
12
|
+
strip_blank(lines).inject do |result, line|
|
13
|
+
result + "\n#{i(indent_level) unless line.empty?}#{line}"
|
14
|
+
end.chomp
|
15
|
+
end
|
16
|
+
|
17
|
+
def print_nested(sections, indent_level = 2)
|
18
|
+
sections.map(&method(:print)).join("\n\n#{i(indent_level)}")
|
19
|
+
end
|
20
|
+
|
21
|
+
def strip_blank(lines)
|
22
|
+
lines.map do |line|
|
23
|
+
line.strip.empty? ? '' : line
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def indentation(level)
|
28
|
+
' ' * level
|
29
|
+
end
|
30
|
+
alias i indentation
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -11,7 +11,12 @@ module Shaf
|
|
11
11
|
usage { Factory.usage }
|
12
12
|
|
13
13
|
def call
|
14
|
-
generator =
|
14
|
+
generator =
|
15
|
+
if Factory.lookup(*args)
|
16
|
+
Factory.create(*args, **options)
|
17
|
+
else
|
18
|
+
Empty.new(*args, **options)
|
19
|
+
end
|
15
20
|
(target, content) = generator.call
|
16
21
|
write_output(target, content)
|
17
22
|
rescue StandardError => e
|
@@ -4,12 +4,12 @@ module Shaf
|
|
4
4
|
class Empty < Base
|
5
5
|
|
6
6
|
identifier %r(\A\Z)
|
7
|
-
usage 'generate migration'
|
7
|
+
usage 'generate migration [name]'
|
8
8
|
|
9
9
|
def validate_args; end
|
10
10
|
|
11
11
|
def compile_migration_name
|
12
|
-
"empty"
|
12
|
+
args.first || "empty"
|
13
13
|
end
|
14
14
|
|
15
15
|
def compile_changes
|
@@ -22,7 +22,7 @@ module Shaf
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def model_class_name
|
25
|
-
Utils
|
25
|
+
Utils.model_name(name)
|
26
26
|
end
|
27
27
|
|
28
28
|
def policy_class_name
|
@@ -155,7 +155,7 @@ module Shaf
|
|
155
155
|
# #{desc}.
|
156
156
|
# Method: #{method}
|
157
157
|
#{example(method, uri)}
|
158
|
-
link
|
158
|
+
link #{Utils.symbol_string(rel)} do
|
159
159
|
#{uri_helper}
|
160
160
|
end
|
161
161
|
EOS
|
@@ -188,7 +188,7 @@ module Shaf
|
|
188
188
|
curie(:doc) { doc_curie_uri('#{name}') }
|
189
189
|
|
190
190
|
link :self, #{plural_name}_uri
|
191
|
-
link :up, root_uri
|
191
|
+
link :'doc:up', root_uri
|
192
192
|
|
193
193
|
#{create_link.join("\n ")}
|
194
194
|
end
|
@@ -207,7 +207,7 @@ module Shaf
|
|
207
207
|
attributes_with_doc: attributes_with_doc,
|
208
208
|
curies_with_doc: curies_with_doc,
|
209
209
|
links_with_doc: links_with_doc,
|
210
|
-
collection_with_doc: collection_with_doc
|
210
|
+
collection_with_doc: collection_with_doc
|
211
211
|
}
|
212
212
|
end
|
213
213
|
|
@@ -21,7 +21,7 @@ class <%= controller_class_name %> < BaseController
|
|
21
21
|
post :<%= plural_name %>_path do
|
22
22
|
authorize! :write
|
23
23
|
<%= name %> = <%= model_class_name %>.create(<%= name %>_params)
|
24
|
-
headers(
|
24
|
+
headers('Location' => <%= name %>_uri(<%= name %>))
|
25
25
|
respond_with <%= name %>, status: 201
|
26
26
|
end
|
27
27
|
|
@@ -31,7 +31,7 @@ class <%= controller_class_name %> < BaseController
|
|
31
31
|
end
|
32
32
|
|
33
33
|
get :edit_<%= name %>_path do
|
34
|
-
authorize! :
|
34
|
+
authorize! :write
|
35
35
|
cache_control(:private, http_cache_max_age: :short)
|
36
36
|
respond_with edit_form
|
37
37
|
end
|
@@ -49,7 +49,8 @@ class <%= controller_class_name %> < BaseController
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def <%= name %>_params
|
52
|
-
# Generated method
|
52
|
+
# Generated method
|
53
|
+
# TODO: Remove any params that should not be allowed for mass update/create!
|
53
54
|
safe_params(<%= params.map { |p| ":#{p[0]}" }.join(', ') %>)
|
54
55
|
end
|
55
56
|
|