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
@@ -0,0 +1,14 @@
1
+ ActiveSupport.on_load(:active_record) do
2
+ require "rails/generators"
3
+ require "rails/generators/erb/scaffold/scaffold_generator"
4
+
5
+ Erb::Generators::ScaffoldGenerator.prepend(
6
+ Module.new do
7
+ private
8
+
9
+ def available_views
10
+ [ "index", "show" ]
11
+ end
12
+ end
13
+ )
14
+ end
@@ -0,0 +1,25 @@
1
+ ActiveSupport.on_load(:active_record) do
2
+ require "rails/generators"
3
+ require "rails/generators/active_record/model/model_generator"
4
+ require "rails/generators/active_record/migration/migration_generator"
5
+
6
+ ActiveRecord::Generators::ModelGenerator.prepend(
7
+ Module.new do
8
+ private
9
+
10
+ def primary_key_type
11
+ ', id: :string, default: -> { "uuid()" }, limit: 36'
12
+ end
13
+ end
14
+ )
15
+
16
+ ActiveRecord::Generators::MigrationGenerator.prepend(
17
+ Module.new do
18
+ private
19
+
20
+ def primary_key_type
21
+ ', id: :string, default: -> { "uuid()" }, limit: 36'
22
+ end
23
+ end
24
+ )
25
+ end
@@ -0,0 +1,3 @@
1
+ StaticDb.configure do |config|
2
+ config.fixture_path = Rails.root.join("content", "data")
3
+ end
@@ -0,0 +1,13 @@
1
+ require "generators/avo/controller_generator"
2
+
3
+ module Generators
4
+ module Avo
5
+ class ControllerGenerator
6
+ def create
7
+ return if override_controller?
8
+
9
+ template "resource/controller.tt", "app/avo/controllers/#{controller_name}.rb"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ require "generators/avo/install_generator"
2
+
3
+ module Generators
4
+ module Avo
5
+ class InstallGenerator
6
+ def create_initializer_file
7
+ template "initializer/avo.tt", "config/initializers/avo.rb"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ require "generators/avo/resource_generator"
2
+
3
+ module Generators
4
+ module Avo
5
+ class ResourceGenerator
6
+ no_tasks do
7
+ def field_string(name, type, options)
8
+ return "field :slug, as: :id, format_using: -> { link_to value, main_app.polymorphic_path(record) }" if name.to_sym == :slug
9
+ "field :#{name}, as: :#{type}#{options}"
10
+ end
11
+
12
+ def generate_fields_from_args
13
+ @args.each do |arg|
14
+ name, type = arg.split(":")
15
+ type = "string" if type.blank?
16
+ fields[name] = field(name, type.to_sym)
17
+ end
18
+
19
+ ", format_index_using: -> { content_tag(:span, \"#\", title: value) }#{generated_fields_template}"
20
+ end
21
+
22
+ def field(name, type)
23
+ return { field: "id" } if name.to_sym == :id
24
+ return { field: "id" } if name.to_sym == :slug
25
+ ::Avo::Mappings::NAMES_MAPPING[name.to_sym] || ::Avo::Mappings::FIELDS_MAPPING[type&.to_sym] || { field: "text" }
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,26 @@
1
+ <% module_namespacing do -%>
2
+ class <%= class_name %> < <%= parent_class_name.classify %>
3
+ <% if attributes.any? { |a| a.name.to_s == "slug" } -%>
4
+ include FriendlyId
5
+ friendly_id :<%= attributes.reject { |a| a.name.to_s == "slug" }.select { |a| a.type.to_s == "string" }.first.name %>
6
+ <% end -%>
7
+ <% attributes.select(&:reference?).each do |attribute| -%>
8
+ belongs_to :<%= attribute.name %><%= ", polymorphic: true" if attribute.polymorphic? %>
9
+ <% end -%>
10
+ <% attributes.select(&:rich_text?).each do |attribute| -%>
11
+ has_rich_text :<%= attribute.name %>
12
+ <% end -%>
13
+ <% attributes.select(&:attachment?).each do |attribute| -%>
14
+ has_one_attached :<%= attribute.name %>
15
+ <% end -%>
16
+ <% attributes.select(&:attachments?).each do |attribute| -%>
17
+ has_many_attached :<%= attribute.name %>
18
+ <% end -%>
19
+ <% attributes.select(&:token?).each do |attribute| -%>
20
+ has_secure_token<% if attribute.name != "token" %> :<%= attribute.name %><% end %>
21
+ <% end -%>
22
+ <% if attributes.any?(&:password_digest?) -%>
23
+ has_secure_password
24
+ <% end -%>
25
+ end
26
+ <% end -%>
@@ -0,0 +1,14 @@
1
+ <p style="color: green"><%%= notice %></p>
2
+
3
+ <%% content_for :title, "<%= human_name.pluralize %>" %>
4
+
5
+ <h1><%= human_name.pluralize %></h1>
6
+
7
+ <div id="<%= plural_table_name %>">
8
+ <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %>
9
+ <%%= render <%= singular_table_name %> %>
10
+ <p>
11
+ <%%= link_to "Show this <%= human_name.downcase %>", <%= model_resource_name(singular_table_name) %> %>
12
+ </p>
13
+ <%% end %>
14
+ </div>
@@ -0,0 +1,17 @@
1
+ <div id="<%%= dom_id <%= singular_name %> %>">
2
+ <% attributes.reject(&:password_digest?).each do |attribute| -%>
3
+ <p>
4
+ <strong><%= attribute.human_name %>:</strong>
5
+ <% if attribute.attachment? -%>
6
+ <%%= link_to <%= singular_name %>.<%= attribute.column_name %>.filename, <%= singular_name %>.<%= attribute.column_name %> if <%= singular_name %>.<%= attribute.column_name %>.attached? %>
7
+ <% elsif attribute.attachments? -%>
8
+ <%% <%= singular_name %>.<%= attribute.column_name %>.each do |<%= attribute.singular_name %>| %>
9
+ <div><%%= link_to <%= attribute.singular_name %>.filename, <%= attribute.singular_name %> %></div>
10
+ <%% end %>
11
+ <% else -%>
12
+ <%%= <%= singular_name %>.<%= attribute.column_name %> %>
13
+ <% end -%>
14
+ </p>
15
+
16
+ <% end -%>
17
+ </div>
@@ -0,0 +1,10 @@
1
+ <p style="color: green"><%%= notice %></p>
2
+
3
+ <%%= render @<%= singular_table_name %> %>
4
+
5
+ <div>
6
+ <%%= link_to "Back to <%= human_name.pluralize.downcase %>", <%= index_helper(type: :path) %> %>
7
+ <%% if Rails.env.development? %>
8
+ <%%= link_to "Edit", avo.resources_<%= show_helper(type: :path) %> %>
9
+ <%% end %>
10
+ </div>
@@ -0,0 +1,11 @@
1
+ <% module_namespacing do -%>
2
+ class <%= controller_class_name %>Controller < ApplicationController
3
+ def index
4
+ @<%= plural_table_name %> = <%= orm_class.all(class_name) %>
5
+ end
6
+
7
+ def show
8
+ @<%= singular_table_name %> = <%= orm_class.find(class_name, "params.expect(:id)") %>
9
+ end
10
+ end
11
+ <% end -%>
@@ -0,0 +1,9 @@
1
+ class CategoriesController < ApplicationController
2
+ def index
3
+ @categories = Category.all
4
+ end
5
+
6
+ def show
7
+ @category = Category.find(params[:slug])
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ class PagesController < ApplicationController
2
+ def show
3
+ @page = Page.find(params[:slug])
4
+ end
5
+ end
@@ -0,0 +1,14 @@
1
+ module MarkdownHelper
2
+ def render_content_from(page)
3
+ erb_processed_content = render(inline: page.content, layout: false)
4
+ Kramdown::Document.new(
5
+ erb_processed_content,
6
+ input: "GFM",
7
+ syntax_highlighter: :rouge
8
+ ).to_html.html_safe
9
+ end
10
+
11
+ def pages_image_tag(path, **kwargs)
12
+ image_tag "pages/#{@page.slug}/#{path}", **kwargs
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ require "decant/content"
2
+
3
+ module Decant
4
+ class Content
5
+ def self.where(**kwargs)
6
+ scope = all
7
+ kwargs.each do |key, value|
8
+ if value.to_s != "all"
9
+ scope.select! { |page| Array.wrap(page.send(key)).intersect?(Array.wrap(value)) }
10
+ end
11
+ end
12
+ scope
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,35 @@
1
+ # This is a demo how to build categories without tying yourself to the DB.
2
+ # This keeps the "Markdown area" of your app separate from the "DB area".
3
+ # A nicer approach for faster editing via the UI would probably be to
4
+ # introduce "Category" and "CategoryPage" as models.
5
+ Category = Data.define(:slug, :title) do
6
+ include Linkable
7
+
8
+ id_method :slug
9
+ label_method :title
10
+
11
+ def self.all
12
+ [
13
+ new(slug: "intro", title: "How it began"),
14
+ new(slug: "outro", title: "How it ended")
15
+ ]
16
+ end
17
+
18
+ def self.where(**kwargs)
19
+ scope = all
20
+ kwargs.each do |key, value|
21
+ if value.to_s != "all"
22
+ scope.select! { |category| Array.wrap(category.send(key)).intersect?(Array.wrap(value)) }
23
+ end
24
+ end
25
+ scope
26
+ end
27
+
28
+ def self.find(slug)
29
+ Category.where(slug: slug).first or raise ActiveRecord::RecordNotFound
30
+ end
31
+
32
+ def pages
33
+ Page.where(category_slugs: slug)
34
+ end
35
+ end
@@ -0,0 +1,41 @@
1
+ module Linkable
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ extend ActiveModel::Naming
6
+ end
7
+
8
+ class_methods do
9
+ def id_method(name = nil, &block)
10
+ define_method(:to_param) do
11
+ if name
12
+ send(name)
13
+ elsif block_given?
14
+ instance_exec(&block)
15
+ else
16
+ raise "Please provide a method name or block!"
17
+ end
18
+ end
19
+ end
20
+
21
+ def label_method(name = nil, &block)
22
+ define_method(:to_s) do
23
+ if name
24
+ send(name)
25
+ elsif block_given?
26
+ instance_exec(&block)
27
+ else
28
+ raise "Please provide a method name or block!"
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ def to_model
35
+ self
36
+ end
37
+
38
+ def persisted?
39
+ true
40
+ end
41
+ end
@@ -0,0 +1,13 @@
1
+ Page = Decant.define(dir: "content/pages", ext: "md") do
2
+ include Linkable
3
+
4
+ id_method :slug
5
+ label_method { title || slug }
6
+
7
+ frontmatter :title
8
+ frontmatter :category_slugs
9
+
10
+ def categories
11
+ Category.where(slug: category_slugs)
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ ---
2
+ title: "frozen:rails"
3
+ category_slugs: ["outro"]
4
+ ---
5
+ # frozen:rails
6
+
7
+ [frozen_rails](https://github.com/dunkelziffer/frozen_rails) was created by Klaus Weidinger.
8
+
9
+ ```ruby
10
+ Rails.frozen?
11
+ #=> true
12
+ ```
@@ -0,0 +1,17 @@
1
+ ---
2
+ title: rails-static.com
3
+ category_slugs: ["intro"]
4
+ ---
5
+ # Rails Static
6
+
7
+ [rails-static.com](https://rails-static.com) was created by François Catuhe.
8
+
9
+ ```ruby
10
+ const_defined?(:Rails)
11
+ #=> true
12
+ ```
13
+
14
+ You are running in the "<%= Rails.env %>" environment.
15
+ It is now <%= l(Time.current) %>.
16
+
17
+ <%= pages_image_tag('rails-static-logo.webp') %>
@@ -0,0 +1,5 @@
1
+ <nav>
2
+ <% @categories.each do |category| %>
3
+ <%= link_to category %>
4
+ <% end %>
5
+ </nav>
@@ -0,0 +1,13 @@
1
+ <nav>
2
+ <%= link_to "Back to all categories", categories_path %>
3
+ </nav>
4
+
5
+ <h1><%= @category.title %></h1>
6
+
7
+ <ul>
8
+ <% @category.pages.each do |page| %>
9
+ <li>
10
+ <%= link_to page %>
11
+ </li>
12
+ <% end %>
13
+ </ul>
@@ -0,0 +1,5 @@
1
+ <% @page.categories.each do |category| %>
2
+ <%= link_to category %>
3
+ <% end %>
4
+
5
+ <%= render_content_from @page %>
@@ -0,0 +1,3 @@
1
+ <%%- tags.reject { |_key, content| content.blank? }.each do |key, content| %%>
2
+ %%%= og_meta_tag(key, content) %%%>
3
+ <%% end %%>
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # generated by frozen:seo
4
+ module Sitemap
5
+ module RobotsGeneratable
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ # include behavior to build robots.txt entries
10
+ end
11
+
12
+ # TODO: define helper methods for robots generation
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # generated by frozen:seo
4
+ # A placeholder sitemap model; adapt according to your needs.
5
+ # See docs/web-page-metadata.md and docs/sitemap-and-robots.md for guidance.
6
+
7
+ class Sitemap
8
+ include ActiveModel::Model
9
+
10
+ attr_accessor :base_url, :build_dir, :generate_robots
11
+
12
+ validates :base_url, :build_dir, presence: true
13
+
14
+ # TODO: implement generation logic
15
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # generated by frozen:seo
4
+ module Sitemap
5
+ class Entry
6
+ include ActiveModel::Model
7
+
8
+ attr_accessor :url, :lastmod, :changefreq, :priority
9
+
10
+ # TODO: add methods for timestamp determination
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # Intentionally use same storage location for local and parklife!!!
2
+ local: &default
3
+ service: Disk
4
+ root: <%= Rails.root.join("content/storage") %>
5
+
6
+ parklife:
7
+ <<: *default
8
+ service: Parklife
9
+
10
+ test:
11
+ service: Disk
12
+ root: <%= Rails.root.join("tmp/storage") %>
@@ -0,0 +1,5 @@
1
+ # generated by frozen:ui
2
+ # Action Cable configuration used for development and spark
3
+
4
+ development:
5
+ adapter: async
@@ -0,0 +1,9 @@
1
+ // generated by frozen:ui
2
+ import { Controller } from "@hotwired/stimulus"
3
+
4
+ export default class extends Controller {
5
+ click(event) {
6
+ event.preventDefault()
7
+ this.element.click()
8
+ }
9
+ }
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "frozen_rails/generator"
4
+
5
+ module Frozen
6
+ module Generators
7
+ class UiGenerator < FrozenRails::Generator
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ desc "Wire up UI helpers: classless CSS, importmap example, Hotwire Spark, Stimulus controller, and related configuration"
11
+
12
+ def add_gems
13
+ add_frozen_gems <<~RUBY, env: "development"
14
+ # frozen:ui
15
+ gem "hotwire-spark"
16
+ RUBY
17
+ end
18
+
19
+ def bundle_gems
20
+ bundle!
21
+ end
22
+
23
+ def add_water_css
24
+ return unless File.exist?("app/views/layouts/application.html.erb")
25
+
26
+ inject_into_file "app/views/layouts/application.html.erb",
27
+ after: "<head>\n" do
28
+ <<~ERB
29
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/water.css">
30
+
31
+ ERB
32
+ end
33
+ end
34
+
35
+ def ensure_importmap_rb
36
+ if File.exist?("config/importmap.rb")
37
+ append_to_file "config/importmap.rb" do
38
+ <<~RUBY
39
+
40
+ # example pin added by frozen:ui
41
+ pin "canvas-confetti", to: "https://cdn.jsdelivr.net/npm/canvas-confetti@1/dist/confetti.module.mjs"
42
+ RUBY
43
+ end
44
+ else
45
+ create_file "config/importmap.rb", <<~RUBY
46
+ # Pin additional scripts to the importmap here.
47
+ # pin "canvas-confetti", to: "https://cdn.jsdelivr.net/npm/canvas-confetti@1/dist/confetti.module.mjs"
48
+ RUBY
49
+ end
50
+ end
51
+
52
+ def add_stimulus_controller
53
+ template "ui/hotkey_controller.js", "app/javascript/controllers/hotkey_controller.js"
54
+ end
55
+
56
+ def configure_spark
57
+ return unless File.exist?("config/environments/development.rb")
58
+
59
+ inject_into_file "config/environments/development.rb",
60
+ after: "Rails.application.configure do\n" do
61
+ <<~RUBY
62
+ config.hotwire.spark.html_paths += %w[ content ]
63
+ config.hotwire.spark.html_extensions += %w[ md ]
64
+
65
+ RUBY
66
+ end
67
+ end
68
+
69
+ def enable_action_cable
70
+ if File.exist?("config/application.rb")
71
+ inject_into_file "config/application.rb",
72
+ before: "module" do
73
+ "require \"action_cable/engine\"\n"
74
+ end
75
+ end
76
+
77
+ template "ui/cable.yml", "config/cable.yml" unless File.exist?("config/cable.yml")
78
+ end
79
+ end
80
+ end
81
+ end