Skip to content

Commit

Permalink
68 move project desc to blog (#69)
Browse files Browse the repository at this point in the history
* Move all projects articles in blog page to reduce clicks

* Add props recommender article

* Improve about me page

* Improve header

* Compile the cljs to the js bundle and update RSS feed
  • Loading branch information
skydread1 authored Nov 12, 2024
1 parent d7fdd1d commit fc745ca
Show file tree
Hide file tree
Showing 19 changed files with 1,369 additions and 1,258 deletions.
Binary file removed resources/public/assets/magic-book.jpg
Binary file not shown.
Binary file added resources/public/assets/magic-hat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion resources/public/blog/rss/clojure-feed.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><atom:link href="https://www.loicblanchard.me/blog/rss/clojure-feed.xml" rel="self" type="application/rss+xml"></atom:link><title>Loic Blanchard - Clojure Blog Feed</title><link>https://www.loicblanchard.me</link><description>Articles related to Clojure</description><language>en-us</language><lastBuildDate>Tue, 03 Sep 2024 12:05:27 +0000</lastBuildDate><generator>clj-rss</generator><item><title>Testing in Clojure</title><link>https://www.loicblanchard.me/blog/testing-in-clojure</link><guid isPermaLink="false">https://www.loicblanchard.me/blog/testing-in-clojure</guid><pubDate>Sat, 10 Aug 2024 10:00:00 +0000</pubDate><description>
<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><atom:link href="https://www.loicblanchard.me/blog/rss/clojure-feed.xml" rel="self" type="application/rss+xml"></atom:link><title>Loic Blanchard - Clojure Blog Feed</title><link>https://www.loicblanchard.me</link><description>Articles related to Clojure</description><language>en-us</language><lastBuildDate>Tue, 12 Nov 2024 15:12:36 +0000</lastBuildDate><generator>clj-rss</generator><item><title>Testing in Clojure</title><link>https://www.loicblanchard.me/blog/testing-in-clojure</link><guid isPermaLink="false">https://www.loicblanchard.me/blog/testing-in-clojure</guid><pubDate>Sat, 10 Aug 2024 10:00:00 +0000</pubDate><description>
Introducing some popular testing tools to developers new to Clojure. Highlight solutions for how to do unit testing with Rich Comment Tests, data validation and generative testing with Malli, running test suites and metrics with Kaocha and how to do integration testing using external containerized services.
</description><content:encoded><![CDATA[<h2>Introduction</h2><p>This article introduces effective testing libraries and methods for those new to Clojure.</p><p>We'll explore using the <a href='https://github.com/lambdaisland/kaocha'>kaocha</a> test runner in both REPL and terminal, along with configurations to enhance feedback. Then we will explain how tests as documentation can be done using <a href='https://github.com/matthewdowney/rich-comment-tests'>rich-comment-tests</a>.</p><p>We will touch on how to do data validation, generation and instrumentation using <a href='https://github.com/metosin/malli'>malli</a>.</p><p>Finally, I will talk about how I manage integrations tests with eventual external services involved.</p><h2>Test good code</h2><h3>Pure functions</h3><p>First of all, always remember that it is important to have as many pure functions as possible. It means, the same input passed to a function always returns the same output. This will simplify the testing and make your code more robust.</p><p>Here is an example of unpredictable <strong>impure</strong> logic:</p><pre><code class="clojure">&#40;defn fib
&quot;Read the Fibonacci list length to be returned from a file,
Expand Down
11 changes: 8 additions & 3 deletions resources/public/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ h5,
h6 {
padding-bottom: 1rem;
padding-top: 1rem;
overflow: auto;
}

@media (max-width: 1024px) {
Expand Down Expand Up @@ -691,7 +692,6 @@ footer .contact-icons img {
margin: 1rem;
padding: 1rem;
flex-grow: 1;
text-align: justify;
border-style: solid;
border-width: 1px;
border-color: var(--border-primary-color);
Expand All @@ -704,13 +704,13 @@ footer .contact-icons img {
flex-basis: 100%;
margin: 0;
padding: 1rem;
text-align: justify;
border-style: solid none;
transition: 0.3s;
}
}

.post {
.post p,
.vignette-container p {
text-align: justify;
}

Expand Down Expand Up @@ -749,6 +749,11 @@ footer .contact-icons img {
text-align: center;
}

.vignette h2 a {
color: var(--text-second-color);
text-decoration: none;
}

/* Posts */

.post-body img {
Expand Down
2,291 changes: 1,146 additions & 1,145 deletions resources/public/main.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/loicb/client/core/dom.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@
[:<>
[header-comp]
[current-page]
[footer-comp]])
[footer-comp]])
17 changes: 15 additions & 2 deletions src/loicb/client/core/dom/header.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,19 @@
[:a {:on-click #(rf/dispatch [:evt.app/toggle-theme])}
text])

(defn tags
[]
[:div.tags
(for [tag ["🛠️ Staff SE"
"📍 Singapore"
"🎫 Pass: EP"
"💻 5+ years of experience"
"🎓 Master in CS"
"🇫🇷 French Citizen"]]
[:div.tag
{:key (str "tag-" tag)}
tag])])

(defn navbar-content-browser []
[:nav.browser
{:id "browser-nav" :class "browser-only"}
Expand Down Expand Up @@ -85,7 +98,7 @@
[svg/menu]])
[:div.name
[:h2 "Loïc Blanchard"]
(when nav-open? [:h3 "Software Engineer in Functional Programming (Clojure)"])]
(when nav-open? [tags])]
(when-not home-page?
[:button.nav-btn.hidden
{:on-click #(rf/dispatch [:evt.app/toggle-theme])}
Expand All @@ -111,7 +124,7 @@
[:div.name
{:class "mobile-only"}
[:h2 "Loïc Blanchard"]
[:h3 "Software Engineer in Functional Programming (Clojure)"]])])
[tags]])])

