potassium 6.1.0 → 6.5.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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +103 -38
  3. data/.circleci/setup-rubygems.sh +3 -0
  4. data/.gitignore +2 -1
  5. data/.node-version +1 -1
  6. data/.rubocop.yml +530 -0
  7. data/CHANGELOG.md +50 -1
  8. data/README.md +11 -3
  9. data/lib/potassium/assets/.circleci/config.yml.erb +107 -40
  10. data/lib/potassium/assets/.pryrc +0 -6
  11. data/lib/potassium/assets/.rubocop.yml +13 -0
  12. data/lib/potassium/assets/README.yml +45 -8
  13. data/lib/potassium/assets/active_admin/policies/admin_user_policy.rb +2 -0
  14. data/lib/potassium/assets/active_admin/policies/comment_policy.rb +2 -0
  15. data/lib/potassium/assets/active_admin/policies/default_policy.rb +49 -0
  16. data/lib/potassium/assets/active_admin/policies/page_policy.rb +2 -0
  17. data/lib/potassium/assets/app/javascript/app.spec.js +1 -1
  18. data/lib/potassium/assets/app/views/shared/_gtm_body.html.erb +4 -0
  19. data/lib/potassium/assets/app/views/shared/_gtm_head.html.erb +7 -0
  20. data/lib/potassium/assets/testing/.rspec +1 -0
  21. data/lib/potassium/assets/testing/devise_config.rb +6 -0
  22. data/lib/potassium/assets/testing/factory_bot_config.rb +3 -0
  23. data/lib/potassium/assets/testing/faker_config.rb +1 -0
  24. data/lib/potassium/assets/testing/power_types_config.rb +1 -0
  25. data/lib/potassium/assets/testing/rails_helper.rb +130 -49
  26. data/lib/potassium/assets/testing/shoulda_matchers_config.rb +8 -0
  27. data/lib/potassium/assets/testing/simplecov_config.rb +64 -0
  28. data/lib/potassium/assets/testing/system_tests_config.rb +6 -0
  29. data/lib/potassium/cli_options.rb +19 -3
  30. data/lib/potassium/helpers/template-helpers.rb +4 -0
  31. data/lib/potassium/recipes/admin.rb +27 -17
  32. data/lib/potassium/recipes/api.rb +2 -0
  33. data/lib/potassium/recipes/background_processor.rb +43 -32
  34. data/lib/potassium/recipes/ci.rb +9 -39
  35. data/lib/potassium/recipes/coverage.rb +35 -0
  36. data/lib/potassium/recipes/file_storage.rb +1 -1
  37. data/lib/potassium/recipes/front_end.rb +26 -13
  38. data/lib/potassium/recipes/google_tag_manager.rb +94 -0
  39. data/lib/potassium/recipes/heroku.rb +43 -31
  40. data/lib/potassium/recipes/mailer.rb +18 -5
  41. data/lib/potassium/recipes/monitoring.rb +5 -0
  42. data/lib/potassium/recipes/pundit.rb +29 -10
  43. data/lib/potassium/recipes/rails.rb +0 -4
  44. data/lib/potassium/recipes/schedule.rb +16 -1
  45. data/lib/potassium/recipes/spring.rb +9 -0
  46. data/lib/potassium/recipes/style.rb +2 -2
  47. data/lib/potassium/recipes/testing.rb +75 -18
  48. data/lib/potassium/templates/application.rb +7 -2
  49. data/lib/potassium/version.rb +7 -4
  50. data/potassium.gemspec +3 -1
  51. data/spec/features/background_processor_spec.rb +7 -5
  52. data/spec/features/ci_spec.rb +7 -4
  53. data/spec/features/coverage_spec.rb +26 -0
  54. data/spec/features/front_end_spec.rb +18 -1
  55. data/spec/features/google_tag_manager_spec.rb +36 -0
  56. data/spec/features/heroku_spec.rb +0 -4
  57. data/spec/features/mailer_spec.rb +16 -0
  58. data/spec/features/node_spec.rb +1 -1
  59. data/spec/features/pundit_spec.rb +34 -0
  60. data/spec/features/schedule_spec.rb +11 -4
  61. data/spec/features/testing_spec.rb +56 -0
  62. data/spec/support/potassium_test_helpers.rb +2 -3
  63. data/tmp/.keep +0 -0
  64. metadata +64 -15
  65. data/lib/potassium/assets/Dockerfile.ci +0 -6
  66. data/lib/potassium/assets/active_admin/admin_user_policy.rb +0 -2
  67. data/lib/potassium/assets/active_admin/comment_policy.rb +0 -2
  68. data/lib/potassium/assets/active_admin/pundit_page_policy.rb +0 -5
  69. data/lib/potassium/assets/bin/cibuild.erb +0 -117
  70. data/lib/potassium/assets/docker-compose.ci.yml +0 -12
  71. data/lib/potassium/assets/sidekiq_scheduler.yml +0 -9
