potassium 6.5.0 → 6.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/README.md +30 -1
  4. data/docs/CONTRIBUTING.md +2 -2
  5. data/lib/potassium/assets/.circleci/config.yml.erb +31 -8
  6. data/lib/potassium/assets/.eslintrc.json +16 -9
  7. data/lib/potassium/assets/.rubocop.yml +1 -0
  8. data/lib/potassium/assets/Aptfile +5 -0
  9. data/lib/potassium/assets/README.yml +30 -6
  10. data/lib/potassium/assets/active_admin/admin-component.vue +22 -30
  11. data/lib/potassium/assets/app/javascript/api/index.ts +55 -0
  12. data/lib/potassium/assets/app/javascript/{app.spec.js → components/app.spec.ts} +1 -1
  13. data/lib/potassium/assets/app/javascript/components/app.vue +9 -0
  14. data/lib/potassium/assets/app/javascript/types/vue.d.ts +5 -0
  15. data/lib/potassium/assets/app/javascript/utils/case-converter.ts +39 -0
  16. data/lib/potassium/assets/app/jobs/shrine_promote_job.rb +14 -0
  17. data/lib/potassium/assets/app/mailers/application_mailer.rb +1 -1
  18. data/lib/potassium/assets/app/mailers/example_mailer.rb +6 -0
  19. data/lib/potassium/assets/app/serializers/base_serializer.rb +3 -0
  20. data/lib/potassium/assets/app/serializers/concerns/image_handling_attributes.rb +20 -0
  21. data/lib/potassium/assets/app/uploaders/cover_image_uploader.rb +52 -0
  22. data/lib/potassium/assets/app/views/example_mailer/example_mail.html.mjml +7 -0
  23. data/lib/potassium/assets/app/views/layouts/default_mail.html.mjml +49 -0
  24. data/lib/potassium/assets/config/initializers/shrine/plugins/image_handling_utilities.rb +143 -0
  25. data/lib/potassium/assets/config/mailer.rb.erb +0 -2
  26. data/lib/potassium/assets/config/shrine.rb +15 -0
  27. data/lib/potassium/assets/config/webpack/rules/css.js +5 -0
  28. data/lib/potassium/assets/config/webpack/rules/index.js +11 -0
  29. data/lib/potassium/assets/config/webpack/rules/jquery.js +11 -0
  30. data/lib/potassium/assets/config/webpack/rules/typescript.js +32 -0
  31. data/lib/potassium/assets/config/webpack/rules/vue.js +19 -0
  32. data/lib/potassium/assets/config/webpack/webpack.config.js +4 -0
  33. data/lib/potassium/assets/public/mails/platanus-logo.png +0 -0
  34. data/lib/potassium/assets/tsconfig.json +32 -0
  35. data/lib/potassium/cli/commands/create.rb +3 -1
  36. data/lib/potassium/cli_options.rb +15 -3
  37. data/lib/potassium/platanus_config.rb +20 -0
  38. data/lib/potassium/recipes/admin.rb +50 -1
  39. data/lib/potassium/recipes/api.rb +6 -85
  40. data/lib/potassium/recipes/coverage.rb +31 -0
  41. data/lib/potassium/recipes/file_storage.rb +50 -0
  42. data/lib/potassium/recipes/front_end.rb +96 -110
  43. data/lib/potassium/recipes/google_tag_manager.rb +1 -1
  44. data/lib/potassium/recipes/mailer.rb +22 -10
  45. data/lib/potassium/recipes/mjml.rb +31 -0
  46. data/lib/potassium/recipes/node.rb +11 -13
  47. data/lib/potassium/recipes/style.rb +10 -2
  48. data/lib/potassium/recipes/vue_admin.rb +38 -8
  49. data/lib/potassium/templates/application.rb +1 -0
  50. data/lib/potassium/version.rb +8 -3
  51. data/spec/features/api_spec.rb +6 -1
  52. data/spec/features/coverage_spec.rb +17 -0
  53. data/spec/features/file_storage_spec.rb +102 -26
  54. data/spec/features/front_end_spec.rb +17 -46
  55. data/spec/features/mailer_spec.rb +79 -33
  56. data/spec/features/mjml_spec.rb +53 -0
  57. data/spec/features/vue_admin_spec.rb +0 -10
  58. data/spec/support/shared_examples.rb +5 -0
  59. metadata +30 -21
  60. data/lib/potassium/assets/active_admin/admin_application.js +0 -14
  61. data/lib/potassium/assets/active_admin/init_activeadmin_vue.rb +0 -10
  62. data/lib/potassium/assets/app/graphql/graphql_controller.rb +0 -55
  63. data/lib/potassium/assets/app/graphql/mutations/login_mutation.rb +0 -23
  64. data/lib/potassium/assets/app/graphql/queries/base_query.rb +0 -4
  65. data/lib/potassium/assets/app/graphql/types/base/base_argument.rb +0 -4
  66. data/lib/potassium/assets/app/graphql/types/base/base_enum.rb +0 -4
  67. data/lib/potassium/assets/app/graphql/types/base/base_field.rb +0 -5
  68. data/lib/potassium/assets/app/graphql/types/base/base_input_object.rb +0 -5
  69. data/lib/potassium/assets/app/graphql/types/base/base_interface.rb +0 -7
  70. data/lib/potassium/assets/app/graphql/types/base/base_object.rb +0 -5
  71. data/lib/potassium/assets/app/graphql/types/base/base_scalar.rb +0 -4
  72. data/lib/potassium/assets/app/graphql/types/base/base_union.rb +0 -4
  73. data/lib/potassium/assets/app/graphql/types/mutation_type.rb +0 -10
  74. data/lib/potassium/assets/app/graphql/types/query_type.rb +0 -13
  75. data/lib/potassium/assets/config/graphql_playground.rb +0 -20
  76. data/spec/features/graphql_spec.rb +0 -71