(defn header-comp []
(let [nav-open? @(rf/subscribe [:subs/pattern '{:nav.main/open? ?x}])
Expand Down
81 changes: 45 additions & 36 deletions src/loicb/client/core/dom/page.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
(for [article articles
:let [[article-title article-link] article]]
[:a
{:key (str "article-" article-title) :href article-link :rel "noreferrer" :target "_blank"}
{:key (str "article-" article-title) :href article-link}
[:div.link
[:div.img
[:img
Expand Down Expand Up @@ -89,7 +89,7 @@
[:p "Found any typo, errors or parts that need clarification? Feel free to raise a PR on the "
[:a {:href "https://github.com/skydread1/portfolio" :rel "noreferrer" :target "_blank"}
"GitHub repo"]
" and become a co-author."]])]]))
" and become a contributor."]])]]))

(defn vignette-link
"Vignette link to a portfolio project article."
Expand All @@ -100,7 +100,7 @@
[:<>
[:h2
(internal-link
title
(str "🔗 " title)
link-params)]
[:h5.info
(str (date-str date) " | " (if employer employer "Personal Project"))]
Expand Down Expand Up @@ -131,50 +131,59 @@
(str (date-str date) " | " "Loic Blanchard")]
[all-tags tags]]]]))

(defn post-link
"Represnetation of a link to a post depending on the page.
A link to a post can be represented as:
- [[vignette-link]] for the portfolio page
- [[simple-link]] for the blog page."
[{:post/keys [css-class id page] :as post} link-params]
(if (= :blog page)
(internal-link
[:div.simple-link
{:key (str "simple-link-" id)
:id (str "simple-link-" id)
:class (str "simple-link-" css-class)}
[simple-link post]]
link-params)
[:div.vignette-container
{:key (str "vignette-container-" id)
:id (str "vignette-container-" id)}
[:div.vignette
{:key (str "vignette-link-" id)
:id (str "vignette-link-" id)
:class (str "vignette-link-" css-class)}
[vignette-link post link-params]]]))
(defn portfolio-page
[]
(let [page-title @(rf/subscribe [:subs/pattern '{:app/current-view {:data {:title ?x}}}])
page-name "portfolio"
db-page-name :blog
post-route @(rf/subscribe [:subs/pattern '{:app/current-view {:data {:post-route ?x}}}])
posts (->> @(rf/subscribe [:subs.post/posts db-page-name])
(sort-by #(first (:post/date %)))
reverse)]
[:section.container
{:id page-name
:key page-name}
[:h1 page-title]
[:div.post-links
{:class "vignettes"}
(doall
(for [{:post/keys [id css-class home-page?] :as post} posts]
(when home-page?
[:div.vignette-container
{:key (str "vignette-container-" id)
:id (str "vignette-container-" id)}
[:div.vignette
{:key (str "vignette-link-" id)
:id (str "vignette-link-" id)
:class (str "vignette-link-" css-class)}
[vignette-link post {:post-id id
:page-name (or post-route "blog")}]]])))]]))

(defn page-with-post-links
"Page with post links."
(defn blog-page
[]
(let [page-title @(rf/subscribe [:subs/pattern '{:app/current-view {:data {:title ?x}}}])
page-name @(rf/subscribe [:subs/pattern '{:app/current-view {:data {:name ?x}}}])
db-page-name @(rf/subscribe [:subs/pattern '{:app/current-view {:data {:db-page-name ?x}}}])
page-name "blog"
db-page-name :blog
post-route @(rf/subscribe [:subs/pattern '{:app/current-view {:data {:post-route ?x}}}])
posts (->> @(rf/subscribe [:subs.post/posts db-page-name])
(sort-by #(first (:post/date %)))
reverse)]
[:section.container
{:id (name page-name)
:key (name page-name)}
{:id page-name
:key page-name}
[:h1 page-title]
[:div.post-links
{:class (if (= :blog page-name)
"simple-links" "vignettes")}
{:class "simple-links"}
(doall
(for [post posts]
(post-link post {:post-id (:post/id post)
:page-name (or post-route page-name)})))]]))
(for [{:post/keys [id css-class] :as post} posts]
(internal-link
[:div.simple-link
{:key (str "simple-link-" id)
:id (str "simple-link-" id)
:class (str "simple-link-" css-class)}
[simple-link post]]
{:post-id id
:page-name (or post-route "blog")})))]]))

(defn page-with-a-post
"Page that displays the full post content.
Expand Down
16 changes: 5 additions & 11 deletions src/loicb/client/core/router.cljs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
(ns loicb.client.core.router
(:require [loicb.client.core.dom.page :refer [about-page page-with-post-links page-with-a-post]]
(:require [loicb.client.core.dom.page :refer [about-page blog-page portfolio-page page-with-a-post]]
[goog.object :as gobj]
[reitit.frontend :as rei]
[reitit.frontend.easy :as rfe]
Expand All @@ -14,24 +14,18 @@
["/portfolio"
[""
{:name :portfolio
:db-page-name :portfolio
:post-route :portfolio/post
:title "Portfolio"
:view page-with-post-links}]

["/:post-id"
{:name :portfolio/post
:db-page-name :portfolio
:db-page-name :blog
:post-route :blog/post
:title "Portfolio"
:view page-with-a-post}]]
:view portfolio-page}]]

["/blog"
[""
{:name :blog
:db-page-name :blog
:post-route :blog/post
:title "Blog"
:view page-with-post-links}]
:view blog-page}]

["/:post-id"
{:name :blog/post
Expand Down
1 change: 1 addition & 0 deletions src/loicb/common/validation.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
[:post/id :string]
[:post/page :keyword]
[:post/title :string]
[:post/home-page? {:optional true} :boolean]
[:post/date [:and
[:vector :string]
[:fn #(date-valid? %)]]]
Expand Down
65 changes: 22 additions & 43 deletions src/loicb/server/content/about/aboutme.md
Original file line number Diff line number Diff line change
@@ -1,73 +1,52 @@
#:post{:id "about-me"
:page :about
:date ["2024-01-06"]
:date ["2024-11-12"]
:employer "My journey so far"
:repos [["My GitHub" "https://github.com/skydread1"]]
:articles [["My Tech Blog" "../blog"]]
:title "About Me"
:tags ["🛠️ Staff SE" "📍 Singapore" "🎫 Pass: EP" "💻 5+ years of experience" "🎓 Master in CS" "🇫🇷 French Citizen"]
:css-class "about-me"
:image #:image{:src "/assets/loic-logo.png"
:src-dark "/assets/loic-logo.png"
:alt "Loic Logo"}}
+++
+++
## Work Experiences
## In short

### 2024-now: Staff Software Engineer | [Flybot Pte Ltd](https://www.flybot.sg/), Singapore
👋🏻 Hi, I'm Loic Blanchard, a Staff Software Engineer based in Singapore with over 5 years of experience in building scalable software solutions. My expertise lies in designing and leading the development of high-quality software systems, primarily using the Functional Programming language **Clojure** and its ecosystem. I am currently leading the engineering team at [Flybot Pte Ltd](https://www.flybot.sg/).

- Lead the Flybot's engineering team into meeting our client's expectations.
- Report to the CEO directly to gather client's needs and plan accordingly
- Design software architecture and delegate project responsibilities to the team's engineers.
## My journey

### 2023: Senior Software Engineer | [Flybot Pte Ltd](https://www.flybot.sg/), Singapore
- Designed a challenge recommender that suggests personal challenges to Golden Island's players. The recommender is a Clojure application deployed in a POD in AWS EKS that consume events from kafka topics and produces personalized challenges to a dedicated kafka topic. It uses Datomic as storage solution within the EKS cluster | *Clojure, AWS EKS, k8s, datomic, kafka*
- Developed the company blog mobile app with React Native framework. The mobile frontend and web frontend share most of the re-frame (state management) logic | *ClojureScript, ReactNative* (open-source)
- Conducted technical interviews for junior developers and onboarding of new employees
After I got my Master in CS at [🇫🇷 CPE Lyon](https://www.cpe.fr/en/), I started my career as a software engineer with at [🇸🇬 Flybot Pte Ltd](https://www.flybot.sg/) developing pure Game backend API solutions using the **Functional Programming** paradigm with **Clojure**. I then worked on porting our Clojure logic to the CLR using the [Magic Compiler](https://github.com/nasser/magic) that generates .NET assemblies from Clojure code and which is compatible with Unity, the frontend used for our games.

### 2020-2023: Software Engineer | [Flybot Pte Ltd](https://www.flybot.sg/), Singapore
I also created the company's website using a Clojure(Script) **full-stack**, leveraging some of our CEO's open-source libraries to showcase our custom web stack. I also experimented with mobile app development demonstrating that we could reuse most of the ClojureScript frontend state management (Re-Frame) from the web app.

- Developed the company full-stack web app. The website has a blog. Oauth2 is used for authentication. The website is deployed in a docker container on AWS. It showcases some of Flybot's open-source libs for dependency injections and data pulling | *Clojure, ClojureScript, React* (open-source)
- Developed a basic Monte Carlo Tree Search bot for our card games | *Clojure*
- Ported our Clojure backend libraries to Unity so the Unity frontend developers can use the Clojure logic in Unity | *Clojure, C#*
- Improved the Nostrand project management to ease the compilation with the Magic compiler (compile Clojure file to .NET assemblies) | *Clojure, C#* (open-source)
- Developed a library called `MetaGame` to compose card games (play multiple rounds, make it a tournament). An entire tournament can be sent up using pure Clojure data | *Clojure*
- Developed online Chinese card games (Pǎo Dé Kuài (跑得快) and Big two (锄大地) ) backend | *Clojure*
I then worked on a **event-driven** multi-recommender system to send personal challenges to our players. It uses `Kafka` for event streaming, `Datomic` for storage, `Prometheus/Grafana` for monitoring, and it is containerized. The **CI/CD** is done in `Gitlab`. The apps are deployed in `AWS EKS`.

### 2019: End of study project | [Bosch SEA Pte Ltd](https://www.bosch.com.sg/our-company/bosch-in-singapore/), Singapore
- Modeled and provisioned infrastructure using AWS CloudFormation for a project that consists in facilitating the diagnosis of damaged automobile pieces via trend detection
- Deployed and maintained AWS resources with Jenkins
- Cohered Agile Software Development using Jira Kanban and Scrum as frameworks, Git for version-control system and Atlassian software *| Bitbucket, Jira and SourceTree*
Finally, I am currently working on a data aggregator using Red Planet Labs's [RAMA](https://redplanetlabs.com/learn-rama) platform which simplifies building distributed systems by providing a high-level, declarative framework that abstracts away the complexities of managing state, concurrency, and communication.

### 2017-2018: One-year internship | [Electriduct Inc](https://www.electriduct.com/), Fort Lauderdale, Florida, USA
- Improved Web Design and responsivity | *HTML, CSS, JS, 3dcart templates*
- Optimized online ad campaigns | *Google AdWords/Shopping/Analytics*
- Developed an inventory management program using UPC barcode reading | *PHP, SQL, HTML, CSS, JS*
- Developed a customized barcode generator for either sheet printers or thermal printer | *C#, SQL*
### Responsibilities:

## Education

### 2015-2019: Master’s Degree | [CPE](https://www.cpe.fr/en/) Lyon, France

- **Specialization**: Software Engineering
- **Major Project**: Full-stack JS web app and Mobile App development allowing users to find new friends to go to common interest nearby events together *| Node.js, ReactJS, React Native*
- **Secondary Projects**: Android Chat App *(Java)*, Big Data hackathon *(Hadoop, Tableau)*, Chess Game *(Java)*, Siam Game *(C)*, UX design *(Balsamiq)*

### 2014-2015: Undergraduate in Engineering Sciences | [CPE](https://www.cpe.fr/en/) Lyon, France
- **Major**: mathematics and physics
- **Minor**: computer sciences and automatism
- **Leading** software architecture design and project management, reporting directly to the CEO.
- **Conducting** technical interviews and **onboarding** new engineers.
- **Developing** scalable and robust backend systems using `Clojure`, `Kafka`, and `Datomic`.
- **Designing** mobile and web applications that share logic using `ClojureScript`, `Re-frame`, `Reagent (React)` and `Reagent React Native`.
- Building **Containerized** applications to enhance our gaming platforms.
- **Porting** Clojure backend logic to .NET to allow Clojure backend APIs to be used in Unity.

## Skills

### ICTS
- **Back-End**: Clojure, Python, Java, PHP, Node.js, C, C++, C#
- **HTTP**: Clojure Ring, Clojure Aleph
- **Front-End**: ClojureScript, HTML, CSS, JS, C#, Reagent (React), Re-frame, figwheel-main
- **Database**: MySQL, PostgreSQL, Datomic, Datalevin, Cassandra
### ICTS

- **Back-End**: Clojure, Java, C#
- **Front-End**: ClojureScript, HTML, CSS, JS, Reagent (React), Re-frame, figwheel-main
- **Database**: PostgreSQL, Datomic, Datalevin
- **Mobile**: Reagent React Native, figwheel-main
- **Cloud**: AWS, Vercel, Netlify
- **Containers**: Docker, k8s, AWS EKS
- **Event Streaming**: Kafka
- **Proj Management**: GitHub, Gitlab, Bitbucket, Trello, Jira, Slack, Jenkins
- **Project Management**: GitHub, Gitlab, Bitbucket, Trello, Jira, Slack, Jenkins

### Certifications
- **AWS**: Solutions Architect - Associate
3 changes: 2 additions & 1 deletion src/loicb/server/content/portfolio/blog-django.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#:post{:id "blog-django"
:page :portfolio
:page :blog
:home-page? true
:date ["2023-05-27" "2023-11-12"]
:repos [["Blog" "https://github.com/skydread1/blog"]]
:articles [["Deploy Django Blog in AWS Beanstalk" "../blog/deploy-django-aws-beanstalk"]]
Expand Down
3 changes: 2 additions & 1 deletion src/loicb/server/content/portfolio/flybot-card-games.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#:post{:id "card-games-api"
:page :portfolio
:page :blog
:home-page? true
:employer "Flybot Pte Ltd"
:date ["2020-01-06" "2021-10-29"]
:repos [["Magic" "https://github.com/nasser/magic"]]
Expand Down
3 changes: 2 additions & 1 deletion src/loicb/server/content/portfolio/flybot-mobile-app.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#:post{:id "clojure-mobile-app"
:page :portfolio
:page :blog
:home-page? true
:employer "Flybot Pte Ltd"
:date ["2023-02-13" "2023-08-08"]
:repos [["Flybot" "https://github.com/skydread1/flybot.sg"]]
Expand Down
3 changes: 2 additions & 1 deletion src/loicb/server/content/portfolio/flybot-website.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#:post{:id "clojure-full-stack-webapp"
:page :portfolio
:page :blog
:home-page? true
:employer "Flybot Pte Ltd"
:date ["2022-05-20" "2023-08-04"]
:repos [["Flybot" "https://github.com/skydread1/flybot.sg"]]
Expand Down
Loading

0 comments on commit fc745ca

Please sign in to comment.