@@ -0,0 +1,64 @@
1
+ require 'simplecov'
2
+ require 'simplecov_text_formatter'
3
+ require 'simplecov_linter_formatter'
4
+
5
+ SimpleCovLinterFormatter.setup do |config|
6
+ config.scope = ENV.fetch(
7
+ "SIMPLE_COV_LINTER_SCOPE", :own_changes
8
+ )
9
+ config.json_filename = ENV.fetch(
10
+ "SIMPLE_COV_LINTER_JSON_FILENAME", ".resultset.json"
11
+ )
12
+ config.summary_enabled = ENV.fetch(
13
+ "SIMPLE_COV_LINTER_SUMMARY_ENABLED", true
14
+ )
15
+ config.summary_enabled_bg = ENV.fetch(
16
+ "SIMPLE_COV_LINTER_SUMMARY_BG_ENABLED", true
17
+ )
18
+ config.summary_covered_bg_color = ENV.fetch(
19
+ "SIMPLE_COV_LINTER_SUMMARY_COVERED_BG_COLOR", :darkgreen
20
+ )
21
+ config.summary_not_covered_bg_color = ENV.fetch(
22
+ "SIMPLE_COV_LINTER_SUMMARY_NOT_COVERED_BG_COLOR", :firebrick
23
+ )
24
+ config.summary_text_color = ENV.fetch(
25
+ "SIMPLE_COV_LINTER_SUMMARY_TEXT_COLOR", :white
26
+ )
27
+ config.summary_files_sorting = ENV.fetch(
28
+ "SIMPLE_COV_LINTER_SUMMARY_FILES_SORTING", :coverage
29
+ )
30
+ end
31
+
32
+ SimpleCov.start 'rails' do
33
+ add_group 'Commands', 'app/commands'
34
+ add_group 'Services', 'app/services'
35
+ add_group 'Observers', 'app/observers'
36
+ add_group 'Policies', 'app/policies'
37
+ add_group 'Utils', 'app/utils'
38
+ add_group 'Extensions', 'app/extensions'
39
+
40
+ add_filter %r{app/controllers/([a-z]|_)*_controller.rb}
41
+ add_filter 'app/admin'
42
+ add_filter 'app/channels'
43
+ add_filter 'app/uploaders'
44
+ add_filter 'app/serializers'
45
+ add_filter 'app/clients'
46
+ add_filter 'app/helpers'
47
+ add_filter 'app/decorators'
48
+ add_filter 'app/responders'
49
+ add_filter 'lib/fake_data_loader.rb'
50
+ add_filter 'lib/vue_component.rb'
51
+
52
+ if ENV["CIRCLECI"]
53
+ formatter(SimpleCov::Formatter::TextFormatter)
54
+ else
55
+ formatter(
56
+ SimpleCov::Formatter::MultiFormatter.new(
57
+ [
58
+ SimpleCov::Formatter::LinterFormatter,
59
+ SimpleCov::Formatter::HTMLFormatter
60
+ ]
61
+ )
62
+ )
63
+ end
64
+ end
@@ -0,0 +1,6 @@
1
+ RSpec.configure do |config|
2
+ config.before(:each, type: :system) do |example|
3
+ driver = example.metadata[:no_js] ? :rack_test : :selenium_chrome_headless
4
+ driven_by(driver)
5
+ end
6
+ end
@@ -97,10 +97,10 @@ module Potassium::CliOptions # rubocop:disable Metrics/ModuleLength
97
97
  default_test_value: false
