potassium 5.2.2 → 6.3.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 (121) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +103 -28
  3. data/.circleci/setup-rubygems.sh +3 -0
  4. data/.gitignore +2 -1
  5. data/.node-version +1 -0
  6. data/.rubocop.yml +530 -0
  7. data/.ruby-version +1 -1
  8. data/CHANGELOG.md +92 -1
  9. data/README.md +51 -45
  10. data/docs/DSL.md +5 -5
  11. data/lib/potassium/assets/.circleci/config.yml.erb +151 -0
  12. data/lib/potassium/assets/.eslintrc.json +352 -0
  13. data/lib/potassium/assets/.github/pull_request_template.md +9 -0
  14. data/lib/potassium/assets/.rubocop.yml +528 -0
  15. data/lib/potassium/assets/.stylelintrc.json +46 -0
  16. data/lib/potassium/assets/Makefile.erb +21 -32
  17. data/lib/potassium/assets/README.yml +59 -15
  18. data/lib/potassium/assets/active_admin/admin-component.vue +35 -0
  19. data/lib/potassium/assets/active_admin/admin_application.js +14 -0
  20. data/lib/potassium/assets/active_admin/init_activeadmin_vue.rb +10 -0
  21. data/lib/potassium/assets/app/graphql/graphql_controller.rb +55 -0
  22. data/lib/potassium/assets/app/graphql/mutations/login_mutation.rb +23 -0
  23. data/lib/potassium/assets/app/graphql/queries/base_query.rb +4 -0
  24. data/lib/potassium/assets/app/graphql/types/base/base_argument.rb +4 -0
  25. data/lib/potassium/assets/app/graphql/types/base/base_enum.rb +4 -0
  26. data/lib/potassium/assets/app/graphql/types/base/base_field.rb +5 -0
  27. data/lib/potassium/assets/app/graphql/types/base/base_input_object.rb +5 -0
  28. data/lib/potassium/assets/app/graphql/types/base/base_interface.rb +7 -0
  29. data/lib/potassium/assets/app/graphql/types/base/base_object.rb +5 -0
  30. data/lib/potassium/assets/app/graphql/types/base/base_scalar.rb +4 -0
  31. data/lib/potassium/assets/app/graphql/types/base/base_union.rb +4 -0
  32. data/lib/potassium/assets/app/graphql/types/mutation_type.rb +10 -0
  33. data/lib/potassium/assets/app/graphql/types/query_type.rb +13 -0
  34. data/lib/potassium/assets/app/javascript/app.spec.js +14 -0
  35. data/lib/potassium/assets/app/uploaders/base_uploader.rb +11 -0
  36. data/lib/potassium/assets/app/uploaders/image_uploader.rb +5 -0
  37. data/lib/potassium/assets/app/views/shared/_gtm_body.html.erb +4 -0
  38. data/lib/potassium/assets/app/views/shared/_gtm_head.html.erb +7 -0
  39. data/lib/potassium/assets/bin/release +1 -1
  40. data/lib/potassium/assets/bin/setup.erb +1 -1
  41. data/lib/potassium/assets/config/database_mysql.yml.erb +2 -2
  42. data/lib/potassium/assets/config/database_postgresql.yml.erb +2 -2
  43. data/lib/potassium/assets/config/graphql_playground.rb +20 -0
  44. data/lib/potassium/assets/config/puma.rb +1 -1
  45. data/lib/potassium/assets/config/shrine.rb +36 -0
  46. data/lib/potassium/assets/lib/tasks/auto_annotate_models.rake +2 -1
  47. data/lib/potassium/assets/package.json +4 -1
  48. data/lib/potassium/assets/redis.yml +1 -2
  49. data/lib/potassium/assets/testing/rails_helper.rb +2 -0
  50. data/lib/potassium/cli/commands/create.rb +12 -19
  51. data/lib/potassium/cli_options.rb +77 -26
  52. data/lib/potassium/helpers/gem-helpers.rb +1 -1
  53. data/lib/potassium/helpers/template-helpers.rb +8 -0
  54. data/lib/potassium/newest_version_ensurer.rb +19 -36
  55. data/lib/potassium/node_version_ensurer.rb +30 -0
  56. data/lib/potassium/recipes/admin.rb +3 -3
  57. data/lib/potassium/recipes/annotate.rb +1 -1
  58. data/lib/potassium/recipes/api.rb +93 -21
  59. data/lib/potassium/recipes/background_processor.rb +66 -19
  60. data/lib/potassium/recipes/ci.rb +10 -38
  61. data/lib/potassium/recipes/data_migrate.rb +44 -0
  62. data/lib/potassium/recipes/database.rb +4 -0
  63. data/lib/potassium/recipes/database_container.rb +7 -5
  64. data/lib/potassium/recipes/draper.rb +1 -10
  65. data/lib/potassium/recipes/file_storage.rb +66 -0
  66. data/lib/potassium/recipes/front_end.rb +225 -9
  67. data/lib/potassium/recipes/github.rb +93 -15
  68. data/lib/potassium/recipes/google_tag_manager.rb +90 -0
  69. data/lib/potassium/recipes/heroku.rb +42 -29
  70. data/lib/potassium/recipes/mailer.rb +18 -2
  71. data/lib/potassium/recipes/monitoring.rb +5 -0
  72. data/lib/potassium/recipes/node.rb +21 -0
  73. data/lib/potassium/recipes/rack_cors.rb +18 -15
  74. data/lib/potassium/recipes/schedule.rb +17 -2
  75. data/lib/potassium/recipes/style.rb +21 -3
  76. data/lib/potassium/recipes/vue_admin.rb +124 -0
  77. data/lib/potassium/templates/application.rb +10 -7
  78. data/lib/potassium/version.rb +7 -4
  79. data/potassium.gemspec +11 -6
  80. data/spec/features/api_spec.rb +25 -0
  81. data/spec/features/background_processor_spec.rb +19 -6
  82. data/spec/features/ci_spec.rb +7 -4
  83. data/spec/features/data_migrate_spec.rb +14 -0
  84. data/spec/features/database_container_spec.rb +1 -5
  85. data/spec/features/draper_spec.rb +1 -6
  86. data/spec/features/file_storage_spec.rb +75 -0
  87. data/spec/features/front_end_spec.rb +102 -0
  88. data/spec/features/github_spec.rb +53 -8
  89. data/spec/features/google_tag_manager_spec.rb +59 -0
  90. data/spec/features/graphql_spec.rb +71 -0
  91. data/spec/features/heroku_spec.rb +1 -1
  92. data/spec/features/mailer_spec.rb +16 -0
  93. data/spec/features/new_project_spec.rb +6 -14
  94. data/spec/features/node_spec.rb +28 -0
  95. data/spec/features/power_types_spec.rb +5 -16
  96. data/spec/features/schedule_spec.rb +11 -4
  97. data/spec/features/vue_admin_spec.rb +47 -0
  98. data/spec/spec_helper.rb +5 -0
  99. data/spec/support/fake_octokit.rb +31 -0
  100. data/spec/support/potassium_test_helpers.rb +26 -9
  101. data/tmp/.keep +0 -0
  102. metadata +152 -46
  103. data/lib/potassium/assets/.circleci/config.yml +0 -20
  104. data/lib/potassium/assets/Dockerfile.ci +0 -6
  105. data/lib/potassium/assets/active_admin/active_admin.js.coffee +0 -4
  106. data/lib/potassium/assets/active_admin/init_activeadmin_angular.rb +0 -8
  107. data/lib/potassium/assets/api/api_error_concern.rb +0 -32
  108. data/lib/potassium/assets/api/base_controller.rb +0 -9
  109. data/lib/potassium/assets/api/draper_responder.rb +0 -62
  110. data/lib/potassium/assets/api/responder.rb +0 -41
  111. data/lib/potassium/assets/aws.rb +0 -1
  112. data/lib/potassium/assets/bin/cibuild.erb +0 -100
  113. data/lib/potassium/assets/docker-compose.ci.yml +0 -11
  114. data/lib/potassium/assets/sidekiq_scheduler.yml +0 -9
  115. data/lib/potassium/assets/testing/paperclip.rb +0 -59
  116. data/lib/potassium/recipes/active_storage.rb +0 -40
  117. data/lib/potassium/recipes/angular_admin.rb +0 -56
  118. data/lib/potassium/recipes/aws_sdk.rb +0 -7
  119. data/lib/potassium/recipes/paperclip.rb +0 -47
  120. data/spec/features/active_storage_spec.rb +0 -30
  121. data/spec/features/front_end.rb +0 -30
