shaf 1.0.4 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ec94396d12fceb99bdb58bcb8a11ec6851b730cd2dad7c1dd19b320f340dd48b
4
- data.tar.gz: edab56921f648eeaa6532499a7b4892e86bc493fcbb7513d8736b6b0cdc5d7c3
3
+ metadata.gz: f71cd9b7d938bb9cf8c6cabb87dc85bd912052e371884ac253ec20afe8657c90
4
+ data.tar.gz: e09e82d1f026f3d00e9a849b7ff65c04479940838f0f45bbdfa644ecdc47ef81
5
5
  SHA512:
6
- metadata.gz: b1f4ac6773adebc04da603a4c7e6b9e6cde1c1ba5bbe547e526f42cc88a178942a11f07a14abe6206c5b3de7a36e59c58c6173a9e388d6512bb08a0da71b45d1
7
- data.tar.gz: 4d14203e911733e0ba30b769ed5e37204c225261649da5eed9c80bf4574af9e1e25e99c1c7739771340cecd6b53484fc5210b5be4e8b799be88237646c4acacf
6
+ metadata.gz: 4140819946300e345ad2898212241275017c57f586c5dc47d51872047e2a9d4dd48e2d5e06662e83b98ba8963b6c7e87d69d7cae0fe4e79b11ecf0639f739d42
7
+ data.tar.gz: 3a75893536dd2a17edcdacb8aaba291baa0ecbebc33d2b16b79015d80f922de0f3af79264f87b8acdf0c5b98f6285c6289d2e1e9db419e62cc7af5341620d7e0
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -11,9 +11,10 @@ module Shaf
11
11
  end
12
12
 
13
13
  require 'shaf/command/base'
14
- require 'shaf/command/new'
15
- require 'shaf/command/upgrade'
16
- require 'shaf/command/server'
17
14
  require 'shaf/command/console'
18
15
  require 'shaf/command/generate'
16
+ require 'shaf/command/new'
17
+ require 'shaf/command/server'
18
+ require 'shaf/command/test'
19
+ require 'shaf/command/upgrade'
19
20
  require 'shaf/command/version'
@@ -41,11 +41,16 @@ module Shaf
41
41
 
42
42
  def parse_options!
43
43
  parser = OptionParser.new
44
- self.class.options(parser, @options)
44
+ common_options(parser, options)
45
+ self.class.options(parser, options)
45
46
  parser.parse!(args)
46
47
  rescue OptionParser::InvalidOption => e
47
48
  raise ArgumentError, e.message
48
49
  end
50
+
51
+ def common_options(parser, _options)
52
+ parser.on('--trace', 'Show backtrace on command failure')
53
+ end
49
54
  end
50
55
  end
51
56
  end
