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
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
+ [![Tests](https://github.com/kanejamison/bunko/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/kanejamison/bunko/actions/workflows/ci.yml)
6
+ [![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/standardrb/standard)
7
+ [![Gem Version](https://badge.fury.io/rb/bunko.svg?icon=si%3Arubygems)](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).