@@ -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,8 +24,7 @@ class Recipes::Mailer < Rails::AppBuilder
24
24
  dependencies(email_service)
25
25
  config(email_service)
26
26
 
27
- background_processor = load_recipe(:background_processor)
28
- background_processor.add_sidekiq unless background_processor.installed?
27
+ ensure_sidekiq_install_and_add_mailers_queue
29
28
  end
30
29
 
31
30
  def install
@@ -33,6 +32,10 @@ class Recipes::Mailer < Rails::AppBuilder
33
32
  create
34
33
  end
35
34
 
35
+ def add_mailer_queue
36
+ insert_into_file "config/sidekiq.yml", " - mailers", after: "- default\n"
37
+ end
38
+
36
39
  private
37
40
 
38
41
  def email_services(service_name)
@@ -99,4 +102,17 @@ class Recipes::Mailer < Rails::AppBuilder
99
102
  gather_gems(:development) { gather_gem("letter_opener") }
100
103
  application "config.action_mailer.delivery_method = :letter_opener", env: "development"
101
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
102
118
  end
@@ -0,0 +1,5 @@
1
+ class Recipes::Monitoring < Rails::AppBuilder
2
+ def create
3
+ gather_gem('scout_apm')
4
+ end
5
+ end
@@ -0,0 +1,21 @@
1
+ require 'net/http'
2
+ require 'semantic'
3
+ require 'json'
4
+
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
15
+
16
+ private
17
+
18
+ def version
19
+ Potassium::NODE_VERSION
20
+ end
21
+ end
@@ -4,23 +4,26 @@ class Recipes::RackCors < Rails::AppBuilder
4
4
  end
