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
@@ -3,10 +3,18 @@ module Shaf
|
|
3
3
|
module HttpUtils
|
4
4
|
include ::Rack::Test::Methods
|
5
5
|
|
6
|
-
[
|
7
|
-
define_method m do
|
6
|
+
%i[get put patch post delete options head link unlink].each do |m|
|
7
|
+
define_method m do |uri, payload = nil, options = {}|
|
8
8
|
set_headers
|
9
|
-
|
9
|
+
|
10
|
+
if payload
|
11
|
+
payload = JSON.generate(payload) if payload.respond_to? :to_h
|
12
|
+
options['CONTENT_TYPE'] ||= 'application/json'
|
13
|
+
super(uri, payload, options)
|
14
|
+
else
|
15
|
+
super(uri, options)
|
16
|
+
end
|
17
|
+
|
10
18
|
set_payload parse_response(last_response.body)
|
11
19
|
end
|
12
20
|
end
|
@@ -6,14 +6,14 @@ module Shaf
|
|
6
6
|
include PayloadUtils
|
7
7
|
include HttpUtils
|
8
8
|
|
9
|
-
register_spec_type self do |
|
10
|
-
next unless args
|
9
|
+
register_spec_type self do |_desc, args|
|
10
|
+
next unless args&.is_a?(Hash)
|
11
11
|
args[:type]&.to_s == 'integration'
|
12
12
|
end
|
13
13
|
|
14
14
|
def set_headers
|
15
15
|
if defined?(@__integration_test_auth_token) && @__integration_test_auth_token
|
16
|
-
header
|
16
|
+
header Settings.auth_token_header, @__integration_test_auth_token
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
@@ -25,7 +25,7 @@ module Shaf
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def app
|
28
|
-
App.
|
28
|
+
App.app
|
29
29
|
end
|
30
30
|
|
31
31
|
def follow_rel(rel, method: nil)
|
@@ -11,7 +11,7 @@ module Shaf
|
|
11
11
|
args[:type]&.to_s == 'serializer'
|
12
12
|
end
|
13
13
|
|
14
|
-
def serialize(resource, current_user:)
|
14
|
+
def serialize(resource, current_user: nil)
|
15
15
|
serializer = __serializer || HALPresenter
|
16
16
|
set_payload serializer.to_hal(resource, current_user: current_user)
|
17
17
|
end
|
@@ -14,23 +14,48 @@ module Shaf
|
|
14
14
|
@files[:regexp] = build_patterns(params[:substitutes])
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
18
|
-
files[:patch]
|
17
|
+
def patches
|
18
|
+
files[:patch]
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
22
|
-
files[:
|
21
|
+
def additions
|
22
|
+
files[:add]
|
23
|
+
end
|
24
|
+
|
25
|
+
def removals
|
26
|
+
files[:drop]
|
27
|
+
end
|
28
|
+
|
29
|
+
def regexps
|
30
|
+
files[:regexp]
|
31
|
+
end
|
32
|
+
|
33
|
+
def patches_for(file)
|
34
|
+
patches.select { |_, pattern| file =~ pattern }.keys
|
35
|
+
end
|
36
|
+
|
37
|
+
def regexps_for(file)
|
38
|
+
regexps.select { |_, pattern| file =~ pattern }.keys
|
23
39
|
end
|
24
40
|
|
25
41
|
def drop?(file)
|
26
|
-
|
42
|
+
removals.any? { |pattern| file =~ pattern }
|
27
43
|
end
|
28
44
|
|
29
45
|
def stats
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
46
|
+
{
|
47
|
+
additions: additions.size,
|
48
|
+
removals: removals.size,
|
49
|
+
patches: patches.size,
|
50
|
+
regexps: regexps.size
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def stats_str
|
55
|
+
"Add: #{additions.size}, " \
|
56
|
+
"Del: #{removals.size}, " \
|
57
|
+
"Patch: #{patches.size}, " \
|
58
|
+
"Regexp: #{regexps.size}"
|
34
59
|
end
|
35
60
|
|
36
61
|
private
|
data/lib/shaf/upgrade/package.rb
CHANGED
@@ -77,15 +77,15 @@ module Shaf
|
|
77
77
|
|
78
78
|
def apply(dir = nil)
|
79
79
|
apply_patches(dir)
|
80
|
-
apply_additions
|
81
80
|
apply_drops(dir)
|
81
|
+
apply_additions
|
82
82
|
apply_substitutes(dir)
|
83
83
|
end
|
84
84
|
|
85
85
|
def to_s
|
86
86
|
str = "Shaf::Upgrade::Package for version #{@version}"
|
87
87
|
return str if @manifest.nil?
|
88
|
-
"#{str} (#{@manifest.
|
88
|
+
"#{str} (#{@manifest.stats_str})"
|
89
89
|
end
|
90
90
|
|
91
91
|
private
|
@@ -143,8 +143,8 @@ module Shaf
|
|
143
143
|
end
|
144
144
|
|
145
145
|
def apply_patches(dir = nil)
|
146
|
-
files_in(dir).
|
147
|
-
@manifest.
|
146
|
+
files_in(dir).each do |file|
|
147
|
+
@manifest.patches_for(file).each do |name|
|
148
148
|
patch = @files[name]
|
149
149
|
apply_patch(file, patch)
|
150
150
|
end
|
@@ -152,35 +152,40 @@ module Shaf
|
|
152
152
|
end
|
153
153
|
|
154
154
|
def apply_patch(file, patch)
|
155
|
-
|
156
|
-
Open3.popen3('patch', file, '-r', '-') do |i, o, e, t|
|
155
|
+
Open3.popen3('patch', file) do |i, o, e, t|
|
157
156
|
i.write patch
|
158
157
|
i.close
|
159
158
|
puts o.read
|
160
159
|
err = e.read
|
161
160
|
puts err unless err.empty?
|
162
|
-
|
161
|
+
next if t.value.success?
|
162
|
+
STDERR.puts "Failed to apply patch for: #{file}\n"
|
163
163
|
end
|
164
|
-
success
|
165
164
|
end
|
166
165
|
|
167
166
|
def apply_additions
|
168
|
-
@manifest.
|
167
|
+
puts '' unless @manifest.additions.empty?
|
168
|
+
@manifest.additions.each do |chksum, filename|
|
169
169
|
content = @files[chksum]
|
170
170
|
FileUtils.mkdir_p File.dirname(filename)
|
171
|
+
puts "adding file: #{filename}"
|
171
172
|
File.open(filename, 'w') { |file| file.write(content) }
|
172
173
|
end
|
173
174
|
end
|
174
175
|
|
175
176
|
def apply_drops(dir = nil)
|
177
|
+
puts '' unless @manifest.removals.empty?
|
176
178
|
files_in(dir).map do |file|
|
177
|
-
|
179
|
+
next unless @manifest.drop?(file)
|
180
|
+
puts "removing file: #{file}"
|
181
|
+
File.unlink(file)
|
178
182
|
end
|
179
183
|
end
|
180
184
|
|
181
185
|
def apply_substitutes(dir = nil)
|
186
|
+
puts '' unless @manifest.regexps.empty?
|
182
187
|
files_in(dir).all? do |file|
|
183
|
-
@manifest.
|
188
|
+
@manifest.regexps_for(file).all? do |name|
|
184
189
|
params = symbolize_keys(YAML.safe_load(@files[name]))
|
185
190
|
apply_substitute(file, params)
|
186
191
|
end
|
@@ -188,18 +193,22 @@ module Shaf
|
|
188
193
|
end
|
189
194
|
|
190
195
|
def apply_substitute(file, params)
|
191
|
-
|
192
|
-
|
196
|
+
return unless params[:pattern] && params[:replace]
|
197
|
+
|
198
|
+
pattern = Regexp.new(params[:pattern])
|
199
|
+
replacement = params[:replace]
|
200
|
+
|
193
201
|
tmp = Tempfile.open do |new_file|
|
194
202
|
File.readlines(file).each do |line|
|
195
|
-
new_file << line.
|
203
|
+
new_file << line.gsub(pattern, replacement)
|
196
204
|
end
|
197
205
|
new_file
|
198
206
|
end
|
207
|
+
|
199
208
|
FileUtils.mv(tmp.path, file)
|
200
209
|
end
|
201
210
|
|
202
|
-
# Refactor this when support for ruby
|
211
|
+
# Refactor this when support for ruby 2.4 is dropped
|
203
212
|
def symbolize_keys(hash)
|
204
213
|
hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
|
205
214
|
end
|
data/lib/shaf/utils.rb
CHANGED
@@ -1,39 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
1
4
|
require 'shaf/version'
|
2
5
|
|
3
6
|
module Shaf
|
4
7
|
module Utils
|
8
|
+
extend Forwardable
|
9
|
+
|
5
10
|
class ProjectRootNotFound < StandardError; end
|
6
11
|
|
7
12
|
SHAF_VERSION_FILE = '.shaf'.freeze
|
8
13
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
14
|
+
class << self
|
15
|
+
def model_name(name)
|
16
|
+
name.capitalize.gsub(/[_-](\w)/) { $1.upcase }
|
17
|
+
end
|
13
18
|
|
14
|
-
|
15
|
-
|
16
|
-
|
19
|
+
def gem_root
|
20
|
+
File.expand_path('../..', __dir__)
|
21
|
+
end
|
17
22
|
|
18
|
-
|
19
|
-
|
20
|
-
|
23
|
+
# FIXME!!!
|
24
|
+
def pluralize(noun)
|
25
|
+
return pluralize(noun.to_s).to_sym if noun.is_a? Symbol
|
26
|
+
noun + 's'
|
27
|
+
end
|
21
28
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
29
|
+
# FIXME!!!
|
30
|
+
def singularize(noun)
|
31
|
+
return singularize(noun.to_s).to_sym if noun.is_a? Symbol
|
32
|
+
return noun unless noun.end_with? 's'
|
33
|
+
noun[0..-2]
|
34
|
+
end
|
28
35
|
|
29
|
-
|
30
|
-
|
31
|
-
|
36
|
+
def symbolize(str)
|
37
|
+
:"#{str}"
|
38
|
+
end
|
32
39
|
|
33
|
-
|
34
|
-
|
40
|
+
def symbol_string(str)
|
41
|
+
symbolize(str).inspect
|
42
|
+
end
|
35
43
|
end
|
36
44
|
|
45
|
+
def_delegators Utils, :pluralize, :singularize, :symbolize, :symbol_string, :gem_root
|
46
|
+
|
37
47
|
def project_root
|
38
48
|
return @project_root if defined? @project_root
|
39
49
|
dir = Dir.pwd
|
data/lib/shaf/version.rb
CHANGED
data/templates/Rakefile
CHANGED
@@ -10,3 +10,30 @@ Shaf::ApiDocTask.new do |api_doc|
|
|
10
10
|
api_doc.html_output_dir = File.join(Shaf::Settings.public_folder, "doc")
|
11
11
|
api_doc.yaml_output_dir = Shaf::Settings.documents_dir || "doc/api"
|
12
12
|
end
|
13
|
+
|
14
|
+
namespace :test do
|
15
|
+
Shaf::TestTask.new(:integration) do |t|
|
16
|
+
t.pattern = "spec/integration/**/*_spec.rb"
|
17
|
+
end
|
18
|
+
|
19
|
+
Shaf::TestTask.new(:models) do |t|
|
20
|
+
t.pattern = "spec/models/**/*_spec.rb"
|
21
|
+
end
|
22
|
+
|
23
|
+
Shaf::TestTask.new(:serializers) do |t|
|
24
|
+
t.pattern = "spec/serializers/**/*_spec.rb"
|
25
|
+
end
|
26
|
+
|
27
|
+
Shaf::TestTask.new(:lib) do |t|
|
28
|
+
t.pattern = "spec/lib/**/*_spec.rb"
|
29
|
+
end
|
30
|
+
|
31
|
+
Shaf::TestTask.new(:all) do |t|
|
32
|
+
t.pattern = [
|
33
|
+
"spec/lib/**/*_spec.rb",
|
34
|
+
"spec/models/**/*_spec.rb",
|
35
|
+
"spec/serializers/**/*_spec.rb",
|
36
|
+
"spec/integration/**/*_spec.rb"
|
37
|
+
]
|
38
|
+
end
|
39
|
+
end
|
@@ -7,20 +7,22 @@ class BaseController < Sinatra::Base
|
|
7
7
|
enable :logging
|
8
8
|
enable :method_override
|
9
9
|
mime_type :hal, 'application/hal+json'
|
10
|
-
set :views,
|
10
|
+
set :views, Shaf::Settings.views_folder
|
11
11
|
set :static, !production?
|
12
|
-
set :public_folder,
|
12
|
+
set :public_folder, Shaf::Settings.public_folder
|
13
13
|
disable :dump_errors
|
14
14
|
set :show_exceptions, :after_handler
|
15
15
|
enable :current_user
|
16
|
-
set :auth_token_header,
|
16
|
+
set :auth_token_header, Shaf::Settings.auth_token_header
|
17
17
|
end
|
18
18
|
|
19
19
|
use Rack::Deflater
|
20
20
|
|
21
|
+
Shaf::Router.mount(self, default: true)
|
22
|
+
|
21
23
|
def self.inherited(controller)
|
22
24
|
super
|
23
|
-
Shaf::
|
25
|
+
Shaf::Router.mount controller
|
24
26
|
end
|
25
27
|
|
26
28
|
def self.log
|
@@ -39,16 +41,19 @@ class BaseController < Sinatra::Base
|
|
39
41
|
log.debug "Payload: #{payload || 'empty'}"
|
40
42
|
end
|
41
43
|
|
44
|
+
not_found do
|
45
|
+
err = NotFoundError.new "Resource \"#{request.path_info}\" does not exist"
|
46
|
+
respond_with(err, status: err.http_status)
|
47
|
+
end
|
48
|
+
|
42
49
|
error StandardError do
|
43
50
|
err = env['sinatra.error']
|
44
51
|
log.error err.message
|
45
|
-
err.backtrace.each(&log.method(:error))
|
52
|
+
Array(err.backtrace).each(&log.method(:error))
|
46
53
|
|
47
54
|
api_error = to_api_error(err)
|
48
55
|
|
49
|
-
respond_with api_error
|
50
|
-
status: api_error.http_status,
|
51
|
-
serializer: ErrorSerializer
|
56
|
+
respond_with(api_error, status: api_error.http_status)
|
52
57
|
end
|
53
58
|
|
54
59
|
def to_api_error(err)
|
@@ -9,37 +9,32 @@ class FormSerializer < BaseSerializer
|
|
9
9
|
(options[:method] || resource&.method || 'POST').to_s.upcase
|
10
10
|
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
attribute :title do
|
17
|
-
options[:title] || resource&.title
|
18
|
-
end
|
19
|
-
|
20
|
-
attribute :href do
|
21
|
-
options[:href] || resource&.href
|
22
|
-
end
|
23
|
-
|
24
|
-
attribute :type do
|
25
|
-
options[:type] || resource&.type
|
12
|
+
%i[name title href type submit].each do |sym|
|
13
|
+
attribute sym do
|
14
|
+
options[sym] || resource&.public_send(sym)
|
15
|
+
end
|
26
16
|
end
|
27
17
|
|
28
18
|
link :self do
|
29
19
|
options[:self_link] || resource&.self_link
|
30
20
|
end
|
31
21
|
|
22
|
+
link :profile do
|
23
|
+
Shaf::Settings.form_profile_uri
|
24
|
+
end
|
25
|
+
|
32
26
|
post_serialize do |hash|
|
33
27
|
fields = resource&.fields
|
34
28
|
break if fields.nil? || fields.empty?
|
35
29
|
hash[:fields] = fields.map do |field|
|
36
30
|
{
|
37
31
|
name: field.name,
|
38
|
-
type: field.type
|
39
|
-
label: field.label,
|
32
|
+
type: field.type
|
40
33
|
}.tap do |f|
|
34
|
+
f[:title] = field.title if field.title
|
41
35
|
f[:value] = field.value if field.has_value?
|
42
36
|
f[:required] = true if field.required
|
37
|
+
f[:hidden] = true if field.hidden
|
43
38
|
end
|
44
39
|
end
|
45
40
|
end
|