frozen_rails 0.0.2 → 0.0.4

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +79 -2
  3. data/lib/frozen_rails/appendable.rb +13 -0
  4. data/lib/frozen_rails/bundleable.rb +11 -0
  5. data/lib/frozen_rails/copyable.rb +14 -0
  6. data/lib/frozen_rails/generator.rb +16 -0
  7. data/lib/frozen_rails/taggable.rb +58 -0
  8. data/lib/frozen_rails/version.rb +1 -1
  9. data/lib/frozen_rails.rb +0 -1
  10. data/lib/generators/frozen/db_generator.rb +128 -0
  11. data/lib/generators/frozen/md_generator.rb +84 -0
  12. data/lib/generators/frozen/rails_generator.rb +39 -0
  13. data/lib/generators/frozen/seo_generator.rb +109 -0
  14. data/lib/generators/frozen/ssg_generator.rb +48 -0
  15. data/lib/generators/frozen/templates/db/database.yml +18 -0
  16. data/lib/generators/frozen/templates/db/initializers/friendly_id.rb +13 -0
  17. data/lib/generators/frozen/templates/db/initializers/readonly_routes.rb +14 -0
  18. data/lib/generators/frozen/templates/db/initializers/sqlite_uuid.rb +25 -0
  19. data/lib/generators/frozen/templates/db/initializers/static_db.rb +3 -0
  20. data/lib/generators/frozen/templates/db/lib/generators/avo/controller/controller_generator.rb +13 -0
  21. data/lib/generators/frozen/templates/db/lib/generators/avo/install/install_generator.rb +11 -0
  22. data/lib/generators/frozen/templates/db/lib/generators/avo/resource/resource_generator.rb +30 -0
  23. data/lib/generators/frozen/templates/db/lib/templates/active_record/model/model.rb.tt +26 -0
  24. data/lib/generators/frozen/templates/db/lib/templates/erb/scaffold/index.html.erb.tt +14 -0
  25. data/lib/generators/frozen/templates/db/lib/templates/erb/scaffold/partial.html.erb.tt +17 -0
  26. data/lib/generators/frozen/templates/db/lib/templates/erb/scaffold/show.html.erb.tt +10 -0
  27. data/lib/generators/frozen/templates/db/lib/templates/rails/scaffold_controller/controller.rb.tt +11 -0
  28. data/lib/generators/frozen/templates/md/controllers/categories_controller.rb +9 -0
  29. data/lib/generators/frozen/templates/md/controllers/pages_controller.rb +5 -0
  30. data/lib/generators/frozen/templates/md/helpers/markdown_helper.rb +14 -0
  31. data/lib/generators/frozen/templates/md/initializers/decant_extensions.rb +15 -0
  32. data/lib/generators/frozen/templates/md/models/category.rb +35 -0
  33. data/lib/generators/frozen/templates/md/models/concerns/linkable.rb +41 -0
  34. data/lib/generators/frozen/templates/md/models/page.rb +13 -0
  35. data/lib/generators/frozen/templates/md/pages/frozen-rails.md +12 -0
  36. data/lib/generators/frozen/templates/md/pages/rails-static/rails-static-logo.webp +0 -0
  37. data/lib/generators/frozen/templates/md/pages/rails-static.md +17 -0
  38. data/lib/generators/frozen/templates/md/views/categories/index.html.erb +5 -0
  39. data/lib/generators/frozen/templates/md/views/categories/show.html.erb +13 -0
  40. data/lib/generators/frozen/templates/md/views/pages/show.html.erb +5 -0
  41. data/lib/generators/frozen/templates/seo/_og_meta_tags.html.erb +3 -0
  42. data/lib/generators/frozen/templates/seo/robots_generatable.rb +14 -0
  43. data/lib/generators/frozen/templates/seo/sitemap.rb +15 -0
  44. data/lib/generators/frozen/templates/seo/sitemap_entry.rb +12 -0
  45. data/lib/generators/frozen/templates/ssg/storage.yml +12 -0
  46. data/lib/generators/frozen/templates/ui/cable.yml +5 -0
  47. data/lib/generators/frozen/templates/ui/hotkey_controller.js +9 -0
  48. data/lib/generators/frozen/ui_generator.rb +81 -0
  49. metadata +53 -52
  50. data/lib/frozen_rails/railtie.rb +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e185eb3c340edef2f4261d427c7f89af260850823fd580424605617747da9737
