schienenzeppelin 0.1

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 (117) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/main.yml +17 -0
  3. data/.github/workflows/outdated.yml +20 -0
  4. data/.gitignore +282 -0
  5. data/.rspec_status +3 -0
  6. data/.rubocop.yml +28 -0
  7. data/.tool-versions +1 -0
  8. data/CODE_OF_CONDUCT.md +84 -0
  9. data/GALLERY.md +29 -0
  10. data/Gemfile +13 -0
  11. data/Gemfile.lock +184 -0
  12. data/LICENSE +21 -0
  13. data/LICENSE.txt +21 -0
  14. data/README.md +174 -0
  15. data/Rakefile +12 -0
  16. data/TODO.md +32 -0
  17. data/bin/console +15 -0
  18. data/bin/setup +8 -0
  19. data/exe/sz +25 -0
  20. data/images/create.png +0 -0
  21. data/images/error.png +0 -0
  22. data/images/home.png +0 -0
  23. data/images/index.png +0 -0
  24. data/images/login.png +0 -0
  25. data/images/mobile.png +0 -0
  26. data/lib/schienenzeppelin.rb +18 -0
  27. data/lib/schienenzeppelin/app_builder.rb +88 -0
  28. data/lib/schienenzeppelin/app_generator.rb +95 -0
  29. data/lib/schienenzeppelin/helper_base.rb +28 -0
  30. data/lib/schienenzeppelin/helpers/annotate.rb +11 -0
  31. data/lib/schienenzeppelin/helpers/capistrano.rb +35 -0
  32. data/lib/schienenzeppelin/helpers/continuous_integration.rb +12 -0
  33. data/lib/schienenzeppelin/helpers/credentials.rb +49 -0
  34. data/lib/schienenzeppelin/helpers/devise.rb +45 -0
  35. data/lib/schienenzeppelin/helpers/errors.rb +67 -0
  36. data/lib/schienenzeppelin/helpers/factory_bot.rb +13 -0
  37. data/lib/schienenzeppelin/helpers/generators.rb +26 -0
  38. data/lib/schienenzeppelin/helpers/high_voltage.rb +11 -0
  39. data/lib/schienenzeppelin/helpers/home.rb +17 -0
  40. data/lib/schienenzeppelin/helpers/hotwire.rb +11 -0
  41. data/lib/schienenzeppelin/helpers/pundit.rb +12 -0
  42. data/lib/schienenzeppelin/helpers/rspec.rb +13 -0
  43. data/lib/schienenzeppelin/helpers/rubocop.rb +11 -0
  44. data/lib/schienenzeppelin/helpers/scaffold.rb +11 -0
  45. data/lib/schienenzeppelin/helpers/services.rb +11 -0
  46. data/lib/schienenzeppelin/helpers/sidekiq.rb +13 -0
  47. data/lib/schienenzeppelin/helpers/stimulus.rb +11 -0
  48. data/lib/schienenzeppelin/helpers/stimulus_components.rb +29 -0
  49. data/lib/schienenzeppelin/helpers/tailwind.rb +18 -0
  50. data/lib/schienenzeppelin/version.rb +7 -0
  51. data/schienenzeppelin.gemspec +37 -0
  52. data/schienenzeppelin.jpg +0 -0
  53. data/script.md +104 -0
  54. data/templates/.dockerignore.erb +51 -0
  55. data/templates/.entrypoint.sh.erb +16 -0
  56. data/templates/.env.development.erb +3 -0
  57. data/templates/.foreman.erb +1 -0
  58. data/templates/.github/workflows/build.yml.erb +49 -0
  59. data/templates/.gitignore.erb +285 -0
  60. data/templates/.irbrc.erb +12 -0
  61. data/templates/.rubocop.yml.erb +37 -0
  62. data/templates/.tool-versions.erb +1 -0
  63. data/templates/Capfile.erb +24 -0
  64. data/templates/Dockerfile.erb +45 -0
  65. data/templates/Gemfile.erb +122 -0
  66. data/templates/Procfile.dev.erb +6 -0
  67. data/templates/Procfile.erb +2 -0
  68. data/templates/README.md.erb +43 -0
  69. data/templates/app/controllers/authorized_controller.rb.erb +10 -0
  70. data/templates/app/controllers/errors_controller.rb.erb +24 -0
  71. data/templates/app/javascript/images/checkmark.svg +1 -0
  72. data/templates/app/javascript/images/logo.svg +1 -0
  73. data/templates/app/javascript/stylesheets/components.scss +206 -0
  74. data/templates/app/policies/application_policy.rb +55 -0
  75. data/templates/app/services/application_service.rb +14 -0
  76. data/templates/app/views/devise/confirmations/new.html.erb +24 -0
  77. data/templates/app/views/devise/passwords/edit.html.erb +34 -0
  78. data/templates/app/views/devise/passwords/new.html.erb +23 -0
  79. data/templates/app/views/devise/registrations/edit.html.erb +62 -0
  80. data/templates/app/views/devise/registrations/new.html.erb +45 -0
  81. data/templates/app/views/devise/sessions/new.html.erb +33 -0
  82. data/templates/app/views/devise/shared/_error_messages.html.erb +15 -0
  83. data/templates/app/views/devise/shared/_form_wrap.html.erb +5 -0
  84. data/templates/app/views/devise/shared/_links.html.erb +25 -0
  85. data/templates/app/views/devise/unlocks/new.html.erb +22 -0
  86. data/templates/app/views/errors/internal_error.html.erb +14 -0
  87. data/templates/app/views/errors/not_found.html.erb +14 -0
  88. data/templates/app/views/errors/unacceptable.html.erb +14 -0
  89. data/templates/app/views/layouts/application.html.erb.tt +30 -0
  90. data/templates/app/views/pages/home.html.erb.tt +20 -0
  91. data/templates/app/views/shared/_flashes.html.erb.tt +12 -0
  92. data/templates/app/views/shared/_footer.html.erb.tt +21 -0
  93. data/templates/app/views/shared/_navbar.html.erb.tt +40 -0
  94. data/templates/bin/setup.erb +146 -0
  95. data/templates/config/credentials.yml.erb +7 -0
  96. data/templates/config/initializers/high_voltage.rb +6 -0
  97. data/templates/config/initializers/lograge.rb +5 -0
  98. data/templates/config/initializers/sidekiq.rb +10 -0
  99. data/templates/config/postgresql.yml.erb +23 -0
  100. data/templates/docker-compose.yml.erb +21 -0
  101. data/templates/lib/capistrano/tasks/seeds.rake +12 -0
  102. data/templates/lib/capistrano/tasks/sidekiq.rake +36 -0
  103. data/templates/lib/generators/rails/navigation/USAGE +6 -0
  104. data/templates/lib/generators/rails/navigation/navigation_generator.rb +15 -0
  105. data/templates/lib/generators/rails/scaffold_controller_generator.rb +12 -0
  106. data/templates/lib/tasks/auto_annotate_models.rake +58 -0
  107. data/templates/lib/templates/erb/scaffold/_form.html.erb +39 -0
  108. data/templates/lib/templates/erb/scaffold/edit.html.erb +7 -0
  109. data/templates/lib/templates/erb/scaffold/index.html.erb +34 -0
  110. data/templates/lib/templates/erb/scaffold/new.html.erb +7 -0
  111. data/templates/lib/templates/erb/scaffold/show.html.erb +18 -0
  112. data/templates/public/500.html.erb +26 -0
  113. data/templates/spec/rails_helper.rb +26 -0
  114. data/templates/spec/spec_helper.rb +61 -0
  115. data/templates/spec/support/factory_bot.rb +5 -0
  116. data/templates/spec/support/shoulda_matchers.rb +8 -0
  117. metadata +179 -0
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schienenzeppelin
4
+ module Helpers
5
+ class Devise < HelperBase
6
+ def apply
7
+ generate('devise:install', capture: true)
8
+ generate(:devise, 'User', 'name', 'admin:boolean', capture: true)
9
+ directory('app/views/devise', 'app/views/devise')
10
+
11
+ inject_into_file 'config/environments/development.rb', before: /^end/ do
12
+ <<-RUBY
13
+
14
+ # Enable devise mailer
15
+ config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
16
+ RUBY
17
+ end
18
+
19
+ gsub_file 'config/initializers/devise.rb', /# config.pepper = .+/, " # config.pepper = 'pepper'"
20
+ gsub_file 'config/initializers/devise.rb', /# config.secret_key = .+/, " # config.secret_key = 'secret_key'"
21
+
22
+ db_changes
23
+ end
24
+
25
+ private
26
+
27
+ def db_changes
28
+ in_root do
29
+ migration = Dir.glob('db/migrate/*').max_by { |f| File.mtime(f) }
30
+ gsub_file migration, /:admin/, ':admin, default: false'
31
+ end
32
+ insert_into_file 'db/seeds.rb' do
33
+ <<~RUBY
34
+ # Create an initial admin user for development
35
+ User.find_or_create_by(email: "admin@admin.com") do |user|
36
+ user.name = 'Admin'
37
+ user.password = 'password'
38
+ user.admin = true
39
+ end
40
+ RUBY
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schienenzeppelin
4
+ module Helpers
5
+ class Errors < HelperBase
6
+ def apply
7
+ say 'Setting up custom error pages'
8
+ add_errors
9
+ setup_public
10
+ add_routes
11
+ patch_turbolinks
12
+ end
13
+
14
+ private
15
+
16
+ def setup_public
17
+ template('public/500.html.erb', 'public/500.html', force: true)
18
+ remove_file('public/404.html')
19
+ remove_file('public/422.html')
20
+ end
21
+
22
+ def add_errors
23
+ directory('app/views/errors', 'app/views/errors')
24
+ template('app/controllers/errors_controller.rb.erb', 'app/controllers/errors_controller.rb')
25
+ inject_into_file 'config/application.rb', before: /^ {2}end\n/ do
26
+ <<-RUBY
27
+ # Enable custom error pages
28
+ config.exceptions_app = routes
29
+ RUBY
30
+ end
31
+ end
32
+
33
+ def add_routes
34
+ inject_into_file 'config/routes.rb', before: /^end/ do
35
+ <<-RUBY
36
+ get '/404', to: 'errors#not_found'
37
+ get '/422', to: 'errors#unacceptable'
38
+ get '/500', to: 'errors#internal_error'
39
+ RUBY
40
+ end
41
+ end
42
+
43
+ def patch_turbolinks
44
+ inject_into_file 'app/javascript/packs/application.js' do
45
+ <<~JS
46
+ // Patching turbolinks to allow custom errors
47
+ // See https://github.com/turbolinks/turbolinks/issues/179
48
+ window.Turbolinks.HttpRequest.prototype.requestLoaded = function() {
49
+ return this.endRequest(function() {
50
+ var code = this.xhr.status;
51
+ if (200 <= code && code < 300 ||
52
+ code === 403 || code === 404 || code === 500) {
53
+ this.delegate.requestCompletedWithResponse(
54
+ this.xhr.responseText,
55
+ this.xhr.getResponseHeader("Turbolinks-Location"));
56
+ } else {
57
+ this.failed = true;
58
+ this.delegate.requestFailedWithStatusCode(code, this.xhr.responseText);
59
+ }
60
+ }.bind(this));
61
+ };
62
+ JS
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schienenzeppelin
4
+ module Helpers
5
+ class FactoryBot < HelperBase
6
+ def apply
7
+ # TODO: Make this work for minitest
8
+ empty_directory 'spec/factories'
9
+ template('spec/support/factory_bot.rb')
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schienenzeppelin
4
+ module Helpers
5
+ class Generators < HelperBase
6
+ def apply
7
+ directory('lib/generators', 'lib/generators')
8
+ generators = <<-RUBY
9
+ config.generators do |generate|
10
+ require_relative '../lib/generators/rails/scaffold_controller_generator'
11
+ generate.helper false
12
+ generate.javascripts false
13
+ generate.request_specs false
14
+ generate.routing_specs false
15
+ generate.stylesheets false
16
+ generate.test_framework :rspec
17
+ generate.view_specs false
18
+ generate.jb true
19
+ generate.factory_bot true
20
+ end
21
+ RUBY
22
+ inject_into_file 'config/application.rb', generators, before: /^ {2}end\n/
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schienenzeppelin
4
+ module Helpers
5
+ class HighVoltage < HelperBase
6
+ def apply
7
+ template('config/initializers/high_voltage.rb')
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schienenzeppelin
4
+ module Helpers
5
+ class Home < HelperBase
6
+ def apply
7
+ say 'Setting up custom landing page'
8
+ directory 'app/views/layouts', 'app/views/layouts', force: true
9
+ directory 'app/views/pages', 'app/views/pages'
10
+ directory 'app/views/shared', 'app/views/shared'
11
+ # Make sure we can access any images put into the images folder
12
+ inject_into_file('app/javascript/packs/application.js', "require.context('../images', true)")
13
+ directory 'app/javascript/images', 'app/javascript/images'
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schienenzeppelin
4
+ module Helpers
5
+ class Hotwire < HelperBase
6
+ def apply
7
+ rails_command('hotwire:install')
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schienenzeppelin
4
+ module Helpers
5
+ class Pundit < HelperBase
6
+ def apply
7
+ directory 'app/policies', 'app/policies'
8
+ template 'app/controllers/authorized_controller.rb.erb', 'app/controllers/authorized_controller.rb'
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schienenzeppelin
4
+ module Helpers
5
+ class Rspec < HelperBase
6
+ def apply
7
+ create_file('.rspec', '--require spec_helper')
8
+ template('spec/rails_helper.rb')
9
+ template('spec/spec_helper.rb')
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schienenzeppelin
4
+ module Helpers
5
+ class Rubocop < HelperBase
6
+ def apply
7
+ template '.rubocop.yml.erb', '.rubocop.yml'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schienenzeppelin
4
+ module Helpers
5
+ class Scaffold < HelperBase
6
+ def apply
7
+ directory 'lib', 'lib'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schienenzeppelin
4
+ module Helpers
5
+ class Services < HelperBase
6
+ def apply
7
+ directory 'app/services', 'app/services'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schienenzeppelin
4
+ module Helpers
5
+ class Sidekiq < HelperBase
6
+ def apply
7
+ say 'Setting up sidekiq'
8
+ empty_directory('app/workers')
9
+ create_file('app/workers/.keep')
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schienenzeppelin
4
+ module Helpers
5
+ class Stimulus < HelperBase
6
+ def apply
7
+ rails_command('stimulus:install')
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schienenzeppelin
4
+ module Helpers
5
+ class StimulusComponents < HelperBase
6
+ def apply
7
+ run('yarn add tailwindcss-stimulus-components')
8
+ inject_into_file('app/javascript/packs/application.js') do
9
+ <<~JS
10
+ import { Application } from "stimulus"
11
+ import { definitionsFromContext } from "stimulus/webpack-helpers"
12
+
13
+ const application = Application.start();
14
+ const context = require.context("controllers", true, /.js$/);
15
+ application.load(definitionsFromContext(context));
16
+
17
+ import { Dropdown, Modal, Tabs, Popover, Toggle, Slideover } from "tailwindcss-stimulus-components"
18
+ application.register('dropdown', Dropdown)
19
+ application.register('modal', Modal)
20
+ application.register('tabs', Tabs)
21
+ application.register('popover', Popover)
22
+ application.register('toggle', Toggle)
23
+ application.register('slideover', Slideover)
24
+ JS
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schienenzeppelin
4
+ module Helpers
5
+ class Tailwind < HelperBase
6
+ def apply
7
+ rails_command('tailwindcss:install')
8
+ template('app/javascript/stylesheets/components.scss')
9
+ insert_into_file 'app/javascript/stylesheets/application.scss' do
10
+ <<~CSS
11
+
12
+ @import "components";
13
+ CSS
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schienenzeppelin
4
+ VERSION = '0.1'
5
+ RAILS_VERSION = '~> 6.1.2'
6
+ RUBY_VERSION = '3.0.0'
7
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+ require_relative 'lib/schienenzeppelin/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'schienenzeppelin'
8
+ spec.version = Schienenzeppelin::VERSION
9
+ spec.authors = ['Hans-Jörg Schnedlitz']
10
+ spec.email = ['hans.schnedlitz@gmail.com']
11
+ spec.date = Date.today.strftime('%Y-%m-%d')
12
+
13
+ spec.summary = 'The Rails app generator that gets you started quickly'
14
+ spec.description = <<~HEREDOC
15
+ Schienenzeppelin is the Ruby on Rails app generator to get you started quickly. Includes TailwindCSS,
16
+ Devise, Capistrano and much more.
17
+ HEREDOC
18
+ spec.license = 'MIT'
19
+ spec.homepage = 'https://github.com/hschne/schienenzeppelin'
20
+
21
+ spec.required_ruby_version = Gem::Requirement.new(">= #{Schienenzeppelin::RUBY_VERSION}")
22
+
23
+ spec.metadata['homepage_uri'] = spec.homepage
24
+ spec.metadata['source_code_uri'] = 'https://github.com/hschne/schienenzeppelin'
25
+ spec.metadata['changelog_uri'] = 'https://github.com/hschne/schienenzeppelin/CHANGELOG.md'
26
+
27
+ # Specify which files should be added to the gem when it is released.
28
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
29
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
30
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
31
+ end
32
+ spec.bindir = 'exe'
33
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
34
+ spec.require_paths = ['lib']
35
+
36
+ spec.add_dependency 'rails', Schienenzeppelin::RAILS_VERSION
37
+ end
Binary file
data/script.md ADDED
@@ -0,0 +1,104 @@
1
+
2
+ # Script
3
+
4
+ ## Prep
5
+
6
+ - SSH agent eval
7
+ - Empty GH repo
8
+ - Reset local docker
9
+ - Droplet reset database (but make sure DB exists!)
10
+ - Make sure spring boot is not on
11
+
12
+ ## Steps
13
+
14
+ When you start a new application with Rails, I usually spend ours adding things to make it production ready. From database configuration, to little
15
+ development tweaks, to thing like authentication and styling. Wouldn't it be nice if generating a new application gave you
16
+ all these things out of the box?
17
+
18
+ Schienenzeppelin was made to create a production ready app that you can deploy within minutes. And that's exactly what I'll show here.
19
+
20
+ After you've installed the gem, just run it to create a new application.
21
+
22
+ ```bash
23
+ sz tweeter
24
+ ```
25
+
26
+ This generates a new application, like rails new, but with some tweaks. The focus here is to get a nice looking, functional
27
+ application to production as quick as possible.
28
+
29
+ That includes
30
+ - Basic styling - using Tailwind
31
+ - Authentication using Devise
32
+ - A production ready database and credential setup
33
+ - And capistrano deployment out of the box
34
+
35
+ There are tons of little details and utilities included, I'll leave a link in the description for you to check out.
36
+
37
+ Now that that's done, let's have a look at what we got.
38
+ ```bash
39
+ cd tweeter
40
+ ```
41
+
42
+ Now the first nice thing is that although we depend on things like postgres and redis for example for Sidekiq, there's no need to install
43
+ any of that if you have docker and docker compose. Foreman start to start everything
44
+ ```ruby
45
+ foreman start
46
+ ```
47
+ We still have to do the usual first time setup
48
+ ```
49
+ rails db:setup
50
+ rails db:migrate
51
+ ```
52
+ Out of the box we get a responsive homepage, and devise integration, with custom views to boot. A seedfile is provided
53
+ to generate an admin user
54
+ ```
55
+ rails db:seed
56
+ ```
57
+ So we can login, and edit our profile if we want.
58
+
59
+ Let's add a new model and see how that looks.
60
+ ```bash
61
+ rails g scaffold tweet
62
+ rails db:migrate
63
+ ```
64
+ We can see that that generates a new link, and the scaffold is styled too.
65
+
66
+ That's dandy and all, let's deploy that thing. We have to edit some files for that.
67
+ ```
68
+ vim config/deploy.rb
69
+ set :repo_url, "git@github.com:hschne/tweeter.git"
70
+ ```
71
+
72
+ This assumes you already have set up a server. I'll also leave a link how to set one up.
73
+ ```
74
+ vim config/production.rb
75
+ server "207.154.218.89", user: "deploy", roles: %w{app db web}
76
+ ```
77
+
78
+ Before we can deploy, let's push our changes:
79
+ ```
80
+ git add . && git commit -m "Initial commit"
81
+ git remote add origin git@github.com:hschne/tweeter.git
82
+ git branch -M main
83
+ git push -f -u origin main
84
+ ```
85
+
86
+ And deploy
87
+ ```
88
+ cap production deploy
89
+ ```
90
+
91
+ Get secret key
92
+
93
+ ```
94
+ cat config/credentials/production.key | xclip -sel clipboard
95
+ ```
96
+
97
+ BREAK
98
+
99
+ Once that is finished, we can check out the deployed version.
100
+
101
+ And that is how you deploy a nice looking production ready app using Schienenzeppelin.
102
+
103
+ Let me know what you think, and have fun!
104
+