potassium 6.5.0 → 6.6.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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -0
  3. data/README.md +30 -1
  4. data/docs/CONTRIBUTING.md +2 -2
  5. data/lib/potassium/assets/.circleci/config.yml.erb +11 -1
  6. data/lib/potassium/assets/.eslintrc.json +15 -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/{app.spec.js → components/app.spec.ts} +1 -1
  12. data/lib/potassium/assets/app/javascript/components/app.vue +9 -0
  13. data/lib/potassium/assets/app/javascript/types/vue.d.ts +5 -0
  14. data/lib/potassium/assets/app/jobs/shrine_promote_job.rb +14 -0
  15. data/lib/potassium/assets/app/mailers/application_mailer.rb +1 -1
  16. data/lib/potassium/assets/app/mailers/example_mailer.rb +6 -0
  17. data/lib/potassium/assets/app/serializers/base_serializer.rb +3 -0
  18. data/lib/potassium/assets/app/serializers/concerns/image_handling_attributes.rb +20 -0
  19. data/lib/potassium/assets/app/uploaders/cover_image_uploader.rb +52 -0
  20. data/lib/potassium/assets/app/views/example_mailer/example_mail.html.mjml +7 -0
  21. data/lib/potassium/assets/app/views/layouts/default_mail.html.mjml +49 -0
  22. data/lib/potassium/assets/config/initializers/shrine/plugins/image_handling_utilities.rb +143 -0
  23. data/lib/potassium/assets/config/mailer.rb.erb +0 -2
  24. data/lib/potassium/assets/config/shrine.rb +15 -0
  25. data/lib/potassium/assets/config/webpack/rules/css.js +5 -0
  26. data/lib/potassium/assets/config/webpack/rules/index.js +11 -0
  27. data/lib/potassium/assets/config/webpack/rules/jquery.js +11 -0
  28. data/lib/potassium/assets/config/webpack/rules/typescript.js +32 -0
  29. data/lib/potassium/assets/config/webpack/rules/vue.js +19 -0
  30. data/lib/potassium/assets/config/webpack/webpack.config.js +4 -0
  31. data/lib/potassium/assets/public/mails/platanus-logo.png +0 -0
  32. data/lib/potassium/assets/tsconfig.json +31 -0
  33. data/lib/potassium/cli/commands/create.rb +3 -1
  34. data/lib/potassium/cli_options.rb +15 -3
  35. data/lib/potassium/platanus_config.rb +20 -0
  36. data/lib/potassium/recipes/admin.rb +11 -0
  37. data/lib/potassium/recipes/api.rb +6 -85
  38. data/lib/potassium/recipes/coverage.rb +31 -0
  39. data/lib/potassium/recipes/file_storage.rb +50 -0
  40. data/lib/potassium/recipes/front_end.rb +82 -110
  41. data/lib/potassium/recipes/google_tag_manager.rb +1 -1
  42. data/lib/potassium/recipes/mailer.rb +22 -10
  43. data/lib/potassium/recipes/mjml.rb +31 -0
  44. data/lib/potassium/recipes/node.rb +11 -13
  45. data/lib/potassium/recipes/style.rb +9 -2
  46. data/lib/potassium/recipes/vue_admin.rb +38 -8
  47. data/lib/potassium/templates/application.rb +1 -0
  48. data/lib/potassium/version.rb +8 -3
  49. data/spec/features/api_spec.rb +6 -1
  50. data/spec/features/coverage_spec.rb +17 -0
  51. data/spec/features/file_storage_spec.rb +102 -26
  52. data/spec/features/front_end_spec.rb +14 -48
  53. data/spec/features/mailer_spec.rb +79 -33
  54. data/spec/features/mjml_spec.rb +53 -0
  55. data/spec/features/vue_admin_spec.rb +0 -10
  56. data/spec/support/shared_examples.rb +5 -0
  57. metadata +28 -21
  58. data/lib/potassium/assets/active_admin/admin_application.js +0 -14
  59. data/lib/potassium/assets/active_admin/init_activeadmin_vue.rb +0 -10
  60. data/lib/potassium/assets/app/graphql/graphql_controller.rb +0 -55
  61. data/lib/potassium/assets/app/graphql/mutations/login_mutation.rb +0 -23
  62. data/lib/potassium/assets/app/graphql/queries/base_query.rb +0 -4
  63. data/lib/potassium/assets/app/graphql/types/base/base_argument.rb +0 -4
  64. data/lib/potassium/assets/app/graphql/types/base/base_enum.rb +0 -4
  65. data/lib/potassium/assets/app/graphql/types/base/base_field.rb +0 -5
  66. data/lib/potassium/assets/app/graphql/types/base/base_input_object.rb +0 -5
  67. data/lib/potassium/assets/app/graphql/types/base/base_interface.rb +0 -7
  68. data/lib/potassium/assets/app/graphql/types/base/base_object.rb +0 -5
  69. data/lib/potassium/assets/app/graphql/types/base/base_scalar.rb +0 -4
  70. data/lib/potassium/assets/app/graphql/types/base/base_union.rb +0 -4
  71. data/lib/potassium/assets/app/graphql/types/mutation_type.rb +0 -10
  72. data/lib/potassium/assets/app/graphql/types/query_type.rb +0 -13
  73. data/lib/potassium/assets/config/graphql_playground.rb +0 -20
  74. data/spec/features/graphql_spec.rb +0 -71