@@ -0,0 +1,134 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ require 'set'
4
+ Bundler.require :development, :test
5
+ require 'shaf/command/test/filter'
6
+ require 'shaf/command/test/runner'
7
+ require 'shaf/command/test/runnable_method'
8
+
9
+ module Shaf
10
+ module Command
11
+ class Test < Base
12
+ identifier %r{\A(t(est)?|spec)\Z}
13
+ usage 'test [FILTER] [..]'
14
+
15
+ def call
16
+ disable_autorun
17
+
18
+ bootstrap(env: 'test') do
19
+ setup_loadpath
20
+ reporter.start
21
+
22
+ spec_files.each do |file|
23
+ lines = lines_to_run(file)
24
+ run(file, lines) if lines
25
+ end
26
+
27
+ reporter.report
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def setup_loadpath
34
+ $LOAD_PATH.unshift(spec_dir) unless $LOAD_PATH.include?(spec_dir)
35
+ end
36
+
37
+ def disable_autorun
38
+ # This makes sure this at_exit handler is registered after minitest/autorun
39
+ # This will make it run before the at_exit handler in minitest/autorun
40
+ require 'minitest/autorun'
41
+ at_exit do
42
+ exit! reporter.passed?
43
+ end
44
+ end
45
+
46
+ def spec_dir
47
+ @spec_dir ||= Settings.spec_dir || 'spec'
48
+ end
49
+
50
+ def spec_files
51
+ Dir["#{spec_dir}/**/*.rb"]
52
+ end
53
+
54
+ def filters
55
+ @filters ||=
56
+ if args.empty?
57
+ [Filter::None]
58
+ else
59
+ args.map { |arg| Filter.new(arg) }
60
+ end
61
+ end
62
+
63
+ def lines_to_run(file)
64
+ lines = filters
65
+ .select { |f| f.match? file }
66
+ .map(&:lines)
67
+
68
+ return if lines.empty?
69
+ return [] if lines.any?(&:empty?)
70
+ lines.flatten.to_set
71
+ end
72
+
73
+ def run(file, lines = [])
74
+ runners(file, lines).each do |runner|
75
+ runner.call(reporter)
76
+ end
77
+ end
78
+
79
+ def reporter
80
+ @reporter ||= Minitest::CompositeReporter.new.tap do |reporter|
81
+ reporter << Minitest::SummaryReporter.new($stdout)
82
+ reporter << Minitest::ProgressReporter.new($stdout)
83
+ end
84
+ end
85
+
86
+ def runners(file, lines)
87
+ runnables = runnables_in(file)
88
+
89
+ return runnables.map { |r| Runner.new r } if lines.empty?
90
+
91
+ methods = methods_for(runnables)
92
+
93
+ lines.each_with_object([]) do |line, runners|
94
+ if methods.empty? || line < methods.first.line
95
+ runnables.map { |r| runners << Runner.new(r) }
96
+ else
97
+ spec = methods.partition { |m| m.line < line }.first.last or next
98
+ runners << Runner.new(spec.runnable, spec.name)
99
+ end
100
+ end
101
+ end
102
+
103
+ def runnables_in(file)
104
+ require file
105
+
106
+ @runnables ||= Set.new
107
+
108
+ Minitest::Runnable.runnables.each_with_object([]) do |runnable, loaded|
109
+ next unless runnable.runnable_methods.any?
110
+ next if @runnables.include? runnable
111
+ @runnables << runnable
112
+ loaded << runnable
113
+ end
114
+ end
115
+
116
+ def methods_for(runnables)
117
+ methods = []
118
+
119
+ runnables.each do |runnable|
120
+ runnable.runnable_methods.each do |name|
121
+ methods << RunnableMethod.from(runnable, name)
122
+ end
123
+ end
124
+
125
+ methods.sort_by { |m| m.line }
126
+ end
127
+
128
+ def relative_to_root(file)
129
+ file = File.expand_path(file)
130
+ file.sub(File.join(Settings.app_root, ''), '')
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shaf
4
+ module Command
5
+ class Test < Base
6
+ class Filter
7
+ attr_reader :pattern, :lines
8
+
9
+ def initialize(criteria)
10
+ pattern, *lines = criteria.split(':')
11
+ @lines = lines.map(&:to_i)
12
+ @pattern = Regexp.new(pattern)
13
+ end
14
+
15
+ def match?(file)
16
+ pattern.match? file
17
+ end
18
+ end
19
+
20
+ Filter::None = Filter.new('.*.rb')
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shaf
4
+ module Command
5
+ class Test < Base
6
+ class RunnableMethod
7
+ attr_reader :runnable, :name, :line
8
+
9
+ def self.from(runnable, method_name)
10
+ _, line = runnable.instance_method(method_name).source_location
11
+ new(runnable, method_name, line)
12
+ end
13
+
14
+ def initialize(runnable, name, line)
15
+ @runnable = runnable
16
+ @name = name
17
+ @line = line
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+ module Shaf
3
+ module Command
4
+ class Test < Base
5
+ class Runner
6
+ attr_reader :runnable, :method_name
7
+
8
+ def initialize(runnable, method_name = nil)
9
+ @runnable = runnable
10
+ @method_name = method_name
11
+ end
12
+
13
+ def call(reporter)
14
+ runnable.run(reporter, options)
15
+ end
16
+
17
+ def options
18
+ return {} unless method_name
19
+
20
+ {filter: method_name}
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -22,7 +22,7 @@ module Shaf
22
22
  end
23
23
 
24
24
  def initialize(msg = nil)
25
- msg ||= "The request cannot be understood"
25
+ msg ||= "The request could not be understood"
26
26
  super(msg, code: "INVALID_REQUEST", title: "Invalid request")