@@ -44,6 +44,56 @@ class Recipes::FileStorage < Rails::AppBuilder
44
44
  copy_file('../assets/app/uploaders/image_uploader.rb', 'app/uploaders/image_uploader.rb')
45
45
  copy_file('../assets/app/uploaders/base_uploader.rb', 'app/uploaders/base_uploader.rb')
46
46
  append_to_file('.gitignore', "/public/uploads\n")
47
+ add_image_handling_and_cover_image_uploader
48
+ end
49
+
50
+ def add_image_handling_and_cover_image_uploader
51
+ gather_gem('image_processing', '~> 1.8')
52
+ gather_gem('blurhash', '~> 0.1')
53
+ gather_gem('ruby-vips', '~> 2.1')
54
+ append_to_file('.env.development', "SHRINE_SECRET_KEY=#{SecureRandom.hex}\n")
55
+ copy_file('../assets/app/jobs/shrine_promote_job.rb', 'app/jobs/shrine_promote_job.rb')
56
+ add_image_handling_plugin
57
+ add_cover_image_uploader
58
+ add_image_handling_serializer_concern if get(:api)
59
+ add_image_handling_heroku_setup if get(:heroku)
60
+ end
61
+
62
+ def add_cover_image_uploader
63
+ copy_file(
64
+ '../assets/app/uploaders/cover_image_uploader.rb', 'app/uploaders/cover_image_uploader.rb'
65
+ )
66
+ insert_into_file "config/routes.rb", after: "Rails.application.routes.draw do\n" do
67
+ <<~HERE.indent(2)
68
+ mount CoverImageUploader.derivation_endpoint => "/derivations/cover_image"
69
+ HERE
70
+ end
71
+ end
72
+
73
+ def add_image_handling_plugin
74
+ copy_file(
75
+ '../assets/config/initializers/shrine/plugins/image_handling_utilities.rb',
76
+ 'config/initializers/shrine/plugins/image_handling_utilities.rb'
77
+ )
78
+ end
79
+
80
+ def add_image_handling_serializer_concern
81
+ copy_file(
82
+ '../assets/app/serializers/concerns/image_handling_attributes.rb',
83
+ 'app/serializers/concerns/image_handling_attributes.rb'
84
+ )
85
+ copy_file('../assets/app/serializers/base_serializer.rb', 'app/serializers/base_serializer.rb')
86
+ end
87
+
88
+ def add_image_handling_heroku_setup
89
+ prepend_file(
90
+ '.buildpacks',
91
+ <<~HERE
92
+ https://github.com/heroku/heroku-buildpack-apt
93
+ https://github.com/brandoncc/heroku-buildpack-vips
94
+ HERE
95
+ )
96
+ copy_file('../assets/Aptfile', 'Aptfile')
47
97
  end
48
98
 
49
99
  def common_setup
@@ -1,10 +1,15 @@
1
1
  class Recipes::FrontEnd < Rails::AppBuilder
