shaf 0.8.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|