27
27
  end
28
28
  end
@@ -40,8 +40,7 @@ module Shaf
40
40
 
41
41
  def __method_for(action)
42
42
  return action if action.to_s.end_with? '?'
43
- "#{action}?".to_sym
43
+ :"#{action}?"
44
44
  end
45
45
  end
46
-
47
46
  end
@@ -19,7 +19,7 @@ module Shaf
19
19
  'User model with a column auth_token_digest'.freeze
20
20
 
21
21
  def auth_token
22
- header = settings.auth_token_header
22
+ header = Utils.rackify_header(settings.auth_token_header)
23
23
  request.env[header]
24
24
  end
25
25
 
@@ -2,22 +2,47 @@ require 'shaf/formable/builder'
2
2
 
3
3
  module Shaf
4
4
  module Formable
5
+ def self.add_class_reader(clazz, name, form)
6
+ clazz.define_singleton_method(name) { form }
7
+ end
8
+
9
+ def self.add_instance_reader(clazz, name, form, prefill)
10
+ clazz.define_method(name) do
11
+ form.tap do |f|
12
+ f.resource = self
13
+ f.fill! if prefill
14
+ end
15
+ end
16
+ end
17
+
18
+ # Deprecated legacy way of specifying forms inside models
5
19
  def form(&block)
20
+ forms_for(self, &block)
21
+ return unless defined? $logger
22
+
23
+ $logger.info <<~MSG
24
+
25
+
26
+ DEPRECATED method ::form in #{self}
27
+ Declare forms in a separate class extending Shaf::Formable with the class method forms_for!
28
+ MSG
29
+ end
30
+
31
+ # New way of writing forms in a separate class/file
32
+ def forms_for(clazz, &block)
6
33
  builder = Formable::Builder.new(&block)
7
- builder.forms.each do |f|
8
- next unless f.action
9
- getter = "#{f.action}_form"
10
-
11
- define_singleton_method(getter) { f }
12
- next unless instance_accessor = builder.instance_accessor_for(f)
13
-
14
- define_method(getter) do
15
- f.dup.tap do |fm|
16
- fm.resource = self
17
- fm.fill! if instance_accessor.prefill?
18
- end
34
+ builder.forms.each do |form|
35
+ next unless form.action
36
+ method_name = "#{form.action}_form"
37
+
38
+ Formable.add_class_reader(clazz, method_name, form.dup)
39
+
40
+ if instance_accessor = builder.instance_accessor_for(form)
41
+ prefill_form = instance_accessor.prefill?
42
+ Formable.add_instance_reader(clazz, method_name, form.dup, prefill_form)
19
43
  end
20
44
  end
21
45
  end
46
+ alias form_for forms_for
22
47
  end
23
48
  end
@@ -10,6 +10,7 @@ end
10
10
 
11
11
  require 'shaf/generator/base'
12
12
  require 'shaf/generator/controller'
13
+ require 'shaf/generator/forms'
13
14
  require 'shaf/generator/migration'
14
15
  require 'shaf/generator/model'
15
16
  require 'shaf/generator/policy'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fileutils'
2
4
  require 'erb'
3
5
  require 'shaf/generator/helper'
@@ -31,13 +33,13 @@ module Shaf
31
33
  def call; end
32
34
 
33
35
  def template_dir
34
- File.expand_path('../templates', __FILE__)
36
+ File.expand_path('templates', __dir__)
35
37
  end
36
38
 
37
39
  def read_template(file, directory = nil)
38
40
  directory ||= template_dir
39
41
  filename = File.join(directory, file)
40
- filename << ".erb" unless filename.end_with?(".erb")
42
+ filename << '.erb' unless filename.end_with?('.erb')
41
43
  File.read(filename)
42
44
  end
43
45
 
@@ -46,8 +48,8 @@ module Shaf
46
48
  locals[:changes] ||= []
47
49
  b = Helper.new(locals).binding
48
50
 
49
- return ERB.new(str, 0, '%-<>').result(b) if RUBY_VERSION < "2.6.0"
50
- ERB.new(str,trim_mode: '-<>').result(b)
51
+ return ERB.new(str, 0, '%-<>').result(b) if RUBY_VERSION < '2.6.0'
52
+ ERB.new(str, trim_mode: '-<>').result(b)
51
53
  rescue SystemCallError => e