98
98
  },
99
99
  {
100
- type: :flag,
100
+ type: :switch,
101
101
  name: "background_processor",
102
- desc: "Decides which background processor to use. Available: sidekiq, delayed_job, none",
103
- default_test_value: "None"
102
+ desc: "Whether to use Sidekiq for background processing or not",
103
+ default_test_value: false
104
104
  },
105
105
  {
106
106
  type: :switch,
@@ -177,6 +177,14 @@ module Potassium::CliOptions # rubocop:disable Metrics/ModuleLength
177
177
  desc: "Decides which front-end framework to use. Available: Vue, Angular 2, None",
178
178
  default_test_value: "None"
179
179
  },
180
+ {
181
+ type: :switch,
182
+ name: 'google_tag_manager',
183
+ desc: 'Whether to use google tag manager',
184
+ negatable: true,
185
+ default_value: 'none',
186
+ default_test_value: false
187
+ },
180
188
  {
181
189
  type: :switch,
182
190
  name: "test",
@@ -184,6 +192,14 @@ module Potassium::CliOptions # rubocop:disable Metrics/ModuleLength
184
192
  negatable: true,
185
193
  default_value: false,
186
194
  default_test_value: true
195
+ },
196
+ {
197
+ type: :switch,
198
+ name: "spring",
199
+ desc: "Whether to use Spring",
200
+ negatable: true,
201
+ default_value: true,
202
+ default_test_value: false
187
203
  }
188
204
  ]
189
205
 
@@ -7,6 +7,10 @@ module TemplateHelpers
7
7
  "#{Potassium::NODE_VERSION}.x"
8
8
  end
9
9
 
10
+ def ruby_version
11
+ Semantic::Version.new(Potassium::RUBY_VERSION).instance_eval { "#{major}.#{minor}" }
12
+ end
13
+
10
14
  def load_recipe(recipe_name)
11
15
  @recipes ||= {}
12
16
  @recipes[recipe_name] ||= get_recipe_class(recipe_name.to_sym).new(self)
@@ -32,12 +32,11 @@ class Recipes::Admin < Rails::AppBuilder
32
32
  private
33
33
 
34
34
  def add_active_admin
35
- gather_gem 'activeadmin', '~> 2.6'
35
+ gather_gem 'activeadmin', '~> 2.9'
36
36
  gather_gem 'activeadmin_addons'
37
- gather_gem 'active_skin', github: 'SoftwareBrothers/active_skin'
38
37
  add_readme_section :internal_dependencies, :active_admin
39
38
  after(:gem_install, wrap_in_action: :admin_install) do
40
- generate "active_admin:install"
39
+ generate "active_admin:install --use_webpacker"
41
40
  line = "ActiveAdmin.setup do |config|"
42
41
  initializer = "config/initializers/active_admin.rb"
