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 {