52
54
  puts "Failed to render template #{template}: #{e.message}"
53
55
  raise
@@ -0,0 +1,68 @@
1
+ module Shaf
2
+ module Generator
3
+ class Forms < Base
4
+ identifier :forms
5
+ usage 'generate forms MODEL_NAME [attribute:type[:label]] [..]'
6
+
7
+ def call
8
+ create_forms
9
+ end
10
+
11
+ def model_name
12
+ n = args.first || ''
13
+ return n unless n.empty?
14
+ raise Command::ArgumentError,
15
+ 'Please provide a model name when using the forms generator!'
16
+ end
17
+
18
+ def model_class_name
19
+ Utils.model_name(model_name)
20
+ end
21
+
22
+ def class_name
23
+ "#{model_class_name}Forms"
24
+ end
25
+
26
+ def template
27
+ 'api/forms.rb'
28
+ end
29
+
30
+ def target
31
+ "api/forms/#{model_name}_forms.rb"
32
+ end
33
+
34
+ def create_forms
35
+ content = render(template, opts)
36
+ write_output(target, content)
37
+ end
38
+
39
+ def opts
40
+ {
41
+ model_name: model_name,
42
+ class_name: class_name,
43
+ model_class_name: model_class_name,
44
+ fields: fields
45
+ }
46
+ end
47
+
48
+ def fields
49
+ args[1..-1].map do |f|
50
+ (name, type, label) = f.split(':')
51
+ label_str = label ? %(, label: "#{label}") : ''
52
+ format 'field :%s, type: "%s"%s' % [name, rewrite(type), label_str]
53
+ end
54
+ end
55
+
56
+ def rewrite(type)
57
+ case type
58
+ when /foreign_key/
59
+ 'integer'
60
+ when NilClass
61
+ 'string'
62
+ else
63
+ type
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -9,21 +9,22 @@ module Shaf
9
9
  create_model
10
10
  create_migration
11
11
  create_serializer
12
+ create_forms
12
13
  end
13
14
 
14
15
  def model_name
15
- n = args.first || ""
16
+ n = args.first || ''
16
17
  return n unless n.empty?
17
18
  raise Command::ArgumentError,
18
- "Please provide a model name when using the model generator!"
19
+ 'Please provide a model name when using the model generator!'
19
20
  end
20
21
 
21
22
  def model_class_name
22
- Utils::model_name(model_name)
23
+ Utils.model_name(model_name)
23
24
  end
24
25
 
25
26
  def table_name
26
- Utils::pluralize model_name
27
+ Utils.pluralize model_name
27
28
  end
28
29
 
29
30
  def template
@@ -39,30 +40,9 @@ module Shaf
39
40
  write_output(target, content)
40
41
  end
41
42
 
42
- def form_fields
43
- args[1..-1].map do |f|
44
- (name, type, label) = f.split(':')
45
- label_str = label ? %(, label: "#{label}") : ''
46
- format 'field :%s, type: "%s"%s' % [name, rewrite(type), label_str]
47
- end
48
- end
49
-
50
- def rewrite(type)
51
- case type
52
- when /foreign_key/
53
- 'integer'
54
- when NilClass
55
- 'string'
56
- else
57
- type
58
- end
59
- end
60
-
61
43
  def opts
62
44
  {
63
- model_name: model_name,
64
45
  class_name: model_class_name,
65
- form_fields: form_fields
66
46
  }
67
47
  end
68
48
 
@@ -76,6 +56,11 @@ module Shaf
76
56
  serializer_args += args[1..-1].map { |arg| arg.split(':').first }
77
57
  Generator::Factory.create(*serializer_args, **options).call
78
58
  end
