potassium 6.1.0 → 6.2.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.
data/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 6.2.0
6
+
7
+ Features:
8
+ - Add Google Tag Manager [#326](https://github.com/platanus/potassium/pull/326)
9
+ - Update rubocop and rubocop-rspec for potassium and generated projects [#337](https://github.com/platanus/potassium/pull/337)
10
+ - Adds `mailers` queue to `sidekiq.yml` when installing or creating mailer recipe [#341](https://github.com/platanus/potassium/pull/341)
11
+
12
+ Fixes:
13
+ - Change `backgroud_processor` cli option to a switch. As of [#137](https://github.com/platanus/potassium/pull/137) we no longer have `delayed_jobs` as an option, it's only `sidekiq` now [#340](https://github.com/platanus/potassium/pull/340)
14
+ - Fixes mailer recipe install when background_processor wasn't installed [#341](https://github.com/platanus/potassium/pull/341)
15
+ - Update `heroku` recipe to check if an app or pipeline name is valid before creating it on Heroku [#344](https://github.com/platanus/potassium/pull/344)
16
+
5
17
  ## 6.1.0
6
18
 
7
19
  Features:
data/README.md CHANGED
@@ -60,13 +60,13 @@ The following optional integrations are also added:
60
60
  - [ActiveAdmin](http://activeadmin.info) for admin interfaces
61
61
  - [ActiveAdminAddons](https://github.com/platanus/activeadmin_addons) for some help with ActiveAdmin
62
62
  - [Pundit](https://github.com/elabs/pundit) for role-based authorization
63
- - [DelayedJob](https://github.com/collectiveidea/delayed_job) to execute longer tasks in the background
64
63
  - [Sidekiq](https://github.com/mperham/sidekiq) a simple, efficient background processing for Ruby
65
64
  - [Sidekiq-scheduler](https://github.com/moove-it/sidekiq-scheduler) to run scheduled processes
66
65
  - Mailing configuration for [AWS SES](https://github.com/aws/aws-sdk-rails)
67
66
  and [Sendgrid](https://github.com/platanus/send_grid_mailer) with recipient interceptor support
68
67
  - [Sentry](https://sentry.io) to monitor exceptions and errors
69
68
  - [Vue.js](https://vuejs.org) or [Angular 2](https://angular.io/) for frontend development
69
+ - [Google Tag Manager](https://tagmanager.google.com/) for analytics
70
70
  - Creates the Github repository of your choice for the project. A local git repository will always be created.
71
71
 
72
72
  A few more things are added to the project:
@@ -502,6 +502,19 @@ Performance/RedundantBlockCall:
502
502
  Description: Use `yield` instead of `block.call`.
503
503
  Reference: https://github.com/JuanitoFatas/fast-ruby#proccall-vs-yield-code
504
504
  Enabled: false
505
+ Style/OptionalBooleanParameter:
506
+ Description: 'Use keyword arguments when defining method with boolean argument.'
507
+ Enabled: false
508
+ Lint/MissingSuper:
509
+ Description: >-
510
+ This cop checks for the presence of constructors and lifecycle callbacks
511
+ without calls to `super`'.
512
+ Enabled: false
513
+ Style/RedundantFileExtensionInRequire:
514
+ Description: >-
515
+ Checks for the presence of superfluous `.rb` extension in
516
+ the filename provided to `require` and `require_relative`.
517
+ Enabled: false
505
518
  RSpec/MultipleExpectations:
506
519
  Max: 5
507
520
  RSpec/NestedGroups:
@@ -112,9 +112,6 @@ readme:
112
112
  pundit:
113
113
  title: "Authorization"
114
114
  body: "For defining which parts of the system each user has access to, we have chosen to include the [Pundit](https://github.com/elabs/pundit) gem, by [Elabs](http://elabs.se/)."
115
- delayed_job:
116
- title: "Queue System"
117
- body: "For managing tasks in the background, this project uses [DelayedJob](https://github.com/collectiveidea/delayed_job)"
118
115
  sidekiq:
119
116
  title: "Queue System"
120
117
  body: "For managing tasks in the background, this project uses [Sidekiq](https://github.com/mperham/sidekiq)"
@@ -0,0 +1,4 @@
1
+ <!-- Google Tag Manager (noscript) -->
2
+ <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=<%= ENV['GTM_CONTAINER_ID'] %>"
3
+ height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
4
+ <!-- End Google Tag Manager (noscript) -->
@@ -0,0 +1,7 @@
1
+ <!-- Google Tag Manager -->
2
+ <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
3
+ new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
4
+ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
5
+ 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
6
+ })(window,document,'script','dataLayer','<%=ENV['GTM_CONTAINER_ID']%>');</script>
7
+ <!-- End Google Tag Manager -->
@@ -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",
@@ -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
@@ -0,0 +1,90 @@
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
+ if Rails.env.development?
72
+ policy.connect_src :self, :https, 'http://localhost:3035', 'ws://localhost:3035'
73
+ policy.script_src :self, :https, :unsafe_eval
74
+ else
75
+ policy.script_src :self, :https
76
+ # google tag manager requires to enable unsafe inline:
77
+ # https://developers.google.com/tag-manager/web/csp
78
+ policy.connect_src :self, :https, 'https://www.google-analytics.com'
79
+ policy.script_src :self,
80
+ :https,
81
+ :unsafe_inline,
82
+ 'https://www.googletagmanager.com',
83
+ 'https://www.google-analytics.com',
84
+ 'https://ssl.google-analytics.com'
85
+ policy.img_src :self, :https, 'https://www.googletagmanager.com', 'https://www.google-analytics.com'
86
+ end
87
+ end
88
+ HERE
89
+ end
90
+ end
@@ -1,13 +1,7 @@
1
1
  class Recipes::Heroku < Rails::AppBuilder
2
2
  NAME_PREFIX = 'pl'
3
-
4
- attr_accessor :app_name_staging
5
-
6
- def initialize(args)
7
- super(args)
8
- set(:heroku_app_name_staging, app_name_for('staging'))
9
- set(:heroku_app_name_production, app_name_for('production'))
10
- end
3
+ ENVIRONMENTS = ['staging', 'production']
4
+ HEROKU_NAMES_MAX_CHARS = 30
11
5
 
12
6
  def ask
13
7
  heroku = answer(:heroku) do
@@ -16,6 +10,8 @@ class Recipes::Heroku < Rails::AppBuilder
16
10
 
17
11
  if heroku
18
12
  set(:heroku, heroku)
13
+
14
+ ENVIRONMENTS.each { |environment| set_app_name_for(environment) }
19
15
  end
20
16
  end
21
17
 
@@ -47,31 +43,34 @@ class Recipes::Heroku < Rails::AppBuilder
47
43
  template "../assets/bin/setup_heroku.erb", "bin/setup_heroku", force: true
48
44
  run "chmod a+x bin/setup_heroku"
49
45
 
50
- if logged_in?
51
- %w(staging production).each do |environment|
52
- create_app_on_heroku(environment)
53
- end
54
- puts "Remember to connect the github repository to the new pipeline"
55
- open_pipeline_command = "\e[33mheroku pipelines:open #{heroku_pipeline_name}\e[0m"
56
- puts "run #{open_pipeline_command} to open the dashboard"
57
- else
58
- puts "You are not logged in into heroku"
59
- login_command = "\e[33mheroku login\e[0m"
60
- puts "Run #{login_command} and enter your credentials"
61
- puts "You can install the heroku recipe again create the app in heroku"
62
- install_command = "\e[33mpostassium install heroku --force\e[0m"
63
- puts "Just run #{install_command}"
64
- end
46
+ logged_in? ? create_apps : puts_not_logged_in_msg
65
47
 
66
48
  add_readme_header :deployment
67
49
  end
68
50
 
51
+ def create_apps
52
+ ENVIRONMENTS.each { |environment| create_app_on_heroku(environment) }
53
+ puts "Remember to connect the github repository to the new pipeline"
54
+ open_pipeline_command = "\e[33mheroku pipelines:open #{heroku_pipeline_name}\e[0m"
55
+ puts "run #{open_pipeline_command} to open the dashboard"
56
+ end
57
+
58
+ def puts_not_logged_in_msg
59
+ puts "You are not logged in into heroku"
60
+ login_command = "\e[33mheroku login\e[0m"
61
+ puts "Run #{login_command} and enter your credentials"
62
+ puts "You can install the heroku recipe again to create the app in heroku"
63
+ install_command = "\e[33mpostassium install heroku --force\e[0m"
64
+ puts "Just run #{install_command}"
65
+ end
66
+
69
67
  def heroku_pipeline_name
70
- app_name.dasherize
68
+ @heroku_pipeline_name ||= valid_heroku_name(app_name.dasherize, 'pipeline', false)
71
69
  end
72
70
 
73
- def app_name_for(environment)
74
- "#{NAME_PREFIX}-#{app_name.dasherize}-#{environment}"
71
+ def set_app_name_for(environment)
72
+ default_name = "#{NAME_PREFIX}-#{app_name.dasherize}-#{environment}"
73
+ set("heroku_app_name_#{environment}".to_sym, valid_heroku_name(default_name, environment))
75
74
  end
76
75
 
77
76
  def logged_in?
@@ -84,7 +83,7 @@ class Recipes::Heroku < Rails::AppBuilder
84
83
 
85
84
  def create_app_on_heroku(environment)
86
85
  rack_env = "RACK_ENV=production"
87
- staged_app_name = app_name_for(environment)
86
+ staged_app_name = get("heroku_app_name_#{environment}".to_sym)
88
87
 
89
88
  run_toolbelt_command "create #{staged_app_name} --remote #{environment}"
90
89
  run_toolbelt_command "labs:enable runtime-dyno-metadata", staged_app_name
@@ -99,14 +98,14 @@ class Recipes::Heroku < Rails::AppBuilder
99
98
  def set_rails_secrets(environment)
100
99
  run_toolbelt_command(
101
100
  "config:add SECRET_KEY_BASE=#{generate_secret}",
102
- app_name_for(environment)
101
+ get("heroku_app_name_#{environment}".to_sym)
103
102
  )
104
103
  end
105
104
 
106
105
  def set_app_multi_buildpack(environment)
107
106
  run_toolbelt_command(
108
107
  "buildpacks:set https://github.com/heroku/heroku-buildpack-multi.git",
109
- app_name_for(environment)
108
+ get("heroku_app_name_#{environment}".to_sym)
110
109
  )
111
110
  end
112
111
 
@@ -133,4 +132,18 @@ class Recipes::Heroku < Rails::AppBuilder
133
132
  `heroku #{command} --app #{app_env_name}`
134
133
  end
135
134
  end
135
+
136
+ def valid_heroku_name(name, element, force_suffix = true)
137
+ suffix = "-#{element}"
138
+ while name.length > HEROKU_NAMES_MAX_CHARS
139
+ puts "Heroku names must be shorter than #{HEROKU_NAMES_MAX_CHARS} chars."
140
+ if force_suffix
141
+ puts "Potassium uses the heroku-stage gem, because of that '#{suffix}' will be "\
142
+ "added to your app name. The suffix, #{suffix}, counts towards the app name length."
143
+ end
144
+ name = Ask.input("Please enter a valid name for #{element}:")
145
+ name += suffix if force_suffix && !name.end_with?(suffix)
146
+ end
147
+ name
148
+ end
136
149
  end
@@ -24,11 +24,7 @@ class Recipes::Mailer < Rails::AppBuilder
24
24
  dependencies(email_service)
25
25
  config(email_service)
26
26
 
27
- background_processor_recipe = load_recipe(:background_processor)
28
- background_processor_answer = get(:background_processor)
29
-
30
- background_processor_recipe.add_sidekiq unless background_processor_recipe.installed? ||
31
- background_processor_answer
27
+ ensure_sidekiq_install_and_add_mailers_queue
32
28
  end
33
29
 
34
30
  def install
@@ -36,6 +32,10 @@ class Recipes::Mailer < Rails::AppBuilder
36
32
  create
37
33
  end
38
34
 
35
+ def add_mailer_queue
36
+ insert_into_file "config/sidekiq.yml", " - mailers", after: "- default\n"
37
+ end
38
+
39
39
  private
40
40
 
41
41
  def email_services(service_name)
@@ -102,4 +102,17 @@ class Recipes::Mailer < Rails::AppBuilder
102
102
  gather_gems(:development) { gather_gem("letter_opener") }
103
103
  application "config.action_mailer.delivery_method = :letter_opener", env: "development"
104
104
  end
105
+
106
+ def ensure_sidekiq_install_and_add_mailers_queue
107
+ background_processor_recipe = load_recipe(:background_processor)
108
+ background_processor_answer = get(:background_processor)
109
+
110
+ if background_processor_recipe.installed?
111
+ add_mailer_queue
112
+ else
113
+ recipe = self
114
+ after(:install_sidekiq) { recipe.add_mailer_queue }
115
+ background_processor_recipe.add_sidekiq unless background_processor_answer
116
+ end
117
+ end
105
118
  end