4
- data.tar.gz: fd604198415fdede3bf2e27565c96550d747d14a92c1faf04308c0fa99c764b0
3
+ metadata.gz: 0d1e4038850bfbf0972c64d50c2de0ef854c1165cbef4f6fba5c63cc963d5e39
4
+ data.tar.gz: 47a78650b8de3d17aa463157c78c1242789389e4b0df03b56eeef9057efe9fbd
5
5
  SHA512:
6
- metadata.gz: d618e3625585a1b41fa98665773113f9d12826881054dbcc38d9287829ad5b2fc1f4efabef3a4485542342280083845e4fbaaf0e786bbbfdb778479c00a6dcd0
7
- data.tar.gz: c8b5371939545258ea853ed909f6acbe046874c914dfea7b2fd6d03e0aca75ca08c4bd879778ccaced3f25568347531175cdbab2b052ace3e2fb178181340716
6
+ metadata.gz: d33940e9f7a07bc38a4dcdcdb867396b137e5a2bb265207fb46e45c1b65a3593f30127bd63f23ed12c5aa5d01dde3aa924a83049178f3be71d34108b5aca67c7
7
+ data.tar.gz: 3a7a176184cf0b5d87b0a4727187e5ee89ee46e11fbf7a2a13d66a05efcb9a759f72f973c243bab36b962514e94303f1ecc00b3058fcde5a1dbdbf4893faa25d
data/README.md CHANGED
@@ -4,7 +4,12 @@
4
4
 
5
5
  # Frozen Rails
6
6
 
