superglue 0.54.0 → 1.0.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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/superglue/install/install_generator.rb +119 -0
  3. data/lib/{install/templates/web → generators/superglue/install/templates}/application.json.props +2 -2
  4. data/lib/generators/superglue/install/templates/js/application.jsx +35 -0
  5. data/lib/generators/superglue/install/templates/js/application_visit.js +113 -0
  6. data/lib/generators/superglue/install/templates/js/components.js +2 -0
  7. data/lib/generators/superglue/install/templates/js/flash.js +44 -0
  8. data/lib/generators/superglue/install/templates/js/inputs.jsx +302 -0
  9. data/lib/generators/superglue/install/templates/js/jsconfig.json +9 -0
  10. data/lib/generators/superglue/install/templates/js/layout.jsx +16 -0
  11. data/lib/generators/superglue/install/templates/js/page_to_page_mapping.js +35 -0
  12. data/lib/generators/superglue/install/templates/js/store.js +30 -0
  13. data/lib/{install/templates/web/application.js → generators/superglue/install/templates/ts/application.tsx} +10 -16
  14. data/lib/generators/superglue/install/templates/ts/application_visit.ts +122 -0
  15. data/lib/generators/superglue/install/templates/ts/components.ts +2 -0
  16. data/lib/generators/superglue/install/templates/ts/flash.ts +46 -0
  17. data/lib/generators/superglue/install/templates/ts/inputs.tsx +547 -0
  18. data/lib/generators/superglue/install/templates/ts/layout.tsx +16 -0
  19. data/lib/generators/superglue/install/templates/ts/page_to_page_mapping.ts +34 -0
  20. data/lib/generators/superglue/install/templates/ts/store.ts +34 -0
  21. data/lib/generators/superglue/install/templates/ts/tsconfig.json +27 -0
  22. data/lib/generators/superglue/scaffold/scaffold_generator.rb +16 -0
  23. data/lib/generators/superglue/scaffold_controller/scaffold_controller_generator.rb +61 -0
  24. data/lib/generators/superglue/view_collection/templates/js/edit.jsx +40 -0
  25. data/lib/generators/superglue/view_collection/templates/js/index.jsx +62 -0
  26. data/lib/generators/superglue/view_collection/templates/js/new.jsx +38 -0
  27. data/lib/generators/superglue/view_collection/templates/js/show.jsx +26 -0
  28. data/lib/generators/superglue/view_collection/templates/props/edit.json.props +9 -0
  29. data/lib/generators/superglue/view_collection/templates/props/index.json.props +14 -0
  30. data/lib/generators/superglue/view_collection/templates/props/new.json.props +10 -0
  31. data/lib/generators/superglue/view_collection/templates/props/show.json.props +6 -0
  32. data/lib/generators/superglue/view_collection/templates/ts/edit.tsx +54 -0
  33. data/lib/generators/superglue/view_collection/templates/ts/index.tsx +77 -0
  34. data/lib/generators/superglue/view_collection/templates/ts/new.tsx +50 -0
  35. data/lib/generators/superglue/view_collection/templates/ts/show.tsx +37 -0
  36. data/lib/generators/superglue/view_collection/view_collection_generator.rb +180 -0
  37. data/lib/superglue/helpers.rb +1 -1
  38. data/lib/superglue.rb +2 -1
  39. metadata +60 -43
  40. data/lib/generators/rails/scaffold_controller_generator.rb +0 -12
  41. data/lib/generators/rails/superglue_generator.rb +0 -98
  42. data/lib/generators/rails/templates/controller.rb.tt +0 -68
  43. data/lib/generators/rails/templates/edit.json.props +0 -12
  44. data/lib/generators/rails/templates/index.json.props +0 -14
  45. data/lib/generators/rails/templates/new.json.props +0 -13
  46. data/lib/generators/rails/templates/show.json.props +0 -6
  47. data/lib/generators/rails/templates/web/edit.js +0 -35
  48. data/lib/generators/rails/templates/web/index.js +0 -56
  49. data/lib/generators/rails/templates/web/new.js +0 -33
  50. data/lib/generators/rails/templates/web/show.js +0 -28
  51. data/lib/install/templates/web/actions.js +0 -6
  52. data/lib/install/templates/web/application_visit.js +0 -65
  53. data/lib/install/templates/web/flash.js +0 -19
  54. data/lib/install/templates/web/page_to_page_mapping.js +0 -12
  55. data/lib/install/templates/web/pages.js +0 -15
  56. data/lib/install/templates/web/store.js +0 -32
  57. data/lib/install/web.rb +0 -55
  58. data/lib/tasks/install.rake +0 -9
  59. /data/lib/{install/templates/web → generators/superglue/install/templates}/initializer.rb +0 -0
  60. /data/lib/generators/{rails/templates/web → superglue/view_collection/templates/erb}/edit.html.erb +0 -0
  61. /data/lib/generators/{rails/templates/web → superglue/view_collection/templates/erb}/index.html.erb +0 -0
  62. /data/lib/generators/{rails/templates/web → superglue/view_collection/templates/erb}/new.html.erb +0 -0
  63. /data/lib/generators/{rails/templates/web → superglue/view_collection/templates/erb}/show.html.erb +0 -0
  64. /data/lib/generators/{rails/templates → superglue/view_collection/templates/props}/_form.json.props +0 -0
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file was copied over from Rails land and slightly modified to account
4
+ # for Superglue templates
5
+
6
+ require "rails/generators/resource_helpers"
7
+ require "rails/generators/rails/scaffold_controller/scaffold_controller_generator"
8
+
9
+ module Superglue
10
+ module Generators
11
+ class ScaffoldControllerGenerator < Rails::Generators::NamedBase # :nodoc:
12
+ include Rails::Generators::ResourceHelpers
13
+
14
+ # Superglue uses the out-of-the-box controller generated by Rails.
15
+ source_root Rails::Generators::ScaffoldControllerGenerator.source_root
16
+
17
+ check_class_collision suffix: "Controller"
18
+
19
+ class_option :helper, type: :boolean
20
+ class_option :orm, banner: "NAME", type: :string, required: true,
21
+ desc: "ORM to generate the controller for"
22
+
23
+ class_option :skip_routes, type: :boolean, desc: "Don't add routes to config/routes.rb."
24
+
25
+ argument :attributes, type: :array, default: [], banner: "field:type field:type"
26
+
27
+ def create_controller_files
28
+ template "controller.rb", File.join("app/controllers", controller_class_path, "#{controller_file_name}_controller.rb")
29
+ end
30
+
31
+ # Replaces template_engine (and its default erb), with view_collection
32
+ # defaulting to superglue:view_collection
33
+ hook_for :view_collection, required: true, default: "view_collection"
34
+
35
+ hook_for :resource_route, in: :rails, required: true do |route|
36
+ invoke route unless options.skip_routes?
37
+ end
38
+
39
+ hook_for :test_framework, in: :rails, as: :scaffold
40
+
41
+ # Invoke the helper using the controller name (pluralized)
42
+ hook_for :helper, in: :rails, as: :scaffold do |invoked|
43
+ invoke invoked, [controller_name]
44
+ end
45
+
46
+ private
47
+
48
+ def permitted_params
49
+ attachments, others = attributes_names.partition { |name| attachments?(name) }
50
+ params = others.map { |name| ":#{name}" }
51
+ params += attachments.map { |name| "#{name}: []" }
52
+ params.join(", ")
53
+ end
54
+
55
+ def attachments?(name)
56
+ attribute = attributes.find { |attr| attr.name == name }
57
+ attribute&.attachments?
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,40 @@
1
+ import React from 'react'
2
+ import {
3
+ Form,
4
+ Layout,
5
+ <%- attributes.each do |attr| -%>
6
+ <%= js_component(attr)%>,
7
+ <%- end -%>
8
+ SubmitButton
9
+ } from '@javascript/components'
10
+ import { useContent } from '@thoughtbot/superglue'
11
+ import { useAppSelector } from '@javascript/store'
12
+
13
+ export default function <%= js_plural_table_name(:upper) %>Edit() {
14
+ const {
15
+ <%= js_singular_table_name %>Form,
16
+ <%= js_singular_table_name %>Path,
17
+ <%= js_plural_table_name %>Path,
18
+ } = useContent()
19
+
20
+ const {
21
+ inputs,
22
+ form,
23
+ extras
24
+ } = <%= js_singular_table_name %>Form
25
+ const validationErrors = useAppSelector((state) => state.flash["<%= js_singular_table_name%>FormErrors"])
26
+
27
+ return (
28
+ <Layout>
29
+ <Form {...form} extras={extras} validationErrors={validationErrors} data-sg-visit>
30
+ <%- attributes.each do |attr| -%>
31
+ <<%= js_component(attr)%> {...inputs.<%= attr.column_name.camelize(:lower)%>} label="<%= attr.column_name.humanize %>" errorKey="<%= attr.column_name %>" />
32
+ <%- end -%>
33
+ <SubmitButton {...inputs.submit} type="submit"> {inputs.submit.text} </SubmitButton>
34
+ </Form>
35
+
36
+ <a href={<%= js_singular_table_name %>Path} data-sg-visit>Show</a>
37
+ <a href={<%= js_plural_table_name %>Path} data-sg-visit>Back</a>
38
+ </Layout>
39
+ )
40
+ }
@@ -0,0 +1,62 @@
1
+ import React from 'react'
2
+ import { Form, Layout } from '@javascript/components'
3
+ import { useContent } from '@thoughtbot/superglue'
4
+
5
+ export default function <%= js_plural_table_name(:upper) %>Index() {
6
+ const {
7
+ new<%= js_singular_table_name(:upper) %>Path,
8
+ <%= js_plural_table_name %> = [],
9
+ } = useContent()
10
+
11
+ const <%= js_singular_table_name %>Items = <%= js_plural_table_name %>.map((<%= js_singular_table_name %>) => {
12
+ const {
13
+ id,
14
+ <%- showable_attributes.each do |attr| -%>
15
+ <%= attr.column_name.camelize(:lower)%>,
16
+ <%- end -%>
17
+ edit<%= js_singular_table_name(:upper) %>Path,
18
+ <%= js_singular_table_name %>Path,
19
+ deleteForm
20
+ } = <%= js_singular_table_name %>
21
+
22
+ const { form, extras } = deleteForm;
23
+
24
+ return (
25
+ <tr key={id}>
26
+ <%- showable_attributes.each do |attr| -%>
27
+ <td>{<%=attr.column_name.camelize(:lower)%>}</td>
28
+ <%- end -%>
29
+ <td><a href={ <%= js_singular_table_name %>Path } data-sg-visit>Show</a></td>
30
+ <td><a href={ edit<%= js_singular_table_name(:upper) %>Path } data-sg-visit>Edit</a></td>
31
+ <td>
32
+ <Form {...form} extras={extras} data-sg-visit>
33
+ <button type="submit">Delete</button>
34
+ </Form>
35
+ </td>
36
+ </tr>
37
+ )
38
+ })
39
+
40
+ return (
41
+ <Layout>
42
+ <h1><%= js_plural_table_name(:upper) %></h1>
43
+
44
+ <table>
45
+ <thead>
46
+ <%- showable_attributes.each do |attr| -%>
47
+ <tr><th><%=attr.column_name.humanize%></th></tr>
48
+ <%- end -%>
49
+ <tr>
50
+ <th colSpan={3}></th>
51
+ </tr>
52
+ </thead>
53
+
54
+ <tbody>
55
+ {<%= js_singular_table_name %>Items}
56
+ </tbody>
57
+ </table>
58
+ <br />
59
+ <a href={new<%= js_singular_table_name(:upper) %>Path} data-sg-visit>New <%= singular_table_name.humanize %></a>
60
+ </Layout>
61
+ )
62
+ }
@@ -0,0 +1,38 @@
1
+ import React from 'react'
2
+ import {
3
+ Form,
4
+ Layout,
5
+ <%- attributes.each do |attr| -%>
6
+ <%= js_component(attr)%>,
7
+ <%- end -%>
8
+ SubmitButton
9
+ } from '@javascript/components'
10
+ import { useContent } from '@thoughtbot/superglue'
11
+ import { useAppSelector } from '@javascript/store'
12
+
13
+ export default function <%= js_plural_table_name(:upper) %>New() {
14
+ const {
15
+ <%= js_singular_table_name %>Form,
16
+ <%= js_plural_table_name %>Path
17
+ } = useContent()
18
+
19
+ const {
20
+ inputs,
21
+ form,
22
+ extras
23
+ } = <%= js_singular_table_name %>Form
24
+ const validationErrors = useAppSelector((state) => state.flash["<%= js_singular_table_name%>FormErrors"])
25
+
26
+ return (
27
+ <Layout>
28
+ <Form {...form} extras={extras} validationErrors={validationErrors} data-sg-visit>
29
+ <%- attributes.each do |attr| -%>
30
+ <<%= js_component(attr)%> {...inputs.<%= attr.column_name.camelize(:lower)%>} label="<%= attr.column_name.humanize %>" errorKey="<%= attr.column_name %>" />
31
+ <%- end -%>
32
+ <SubmitButton {...inputs.submit} type="submit"> {inputs.submit.text} </SubmitButton>
33
+ </Form>
34
+
35
+ <a href={<%= js_plural_table_name %>Path} data-sg-visit>Back</a>
36
+ </Layout>
37
+ )
38
+ }
@@ -0,0 +1,26 @@
1
+ import React from 'react'
2
+ import { Layout } from '@javascript/components'
3
+ import { useContent } from '@thoughtbot/superglue'
4
+
5
+ export default function <%= js_plural_table_name(:upper) %>Show() {
6
+ const {
7
+ <%- showable_attributes.each do |attr| -%>
8
+ <%= attr.column_name.camelize(:lower) %>,
9
+ <%- end -%>
10
+ edit<%= js_singular_table_name(:upper) %>Path,
11
+ <%= js_plural_table_name %>Path
12
+ } = useContent()
13
+
14
+ return (
15
+ <Layout>
16
+ <%- showable_attributes.each do |attr| -%>
17
+ <p>
18
+ <strong><%= attr.column_name.humanize %>:</strong>
19
+ {<%=attr.column_name.camelize(:lower)%>}
20
+ </p>
21
+ <%- end -%>
22
+ <a href={ edit<%= js_singular_table_name(:upper) %>Path } data-sg-visit>Edit</a>
23
+ <a href={ <%= js_plural_table_name %>Path } data-sg-visit>Back</a>
24
+ </Layout>
25
+ )
26
+ }
@@ -0,0 +1,9 @@
1
+ if @<%= singular_table_name %>.errors.any?
2
+ flash.now["<%= js_singular_table_name%>FormErrors"] = @<%= singular_table_name %>.errors.as_json
3
+ end
4
+
5
+ json.<%= js_singular_table_name %>Form(partial: 'form') do
6
+ end
7
+
8
+ json.<%= js_singular_table_name%>Path <%= singular_table_name%>_path(@<%=singular_table_name%>)
9
+ json.<%= js_plural_table_name %>Path <%= plural_table_name %>_path
@@ -0,0 +1,14 @@
1
+ json.<%= js_plural_table_name %> do
2
+ json.array! @<%= plural_table_name %> do |<%= singular_table_name %>|
3
+ <%- attributes_list_with_timestamps.each do |attr| -%>
4
+ json.<%=attr.to_s.camelize(:lower)%> <%= singular_table_name %>.<%=attr%>
5
+ <%- end -%>
6
+ json.edit<%=js_singular_table_name(:upper)%>Path edit_<%=singular_table_name%>_path(<%=singular_table_name%>)
7
+ json.<%=js_singular_table_name%>Path <%=singular_table_name%>_path(<%=singular_table_name%>)
8
+ json.deleteForm do
9
+ form_props(model: <%=singular_table_name%>, method: :delete)
10
+ end
11
+ end
12
+ end
13
+
14
+ json.new<%= js_singular_table_name(:upper) %>Path new_<%= singular_table_name %>_path
@@ -0,0 +1,10 @@
1
+ if @<%= singular_table_name %>.errors.any?
2
+ flash.now["<%= js_singular_table_name%>FormErrors"] = @<%= singular_table_name %>.errors.as_json
3
+ end
4
+
5
+ json.<%= js_singular_table_name %>Form(partial: 'form') do
6
+ end
7
+
8
+ json.<%= js_plural_table_name %>Path <%= plural_table_name %>_path
9
+
10
+
@@ -0,0 +1,6 @@
1
+ <%- attributes_list_with_timestamps.each do |attr|-%>
2
+ json.<%=attr.to_s.camelize(:lower)%> @<%= singular_table_name %>.<%=attr%>
3
+ <%- end -%>
4
+
5
+ json.<%= js_plural_table_name %>Path <%= plural_table_name %>_path
6
+ json.edit<%= js_singular_table_name(:upper) %>Path edit_<%= singular_table_name %>_path(@<%= singular_table_name %>)
@@ -0,0 +1,54 @@
1
+ import React from 'react'
2
+ import {
3
+ Form,
4
+ FormProps,
5
+ Layout,
6
+ <%- attributes.each do |attr| -%>
7
+ <%= js_component(attr)%>,
8
+ <%= js_component(attr)%>Props,
9
+ <%- end -%>
10
+ SubmitButton,
11
+ SubmitButtonProps
12
+ } from '@javascript/components'
13
+ import { useContent } from '@thoughtbot/superglue'
14
+ import { useAppSelector } from '@javascript/store'
15
+
16
+ type ContentProps = {
17
+ <%= js_singular_table_name %>Path: string
18
+ <%= js_plural_table_name %>Path: string,
19
+ <%= js_singular_table_name %>Form: FormProps<{
20
+ <%- attributes.each do |attr| -%>
21
+ <%= attr.column_name.camelize(:lower)%>: <%= js_component(attr)%>Props
22
+ <%- end -%>
23
+ submit: SubmitButtonProps
24
+ }>
25
+ }
26
+
27
+ export default function <%= js_plural_table_name(:upper) %>Edit() {
28
+ const {
29
+ <%= js_singular_table_name %>Form,
30
+ <%= js_singular_table_name %>Path,
31
+ <%= js_plural_table_name %>Path,
32
+ } = useContent<ContentProps>()
33
+
34
+ const {
35
+ inputs,
36
+ form,
37
+ extras
38
+ } = <%= js_singular_table_name %>Form
39
+ const validationErrors = useAppSelector((state) => state.flash["<%= js_singular_table_name%>FormErrors"])
40
+
41
+ return (
42
+ <Layout>
43
+ <Form {...form} extras={extras} validationErrors={validationErrors} data-sg-visit>
44
+ <%- attributes.each do |attr| -%>
45
+ <<%= js_component(attr)%> {...inputs.<%= attr.column_name.camelize(:lower)%>} label="<%= attr.column_name.humanize %>" errorKey="<%= attr.column_name %>" />
46
+ <%- end -%>
47
+ <SubmitButton {...inputs.submit} type="submit"> {inputs.submit.text} </SubmitButton>
48
+ </Form>
49
+
50
+ <a href={<%= js_singular_table_name %>Path} data-sg-visit>Show</a>
51
+ <a href={<%= js_plural_table_name %>Path} data-sg-visit>Back</a>
52
+ </Layout>
53
+ )
54
+ }
@@ -0,0 +1,77 @@
1
+ import React from 'react'
2
+ import { Form, FormProps, Layout } from '@javascript/components'
3
+ import { useContent } from '@thoughtbot/superglue'
4
+
5
+ type ContentProps = {
6
+ new<%= js_singular_table_name(:upper) %>Path: string
7
+ <%= js_plural_table_name %>: {
8
+ id: number,
9
+ <%- showable_attributes.each do |attr| -%>
10
+ <%= attr.column_name.camelize(:lower)%>: <%= json_mappable_type(attr)%>
11
+ <%- end -%>
12
+ createdAt: string,
13
+ updatedAt: string,
14
+ edit<%= js_singular_table_name(:upper) %>Path: string,
15
+ <%= js_singular_table_name %>Path: string,
16
+ deleteForm: FormProps
17
+ }[]
18
+ }
19
+
20
+ export default function <%= js_plural_table_name(:upper) %>Index() {
21
+ const {
22
+ new<%= js_singular_table_name(:upper) %>Path,
23
+ <%= js_plural_table_name %> = [],
24
+ } = useContent<ContentProps>()
25
+
26
+ const <%= js_singular_table_name %>Items = <%= js_plural_table_name %>.map((<%= js_singular_table_name %>) => {
27
+ const {
28
+ id,
29
+ <%- showable_attributes.each do |attr| -%>
30
+ <%= attr.column_name.camelize(:lower)%>,
31
+ <%- end -%>
32
+ edit<%= js_singular_table_name(:upper) %>Path,
33
+ <%= js_singular_table_name %>Path,
34
+ deleteForm
35
+ } = <%= js_singular_table_name %>
36
+
37
+ const { form, extras } = deleteForm;
38
+
39
+ return (
40
+ <tr key={id}>
41
+ <%- showable_attributes.each do |attr| -%>
42
+ <td>{<%=attr.column_name.camelize(:lower)%>}</td>
43
+ <%- end -%>
44
+ <td><a href={ <%= js_singular_table_name %>Path } data-sg-visit>Show</a></td>
45
+ <td><a href={ edit<%= js_singular_table_name(:upper) %>Path } data-sg-visit>Edit</a></td>
46
+ <td>
47
+ <Form {...form} extras={extras} data-sg-visit>
48
+ <button type="submit">Delete</button>
49
+ </Form>
50
+ </td>
51
+ </tr>
52
+ )
53
+ })
54
+
55
+ return (
56
+ <Layout>
57
+ <h1><%= js_plural_table_name(:upper) %></h1>
58
+
59
+ <table>
60
+ <thead>
61
+ <%- showable_attributes.each do |attr| -%>
62
+ <tr><th><%=attr.column_name.humanize%></th></tr>
63
+ <%- end -%>
64
+ <tr>
65
+ <th colSpan={3}></th>
66
+ </tr>
67
+ </thead>
68
+
69
+ <tbody>
70
+ {<%= js_singular_table_name %>Items}
71
+ </tbody>
72
+ </table>
73
+ <br />
74
+ <a href={new<%= js_singular_table_name(:upper) %>Path} data-sg-visit>New <%= singular_table_name.humanize %></a>
75
+ </Layout>
76
+ )
77
+ }
@@ -0,0 +1,50 @@
1
+ import React from 'react'
2
+ import {
3
+ Form,
4
+ FormProps,
5
+ Layout,
6
+ <%- attributes.each do |attr| -%>
7
+ <%= js_component(attr)%>,
8
+ <%= js_component(attr)%>Props,
9
+ <%- end -%>
10
+ SubmitButton,
11
+ SubmitButtonProps
12
+ } from '@javascript/components'
13
+ import { useContent } from '@thoughtbot/superglue'
14
+ import { useAppSelector } from '@javascript/store'
15
+
16
+ type ContentProps = {
17
+ <%= js_plural_table_name %>Path: string
18
+ <%= js_singular_table_name %>Form: FormProps<{
19
+ <%- attributes.each do |attr| -%>
20
+ <%= attr.column_name.camelize(:lower)%>: <%= js_component(attr)%>Props
21
+ <%- end -%>
22
+ submit: SubmitButtonProps
23
+ }>
24
+ }
25
+
26
+ export default function <%= js_plural_table_name(:upper) %>New() {
27
+ const {
28
+ <%= js_singular_table_name %>Form,
29
+ <%= js_plural_table_name %>Path,
30
+ } = useContent<ContentProps>()
31
+ const {
32
+ inputs,
33
+ form,
34
+ extras
35
+ } = <%= js_singular_table_name %>Form
36
+ const validationErrors = useAppSelector((state) => state.flash["<%= js_singular_table_name%>FormErrors"])
37
+
38
+ return (
39
+ <Layout>
40
+ <Form {...form} extras={extras} validationErrors={validationErrors} data-sg-visit>
41
+ <%- attributes.each do |attr| -%>
42
+ <<%= js_component(attr)%> {...inputs.<%= attr.column_name.camelize(:lower)%>} label="<%= attr.column_name.humanize %>" errorKey="<%= attr.column_name %>" />
43
+ <%- end -%>
44
+ <SubmitButton {...inputs.submit} type="submit"> {inputs.submit.text} </SubmitButton>
45
+ </Form>
46
+
47
+ <a href={<%= js_plural_table_name %>Path} data-sg-visit>Back</a>
48
+ </Layout>
49
+ )
50
+ }
@@ -0,0 +1,37 @@
1
+ import React from 'react'
2
+ import { Layout } from '@javascript/components'
3
+ import { useContent } from '@thoughtbot/superglue'
4
+
5
+ type ContentProps = {
6
+ id: string
7
+ <%- attributes.each do |attr| -%>
8
+ <%= attr.column_name.camelize(:lower)%>: <%= json_mappable_type(attr)%>
9
+ <%- end -%>
10
+ createdAt: string
11
+ updatedAt: string
12
+ <%= js_plural_table_name %>Path: string
13
+ edit<%= js_singular_table_name(:upper) %>Path: string
14
+ }
15
+
16
+ export default function <%= js_plural_table_name(:upper) %>Show() {
17
+ const {
18
+ <%- showable_attributes.each do |attr| -%>
19
+ <%= attr.column_name.camelize(:lower) %>,
20
+ <%- end -%>
21
+ edit<%= js_singular_table_name(:upper) %>Path,
22
+ <%= js_plural_table_name %>Path,
23
+ } = useContent<ContentProps>()
24
+
25
+ return (
26
+ <Layout>
27
+ <%- showable_attributes.each do |attr| -%>
28
+ <p>
29
+ <strong><%= attr.column_name.humanize %>:</strong>
30
+ {<%=attr.column_name.camelize(:lower)%>}
31
+ </p>
32
+ <%- end -%>
33
+ <a href={ edit<%= js_singular_table_name(:upper) %>Path } data-sg-visit>Edit</a>
34
+ <a href={ <%= js_plural_table_name %>Path } data-sg-visit>Back</a>
35
+ </Layout>
36
+ )
37
+ }
@@ -0,0 +1,180 @@
1
+ require "rails/generators/named_base"
2
+ require "rails/generators/resource_helpers"
3
+
4
+ module Superglue
5
+ module Generators
6
+ class ViewCollectionGenerator < Rails::Generators::NamedBase
7
+ include Rails::Generators::ResourceHelpers
8
+
9
+ source_root File.expand_path("../templates", __FILE__)
10
+
11
+ argument :attributes, type: :array, default: [], banner: "field:type field:type"
12
+
13
+ class_option :typescript,
14
+ type: :boolean,
15
+ required: false,
16
+ default: false,
17
+ desc: "Use typescript"
18
+
19
+ def create_root_folder
20
+ path = File.join("app/views", controller_file_path)
21
+ empty_directory path unless File.directory?(path)
22
+ end
23
+
24
+ def copy_erb_files
25
+ available_views.each do |view|
26
+ @action_name = view
27
+ filename = filename_with_html_extensions(view)
28
+ template "erb/" + filename, File.join("app/views", controller_file_path, filename)
29
+ end
30
+ end
31
+
32
+ def copy_prop_files
33
+ available_views.each do |view|
34
+ @action_name = view
35
+ filename = filename_with_extensions(view)
36
+ template "props/" + filename, File.join("app/views", controller_file_path, filename)
37
+ end
38
+
39
+ template "props/_form.json.props", File.join("app/views", controller_file_path, "_form.json.props")
40
+ end
41
+
42
+ def copy_js_files
43
+ available_views.each do |view|
44
+ @action_name = view
45
+ if options["typescript"]
46
+ filename = filename_with_tsx_extensions(view)
47
+ template "ts/" + filename, File.join("app/views", controller_file_path, filename)
48
+ else
49
+ filename = filename_with_jsx_extensions(view)
50
+ template "js/" + filename, File.join("app/views", controller_file_path, filename)
51
+ end
52
+ end
53
+ end
54
+
55
+ def append_mapping
56
+ available_views.each do |action|
57
+ app_js = if options["typescript"]
58
+ "#{app_js_path}/page_to_page_mapping.ts"
59
+ else
60
+ "#{app_js_path}/page_to_page_mapping.js"
61
+ end
62
+
63
+ component_name = [plural_table_name, action].map(&:camelcase).join
64
+
65
+ if match_file(app_js, /pageIdentifierToPageComponent = {$/)
66
+ prepend_to_file app_js do
67
+ "import #{component_name} from '#{view_path}/#{controller_file_path}/#{action}'\n"
68
+ end
69
+
70
+ inject_into_file app_js, after: /pageIdentifierToPageComponent = {$/ do
71
+ "\n '#{[controller_file_path, action].join("/")}': #{component_name},"
72
+ end
73
+ else
74
+ say "Skipping appending to #{app_js}, you may be using a bundler that supports globing."
75
+ end
76
+ end
77
+ end
78
+
79
+ protected
80
+
81
+ def js_component(attribute)
82
+ case attribute.type
83
+ when :string
84
+ "TextField"
85
+ when :text, :rich_text
86
+ "TextArea"
87
+ when :integer, :float, :decimal
88
+ "NumberField"
89
+ when :datetime, :timestamp, :time
90
+ "DatetimeLocalField"
91
+ when :date
92
+ "DateField"
93
+ when :boolean
94
+ "Checkbox"
95
+ when :attachments, :attachment
96
+ "File"
97
+ else
98
+ "TextField"
99
+ end
100
+ end
101
+
102
+ def json_mappable_type(attribute)
103
+ case attribute.type
104
+ when :string
105
+ "string"
106
+ when :text, :rich_text
107
+ "string"
108
+ when :integer, :float, :decimal
109
+ "number"
110
+ when :datetime, :timestamp, :time
111
+ "string"
112
+ when :date
113
+ "string"
114
+ when :boolean
115
+ "boolean"
116
+ else
117
+ "string"
118
+ end
119
+ end
120
+
121
+ def js_singular_table_name(casing = :lower)
122
+ singular_table_name.camelize(casing)
123
+ end
124
+
125
+ def js_plural_table_name(casing = :lower)
126
+ plural_table_name.camelize(casing)
127
+ end
128
+
129
+ def available_views
130
+ %w[index edit show new]
131
+ end
132
+
133
+ def view_path
134
+ "@views"
135
+ end
136
+
137
+ def app_js_path
138
+ "app/javascript"
139
+ end
140
+
141
+ attr_reader :action_name
142
+
143
+ def attributes_names
144
+ [:id] + super
145
+ end
146
+
147
+ def filename_with_extensions(name)
148
+ [name, :json, :props].join(".")
149
+ end
150
+
151
+ def filename_with_jsx_extensions(name)
152
+ [name, :jsx].join(".")
153
+ end
154
+
155
+ def filename_with_tsx_extensions(name)
156
+ [name, :tsx].join(".")
157
+ end
158
+
159
+ def filename_with_html_extensions(name)
160
+ [name, :html, :erb].join(".")
161
+ end
162
+
163
+ def showable_attributes
164
+ attributes.reject { |attr| %w[password password_confirmation].include? attr.name }
165
+ end
166
+
167
+ def attributes_list_with_timestamps
168
+ attributes_list(attributes_names + %w[created_at updated_at])
169
+ end
170
+
171
+ def attributes_list(attributes = attributes_names)
172
+ if self.attributes.any? { |attr| attr.name == "password" && attr.type == :digest }
173
+ attributes = attributes.reject { |name| %w[password password_confirmation].include? name }
174
+ end
175
+
176
+ attributes
177
+ end
178
+ end
179
+ end
180
+ end