2
2
  VUE_LOADER_VERSION = Potassium::VUE_LOADER_VERSION
3
+ VUE_VERSION = Potassium::VUE_VERSION
4
+ VUE_TEST_UTILS_VERSION = Potassium::VUE_TEST_UTILS_VERSION
5
+ POSTCSS_VERSION = Potassium::POSTCSS_VERSION
6
+ TAILWINDCSS_VERSION = Potassium::TAILWINDCSS_VERSION
7
+ AUTOPREFIXER_VERSION = Potassium::AUTOPREFIXER_VERSION
8
+ JEST_VERSION = Potassium::JEST_VERSION
3
9
 
4
10
  def ask
5
11
  frameworks = {
6
12
  vue: "Vue",
7
- angular: "Angular 2",
8
13
  none: "None"
9
14
  }
10
15
 
@@ -17,15 +22,20 @@ class Recipes::FrontEnd < Rails::AppBuilder
17
22
  end
18
23
 
19
24
  def create
25
+ gather_gem('shakapacker', '~> 6.2.0')
20
26
  recipe = self
21
- after(:gem_install) do
22
- value = get(:front_end)
27
+ after(:gem_install, wrap_in_action: :webpacker_install) do
23
28
  run "rails webpacker:install"
24
- run "rails webpacker:install:#{value}" unless [:none, :None].include? value.to_sym
25
-
29
+ end
30
+ after(:webpacker_install) do
31
+ value = get(:front_end)
32
+ recipe.copy_webpack_rules
33
+ recipe.add_assets_path
34
+ recipe.setup_typescript
26
35
  recipe.setup_vue if value == :vue
27
36
  recipe.add_responsive_meta_tag
28
37
  recipe.setup_tailwind
38
+ recipe.setup_api_client
29
39
  add_readme_header :webpack
30
40
  end
31
41
  end
@@ -38,13 +48,37 @@ class Recipes::FrontEnd < Rails::AppBuilder
38
48
  def installed?
39
49
  package_file = 'package.json'
40
50
  return false unless file_exist?(package_file)
51
+
41
52
  package_content = read_file(package_file)
42
- package_content.include?("\"@angular/core\"") || package_content.include?("\"vue\"")
53
+ package_content.include?("\"vue\"")
54
+ end
55
+
56
+ def copy_webpack_rules
57
+ copy_file '../assets/config/webpack/webpack.config.js',
58
+ 'config/webpack/webpack.config.js',
59
+ force: true
60
+ copy_file '../assets/config/webpack/rules/index.js', 'config/webpack/rules/index.js'
61
+ copy_file '../assets/config/webpack/rules/css.js', 'config/webpack/rules/css.js'
62
+ copy_file '../assets/config/webpack/rules/vue.js', 'config/webpack/rules/vue.js'
63
+ copy_file '../assets/config/webpack/rules/jquery.js', 'config/webpack/rules/jquery.js'
64
+ copy_file '../assets/config/webpack/rules/typescript.js', 'config/webpack/rules/typescript.js'
65
+ end
66
+
67
+ def add_assets_path
68
+ gsub_file(
69
+ 'config/webpacker.yml',
70
+ 'additional_paths: []',
71
+ "additional_paths: ['app/assets']"
72
+ )
73
+ end
74
+
75
+ def setup_typescript
76
+ run "bin/yarn add typescript fork-ts-checker-webpack-plugin ts-loader @types/node"
77
+ copy_file '../assets/tsconfig.json', 'tsconfig.json'
43
78
  end
44
79
 
45
80
  def setup_vue_with_compiler_build
46
- application_js = 'app/javascript/packs/application.js'
47
- remove_file "app/javascript/packs/hello_vue.js"
81
+ application_js = 'app/javascript/application.js'
48
82
  create_file application_js, application_js_content, force: true
49
83
 
50
84
  layout_file = "app/views/layouts/application.html.erb"
@@ -63,102 +97,66 @@ class Recipes::FrontEnd < Rails::AppBuilder
63
97
  end
64
98
 
65
99
  def setup_tailwind
66
- run "bin/yarn add tailwindcss@#{Potassium::TAILWINDCSS}"
67
- specify_autoprefixer_postcss_compatibility_versions
100
+ run "bin/yarn add css-loader style-loader mini-css-extract-plugin @types/tailwindcss "\
101
+ "css-minimizer-webpack-plugin postcss@#{POSTCSS_VERSION} postcss-loader "\
102
+ "tailwindcss@#{TAILWINDCSS_VERSION} autoprefixer@#{AUTOPREFIXER_VERSION} sass sass-loader "\
103
+ "eslint-plugin-tailwindcss"
104
+ run "npx tailwindcss init -p"
68
105
  setup_client_css
