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.
- checksums.yaml +4 -4
- data/README.md +79 -2
- data/lib/frozen_rails/appendable.rb +13 -0
- data/lib/frozen_rails/bundleable.rb +11 -0
- data/lib/frozen_rails/copyable.rb +14 -0
- data/lib/frozen_rails/generator.rb +16 -0
- data/lib/frozen_rails/taggable.rb +58 -0
- data/lib/frozen_rails/version.rb +1 -1
- data/lib/frozen_rails.rb +0 -1
- data/lib/generators/frozen/db_generator.rb +128 -0
- data/lib/generators/frozen/md_generator.rb +84 -0
- data/lib/generators/frozen/rails_generator.rb +39 -0
- data/lib/generators/frozen/seo_generator.rb +109 -0
- data/lib/generators/frozen/ssg_generator.rb +48 -0
- data/lib/generators/frozen/templates/db/database.yml +18 -0
- data/lib/generators/frozen/templates/db/initializers/friendly_id.rb +13 -0
- data/lib/generators/frozen/templates/db/initializers/readonly_routes.rb +14 -0
- data/lib/generators/frozen/templates/db/initializers/sqlite_uuid.rb +25 -0
- data/lib/generators/frozen/templates/db/initializers/static_db.rb +3 -0
- data/lib/generators/frozen/templates/db/lib/generators/avo/controller/controller_generator.rb +13 -0
- data/lib/generators/frozen/templates/db/lib/generators/avo/install/install_generator.rb +11 -0
- data/lib/generators/frozen/templates/db/lib/generators/avo/resource/resource_generator.rb +30 -0
- data/lib/generators/frozen/templates/db/lib/templates/active_record/model/model.rb.tt +26 -0
- data/lib/generators/frozen/templates/db/lib/templates/erb/scaffold/index.html.erb.tt +14 -0
- data/lib/generators/frozen/templates/db/lib/templates/erb/scaffold/partial.html.erb.tt +17 -0
- data/lib/generators/frozen/templates/db/lib/templates/erb/scaffold/show.html.erb.tt +10 -0
- data/lib/generators/frozen/templates/db/lib/templates/rails/scaffold_controller/controller.rb.tt +11 -0
- data/lib/generators/frozen/templates/md/controllers/categories_controller.rb +9 -0
- data/lib/generators/frozen/templates/md/controllers/pages_controller.rb +5 -0
- data/lib/generators/frozen/templates/md/helpers/markdown_helper.rb +14 -0
- data/lib/generators/frozen/templates/md/initializers/decant_extensions.rb +15 -0
- data/lib/generators/frozen/templates/md/models/category.rb +35 -0
- data/lib/generators/frozen/templates/md/models/concerns/linkable.rb +41 -0
- data/lib/generators/frozen/templates/md/models/page.rb +13 -0
- data/lib/generators/frozen/templates/md/pages/frozen-rails.md +12 -0
- data/lib/generators/frozen/templates/md/pages/rails-static/rails-static-logo.webp +0 -0
- data/lib/generators/frozen/templates/md/pages/rails-static.md +17 -0
- data/lib/generators/frozen/templates/md/views/categories/index.html.erb +5 -0
- data/lib/generators/frozen/templates/md/views/categories/show.html.erb +13 -0
- data/lib/generators/frozen/templates/md/views/pages/show.html.erb +5 -0
- data/lib/generators/frozen/templates/seo/_og_meta_tags.html.erb +3 -0
- data/lib/generators/frozen/templates/seo/robots_generatable.rb +14 -0
- data/lib/generators/frozen/templates/seo/sitemap.rb +15 -0
- data/lib/generators/frozen/templates/seo/sitemap_entry.rb +12 -0
- data/lib/generators/frozen/templates/ssg/storage.yml +12 -0
- data/lib/generators/frozen/templates/ui/cable.yml +5 -0
- data/lib/generators/frozen/templates/ui/hotkey_controller.js +9 -0
- data/lib/generators/frozen/ui_generator.rb +81 -0
- metadata +53 -52
- data/lib/frozen_rails/railtie.rb +0 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0d1e4038850bfbf0972c64d50c2de0ef854c1165cbef4f6fba5c63cc963d5e39
|
|
4
|
+
data.tar.gz: 47a78650b8de3d17aa463157c78c1242789389e4b0df03b56eeef9057efe9fbd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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
|
-
|
|
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,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
|
data/lib/frozen_rails/version.rb
CHANGED
data/lib/frozen_rails.rb
CHANGED
|
@@ -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
|