bunko 0.2.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 (46) hide show
  1. checksums.yaml +7 -0
  2. data/.standard.yml +3 -0
  3. data/CHANGELOG.md +41 -0
  4. data/CLAUDE.md +351 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +641 -0
  7. data/ROADMAP.md +519 -0
  8. data/Rakefile +10 -0
  9. data/lib/bunko/configuration.rb +180 -0
  10. data/lib/bunko/controllers/acts_as.rb +22 -0
  11. data/lib/bunko/controllers/collection.rb +160 -0
  12. data/lib/bunko/controllers.rb +5 -0
  13. data/lib/bunko/models/acts_as.rb +24 -0
  14. data/lib/bunko/models/post_methods/publishable.rb +51 -0
  15. data/lib/bunko/models/post_methods/sluggable.rb +47 -0
  16. data/lib/bunko/models/post_methods/word_countable.rb +76 -0
  17. data/lib/bunko/models/post_methods.rb +75 -0
  18. data/lib/bunko/models/post_type_methods.rb +18 -0
  19. data/lib/bunko/models.rb +6 -0
  20. data/lib/bunko/railtie.rb +22 -0
  21. data/lib/bunko/routing/mapper_methods.rb +103 -0
  22. data/lib/bunko/routing.rb +4 -0
  23. data/lib/bunko/version.rb +5 -0
  24. data/lib/bunko.rb +11 -0
  25. data/lib/tasks/bunko/add.rake +259 -0
  26. data/lib/tasks/bunko/helpers.rb +25 -0
  27. data/lib/tasks/bunko/install.rake +125 -0
  28. data/lib/tasks/bunko/sample_data.rake +128 -0
  29. data/lib/tasks/bunko/setup.rake +186 -0
  30. data/lib/tasks/support/sample_data_generator.rb +399 -0
  31. data/lib/tasks/templates/INSTALL.md +62 -0
  32. data/lib/tasks/templates/config/initializers/bunko.rb.tt +45 -0
  33. data/lib/tasks/templates/controllers/controller.rb.tt +25 -0
  34. data/lib/tasks/templates/controllers/pages_controller.rb.tt +29 -0
  35. data/lib/tasks/templates/db/migrate/create_post_types.rb.tt +14 -0
  36. data/lib/tasks/templates/db/migrate/create_posts.rb.tt +31 -0
  37. data/lib/tasks/templates/models/post.rb.tt +8 -0
  38. data/lib/tasks/templates/models/post_type.rb.tt +8 -0
  39. data/lib/tasks/templates/views/collections/index.html.erb.tt +67 -0
  40. data/lib/tasks/templates/views/collections/show.html.erb.tt +39 -0
  41. data/lib/tasks/templates/views/layouts/bunko_footer.html.erb.tt +3 -0
  42. data/lib/tasks/templates/views/layouts/bunko_nav.html.erb.tt +9 -0
  43. data/lib/tasks/templates/views/layouts/bunko_styles.html.erb.tt +3 -0
  44. data/lib/tasks/templates/views/pages/show.html.erb.tt +16 -0
  45. data/sig/bunko.rbs +4 -0
  46. metadata +116 -0
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= controller_name %> < ApplicationController
4
+ bunko_collection :<%= collection_name %>
5
+
6
+ # The bunko_collection method automatically provides index and show actions.
7
+ #
8
+ # To customize, you can override these methods:
9
+ #
10
+ # def index
11
+ # super # calls bunko_collection's index
12
+ # # Add your customizations here
13
+ # end
14
+ #
15
+ # def show
16
+ # super # calls bunko_collection's show
17
+ # # Add your customizations here
18
+ # end
19
+ #
20
+ # Available instance variables in your views:
21
+ # - @posts (index action)
22
+ # - @post (show action)
23
+ # - @collection_name
24
+ # - @pagination
25
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PagesController < ApplicationController
4
+ def show
5
+ # Extract page slug from request path (not user-controllable params)
6
+ # This prevents path traversal attacks via query string manipulation
7
+ # e.g., GET /about?page=../../admin/users
8
+ page_slug = request.path.split('/').reject(&:empty?).last
9
+
10
+ @post = Post.published.find_by(
11
+ post_type: PostType.find_by(name: "pages"),
12
+ slug: page_slug
13
+ )
14
+
15
+ unless @post
16
+ raise ActiveRecord::RecordNotFound, "Page not found"
17
+ end
18
+
19
+ # Check if a custom view exists for this page
20
+ # Use DB-validated slug to prevent rendering arbitrary templates
21
+ # e.g., app/views/pages/about.html.erb
22
+ if template_exists?(@post.slug, "pages")
23
+ render @post.slug
24
+ else
25
+ # Otherwise render the default show.html.erb
26
+ render :show
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreatePostTypes < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
4
+ def change
5
+ create_table :post_types do |t|
6
+ t.string :name, null: false # Identifier (e.g., "blog", "case_studies")
7
+ t.string :title, null: false # Display name (e.g., "Blog", "Case Studies")
8
+
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :post_types, :name, unique: true
13
+ end
14
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreatePosts < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
4
+ def change
5
+ create_table :posts do |t|
6
+ t.string :title, null: false
7
+ t.string :slug, null: false
8
+ <% if use_json_content? -%>
9
+ t.jsonb :content
10
+ <% else -%>
11
+ t.text :content
12
+ <% end -%>
13
+ t.references :post_type, null: false, foreign_key: true
14
+ t.string :status, null: false, default: "draft"
15
+ t.datetime :published_at
16
+ <% if include_seo_fields? -%>
17
+
18
+ # SEO fields
19
+ t.string :title_tag
20
+ t.text :meta_description
21
+ <% end -%>
22
+
23
+ # Metrics
24
+ t.integer :word_count
25
+
26
+ t.timestamps
27
+ end
28
+
29
+ add_index :posts, [:post_type_id, :slug], unique: true
30
+ end
31
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Post < ApplicationRecord
4
+ acts_as_bunko_post
5
+
6
+ # All standard Bunko behavior (associations, validations, scopes) is included via acts_as_bunko_post
7
+ # Add custom validations, associations, or methods below as needed
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PostType < ApplicationRecord
4
+ acts_as_bunko_post_type
5
+
6
+ # All standard Bunko behavior (associations, validations) is included via acts_as_bunko_post_type
7
+ # Add custom validations, associations, or methods below as needed
8
+ end
@@ -0,0 +1,67 @@
1
+ <%%= render "shared/bunko_styles" %>
2
+ <%%= render "shared/bunko_nav" %>
3
+
4
+ <main class="container <%= collection_name %>-index">
5
+ <h1><%= collection_title %></h1>
6
+
7
+ <%% if @posts.any? %>
8
+ <div class="posts">
9
+ <%% @posts.each do |post| %>
10
+ <article class="post-preview">
11
+ <h2>
12
+ <% if is_collection %>
13
+ <%# Collections link to canonical PostType URLs %>
14
+ <%%= link_to post.title, send("#{post.post_type.name.singularize}_path", post.slug) %>
15
+ <% else %>
16
+ <%# PostTypes link to their own show page %>
17
+ <%%= link_to post.title, <%= path_helper %>(post.slug) %>
18
+ <% end %>
19
+ </h2>
20
+
21
+ <div class="post-meta">
22
+ <strong>
23
+ <%% if post.published_at %>
24
+ <time datetime="<%%= post.published_at.iso8601 %>">
25
+ <%%= post.published_at.strftime("%B %d, %Y") %>
26
+ </time>
27
+ <%% end %>
28
+
29
+ <%% if post.reading_time %>
30
+ <span>&nbsp;•&nbsp;</span>
31
+ <span class="reading-time">
32
+ <%%= post.reading_time %> min read
33
+ </span>
34
+ <%% end %>
35
+ </strong>
36
+ </div>
37
+
38
+ <%% if post.content.present? %>
39
+ <div class="post-excerpt">
40
+ <%%= post.excerpt(length: 200) %>
41
+ </div>
42
+ <%% end %>
43
+ </article>
44
+ <%% end %>
45
+ </div>
46
+
47
+ <%% if @pagination[:total_pages] > 1 %>
48
+ <section class="pagination" style=display: flex; justify-content: space-between;">
49
+ <%% if @pagination[:prev_page] %>
50
+ <%%= link_to "← Previous", <%= index_path_helper %>(page: @pagination[:current_page] - 1), class: "pagination-prev" %>
51
+ <%% end %>
52
+
53
+ <span class="pagination-info">
54
+ Page <%%= @pagination[:current_page] %> of <%%= @pagination[:total_pages] %>
55
+ </span>
56
+
57
+ <%% if @pagination[:next_page] %>
58
+ <%%= link_to "Next →", <%= index_path_helper %>(page: @pagination[:current_page] + 1), class: "pagination-next" %>
59
+ <%% end %>
60
+ </section>
61
+ <%% end %>
62
+ <%% else %>
63
+ <p class="no-posts">No <%= collection_title.downcase %> posts yet.</p>
64
+ <%% end %>
65
+ </main>
66
+
67
+ <%%= render "shared/bunko_footer" %>
@@ -0,0 +1,39 @@
1
+ <%%= render "shared/bunko_styles" %>
2
+ <%%= render "shared/bunko_nav" %>
3
+
4
+ <main class="container">
5
+ <article class="post">
6
+ <header class="post-header">
7
+ <h1><%%= @post.title %></h1>
8
+
9
+ <div class="post-meta">
10
+ <%% if @post.published_at %>
11
+ <time datetime="<%%= @post.published_at.iso8601 %>">
12
+ <%%= @post.published_at.strftime("%B %d, %Y") %>
13
+ </time>
14
+ <%% end %>
15
+
16
+ <%% if @post.reading_time %>
17
+ <span>&nbsp;•&nbsp;</span>
18
+ <span class="reading-time">
19
+ <%%= @post.reading_time %> min read
20
+ </span>
21
+ <%% end %>
22
+ </div>
23
+ </header>
24
+
25
+ <div class="post-content">
26
+ <% if format == "html" -%>
27
+ <%%= sanitize(@post.content) %>
28
+ <% else -%>
29
+ <%%= simple_format(@post.content) %>
30
+ <% end -%>
31
+ </div>
32
+
33
+ <footer class="post-footer">
34
+ <%%= link_to "← Back to <%= collection_title %>", <%= index_path_helper %>, class: "back-link" %>
35
+ </footer>
36
+ </article>
37
+ </main>
38
+
39
+ <%%= render "shared/bunko_footer" %>
@@ -0,0 +1,3 @@
1
+ <footer class="container">
2
+ <small>© <%%= Date.current.year %> • Built with <a href="https://github.com/kanejamison/bunko">Bunko</a></small>
3
+ </footer>
@@ -0,0 +1,9 @@
1
+ <header class="container">
2
+ <nav style="gap: 1rem;">
3
+ <%%= link_to "Home", root_path %>
4
+
5
+ <div style="display: flex; gap: 1rem; flex-wrap: wrap; justify-content: end;">
6
+ <%%# bunko_collection_links - additional collections will be added here unless you delete this line %>
7
+ </div>
8
+ </nav>
9
+ </header>
@@ -0,0 +1,3 @@
1
+ <%%# Bunko ships our basic view files with https://picocss.com/ to give you a basic styled experience. %>
2
+ <%%# Feel free to delete this partial and references as you add your own design. %>
3
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
@@ -0,0 +1,16 @@
1
+ <%%= render "shared/bunko_styles" %>
2
+ <%%= render "shared/bunko_nav" %>
3
+
4
+ <main class="container">
5
+ <article class="page">
6
+ <header class="page-header">
7
+ <h1><%%= @post.title %></h1>
8
+ </header>
9
+
10
+ <div class="page-content">
11
+ <%%= sanitize(@post.content) %>
12
+ </div>
13
+ </article>
14
+ </main>
15
+
16
+ <%%= render "shared/bunko_footer" %>
data/sig/bunko.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Bunko
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bunko
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Kane Jamison
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rails
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '8.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '8.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: sqlite3
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.0'
40
+ description: Bunko provides a lightweight CMS structure for Rails based on a 'one
41
+ model, infinite collections' philosophy. Create multiple content collections (blog,
42
+ docs, changelog) without duplicate code or migrations.
43
+ email:
44
+ - 918780+kanejamison@users.noreply.github.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".standard.yml"
50
+ - CHANGELOG.md
51
+ - CLAUDE.md
52
+ - LICENSE.txt
53
+ - README.md
54
+ - ROADMAP.md
55
+ - Rakefile
56
+ - lib/bunko.rb
57
+ - lib/bunko/configuration.rb
58
+ - lib/bunko/controllers.rb
59
+ - lib/bunko/controllers/acts_as.rb
60
+ - lib/bunko/controllers/collection.rb
61
+ - lib/bunko/models.rb
62
+ - lib/bunko/models/acts_as.rb
63
+ - lib/bunko/models/post_methods.rb
64
+ - lib/bunko/models/post_methods/publishable.rb
65
+ - lib/bunko/models/post_methods/sluggable.rb
66
+ - lib/bunko/models/post_methods/word_countable.rb
67
+ - lib/bunko/models/post_type_methods.rb
68
+ - lib/bunko/railtie.rb
69
+ - lib/bunko/routing.rb
70
+ - lib/bunko/routing/mapper_methods.rb
71
+ - lib/bunko/version.rb
72
+ - lib/tasks/bunko/add.rake
73
+ - lib/tasks/bunko/helpers.rb
74
+ - lib/tasks/bunko/install.rake
75
+ - lib/tasks/bunko/sample_data.rake
76
+ - lib/tasks/bunko/setup.rake
77
+ - lib/tasks/support/sample_data_generator.rb
78
+ - lib/tasks/templates/INSTALL.md
79
+ - lib/tasks/templates/config/initializers/bunko.rb.tt
80
+ - lib/tasks/templates/controllers/controller.rb.tt
81
+ - lib/tasks/templates/controllers/pages_controller.rb.tt
82
+ - lib/tasks/templates/db/migrate/create_post_types.rb.tt
83
+ - lib/tasks/templates/db/migrate/create_posts.rb.tt
84
+ - lib/tasks/templates/models/post.rb.tt
85
+ - lib/tasks/templates/models/post_type.rb.tt
86
+ - lib/tasks/templates/views/collections/index.html.erb.tt
87
+ - lib/tasks/templates/views/collections/show.html.erb.tt
88
+ - lib/tasks/templates/views/layouts/bunko_footer.html.erb.tt
89
+ - lib/tasks/templates/views/layouts/bunko_nav.html.erb.tt
90
+ - lib/tasks/templates/views/layouts/bunko_styles.html.erb.tt
91
+ - lib/tasks/templates/views/pages/show.html.erb.tt
92
+ - sig/bunko.rbs
93
+ homepage: https://github.com/kanejamison/bunko
94
+ licenses:
95
+ - MIT
96
+ metadata:
97
+ homepage_uri: https://github.com/kanejamison/bunko
98
+ changelog_uri: https://github.com/kanejamison/bunko/blob/main/CHANGELOG.md
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '3.2'
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubygems_version: 3.6.9
114
+ specification_version: 4
115
+ summary: A simple and lightweight content management system for Rails.
116
+ test_files: []