69
106
  remove_server_css_requires
70
107
  setup_tailwind_requirements
71
108
  end
72
109
 
73
110
  def setup_jest
74
- run 'bin/yarn add jest vue-jest babel-jest @vue/test-utils jest-serializer-vue babel-core@^7.0.0-bridge.0 --dev'
111
+ run "bin/yarn add jest@#{JEST_VERSION} @vue/vue3-jest@#{JEST_VERSION} "\
112
+ "babel-jest@#{JEST_VERSION} @vue/test-utils@#{VUE_TEST_UTILS_VERSION} ts-jest@#{JEST_VERSION} "\
113
+ "jest-environment-jsdom@#{JEST_VERSION} --dev"
114
+ run "bin/yarn add @types/jest@#{JEST_VERSION}"
75
115
  json_file = File.read(Pathname.new("package.json"))
76
116
  js_package = JSON.parse(json_file)
77
117
  js_package = js_package.merge(jest_config)
78
118
  json_string = JSON.pretty_generate(js_package)
79
119
  create_file 'package.json', json_string, force: true
80
120
 
81
- copy_file '../assets/app/javascript/app.spec.js', 'app/javascript/app.spec.js'
82
- end
83
-
84
- def setup_apollo
85
- run 'bin/yarn add vue-apollo graphql apollo-client apollo-link apollo-link-http apollo-cache-inmemory graphql-tag'
86
-
87
- inject_into_file(
88
- 'app/javascript/packs/application.js',
89
- apollo_imports,
90
- after: "import App from '../app.vue';"
91
- )
92
-
93
- inject_into_file(
94
- 'app/javascript/packs/application.js',
95
- apollo_loading,
96
- after: "import VueApollo from 'vue-apollo';"
97
- )
98
- inject_into_file(
99
- 'app/javascript/packs/application.js',
100
- "\n apolloProvider,",
101
- after: "components: { App },"
102
- )
103
- end
104
-
105
- def foce_vue_loader_version
106
- run "bin/yarn add vue-loader@#{VUE_LOADER_VERSION}"
121
+ copy_file '../assets/app/javascript/components/app.spec.ts',
122
+ 'app/javascript/components/app.spec.ts'
107
123
  end
108
124
 
109
125
  def setup_vue
110
- foce_vue_loader_version
126
+ run "bin/yarn add vue@#{VUE_VERSION} vue-loader@#{VUE_LOADER_VERSION} "\
127
+ "babel-preset-typescript-vue3 @types/humps"
128
+ run "bin/yarn add vue-tsc --dev"
129
+ gsub_file(
130
+ 'config/webpack/webpack.config.js',
131
+ ' merge(typescriptConfig, cssConfig, jQueryConfig, webpackConfig);',
132
+ ' merge(vueConfig, typescriptConfig, cssConfig, jQueryConfig, webpackConfig);'
133
+ )
134
+ copy_file '../assets/app/javascript/components/app.vue', 'app/javascript/components/app.vue'
135
+ copy_file '../assets/app/javascript/types/vue.d.ts', 'app/javascript/types/vue.d.ts'
111
136
  setup_vue_with_compiler_build
112
- setup_jest
113
- if get(:api) == :graphql
114
- setup_apollo
137
+ recipe = self
138
+ run_action(:setup_jest) do
139
+ recipe.setup_jest
115
140
  end
116
141
  end
117
142
 
143
+ def setup_api_client
144
+ run "bin/yarn add axios humps"
145
+ copy_file '../assets/app/javascript/api/index.ts', 'app/javascript/api/index.ts'
146
+ copy_file '../assets/app/javascript/utils/case-converter.ts',
147
+ 'app/javascript/utils/case-converter.ts'
148
+ end
149
+
118
150
  private
119
151
 
120
152
  def frameworks(framework)
121
153
  frameworks = {
122
154
  vue: "vue",
123
- angular: "angular",
124
155
  none: nil
125
156
  }
126
157
  frameworks[framework]
127
158
  end
128
159
 