@@ -0,0 +1,143 @@
1
+ class Shrine
2
+ module Plugins
3
+ module ImageHandlingUtilities
4
+ # rubocop:disable Metrics/ModuleLength
5
+ module AttachmentMethods
6
+ def included(klass)
7
+ super
8
+
9
+ define_instance_methods
10
+ define_class_methods(klass)
11
+ end
12
+
13
+ private
14
+
15
+ def define_instance_methods
16
+ define_with_stored_attacher_method
17
+ define_blurhash_instance_method
18
+ define_generate_derivatives_instance_method
19
+ define_generate_metadata_instance_method
20
+ define_generate_derivatives_and_metadata_instance_method
21
+ end
22
+
23
+ def define_blurhash_instance_method
24
+ name = @name
25
+ define_method(:"#{@name}_blurhash") do
26
+ send(name)&.metadata&.[]('blurhash')
27
+ end
28
+ end
29
+
30
+ def define_generate_derivatives_instance_method
31
+ name = @name
32
+ define_method(:"generate_#{name}_derivatives") do
33
+ send("with_stored_#{name}_attacher") do |attacher|
34
+ old_derivatives = attacher.derivatives
35
+
36
+ attacher.set_derivatives({})
37
+ attacher.create_derivatives
38
+
39
+ begin
40
+ attacher.atomic_persist
41
+ attacher.delete_derivatives(old_derivatives) if old_derivatives.present?
42
+ rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
43
+ attacher.delete_derivatives
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ def define_generate_metadata_instance_method
50
+ name = @name
51
+ define_method(:"generate_#{name}_metadata") do
52
+ send("with_stored_#{name}_attacher") do |attacher|
53
+ attacher.refresh_metadata!
54
+ attacher.atomic_persist
55
+ end
56
+ end
57
+ end
58
+
59
+ # rubocop:disable Metrics/MethodLength
60
+ def define_generate_derivatives_and_metadata_instance_method
61
+ name = @name
62
+ define_method(:"generate_#{name}_derivatives_and_metadata") do
63
+ send("with_stored_#{name}_attacher") do |attacher|
64
+ old_derivatives = attacher.derivatives
65
+
66
+ attacher.set_derivatives({})
67
+ attacher.file.open do
68
+ attacher.create_derivatives
69
+ attacher.refresh_metadata!
70
+ end
71
+
72
+ begin
73
+ attacher.atomic_persist
74
+ attacher.delete_derivatives(old_derivatives) if old_derivatives.present?
75
+ rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
76
+ attacher.delete_derivatives
77
+ end
78
+ end
79
+ end
80
+ end
81
+ # rubocop:enable Metrics/MethodLength
82
+
83
+ def define_class_methods(klass)
84
+ define_generate_all_derivatives_class_method(klass)
85
+ define_generate_all_metadata_class_method(klass)
86
+ define_generate_all_derivatives_and_metadata_class_method(klass)
87
+ end
88
+
89
+ def define_generate_all_derivatives_class_method(klass)
90
+ name = @name
91
+ klass.send(
92
+ :define_singleton_method, :"generate_all_#{name}_derivatives"
93
+ ) do |&error_block|
94
+ all.find_each do |record|
95
+ record.send(:"generate_#{name}_derivatives")
96
+ rescue StandardError => e
97
+ error_block.call(record, e) if error_block.present?
98
+ end
99
+ end
100
+ end
101
+
102
+ def define_generate_all_metadata_class_method(klass)
103
+ name = @name
104
+ klass.send(:define_singleton_method, :"generate_all_#{name}_metadata") do |&error_block|
105
+ all.find_each do |record|
106
+ record.send(:"generate_#{name}_metadata")
107
+ rescue StandardError => e
108
+ error_block.call(record, e) if error_block.present?
109
+ end
110
+ end
111
+ end
112
+
113
+ def define_generate_all_derivatives_and_metadata_class_method(klass)
114
+ name = @name
115
+ klass.send(
116
+ :define_singleton_method, :"generate_all_#{name}_derivatives_and_metadata"
117
+ ) do |&error_block|
118
+ all.find_each do |record|
119
+ record.send(:"generate_#{name}_derivatives_and_metadata")
120
+ rescue StandardError => e
121
+ error_block.call(record, e) if error_block.present?
122
+ end
123
+ end
124
+ end
125
+
126
+ def define_with_stored_attacher_method
127
+ name = @name
128
+ define_method(:"with_stored_#{name}_attacher") do |&block|
129
+ return if send(name).blank?
130
+
131
+ attacher = send(:"#{name}_attacher")
132
+
133
+ block.call(attacher) if attacher.stored?
134
+ end
135
+ private :"with_stored_#{name}_attacher"
136
+ end
137
+ end
138
+ # rubocop:enable Metrics/ModuleLength
139
+ end
140
+
141
+ register_plugin(:image_handling_utilities, ImageHandlingUtilities)
142
+ end
143
+ end
@@ -7,8 +7,6 @@ Rails.application.config.action_mailer.default_url_options = {
7
7
  }