5
5
 
6
6
  def create
7
- gather_gem('rack-cors', '~> 0.4.0')
7
+ gather_gem('rack-cors', '~> 1.1')
8
+ recipe = self
8
9
  after(:gem_install) do
9
- rack_cors_config =
10
- <<~RUBY
11
- config.middleware.insert_before 0, Rack::Cors do
12
- allow do
13
- origins '*'
14
- resource '*',
15
- headers: :any,
16
- expose: ['X-Page', 'X-PageTotal'],
17
- methods: [:get, :post, :delete, :put, :options]
18
- end
19
- end
10
+ application recipe.rack_cors_config
11
+ end
12
+ end
20
13
 
21
- RUBY
14
+ def rack_cors_config
15
+ <<~RUBY
16
+ config.middleware.insert_before 0, Rack::Cors do
17
+ allow do
18
+ origins '*'
19
+ resource '/public/*', headers: :any, methods: :get
20
+ resource '/api/*',
21
+ headers: :any,
22
+ expose: ['X-Page', 'X-PageTotal'],
23
+ methods: [:get, :post, :patch, :put, :delete, :options]
24
+ end
25
+ end
22
26
 
23
- application rack_cors_config
24
- end
27
+ RUBY
25
28
  end
26
29
  end
@@ -8,10 +8,10 @@ class Recipes::Schedule < Rails::AppBuilder
8
8
 
9
9
  def create
10
10
  if selected?(:schedule)
11
- gather_gem 'sidekiq-scheduler'
11
+ gather_gem 'sidekiq-scheduler', '>= 3.0.1'
12
12
  add_readme_section :internal_dependencies, :sidekiq_scheduler
13
+ append_schedule_section_to_yml
13
14
  end
14
- template '../assets/sidekiq_scheduler.yml', 'config/sidekiq.yml', force: true
15
15
  end
16
16
 
17
17
  def install
@@ -22,4 +22,19 @@ class Recipes::Schedule < Rails::AppBuilder
22
22
  def installed?
23
23
  gem_exists?(/sidekiq-scheduler/) && file_exist?('config/sidekiq.yml')
24
24
  end
25
+
26
+ private
27
+
28
+ def append_schedule_section_to_yml
29
+ append_to_file(
30
+ 'config/sidekiq.yml',
31
+ <<-HERE.gsub(/^ {8}/, '')
32
+ # :schedule:
33
+ # an_scheduled_task:
34
+ # cron: '0 * * * * *' # Runs once per minute
35
+ # class: ExampleJob
36
+ # args: ['a', 'b']
37
+ HERE
38
+ )
39
+ end
25
40
  end
@@ -1,12 +1,30 @@
1
1
  class Recipes::Style < Rails::AppBuilder
2
2
  def create
3
- append_to_file ".gitignore", ".rubocop.yml\n"
4
- append_to_file ".gitignore", ".eslintrc.json\n"
5
- append_to_file ".gitignore", ".sscs-lint.yml\n"
3
+ add_linters
4
+ add_config_files
6
5
  add_readme_header :style_guide
7
6
  end
8
7
 
9
8
  def install
10
9
  create
11
10
  end