129
- def apollo_imports
130
- <<~JS
131
- \n
132
- import { ApolloClient } from 'apollo-client';
133
- import { createHttpLink } from 'apollo-link-http';
134
- import { InMemoryCache } from 'apollo-cache-inmemory';
135
- import VueApollo from 'vue-apollo';
136
- JS
137
- end
138
-
139
- def apollo_loading
140
- <<~JS
141
- \n
142
- const httpLink = createHttpLink({
143
- uri: `${window.location.origin}/graphql`,
144
- })
145
- const cache = new InMemoryCache()
146
- const apolloClient = new ApolloClient({
147
- link: httpLink,
148
- cache,
149
- })
150
-
151
- Vue.use(VueApollo)
152
- const apolloProvider = new VueApollo({
153
- defaultClient: apolloClient,
154
- })
155
- JS
156
- end
157
-
158
- def specify_autoprefixer_postcss_compatibility_versions
159
- run 'bin/yarn -D add postcss@^7 autoprefixer@^9'
160
- end
161
-
162
160
  def setup_client_css
163
161
  application_css = 'app/javascript/css/application.css'
164
162
  create_file application_css, "", force: true
@@ -167,14 +165,14 @@ class Recipes::FrontEnd < Rails::AppBuilder
167
165
  layout_file = "app/views/layouts/application.html.erb"
168
166
  insert_into_file layout_file, stylesheet_pack_tag, before: "</head>"
169
167
 
170
- application_js = 'app/javascript/packs/application.js'
168
+ application_js = 'app/javascript/application.js'
171
169
  if get(:front_end) != :vue
172
- create_file application_js, "import '../css/application.css';\n", force: true
170
+ create_file application_js, "import './css/application.css';\n", force: true
173
171
  else
174
172
  insert_into_file(
175
173
  application_js,
176
- "\nimport '../css/application.css';",
177
- after: "import App from '../app.vue';"
174
+ "\nimport './css/application.css';",
175
+ after: "import App from './components/app.vue';"
178
176
  )
179
177
  end
180
178
  end
@@ -185,9 +183,6 @@ class Recipes::FrontEnd < Rails::AppBuilder
185
183
 
186
184
  tailwind_config = 'tailwind.config.js'
187
185
  create_file tailwind_config, tailwind_config_content, force: true
188
-
189
- postcss_file = 'postcss.config.js'
190
- insert_into_file postcss_file, postcss_require_tailwind, after: "plugins: [\n"
191
186
  end
192
187
 
193
188
  def remove_server_css_requires
@@ -197,14 +192,14 @@ class Recipes::FrontEnd < Rails::AppBuilder
197
192
 
198
193
  def application_js_content
199
194
  <<~JS
200
- import Vue from 'vue/dist/vue.esm';
201
- import App from '../app.vue';
195
+ import { createApp } from 'vue';
196
+ import App from './components/app.vue';
202
197
 
203
198
  document.addEventListener('DOMContentLoaded', () => {
204
- const app = new Vue({
205
- el: '#vue-app',
199
+ const app = createApp({
206
200
  components: { App },
207
201
  });
202
+ app.mount('#vue-app');
208
203
 
209
204
  return app;
210
205
  });
@@ -213,9 +208,9 @@ class Recipes::FrontEnd < Rails::AppBuilder
213
208
 
214
209
  def tailwind_client_css
215
210
  <<~CSS
216
- @import 'tailwindcss/base';
217
- @import 'tailwindcss/components';
218
- @import 'tailwindcss/utilities';
211
+ @tailwind base;
212
+ @tailwind components;
213
+ @tailwind utilities;
219
214
  CSS
220
215
  end
221
216
 
@@ -228,26 +223,16 @@ class Recipes::FrontEnd < Rails::AppBuilder
228
223
  },
229
224
  variants: {},
230
225
  plugins: [],
231
- purge: {
232
- enabled: process.env.NODE_ENV === 'production',
233
- content: [
234
- './app/**/*.html',
235
- './app/**/*.vue',
236
- './app/**/*.js',
237
- './app/**/*.erb',
238
- ],
239
- }
226
+ content: [
227
+ './app/**/*.html',
228
+ './app/**/*.vue',
229
+ './app/**/*.js',
230
+ './app/**/*.erb',
231
+ ],
240
232
  };
241
233
  JS
242
234
  end
243
235
 
244
- def postcss_require_tailwind
245
- <<-JS.gsub(/^ {4}/, ' ')
246
- require('tailwindcss'),
247
- require('autoprefixer'),
248
- JS
249
- end
250
-
251
236
  def jest_config