8
8
 
9
9
  Rails.application.config.action_mailer.default_options = { from: ENV['DEFAULT_EMAIL_ADDRESS'] }
10
- ASSET_HOST = ENV.fetch("ASSET_HOST", ENV.fetch("APPLICATION_HOST"))
11
- Rails.application.config.action_mailer.asset_host = ASSET_HOST
12
10
 
13
11
  if ENV["EMAIL_RECIPIENTS"].present?
14
12
  Mail.register_interceptor RecipientInterceptor.new(
@@ -34,3 +34,18 @@ Shrine.plugin :activerecord
34
34
  Shrine.plugin :cached_attachment_data
35
35
  Shrine.plugin :restore_cached_data
36
36
  Shrine.plugin :determine_mime_type, analyzer: :marcel
37
+ Shrine.plugin :derivatives
38
+ Shrine.plugin :default_url
39
+ Shrine.plugin :derivation_endpoint, secret_key: ENV.fetch('SHRINE_SECRET_KEY')
40
+ Shrine.plugin :refresh_metadata
41
+ Shrine.plugin :backgrounding
42
+
43
+ Shrine::Attacher.promote_block do |attacher|
44
+ ShrinePromoteJob.perform_later(
45
+ attacher.class.name,
46
+ attacher.record.class.name,
47
+ attacher.record.id,
48
+ attacher.name,
49
+ attacher.file_data
50
+ )
51
+ end
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ resolve: {
3
+ extensions: ['.css', '.scss']
4
+ }
5
+ }
@@ -0,0 +1,11 @@
1
+ const vueConfig = require('./vue');
2
+ const cssConfig = require('./css');
3
+ const jQueryConfig = require('./jquery');
4
+ const typescriptConfig = require('./typescript');
5
+
6
+ module.exports = {
7
+ vueConfig,
8
+ cssConfig,
9
+ jQueryConfig,
10
+ typescriptConfig,
11
+ };
@@ -0,0 +1,11 @@
1
+ const webpack = require('webpack');
2
+
3
+ module.exports = {
4
+ plugins: [
5
+ new webpack.ProvidePlugin({
6
+ $: 'jquery',
7
+ jQuery: 'jquery',
8
+ "window.jQuery":"jquery"
9
+ })
10
+ ],
11
+ }
@@ -0,0 +1,32 @@
1
+ const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
2
+
3
+ module.exports = {
4
+ module: {
5
+ rules: [
6
+ {
7
+ test: /\.ts$/,
8
+ loader: 'ts-loader',
9
+ exclude: /node_modules/,
10
+ options: {
11
+ appendTsSuffixTo: [/\.vue$/],
12
+ transpileOnly: true,
13
+ },
14
+ },
15
+ ],
16
+ },
17
+ resolve: {
18
+ extensions: ['.ts'],
19
+ },
20
+ plugins: [
21
+ new ForkTsCheckerWebpackPlugin({
22
+ typescript: {
23
+ extensions: {
24
+ vue: {
25
+ enabled: true,
26
+ compiler: '@vue/compiler-sfc',
27
+ },
28
+ },
29
+ },
30
+ }),
31
+ ],
32
+ };
@@ -0,0 +1,19 @@
1
+ const { VueLoaderPlugin } = require('vue-loader')
2
+
3
+ module.exports = {
4
+ module: {
5
+ rules: [
6
+ {
7
+ test: /\.vue$/,
8
+ loader: 'vue-loader'
9
+ }
10
+ ]
11
+ },
12
+ plugins: [new VueLoaderPlugin()],
13
+ resolve: {
14
+ extensions: ['.vue'],
15
+ alias: {
16
+ 'vue$': 'vue/dist/vue.esm-bundler.js',
17
+ }
18
+ },
19
+ }
@@ -0,0 +1,4 @@
1
+ const { webpackConfig, merge } = require('shakapacker');
2
+ const { vueConfig, cssConfig, jQueryConfig, typescriptConfig } = require('./rules');
3
+
4
+ module.exports = merge(typescriptConfig, cssConfig, jQueryConfig, webpackConfig);
@@ -0,0 +1,31 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "ESNext",
4
+ "moduleResolution": "Node",
5
+ "resolveJsonModule": true,
6
+ "useDefineForClassFields": true,
7
+ "jsx": "preserve",
8
+ "noImplicitThis": true,
9
+ "strict": true,
10
+ "isolatedModules": true,
11
+ "preserveValueImports": true,
12
+ "importsNotUsedAsValues": "error",
13
+ "target": "esnext",
14
+ "allowJs": true,
15
+
16
+ // Recommended
17
+ "esModuleInterop": true,
18
+ "forceConsistentCasingInFileNames": true,
19
+ // See <https://github.com/vuejs/vue-cli/pull/5688>
20
+ "skipLibCheck": true,
21
+ "baseUrl": "app/javascript/",
22
+ "types": ["@types/jest", "@types/node"],
23
+ },
24
+ "include": [
25
+ "app/javascript/**/*.ts",
26
+ "app/javascript/**/*.vue"
27
+ ],
28
+ "exclude": [
29
+ "node_modules"
30
+ ]
31
+ }
@@ -12,17 +12,19 @@ module Potassium::CLI
12
12
  c.action do |_global_options, options, _args|
