shaf 2.1.0 → 3.0.1

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