59
+
60
+ def create_forms
61
+ form_args = %W(forms #{model_name}) + args[1..-1]
62
+ Generator::Factory.create(*form_args, **options).call
63
+ end
79
64
  end
80
65
  end
81
66
  end
@@ -162,21 +162,21 @@ module Shaf
162
162
  end
163
163
 
164
164
  def example(method, uri)
165
- method_args = ""
165
+ curl_args = +'-H "Authorization: abcdef \\"'
166
166
  case method
167
167
  when "POST"
168
- method_args = "\n# -d@payload \\"
168
+ curl_args << "\n# -d@payload \\"
169
169
  when "PUT"
170
- method_args = "\n# -X PUT -d@payload \\"
170
+ curl_args << "\n# -X PUT -d@payload \\"
171
171
  when "DELETE"
172
- method_args = "\n# -X DELETE \\"
172
+ curl_args << "\n# -X DELETE \\"
173
173
  end
174
174
 
175
175
  <<~EOS.chomp
176
176
  # Example:
177
177
  # ```
178
178
  # curl -H "Accept: application/hal+json" \\
179
- # -H "Authorization: abcdef" \\#{method_args}
179
+ # #{curl_args}
180
180
  # #{uri}
181
181
  #```
182
182
  EOS
@@ -0,0 +1,19 @@
1
+ class <%= class_name %>
2
+ extend Shaf::Formable
3
+
4
+ forms_for(<%= model_class_name %>) do
5
+ <%= fields.join("\n ") %>
6
+
7
+ create do
8
+ title 'Create <%= model_class_name %>'
9
+ name 'create-<%= model_name %>'
10
+ end
11
+
12
+ edit do
13
+ instance_accessor
14
+ title 'Update <%= model_class_name %>'
15
+ name 'update-<%= model_name %>'
16
+ end
17
+ end
18
+ end
19
+
@@ -1,21 +1,4 @@
1
1
  class <%= class_name %> < Sequel::Model
2
- extend Shaf::Formable
3
2
 
4
- <% if form_fields.any? %>
5
- form do
6
- <%= form_fields.join("\n ") %>
7
-
8
- create do
9
- title 'Create <%= class_name %>'
10
- name 'create-<%= model_name %>'
11
- end
12
-
13
- edit do
14
- instance_accessor
15
- title 'Update <%= class_name %>'
16
- name 'update-<%= model_name %>'
17
- end
18
- end
19
- <% end %>
20
3
  end
21
4
 
@@ -26,18 +26,11 @@ module Shaf
26
26
  private
27
27
 
28
28
  def payload
29
- @@request_id ||= nil
30
- @@payload ||= nil
31
-
32
- if @@request_id != request.env["REQUEST_ID"]
33
- @@request_id = request.env["REQUEST_ID"]
34
- @@payload = parse_payload
35
- end
36
- @@payload
29
+ @payload ||= parse_payload
37
30
  end
38
31
 
39
32
  def read_input
40
- request.body.rewind unless request.body.pos == 0
33
+ request.body.rewind unless request.body.pos.zero?
41
34
  request.body.read
42
35
  ensure
43
36
  request.body.rewind
@@ -45,19 +38,27 @@ module Shaf
45
38
 
46
39
  def parse_payload
47
40
  if request.env['CONTENT_TYPE'] == 'application/x-www-form-urlencoded'
48
- return params.reject { |key,_| EXCLUDED_FORM_PARAMS.include? key }
41
+ return params.reject { |key, _| EXCLUDED_FORM_PARAMS.include? key }
49
42
  end
50
43
 
51
44
  input = read_input
52
45
  return {} if input.empty?
53
46
 
54
- if request.env['CONTENT_TYPE'] =~ %r(\Aapplication/(hal\+)?json)
55
- JSON.parse(input, symbolize_names: true)
56
- else
57
- raise Errors::UnsupportedMediaTypeError.new(request: request)
58
- end
59
- rescue StandardError
60
- raise Errors::BadRequestError.new
47
+ raise raise_unsupported_media_type_error(request) unless suported_media_type?
48
+
49
+ JSON.parse(input, symbolize_names: true)
50
+ rescue Errors::UnsupportedMediaTypeError
51
+ raise
52
+ rescue StandardError => e
53
+ raise Errors::BadRequestError, "Failed to parse input payload: #{e.message}"
54
+ end
55
+
56
+ def suported_media_type?
57
+ request.env['CONTENT_TYPE'].match? %r{\Aapplication/(hal\+)?json}
58
+ end
59
+
60
+ def raise_unsupported_media_type_error(request)
61
+ raise Errors::UnsupportedMediaTypeError.new(request: request)
61
62
  end
62
63
 
63
64
  def safe_params(*fields)
@@ -66,16 +67,14 @@ module Shaf
66
67
  fields = fields.map { |f| f.to_sym.downcase }.to_set
67
68
  fields << :id
68
69
 
69
- {}.tap do |allowed|
70
- fields.each do |f|
71
- allowed[f] = payload[f] if payload.key? f
72
- allowed[f] ||= payload[f.to_s] if payload.key? f.to_s
73
- end
70
+ fields.each_with_object({}) do |f, allowed|
71
+ allowed[f] = payload[f] if payload.key? f
72
+ allowed[f] ||= payload[f.to_s] if payload.key? f.to_s
74
73
  end
75
74
  end
76
75
 
77
76
  def ignore_form_input?(name)
78
- return name == '_method'
77
+ name == '_method'
79
78
  end
80
79
 
81
80
  def profile(value = nil)
@@ -99,6 +98,7 @@ module Shaf
99
98
  preferred_response = preferred_response_type(resource)
100
99
  http_cache = kwargs.delete(:http_cache) { Settings.http_cache }
101
100
 
101
+ serializer ||= HALPresenter.lookup_presenter(resource)
102
102
  serialized = serialize(resource, serializer, collection, **kwargs)
103
103
  add_cache_headers(serialized) if http_cache
104
104
 
@@ -107,12 +107,11 @@ module Shaf
107
107
  if preferred_response == mime_type(:html)
108
108
  respond_with_html(resource, serialized)
109
109
  else
110
- respond_with_hal(resource, serialized)
110
+ respond_with_hal(resource, serialized, serializer)
111
111
  end
112
112
  end
113
113
 
114
114
  def serialize(resource, serializer, collection, **options)
115
- serializer ||= HALPresenter
116
115
  if collection
117
116
  serializer.to_collection(resource, current_user: current_user, **options)
118
117
  else
@@ -120,9 +119,9 @@ module Shaf
120
119
  end
121
120
  end
122
121
 
123
- def respond_with_hal(resource, serialized)
122
+ def respond_with_hal(resource, serialized, serializer)
124
123
  log.debug "Response payload (#{resource.class}): #{serialized}"
125
- content_type :hal, content_type_params(resource)
124
+ content_type :hal, content_type_params(serializer)
126
125
  body serialized
127
126
  end
128
127
 
@@ -144,18 +143,10 @@ module Shaf
144
143
  etag sha1, :weak # Weak or Strong??
145
144
  end
146
145
 
147
- def content_type_params(resource)
146
+ def content_type_params(serializer)
148
147
  return {profile: profile} if profile
149
148
 
150
- name =
151
- case resource
152
- when Formable::Form
153
- Shaf::Settings.form_profile_name
154
- when Errors::ServerError
155
- Shaf::Settings.error_profile_name
156
- end
157
-
158
- {profile: name}.compact
149
+ {profile: serializer.semantic_profile}.compact
159
150
  end
160
151
  end
161
152
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'yaml'
4
+ require 'shaf/utils'
4
5
 
5
6
  module Shaf
6
7
  class Settings
@@ -26,12 +27,8 @@ module Shaf
26
27
  return {} unless File.exist? file
27
28
 
28
29
  yaml = File.read(file)
29
- if RUBY_VERSION < '2.5.0'
30
- YAML.safe_load(yaml, [], [], true).each_with_object({}) do |(k, v), hash|
31
- hash[k.to_sym] = v
32
- end
33
- elsif RUBY_VERSION < '2.6.0'
34
- YAML.safe_load(yaml, [], [], true).transform_keys(&:to_sym)
30
+ if RUBY_VERSION < '2.6.0'
31
+ Utils.deep_symbolize_keys(YAML.safe_load(yaml, [], [], true))
35
32
  else
36
33
  YAML.safe_load(yaml, aliases: true, symbolize_names: true)
37
34
  end
@@ -48,20 +48,24 @@ module Shaf
48
48
  end
49
49
  end
50
50
 
51
- def fill_form(fields)
52
- fields.map do |field|
53
- value = case field[:type]
54
- when 'integer'
55
- field[:name].size
56
- when 'string'
57
- "value for #{field[:name]}"
58
- when 'boolean'
59
- true
60
- else
61
- "type not supported"
62
- end
63
- [field[:name], value]
64
- end.to_h
51
+ def fill_form(fields, opts = {})
52
+ fields.each_with_object({}) do |field, payload|
53
+ key = field[:name]
54
+ payload[key] = opts.fetch(key, default_field_value(field))
55
+ end
56
+ end
57
+
58
+ def default_field_value(field)
59
+ case field[:type]
60
+ when 'integer'
61
+ field[:name].size
62
+ when 'string'
63
+ "value for #{field[:name]}"
64
+ when 'boolean'
65
+ true
66
+ else
67
+ 'type not supported'
68
+ end
65
69
  end
66
70
 
67
71
  def assert_status(code)
@@ -186,7 +186,8 @@ module Shaf
186
186
  puts '' unless @manifest.regexps.empty?
187
187
  files_in(dir).all? do |file|
188
188
  @manifest.regexps_for(file).all? do |name|
189
- params = symbolize_keys(YAML.safe_load(@files[name]))
189
+ params = YAML.safe_load(@files[name])
190
+ params.transform_keys!(&:to_sym)
190
191
  apply_substitute(file, params)
191
192
  end
192
193
  end
@@ -207,11 +208,6 @@ module Shaf
207
208
 
208
209
  FileUtils.mv(tmp.path, file)
209
210
  end
210
-
211
- # Refactor this when support for ruby 2.4 is dropped
212
- def symbolize_keys(hash)
213
- hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
214
- end
215
211
  end
216
212
  end
217
213
  end
@@ -40,9 +40,29 @@ module Shaf
40
40
  def symbol_string(str)
41
41
  symbolize(str).inspect
42
42
  end
43
+
44
+ def rackify_header(str)
45
+ return if str.nil?
46
+ str.upcase.tr('-', '_').tap do |key|
47
+ key.prepend('HTTP_') unless key.start_with? 'HTTP_'
48
+ end
49
+ end
50
+
51
+ def deep_symbolize_keys(value)
52
+ case value
53
+ when Hash
54
+ value.each_with_object({}) do |(k, v), h|
55
+ h[k.to_sym] = deep_symbolize_keys(v)
56
+ end
57
+ when Array
58
+ value.map { |v| deep_symbolize_keys(v) }
59
+ else
60
+ value
61
+ end
62
+ end
43
63
  end
44
64
 
45
- def_delegators Utils, :pluralize, :singularize, :symbolize, :symbol_string, :gem_root
65
+ def_delegators Utils, :pluralize, :singularize, :symbolize, :symbol_string, :gem_root, :rackify_header
46
66
 
47
67
  def project_root
48
68
  return @project_root if defined? @project_root
@@ -73,9 +93,9 @@ module Shaf
73
93
  end
74
94
  end
75
95
 
76
- def bootstrap
96
+ def bootstrap(env: 'development')
77
97
  in_project_root do
78
- ENV['RACK_ENV'] ||= 'development'
98
+ ENV['RACK_ENV'] ||= env
79
99
  require 'config/bootstrap'
80
100
  yield if block_given?
81
101
  end
@@ -1,3 +1,3 @@
1
1
  module Shaf
2
- VERSION = "1.0.4"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -2,6 +2,7 @@ require 'serializers/base_serializer'
2
2
 
3
3
  class ErrorSerializer < BaseSerializer
4
4
  model Shaf::Errors::ServerError
5
+ profile Shaf::Settings.error_profile_name
5
6
 
6
7
  attribute :title
7
8
  attribute :code
@@ -2,8 +2,8 @@ require 'serializers/base_serializer'
2
2
  require 'shaf/formable'
3
3
 
4
4
  class FormSerializer < BaseSerializer
5
-
6
5
  model Shaf::Formable::Form
6
+ profile Shaf::Settings.form_profile_name
7
7
 
8
8
  attribute :method do
9
9
  (options[:method] || resource&.method || 'POST').to_s.upcase
@@ -25,7 +25,7 @@ class FormSerializer < BaseSerializer
25
25
 
26
26
  post_serialize do |hash|
27
27
  fields = resource&.fields
28
- break if fields.nil? || fields.empty?
28
+ next if fields.nil? || fields.empty?
29
29
  hash[:fields] = fields.map do |field|
30
30
  {
31
31
  name: field.name,
@@ -1,5 +1,6 @@
1
1
  class ValidationErrorSerializer < BaseSerializer
2
2
  model Shaf::Errors::ValidationError
3
+ profile Shaf::Settings.error_profile_name
3
4
 
4
5
  attribute :title
5
6
  attribute :code
@@ -5,3 +5,4 @@ Shaf::Settings.app_root = app_root
5
5
  Shaf::Settings.app_dir = File.expand_path('api', app_root)
6
6
  Shaf::Settings.src_dir = File.expand_path('src', app_root)
7
7
  Shaf::Settings.lib_dir = File.expand_path('lib', app_root)
8
+ Shaf::Settings.spec_dir = File.expand_path('spec', app_root)
Binary file
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: 1.0.4
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sammy Henningsson
@@ -30,7 +30,7 @@ cert_chain:
30
30
  ZMhjYR7sRczGJx+GxGU2EaR0bjRsPVlC4ywtFxoOfRG3WaJcpWGEoAoMJX6Z0bRv
31
31
  M40=
32
32
  -----END CERTIFICATE-----
33
- date: 2019-05-04 00:00:00.000000000 Z
33
+ date: 2019-08-31 00:00:00.000000000 Z
34
34
  dependencies:
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: minitest
@@ -141,6 +141,10 @@ files:
141
141
  - lib/shaf/command/new.rb
142
142
  - lib/shaf/command/server.rb
143
143
  - lib/shaf/command/templates/Gemfile.erb
144
+ - lib/shaf/command/test.rb
145
+ - lib/shaf/command/test/filter.rb
146
+ - lib/shaf/command/test/runnable_method.rb
147
+ - lib/shaf/command/test/runner.rb
144
148
  - lib/shaf/command/upgrade.rb
145
149
  - lib/shaf/command/version.rb
146
150
  - lib/shaf/doc_model.rb
@@ -158,6 +162,7 @@ files:
158
162
  - lib/shaf/generator.rb
159
163
  - lib/shaf/generator/base.rb
160
164
  - lib/shaf/generator/controller.rb
165
+ - lib/shaf/generator/forms.rb
161
166
  - lib/shaf/generator/helper.rb
162
167
  - lib/shaf/generator/migration.rb
163
168
  - lib/shaf/generator/migration/add_column.rb
@@ -174,6 +179,7 @@ files:
174
179
  - lib/shaf/generator/scaffold.rb
175
180
  - lib/shaf/generator/serializer.rb
176
181
  - lib/shaf/generator/templates/api/controller.rb.erb
182
+ - lib/shaf/generator/templates/api/forms.rb.erb
177
183
  - lib/shaf/generator/templates/api/model.rb.erb
178
184
  - lib/shaf/generator/templates/api/policy.rb.erb
179
185
  - lib/shaf/generator/templates/api/serializer.rb.erb
@@ -253,6 +259,7 @@ files:
253
259
  - upgrades/0.6.0.tar.gz
254
260
  - upgrades/1.0.0.tar.gz
255
261
  - upgrades/1.0.4.tar.gz
262
+ - upgrades/1.1.0.tar.gz
256
263
  homepage:
257
264
  licenses:
258
265
  - MIT
@@ -267,7 +274,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
267
274
  requirements:
268
275
  - - ">="
269
276
  - !ruby/object:Gem::Version
270
- version: '2.4'
277
+ version: '2.5'
271
278
  required_rubygems_version: !ruby/object:Gem::Requirement
272
279
  requirements:
273
280
  - - ">="
metadata.gz.sig CHANGED
@@ -1,2 +1 @@
1
- �,zҳ�Ns 0Fm���)5�=@>���~(�١��wy,}��lr `��<������;=�1��p/�ڪ�!����I=��X9?-�|)"�O8��K�[�m> ��<��|�&�I䛊H F��䖖���F���Xl����w� �Mf1�)���:2m�4���;f����.ٺVW�"��9���|F“��4�=�b��&w����=Y�K�i�`
2
- E����{�1ߧl��(�L�Ct�� �^�x�>7�V�I
1
+ |�_�σDc_OdC9� N��SC6��X=����3s zW>�(v���;�}fag<