shaf 2.1.0 → 3.0.1

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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/lib/shaf/app.rb +27 -9
  4. data/lib/shaf/command/base.rb +4 -0
  5. data/lib/shaf/command/new.rb +5 -1
  6. data/lib/shaf/command/server.rb +5 -1
  7. data/lib/shaf/command/templates/config/settings.yml.erb +9 -8
  8. data/lib/shaf/extensions/resource_uris.rb +37 -155
  9. data/lib/shaf/extensions/symbolic_routes.rb +5 -18
  10. data/lib/shaf/formable/builder.rb +58 -19
  11. data/lib/shaf/formable/form.rb +13 -10
  12. data/lib/shaf/formable.rb +10 -23
  13. data/lib/shaf/generator/base.rb +82 -0
  14. data/lib/shaf/generator/controller.rb +19 -35
  15. data/lib/shaf/generator/forms.rb +10 -14
  16. data/lib/shaf/generator/migration/add_column.rb +0 -4
  17. data/lib/shaf/generator/migration/add_index.rb +0 -4
  18. data/lib/shaf/generator/migration/base.rb +8 -0
  19. data/lib/shaf/generator/migration/create_table.rb +0 -4
  20. data/lib/shaf/generator/migration/drop_column.rb +0 -4
  21. data/lib/shaf/generator/migration/rename_column.rb +0 -4
  22. data/lib/shaf/generator/model.rb +29 -14
  23. data/lib/shaf/generator/policy.rb +8 -14
  24. data/lib/shaf/generator/profile.rb +9 -14
  25. data/lib/shaf/generator/scaffold.rb +6 -9
  26. data/lib/shaf/generator/serializer.rb +31 -30
  27. data/lib/shaf/generator/templates/api/controller.rb.erb +13 -13
  28. data/lib/shaf/generator/templates/api/forms.rb.erb +2 -2
  29. data/lib/shaf/generator/templates/api/model.rb.erb +1 -1
  30. data/lib/shaf/generator/templates/api/profile.rb.erb +1 -1
  31. data/lib/shaf/generator/templates/api/serializer.rb.erb +1 -1
  32. data/lib/shaf/generator/templates/spec/integration_spec.rb.erb +14 -14
  33. data/lib/shaf/helpers/paginate.rb +1 -1
  34. data/lib/shaf/link_relations.rb +77 -0
  35. data/lib/shaf/profile.rb +8 -8
  36. data/lib/shaf/registrable_factory.rb +62 -32
  37. data/lib/shaf/responder/problem_json.rb +1 -1
  38. data/lib/shaf/router.rb +65 -12
  39. data/lib/shaf/spec/integration_spec.rb +1 -1
  40. data/lib/shaf/tasks.rb +0 -1
  41. data/lib/shaf/upgrade/package.rb +5 -7
  42. data/lib/shaf/utils.rb +2 -2
  43. data/lib/shaf/version.rb +1 -1
  44. data/lib/shaf/yard/link_object.rb +2 -3
  45. data/templates/Rakefile +0 -6
  46. data/templates/api/controllers/base_controller.rb +0 -2
  47. data/templates/api/serializers/root_serializer.rb +0 -11
  48. data/templates/config/initializers/middleware.rb +6 -0
  49. data/templates/spec/spec_helper.rb +4 -1
  50. data/upgrades/3.0.0.tar.gz +0 -0
  51. data/yard_templates/api_doc/profile_attribute/html/attribute.erb +2 -1
  52. data/yard_templates/api_doc/resource_attribute/html/attribute.erb +2 -1
  53. data/yard_templates/api_doc/sidebar/html/profile_list.erb +2 -1
  54. data/yard_templates/api_doc/sidebar/html/serializer_list.erb +2 -1
  55. data.tar.gz.sig +0 -0
  56. metadata +34 -36
  57. metadata.gz.sig +0 -0
  58. data/lib/shaf/api_doc/comment.rb +0 -27
  59. data/lib/shaf/api_doc/document.rb +0 -137
  60. data/lib/shaf/api_doc/link_relations.rb +0 -77
  61. data/lib/shaf/middleware.rb +0 -1
  62. data/lib/shaf/tasks/api_doc_task.rb +0 -146
data/lib/shaf/formable.rb CHANGED
@@ -3,43 +3,30 @@ require 'shaf/formable/builder'
3
3
  module Shaf
4
4
  module Formable
