ragerender 0.1.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 670837503b9bdfb7e314412c2ad7f3fbed616035c83e231e94afbc1a8461ae7d
4
+ data.tar.gz: 4642302a27d7a2433267abca12fda09703a5b583593501279f87196f0885aa28
5
+ SHA512:
6
+ metadata.gz: 6dcfa8001b76f24b00360b52da55889307e2ada02b93b5fe9f1dfa8561b28f3c2cf6939a477977f27c356c43a1e7a596ba7d5007f371a689b9b17b2ccb29e7d1
7
+ data.tar.gz: 687ccd2273c923d13178dce12e9de7afba951e6bcabe576c9c1fe67e6a2a21b987b99dda8a73bd95897ca930cd05b96e7029af377e48e7387e6fd3f1847892e4
data/README.rdoc ADDED
@@ -0,0 +1,221 @@
1
+ = RageRender
2
+
3
+ A template parser and site generator that can render a webcomic site using
4
+ ComicFury templates, using the Jekyll static site generator.
5
+
6
+ == What's this?
7
+
8
+ {ComicFury}[https://comicfury.com] is an excellent no-bullshit webcomic hosting
9
+ site created and maintained by the legend Kyo. You should support them on
10
+ {Patreon}[https://www.patreon.com/comicfury]!
11
+
12
+ {Jekyll}[https://jekyllrb.com] is a highly regarded and widespread static site
13
+ generator. It builds simple slowly-changing content into HTML files using
14
+ templates.
15
+
16
+ RageRender allows you to use your ComicFury templates to generate a static
17
+ version of your webcomic site using Jekyll. You just supply your templates,
18
+ comics and blogs, and RageRender will output a site that mimics your ComicFury
19
+ site.
20
+
21
+ Well, I say "mimics". Output is a static site, which means all of the
22
+ interactive elements of ComicFury don't work. This includes comments,
23
+ subscriptions, search, and comic management.
24
+
25
+ === But why?!
26
+
27
+ RageRender allows those of us who work on making changes to ComicFury site
28
+ templates to test our changes before we put them live.
29
+
30
+ With RageRender, you can edit your CSS, HTML templates and site settings before
31
+ you upload them to ComicFury. This makes the process of testing changes quicker
32
+ and makes it much more likely that you catch mistakes before any comic readers
33
+ have a chance to see them.
34
+
35
+ RageRender doesn't compete with the most excellent ComicFury (who's Patreon you
36
+ should contribute to, as I do!) – you should continue to use ComicFury for all
37
+ your day-to-day artistic rage management needs. But if you find yourself making
38
+ changes to a site design, RageRender may be able to help you.
39
+
40
+ == Getting started
41
+
42
+ First, you need to have {Ruby}[https://www.ruby-lang.org/] and
43
+ {Bundler}[https://bundle.io/] installed. The Jekyll site has {good guides on how
44
+ to do that}[https://jekyllrb.com/docs/installation/] depending on your operating
45
+ system.
46
+
47
+ To set up a new site, open a terminal and type:
48
+
49
+ mkdir mycomic && cd mycomic
50
+ bundle init
51
+ bundle add jekyll
52
+ bundle add ragerender
53
+
54
+ Now you can add comics! Add the image into an <tt>images</tt> folder:
55
+
56
+ mkdir images
57
+ cp 'cool comic.jpg' 'images/My first page.jpg'
58
+
59
+ The file name of the image will be the title of your comic page. And that's it,
60
+ you added your first comic!
61
+
62
+ If you want to add an author note, create a text file in a folder called
63
+ <tt>_comics</tt> that has the same file name, but with a <tt>.md</tt> extension:
64
+
65
+ mkdir _comics
66
+ echo "Check out my cool comic y'all!" > '_comics/My first page.md'
67
+
68
+ Generate the site using:
69
+
70
+ bundle exec jekyll build
71
+
72
+ Or start a local website to see it in your browser:
73
+
74
+ bundle exec jekyll serve
75
+ # Now visit http://localhost:4000!
76
+
77
+ === Customising your site
78
+
79
+ You'll notice a few things that might be off about your site, including that the
80
+ webcomic title and author name are probably not what you were expecting.
81
+
82
+ You can create a configuration file to tell RageRender the important details.
83
+ Put something like this in your webcomic folder and call it
84
+ <tt>_config.yml</tt>:
85
+
86
+ title: "My awesome webcomic!"
87
+ slogan: "It's the best!"
88
+ description: >
89
+ My epic story about how him and her
90
+ fell into a romantic polycule with they and them
91
+
92
+ defaults:
93
+ - scope:
94
+ path: ''
95
+ values:
96
+ author: "John smith"
97
+
98
+ theme: ragerender
99
+
100
+ Your webcomic now has its basic information set up.
101
+
102
+ === Adding your layouts
103
+
104
+ RageRender will use a ComicFury default layout if you don't supply your own
105
+ files.
106
+
107
+ If you want to keep using the "simple" layout, you can add the details into your
108
+ <tt>_config.yml</tt> too:
109
+
110
+ layout:
111
+
112
+ If you want to use your own layout code, then create a <tt>_layouts</tt>
113
+ directory and put the contents of each of your ComicFury layout tabs in there,
114
+ and then put your CSS in the main folder. You should end up with a full set of
115
+ files like:
116
+
117
+ _layouts
118
+ archive.html
119
+ blog-archive.html
120
+ blog-display.html
121
+ comic-page.html
122
+ error-page.html
123
+ overall.html
124
+ overview.html
125
+ search.html
126
+ layout.css
127
+
128
+ Now when you build your site, your custom templates and styles will be used
129
+ instead.
130
+
131
+ === Adding blogs
132
+
133
+ Add your blogs into a folder called `_posts`:
134
+
135
+ cat _posts/2025-05-29-my-new-comic.md
136
+ Hey guys, welcome to my new comic! It's gonna be so sick!
137
+
138
+ Note that the name of your blog post has to include the date and the title, or
139
+ it'll be ignored.
140
+
141
+ === Customising comics and blogs
142
+
143
+ You can add {Front Matter}[https://jekyllrb.com/docs/front-matter/] to set the
144
+ details of your author notes and blogs manually:
145
+
146
+ ---
147
+ title: "spooky comic page"
148
+ date: "2025-03-05 16:20"
149
+ image: "images/ghost.png"
150
+ author: "Jane doe"
151
+ custom:
152
+ # use yes and no for tickbox settings
153
+ spooky: yes
154
+ # use text in quotes for short texts
155
+ mantra: "live long and prosper"
156
+ # use indented text for long texts
157
+ haiku: >
158
+ Testing webcomics
159
+ Now easier than ever
160
+ Thanks to RageRender
161
+ comments:
162
+ - author: "Skippy"
163
+ date: "13 Mar 2025, 3.45 PM"
164
+ comment: "Wow this is so sick!"
165
+ ---
166
+ Your author note still goes at the end, like this!
167
+
168
+ === Adding extra pages
169
+
170
+ You can add extra pages just by adding new HTML files to your webcomic folder.
171
+ The name of the file becomes the URL that it will use.
172
+
173
+ Pages by default won't be embedded into your 'Overall' layout. You can change
174
+ that and more with optional Front Matter:
175
+
176
+ ---
177
+ # Include this line to set the page title
178
+ title: "Bonus content"
179
+ # Include this line to hide the page from the navigation menu
180
+ hidden: yes
181
+ # Include this line to embed this page in the overall layout
182
+ layout: Overall
183
+ ---
184
+ <h1>yo check out my bonus content!</h1>
185
+
186
+ === Stuff that doesn't work
187
+
188
+ Here is a probably incomplete list of things you can expect to be different
189
+ about your local site compared to ComicFury:
190
+
191
+ - Any comments you specify in Front Matter will be present, but you can't add
192
+ new ones
193
+ - Search doesn't do anything at all
194
+ - Saving and loading your place in the comic isn't implemented
195
+ - GET and POST variables in templates are ignored and will always be blank
196
+ - Random numbers in templates will be random only once per site build, not once
197
+ per page call
198
+
199
+ == Without Jekyll
200
+
201
+ RageRender can also be used without Jekyll to turn ComicFury templates into
202
+ templates in other languages.
203
+
204
+ E.g:
205
+
206
+ gem install ragerender
207
+ echo "[c:iscomicpage]<div>[f:js|v:comictitle]</div>[/]" > template.html
208
+ ruby $(gem which ragerender/to_liquid) template.html
209
+ # {% if iscomicpage %}<div>{{ comictitle | escape }}</div>{% endif %}
210
+ ruby $(gem which ragerender/to_erb) template.html
211
+ # <% if iscomicpage %><div><%= js(comictitle) %></div><% end %>
212
+
213
+ You still need to pass the correct variables to these templates; browse {this
214
+ unofficial documentation}[https://github.com/heyeinin/comicfury-documentation]
215
+ or RageRender::ComicDrop etc. to see which variables work on which templates.
216
+
217
+ == Get help
218
+
219
+ That's not a proclamation but an invitation! Reach out if you're having trouble
220
+ by {raising an issue}[https://github.com/simonwo/ragerender/issues] or posting
221
+ in the ComicFury forums.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "rake/testtask"
2
+
3
+ Rake::TestTask.new(:test) do |t|
4
+ t.libs << "test"
5
+ t.libs << "lib"
6
+ t.test_files = FileList["test/**/*_test.rb"]
7
+ t.verbose = true
8
+ end
9
+
10
+ task :default => :test
data/assets/404.html ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ title: No such page
3
+ layout: error-page
4
+ hidden: true
5
+ permalink: "/404.html"
6
+ ---
7
+ The page you were trying to access does not exist on this webcomic.
@@ -0,0 +1,7 @@
1
+ ---
2
+ title: Archive
3
+ layout: archive
4
+ hidden: true
5
+ permalink: "/archive/comics/index.html"
6
+ mode: comics
7
+ ---
@@ -0,0 +1,6 @@
1
+ ---
2
+ title: Archive
3
+ layout: archive
4
+ hidden: true
5
+ permalink: "/archive/index.html"
6
+ ---
data/assets/blog.html ADDED
@@ -0,0 +1,6 @@
1
+ ---
2
+ title: Blog
3
+ layout: blog-archive
4
+ permalink: "/blog/"
5
+ hidden: true
6
+ ---
@@ -0,0 +1,6 @@
1
+ ---
2
+ title: Overview
3
+ layout: overview
4
+ hidden: true
5
+ permalink: "/overview/index.html"
6
+ ---
@@ -0,0 +1,6 @@
1
+ ---
2
+ title: Search
3
+ layout: search
4
+ hidden: true
5
+ permalink: '/search/'
6
+ ---
@@ -0,0 +1,6 @@
1
+ # E.g. 20th Nov 2024, 2:35 PM
2
+ SUFFIXES = {1 => 'st', 2 => 'nd', 3 => 'rd'}
3
+ def comicfury_date time
4
+ fmt = "%-d#{SUFFIXES.fetch(time.day, 'th')} %b %Y, %-I:%M %p"
5
+ time.strftime(fmt)
6
+ end
@@ -0,0 +1,147 @@
1
+ require 'jekyll/generator'
2
+ require 'jekyll/drops/drop'
3
+ require_relative 'comics'
4
+ require_relative 'pagination'
5
+ require_relative 'named_data_delegator'
6
+
7
+ # Pass the right variables to archive pages. Note that this doesn't apply to
8
+ # chapter pages because they are not "pages"
9
+ Jekyll::Hooks.register :pages, :pre_render do |page, payload|
10
+ if page.data['layout'] == 'archive'
11
+ payload.merge! RageRender::ArchiveDrop.new(page).to_liquid
12
+ end
13
+ end
14
+
15
+ module RageRender
16
+ COMICS_PER_PAGE = 160
17
+
18
+ # Sets the main archive page at '/archive' to be either the chapter index if
19
+ # chapters are enabled or the comics list if there are no chapters.
20
+ class MainArchivePageGenerator < Jekyll::Generator
21
+ def generate site
22
+ archive = site.pages.detect {|page| page.data['layout'] == 'archive' && !page.data.include?('mode') }
23
+ archive.data['mode'] = unless site.collections['comics'].docs.any? {|c| c.data.include? 'chapter' }
24
+ 'comics'
25
+ end
26
+ end
27
+ end
28
+
29
+ # A simple list of all the comics exists under '/archive/comics',
30
+ # with one page per 160 comics.
31
+ class ComicArchivePaginator < Jekyll::Generator
32
+ include PaginationGenerator
33
+
34
+ def source_page site
35
+ site.pages.detect {|page| page.data['layout'] == 'archive' && page.data['mode'] == 'comics' }
36
+ end
37
+
38
+ def num_pages site
39
+ site.collections['comics'].docs.each_slice(COMICS_PER_PAGE).size
40
+ end
41
+
42
+ def permalink
43
+ '/archive/comics/page/:number/index.html'
44
+ end
45
+ end
46
+
47
+ # Now there is also one page per chapter... but also the chapter pages are
48
+ # paginated if there are more than 160 comics per chapter. So we have to
49
+ # handle that pagination manually by calling another paginator for each page
50
+ # we generate here.
51
+ class ChapterArchiveGenerator < Jekyll::Generator
52
+ priority :high
53
+
54
+ def generate site
55
+ site.collections['chapters'].docs.each do |page|
56
+ page.data['mode'] = 'chapters'
57
+ ChapterArchivePaginator.new(page).generate(site)
58
+ end
59
+ end
60
+ end
61
+
62
+ # Note that this one doesn't descend from Jekyll::Generator, because we don't
63
+ # want it to be invoked automatically, only when we create a chapter page.
64
+ class ChapterArchivePaginator
65
+ include PaginationGenerator
66
+
67
+ def initialize chapter
68
+ @page = chapter
69
+ end
70
+
71
+ def source_page site
72
+ @page
73
+ end
74
+
75
+ def num_pages site
76
+ site.collections['comics'].docs.select do |c|
77
+ c.data['chapter'] == @page.data['slug']
78
+ end.each_slice(COMICS_PER_PAGE).size
79
+ end
80
+
81
+ def permalink
82
+ Pathname.new(@page.permalink).join('../').join('page/:number/index.html').to_path
83
+ end
84
+ end
85
+
86
+ # A Drop that provides all of the page variables for the archive pages.
87
+ class ArchiveDrop < Jekyll::Drops::Drop
88
+ private delegate_method_as :data, :fallback_data
89
+ extend NamedDataDelegator
90
+
91
+ def ischapterarchive
92
+ @obj.type == :chapters
93
+ end
94
+
95
+ def show_comic_list
96
+ ischapterarchive || @obj.data['mode'] == 'comics'
97
+ end
98
+
99
+ def show_chapter_overview
100
+ !show_comic_list
101
+ end
102
+
103
+ def chapters
104
+ unless show_chapter_overview
105
+ @obj.site.collections['chapters'].docs.map do |page|
106
+ ChapterDrop.new(page).to_liquid
107
+ end
108
+ end
109
+ end
110
+
111
+ def comics_paginated
112
+ number = @obj.data['number']
113
+ comics = if number
114
+ selected_comics.to_a[number - 1]
115
+ else
116
+ selected_comics.to_a.flatten
117
+ end.group_by {|c| c.data['chapter'] }
118
+
119
+ comics.map do |chapter, comics|
120
+ chapter_data = ChapterDrop.new(@obj.site.collections['chapters'].docs.detect {|c| c.data['slug'] == chapter })
121
+ comics.each_with_index.map do |comic, index|
122
+ drop = ComicDrop.new(comic)
123
+ {
124
+ **ComicDrop::PAGINATION_FIELDS.map {|field| [field, drop[field]] }.to_h,
125
+ **ChapterDrop::PAGINATION_FIELDS.map {|field| [field, chapter_data[field]] }.to_h,
126
+ 'number' => index + 1,
127
+ 'newchapter' => index == 0,
128
+ 'chapterend' => index == comics.size - 1,
129
+ }
130
+ end
131
+ end.flatten
132
+ end
133
+
134
+ def lastpagenumber
135
+ selected_comics.size
136
+ end
137
+
138
+ private
139
+ def selected_comics
140
+ comics = @obj.site.collections['comics'].docs.reject {|c| SPECIAL_COMIC_SLUGS.include? c.data['slug'] }
141
+ if @obj.type == :chapters
142
+ comics = comics.select {|c| c.data['chapter'] == @obj.data['slug'] }
143
+ end
144
+ comics.each_slice(COMICS_PER_PAGE)
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,37 @@
1
+ require 'jekyll/hooks'
2
+ require 'jekyll/drops/document_drop'
3
+ require_relative '../date_formats'
4
+ require_relative 'named_data_delegator'
5
+
6
+ Jekyll::Hooks.register :posts, :pre_render do |post, payload|
7
+ payload.merge! RageRender::BlogDrop.new(post).to_liquid
8
+ end
9
+
10
+ module RageRender
11
+ class BlogDrop < Jekyll::Drops::DocumentDrop
12
+ private delegate_method_as :data, :fallback_data
13
+ extend NamedDataDelegator
14
+
15
+ def_data_delegator :title, :blogtitle
16
+ def_data_delegator :author, :authorname
17
+ delegate_method_as :content, :blog
18
+
19
+ def posttime
20
+ comicfury_date @obj.date
21
+ end
22
+
23
+ def prevbloglink
24
+ @obj.previous_doc&.url
25
+ end
26
+
27
+ def nextbloglink
28
+ @obj.next_doc&.url
29
+ end
30
+
31
+ def to_liquid
32
+ super.reject do |k, v|
33
+ Jekyll::Drops::DocumentDrop::NESTED_OBJECT_FIELD_BLACKLIST.include? k
34
+ end.to_h
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,115 @@
1
+ require 'jekyll/generator'
2
+ require 'jekyll/drops/drop'
3
+ require 'jekyll/drops/document_drop'
4
+ require_relative '../date_formats'
5
+ require_relative 'pagination'
6
+ require_relative 'named_data_delegator'
7
+
8
+ # Pass the right variables to blog archive pages.
9
+ Jekyll::Hooks.register :pages, :pre_render do |page, payload|
10
+ if page.data['layout'] == 'blog-archive'
11
+ payload.merge! RageRender::BlogArchiveDrop.new(page).to_liquid
12
+ end
13
+ end
14
+
15
+ module RageRender
16
+ BLOGS_PER_PAGE = 15
17
+
18
+ # Creates each page of the blog archive by copying the root blog page and
19
+ # updating the page number. Blog archive pages are available under both
20
+ # '/blog' and '/blogarchive'.
21
+ class PaginatedBlogsGenerator < Jekyll::Generator
22
+ include PaginationGenerator
23
+
24
+ def source_page site
25
+ site.pages.detect {|page| page['layout'] == 'blog-archive' }
26
+ end
27
+
28
+ def num_pages site
29
+ site.posts.docs.each_slice(BLOGS_PER_PAGE).size
30
+ end
31
+
32
+ def permalink
33
+ '/blog/page/:number/index.html'
34
+ end
35
+ end
36
+
37
+ # As above.
38
+ class PaginatedBlogArchiveGenerator < Jekyll::Generator
39
+ include PaginationGenerator
40
+
41
+ def source_page site
42
+ site.pages.detect {|page| page['layout'] == 'blog-archive' }
43
+ end
44
+
45
+ def num_pages site
46
+ site.posts.docs.each_slice(BLOGS_PER_PAGE).size
47
+ end
48
+
49
+ def permalink
50
+ '/blogarchive/page/:number/index.html'
51
+ end
52
+ end
53
+
54
+ # Data to pass to a blog archive page.
55
+ class BlogArchiveDrop < Jekyll::Drops::Drop
56
+ private delegate_method_as :data, :fallback_data
57
+ data_delegator 'number'
58
+
59
+ def blogs_paginated
60
+ all_blogs[number-1]&.map {|blog| PaginatedBlogDrop.new(blog).to_liquid } || []
61
+ end
62
+
63
+ def lastpagenumber
64
+ all_blogs.size
65
+ end
66
+
67
+ # Objects used for laying out page numbers.
68
+ #
69
+ # The page numbers always include:
70
+ # - the first page
71
+ # - the last page
72
+ # - two pages around the current page
73
+ def pages
74
+ [1].chain([1, all_blogs.size, *(number-2..number+2).to_a].uniq).select {|i| i >= 1 && i <= all_blogs.size }.sort.each_cons(2).map do |prev, page|
75
+ {
76
+ 'page' => page,
77
+ 'pagelink' => File.join(@obj.url, 'page', page.to_s),
78
+ 'is_current' => page == number,
79
+ 'skipped_ahead' => page - prev > 1,
80
+ }
81
+ end
82
+ end
83
+
84
+ private
85
+ def all_blogs
86
+ @all_blogs = @obj.site.posts.docs.each_slice(BLOGS_PER_PAGE).to_a
87
+ end
88
+ end
89
+
90
+ # Data representing a single paginated blog entry, as available from
91
+ # [l:blogs_paginated].
92
+ class PaginatedBlogDrop < Jekyll::Drops::DocumentDrop
93
+ extend NamedDataDelegator
94
+
95
+ def_data_delegator :title, :blogtitle
96
+ def_delegator :@obj, :url, :bloglink
97
+ def_data_delegator :author, :authorname
98
+ def_delegator :@obj, :content, :blog
99
+ # TODO profilelink
100
+
101
+ private delegate_method_as :data, :fallback_data
102
+
103
+ def posttime
104
+ comicfury_date(@obj.date)
105
+ end
106
+
107
+ def allowcomments
108
+ @obj.site.config['allowcomments']
109
+ end
110
+
111
+ def comments
112
+ (@obj.data['comments'] || []).size
113
+ end
114
+ end
115
+ end