You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2023/01/25 00:41:52 UTC
[groovy-website] branch asf-site updated: Add a blog section to the website
This is an automated email from the ASF dual-hosted git repository.
paulk pushed a commit to branch asf-site
in repository https://gitbox.apache.org/repos/asf/groovy-website.git
The following commit(s) were added to refs/heads/asf-site by this push:
new a3d2d41 Add a blog section to the website
a3d2d41 is described below
commit a3d2d41d7c1a3984b156d758741b6184d145908c
Author: Paul King <pa...@asert.com.au>
AuthorDate: Wed Jan 25 10:41:42 2023 +1000
Add a blog section to the website
---
.../src/main/groovy/generator/SiteGenerator.groovy | 29 +++++
generator/src/main/groovy/model/SiteMap.groovy | 1 +
site-dev/build.gradle | 6 +
site/src/site/blog/fun-with-rating-stars.adoc | 129 +++++++++++++++++++++
site/src/site/blog/img/star_ratings_csharp.png | Bin 0 -> 312550 bytes
site/src/site/pages/blog.groovy | 68 +++++++++++
site/src/site/pages/blogs.groovy | 33 ++++++
site/src/site/sitemap-user.groovy | 1 +
8 files changed, 267 insertions(+)
diff --git a/generator/src/main/groovy/generator/SiteGenerator.groovy b/generator/src/main/groovy/generator/SiteGenerator.groovy
index 6f58dd0..a32fcbc 100644
--- a/generator/src/main/groovy/generator/SiteGenerator.groovy
+++ b/generator/src/main/groovy/generator/SiteGenerator.groovy
@@ -137,6 +137,10 @@ class SiteGenerator {
renderWiki()
}
+ if (siteMap.blog) {
+ renderBlog()
+ }
+
long dur = System.currentTimeMillis() - sd
println "Generated site into $outputDir in ${dur}ms"
}
@@ -220,6 +224,31 @@ class SiteGenerator {
render 'geps', "geps", [list: gepList], 'wiki'
}
+ private void renderBlog() {
+ def asciidoctor = AsciidoctorFactory.instance
+ println "Rendering blog"
+
+ def blogDir = new File(sourcesDir, "blog")
+ def blogList = [:]
+ blogDir.eachFileRecurse { f->
+ if (f.name.endsWith('.adoc')) {
+ def bn = f.name.substring(0, f.name.lastIndexOf('.adoc'))
+ def header = asciidoctor.readDocumentHeader(f)
+ println "Rendering $bn"
+ def relativePath = []
+ def p = f.parentFile
+ while (p != blogDir) {
+ relativePath << p.name
+ p = p.parentFile
+ }
+ String baseDir = relativePath ? "blog${File.separator}${relativePath.join(File.separator)}" : 'blog'
+ render 'blog', bn, [notes:f.getText('utf-8'), header: header], baseDir
+ blogList[bn] = bn
+ }
+ }
+ render 'blogs', "blogs", [list: blogList], 'blog'
+ }
+
static void main(String... args) {
def sourcesDir = args[0] as File
def outputDir = args[1] as File
diff --git a/generator/src/main/groovy/model/SiteMap.groovy b/generator/src/main/groovy/model/SiteMap.groovy
index 95cffcf..56c3e74 100644
--- a/generator/src/main/groovy/model/SiteMap.groovy
+++ b/generator/src/main/groovy/model/SiteMap.groovy
@@ -41,6 +41,7 @@ class SiteMap {
boolean changelogs = true
boolean releaseNotes = true
boolean wiki = true
+ boolean blog = true
private SiteMap() {}
diff --git a/site-dev/build.gradle b/site-dev/build.gradle
index 4797d00..bce069e 100644
--- a/site-dev/build.gradle
+++ b/site-dev/build.gradle
@@ -68,11 +68,17 @@ task copyWikiAssets(type:Copy) {
into file("$buildDir/site/wiki/img")
}
+task copyBlogAssets(type:Copy) {
+ from file('../site/src/site/blog/img')
+ into file("$buildDir/site/blog/img")
+}
+
task generateSite(type:JavaExec) {
description = 'Generates the Groovy Dev Website'
dependsOn copyAssets
dependsOn copyWikiAssets
+ dependsOn copyBlogAssets
ext.sources = file('../site/src/site')
ext.outputDir = file("$buildDir/site")
diff --git a/site/src/site/blog/fun-with-rating-stars.adoc b/site/src/site/blog/fun-with-rating-stars.adoc
new file mode 100644
index 0000000..6689972
--- /dev/null
+++ b/site/src/site/blog/fun-with-rating-stars.adoc
@@ -0,0 +1,129 @@
+= Fun with rating stars
+
+:category: blog
+
+== Star Ratings
+
+A recent tweet showed some C# code for producing a String of stars that might
+be used when displaying ratings on a website:
+
+image:img/star_ratings_csharp.png[image]
+
+Let's have a look at several ways to do the same thing in Groovy.
+
+```
+def rating0(percentage) {
+ int stars = Math.ceil(percentage * 10)
+ ("⬤" * stars).padRight(10, "○")
+}
+```
+```
+def rating1(percentage) {
+ int stars = Math.ceil(percentage * 10)
+ "⬤" * stars + "○" * (10-stars)
+}
+```
+```
+def rating2(percentage) {
+ int skip = 10 - Math.ceil(percentage * 10)
+ "⬤⬤⬤⬤⬤⬤⬤⬤⬤⬤○○○○○○○○○○"[skip..<10+skip]
+}
+```
+
+```
+def rating3(percentage) {
+ switch(percentage) {
+ case 0 -> "○○○○○○○○○○"
+ case { it <= 0.1 } -> "⬤○○○○○○○○○"
+ case { it <= 0.2 } -> "⬤⬤○○○○○○○○"
+ case { it <= 0.3 } -> "⬤⬤⬤○○○○○○○"
+ case { it <= 0.4 } -> "⬤⬤⬤⬤○○○○○○"
+ case { it <= 0.5 } -> "⬤⬤⬤⬤⬤○○○○○"
+ case { it <= 0.6 } -> "⬤⬤⬤⬤⬤⬤○○○○"
+ case { it <= 0.7 } -> "⬤⬤⬤⬤⬤⬤⬤○○○"
+ case { it <= 0.8 } -> "⬤⬤⬤⬤⬤⬤⬤⬤○○"
+ case { it <= 0.9 } -> "⬤⬤⬤⬤⬤⬤⬤⬤⬤○"
+ default -> "⬤⬤⬤⬤⬤⬤⬤⬤⬤⬤"
+ }
+}
+```
+
+If you want you can test the various edge cases:
+
+```
+for (i in 0..3)
+ for (j in [0, 0.09, 0.1, 0.11, 0.9, 1])
+ println "rating$i"(j)
+
+```
+
+== Increasing Robustness
+
+The code examples here assume that the input is in the range `0 \<= percentage \<= 1`. There are several tweaks we could do to guard against inputs outside those ranges.
+
+We could simply add an assert, e.g.:
+
+```
+def rating4(percentage) {
+ assert percentage >= 0 && percentage <= 1
+ int stars = Math.ceil(percentage * 10)
+ ("⬤" * stars).padRight(10, "○")
+}
+```
+
+Or, if we wanted to not fail, tweak some of our conditions, e.g.
+for `rating3`, instead of `case 0`, we could use `case { it <= 0 }`.
+
+We could push the checks into our types by making a special class, `Percent` say, which only permitted the allowed values:
+```
+final class Percent {
+ final Double value
+ Percent(Double value) {
+ assert value >= 0 && value <= 1
+ this.value = value
+ }
+}
+```
+And we could optionally also use some metaprogramming to provide a custom `isCase` method:
+```
+Double.metaClass.isCase = { Percent p -> delegate >= p.value }
+```
+Which means we could tweak the rating method to be:
+```
+def rating5(Percent p) {
+ switch(p) {
+ case 0.0d -> "○○○○○○○○○○"
+ case 0.1d -> "⬤○○○○○○○○○"
+ case 0.2d -> "⬤⬤○○○○○○○○"
+ case 0.3d -> "⬤⬤⬤○○○○○○○"
+ case 0.4d -> "⬤⬤⬤⬤○○○○○○"
+ case 0.5d -> "⬤⬤⬤⬤⬤○○○○○"
+ case 0.6d -> "⬤⬤⬤⬤⬤⬤○○○○"
+ case 0.7d -> "⬤⬤⬤⬤⬤⬤⬤○○○"
+ case 0.8d -> "⬤⬤⬤⬤⬤⬤⬤⬤○○"
+ case 0.9d -> "⬤⬤⬤⬤⬤⬤⬤⬤⬤○"
+ default -> "⬤⬤⬤⬤⬤⬤⬤⬤⬤⬤"
+ }
+}
+```
+
+We can be fancier here using `@EqualsAndHashCode` on the `Percent` class and/or using `@Delegate` on the `value` property, depending on how rich in
+functionality we wanted the `Percent` instances to be.
+
+Alternatively, we could use a design-by-contract approach:
+
+```
+@Requires({ percentage >= 0 && percentage <= 1 })
+def rating6(percentage) {
+ int stars = Math.ceil(percentage * 10)
+ ("⬤" * stars).padRight(10, "○")
+}
+```
+
+== References
+
+https://twitter.com/JeroenFrijters/status/1615204074588180481[Original tweet]
+
+== Update history
+
+1.0 (2023-01-25):: Initial version
diff --git a/site/src/site/blog/img/star_ratings_csharp.png b/site/src/site/blog/img/star_ratings_csharp.png
new file mode 100644
index 0000000..cbb0451
Binary files /dev/null and b/site/src/site/blog/img/star_ratings_csharp.png differ
diff --git a/site/src/site/pages/blog.groovy b/site/src/site/pages/blog.groovy
new file mode 100644
index 0000000..4de64ee
--- /dev/null
+++ b/site/src/site/pages/blog.groovy
@@ -0,0 +1,68 @@
+import generator.DocUtils
+import org.asciidoctor.ast.DocumentHeader
+
+modelTypes = {
+ DocumentHeader header
+ String title
+ String notes
+}
+
+title = header.documentTitle.main
+
+layout 'layouts/main.groovy', true,
+ pageTitle: "The Apache Groovy programming language - Developer docs - $title",
+ extraStyles: ['https://cdnjs.cloudflare.com/ajax/libs/prettify/r298/prettify.min.css'],
+ extraFooter: contents {
+ script(src:'https://cdnjs.cloudflare.com/ajax/libs/prettify/r298/prettify.min.js') { }
+ script { yieldUnescaped "document.addEventListener('DOMContentLoaded',prettyPrint)" }
+ },
+ mainContent: contents {
+ Map options = [attributes:[DOCS_BASEURL:DocUtils.DOCS_BASEURL]]
+ def notesAsHTML = asciidocText(notes,options)
+ def matcher = notesAsHTML =~ /<h2 id="(.+?)">(.+?)<\/h2>/
+ def sections = [:]
+ while (matcher.find()) {
+ sections[matcher.group(1)] = matcher.group(2)
+ }
+
+ div(id: 'content', class: 'page-1') {
+ div(class: 'row') {
+ div(class: 'row-fluid') {
+ div(class: 'col-lg-3') {
+ ul(class: 'nav-sidebar') {
+ li(class:'active') {
+ a(href: '#doc', title)
+ }
+ sections.each { k,v ->
+ li {
+ a(href:"#$k", class: 'anchor-link', v)
+ }
+ }
+ }
+ }
+
+ div(class: 'col-lg-8 col-lg-pull-0') {
+ a(name:"doc"){}
+ h1(title)
+ if (header.author) {
+ p {
+ yield 'Author: '
+ i(header.author.fullName)
+ }
+ } else if (header.authors) {
+ p {
+ yield 'Authors: '
+ i(header.authors*.fullName.join(', '))
+ }
+
+ }
+ if (header.revisionInfo?.date) {
+ p("Last update: ${header.revisionInfo.date} (${header.revisionInfo.remark?:'no comment'})")
+ }
+ hr()
+ yieldUnescaped notesAsHTML
+ }
+ }
+ }
+ }
+ }
diff --git a/site/src/site/pages/blogs.groovy b/site/src/site/pages/blogs.groovy
new file mode 100644
index 0000000..9f1b899
--- /dev/null
+++ b/site/src/site/pages/blogs.groovy
@@ -0,0 +1,33 @@
+layout 'layouts/main.groovy', true,
+ pageTitle: "The Apache Groovy programming language - Blogs",
+ mainContent: contents {
+ def sorted = list.sort()
+ div(id: 'content', class: 'page-1') {
+ div(class: 'row') {
+ div(class: 'row-fluid') {
+ div(class: 'col-lg-3') {
+ ul(class: 'nav-sidebar') {
+ li(class:'active') {
+ a(href: '#gep', "Blogs")
+ }
+ sorted.each { blog ->
+ li { a(href: "#$blog.key", class: 'anchor-link', "$blog.key") }
+ }
+ }
+ }
+
+ div(class: 'col-lg-8 col-lg-pull-0') {
+ h1('Blogs for Groovy')
+ p 'Here you can find the Blogs for the Groovy programming language:'
+ ul {
+ sorted.each { blog ->
+ li {
+ a(href: "${blog.key}.html", "$blog.key: $blog.value")
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
diff --git a/site/src/site/sitemap-user.groovy b/site/src/site/sitemap-user.groovy
index b0f0ccc..8ebc5c8 100644
--- a/site/src/site/sitemap-user.groovy
+++ b/site/src/site/sitemap-user.groovy
@@ -18,6 +18,7 @@
*/
wiki = false
+blog = false
def devSiteBase = 'https://groovy.apache.org/'
menu {