shaf 0.8.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/bin/shaf +19 -4
  5. data/lib/shaf.rb +1 -0
  6. data/lib/shaf/app.rb +7 -10
  7. data/lib/shaf/command/server.rb +4 -2
  8. data/lib/shaf/errors.rb +22 -2
  9. data/lib/shaf/extensions/controller_hooks.rb +14 -0
  10. data/lib/shaf/extensions/resource_uris.rb +1 -1
  11. data/lib/shaf/formable/field.rb +10 -6
  12. data/lib/shaf/formable/form.rb +4 -0
  13. data/lib/shaf/generator/base.rb +2 -2
  14. data/lib/shaf/generator/controller.rb +11 -11
  15. data/lib/shaf/generator/helper.rb +33 -0
  16. data/lib/shaf/generator/migration.rb +6 -1
  17. data/lib/shaf/generator/migration/empty.rb +2 -2
  18. data/lib/shaf/generator/serializer.rb +4 -4
  19. data/lib/shaf/generator/templates/api/controller.rb.erb +4 -3
  20. data/lib/shaf/generator/templates/api/serializer.rb.erb +10 -12
  21. data/lib/shaf/generator/templates/spec/serializer_spec.rb.erb +4 -2
  22. data/lib/shaf/helpers.rb +2 -0
  23. data/lib/shaf/helpers/http_header.rb +26 -0
  24. data/lib/shaf/helpers/paginate.rb +4 -2
  25. data/lib/shaf/helpers/payload.rb +33 -8
  26. data/lib/shaf/middleware/request_id.rb +3 -1
  27. data/lib/shaf/rake/db.rb +5 -5
  28. data/lib/shaf/rake/test.rb +0 -27
  29. data/lib/shaf/router.rb +133 -0
  30. data/lib/shaf/settings.rb +36 -11
  31. data/lib/shaf/spec/http_method_utils.rb +11 -3
  32. data/lib/shaf/spec/integration_spec.rb +4 -4
  33. data/lib/shaf/spec/serializer_spec.rb +1 -1
  34. data/lib/shaf/upgrade/manifest.rb +34 -9
  35. data/lib/shaf/upgrade/package.rb +24 -15
  36. data/lib/shaf/utils.rb +31 -21
  37. data/lib/shaf/version.rb +1 -1
  38. data/templates/Rakefile +27 -0
  39. data/templates/api/controllers/base_controller.rb +13 -8
  40. data/templates/api/serializers/error_serializer.rb +3 -1
  41. data/templates/api/serializers/form_serializer.rb +11 -16
  42. data/templates/api/serializers/validation_error_serializer.rb +13 -0
  43. data/templates/config.ru +1 -1
  44. data/templates/config/bootstrap.rb +3 -1
  45. data/templates/config/database.rb +6 -7
  46. data/templates/config/directories.rb +3 -2
  47. data/templates/config/helpers.rb +1 -1
  48. data/templates/config/initializers/db_migrations.rb +14 -8
  49. data/templates/config/initializers/logging.rb +2 -2
  50. data/templates/config/initializers/sequel.rb +1 -0
  51. data/templates/config/paths.rb +7 -0
  52. data/templates/config/settings.yml +5 -0
  53. data/upgrades/1.0.0.tar.gz +0 -0
  54. metadata +38 -19
  55. metadata.gz.sig +0 -0
  56. 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
- [:get, :put, :patch, :post, :delete, :options, :head, :link, :unlink].each do |m|
7
- define_method m do |*args|
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
- super(*args)
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 |desc, args|
10
- next unless args && args.is_a?(Hash)
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 'X-AUTH-TOKEN', @__integration_test_auth_token
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.instance
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 patch_for(file)
18
- files[:patch].select { |_, pattern| file =~ pattern }.keys
17
+ def patches
18
+ files[:patch]
19
19
  end
20
20
 
21
- def regexp_for(file)
22
- files[:regexp].select { |_, pattern| file =~ pattern }.keys
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
- files[:drop].any? { |pattern| file =~ pattern }
42
+ removals.any? { |pattern| file =~ pattern }
27
43
  end
28
44
 
29
45
  def stats
30
- "Add: #{files[:add].size}, " \
31
- "Del: #{files[:drop].size}, " \
32
- "Patch: #{files[:patch].size}, " \
33
- "Regexp: #{files[:regexp].size}"
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
@@ -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.stats})"
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).all? do |file|
147
- @manifest.patch_for(file).all? do |name|
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
- success = nil
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
- success = t.value.success?
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.files[:add].each do |chksum, filename|
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
- File.unlink(file) if @manifest.drop?(file)
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.regexp_for(file).all? do |name|
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
- pattern = params[:pattern] or return
192
- replacement = params[:replace] or return
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.sub(Regexp.new(pattern), replacement)
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 < 2.5 is dropped
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
@@ -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
- # FIXME!!!
10
- def self.pluralize(noun)
11
- noun + 's' # FIXME!!
12
- end
14
+ class << self
15
+ def model_name(name)
16
+ name.capitalize.gsub(/[_-](\w)/) { $1.upcase }
17
+ end
13
18
 
14
- def self.model_name(name)
15
- name.capitalize.gsub(/[_-](\w)/) { $1.upcase }
16
- end
19
+ def gem_root
20
+ File.expand_path('../..', __dir__)
21
+ end
17
22
 
18
- def self.gem_root
19
- File.expand_path('../../..', __FILE__)
20
- end
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
- # FIXME!!!
23
- def self.singularize(noun)
24
- return singularize(noun.to_s).to_sym if noun.is_a? Symbol
25
- return noun unless noun.end_with? 's'
26
- noun[0..-2]
27
- end
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
- def pluralize(noun)
30
- Utils::pluralize(noun)
31
- end
36
+ def symbolize(str)
37
+ :"#{str}"
38
+ end
32
39
 
33
- def gem_root
34
- self.class.gem_root
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
@@ -1,3 +1,3 @@
1
1
  module Shaf
2
- VERSION = "0.8.0"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -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, VIEWS_DIR
10
+ set :views, Shaf::Settings.views_folder
11
11
  set :static, !production?
12
- set :public_folder, ASSETS_DIR
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, 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::App.use controller
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)
@@ -1,11 +1,13 @@
1
1
  require 'serializers/base_serializer'
2
2
 
3
3
  class ErrorSerializer < BaseSerializer
4
-
5
4
  model Shaf::Errors::ServerError
6
5
 
7
6
  attribute :title
8
7
  attribute :code
9
8
  attribute :message
10
9
 
10
+ link :profile do
11
+ Shaf::Settings.error_profile_uri
12
+ end
11
13
  end
@@ -9,37 +9,32 @@ class FormSerializer < BaseSerializer
9
9
  (options[:method] || resource&.method || 'POST').to_s.upcase
10
10
  end
11
11
 
12
- attribute :name do
13
- options[:name] || resource&.name
14
- end
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
@@ -0,0 +1,13 @@
1
+ class ValidationErrorSerializer < BaseSerializer
2
+ model Shaf::Errors::ValidationError
3
+
4
+ attribute :title
5
+ attribute :code
6
+ attribute :fields do
7
+ resource.fields
8
+ end
9
+
10
+ link :profile do
11
+ Shaf::Settings.error_profile_uri
12
+ end
13
+ end