5
5
  def self.add_class_reader(clazz, name, form)
6
- clazz.define_singleton_method(name) { form }
6
+ clazz.define_singleton_method(name) { form.dup }
7
7
  end
8
8
 
9
9
  def self.add_instance_reader(clazz, name, form, prefill)
10
10
  clazz.define_method(name) do
11
- form.tap do |f|
11
+ form.dup.tap do |f|
12
12
  f.resource = self
13
13
  f.fill! if prefill
14
14
  end
15
15
  end
16
16
  end
17
17
 
18
- # Deprecated legacy way of specifying forms inside models
19
- def form(&block)
20
- forms_for(self, &block)
21
-
22
- Shaf.log.info <<~MSG
23
-
24
-
25
- DEPRECATED method ::form in #{self}
26
- Declare forms in a separate class extending Shaf::Formable with the class method forms_for!
27
- MSG
28
- end
29
-
30
- # New way of writing forms in a separate class/file
31
18
  def forms_for(clazz, &block)
32
19
  builder = Formable::Builder.new(&block)
33
- builder.forms.each do |form|
34
- next unless form.action
35
- method_name = "#{form.action}_form"
20
+ builder.forms.each do |form_wrapper|
21
+ method_name = form_wrapper.method_name
22
+ next unless method_name
36
23
 
37
- Formable.add_class_reader(clazz, method_name, form.dup)
24
+ form = form_wrapper.form
38
25
 
39
- if instance_accessor = builder.instance_accessor_for(form)
40
- prefill_form = instance_accessor.prefill?
41
- Formable.add_instance_reader(clazz, method_name, form.dup, prefill_form)
42
- end
26
+ Formable.add_class_reader(clazz, method_name, form)
27
+ next unless form_wrapper.instance_accessor?
28
+
29
+ Formable.add_instance_reader(clazz, method_name, form, form_wrapper.prefill?)
43
30
  end
44
31
  end
45
32
  alias form_for forms_for
@@ -23,6 +23,10 @@ module Shaf
23
23
  @usage = str || block
24
24
  end
25
25
 
26
+ def identified_by
27
+ @identifiers
28
+ end
29
+
26
30
  def options(option_parser, options); end
27
31
  end
28
32
 
@@ -33,6 +37,60 @@ module Shaf
33
37
 
34
38
  def call; end
35
39
 
40
+ def identifier
41
+ self.class.identified_by
42
+ end
43
+
44
+ private
45
+
46
+ def params
47
+ args[1..-1].map { |param| param.split(':')}
48
+ end
49
+
50
+ def name_arg
51
+ n = args.first || ''
52
+ return n unless n.empty?
53
+ raise Command::ArgumentError,
54
+ "Please provide a name when using the #{identifier} generator!"
55
+ end
56
+
57
+ def name
58
+ name_arg.split('/', 2).last
59
+ end
60
+
61
+ def model_class_name
62
+ Utils.model_name(name)
63
+ end
64
+
65
+ def plural_name
66
+ Utils.pluralize(name)
67
+ end
68
+
69
+ def resource_name
70
+ [namespace, name].compact.join('_')
71
+ end
72
+
73
+ def collection_name
74
+ [namespace, plural_name].compact.join('_')
75
+ end
76
+
77
+ def namespace
78
+ names = name_arg.split('/')
79
+ return if names.size == 1
80
+
81
+ warn "Only a single namespaces is allowed: #{name_arg}" if names.size > 2
82
+
83
+ names.first
84
+ end
85
+
86
+ def module_name
87
+ Utils.model_name(namespace) if namespace
88
+ end
89
+
90
+ def target(directory: target_dir, ns: namespace, name: target_name)
91
+ File.join(*[directory, ns, name].compact)
92
+ end
93
+
36
94
  def template_dir
37
95
  File.expand_path('templates', __dir__)
38
96
  end
@@ -56,6 +114,30 @@ module Shaf
56
114
  raise
57
115
  end
58
116
 