13
13
  require 'potassium/newest_version_ensurer'
14
14
  require 'potassium/node_version_ensurer'
15
+ require 'potassium/platanus_config'
15
16
  require 'potassium/generators/application'
16
17
  require 'potassium/template_finder'
17
18
 
18
19
  begin
19
20
  Potassium::NewestVersionEnsurer.new.ensure! if options['version-check']
20
21
  Potassium::NodeVersionEnsurer.new.ensure! if options['node-version-check']
22
+ options = Potassium::PlatanusConfig.new(options).generate! if options['platanus-config']
21
23
  template_finder = Potassium::TemplateFinder.new
22
24
  template = template_finder.default_template
23
25
  template.cli_options = options
24
26
  template.source_paths << Rails::Generators::AppGenerator.source_root
25
- ARGV.push('--skip-webpack-install', '--skip-bundle')
27
+ ARGV.push('--skip-javascript', '--skip-bundle')
26
28
  template.start
27
29
  rescue VersionError => e
28
30
  print "\nError: #{e.message}" # rubocop:disable Rails/Output
@@ -75,11 +75,11 @@ module Potassium::CliOptions # rubocop:disable Metrics/ModuleLength
75
75
  default_test_value: false
76
76
  },
77
77
  {
78
- type: :flag,
78
+ type: :switch,
79
79
  name: "api",
80
- desc: "Which API interface to use",
80
+ desc: "Whether to include power_api for API support or not",
81
81
  default_value: "none",
82
- default_test_value: "None"
82
+ default_test_value: false
83
83
  },
84
84
  {
85
85
  type: :flag,
@@ -200,6 +200,14 @@ module Potassium::CliOptions # rubocop:disable Metrics/ModuleLength
200
200
  negatable: true,
201
201
  default_value: true,
202
202
  default_test_value: false
203
+ },
204
+ {
205
+ type: :switch,
206
+ name: 'platanus-config',
207
+ desc: 'Wheter to use the Platanus configuration.',
208
+ negatable: true,
209
+ default_value: false,
210
+ default_test_value: false
203
211
  }
204
212
  ]
205
213
 
