potassium 5.2.3 → 6.0.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +22 -9
  3. data/.node-version +1 -0
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +26 -0
  6. data/docs/DSL.md +5 -5
  7. data/lib/potassium/assets/.circleci/config.yml.erb +102 -0
  8. data/lib/potassium/assets/.eslintrc.json +343 -0
  9. data/lib/potassium/assets/.rubocop.yml +515 -0
  10. data/lib/potassium/assets/.stylelintrc.json +46 -0
  11. data/lib/potassium/assets/Dockerfile.ci +1 -1
  12. data/lib/potassium/assets/Makefile.erb +6 -32
  13. data/lib/potassium/assets/README.yml +51 -7
  14. data/lib/potassium/assets/active_admin/admin-component.vue +35 -0
  15. data/lib/potassium/assets/active_admin/admin_application.js +14 -0
  16. data/lib/potassium/assets/active_admin/init_activeadmin_vue.rb +10 -0
  17. data/lib/potassium/assets/api/base_controller.rb +1 -3
  18. data/lib/potassium/assets/app/javascript/app.spec.js +14 -0
  19. data/lib/potassium/assets/app/uploaders/base_uploader.rb +13 -0
  20. data/lib/potassium/assets/app/uploaders/image_uploader.rb +5 -0
  21. data/lib/potassium/assets/bin/cibuild.erb +18 -1
  22. data/lib/potassium/assets/bin/setup.erb +1 -1
  23. data/lib/potassium/assets/config/database_mysql.yml.erb +2 -2
  24. data/lib/potassium/assets/config/database_postgresql.yml.erb +2 -2
  25. data/lib/potassium/assets/config/shrine.rb +33 -0
  26. data/lib/potassium/assets/docker-compose.ci.yml +1 -0
  27. data/lib/potassium/assets/lib/tasks/auto_annotate_models.rake +2 -1
  28. data/lib/potassium/assets/package.json +4 -1
  29. data/lib/potassium/cli/commands/create.rb +1 -0
  30. data/lib/potassium/cli_options.rb +6 -6
  31. data/lib/potassium/helpers/template-helpers.rb +4 -0
  32. data/lib/potassium/recipes/admin.rb +3 -3
  33. data/lib/potassium/recipes/annotate.rb +1 -1
  34. data/lib/potassium/recipes/ci.rb +1 -1
  35. data/lib/potassium/recipes/database_container.rb +5 -4
  36. data/lib/potassium/recipes/draper.rb +1 -1
  37. data/lib/potassium/recipes/file_storage.rb +15 -33
  38. data/lib/potassium/recipes/front_end.rb +128 -5
  39. data/lib/potassium/recipes/mailer.rb +5 -2
  40. data/lib/potassium/recipes/node.rb +21 -0
  41. data/lib/potassium/recipes/rack_cors.rb +18 -15
  42. data/lib/potassium/recipes/schedule.rb +1 -1
  43. data/lib/potassium/recipes/style.rb +21 -3
  44. data/lib/potassium/recipes/vue_admin.rb +124 -0
  45. data/lib/potassium/templates/application.rb +4 -2
  46. data/lib/potassium/version.rb +4 -3
  47. data/potassium.gemspec +6 -5
  48. data/spec/features/database_container_spec.rb +1 -1
  49. data/spec/features/file_storage_spec.rb +12 -16
  50. data/spec/features/front_end_spec.rb +74 -0
  51. data/spec/features/new_project_spec.rb +1 -1
  52. data/spec/features/node_spec.rb +28 -0
  53. data/spec/features/power_types_spec.rb +5 -16
  54. data/spec/features/vue_admin_spec.rb +47 -0
  55. data/spec/support/potassium_test_helpers.rb +23 -9
  56. metadata +56 -29
  57. data/lib/potassium/assets/.circleci/config.yml +0 -20
  58. data/lib/potassium/assets/active_admin/active_admin.js.coffee +0 -4
  59. data/lib/potassium/assets/active_admin/init_activeadmin_angular.rb +0 -8
  60. data/lib/potassium/assets/testing/paperclip.rb +0 -59
  61. data/lib/potassium/recipes/angular_admin.rb +0 -56
  62. data/spec/features/front_end.rb +0 -33
