potassium 6.5.0 → 6.6.0

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