shaf 1.0.4 → 1.1.0

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