43
42
  gsub_file initializer, /(#{Regexp.escape(line)})/mi do |_match|
@@ -50,26 +49,37 @@ class Recipes::Admin < Rails::AppBuilder
50
49
  end\n
51
50
  ActiveAdmin.setup do |config|
52
51
  config.view_factory.footer = CustomFooter
52
+ meta_tags_options = { viewport: 'width=device-width, initial-scale=1' }
53
+ config.meta_tags = meta_tags_options
54
+ config.meta_tags_for_logged_out_pages = meta_tags_options
53
55
  HERE
54
56
  end
55
57
 
56
- line = "@import \"active_admin/base\";"
57
- style = "app/assets/stylesheets/active_admin.css.scss"
58
- style = File.exist?(style) ? style : "app/assets/stylesheets/active_admin.scss"
58
+ generate "activeadmin_addons:install"
59
59
 
60
- gsub_file style, /(#{Regexp.escape(line)})/mi do |_match|
61
- <<~HERE
62
- #{line}
63
- $skinActiveColor: #001CEE;
64
- $skinHeaderBck: #002744;
65
- $panelHeaderBck: #002744;
66
- //$skinLogo: $skinHeaderBck image-url("logo_admin.png") no-repeat center center;
60
+ run "bin/yarn add arctic_admin @fortawesome/fontawesome-free"
67
61
 
68
- @import "active_skin";
69
- HERE
70
- end
62
+ aa_style = "app/javascript/stylesheets/active_admin.scss"
71
63
 
72
- generate "activeadmin_addons:install"
64
+ gsub_file(
65
+ aa_style,
66
+ "@import \"~@activeadmin/activeadmin/src/scss/mixins\";\n" +
67
+ "@import \"~@activeadmin/activeadmin/src/scss/base\";",
68
+ "@import '~arctic_admin/src/scss/main'; \n"
69
+ )
70
+
71
+ aa_js = "app/javascript/packs/active_admin.js"
72
+ js_line = "import \"@activeadmin/activeadmin\";\n"
73
+
74
+ gsub_file(
75
+ aa_js,
76
+ js_line,
77
+ <<~HERE
78
+ #{js_line}
79
+ import '@fortawesome/fontawesome-free/css/all.css';
80
+ import 'arctic_admin';
81
+ HERE
82
+ )
73
83
  end
74
84
  end
75
85
  end
@@ -38,6 +38,8 @@ class Recipes::Api < Rails::AppBuilder
38
38
  end
39
39
 
40
40
  add_readme_section :internal_dependencies, :power_api
41
+ rubocop_example = "RSpec:\n Language:\n Includes:\n Examples:\n - run_test!"
42
+ append_to_file('.rubocop.yml', rubocop_example)
41
43
 
42
44
  after(:gem_install) do
43
45
  generate "power_api:install"
@@ -1,12 +1,12 @@
1
1
  class Recipes::BackgroundProcessor < Rails::AppBuilder
2
2
  def ask
3
- response = if selected?(:email_service, :none)
3
+ response = if enabled_mailer?
4
+ info "Note: Emails should be sent on background jobs. We'll install sidekiq"
5
+ true
6
+ else
4
7
  answer(:background_processor) do
5
8
  Ask.confirm("Do you want to use Sidekiq for background job processing?")
6
9
  end
7
- else
8
- info "Note: Emails should be sent on background jobs. We'll install sidekiq"
9
- true
10
10
  end
11
11
  set(:background_processor, response)
12
12
  end
@@ -30,6 +30,42 @@ class Recipes::BackgroundProcessor < Rails::AppBuilder
30
30
  gem_exists?(/sidekiq/)
31
31
  end
32
32
 
33
+ def add_sidekiq
34
+ recipe = self
35
+ run_action(:install_sidekiq) do
36
+ gather_gem("sidekiq")
37
+ recipe.add_adapters("sidekiq")
38
+ add_readme_section :internal_dependencies, :sidekiq
39
+ recipe.edit_procfile("bundle exec sidekiq")
40
+ append_to_file(".env.development", "DB_POOL=25\n")
41
+ template("../assets/sidekiq.rb.erb", "config/initializers/sidekiq.rb", force: true)
42
+ copy_file("../assets/sidekiq.yml", "config/sidekiq.yml", force: true)
43
+ copy_file("../assets/redis.yml", "config/redis.yml", force: true)
44
+ recipe.mount_sidekiq_routes
45
+ end
46
+ end
47
+
48
+ def edit_procfile(cmd)
49
+ heroku = load_recipe(:heroku)
50
+ if selected?(:heroku) || heroku.installed?
51
+ gsub_file('Procfile', /^.*$/m) { |match| "#{match}worker: #{cmd}" }
52
+ end
53
+ end
54
+
55
+ def add_adapters(name)
56
+ application("config.active_job.queue_adapter = :#{name}")
57
+ application "config.active_job.queue_adapter = :async", env: "development"
58
+ application "config.active_job.queue_adapter = :test", env: "test"
59
+ end
60
+
61
+ def mount_sidekiq_routes
62
+ insert_into_file "config/routes.rb", after: "Rails.application.routes.draw do\n" do
63
+ <<-HERE.gsub(/^ {6}/, '')
64
+ mount Sidekiq::Web => '/queue'
65
+ HERE
66
+ end
67
+ end
68
+
33
69
  private
34
70
 
35
71
  def add_docker_compose_redis_config
@@ -59,33 +95,8 @@ class Recipes::BackgroundProcessor < Rails::AppBuilder
59
95
  )
60
96
  end
61
97
 
62
- def add_sidekiq
63
- gather_gem("sidekiq")
64
- add_adapters("sidekiq")
65
- add_readme_section :internal_dependencies, :sidekiq
66
- edit_procfile("bundle exec sidekiq")
67
- append_to_file(".env.development", "DB_POOL=25\n")
68
- template("../assets/sidekiq.rb.erb", "config/initializers/sidekiq.rb", force: true)
69
- copy_file("../assets/sidekiq.yml", "config/sidekiq.yml", force: true)
70
- copy_file("../assets/redis.yml", "config/redis.yml", force: true)
71
-
72
- insert_into_file "config/routes.rb", after: "Rails.application.routes.draw do\n" do
73
- <<-HERE.gsub(/^ {6}/, '')
74
- mount Sidekiq::Web => '/queue'
75
- HERE
76
- end
77
- end
78
-
79
- def edit_procfile(cmd)
80
- heroku = load_recipe(:heroku)
81
- if selected?(:heroku) || heroku.installed?
82
- gsub_file('Procfile', /^.*$/m) { |match| "#{match}worker: #{cmd}" }
83
- end
84
- end
85
-
86
- def add_adapters(name)
87
- application("config.active_job.queue_adapter = :#{name}")
88
- application "config.active_job.queue_adapter = :async", env: "development"
89
- application "config.active_job.queue_adapter = :test", env: "test"
98
+ def enabled_mailer?
99
+ mailer_answer = get(:email_service)
100
+ mailer_answer && ![:none, :None].include?(mailer_answer.to_sym)
90
101
  end
91
102
  end
@@ -1,50 +1,20 @@
1
1
  class Recipes::Ci < Rails::AppBuilder
2
2
  def create
3
- copy_file '../assets/Dockerfile.ci', 'Dockerfile.ci'
4
3
  template '../assets/.circleci/config.yml.erb', '.circleci/config.yml'
5
4
 
6
- template '../assets/bin/cibuild.erb', 'bin/cibuild'
7
- run "chmod a+x bin/cibuild"
8
-
9
- copy_file '../assets/docker-compose.ci.yml', 'docker-compose.ci.yml'
10
-
11
5
  gather_gems(:test) do
12
- gather_gem 'rspec_junit_formatter', '0.2.2'
13
- end
14
-
15
- compose = DockerHelpers.new('docker-compose.ci.yml')
16
-
17
- if selected?(:database, :mysql)
18
- srv =
19
- <<~YAML
20
- image: mysql:#{Potassium::MYSQL_VERSION}
21
- environment:
22
- MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
23
- YAML
24
- compose.add_service("mysql", srv)
25
- compose.add_link('test', 'mysql')
26
- compose.add_env('test', 'MYSQL_HOST', 'mysql')
27
- compose.add_env('test', 'MYSQL_PORT', '3306')
28
-
29
- elsif selected?(:database, :postgresql)
30
- srv =
31
- <<~YAML
32
- image: "postgres:#{Potassium::POSTGRES_VERSION}"
33
- environment:
34
- POSTGRES_USER: postgres
35
- POSTGRES_PASSWORD: ''
36
- YAML
37
- compose.add_service("postgresql", srv)
38
- compose.add_link('test', 'postgresql')
39
- compose.add_env('test', 'DB_USER', 'postgres')
40
- compose.add_env('test', 'DB_HOST', 'postgresql')
41
- compose.add_env('test', 'DB_PORT', '5432')
6
+ gather_gem 'rspec_junit_formatter', '~> 0.4'
42
7
  end
43
8
 
44
- compose.add_volume('test_data')
45
-
46
9
  add_readme_header :ci
47
-
48
10
  application 'config.assets.js_compressor = :uglifier', env: 'test'
49
11
  end
12
+
13
+ def install
14
+ create
15
+ end
16
+
17
+ def installed?
18
+ file_exist?('.circleci/config.yml')
19
+ end
50
20
  end
@@ -0,0 +1,35 @@
1
+ class Recipes::Coverage < Rails::AppBuilder
2
+ def create
3
+ load_gems
4
+ configure_rails_helper
5
+ append_to_file('.gitignore', "/coverage/*\n")
6
+ end
7
+
8
+ def installed?
9
+ gem_exists?(/simplecov/)
10
+ end
11
+
12
+ def install
13
+ create
14
+ end
15
+
16
+ private
17
+
18
+ def load_gems
19
+ gather_gems(:test) do
20
+ gather_gem 'simplecov'
21
+ gather_gem 'simplecov_linter_formatter', '~> 0.2'
22
+ gather_gem 'simplecov_text_formatter'
23
+ end
24
+ end
25
+
26
+ def configure_rails_helper
27
+ copy_file '../assets/testing/simplecov_config.rb', 'spec/simplecov_config.rb'
28
+
29
+ after(:gem_install) do
30
+ gsub_file 'spec/rails_helper.rb', "ENV['RACK_ENV'] ||= 'test'" do |match|
31
+ "#{match}\nrequire 'simplecov_config'"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -39,7 +39,7 @@ class Recipes::FileStorage < Rails::AppBuilder
39
39
 
40
40
  def add_shrine
41
41
  gather_gem('shrine', '~> 3.0')
42
- gather_gem('marcel', '~> 0.3.3')
42
+ gather_gem('marcel', '~> 1.0')
43
43
  copy_file('../assets/config/shrine.rb', 'config/initializers/shrine.rb', force: true)
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')
@@ -1,4 +1,6 @@
1
1
  class Recipes::FrontEnd < Rails::AppBuilder
2
+ VUE_LOADER_VERSION = Potassium::VUE_LOADER_VERSION
3
+
2
4
  def ask
3
5
  frameworks = {
4
6
  vue: "Vue",
@@ -15,21 +17,13 @@ class Recipes::FrontEnd < Rails::AppBuilder
15
17
  end
16
18
 
17
19
  def create
18
- return if [:none, :None].include? get(:front_end).to_sym
19
-
20
20
  recipe = self
21
21
  after(:gem_install) do
22
22
  value = get(:front_end)
23
23
  run "rails webpacker:install"
24
- run "rails webpacker:install:#{value}" if value
25
-
26
- if value == :vue
27
- recipe.setup_vue_with_compiler_build
28
- recipe.setup_jest
29
- if get(:api) == :graphql
30
- recipe.setup_apollo
31
- end
32
- end
24
+ run "rails webpacker:install:#{value}" unless [:none, :None].include? value.to_sym
25
+
26
+ recipe.setup_vue if value == :vue
33
27
  recipe.add_responsive_meta_tag
34
28
  recipe.setup_tailwind
35
29
  add_readme_header :webpack
@@ -69,7 +63,8 @@ class Recipes::FrontEnd < Rails::AppBuilder
69
63
  end
70
64
 
71
65
  def setup_tailwind
72
- run 'bin/yarn add tailwindcss'
66
+ run "bin/yarn add tailwindcss@#{Potassium::TAILWINDCSS}"
67
+ specify_autoprefixer_postcss_compatibility_versions
73
68
  setup_client_css
74
69
  remove_server_css_requires
75
70
  setup_tailwind_requirements
@@ -107,6 +102,19 @@ class Recipes::FrontEnd < Rails::AppBuilder
107
102
  )
108
103
  end
109
104
 
105
+ def foce_vue_loader_version
106
+ run "bin/yarn add vue-loader@#{VUE_LOADER_VERSION}"
107
+ end
108
+
109
+ def setup_vue
110
+ foce_vue_loader_version
111
+ setup_vue_with_compiler_build
112
+ setup_jest
113
+ if get(:api) == :graphql
114
+ setup_apollo
115
+ end
116
+ end
117
+
110
118
  private
111
119
 
112
120
  def frameworks(framework)
@@ -147,6 +155,10 @@ class Recipes::FrontEnd < Rails::AppBuilder
147
155
  JS
148
156
  end
149
157
 
158
+ def specify_autoprefixer_postcss_compatibility_versions
159
+ run 'bin/yarn -D add postcss@^7 autoprefixer@^9'
160
+ end
161
+
150
162
  def setup_client_css
151
163
  application_css = 'app/javascript/css/application.css'
152
164
  create_file application_css, "", force: true
@@ -264,7 +276,8 @@ class Recipes::FrontEnd < Rails::AppBuilder
264
276
  },