252
237
  {
253
238
  "scripts": {
@@ -265,18 +250,19 @@ class Recipes::FrontEnd < Rails::AppBuilder
265
250
  "moduleNameMapper": {
266
251
  "^@/(.*)$": "app/javascript/$1"
267
252
  },
253
+ "testEnvironmentOptions": {
254
+ "customExportConditions": ["node", "node-addons"]
255
+ },
268
256
  "moduleFileExtensions": [
269
257
  "js",
258
+ "ts",
270
259
  "json",
271
260
  "vue"
272
261
  ],
273
262
  "transform": {
274
- "^.+\\.js$": "<rootDir>/node_modules/babel-jest",
275
- ".*\\.(vue)$": "<rootDir>/node_modules/vue-jest"
263
+ "^.+\\.ts$": "ts-jest",
264
+ ".*\\.(vue)$": "@vue/vue3-jest"
276
265
  },
277
- "snapshotSerializers": [
278
- "<rootDir>/node_modules/jest-serializer-vue"
279
- ],
280
266
  "testEnvironment": "jsdom"
281
267
  }
282
268
  }
@@ -87,7 +87,7 @@ class Recipes::GoogleTagManager < Rails::AppBuilder
87
87
  'https://www.google-analytics.com',
88
88
  'https://ssl.google-analytics.com'
89
89
  )
90
- policy.img_src :self, :https, 'https://www.googletagmanager.com', 'https://www.google-analytics.com'
90
+ policy.img_src :self, 'data:', :https, 'https://www.googletagmanager.com', 'https://www.google-analytics.com'
91
91
  end
92
92
  HERE
93
93
  end
@@ -63,20 +63,32 @@ class Recipes::Mailer < Rails::AppBuilder
63
63
  gather_gem 'recipient_interceptor'
64
64
  end
65
65
 
66
+ def config_environments
67
+ gsub_file 'config/environments/production.rb', /$\s*config.action_mailer.*/, ''
68
+ asset_host_dev = <<~RUBY
69
+ config.action_mailer.asset_host = "http://\#{ENV.fetch('APPLICATION_HOST')}"
70
+ RUBY
71
+ application asset_host_dev, env: "development"
72
+ asset_host_prod = <<~RUBY
73
+ config.action_mailer.asset_host = "https://\#{ENV.fetch('APPLICATION_HOST')}"
74
+ RUBY
75
+ application asset_host_prod, env: "production"
76
+ mailer_config = <<~RUBY
77
+ require Rails.root.join("config", "mailer")
78
+ RUBY
79
+
80
+ prepend_file "config/environments/production.rb", mailer_config
81
+ end
82
+
66
83
  def config(service)
67
84
  template "../assets/config/mailer.rb.erb", 'config/mailer.rb'
68
- gsub_file 'config/environments/production.rb', /$\s*config.action_mailer.*/, ''
85
+
69
86
  append_to_file '.env.development', "APPLICATION_HOST=localhost:3000\n"
70
87
  append_to_file '.env.development', "EMAIL_RECIPIENTS=\n"
71
88
 
72
- mailer_config =
73
- <<~RUBY
74
- require Rails.root.join("config", "mailer")
75
- RUBY
76
-
77
- prepend_file "config/environments/production.rb", mailer_config
78
- copy_file '../assets/app/mailers/application_mailer.rb', 'app/mailers/application_mailer.rb', force: true
79
-
89
+ copy_file '../assets/app/mailers/application_mailer.rb', 'app/mailers/application_mailer.rb',
90
+ force: true
91
+ config_environments
80
92
  send(service[:name])
81
93
  end
82
94
 
@@ -88,7 +100,7 @@ class Recipes::Mailer < Rails::AppBuilder
88
100
  }
89
101
  RUBY
90
102
  inject_into_file 'config/mailer.rb', sendgrid_settings,
91
- after: "Rails.application.config.action_mailer.delivery_method = :sendgrid\n"
103
+ after: "Rails.application.config.action_mailer.delivery_method = :sendgrid\n"
92
104
  sendgrid_dev_settings = <<~RUBY