@@ -219,4 +227,8 @@ module Potassium::CliOptions # rubocop:disable Metrics/ModuleLength
219
227
  memo
220
228
  end
221
229
  end
230
+
231
+ def self.option_names
232
+ CREATE_OPTIONS.map { |option| option[:name] }.flatten.map(&:to_sym)
233
+ end
222
234
  end
@@ -0,0 +1,20 @@
1
+ module Potassium
2
+ class PlatanusConfig
3
+ def initialize(options)
4
+ @options = options
5
+ @option_key_names = Potassium::CliOptions.option_names
6
+ end
7
+
8
+ def generate!
9
+ default_options = {
10
+ 'db': 'postgresql', 'locale': 'es-CL', 'email_service': 'sendgrid', 'devise': true,
11
+ 'devise-user-model': true, 'admin': true, 'vue_admin': true, 'pundit': true,
12
+ 'api': true, 'storage': 'shrine', 'heroku': true, 'background_processor': true,
13
+ 'draper': true, 'schedule': true, 'sentry': true, 'front_end': 'vue',
14
+ 'google_tag_manager': true, 'test': true, 'spring': true
15
+ }
16
+ default_options = default_options.filter { |key, _| @option_key_names.include?(key) }
17
+ @options.merge(default_options, default_options.stringify_keys)
18
+ end
19
+ end
20
+ end
@@ -37,6 +37,7 @@ class Recipes::Admin < Rails::AppBuilder
37
37
  add_readme_section :internal_dependencies, :active_admin
38
38
  after(:gem_install, wrap_in_action: :admin_install) do
39
39
  generate "active_admin:install --use_webpacker"
40
+ run 'bin/yarn add @activeadmin/activeadmin'
40
41
  line = "ActiveAdmin.setup do |config|"
41
42
  initializer = "config/initializers/active_admin.rb"
42
43
  gsub_file initializer, /(#{Regexp.escape(line)})/mi do |_match|
@@ -80,6 +81,16 @@ class Recipes::Admin < Rails::AppBuilder
80
81
  import 'arctic_admin';
81
82
  HERE
82
83
  )
84
+
85
+ run "mv app/javascript/packs/active_admin.js app/javascript/active_admin.js"
86
+ gsub_file(
87
+ "app/javascript/active_admin.js",
88
+ 'import "../stylesheets/active_admin";',
89
+ 'import "./stylesheets/active_admin.scss";'
90
+ )
91
+
92
+ run 'rm -rf config/webpack/plugins'
93
+ run 'rm -rf app/javascript/packs/active_admin'
83
94
  end
84
95
  end
85
96
  end
@@ -1,22 +1,11 @@
1
1
  class Recipes::Api < Rails::AppBuilder
2
2
  def ask
3
- api_interfaces = {
4
- rest: "REST (with Power API)",
5
- graphql: "GraphQL (beta)",
6
- none: "None, thanks"
7
- }
8
- api_interface = answer(:api) do
9
- api_interfaces.keys[Ask.list("Which API interface are you using?", api_interfaces.values)]
10
- end
11
- set :api, api_interface.to_sym
3
+ api_support = answer(:api) { Ask.confirm("Do you want to enable API support?") }
4
+ set :api, api_support
12
5
  end
13
6
 
14
7
  def create
15
- if get(:api) == :graphql
16
- add_graphql
17
- elsif get(:api) == :rest
18
- add_power_api
19
- end
8
+ add_power_api if get(:api)
20
9
  end
21
10
 
22
11
  def install
@@ -25,13 +14,13 @@ class Recipes::Api < Rails::AppBuilder
25
14
  end
26
15
 
27
16
  def installed?
28
- gem_exists?(/power_api/) || gem_exists?(/graphql/)
17
+ gem_exists?(/power_api/)
29
18
  end
30
19
 
31
20
  private
32
21
 
33
22
  def add_power_api
34
- gather_gem 'power_api'
23
+ gather_gem 'power_api', '~> 2.0'
35
24
 
36
25
  gather_gems(:development, :test) do
37
26
  gather_gem 'rswag-specs'
@@ -43,75 +32,7 @@ class Recipes::Api < Rails::AppBuilder
43
32
 
44
33
  after(:gem_install) do
45
34
  generate "power_api:install"