11
+
12
+ private
13
+
14
+ def add_linters
15
+ gather_gems(:development, :test) do
16
+ gather_gem 'rubocop', Potassium::RUBOCOP_VERSION
17
+ gather_gem 'rubocop-performance'
18
+ gather_gem 'rubocop-rails'
19
+ gather_gem 'rubocop-rspec', Potassium::RUBOCOP_RSPEC_VERSION
20
+ 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
+ end
24
+
25
+ def add_config_files
26
+ copy_file '../assets/.rubocop.yml', '.rubocop.yml'
27
+ copy_file '../assets/.eslintrc.json', '.eslintrc.json'
28
+ copy_file '../assets/.stylelintrc.json', '.stylelintrc.json'
29
+ end
12
30
  end
@@ -0,0 +1,124 @@
1
+ class Recipes::VueAdmin < Rails::AppBuilder
2
+ def ask
3
+ if selected?(:admin_mode)
4
+ vue_admin = answer(:vue_admin) do
5
+ Ask.confirm "Do you want Vue support for ActiveAdmin?"
6
+ end
7
+ set(:vue_admin, vue_admin)
8
+ set(:front_end, :vue) if vue_admin
9
+ end
10
+ end
11
+
12
+ def create
13
+ recipe = self
14
+ if selected?(:vue_admin)
15
+ after(:admin_install) do
16
+ recipe.add_vue_admin
17
+ end
18
+ end
19
+ end
20
+
21
+ def install
22
+ active_admin = load_recipe(:admin)
23
+ if active_admin.installed?
24
+ add_vue_admin
25
+ info "VueAdmin installed"
26
+ else
27
+ info "VueAdmin can't be installed because Active Admin isn't installed."
28
+ end
29
+ end
30
+
31
+ def installed?
32
+ dir_exist?("app/assets/javascripts/admin")
33
+ end
34
+
35
+ def add_vue_admin
36
+ add_vue_component_library
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'
44
+ copy_file '../assets/active_admin/admin-component.vue',
45
+ 'app/javascript/components/admin-component.vue',
46
+ force: true
47
+ end
48
+
49
+ def add_component_integration
50
+ line = "class CustomFooter < ActiveAdmin::Component"
51
+ initializer = "config/initializers/active_admin.rb"
52
+ gsub_file initializer, /(#{Regexp.escape(line)})/mi do |_match|
53
+ <<~HERE
54
+ require "vue_component.rb"
55
+ AUTO_BUILD_ELEMENTS=[:admin_component, :template, :slot]
56
+ component_creator(AUTO_BUILD_ELEMENTS)
57
+
58
+ #{line}
59
+ HERE
60
+ end
61
+ end
62
+
63
+ def add_vue_component_library
64
+ lib_component_path = "lib/vue_component.rb"
65
+ class_definitions =
66
+ <<~HERE
67
+ #{vue_component}
68
+ #{component_builder}
69
+ HERE
70
+ File.open(lib_component_path, "w") { |file| file.write(class_definitions) }
71
+ end
72
+
73
+ def vue_component
74
+ <<~HERE
75
+ class VueComponent < Arbre::Component
76
+ builder_method :root
77
+ def tag_name
78
+ :root
79
+ end
80
+
81
+ def initialize(*)
82
+ super
83
+ end
84
+
85
+ def build(attributes = {})
86
+ super(process_attributes(attributes))
87
+ end
88
+
89
+ def process_attributes(attributes)
90
+ vue_attributes = {}
91
+ attributes.each do |key, value|
92
+ dasherized_key = key.to_s.dasherize
93
+ if value.is_a?(String)
94
+ vue_attributes[dasherized_key] = value
95
+ elsif !dasherized_key.index(':').nil? && dasherized_key.index(':').zero?
96
+ vue_attributes[dasherized_key] = value.to_json
97
+ else
98
+ vue_attributes[":" + dasherized_key] = value.to_json
99
+ end
100
+ end
101
+ vue_attributes
102
+ end
103
+ end
104
+ HERE
105
+ end
106
+
107
+ def component_builder
108
+ <<~HERE
109
+ def component_creator(auto_build_elements)
110
+ auto_build_elements.each do |element|
111
+ as_string=element.to_s
112
+ camelized_element = as_string.camelize
113
+ Object.const_set(camelized_element,Class.new(VueComponent))
114
+ Object.const_get(camelized_element).class_eval do
115
+ builder_method as_string.to_sym
116
+ def tag_name
117
+ self.class.to_s.underscore
118
+ end
119
+ end
120
+ end
121
+ end
122
+ HERE
123
+ end
124
+ end