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.
- checksums.yaml +7 -0
- data/.standard.yml +3 -0
- data/CHANGELOG.md +41 -0
- data/CLAUDE.md +351 -0
- data/LICENSE.txt +21 -0
- data/README.md +641 -0
- data/ROADMAP.md +519 -0
- data/Rakefile +10 -0
- data/lib/bunko/configuration.rb +180 -0
- data/lib/bunko/controllers/acts_as.rb +22 -0
- data/lib/bunko/controllers/collection.rb +160 -0
- data/lib/bunko/controllers.rb +5 -0
- data/lib/bunko/models/acts_as.rb +24 -0
- data/lib/bunko/models/post_methods/publishable.rb +51 -0
- data/lib/bunko/models/post_methods/sluggable.rb +47 -0
- data/lib/bunko/models/post_methods/word_countable.rb +76 -0
- data/lib/bunko/models/post_methods.rb +75 -0
- data/lib/bunko/models/post_type_methods.rb +18 -0
- data/lib/bunko/models.rb +6 -0
- data/lib/bunko/railtie.rb +22 -0
- data/lib/bunko/routing/mapper_methods.rb +103 -0
- data/lib/bunko/routing.rb +4 -0
- data/lib/bunko/version.rb +5 -0
- data/lib/bunko.rb +11 -0
- data/lib/tasks/bunko/add.rake +259 -0
- data/lib/tasks/bunko/helpers.rb +25 -0
- data/lib/tasks/bunko/install.rake +125 -0
- data/lib/tasks/bunko/sample_data.rake +128 -0
- data/lib/tasks/bunko/setup.rake +186 -0
- data/lib/tasks/support/sample_data_generator.rb +399 -0
- data/lib/tasks/templates/INSTALL.md +62 -0
- data/lib/tasks/templates/config/initializers/bunko.rb.tt +45 -0
- data/lib/tasks/templates/controllers/controller.rb.tt +25 -0
- data/lib/tasks/templates/controllers/pages_controller.rb.tt +29 -0
- data/lib/tasks/templates/db/migrate/create_post_types.rb.tt +14 -0
- data/lib/tasks/templates/db/migrate/create_posts.rb.tt +31 -0
- data/lib/tasks/templates/models/post.rb.tt +8 -0
- data/lib/tasks/templates/models/post_type.rb.tt +8 -0
- data/lib/tasks/templates/views/collections/index.html.erb.tt +67 -0
- data/lib/tasks/templates/views/collections/show.html.erb.tt +39 -0
- data/lib/tasks/templates/views/layouts/bunko_footer.html.erb.tt +3 -0
- data/lib/tasks/templates/views/layouts/bunko_nav.html.erb.tt +9 -0
- data/lib/tasks/templates/views/layouts/bunko_styles.html.erb.tt +3 -0
- data/lib/tasks/templates/views/pages/show.html.erb.tt +16 -0
- data/sig/bunko.rbs +4 -0
- metadata +116 -0
data/README.md
ADDED
|
@@ -0,0 +1,641 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img width="960" height="239" alt="bunko-repo-header-image" src="https://github.com/user-attachments/assets/537b4a36-3ba4-41f6-9c54-a633117803a8" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
[](https://github.com/kanejamison/bunko/actions/workflows/ci.yml)
|
|
6
|
+
[](https://github.com/standardrb/standard)
|
|
7
|
+
[](https://badge.fury.io/rb/bunko)
|
|
8
|
+
|
|
9
|
+
# Bunko
|
|
10
|
+
|
|
11
|
+
Bunko (文庫) in Japanese means a small personal library or book collection - a perfect name for a Rails gem that organizes your content elegantly.
|
|
12
|
+
|
|
13
|
+
## ⚠️ Development Status
|
|
14
|
+
|
|
15
|
+
**Bunko is currently in active development and not yet ready for production use.** We're building toward a 1.0.0 release with core functionality that we think is safe for production usage. See the roadmap below for progress.
|
|
16
|
+
|
|
17
|
+
## Philosophy
|
|
18
|
+
|
|
19
|
+
**One model, infinite collections.** Bunko gives you a robust CMS structure in 5 minutes. Whether you just want a classic blog, or if you want dozens of post types across your site, Bunko scales to handle dozens of content collections without new database migrations or excessive code duplication every time you launch a new collection. All content are posts - and you can mount collections of posts to whatever routes you like with #index & #show actions. Need standalone pages like About or Contact? Use `bunko_page` for single-page routes without a collection index.
|
|
20
|
+
|
|
21
|
+
## Overview
|
|
22
|
+
|
|
23
|
+
* **Out-of-the-box blog without the bloat** - Install, generate, and publish in under 5 minutes
|
|
24
|
+
* **One Post model, many collections** - Route the same content model to `/blog/`, `/docs/`, `/changelog/`, `/tutorials/` - whatever you need
|
|
25
|
+
* **Database agnostic** - Works fine with SQLite, PostgreSQL, etc.
|
|
26
|
+
* **Editor agnostic** - ActionText, Lexxy, Trix, markdown, plain text - use what works for your team, or hook it up to a tool like Avo.
|
|
27
|
+
* **View layer agnostic** - We provide helpers, you control the HTML. Works just fine with ERB, HAML, Slim, ViewComponent, or Phlex
|
|
28
|
+
* **Zero JavaScript/CSS opinions** - Bring your own Tailwind, Bootstrap, or vanilla styles
|
|
29
|
+
|
|
30
|
+
## Requirements
|
|
31
|
+
|
|
32
|
+
- Ruby >= 3.2.0
|
|
33
|
+
- Rails >= 8.0
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
### 1. Add to Gemfile
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
gem "bunko"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
bundle install
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 2. Install Bunko
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
rails bunko:install
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This creates:
|
|
54
|
+
- Database migrations for `post_types` and `posts`
|
|
55
|
+
- `Post` and `PostType` models with `acts_as_bunko_post` and `acts_as_bunko_post_type`
|
|
56
|
+
- `config/initializers/bunko.rb` with starter configuration
|
|
57
|
+
|
|
58
|
+
### 3. Run Migrations
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
rails db:migrate
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 4. (Optional) Customize Collections
|
|
65
|
+
|
|
66
|
+
Edit `config/initializers/bunko.rb` to define your content collections:
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
Bunko.configure do |config|
|
|
70
|
+
config.post_type "blog" # Title auto-generated as "Blog"
|
|
71
|
+
|
|
72
|
+
config.post_type "docs", title: "Documentation" # Param style
|
|
73
|
+
|
|
74
|
+
config.post_type "changelog" do |type| # Block style
|
|
75
|
+
type.title = "Changelog"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 5. Generate Controllers, Views, and Routes
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
rails bunko:setup
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
This generates everything you need for each configured post type and collection:
|
|
87
|
+
- ✅ PostTypes in the database
|
|
88
|
+
- ✅ Controllers (e.g., `BlogController`, `DocsController`)
|
|
89
|
+
- ✅ View templates (`app/views/blog/index.html.erb`, `app/views/blog/show.html.erb`)
|
|
90
|
+
- ✅ Routes (`bunko_collection :blog`, `bunko_collection :docs`)
|
|
91
|
+
- ✅ Navigation partial with all collections
|
|
92
|
+
- ✅ Static pages support (`PagesController`, `pages` PostType, default template)
|
|
93
|
+
|
|
94
|
+
These are all vanilla Rails assets - you can delete or customize them to fit your needs.
|
|
95
|
+
|
|
96
|
+
**Styling:** Generated views include [Pico CSS](https://picocss.com/) for basic styling. This is purely optional and can be easily removed or replaced with your own CSS framework. To customize or remove it, simply edit `app/views/shared/_bunko_styles.html.erb` or delete it entirely and add your own stylesheets.
|
|
97
|
+
|
|
98
|
+
**That's it for initial setup!** See "Adding New Post Types or Collections" below for how to add more later.
|
|
99
|
+
|
|
100
|
+
### 6. Create Your First Post
|
|
101
|
+
Create a post in CLI or using the sample data generator described below.
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
# In Rails console or your admin interface
|
|
105
|
+
blog_type = PostType.find_by(name: "blog")
|
|
106
|
+
|
|
107
|
+
Post.create!(
|
|
108
|
+
title: "Welcome to Bunko",
|
|
109
|
+
content: "This is your first blog post!",
|
|
110
|
+
post_type: blog_type,
|
|
111
|
+
status: "published",
|
|
112
|
+
published_at: Time.current
|
|
113
|
+
)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
#### or Generate Sample Data
|
|
117
|
+
|
|
118
|
+
Want to see your collections in action? Bunko includes a sample data generator that creates realistic posts for all your configured post types:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# Generate 100 posts per post type (default)
|
|
122
|
+
rails bunko:sample_data
|
|
123
|
+
|
|
124
|
+
# Generate 50 posts per post type
|
|
125
|
+
rails bunko:sample_data COUNT=50
|
|
126
|
+
|
|
127
|
+
# Generate posts with specific word counts
|
|
128
|
+
rails bunko:sample_data MIN_WORDS=500 MAX_WORDS=1500
|
|
129
|
+
|
|
130
|
+
# Clear existing posts first
|
|
131
|
+
rails bunko:sample_data CLEAR=true
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Content Formats:**
|
|
135
|
+
|
|
136
|
+
The generator supports three content formats:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
# HTML (Default) - Semantic HTML with optional CSS classes
|
|
140
|
+
rails bunko:sample_data FORMAT=html
|
|
141
|
+
|
|
142
|
+
# Markdown - Full markdown formatting with bold, italic, lists, links, blockquotes
|
|
143
|
+
rails bunko:sample_data FORMAT=markdown
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**What gets generated:**
|
|
147
|
+
|
|
148
|
+
The sample data generator creates structured content some what randomly. It's a combination of lorem ipsum and random words, plus various formatted content.
|
|
149
|
+
|
|
150
|
+
All posts include:
|
|
151
|
+
- Realistic titles based on post type
|
|
152
|
+
- Unique slugs
|
|
153
|
+
- Meta descriptions
|
|
154
|
+
- Title tags
|
|
155
|
+
- Published dates (90% past, 10% scheduled for future)
|
|
156
|
+
- Realistic lengths and automatic word count calculation
|
|
157
|
+
|
|
158
|
+
**HTML Format Features:**
|
|
159
|
+
|
|
160
|
+
When using `FORMAT=html`, content includes:
|
|
161
|
+
- Semantic tags (`<h2>`, `<p>`, `<blockquote>`, `<ul>`, `<li>`)
|
|
162
|
+
- Random inline formatting (`<strong>`, `<em>`, `<u>`)
|
|
163
|
+
- Optional CSS classes for styling:
|
|
164
|
+
- `class="content-paragraph"` on some paragraphs
|
|
165
|
+
- `class="section-heading"` on some headings
|
|
166
|
+
- `class="content-list"` on some lists
|
|
167
|
+
- `class="content-quote"` on some blockquotes
|
|
168
|
+
- Safe external links (Ruby on Rails, RubyGems, Bunko GitHub)
|
|
169
|
+
|
|
170
|
+
**Markdown Format Features:**
|
|
171
|
+
|
|
172
|
+
When using `FORMAT=markdown`, content includes:
|
|
173
|
+
- Markdown headings (`## Heading`)
|
|
174
|
+
- Bold (`**text**`) and italic (`_text_`) formatting
|
|
175
|
+
- Unordered lists (`- item`)
|
|
176
|
+
- Blockquotes (`> quote`)
|
|
177
|
+
- Links to safe external resources
|
|
178
|
+
|
|
179
|
+
### 7. Visit Your Blog
|
|
180
|
+
|
|
181
|
+
Start your Rails server and visit:
|
|
182
|
+
- `http://localhost:3000/blog` - Blog index
|
|
183
|
+
- `http://localhost:3000/docs` - Documentation index
|
|
184
|
+
- `http://localhost:3000/changelog` - Changelog index
|
|
185
|
+
|
|
186
|
+
### 8. Wait that's it?
|
|
187
|
+
Yes! For now anyways. The following features are planned but we want to keep them un-opinionated in order to play nicely with your existing setup:
|
|
188
|
+
|
|
189
|
+
- Hook up your own editor however you like.
|
|
190
|
+
- Route your admin/editor behind whatever auth you like.
|
|
191
|
+
- Use whatever SEO gem or helper you like.
|
|
192
|
+
- Use whatever sitemap generator you like.
|
|
193
|
+
|
|
194
|
+
We'll continue building new generators and possibly a mountable UI to help with this. For now we're recommending just using an admin tool like Avo with markdown or Rhino editor which gives you solid editing and Active Record integrations.
|
|
195
|
+
|
|
196
|
+
## Adding More Content Types After Setup
|
|
197
|
+
|
|
198
|
+
Need to add a new blog, documentation section, or any content type? Update your initializer and run one command:
|
|
199
|
+
|
|
200
|
+
```ruby
|
|
201
|
+
# config/initializers/bunko.rb
|
|
202
|
+
Bunko.configure do |config|
|
|
203
|
+
config.post_type "blog"
|
|
204
|
+
config.post_type "changelog" # Add this
|
|
205
|
+
end
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
rails bunko:add[changelog]
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Bunko creates the database entry if it's a post_type and generates the controller, views, routes, and updates your navigation automatically. You can delete any of the generated views and replace them with your custom versions used on other collections.
|
|
213
|
+
|
|
214
|
+
## Generator Options
|
|
215
|
+
|
|
216
|
+
Customize the installation to fit your needs:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Exclude SEO fields (title_tag, meta_description)
|
|
220
|
+
SKIP_SEO=true rails bunko:install
|
|
221
|
+
|
|
222
|
+
# Use JSON/JSONB for content field (for JSON-based editors)
|
|
223
|
+
# This creates a JSONB column for Post.content instead of a text column
|
|
224
|
+
JSON_CONTENT=true rails bunko:install
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Available Features
|
|
228
|
+
|
|
229
|
+
### Post Model Scopes
|
|
230
|
+
|
|
231
|
+
```ruby
|
|
232
|
+
Post.published # All published posts with published_at <= now
|
|
233
|
+
Post.draft # All draft posts
|
|
234
|
+
Post.scheduled # Published posts with published_at > now
|
|
235
|
+
Post.by_post_type("blog") # All posts for a specific collection
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Automatic Slug Generation
|
|
239
|
+
Slugs are generated on save if the slug field is empty. Edit it however you like and it will persist even if you change the title.
|
|
240
|
+
|
|
241
|
+
```ruby
|
|
242
|
+
post = Post.new(title: "Hello World!")
|
|
243
|
+
post.save
|
|
244
|
+
post.slug # => "hello-world"
|
|
245
|
+
|
|
246
|
+
# Handles uniqueness within post_type (adds random suffix)
|
|
247
|
+
post2 = Post.new(title: "Hello World!", post_type: blog_type)
|
|
248
|
+
post2.save
|
|
249
|
+
post2.slug # => "hello-world-a1b2c3d4" (8-character random hex)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Publishing Workflow
|
|
253
|
+
|
|
254
|
+
```ruby
|
|
255
|
+
post = Post.create(title: "My Post", status: "draft")
|
|
256
|
+
post.published_at # => nil
|
|
257
|
+
|
|
258
|
+
# Schedule a post for future publication
|
|
259
|
+
post.update(status: "published", published_at: 1.hour.from_now)
|
|
260
|
+
post.scheduled? # => true
|
|
261
|
+
|
|
262
|
+
# Publish immediately (auto-sets published_at)
|
|
263
|
+
post.update(status: "published")
|
|
264
|
+
post.published_at # => automatically set to current time
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Reading Time Calculation
|
|
268
|
+
|
|
269
|
+
```ruby
|
|
270
|
+
post = Post.create(title: "Article", word_count: 500)
|
|
271
|
+
post.reading_time # => 2 (minutes, based on 250 wpm default)
|
|
272
|
+
post.reading_time_text # => "2 min read"
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Post Convenience Methods
|
|
276
|
+
|
|
277
|
+
Bunko provides instance methods on Post for common view patterns:
|
|
278
|
+
|
|
279
|
+
```ruby
|
|
280
|
+
# Content formatting
|
|
281
|
+
post.excerpt # => "This is a preview of the content..."
|
|
282
|
+
post.excerpt(length: 100, omission: "…") # Custom length and omission
|
|
283
|
+
|
|
284
|
+
# Date formatting
|
|
285
|
+
post.published_date # => "November 09, 2025" (locale-aware, :long format)
|
|
286
|
+
post.published_date(:short) # => "Nov 09" (or locale-specific short format)
|
|
287
|
+
|
|
288
|
+
# Reading time
|
|
289
|
+
post.reading_time_text # => "5 min read"
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
**In your views:**
|
|
293
|
+
|
|
294
|
+
```erb
|
|
295
|
+
<!-- Index: loop over posts -->
|
|
296
|
+
<% @posts.each do |post| %>
|
|
297
|
+
<h2><%= link_to post.title, blog_post_path(post) %></h2>
|
|
298
|
+
<p><%= post.published_date %> · <%= post.reading_time_text %></p>
|
|
299
|
+
<p><%= post.excerpt %></p>
|
|
300
|
+
<% end %>
|
|
301
|
+
|
|
302
|
+
<!-- Show: single post -->
|
|
303
|
+
<h1><%= @post.title %></h1>
|
|
304
|
+
<p><%= @post.published_date(:long) %> · <%= @post.reading_time_text %></p>
|
|
305
|
+
<div><%= @post.content %></div>
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Controller Instance Variables
|
|
309
|
+
|
|
310
|
+
When using `bunko_collection`, these instance variables are available in your views:
|
|
311
|
+
|
|
312
|
+
- `@posts` - Collection of posts (index action)
|
|
313
|
+
- `@post` - Single post (show action)
|
|
314
|
+
- `@collection_name` - Name of the collection (e.g., "blog")
|
|
315
|
+
- `@pagination` - Hash with `:page`, `:per_page`, `:total`, `:total_pages`, `:prev_page`, `:next_page`
|
|
316
|
+
|
|
317
|
+
### Routing Helpers
|
|
318
|
+
|
|
319
|
+
Bunko provides a `bunko_collection` DSL method to simplify route definitions:
|
|
320
|
+
|
|
321
|
+
```ruby
|
|
322
|
+
# config/routes.rb
|
|
323
|
+
Rails.application.routes.draw do
|
|
324
|
+
bunko_collection :blog
|
|
325
|
+
# Generates: /blog (index), /blog/:slug (show)
|
|
326
|
+
end
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
**Automatic hyphenation** - Underscored slugs are automatically converted to hyphens in URLs:
|
|
330
|
+
|
|
331
|
+
```ruby
|
|
332
|
+
bunko_collection :case_study
|
|
333
|
+
# Generates: /case-study/:slug (slug stored as :case_study in database)
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
**Custom paths:**
|
|
337
|
+
|
|
338
|
+
```ruby
|
|
339
|
+
bunko_collection :case_study, path: "case-studies"
|
|
340
|
+
# Generates: /case-studies (index), /case-studies/:slug (show)
|
|
341
|
+
# Path helpers: case_studies_path, case_study_path(post)
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
**Custom controllers:**
|
|
345
|
+
|
|
346
|
+
```ruby
|
|
347
|
+
bunko_collection :blog, controller: "articles"
|
|
348
|
+
# Routes to: articles#index, articles#show
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
**Limit actions:**
|
|
352
|
+
|
|
353
|
+
```ruby
|
|
354
|
+
bunko_collection :blog, only: [:index]
|
|
355
|
+
# Only generates index route (no show route)
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### Static Pages
|
|
359
|
+
|
|
360
|
+
Bunko includes built-in support for standalone pages (like About, Contact, Privacy Policy) that don't need a full collection with an index page.
|
|
361
|
+
|
|
362
|
+
**How it works:**
|
|
363
|
+
|
|
364
|
+
Static pages use the same `Post` model as collections, but with a special `"pages"` post_type and a dedicated routing helper.
|
|
365
|
+
|
|
366
|
+
```ruby
|
|
367
|
+
# config/routes.rb
|
|
368
|
+
bunko_page :about # → GET /about
|
|
369
|
+
bunko_page :contact # → GET /contact
|
|
370
|
+
bunko_page :privacy # → GET /privacy
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
All pages route to a shared `PagesController` that's automatically generated during `rails bunko:setup`.
|
|
374
|
+
|
|
375
|
+
**Creating page content:**
|
|
376
|
+
|
|
377
|
+
```ruby
|
|
378
|
+
# In Rails console or your admin interface
|
|
379
|
+
pages_type = PostType.find_by(name: "pages")
|
|
380
|
+
|
|
381
|
+
Post.create!(
|
|
382
|
+
title: "About Us",
|
|
383
|
+
content: "<p>Welcome to our company...</p>",
|
|
384
|
+
post_type: pages_type,
|
|
385
|
+
slug: "about", # Must match the route name
|
|
386
|
+
status: "published"
|
|
387
|
+
)
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
**Custom page templates:**
|
|
391
|
+
|
|
392
|
+
By default, all pages use `app/views/pages/show.html.erb`. For custom layouts, create a view matching the page slug:
|
|
393
|
+
|
|
394
|
+
```erb
|
|
395
|
+
<!-- app/views/pages/about.html.erb -->
|
|
396
|
+
<%= render "shared/bunko_styles" %>
|
|
397
|
+
<%= render "shared/bunko_nav" %>
|
|
398
|
+
|
|
399
|
+
<main class="container">
|
|
400
|
+
<div class="about-hero">
|
|
401
|
+
<h1><%= @post.title %></h1>
|
|
402
|
+
</div>
|
|
403
|
+
|
|
404
|
+
<div class="about-content">
|
|
405
|
+
<%= sanitize @post.content %>
|
|
406
|
+
</div>
|
|
407
|
+
</main>
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
**Routing options:**
|
|
411
|
+
|
|
412
|
+
```ruby
|
|
413
|
+
# Custom path
|
|
414
|
+
bunko_page :about, path: "about-us"
|
|
415
|
+
# Generates: GET /about-us
|
|
416
|
+
|
|
417
|
+
# Custom controller
|
|
418
|
+
bunko_page :contact, controller: "static_pages"
|
|
419
|
+
# Routes to: static_pages#show
|
|
420
|
+
|
|
421
|
+
# Nested pages (works with namespaces)
|
|
422
|
+
namespace :legal do
|
|
423
|
+
bunko_page :privacy # → GET /legal/privacy
|
|
424
|
+
bunko_page :terms # → GET /legal/terms
|
|
425
|
+
end
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
**Disabling static pages:**
|
|
429
|
+
|
|
430
|
+
If you don't need static pages, disable them in your configuration:
|
|
431
|
+
|
|
432
|
+
```ruby
|
|
433
|
+
# config/initializers/bunko.rb
|
|
434
|
+
Bunko.configure do |config|
|
|
435
|
+
config.allow_static_pages = false
|
|
436
|
+
config.post_type "blog"
|
|
437
|
+
end
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
**Note:** The `"pages"` post_type name is reserved for this feature. If you try to create a post_type named "pages", Bunko will raise an error.
|
|
441
|
+
|
|
442
|
+
### Configuration
|
|
443
|
+
|
|
444
|
+
```ruby
|
|
445
|
+
# frozen_string_literal: true
|
|
446
|
+
|
|
447
|
+
Bunko.configure do |config|
|
|
448
|
+
# Define your post types (use lowercase with underscores)
|
|
449
|
+
# These will be created when you run: rails bunko:setup
|
|
450
|
+
config.post_type "blog" # Title will be auto-generated as "Blog"
|
|
451
|
+
|
|
452
|
+
# Want more? Add additional post types:
|
|
453
|
+
config.post_type "docs" do |type|
|
|
454
|
+
type.title = "Documentation" # Custom title (optional)
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
config.post_type "changelog" # Title: "Changelog"
|
|
458
|
+
|
|
459
|
+
config.post_type "case_studies" do |type|
|
|
460
|
+
type.title = "Case Studies" # Custom title
|
|
461
|
+
end
|
|
462
|
+
#
|
|
463
|
+
# Note: Names use underscores, URLs automatically use hyphens (/case-studies/)
|
|
464
|
+
|
|
465
|
+
# Smart collections - aggregate or filter posts from multiple post types
|
|
466
|
+
config.collection "resources", post_types: ["blog", "docs", "tutorials"]
|
|
467
|
+
config.collection "long_reads" do |c|
|
|
468
|
+
c.post_types = ["blog", "tutorials"]
|
|
469
|
+
c.scope = -> { where("word_count > ?", 1200) }
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
# Enable standalone pages feature (About, Contact, Privacy, etc.)
|
|
473
|
+
# When enabled, rails bunko:setup creates a PagesController and pages PostType
|
|
474
|
+
# Use bunko_page :about in routes to create single-page routes
|
|
475
|
+
# Default: true
|
|
476
|
+
# config.allow_static_pages = true
|
|
477
|
+
|
|
478
|
+
# Reading speed for calculating estimated reading time (in words per minute)
|
|
479
|
+
# Default: 250
|
|
480
|
+
# config.reading_speed = 250
|
|
481
|
+
|
|
482
|
+
# Excerpt length for post.excerpt method (in characters)
|
|
483
|
+
# Default: 160
|
|
484
|
+
# config.excerpt_length = 160
|
|
485
|
+
|
|
486
|
+
# Automatically update word_count when content changes
|
|
487
|
+
# Default: true
|
|
488
|
+
# config.auto_update_word_count = true
|
|
489
|
+
end
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### Collections
|
|
493
|
+
|
|
494
|
+
Every post_type automatically gets its own collection (e.g., `blog` gets `/blog/`).
|
|
495
|
+
|
|
496
|
+
But Bunko also lets you create **dynamic collections** that aggregate or filter content in powerful ways.
|
|
497
|
+
|
|
498
|
+
**Example: Multi-Type Collection**
|
|
499
|
+
|
|
500
|
+
Combine multiple post types into a single collection:
|
|
501
|
+
|
|
502
|
+
```ruby
|
|
503
|
+
config.post_type "articles"
|
|
504
|
+
config.post_type "videos"
|
|
505
|
+
config.post_type "tutorials"
|
|
506
|
+
config.post_type "updates"
|
|
507
|
+
|
|
508
|
+
config.collection "resources", post_types: ["articles", "videos", "tutorials"]
|
|
509
|
+
# Auto-generates title "Resources", creates /resources/
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
This displays all three types together at `/resources/`.
|
|
513
|
+
Posts will still be properly shown through their standard post_type URL.
|
|
514
|
+
|
|
515
|
+
**Example: Scoped Collection**
|
|
516
|
+
|
|
517
|
+
Filter content by word count to create a long-form reading collection:
|
|
518
|
+
|
|
519
|
+
```ruby
|
|
520
|
+
config.collection "long_reads" do |c|
|
|
521
|
+
c.post_types = ["articles", "tutorials"]
|
|
522
|
+
c.scope = -> { where("word_count > ?", 1500) }
|
|
523
|
+
end
|
|
524
|
+
# Auto-generates title "Long Reads", creates /long-reads/
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
This shows only articles and tutorials over 1,500 words at `/long-reads/`.
|
|
528
|
+
|
|
529
|
+
**Example: Custom Title**
|
|
530
|
+
|
|
531
|
+
Override the auto-generated title:
|
|
532
|
+
|
|
533
|
+
```ruby
|
|
534
|
+
# Param style
|
|
535
|
+
config.collection "greatest_hits", title: "Greatest Hits", post_types: ["articles"]
|
|
536
|
+
|
|
537
|
+
# Block style
|
|
538
|
+
config.collection "greatest_hits" do |c|
|
|
539
|
+
c.title = "Greatest Hits"
|
|
540
|
+
c.post_types = ["articles", "videos", "tutorials"]
|
|
541
|
+
c.scope = -> { where(featured: true) }
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
# Mixed style (block overrides params if both set the same option)
|
|
545
|
+
config.collection "greatest_hits", title: "Param Title", post_types: ["articles"] do |c|
|
|
546
|
+
c.title = "Block Title" # Block overrides param → final title is "Block Title"
|
|
547
|
+
c.post_types = ["articles", "videos"] # Block overrides param → final post_types is ["articles", "videos"]
|
|
548
|
+
c.scope = -> { where(featured: true) }
|
|
549
|
+
end
|
|
550
|
+
# Final: title = "Block Title", post_types = ["articles", "videos"], URL: /greatest-hits/
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
**Planned Collections (Not Yet Working)**
|
|
554
|
+
|
|
555
|
+
Future versions will support additional collection types:
|
|
556
|
+
|
|
557
|
+
```ruby
|
|
558
|
+
# Author collections - index of all authors, show page per author
|
|
559
|
+
config.collection "authors", scope_by: :author
|
|
560
|
+
# Title: "Authors"
|
|
561
|
+
# /authors/ - index of all authors
|
|
562
|
+
# /authors/:author_slug - all posts by that author
|
|
563
|
+
|
|
564
|
+
# Tag collections - index of all tags, show page per tag
|
|
565
|
+
config.collection "tags", scope_by: :tag
|
|
566
|
+
# Title: "Tags"
|
|
567
|
+
# /tags/ - index of all tags
|
|
568
|
+
# /tags/:tag_slug - all posts with that tag
|
|
569
|
+
|
|
570
|
+
# Date-based collections - index of years/months, show page per period
|
|
571
|
+
config.collection "archives", scope_by: :year
|
|
572
|
+
# Title: "Archives"
|
|
573
|
+
# /archives/ - index of all years
|
|
574
|
+
# /archives/2024 - all posts from 2024
|
|
575
|
+
|
|
576
|
+
# Featured collections - simple filtered collection
|
|
577
|
+
config.collection "featured", scope_by: :featured
|
|
578
|
+
# Title: "Featured"
|
|
579
|
+
# /featured/ - all featured posts
|
|
580
|
+
|
|
581
|
+
# Combined filters
|
|
582
|
+
config.collection "popular_long_reads" do |c|
|
|
583
|
+
c.post_types = ["articles"]
|
|
584
|
+
c.scope = -> { where("word_count > ?", 1500).where("views > ?", 1000) }
|
|
585
|
+
end
|
|
586
|
+
# Title: "Popular Long Reads", URL: /popular-long-reads/
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
**Usage**
|
|
590
|
+
|
|
591
|
+
Use collections exactly like post types - same command, same routes:
|
|
592
|
+
|
|
593
|
+
```bash
|
|
594
|
+
rails bunko:add[resources]
|
|
595
|
+
rails bunko:add[long_reads]
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
```ruby
|
|
599
|
+
# config/routes.rb
|
|
600
|
+
bunko_collection :resources
|
|
601
|
+
bunko_collection :long_reads
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
Bunko automatically detects whether you're adding a post type or a collection and handles it accordingly.
|
|
605
|
+
|
|
606
|
+
## What Bunko Doesn't Do
|
|
607
|
+
|
|
608
|
+
- **No editor restrictions** - Use Lexxy, Trix, Rhino, markdown, etc.
|
|
609
|
+
- **No authentication** - Use Devise, etc, or whatever you like
|
|
610
|
+
- **No authorization** - Use Pundit, CanCanCan, or your own solution to decide who can edit Posts
|
|
611
|
+
- **No admin UI required** - Use Avo, etc, or build your own
|
|
612
|
+
- **No JavaScript** - No Stimulus controllers or Turbo frames forced on you
|
|
613
|
+
- **No CSS** - Style it however you want
|
|
614
|
+
- **No image handling** - Use ActiveStorage, Cloudinary, or anything else
|
|
615
|
+
- **No comments** - Integrate third party comments, build your own, or skip them
|
|
616
|
+
- **No search** - Use pg_search, etc. Add your own indexes to the Post table as needed
|
|
617
|
+
|
|
618
|
+
## Why Bunko?
|
|
619
|
+
|
|
620
|
+
**For developers who:**
|
|
621
|
+
- Want a blog/CMS that takes 5 minutes to set up
|
|
622
|
+
- Need flexibility to customize everything later
|
|
623
|
+
- Prefer conventions over configuration, but want escape hatches
|
|
624
|
+
- Value clean, understandable code over feature bloat
|
|
625
|
+
- Want to manage multiple content types without duplicate code
|
|
626
|
+
- Want to manage a number of sites with consistent CMS structure
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
## Development
|
|
630
|
+
|
|
631
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
632
|
+
|
|
633
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
634
|
+
|
|
635
|
+
## Contributing
|
|
636
|
+
|
|
637
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/kanejamison/bunko. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to be kind and respectful.
|
|
638
|
+
|
|
639
|
+
## License
|
|
640
|
+
|
|
641
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|