265
277
  "snapshotSerializers": [
266
278
  "<rootDir>/node_modules/jest-serializer-vue"
267
- ]
279
+ ],
280
+ "testEnvironment": "jsdom"
268
281
  }
269
282
  }
270
283
  end
@@ -0,0 +1,94 @@
1
+ class Recipes::GoogleTagManager < Rails::AppBuilder
2
+ def ask
3
+ use_google_tag_manager = answer(:google_tag_manager) do
4
+ Ask.confirm 'Do you want to use Google Tag Manager?'
5
+ end
6
+
7
+ set(:google_tag_manager, use_google_tag_manager)
8
+ end
9
+
10
+ def create
11
+ install if selected?(:google_tag_manager)
12
+ end
13
+
14
+ def install
15
+ add_google_tag_manager
16
+ end
17
+
18
+ def add_google_tag_manager
19
+ copy_tag_manager_files
20
+ append_to_file '.env.development', "GTM_CONTAINER_ID=\n"
21
+ include_tag_manager
22
+ add_content_security_policy
23
+ end
24
+
25
+ def add_content_security_policy
26
+ inject_into_file(
27
+ 'config/initializers/content_security_policy.rb',
28
+ content_security_policy_code
29
+ )
30
+ end
31
+
32
+ def copy_tag_manager_files
33
+ copy_file(
34
+ '../assets/app/views/shared/_gtm_head.html.erb',
35
+ 'app/views/shared/_gtm_head.html.erb',
36
+ force: true
37
+ )
38
+
39
+ copy_file(
40
+ '../assets/app/views/shared/_gtm_body.html.erb',
41
+ 'app/views/shared/_gtm_body.html.erb',
42
+ force: true
43
+ )
44
+ end
45
+
46
+ def include_tag_manager
47
+ inject_into_file(
48
+ 'app/views/layouts/application.html.erb',
49
+ render_string('shared/gtm_head'),
50
+ before: '</head>'
51
+ )
52
+
53
+ inject_into_file(
54
+ 'app/views/layouts/application.html.erb',
55
+ render_string('shared/gtm_body'),
56
+ after: '<body>'
57
+ )
58
+ end
59
+
60
+ private
61
+
62
+ def render_string(file_path)
63
+ " <%if Rails.env.production? %>
64
+ <%= render \"#{file_path}\" %>
65
+ <% end %>\n "
66
+ end
67
+
68
+ def content_security_policy_code
69
+ <<~HERE
70
+ Rails.application.config.content_security_policy do |policy|
71
+ policy.connect_src(
72
+ :self,
73
+ :https,
74
+ 'http://localhost:3035',
75
+ 'ws://localhost:3035',
76
+ 'https://www.google-analytics.com'
77
+ )
78
+ # google tag manager requires to enable unsafe inline and vue unsave eval:
79
+ # https://developers.google.com/tag-manager/web/csp
80
+ # https://vuejs.org/v2/guide/installation.html#CSP-environments
81
+ policy.script_src(
82
+ :self,
83
+ :https,
84
+ :unsafe_inline,
85
+ :unsafe_eval,
86
+ 'https://www.googletagmanager.com',
87
+ 'https://www.google-analytics.com',
88
+ 'https://ssl.google-analytics.com'
89
+ )
90
+ policy.img_src :self, :https, 'https://www.googletagmanager.com', 'https://www.google-analytics.com'
91
+ end
92
+ HERE
93
+ end
94
+ end