@@ -1,3 +1,6 @@
1
1
  {
2
- "name": "<%= @app_name.camelcase %>"
2
+ "name": "<%= @app_name.camelcase %>",
3
+ "engines": {
4
+ "node": "<%= get(:node_version) %>"
5
+ }
3
6
  }
@@ -25,6 +25,7 @@ module Potassium::CLI
25
25
  template = template_finder.default_template
26
26
  template.cli_options = options
27
27
  template.source_paths << Rails::Generators::AppGenerator.source_root
28
+ ARGV.push('--skip-webpack-install', '--skip-bundle')
28
29
  template.start
29
30
  end
30
31
 
@@ -16,7 +16,7 @@ module Potassium::CliOptions # rubocop:disable Metrics/ModuleLength
16
16
  type: :flag,
17
17
  name: [:email_service, :email],
18
18
  desc: "Decides which email adapter to use. Available: aws_ses, sendgrid, none",
19
- default_test_value: "aws_ses"
19
+ default_test_value: "None"
20
20
  },
21
21
  {
22
22
  type: :switch,
@@ -44,8 +44,8 @@ module Potassium::CliOptions # rubocop:disable Metrics/ModuleLength
44
44
  },
45
45
  {
46
46
  type: :switch,
47
- name: "angular-admin",
48
- desc: "Whether to use Angular within ActiveAdmin or not",
47
+ name: :vue_admin,
48
+ desc: "Whether to use Vue within ActiveAdmin or not",
49
49
  negatable: true,
50
50
  default_value: "none",
51
51
  default_test_value: false
@@ -69,9 +69,9 @@ module Potassium::CliOptions # rubocop:disable Metrics/ModuleLength
69
69
  {
70
70
  type: :flag,
71
71
  name: "storage",
72
- desc: "Decides which file storage to use. Available: active_storage, paperclip, none",
72
+ desc: "Decides which file storage to use. Available: active_storage, shrine, none",
73
73
  default_value: "none",
74
- default_test_value: "active_storage"
74
+ default_test_value: "None"
75
75
  },
76
76
  {
77
77
  type: :switch,
@@ -85,7 +85,7 @@ module Potassium::CliOptions # rubocop:disable Metrics/ModuleLength
85
85
  type: :flag,
86
86
  name: "background_processor",
87
87
  desc: "Decides which background processor to use. Available: sidekiq, delayed_job, none",
88
- default_test_value: "sidekiq"
88
+ default_test_value: "None"
89
89
  },
90
90
  {
91
91
  type: :switch,
@@ -3,6 +3,10 @@ module TemplateHelpers
3
3
  @app_name || app_name_from_file
4
4
  end
5
5
 
6
+ def node_version
7
+ "#{Potassium::NODE_VERSION}.x"
8
+ end
9
+
6
10
  def load_recipe(recipe_name)
7
11
  @recipes ||= {}
8
12
  @recipes[recipe_name] ||= get_recipe_class(recipe_name.to_sym).new(self)
@@ -32,10 +32,10 @@ class Recipes::Admin < Rails::AppBuilder
32
32
  private
33
33
 
34
34
  def add_active_admin
35
- gather_gem 'activeadmin', '~> 1.3.0'
35
+ gather_gem 'activeadmin', '~> 2.6'
36
36
  gather_gem 'activeadmin_addons'
37
- gather_gem 'active_skin'
38
-
37
+ gather_gem 'active_skin', github: 'SoftwareBrothers/active_skin'
38
+ add_readme_section :internal_dependencies, :active_admin
39
39
  after(:gem_install, wrap_in_action: :admin_install) do
40
40
  generate "active_admin:install"
41
41
  line = "ActiveAdmin.setup do |config|"
@@ -1,7 +1,7 @@
1
1
  class Recipes::Annotate < Rails::AppBuilder
2
2
  def create
3
3
  gather_gems(:development) do
4
- gather_gem('annotate')
4
+ gather_gem('annotate', '~> 3.0')
5
5
  end
6
6
 
7
7
  template '../assets/lib/tasks/auto_annotate_models.rake',
@@ -1,7 +1,7 @@
1
1
  class Recipes::Ci < Rails::AppBuilder
2
2
  def create
3
3
  copy_file '../assets/Dockerfile.ci', 'Dockerfile.ci'
4
- copy_file '../assets/.circleci/config.yml', '.circleci/config.yml'
4
+ template '../assets/.circleci/config.yml.erb', '.circleci/config.yml'
5
5
 
6
6
  template '../assets/bin/cibuild.erb', 'bin/cibuild'
7
7
  run "chmod a+x bin/cibuild"
@@ -28,16 +28,17 @@ class Recipes::DatabaseContainer < Rails::AppBuilder
28
28
  YAML
29
29
 
30
30
  def create
31
- copy_file '../assets/docker-compose.yml', 'docker-compose.yml'
31
+ db_type = get(:database)
32
+ return if [:None, :none].include? db_type.to_sym
32
33
 
34
+ copy_file '../assets/docker-compose.yml', 'docker-compose.yml'
33
35
  compose = DockerHelpers.new('docker-compose.yml')
34
36
 
35
- db_type = get(:database)
36
37
  compose.add_service(db_type.to_s, self.class.const_get("#{db_type}_service".upcase))
37
38
  compose.add_volume("#{db_type}_data")
38
39
  template '../assets/Makefile.erb', 'Makefile'
39
40
 
40
- run 'make services-up'
41
+ run "docker-compose up -d"
41
42
 
42
43
  set_env(db_type, CONTAINER_VARS[db_type][:port], CONTAINER_VARS[db_type][:user])
43
44
  set_dot_env(db_type, CONTAINER_VARS[db_type][:port], CONTAINER_VARS[db_type][:user])
@@ -53,7 +54,7 @@ class Recipes::DatabaseContainer < Rails::AppBuilder
53
54
  setup_text = # setup file is templated on project creation, manual install is needed
54
55
  <<~TEXT
55
56
  # Set up required services
56
- make services-up
57
+ docker-compose up -d
57
58
 
58
59
  TEXT
59
60
 
@@ -21,7 +21,7 @@ class Recipes::Draper < Rails::AppBuilder
21
21
  end
22
22
 
23
23
  def add_draper
24
- gather_gem 'draper', '3.0.1'
24
+ gather_gem 'draper', '~> 3.1'
25
25
  add_readme_section :internal_dependencies, :draper
26
26
  create_file 'app/decorators/.keep'
27
27
  end
@@ -2,7 +2,7 @@ class Recipes::FileStorage < Rails::AppBuilder
2
2
  def ask
3
3
  storages = {
4
4
  active_storage: 'ActiveStorage',
5
- paperclip: '[DEPRECATED] Paperclip',
5
+ shrine: 'Shrine',
6
6
  none: 'None, thanks'
7
7
  }
8
8
 
@@ -14,45 +14,20 @@ class Recipes::FileStorage < Rails::AppBuilder
14
14
  end
15
15
 
16
16
  def create
17
- add_chosen_storage(check_rspec: false)
17
+ add_chosen_storage
18
18
  end
19
19
 
20
20
  def install
21
21
  ask
22
- add_chosen_storage(check_rspec: true)
22
+ add_chosen_storage
23
23
  end
24
24
 
25
25
  def installed?
26
- gem_exists?(/paperclip/) || file_exist?('config/storage.yml')
26
+ file_exist?('config/storage.yml')
27
27
  end
28
28
 
29
29
  private
30
30
 
31
- def paperclip_config
32
- @paperclip_config ||=
33
- <<~RUBY
34
- config.paperclip_defaults = {
35
- storage: :s3,
36
- s3_protocol: 'https',
37
- s3_region: ENV.fetch('AWS_REGION', 'us-east-1'),
38
- s3_credentials: {
39
- bucket: ENV['S3_BUCKET']
40
- }
41
- }
42
- RUBY
43
- end
44
-
45
- def config_rspec_for_paperclip
46
- copy_file '../assets/testing/platanus.png', 'spec/assets/platanus.png'
47
- copy_file '../assets/testing/paperclip.rb', 'spec/support/paperclip.rb'
48
- end
49
-
50
- def add_paperclip
51
- gather_gem 'paperclip', '~> 6.0'
52
- application paperclip_config, env: 'production'
53
- append_to_file '.gitignore', "/public/system/*\n"
54
- end
55
-
56
31
  def add_active_storage
57
32
  after(:gem_install) { run('bundle exec rails active_storage:install') }
58
33
  copy_file('../assets/config/storage.yml', 'config/storage.yml', force: true)
@@ -62,22 +37,29 @@ class Recipes::FileStorage < Rails::AppBuilder
62
37
  end
63
38
  end
64
39
 
40
+ def add_shrine
41
+ gather_gem('shrine', '~> 3.0')
42
+ gather_gem('marcel', '~> 0.3.3')
43
+ copy_file('../assets/config/shrine.rb', 'config/initializers/shrine.rb', force: true)
44
+ copy_file('../assets/app/uploaders/image_uploader.rb', 'app/uploaders/image_uploader.rb')
45
+ copy_file('../assets/app/uploaders/base_uploader.rb', 'app/uploaders/base_uploader.rb')
46
+ end
47
+
65
48
  def common_setup
66
49
  gather_gem 'aws-sdk-s3', '~> 1.0'
67
50
  add_readme_section :internal_dependencies, get(:storage)
68
51
  append_to_file '.env.development', "S3_BUCKET=\n"
69
52
  end
70
53
 
71
- def add_chosen_storage(check_rspec:)
54
+ def add_chosen_storage
72
55
  return if [:none, :None].include? get(:storage).to_sym
73
56
 
74
57
  common_setup
75
58
  case get(:storage)
76
- when :paperclip
77
- add_paperclip
78
- config_rspec_for_paperclip if !check_rspec || gem_exists?(/rspec-rails/)
79
59
  when :active_storage
80
60
  add_active_storage
61
+ when :shrine
62
+ add_shrine
81
63
  end
82
64
  end
83
65
  end
@@ -17,15 +17,19 @@ class Recipes::FrontEnd < Rails::AppBuilder
17
17
  def create
18
18
  return if [:none, :None].include? get(:front_end).to_sym
19
19
 
20
- gather_gem 'webpacker'
21
-
22
20
  recipe = self
23
21
  after(:gem_install) do
24
22
  value = get(:front_end)
25
23
  run "rails webpacker:install"
26
24
  run "rails webpacker:install:#{value}" if value
27
25
 
28
- recipe.setup_vue_with_compiler_build if value == :vue
26
+ if value == :vue
27
+ recipe.setup_vue_with_compiler_build
28
+ recipe.setup_jest
29
+ end
30
+ recipe.add_responsive_meta_tag
31
+ recipe.setup_tailwind
32
+ add_readme_header :webpack
29
33
  end
30
34
  end
31
35
 
@@ -46,9 +50,7 @@ class Recipes::FrontEnd < Rails::AppBuilder
46
50
  remove_file "app/javascript/packs/hello_vue.js"
47
51
  create_file application_js, application_js_content, force: true
48
52
 
49
- js_pack_tag = "\n <%= javascript_pack_tag 'application' %>\n"
50
53
  layout_file = "app/views/layouts/application.html.erb"
51
- insert_into_file layout_file, js_pack_tag, after: "<%= csrf_meta_tags %>"
52
54
  insert_into_file(
53
55
  layout_file,
54
56
  "<div id=\"vue-app\">\n <app></app>\n ",
@@ -57,6 +59,30 @@ class Recipes::FrontEnd < Rails::AppBuilder
57
59
  insert_into_file layout_file, "\n </div>", after: "<%= yield %>"
58
60
  end
59
61
 
62
+ def add_responsive_meta_tag
63
+ tag = "\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n"
64
+ layout_file = "app/views/layouts/application.html.erb"
65
+ insert_into_file layout_file, tag, after: "<%= csrf_meta_tags %>"
66
+ end
67
+
68
+ def setup_tailwind
69
+ run 'bin/yarn add tailwindcss'
70
+ setup_client_css
71
+ remove_server_css_requires
72
+ setup_tailwind_requirements
73
+ end
74
+
75
+ def setup_jest
76
+ run 'bin/yarn add jest vue-jest babel-jest @vue/test-utils jest-serializer-vue babel-core@^7.0.0-bridge.0 --dev'
77
+ json_file = File.read(Pathname.new("package.json"))
78
+ js_package = JSON.parse(json_file)
79
+ js_package = js_package.merge(jest_config)
80
+ json_string = JSON.pretty_generate(js_package)
81
+ create_file 'package.json', json_string, force: true
82
+
83
+ copy_file '../assets/app/javascript/app.spec.js', 'app/javascript/app.spec.js'
84
+ end
85
+
60
86
  private
61
87
 
62
88
  def frameworks(framework)
@@ -68,6 +94,42 @@ class Recipes::FrontEnd < Rails::AppBuilder
68
94
  frameworks[framework]
69
95
  end
70
96
 
97
+ def setup_client_css
98
+ application_css = 'app/javascript/css/application.css'
99
+ create_file application_css, "", force: true
100
+
101
+ stylesheet_pack_tag = "\n <%= stylesheet_pack_tag 'application' %>\n "
102
+ layout_file = "app/views/layouts/application.html.erb"
103
+ insert_into_file layout_file, stylesheet_pack_tag, before: "</head>"
104
+
105
+ application_js = 'app/javascript/packs/application.js'
106
+ if get(:front_end) != :vue
107
+ create_file application_js, "import '../css/application.css';\n", force: true
108
+ else
109
+ insert_into_file(
110
+ application_js,
111
+ "\nimport '../css/application.css';",
112
+ after: "import App from '../app.vue';"
113
+ )
114
+ end
115
+ end
116
+
117
+ def setup_tailwind_requirements
118
+ application_css = 'app/javascript/css/application.css'
119
+ insert_into_file application_css, tailwind_client_css
120
+
121
+ tailwind_config = 'tailwind.config.js'
122
+ create_file tailwind_config, tailwind_config_content, force: true
123
+
124
+ postcss_file = 'postcss.config.js'
125
+ insert_into_file postcss_file, postcss_require_tailwind, after: "plugins: [\n"
126
+ end
127
+
128
+ def remove_server_css_requires
129
+ assets_css_file = 'app/assets/stylesheets/application.css'
130
+ gsub_file(assets_css_file, " *= require_tree .\n *= require_self\n", "")
131
+ end
132
+
71
133
  def application_js_content
72
134
  <<~JS
73
135
  import Vue from 'vue/dist/vue.esm';
@@ -83,4 +145,65 @@ class Recipes::FrontEnd < Rails::AppBuilder
83
145
  });
84
146
  JS
85
147
  end
148
+
149
+ def tailwind_client_css
150
+ <<~CSS
151
+ @import 'tailwindcss/base';
152
+ @import 'tailwindcss/components';
153
+ @import 'tailwindcss/utilities';
154
+ CSS
155
+ end
156
+
157
+ def tailwind_config_content
158
+ <<~JS
159
+ /* eslint-disable no-undef */
160
+ module.exports = {
161
+ theme: {
162
+ extend: {},
163
+ },
164
+ variants: {},
165
+ plugins: [],
166
+ };
167
+ JS
168
+ end
169
+
170
+ def postcss_require_tailwind
171
+ <<-JS.gsub(/^ {4}/, ' ')
172
+ require('tailwindcss'),
173
+ require('autoprefixer'),
174
+ JS
175
+ end
176
+
177
+ def jest_config
178
+ {
179
+ "scripts": {
180
+ "test": "jest",
181
+ "test:watch": "jest --watch"
182
+ },
183
+ "jest": {
184
+ "roots": [
185
+ "app/javascript"
186
+ ],
187
+ "moduleDirectories": [
188
+ "node_modules",
189
+ "app/javascript"
190
+ ],
191
+ "moduleNameMapper": {
192
+ "^@/(.*)$": "app/javascript/$1"
193
+ },
194
+ "moduleFileExtensions": [
195
+ "js",
196
+ "json",
197
+ "vue"
198
+ ],
199
+ "transform": {
200
+ "^.+\\.js$": "<rootDir>/node_modules/babel-jest",
201
+ ".*\\.(vue)$": "<rootDir>/node_modules/vue-jest"
202
+ },
203
+ "snapshotSerializers": [
204
+ "<rootDir>/node_modules/jest-serializer-vue"
205
+ ]
206
+ }
207
+ }
208
+ end
86
209
  end
@@ -24,8 +24,11 @@ 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
+ 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
29
32
  end
30
33
 
31
34
  def install
@@ -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