7
- TBD
7
+ DISCLAIMER: I generated this repo with AI and haven't tested it yet.
8
+ I will verify manually verify the functionality and use AI to generate a test suite as soon as I get to it. Until then, use at your own risk.
9
+
10
+ This repo automates the instructions from [rails-static.com](https://rails-static.com) and some additional setup which was created by me.
11
+
12
+ Running the full setup `bin/rails g frozen:rails` should turn a fresh Rails application into a full static-site generator that supports Markdown AND structured data (SQLite). The SSG is very developer-focused and might be cumbersome to use for non-devs.
8
13
 
9
14
  ## Installation
10
15
 
@@ -33,7 +38,79 @@ gem "frozen_rails"
33
38
 
34
39
  ## Usage
35
40
 
36
- TBD
41
+ The gem ships with Rails generators under the `frozen` namespace. After including `frozen_rails` in your Rails application the following generators will be available:
42
+
43
+ ```bash
44
+ # ✅ passes, ✅ works correctly, ❌ has test suite
45
+
46
+ # - Adds `decant` with a custom extension.
47
+ # - Adds `kramdown`, `rouge` and ERB interpolation for `.md` files.
48
+ # - Adds application scaffold.
49
+
50
+ bin/rails g frozen:md
51
+ # - Prompts for a rouge theme. Pass `--rouge_theme=name` to run non-interactive.
52
+ ```
53
+
54
+ ```bash
55
+ # ✅ passes, ✅ works correctly, ❌ has test suite
56
+
57
+ # - Adds `parklife`
58
+ # - Adds `.gitlab-ci.yml` and tweaks parklife config.
59
+
60
+ bin/rails g frozen:ssg
61
+ ```
62
+
63
+ ```bash
64
+ # ✅ passes, ✅ works correctly, ❌ has test suite
65
+
66
+ # - Adds `sqlite_extensions-uuid`
67
+ # - Adds `static_db`
68
+ # - Adds `friendly_id`
69
+ # - Adds `avo`
70
+ # - Adjusts Rails scaffold generator and migration generator to be more helpful
71
+
72
+ bin/rails g frozen:db
73
+ ```
74
+
75
+ ```bash
76
+ # ✅ passes, ❓ works correctly, ❌ has test suite
77
+
78
+ # - SEO helpers
79
+ # - sitemap/robots skeleton
80
+
81
+ bin/rails g frozen:seo
82
+ ```
83
+
84
+ ```bash
85
+ # FUTURE: frozen:erb
86
+
87
+ # - view_component
88
+ # - herb
89
+ # - lookbook
90
+ ```
91
+
92
+ ```bash
93
+ # ✅ passes, ❓ works correctly, ❌ has test suite
94
+
95
+ # - water.css
96
+ # - sample importmap pin
97
+ # - Hotwire Spark & Action Cable configuration
98
+ # - Stimulus hotkey controller
99
+
100
+ bin/rails g frozen:ui
101
+ ```
102
+
103
+ ```bash
104
+ # ✅ passes, ❓ works correctly, ❌ has test suite
105
+
106
+ # Full Rails setup (runs all frozen generators in order)
107
+
108
+ bin/rails g frozen:rails
109
+ ```
110
+
111
+ ```bash
112
+ # FUTURE: frozen:meetup
113
+ ```
37
114
 
38
115
  ## Contributing
39
116
 
@@ -0,0 +1,13 @@
1
+ module Appendable
2
+ extend ActiveSupport::Concern
3
+
4
+ private
5
+
6
+ def append_to_application_config(content)
7
+ inject_into_file "config/application.rb", optimize_indentation("\n#{content}", 4), before: / end\nend(?:\n)\z/, verbose: false
8
+ end
9
+
10
+ def append_to_routes(content)
11
+ inject_into_file "config/routes.rb", optimize_indentation("\n#{content}", 2), before: /end(?:\n)\z/, verbose: false
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ module Bundleable
2
+ extend ActiveSupport::Concern
3
+
4
+ private
5
+
6
+ def bundle!
7
+ Bundler.with_unbundled_env do
8
+ system "bundle install"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ module Copyable
2
+ extend ActiveSupport::Concern
3
+
4
+ private
5
+
6
+ def copy_directory(source, target, **options)
7
+ base = File.join(self.class.source_root, source)
8
+ Dir.glob("#{base}/**/*").each do |path|
9
+ next if File.directory?(path)
10
+ rel = path.delete_prefix("#{base}/")
11
+ copy_file "#{source}/#{rel}", "#{target}/#{rel}", **options
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "frozen_rails/appendable"
5
+ require "frozen_rails/bundleable"
6
+ require "frozen_rails/copyable"
7
+ require "frozen_rails/taggable"
8
+
9
+ module FrozenRails
10
+ class Generator < Rails::Generators::Base
11
+ include Appendable
12
+ include Bundleable
13
+ include Copyable
14
+ include Taggable
15
+ end
16
+ end
@@ -0,0 +1,58 @@
1
+ module Taggable
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ class_option :cleanup_frozen_tags, type: :boolean, default: true, hide: true
6
+ end
7
+
8
+ private
9
+
10
+ def add_frozen_gems(content, env: nil)
11
+ add_frozen_gemfile_tags(env:)
12
+ insert_into_file "Gemfile", optimize_indentation("#{content}\n", env ? 2 : 0), before: frozen_end_tag(env:)
13
+ cleanup_frozen_tags!(env:)
14
+ end
15
+
16
+ def frozen_start_tag(env: nil)
17
+ env_attribute = env ? " env=\"#{env}\"" : ""
18
+ "# <frozen_rails#{env_attribute}>"
19
+ end
20
+
21
+ def frozen_end_tag(env: nil)
22
+ frozen_start_tag(env:).gsub("# <", "# </")
23
+ end
24
+
25
+ def add_frozen_gemfile_tags(env: nil)
26
+ unless File.read("Gemfile").include?(frozen_start_tag(env:))
27
+ newlines = env ? "\n" : "\n\n"
28
+ tags = "#{frozen_start_tag(env:)}#{newlines}#{frozen_end_tag(env:)}"
29
+ insert_into_file "Gemfile", tags, after: frozen_tags_after(env:)
30
+ end
31
+ end
32
+
33
+ def frozen_tags_after(env: nil)
34
+ case env
35
+ when nil
36
+ /frozen_rails[^\n]*\n/
37
+ when "local"
38
+ "group :development, :test do\n"
39
+ when "development"
40
+ "group :development do\n"
41
+ else
42
+ raise "Unsupported env!"
43
+ end
44
+ end
45
+
46
+ def cleanup_frozen_tags!(env: nil)
47
+ if options[:cleanup_frozen_tags]
48
+ gsub_file! "Gemfile", /[^\n]*#{frozen_start_tag(env:)}[^\n]*\n/, ""
49
+ gsub_file! "Gemfile", /[^\n]*#{frozen_end_tag(env:)}[^\n]*\n/, ""
50
+ end
51
+ end
52
+
53
+ def cleanup_all_frozen_tags!
54
+ cleanup_frozen_tags!
55
+ cleanup_frozen_tags!(env: "local")
56
+ cleanup_frozen_tags!(env: "development")
57
+ end
58
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FrozenRails # :nodoc:
4
- VERSION = "0.0.2"
4
+ VERSION = "0.0.4"
5
5
  end
data/lib/frozen_rails.rb CHANGED
@@ -1,4 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "frozen_rails/version"
4
- require "frozen_rails/railtie" if defined?(Rails::Railtie)
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "frozen_rails/generator"
4
+
5
+ module Frozen
6
+ module Generators
7
+ class DbGenerator < FrozenRails::Generator
8
+ source_root File.expand_path("templates/db", __dir__)
9
+
10
+ desc "Prepare a Rails app for static_db with SQLite UUIDs, Avo and FriendlyId"
11
+
12
+ def add_gems
13
+ add_frozen_gems <<~RUBY
14
+ # frozen:db
15
+ gem "static_db"
16
+ gem "sqlite_extensions-uuid"
17
+ gem "friendly_id"
18
+ RUBY
19
+
20
+ add_frozen_gems <<~RUBY, env: "development"
21
+ # frozen:db
22
+ gem "avo", ">= 3.2"
23
+ RUBY
24
+ end
25
+
26
+ def bundle_gems
27
+ bundle!
28
+ end
29
+
30
+ def add_routes
31
+ append_to_routes <<~RUBY
32
+ # frozen:db
33
+ if Rails.env.development?
34
+ mount_avo at: "/avo"
35
+ end
36
+ RUBY
37
+ end
38
+
39
+ def copy_files
40
+ copy_file "database.yml", "config/database.yml", force: true
41
+ copy_directory "initializers", "config/initializers"
42
+ copy_directory "lib", "lib"
43
+ end
44
+
45
+ def configure_application
46
+ gsub_file "config/application.rb", /(config\.autoload_lib\(ignore: %w\[).*(\]\))/, '\1assets generators tasks templates\2'
47
+
48
+ append_to_application_config <<~RUBY
49
+ # frozen:db
50
+ config.generators do |g|
51
+ g.orm :active_record, primary_key_type: :uuid
52
+ g.test_framework nil
53
+ g.helper nil
54
+ end
55
+ config.active_storage.draw_routes = true
56
+ RUBY
57
+ end
58
+
59
+ def prepare_db
60
+ rails_command "db:create"
61
+ rails_command "db:schema:load"
62
+ end
63
+
64
+ def setup_active_storage
65
+ rails_command "active_storage:install"
66
+
67
+ active_storage_migration = in_root { Dir.glob("db/migrate/*.active_storage.rb").first }
68
+
69
+ gsub_file active_storage_migration, /# Use.*\n.*primary_and_foreign_key_types/, <<~RUBY.chomp.lines.map(&:chomp).join("\n ")
70
+ # Use custom primary and foreign key types to support SQLite UUIDs.
71
+ primary_key_type = { id: :string, default: -> { "uuid()" }, limit: 36 }
72
+ foreign_key_type = { type: :string, limit: 36 }
73
+ RUBY
74
+ gsub_file active_storage_migration, "id: primary_key_type", "**primary_key_type"
75
+ gsub_file active_storage_migration, "type: foreign_key_type", "**foreign_key_type"
76
+ gsub_file active_storage_migration, /\n\s*private.*end(?=\nend)/m, ""
77
+ end
78
+
79
+ def setup_friendly_id
80
+ rails_command "g migration create_friendly_id_slugs"
81
+
82
+ friendly_id_migration = in_root { Dir.glob("db/migrate/*create_friendly_id_slugs.rb").first }
83
+
84
+ gsub_file friendly_id_migration, /create_table.*?end(?=\n)/m, <<~RUBY.chomp.lines.map(&:chomp).join("\n ")
85
+ create_table :friendly_id_slugs, id: :string, default: -> { "uuid()" }, limit: 36 do |t|
86
+ t.string :slug, null: false
87
+ t.string :sluggable_id, limit: 36, null: false
88
+ t.string :sluggable_type, limit: 50
89
+ t.string :scope
90
+ t.datetime :created_at
91
+ end
92
+ add_index :friendly_id_slugs, [ :sluggable_type, :sluggable_id ]
93
+ add_index :friendly_id_slugs, [ :slug, :sluggable_type ], length: { slug: 140, sluggable_type: 50 }
94
+ add_index :friendly_id_slugs, [ :slug, :sluggable_type, :scope ], length: { slug: 70, sluggable_type: 50, scope: 70 }, unique: true
95
+ RUBY
96
+ end
97
+
98
+ def setup_avo
99
+ rails_command "g avo:install"
100
+
101
+ prepend_to_file "config/initializers/avo.rb", <<~RUBY + "\n"
102
+ if Rails.env.development?
103
+ # Allow moving Avo controllers into `app/avo/controllers/`
104
+ Rails.autoloaders.main.collapse(
105
+ Rails.root.join("app/avo/controllers")
106
+ )
107
+ else
108
+ # Don't load Avo
109
+ Rails.autoloaders.main.ignore(
110
+ Rails.root.join("app/avo"),
111
+ Rails.root.join("app/controllers/avo")
112
+ )
113
+
114
+ # Don't configure Avo
115
+ return
116
+ end
117
+ RUBY
118
+
119
+ run "b rubocop -a config/initializers/avo.rb"
120
+ end
121
+
122
+ def migrate_and_cleanup_db
123
+ rails_command "db:migrate"
124
+ rails_command "db:drop"
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "frozen_rails/generator"
4
+
5
+ module Frozen
6
+ module Generators
7
+ class MdGenerator < FrozenRails::Generator
8
+ source_root File.expand_path("templates/md", __dir__)
9
+
10
+ # When absent, the generator will prompt interactively (unless running non-interactively).
11
+ class_option :rouge_theme, type: :string, desc: "Rouge theme to install (runs non-interactive if provided)"
12
+
13
+ desc "Set up markdown-powered content with Decant, kramdown, ERB processing and Rouge highlighting"
14
+
15
+ def add_gems
16
+ add_frozen_gems <<~RUBY
17
+ # frozen:md
18
+ gem "decant"
19
+ gem "kramdown"
20
+ gem "kramdown-parser-gfm"
21
+ gem "rouge"
22
+ RUBY
23
+ end
24
+
25
+ def bundle_gems
26
+ bundle!
27
+ end
28
+
29
+ def copy_files
30
+ copy_directory "controllers", "app/controllers"
31
+ copy_directory "helpers", "app/helpers"
32
+ copy_directory "models", "app/models"
33
+ copy_directory "views", "app/views"
34
+ copy_directory "initializers", "config/initializers"
35
+ copy_directory "pages", "content/pages"
36
+ end
37
+
38
+ def add_config
39
+ append_to_application_config <<~RUBY
40
+ # frozen:md
41
+ config.assets.paths << Rails.root.join("content")
42
+ config.action_dispatch.rescue_responses["Decant::FileNotFound"] = :not_found
43
+ RUBY
44
+ end
45
+
46
+ def add_routes
47
+ append_to_routes <<~RUBY
48
+ root "categories#index"
49
+
50
+ # frozen:md
51
+ constraints slug: Regexp.union(Category.all.map(&:slug)) do
52
+ resources :categories, param: :slug, only: [ :index, :show ]
53
+ end
54
+ resources :pages, param: :slug, only: [ :show ]
55
+ RUBY
56
+ end
57
+
58
+ def generate_rouge_css_stylesheet
59
+ theme = options[:rouge_theme]
60
+
61
+ if theme.blank? && behavior == :invoke && $stdin.tty?
62
+ # Run in subshell so we pick up newly installed gem without needing to require it in the current process.
63
+ themes = `bundle exec ruby -e "require 'rouge'; puts Rouge::Theme.registry.keys.sort"`.split("\n")
64
+
65
+ say_status :info, "Available Rouge themes (#{themes.size}):", :blue
66
+ themes.each_with_index { |t, i| say " #{i + 1}. #{t}" }
67
+
68
+ answer = ask("Choose a theme (name or number) [github]")
69
+
70
+ theme = if /^\d+$/.match?(answer)
71
+ themes[answer.to_i - 1]
72
+ else
73
+ answer.presence
74
+ end
75
+ end
76
+
77
+ theme ||= "github"
78
+
79
+ say_status :info, "Generating Rouge CSS for theme #{theme}", :blue
80
+ run "rougify style #{theme} > app/assets/stylesheets/rouge.css"
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "frozen_rails/taggable"
5
+
6
+ module Frozen
7
+ module Generators
8
+ # A meta-generator that runs the other frozen generators in a sensible order.
9
+ class RailsGenerator < Rails::Generators::Base
10
+ include Taggable
11
+
12
+ desc "Run all frozen setup generators (md, db, seo, ui, dx, ssg)"
13
+
14
+ # The method name doesn't matter much; Thor runs all public methods in
15
+ # order defined by source.
16
+ def run_all
17
+ say_status :info, "invoking frozen:md"
18
+ invoke "frozen:md", [], cleanup_frozen_tags: false
19
+
20
+ say_status :info, "invoking frozen:db"
21
+ invoke "frozen:db", [], cleanup_frozen_tags: false
22
+
23
+ say_status :info, "invoking frozen:seo"
24
+ invoke "frozen:seo", [], cleanup_frozen_tags: false
25
+
26
+ say_status :info, "invoking frozen:ui"
27
+ invoke "frozen:ui", [], cleanup_frozen_tags: false
28
+
29
+ say_status :info, "invoking frozen:dx"
30
+ invoke "frozen:dx", [], cleanup_frozen_tags: false
31
+
32
+ say_status :info, "invoking frozen:ssg"
33
+ invoke "frozen:ssg", [], cleanup_frozen_tags: false
34
+
35
+ cleanup_all_frozen_tags!
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "frozen_rails/generator"
4
+
5
+ module Frozen
6
+ module Generators
7
+ class SeoGenerator < FrozenRails::Generator
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ desc "Add SEO helpers, metadata partials and sitemap/robots models"
11
+
12
+ # add meta partial and update layout
13
+ def create_og_partial
14
+ template "seo/_og_meta_tags.html.erb", "app/views/layouts/_og_meta_tags.html.erb"
15
+ end
16
+
17
+ def inject_layout_tags
18
+ return unless File.exist?("app/views/layouts/application.html.erb")
19
+
20
+ inject_into_file "app/views/layouts/application.html.erb",
21
+ after: "<head>\n" do
22
+ <<~ERB
23
+ <%= render 'layouts/og_meta_tags', tags: {
24
+ title: title,
25
+ description: description,
26
+ image: image_url('og-image.jpg'),
27
+ author: author,
28
+ type: "website",
29
+ site_name: site_name,
30
+ url: canonical_url,
31
+ } %>
32
+
33
+ <%= tag :link, rel: "canonical", href: canonical_url %>
34
+
35
+ <%= favicon_link_tag 'favicon.ico', sizes: '32x32' %>
36
+ <%= favicon_link_tag 'icon.svg', type: 'image/svg+xml' %>
37
+ <%= favicon_link_tag 'apple-touch-icon.png', rel: 'apple-touch-icon' %>
38
+
39
+ <%= tag :meta, name: "robots", content: robots_content %>
40
+
41
+ ERB
42
+ end
43
+ end
44
+
45
+ # helpers for meta tags
46
+ def add_application_helpers
47
+ unless File.exist?("app/helpers/application_helper.rb")
48
+ create_file "app/helpers/application_helper.rb", "module ApplicationHelper\nend\n"
49
+ end
50
+
51
+ inject_into_file "app/helpers/application_helper.rb",
52
+ after: "module ApplicationHelper\n" do
53
+ <<~RUBY
54
+ def site_name
55
+ "Rails Static"
56
+ end
57
+
58
+ def title
59
+ [ @page&.title.presence, site_name ].uniq.compact.join(" · ")
60
+ end
61
+
62
+ def description
63
+ @page.frontmatter&.dig(:description)
64
+ end
65
+
66
+ def author
67
+ "François Catuhe"
68
+ end
69
+
70
+ def canonical_url
71
+ url_for(only_path: false)
72
+ end
73
+
74
+ def robots_content
75
+ Rails.env.production? ? "index, follow" : "noindex, nofollow"
76
+ end
77
+
78
+ def og_meta_tag(key, content)
79
+ case key
80
+ when :title, :description, :image
81
+ tag(:meta, name: key, property: "og:\#{key}", content: content)
82
+ when :author
83
+ tag(:meta, name: key, content: content)
84
+ else
85
+ tag(:meta, property: "og:\#{key}", content: content)
86
+ end
87
+ end
88
+
89
+ RUBY
90
+ end
91
+ end
92
+
93
+ def add_favicon_notice
94
+ say_status :info, "Add favicons (favicon.ico, icon.svg, apple-touch-icon.png) to app/assets/images and create og-image.jpg"
95
+ end
96
+
97
+ # sitemap models
98
+ def create_sitemap_models
99
+ template "seo/sitemap.rb", "app/models/sitemap.rb"
100
+ template "seo/sitemap_entry.rb", "app/models/sitemap/entry.rb"
101
+ template "seo/robots_generatable.rb", "app/models/sitemap/robots_generatable.rb"
102
+ end
103
+
104
+ def remove_default_robots
105
+ remove_file "public/robots.txt" if File.exist?("public/robots.txt")
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "frozen_rails/generator"
4
+
5
+ module Frozen
6
+ module Generators
7
+ class SsgGenerator < FrozenRails::Generator
8
+ source_root File.expand_path("templates/ssg", __dir__)
9
+
10
+ desc "Set up Parklife static site generation with CI workflows and helper scripts"
11
+
12
+ def add_gems
13
+ add_frozen_gems <<~RUBY
14
+ # frozen:ssg
15
+ gem "parklife-rails"
16
+ RUBY
17
+ end
18
+
19
+ def bundle_gems
20
+ bundle!
21
+ end
22
+
23
+ def run_parklife_init
24
+ run "bundle exec parklife init --rails --github-pages"
25
+ append_to_file ".gitignore", "# frozen:ssg\n/build\n"
26
+ end
27
+
28
+ def setup_gitlab_pages
29
+ copy_file ".gitlab-ci.yml"
30
+ comment_lines "Parkfile", /config\.nested_index = false/
31
+ end
32
+
33
+ def configure_parklife_active_storage_integration
34
+ copy_file "storage.yml", "config/storage.yml", force: true
35
+
36
+ uncomment_lines "config/environments/production.rb", /config\.assume_ssl = true/
37
+ gsub_file "config/environments/production.rb", /config\.active_storage\.service = :\w+/, "config.active_storage.service = :parklife"
38
+
39
+ insert_into_file "config/application.rb", "\n" + <<~RUBY, before: /(\n#.*)?\n#.*\nBundler\.require/
40
+ # frozen:ssg
41
+ if ARGV.first == "build"
42
+ require "parklife-rails/activestorage"
43
+ end
44
+ RUBY
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,18 @@
1
+ default: &default
2
+ adapter: sqlite3
3
+ extensions:
4
+ - <%= SqliteExtensions::UUID.to_path %>
5
+ max_connections: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
6
+ timeout: 5000
7
+
8
+ development:
9
+ <<: *default
10
+ database: storage/development.sqlite3
11
+
12
+ test:
13
+ <<: *default
14
+ database: storage/test.sqlite3
15
+
16
+ production:
17
+ <<: *default
18
+ database: storage/production.sqlite3
@@ -0,0 +1,13 @@
1
+ FriendlyId.defaults do |config|
2
+ config.use :slugged
3
+ config.use :finders
4
+ config.use :history
5
+ config.use :reserved
6
+
7
+ config.reserved_words = %w[
8
+ new edit index
9
+ session login logout users admin
10
+ stylesheets assets javascripts images
11
+ avo pages
12
+ ]
13
+ end