93
105
  Rails.application.config.action_mailer.sendgrid_dev_settings = {
94
106
  api_key: ENV['SENDGRID_API_KEY']
@@ -0,0 +1,31 @@
1
+ class Recipes::Mjml < Rails::AppBuilder
2
+ def create
3
+ return if get(:email_service).to_s.downcase.to_sym == :none
4
+
5
+ gather_gem 'mjml-rails'
6
+ after(:gem_install) do
7
+ run 'bin/yarn add mjml'
8
+ mjml_config
9
+ end
10
+ end
11
+
12
+ def installed?
13
+ gem_exists?(/mjml-rails/)
14
+ end
15
+
16
+ def install
17
+ create
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def mjml_config
24
+ copy_file '../assets/app/views/layouts/default_mail.html.mjml',
25
+ 'app/views/layouts/default_mail.html.mjml', force: true
26
+ copy_file '../assets/app/mailers/example_mailer.rb', 'app/mailers/example_mailer.rb', force: true
27
+ copy_file '../assets/app/views/example_mailer/example_mail.html.mjml',
28
+ 'app/views/example_mailer/example_mail.html.mjml', force: true
29
+ copy_file '../assets/public/mails/platanus-logo.png',
30
+ 'public/mails/platanus-logo.png', force: true
31
+ end
@@ -3,19 +3,17 @@ require 'semantic'
3
3
  require 'json'
4
4
 
5
5
  class Recipes::Node < Rails::AppBuilder
6
- def create
7
- info "Using node version LTS #{version}"
8
- create_file '.node-version', version, force: true
9
- json_file = File.read(Pathname.new("package.json"))
10
- js_package = JSON.parse(json_file)
11
- js_package["engines"] = { "node" => "#{version}.x" }
12
- json_string = JSON.pretty_generate(js_package)
13
- create_file 'package.json', json_string, force: true
14
- end
6
+ NODE_VERSION = Potassium::NODE_VERSION
15
7
 
16
- private
17
-
18
- def version
19
- Potassium::NODE_VERSION
8
+ def create
9
+ info "Using node version LTS #{NODE_VERSION}"
10
+ create_file '.node-version', NODE_VERSION, force: true
11
+ after(:webpacker_install) do
12
+ json_file = File.read(Pathname.new("package.json"))
13
+ js_package = JSON.parse(json_file)
14
+ js_package["engines"] = { "node" => "#{NODE_VERSION}.x" }
15
+ json_string = JSON.pretty_generate(js_package)
16
+ create_file 'package.json', json_string, force: true
17
+ end
20
18
  end
21
19
  end
@@ -17,9 +17,17 @@ class Recipes::Style < Rails::AppBuilder
17
17
  gather_gem 'rubocop-performance'
18
18
  gather_gem 'rubocop-rails'
19
19
  gather_gem 'rubocop-rspec', Potassium::RUBOCOP_RSPEC_VERSION
20
+ gather_gem 'rubocop-platanus'
21
+ end
22
+
23
+ after(:webpacker_install) do
24
+ run "yarn add --dev stylelint eslint eslint-plugin-import "\
25
+ "@typescript-eslint/eslint-plugin @types/jest @typescript-eslint/parser "\
26
+ "eslint-plugin-jest eslint-plugin-platanus"
27
+ if selected?(:front_end, :vue)
28
+ run 'yarn add --dev eslint-plugin-vue @vue/eslint-config-typescript'
29
+ end
20
30
  end
21
- run 'bin/yarn add --dev stylelint eslint eslint-plugin-import'
22
- run 'bin/yarn add --dev eslint-plugin-vue' if selected?(:front_end, :vue)
23
31
  end
24
32
 
25
33
  def add_config_files
@@ -35,15 +35,18 @@ class Recipes::VueAdmin < Rails::AppBuilder
35
35
  def add_vue_admin
36
36
  add_vue_component_library
37
37
  add_component_integration
38
- copy_file '../assets/active_admin/init_activeadmin_vue.rb',
39
- 'config/initializers/init_activeadmin_vue.rb'
40
- copy_file '../assets/active_admin/admin_application.js',
41
- 'app/javascript/packs/admin_application.js',
42
- force: true
43
- empty_directory 'app/javascript/components'
38
+ js_line = 'import "activeadmin_addons"'
39
+ gsub_file(
40
+ 'app/javascript/active_admin.js',
41
+ js_line,
42
+ <<~HERE
43
+ #{js_line}
44
+ #{active_admin_js}
45
+ HERE
46
+ )
44
47
  copy_file '../assets/active_admin/admin-component.vue',
45
- 'app/javascript/components/admin-component.vue',
46
- force: true
48
+ 'app/javascript/components/admin-component.vue',
49
+ force: true
47
50
  end
48
51
 
49
52
  def add_component_integration
@@ -121,4 +124,31 @@ class Recipes::VueAdmin < Rails::AppBuilder
121
124
  end
122
125
  HERE
123
126
  end
127
+
128
+ def active_admin_js
129
+ <<~HERE
130
+ import { createApp } from 'vue';
131
+ import AdminComponent from './components/admin-component.vue';
132
+
133
+ function onLoad() {
134
+ if (document.getElementById('wrapper') !== null) {
135
+ const app = createApp({
136
+ mounted() {
137
+ // We need to re-trigger DOMContentLoaded for ArcticAdmin after Vue replaces DOM elements
138
+ window.document.dispatchEvent(new Event('DOMContentLoaded', {
139
+ bubbles: true,
140
+ cancelable: true,
141
+ }));
142
+ },
143
+ });
144
+ app.component('AdminComponent', AdminComponent);
145
+ app.mount('#wrapper');
146
+ }
147
+
148
+ return null;
149
+ }
150
+
151
+ document.addEventListener('DOMContentLoaded', onLoad, { once: true });
152
+ HERE
153
+ end
124
154
  end
@@ -81,6 +81,7 @@ run_action(:recipe_loading) do
81
81
  create :admin
82
82
  create :vue_admin
83
83
  create :google_tag_manager
84
+ create :mjml
84
85
  end
85
86
 
86
87
  info "Gathered enough information. Applying the template. Wait a minute."
@@ -1,5 +1,5 @@
1
1
  module Potassium
2
- VERSION = "6.5.0"
2
+ VERSION = "6.7.0"
3
3
  RUBY_VERSION = "2.7.0"
4
4
  RAILS_VERSION = "~> 6.1.4.4"
5
5
  RUBOCOP_VERSION = "~> 1.9"
@@ -7,6 +7,11 @@ module Potassium
7
7
  POSTGRES_VERSION = "11.3"
8
8
  MYSQL_VERSION = "5.7"
9
9
  NODE_VERSION = "14"
10
- TAILWINDCSS = "npm:@tailwindcss/postcss7-compat"
11
- VUE_LOADER_VERSION = "^15.9.7"
10
+ TAILWINDCSS_VERSION = "^3"
11
+ POSTCSS_VERSION = "^8"
12
+ AUTOPREFIXER_VERSION = "^10"
13
+ VUE_VERSION = "^3.2.33"
14
+ VUE_LOADER_VERSION = "^16.8.3"
15
+ VUE_TEST_UTILS_VERSION = "^2.0.2"
16
+ JEST_VERSION = "^28.0.1"
12
17
  end
@@ -4,7 +4,7 @@ RSpec.describe "Api" do
4
4
  before :all do
5
5
  drop_dummy_database
6
6
  remove_project_directory
7
- create_dummy_project("api" => :rest)
7
+ create_dummy_project("api" => true)
8
8
  end
9
9
 
10
10
  it "adds power_api related gems to Gemfile" do
@@ -22,4 +22,9 @@ RSpec.describe "Api" do
22
22
  content = IO.read("#{project_path}/app/controllers/api/base_controller.rb")
23
23
  expect(content).to include("Api::BaseController < PowerApi::BaseController")
24
24
  end
25
+
26
+ it "installs internal API mode" do
27
+ content = IO.read("#{project_path}/app/controllers/api/internal/base_controller.rb")
28
+ expect(content).to include("Api::Internal::BaseController < Api::BaseController")
29
+ end
25
30
  end
@@ -23,4 +23,21 @@ RSpec.describe "Coverage" do
23
23
  content = IO.read("#{project_path}/spec/simplecov_config.rb")
24
24
  expect(content).to include("SimpleCov.start 'rails'")
25
25
  end
26
+
27
+ context "with vue" do
28
+ let(:node_modules_file) { IO.read("#{project_path}/package.json") }
29
+
30
+ before(:all) do
31
+ remove_project_directory
32
+ create_dummy_project("front_end" => "vue")
33
+ end
34
+
35
+ it "adds jest coverage configuration" do
36
+ expect(node_modules_file).to include('"collectCoverage": true')
37
+ end
38
+
39
+ it "adds jest text formatter package" do
40
+ expect(node_modules_file).to include('jest-text-formatter')
41
+ end
42
+ end
26
43
  end