46
- end
47
- end
48
-
49
- def add_graphql
50
- gather_gem 'graphql'
51
- if get(:authentication)
52
- gather_gem 'jwt'
53
- end
54
- gather_gems(:development, :test) do
55
- gather_gem 'graphql_playground-rails'
56
- end
57
-
58
- after(:gem_install) do
59
- generate "graphql:install --skip_graphiql"
60
- playground_route = <<~HEREDOC
61
- \n
62
- if Rails.env.development?
63
- mount GraphqlPlayground::Rails::Engine, at: "/graphiql", graphql_path: "/graphql"
64
- end
65
- HEREDOC
66
- inject_into_file(
67
- 'config/routes.rb',
68
- playground_route,
69
- after: 'post "/graphql", to: "graphql#execute"'
70
- )
71
- copy_file(
72
- "../assets/config/graphql_playground.rb",
73
- "config/initializers/graphql_playground.rb"
74
- )
75
- remove_dir 'app/graphql/types'
76
- directory '../assets/app/graphql/types', 'app/graphql/types'
77
- gsub_file 'app/graphql/mutations/base_mutation.rb', 'Types::Base', 'Types::Base::Base'
78
- directory '../assets/app/graphql/queries', 'app/graphql/queries'
79
- gsub_file 'app/graphql/mutations/base_mutation.rb', 'RelayClassic', ''
80
- gsub_file(
81
- 'app/graphql/mutations/base_mutation.rb',
82
- " input_object_class Types::Base::BaseInputObject\n", ''
83
- )
84
-
85
- if get(:authentication)
86
- copy_file(
87
- '../assets/app/graphql/graphql_controller.rb',
88
- 'app/controllers/graphql_controller.rb',
89
- force: true
90
- )
91
- gsub_file(
92
- 'app/controllers/graphql_controller.rb',
93
- 'GqlSampleSchema',
94
- "#{get(:titleized_app_name).delete(' ')}Schema"
95
- )
96
- copy_file(
97
- '../assets/app/graphql/mutations/login_mutation.rb',
98
- 'app/graphql/mutations/login_mutation.rb'
99
- )
100
- inject_into_file(
101
- 'app/graphql/types/mutation_type.rb',
102
- "\n field :login, mutation: Mutations::LoginMutation",
103
- after: 'class MutationType < Types::Base::BaseObject'
104
- )
105
- append_to_file(".env.development", "HMAC_SECRET=\n")
106
- end
107
-
108
- inject_into_file(
109
- 'app/controllers/graphql_controller.rb',
110
- "\n\n skip_before_action :verify_authenticity_token",
111
- after: '# protect_from_forgery with: :null_session'
112
- )
113
-
114
- add_readme_section :internal_dependencies, :graphql
35
+ generate "power_api:internal_api_config"
115
36
  end
116
37
  end
117
38
  end
@@ -3,6 +3,11 @@ class Recipes::Coverage < Rails::AppBuilder
3
3
  load_gems
4
4
  configure_rails_helper
5
5
  append_to_file('.gitignore', "/coverage/*\n")
6
+ recipe = self
7
+ after(:setup_jest) do
8
+ recipe.configure_jest_coverage
9
+ recipe.setup_jest_text_formatter
10
+ end
6
11
  end
7
12
 
8
13
  def installed?
@@ -13,6 +18,18 @@ class Recipes::Coverage < Rails::AppBuilder
13
18
  create
14
19
  end
15
20
 
21
+ def configure_jest_coverage
22
+ json_file = File.read(Pathname.new("package.json"))
23
+ js_package = JSON.parse(json_file)
24
+ js_package = add_coverage_config(js_package)
25
+ json_string = JSON.pretty_generate(js_package)
26
+ create_file 'package.json', json_string, force: true
27
+ end
28
+
29
+ def setup_jest_text_formatter
30
+ run "bin/yarn add jest-text-formatter@1.0.2 --dev"
31
+ end
32
+
16
33
  private
17
34
 
18
35
  def load_gems
@@ -32,4 +49,18 @@ class Recipes::Coverage < Rails::AppBuilder
32
49
  end
33
50
  end
34
51
  end
52
+
53
+ def add_coverage_config(js_package)
54
+ js_package['scripts']['test:changes'] = 'jest --changedSince=master'
55
+ js_package['jest'] = js_package['jest'].merge(coverage_defaults)
56
+ js_package
57
+ end
58
+
59
+ def coverage_defaults
60
+ {
61
+ collectCoverage: true,
62
+ collectCoverageFrom: ['**/*.{js,ts,vue}', '!**/node_modules/**'],
63
+ coverageReporters: ['text']
64
+ }
65
+ end
35
66
  end
@@ -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
+ append_to_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