117
+ def wrap_in_module(content, module_name, search: nil)
118
+ return content if module_name.nil? || module_name.empty?
119
+
120
+ indentation = ' '
121
+ search ||= '(class|module) \w'
122
+ lines = []
123
+ added = false
124
+
125
+ content.split("\n").each do |line|
126
+ unless added
127
+ if m = line.match(/\A(\s*)#{search}/)
128
+ lines << "#{m[1]}module #{module_name}"
129
+ added = true
130
+ end
131
+ end
132
+
133
+ line.prepend(indentation) if added
134
+ lines << line
135
+ end
136
+
137
+ lines << 'end' if added
138
+ lines.join("\n")
139
+ end
140
+
59
141
  def write_output(file, content)
60
142
  FileTransactions::CreateFileCommand.execute(file) { content }
61
143
  puts "Added: #{file}"
@@ -13,24 +13,7 @@ module Shaf
13
13
  add_link_to_root
14
14
  end
15
15
 
16
- def params
17
- args[1..-1].map { |param| param.split(':')}
18
- end
19
-
20
- def name
21
- n = args.first || ''
22
- return n unless n.empty?
23
- raise Command::ArgumentError,
24
- 'Please provide a controller name when using the controller generator!'
25
- end
26
-
27
- def model_class_name
28
- Utils.model_name(name)
29
- end
30
-
31
- def plural_name
32
- Utils.pluralize(name)
33
- end
16
+ private
34
17
 
35
18
  def pluralized_model_name
36
19
  Utils.pluralize(model_class_name)
@@ -44,21 +27,31 @@ module Shaf
44
27
  'spec/integration_spec.rb'
45
28
  end
46
29
 
47
- def target
48
- "api/controllers/#{plural_name}_controller.rb"
30
+ def target_dir
31
+ 'api/controllers'
32
+ end
33
+
34
+ def target_name
35
+ "#{plural_name}_controller.rb"
49
36
  end
50
37
 
51
38
  def spec_target
52
- "spec/integration/#{plural_name}_controller_spec.rb"
39
+ target(directory: 'spec/integration', name: "#{plural_name}_controller_spec.rb")
40
+ end
41
+
42
+ def policy_file
43
+ File.join(['policies', namespace, "#{name}_policy"].compact)
53
44
  end
54
45
 
55
46
  def create_controller
56
47
  content = render(template, opts)
48
+ content = wrap_in_module(content, module_name)
57
49
  write_output(target, content)
58
50
  end
59
51
 
60
52
  def create_integration_spec
61
53
  content = render(spec_template, opts)
54
+ content = wrap_in_module(content, module_name, search: "describe #{model_class_name}")
62
55
  write_output(spec_target, content)
63
56
  end
64
57
 
@@ -66,11 +59,14 @@ module Shaf
66
59
  {
67
60
  name: name,
68
61
  plural_name: plural_name,
62
+ resource_name: resource_name,
63
+ collection_name: collection_name,
69
64
  serializer_class_name: "#{model_class_name}Serializer",
70
65
  model_class_name: model_class_name,
71
66
  controller_class_name: "#{pluralized_model_name}Controller",
72
67
  policy_class_name: "#{model_class_name}Policy",
73
- policy_file: "policies/#{name}_policy",
68
+ policy_file: policy_file,
69
+ namespace: namespace,
74
70
  params: params
75
71
  }
76
72
  end
@@ -97,19 +93,7 @@ module Shaf
97
93
  end
98
94
 
99
95
  def link_content(indentation = '')
100
- <<~DOC.split("\n").map { |line| "#{indentation}#{line}" }
101
-
102
- # Auto generated doc:
103
- # Link to the collection of #{plural_name}.
104
- # Method: GET
105
- # Example:
106
- # ```
107
- # curl -H "Accept: application/hal+json" \\
108
- # -H "Authorization: abcdef" \\
109
- # /#{plural_name}
110
- #```
111
- link :#{plural_name}, #{plural_name}_uri
112
- DOC
96
+ "#{indentation}link :#{plural_name}, #{collection_name}_uri"
113
97
  end
114
98
  end
115
99
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Shaf
2
4
  module Generator
3
5
  class Forms < Base
@@ -8,17 +10,6 @@ module Shaf
8
10
  create_forms
9
11
  end
10
12
 
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
13
  def class_name
23
14
  "#{model_class_name}Forms"
24
15
  end
@@ -27,19 +18,24 @@ module Shaf
27
18
  'api/forms.rb'
28
19
  end
29
20
 
30
- def target
31
- "api/forms/#{model_name}_forms.rb"
21
+ def target_dir
22
+ 'api/forms'
23
+ end
24
+
25
+ def target_name
26
+ "#{name}_forms.rb"
32
27
  end
33
28
 
34
29
  def create_forms
35
30
  content = render(template, opts)
31
+ content = wrap_in_module(content, module_name)
36
32
  # FIXME: Append if file exists!
37
33
  write_output(target, content)
38
34
  end
39
35
 
40
36
  def opts
41
37
  {
42
- model_name: model_name,
38
+ model_name: name,
43
39
  class_name: class_name,
44
40
  model_class_name: model_class_name,
45
41
  fields: fields
@@ -21,10 +21,6 @@ module Shaf
21
21
  "add_#{cols.join('_')}_to_#{table_name}"
22
22
  end
23
23
 
24
- def table_name
25
- args.first
26
- end
27
-
28
24
  def compile_changes
29
25
  add_change add_columns_change
30
26
  end
@@ -16,10 +16,6 @@ module Shaf
16
16
  "add_#{column}_index_to_#{table_name}"
17
17
  end
18
18
 
19
- def table_name
20
- args.first
21
- end
22
-
23
19
  def compile_changes
24
20
  add_change add_index_change
25
21
  end
@@ -19,6 +19,10 @@ module Shaf
19
19
  def usage(str = nil, &block)
20
20
  @usage = str || block
21
21
  end
22
+
23
+ def identified_by
24
+ @identifiers
25
+ end
22
26
  end
23
27
 
24
28
  def initialize(*args, **options)
@@ -35,6 +39,10 @@ module Shaf
35
39
  raise Command::ArgumentError, e.message
36
40
  end
37
41
 
42
+ def table_name
43
+ (args.first || '').tr('/', '_')
44
+ end
45
+
38
46
  def add_change(change)
39
47
  @changes ||= []
40
48
  @changes << change if change
@@ -11,10 +11,6 @@ module Shaf
11
11
  raise "Please provide a table name when generation a create table migration"
12
12
  end
13
13
 
14
- def table_name
15
- args.first || ""
16
- end
17
-
18
14
  def compile_migration_name
19
15
  "create_#{table_name}_table"
20
16
  end
@@ -24,10 +24,6 @@ module Shaf
24
24
  add_change drop_column_change
25
25
  end
26
26
 
27
- def table_name
28
- args.first || ""
29
- end
30
-
31
27
  def column
32
28
  args[1] || ""
33
29
  end
@@ -23,10 +23,6 @@ module Shaf
23
23
  add_change rename_column_change
24
24
  end
25
25
 
26
- def table_name
27
- args.first || ""
28
- end
29
-
30
26
  def from_col
31
27
  args[1] || ""
32
28
  end
@@ -5,44 +5,59 @@ module Shaf
5
5
  identifier :model
6
6
  usage 'generate model MODEL_NAME [attribute:type] [..]'
7
7
 
8
+ def self.options(parser, options)
9
+ parser.on("--skip-model", "don't generate model or migration") do |s|
10
+ options[:skip_model] = s
11
+ end
12
+
13
+ parser.on("--skip-migration", "don't generate a migration") do |s|
14
+ options[:skip_migration] = s
15
+ end
16
+ end
17
+
8
18
  def call
9
- create_model
10
- create_migration
19
+ create_model unless skip_model?
20
+ create_migration unless skip_migration?
11
21
  create_serializer
12
22
  create_forms
13
23
  end
14
24
 
15
- def model_name
16
- n = args.first || ''
17
- return n unless n.empty?
18
- raise Command::ArgumentError,
19
- 'Please provide a model name when using the model generator!'
25
+ private
26
+
27
+ def skip_model?
28
+ options[:skip_model]
20
29
  end
21
30
 
22
- def model_class_name
23
- Utils.model_name(model_name)
31
+ def skip_migration?
32
+ options[:skip_migration] || skip_model?
24
33
  end
25
34
 
26
35
  def table_name
27
- Utils.pluralize model_name
36
+ Utils.pluralize name_arg
28
37
  end
29
38
 
30
39
  def template
31
40
  'api/model.rb'
32
41
  end
33
42
 
34
- def target
35
- "api/models/#{model_name}.rb"
43
+ def target_dir
44
+ 'api/models'
45
+ end
46
+
47
+ def target_name
48
+ "#{name}.rb"
36
49
  end
37
50
 
38
51
  def create_model
39
52
  content = render(template, opts)
53
+ content = wrap_in_module(content, module_name)
40
54
  write_output(target, content)
41
55
  end
42
56
 
43
57
  def opts
44
58
  {
45
59
  class_name: model_class_name,
60
+ table_name: [namespace, plural_name].compact.join('_')
46
61
  }
47
62
  end
48
63
 
@@ -52,12 +67,12 @@ module Shaf
52
67
  end
53
68
 
54
69
  def create_serializer
55
- serializer_args = %W(serializer #{model_name}) + args[1..-1]
70
+ serializer_args = %W(serializer #{name_arg}) + args[1..-1]
56
71
  Generator::Factory.create(*serializer_args, **options).call
57
72
  end
58
73
 
59
74
  def create_forms
60
- form_args = %W(forms #{model_name}) + args[1..-1]
75
+ form_args = %W(forms #{name_arg}) + args[1..-1]
61
76
  Generator::Factory.create(*form_args, **options).call
62
77
  end
63
78
  end
@@ -8,27 +8,21 @@ module Shaf
8
8
  create_policy
9
9
  end
10
10
 
11
- def policy_name
12
- n = args.first || ""
13
- return n unless n.empty?
14
- raise Command::ArgumentError,
15
- "Please provide a policy name when using the policy generator!"
16
- end
17
-
18
- def model_class_name
19
- Utils::model_name(policy_name)
20
- end
21
-
22
11
  def template
23
12
  'api/policy.rb'
24
13
  end
25
14
 
26
- def target
27
- "api/policies/#{policy_name}_policy.rb"
15
+ def target_dir
16
+ 'api/policies'
17
+ end
18
+
19
+ def target_name
20
+ "#{name}_policy.rb"
28
21
  end
29
22
 
30
23
  def create_policy
31
24
  content = render(template, opts)
25
+ content = wrap_in_module(content, module_name)
32
26
  write_output(target, content)
33
27
  end
34
28
 
@@ -39,7 +33,7 @@ module Shaf
39
33
  def opts
40
34
  {
41
35
  policy_class_name: "#{model_class_name}Policy",
42
- name: policy_name,
36
+ name: name,
43
37
  attributes: attributes,
44
38
  }
45
39
  end
@@ -8,23 +8,16 @@ module Shaf
8
8
  create_profile
9
9
  end
10
10
 
11
- def profile_name
12
- n = args.first || ""
13
- return n unless n.empty?
14
- raise Command::ArgumentError,
15
- "Please provide a profile name when using the profile generator!"
16
- end
17
-
18
- def model_class_name
19
- Utils::model_name(profile_name)
20
- end
21
-
22
11
  def template
23
12
  'api/profile.rb'
24
13
  end
25
14
 
26
- def target
27
- "api/profiles/#{profile_name}.rb"
15
+ def target_dir
16
+ 'api/profiles'
17
+ end
18
+
19
+ def target_name
20
+ "#{name}.rb"
28
21
  end
29
22
 
30
23
  def attributes
@@ -37,14 +30,16 @@ module Shaf
37
30
 
38
31
  def create_profile
39
32
  content = render(template, opts)
33
+ content = wrap_in_module(content, module_name, search: 'class \w')
40
34
  write_output(target, content)
41
35
  end
42
36
 
43
37
  def opts
44
38
  {
45
- profile_name: profile_name,
39
+ profile_name: name,
46
40
  profile_class_name: "#{model_class_name}",
47
41
  attributes: attributes,
42
+ resource_name: resource_name,
48
43
  }
49
44
  end
50
45
  end
@@ -6,21 +6,18 @@ module Shaf
6
6
  usage 'generate scaffold RESOURCE_NAME [attribute:type] [..]'
7
7
 
8
8
  def call
9
- if name.empty?
10
- raise "Please provide a resource name when using the scaffold generator!"
11
- end
9
+ check_name_arg!
12
10
 
13
11
  options[:specs] = true if options[:specs].nil?
14
12
  Generator::Factory.create('model', *args, **options).call
15
- Generator::Factory.create('controller', *controller_args, **options).call
13
+ Generator::Factory.create('controller', *args, **options).call
16
14
  end
17
15
 
18
- def name
19
- args.first || ""
20
- end
16
+ def check_name_arg!
17
+ return if args.first && !args.first.empty?
21
18
 
22
- def controller_args
23
- [name] + args[1..-1]
19
+ raise Command::ArgumentError,
20
+ "Please provide a name when using the scaffold generator!"
24
21
  end
25
22
  end
26
23
  end