Build the static content of trelixir.fi with Gleam

This commit is contained in:
Mikko Ahlroth 2023-03-11 11:29:47 +02:00
parent 190ff8885c
commit 7c80218c56
34 changed files with 971 additions and 150 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
build
.elixir_ls
2017-09-21-talk-materials

2
.tool-versions Normal file
View file

@ -0,0 +1,2 @@
gleam 0.27.0
erlang 25.2.2

8
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,8 @@
{
"[html]": {
"editor.defaultFormatter": null,
},
"files.associations": {
"*.html.glemp": "html"
}
}

View file

@ -1,44 +1,56 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TRElixir @ Vincit 21.9.2017</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>
Event 21&period;9&period;2017
TRElixir
</title>
<meta name="description" content="TRElixir is a developer meetup in Tampere focusing on the BEAM ecosystem.">
<link rel="alternate" type="application/rss+xml" href="feed.xml" title="TRElixir RSS feed">
<link rel="stylesheet" href="https://unpkg.com/purecss@0.6.2/build/pure-min.css" integrity="sha384-UQiGfs9ICog+LwheBSRCt1o5cbyKIHbwjWscjemyBMT9YCUMZffs6UqUTd0hObXD" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/purecss@0.6.2/build/grids-responsive-min.css">
<link rel="stylesheet" href="marketing.css">
</head>
<meta name="description" content="TRElixir is a developer meetup focusing on the Elixir / Erlang ecosystem.">
<link rel="stylesheet" href="https://unpkg.com/purecss@0.6.2/build/pure-min.css" integrity="sha384-UQiGfs9ICog+LwheBSRCt1o5cbyKIHbwjWscjemyBMT9YCUMZffs6UqUTd0hObXD" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/purecss@0.6.2/build/grids-responsive-min.css">
<link rel="stylesheet" href="marketing.css">
</head>
<body>
<header class="header">
<div class="home-menu pure-menu pure-menu-horizontal pure-menu-fixed">
<a class="pure-menu-heading" href="index.html">TRElixir</a>
</div>
</header>
<div class="home-menu pure-menu pure-menu-horizontal pure-menu-fixed">
<a class="pure-menu-heading" href="/">⬅ TRElixir</a>
</div>
</header>
<section class="splash-container event-splash">
</section>
<section class="splash-container event-splash">
</section>
<main class="content-wrapper event-content">
<section id="event" class="content contentpadding">
<h2 class="content-head is-center">1<sup>st</sup> event 21.9.2017</h2>
<h2 class="content-head is-center">Event 21&period;9&period;2017</h2>
<div class="pure-g">
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<p>
The first TRElixir event is on the 21<sup>st</sup> of September at <a href="https://www.google.fi/maps/place/Vincit/@61.4481532,23.8608591,17z/data=!3m1!4b1!4m5!3m4!1s0x468edfb7a8f2c79f:0x71c0801aebd30465!8m2!3d61.4481532!4d23.8630531">Vincit premises in Hervanta, Tampere</a>. The evening will start at 18.00. Free attendance, food and drinks will be provided. After the talks there will be time to hang around and get to know other Elixir devs.
</p>
The first TRElixir event was on the 21<sup>st</sup> of September at <a href="https://www.google.fi/maps/place/Vincit/@61.4481532,23.8608591,17z/data=!3m1!4b1!4m5!3m4!1s0x468edfb7a8f2c79f:0x71c0801aebd30465!8m2!3d61.4481532!4d23.8630531">Vincit premises in Hervanta, Tampere</a>.
</p>
<p>
The event is graciously hosted by <a href="https://vincit.com/en">Vincit</a>.
The event is graciously hosted by <a href="https&colon;&sol;&sol;vincit&period;com&sol;">Vincit</a>.
</p>
</div>
<div class="l-box-lrg pure-u-1 pure-u-md-1-2 signupform">
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<a href="" class="pure-button pure-button-disabled">Signup is closed</a>
</div>
</div>
</section>
@ -47,39 +59,48 @@
<h2 class="content-head is-center">Talks</h2>
<div class="pure-g">
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<h3 class="talk-title">Why Elixir matters</h3>
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<h3 class="talk-title">Why Elixir matters</h3>
<img src="mikkonaama.jpg" alt="Mikko Ahlroth" title="Mikko Ahlroth" class="speaker-image pure-img" />
<img src="mikkonaama2&period;webp" alt="Mikko Ahlroth" title="Mikko Ahlroth" class="speaker-image pure-img" />
<p class="speaker-name">Mikko Ahlroth, Vincit</p>
<p class="speaker-name">
Mikko Ahlroth, Vincit Oyj
</p>
<p class="speaker-description">
Mikko works primarily as a Django + AngularJS developer at <a href="https://vincit.com/en">Vincit</a>, but on his free time has dabbled with Elixir since 2013. He has published a simple <a href="https://hex.pm/packages/mbu">buildtool</a> on hex.pm and runs <a href="https://codestats.net/">Code::Stats</a> on Phoenix. His dream is to get to use Elixir at work some day.
</p>
<p class="speaker-description">
Mikko works primarily as a full stack developer at <a href="https://vincit.com/en">Vincit</a>, but on his free time has dabbled with Elixir since 2013. He has published <a href="https://hex.pm/users/nicd">some packages</a> on hex.pm and runs <a href="https://codestats.net/">Code::Stats</a> on Phoenix.
<p class="speaker-description">
<a class="pure-button" target="_blank" href="https://docs.google.com/presentation/d/1uLjOolPhzNs0DzBAOsMMxsQ5bxvqXWxp7ymhydpCNFw/edit?usp=sharing">Mikko's slides</a>
</p>
</div>
</p>
</div>
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<h3 class="talk-title">GenServer &amp; Agent</h3>
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<h3 class="talk-title">GenServer &amp; Agent</h3>
<img src="jannenaama&period;jpg" alt="Janne Tenhovirta" title="Janne Tenhovirta" class="speaker-image pure-img" />
<img src="jannenaama.jpg" alt="Janne Tenhovirta" title="Janne Tenhovirta" class="speaker-image pure-img" />
<p class="speaker-name">
Janne Tenhovirta, Cybersoft
</p>
<p class="speaker-name">Janne Tenhovirta, Cybersoft</p>
<p class="speaker-description">
Janne mainly gets his hands dirty in .NET backend development at his job with <a href="http://headpower.fi">Cybersoft, a subsidiary of Headpower</a>. He feels they get dirtier when fiddling with the frontend. He first learned of Elixir's existence in 2015 and started to fool around with it in 2016.
<p class="speaker-description">
Janne mainly gets his hands dirty in .NET backend development at his job with <a href="http://headpower.fi">Cybersoft, a subsidiary of Headpower</a>. He feels they get dirtier when fiddling with the frontend. He first learned of Elixir's existence in 2015 and started to fool around with it in 2016.
</p>
</div>
</p>
</div>
</div>
</section>
<footer class="footer l-box is-center">
Tampere background © Tampereen kaupunki, licensed under CC-BY-NC.
</footer>
Tampere background © Tampereen kaupunki, licensed under CC-BY-NC.
</footer>
</main>
</body>
</html>

View file

@ -1,49 +1,62 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TRElixir @ Vincit Tampere, 16.3.2023</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>
Event 16&period;3&period;2023
TRElixir
</title>
<meta name="description" content="TRElixir is a developer meetup in Tampere focusing on the BEAM ecosystem.">
<link rel="alternate" type="application/rss+xml" href="feed.xml" title="TRElixir RSS feed">
<link rel="stylesheet" href="https://unpkg.com/purecss@0.6.2/build/pure-min.css" integrity="sha384-UQiGfs9ICog+LwheBSRCt1o5cbyKIHbwjWscjemyBMT9YCUMZffs6UqUTd0hObXD" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/purecss@0.6.2/build/grids-responsive-min.css">
<link rel="stylesheet" href="marketing.css">
</head>
<meta name="description" content="TRElixir is a developer meetup focusing on the BEAM ecosystem.">
<link rel="stylesheet" href="https://unpkg.com/purecss@0.6.2/build/pure-min.css" integrity="sha384-UQiGfs9ICog+LwheBSRCt1o5cbyKIHbwjWscjemyBMT9YCUMZffs6UqUTd0hObXD" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/purecss@0.6.2/build/grids-responsive-min.css">
<link rel="stylesheet" href="marketing.css">
</head>
<body>
<header class="header">
<div class="home-menu pure-menu pure-menu-horizontal pure-menu-fixed">
<a class="pure-menu-heading" href="index.html">⬅ TRElixir</a>
</div>
</header>
<div class="home-menu pure-menu pure-menu-horizontal pure-menu-fixed">
<a class="pure-menu-heading" href="/">⬅ TRElixir</a>
</div>
</header>
<section class="splash-container event-splash">
</section>
<section class="splash-container event-splash"></section>
<main class="content-wrapper event-content">
<section id="event" class="content contentpadding">
<h2 class="content-head is-center">Event 16.3.2023</h2>
<h2 class="content-head is-center">Event 16&period;3&period;2023</h2>
<div class="pure-g">
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<p>
The next TRElixir event is on the 16<sup>th</sup> of March at the <a href="https://asiointi.maanmittauslaitos.fi/karttapaikka/?lang=fi&share=customMarker&n=6822019.623407671&e=328371.01345323544&title=Vincit%20City%20Centre&desc=&zoom=10&layers=W3siaWQiOjIsIm9wYWNpdHkiOjEwMH1d-z" target="_blank">Vincit city centre office</a>, <a href="https://technopolisglobal.com/office-spaces/tampere/yliopistonrinne/directions/" target="_blank">Technopolis Yliopistonrinne</a>, Kalevantie 2, Tampere. The evening will start at 18.00. Free attendance. Between and after the talks there will be time to hang around and get to know other devs.
</p>
The next TRElixir event is on the 16<sup>th</sup> of March at the <a href="https://asiointi.maanmittauslaitos.fi/karttapaikka/?lang=fi&share=customMarker&n=6822019.623407671&e=328371.01345323544&title=Vincit%20City%20Centre&desc=&zoom=10&layers=W3siaWQiOjIsIm9wYWNpdHkiOjEwMH1d-z" target="_blank">Vincit city centre office</a>, <a href="https://technopolisglobal.com/office-spaces/tampere/yliopistonrinne/directions/" target="_blank">Technopolis Yliopistonrinne</a>, Kalevantie 2, Tampere. The evening will start at 18.00. Free attendance. Between and after the talks there will be time to hang around and get to know other devs.
</p>
<p>
<strong style="text-transform: uppercase;">There is no food provided.</strong> There will be snacks and sodas, but if you need to eat properly, then please do that beforehand or bring some takeout with you. The aim here is to make arranging the event simpler and reduce food waste.
</p>
<p>
<strong style="text-transform: uppercase;">There is no food provided.</strong> There will be snacks and sodas, but if you need to eat properly, then please do that beforehand or bring some takeout with you. The aim here is to make arranging the event simpler and reduce food waste.
</p>
<p>
The event is graciously hosted by <a href="https://vincit.com/en">Vincit</a>.
The event is graciously hosted by <a href="https&colon;&sol;&sol;vincit&period;com&sol;">Vincit</a>.
</p>
</div>
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<p>No signup is necessary for this event. You just show up!</p>
<p>The Vincit office is on the south end of the building, just walk to the end of the main corridor and take the elevator up.</p>
<p>The Vincit office is on the south end of the building, just walk to the end of the main corridor and take the elevator up.</p>
</div>
</div>
</section>
@ -52,27 +65,37 @@
<h2 class="content-head is-center">Talks</h2>
<div class="pure-g">
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<h3 class="talk-title">Gleam Type Safe BEAM</h3>
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<h3 class="talk-title">Gleam &ndash; Type Safe BEAM</h3>
<img src="mikkonaama2.webp" alt="Mikko Ahlroth" title="Mikko Ahlroth" class="speaker-image pure-img" />
<img src="mikkonaama2&period;webp" alt="Mikko Ahlroth" title="Mikko Ahlroth" class="speaker-image pure-img" />
<p class="speaker-name">Mikko Ahlroth, Vincit</p>
<p class="speaker-name">
Mikko Ahlroth, Vincit Oyj
</p>
<p class="speaker-description">
Mikko works primarily as a full stack developer at <a href="https://vincit.com/en">Vincit</a>, but on his free time has dabbled with Elixir since 2013. He has published <a href="https://hex.pm/users/nicd">some packages</a> on hex.pm and runs <a href="https://codestats.net/">Code::Stats</a> on Phoenix.
</p>
</div>
<p class="speaker-description">
Mikko works primarily as a full stack developer at <a href="https://vincit.com/en">Vincit</a>, but on his free time has dabbled with Elixir since 2013. He has published <a href="https://hex.pm/users/nicd">some packages</a> on hex.pm and runs <a href="https://codestats.net/">Code::Stats</a> on Phoenix.
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<h3 class="talk-title">More talks TBA</h3>
</div>
</p>
</div>
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<h3 class="talk-title">More talks TBA</h3>
</div>
</div>
</section>
<footer class="footer l-box is-center">
Tampere background © Tampereen kaupunki, licensed under CC-BY-NC.
</footer>
Tampere background © Tampereen kaupunki, licensed under CC-BY-NC.
</footer>
</main>
</body>
</html>

View file

@ -4,10 +4,10 @@
<title>TRElixir</title>
<link>https://trelixir.fi/</link>
<description>TRElixir newsletter feed.</description>
<generator>My hands</generator>
<generator>Glemplate</generator>
<copyright>© Mikko Ahlroth</copyright>
<item>
<title>
<![CDATA[Upcoming event: 16.3.2023]]>
@ -15,20 +15,30 @@
<dc:creator>Mikko Ahlroth</dc:creator>
<link>https://trelixir.fi/event-2023-03-16.html</link>
<guid>https://trelixir.fi/event-2023-03-16.html</guid>
<link>
https://trelixir.fi/event-2023-03-16.html
</link>
<guid>
cfc19ed7-f46d-45b6-a134-e11df93a864d
</guid>
<description>
<![CDATA[
<p>
TRElixir will be next held on the 16th of March, 2023, at Vincit office in central Tampere, starting at 18.00 o'clock.
</p>
TRElixir will be next held on the 16th of March, 2023, at Vincit office in central Tampere, starting at 18.00 o'clock.
</p>
<p>
<a href="https://trelixir.fi/event-2023-03-16.html">Go to event page.</a>
</p>
<p>
<a href="https://trelixir.fi/event-2023-03-16.html">Go to event page.</a>
</p>
]]>
</description>
<pubDate>Tue, 21 Feb 2023 21:30:00 +0200</pubDate>
<pubDate>
Tue, 21 Feb 2023 21:30:00 +0200
</pubDate>
</item>
</channel>
</rss>

15
gleam.toml Normal file
View file

@ -0,0 +1,15 @@
name = "trelixir"
version = "1.0.0"
description = "trelixir.fi site generator"
# Fill out these fields if you intend to generate HTML documentation or publish
# your project to the Hex package manager.
#
# licences = ["MIT"]
# repository = { type = "gitlab", user = "Nicd", repo = "glemplate" }
# links = []
[dependencies]
gleam_stdlib = "~> 0.27.0"
glemplate = "~> 2.0"
gleam_erlang = "~> 0.18"

View file

@ -1,86 +1,137 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TRElixir</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>
TRElixir
</title>
<meta name="description" content="TRElixir is a developer meetup in Tampere focusing on the BEAM ecosystem.">
<link rel="alternate" type="application/rss+xml" href="feed.xml" title="TRElixir RSS feed">
<link rel="stylesheet" href="https://unpkg.com/purecss@0.6.2/build/pure-min.css" integrity="sha384-UQiGfs9ICog+LwheBSRCt1o5cbyKIHbwjWscjemyBMT9YCUMZffs6UqUTd0hObXD" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/purecss@0.6.2/build/grids-responsive-min.css">
<link rel="stylesheet" href="marketing.css">
</head>
<meta name="description" content="TRElixir is a developer meetup in Tampere focusing on the BEAM ecosystem.">
<link rel="alternate" type="application/rss+xml" href="feed.xml" title="TRElixir RSS feed">
<link rel="stylesheet" href="https://unpkg.com/purecss@0.6.2/build/pure-min.css" integrity="sha384-UQiGfs9ICog+LwheBSRCt1o5cbyKIHbwjWscjemyBMT9YCUMZffs6UqUTd0hObXD" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/purecss@0.6.2/build/grids-responsive-min.css">
<link rel="stylesheet" href="marketing.css">
</head>
<body>
<header class="header">
<div class="home-menu pure-menu pure-menu-horizontal pure-menu-fixed">
<a class="pure-menu-heading" href="#top">TRElixir</a>
<div class="home-menu pure-menu pure-menu-horizontal pure-menu-fixed">
<a class="pure-menu-heading" href="#top">TRElixir</a>
<ul class="pure-menu-list">
<li class="pure-menu-item"><a class="pure-menu-link" href="#event">Events</a></li>
<li class="pure-menu-item"><a class="pure-menu-link" href="#contact">Contact</a></li>
<li class="pure-menu-item"><a class="pure-menu-link" href="#sponsors">Sponsored by</a></li>
<li class="pure-menu-item"><a class="pure-menu-link" href="#top">Top</a></li>
</ul>
</div>
<ul class="pure-menu-list">
<li class="pure-menu-item"><a class="pure-menu-link" href="#event">Events</a></li>
<li class="pure-menu-item"><a class="pure-menu-link" href="#contact">Contact</a></li>
<li class="pure-menu-item"><a class="pure-menu-link" href="#sponsors">Sponsored by</a></li>
<li class="pure-menu-item"><a class="pure-menu-link" href="#top">Top</a></li>
</ul>
</div>
</header>
<section class="splash-container ">
<header class="splash">
<h1 class="splash-head">TRElixir</h1>
<p class="splash-subhead">
Tampere 💜 BEAM!
</p>
<p class="splash-subhead">
TRElixir is a developer meetup focusing on the <abbr title="Elixir, Erlang, Gleam, etc.">BEAM</abbr> ecosystem.
</p>
</header>
</section>
<section class="splash-container">
<header class="splash">
<h1 class="splash-head">TRElixir</h1>
<p class="splash-subhead">
Tampere 💜 BEAM!
</p>
<p class="splash-subhead">
TRElixir is a developer meetup focusing on the <abbr title="Elixir, Erlang, Gleam, etc.">BEAM</abbr> ecosystem.
</p>
</header>
</section>
<main class="content-wrapper">
<section id="event" class="ribbon l-box-lrg contentpadding">
<h2 class="content-head content-head-ribbon is-center">Upcoming event</h2>
<h3 class="content-subhead">Thursday&comma; 16&period;3&period;2023&comma; 18&period;00</h3>
<h3 class="content-subhead">Thursday, 16.3.2023, 18.00</h3>
<div class="pure-g">
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<p>
TRElixir will be next held on the 16<sup>th</sup> of March, 2023, at Vincit office in central Tampere, starting at 18.00 o'clock.
</p>
<div class="pure-g">
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<p>
TRElixir will be next held on the 16<sup>th</sup> of March, 2023, at Vincit office in central Tampere, starting at 18.00 o'clock.
</p>
<p>
Planned presentations:
</p>
<p>
Presentations:
</p>
<ul>
<li>Gleam Type Safe BEAM (Mikko Ahlroth)</li>
<li>More talks TBA</li>
</ul>
</div>
<ul>
<li>
Gleam &ndash; Type Safe BEAM (Mikko Ahlroth)
</li>
<li>
More talks TBA
</li>
</ul>
</div>
<div class="l-box-lrg pure-u-1 pure-u-md-1-2 signupform">
<a href="event-2023-03-16.html" class="pure-button">Go to event page</a>
</div>
</div>
<div class="l-box-lrg pure-u-1 pure-u-md-1-2 signupform">
<a href="event-2023-03-16.html" class="pure-button">Go to event page</a>
</div>
</div>
<h2 class="content-head content-head-ribbon is-center">Past events</h2>
<h3 class="content-subhead">21&period;9&period;2017</h3>
<h3 class="content-subhead">1<sup>st</sup> event 21.9.2017</h3>
<div class="pure-g">
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<p>
The first TRElixir event was on the 21<sup>st</sup> of September at Vincit premises in Hervanta, Tampere.
</p>
<div class="pure-g">
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<p>
The first TRElixir event was on the 21<sup>st</sup> of September at Vincit premises in Hervanta, Tampere.
</p>
</div>
<div class="l-box-lrg pure-u-1 pure-u-md-1-2 signupform">
<a href="event-2017-09-21.html" class="pure-button">Go to event page</a>
</div>
</div>
<p>
Presentations:
</p>
<ul>
<li>
Why Elixir matters (Mikko Ahlroth)
</li>
<li>
GenServer &amp; Agent (Janne Tenhovirta)
</li>
</ul>
</div>
<div class="l-box-lrg pure-u-1 pure-u-md-1-2 signupform">
<a href="event-2017-09-21.html" class="pure-button">Go to event page</a>
</div>
</div>
</section>
<section id="contact" class="content contentpadding">
@ -120,8 +171,9 @@
</section>
<footer class="footer l-box is-center">
Tampere background © Tampereen kaupunki, licensed under CC-BY-NC.
</footer>
Tampere background © Tampereen kaupunki, licensed under CC-BY-NC.
</footer>
</main>
</body>
</html>

15
manifest.toml Normal file
View file

@ -0,0 +1,15 @@
# This file was generated by Gleam
# You typically do not need to edit this file
packages = [
{ name = "gleam_erlang", version = "0.18.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "C69F59D086AD50B80DE294FB0963550630971C9DC04E92B1F7AEEDD2C0BE226C" },
{ name = "gleam_stdlib", version = "0.27.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "9DBDD21B48C654182CDD8AA15ACF85E8E74A0438583C68BD7EF08BE89F999C6F" },
{ name = "glemplate", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "glentities", "nibble"], otp_app = "glemplate", source = "hex", outer_checksum = "B4A7B0A2FC4721A364124B32E9B25F74BE1413274B0D26CC615271E7EFBC3035" },
{ name = "glentities", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glentities", source = "hex", outer_checksum = "1D102DA9A4C1991019829DE1C3E49589A63CB82994CCB40924862239F5077446" },
{ name = "nibble", version = "0.2.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "nibble", source = "hex", outer_checksum = "B5D0E11DADEB4EC2F4ACD04DEB3CEBE3F0762B0BD9FF37805948FFC17017F791" },
]
[requirements]
gleam_erlang = "~> 0.18"
gleam_stdlib = "~> 0.27.0"
glemplate = "~> 2.0"

44
src/trelixir.gleam Normal file
View file

@ -0,0 +1,44 @@
import gleam/map
import gleam/string_builder.{StringBuilder}
import gleam/erlang/file
import gleam/list
import trelixir/tpls
import trelixir/index
import trelixir/events/types as event_types
import trelixir/events/utils as event_utils
import trelixir/feed/feed
import trelixir/events/event_2023_03_16
import trelixir/events/event_2017_09_21
pub fn main() {
let assert Ok(templates) = tpls.load_templates()
let assert Ok(index_tpl) = map.get(templates, index.tpl)
let assert Ok(index_content) = index.render(index_tpl, templates)
let assert Ok(_) = write_rendered("index.html", index_content)
let assert Ok(feed_tpl) = map.get(templates, feed.tpl)
let assert Ok(feed_content) = feed.render(feed_tpl, templates)
let assert Ok(_) = write_rendered("feed.xml", feed_content)
let events = [
#(event_2023_03_16.file, event_2023_03_16.data()),
#(event_2017_09_21.file, event_2017_09_21.data()),
]
let assert Ok(_) =
list.try_map(
events,
fn(event) {
let #(file_name, data) = event
let assert Ok(tpl) = map.get(templates, event_types.tpl)
let assert Ok(event_content) = event_utils.render(tpl, templates, data)
write_rendered(file_name <> ".html", event_content)
},
)
}
fn write_rendered(filename: String, rendered: StringBuilder) {
file.write(string_builder.to_string(rendered), filename)
}

View file

@ -0,0 +1,3 @@
<p>
The first TRElixir event was on the 21<sup>st</sup> of September at <a href="https://www.google.fi/maps/place/Vincit/@61.4481532,23.8608591,17z/data=!3m1!4b1!4m5!3m4!1s0x468edfb7a8f2c79f:0x71c0801aebd30465!8m2!3d61.4481532!4d23.8630531">Vincit premises in Hervanta, Tampere</a>.
</p>

View file

@ -0,0 +1,25 @@
import glemplate/assigns
import trelixir/events/types
import trelixir/events/utils
import trelixir/speakers/mikko_ahlroth
import trelixir/speakers/janne_tenhovirta
pub const file = "event-2017-09-21"
pub fn data() {
types.Event(
title: "Event 21.9.2017",
host_name: "Vincit",
host_url: "https://vincit.com/",
description: utils.load_child_tpl(
"event_2017_09_21",
"description",
assigns.new(),
),
signup_info: utils.load_child_tpl("generic", "signup_closed", assigns.new()),
talks: [
types.Talk(title: "Why Elixir matters", speaker: mikko_ahlroth.data()),
types.Talk(title: "GenServer & Agent", speaker: janne_tenhovirta.data()),
],
)
}

View file

@ -0,0 +1,7 @@
<p>
The next TRElixir event is on the 16<sup>th</sup> of March at the <a href="https://asiointi.maanmittauslaitos.fi/karttapaikka/?lang=fi&share=customMarker&n=6822019.623407671&e=328371.01345323544&title=Vincit%20City%20Centre&desc=&zoom=10&layers=W3siaWQiOjIsIm9wYWNpdHkiOjEwMH1d-z" target="_blank">Vincit city centre office</a>, <a href="https://technopolisglobal.com/office-spaces/tampere/yliopistonrinne/directions/" target="_blank">Technopolis Yliopistonrinne</a>, Kalevantie 2, Tampere. The evening will start at 18.00. Free attendance. Between and after the talks there will be time to hang around and get to know other devs.
</p>
<p>
<strong style="text-transform: uppercase;">There is no food provided.</strong> There will be snacks and sodas, but if you need to eat properly, then please do that beforehand or bring some takeout with you. The aim here is to make arranging the event simpler and reduce food waste.
</p>

View file

@ -0,0 +1,3 @@
<p>No signup is necessary for this event. You just show up!</p>
<p>The Vincit office is on the south end of the building, just walk to the end of the main corridor and take the elevator up.</p>

View file

@ -0,0 +1,31 @@
import glemplate/assigns
import trelixir/events/types
import trelixir/events/utils
import trelixir/speakers/mikko_ahlroth
pub const file = "event-2023-03-16"
pub fn data() {
types.Event(
title: "Event 16.3.2023",
host_name: "Vincit",
host_url: "https://vincit.com/",
description: utils.load_child_tpl(
"event_2023_03_16",
"description",
assigns.new(),
),
signup_info: utils.load_child_tpl(
"event_2023_03_16",
"signup_info",
assigns.new(),
),
talks: [
types.Talk(
title: "Gleam Type Safe BEAM",
speaker: mikko_ahlroth.data(),
),
types.Placeholder,
],
)
}

View file

@ -0,0 +1 @@
<a href="" class="pure-button pure-button-disabled">Signup is closed</a>

View file

@ -0,0 +1,26 @@
pub const tpl = "page.html.glemp"
pub type Company {
NoCompany
Company(name: String)
}
pub type Speaker {
Speaker(name: String, img: String, company: Company, description: String)
}
pub type Talk {
Placeholder
Talk(title: String, speaker: Speaker)
}
pub type Event {
Event(
title: String,
description: String,
host_name: String,
host_url: String,
signup_info: String,
talks: List(Talk),
)
}

View file

@ -0,0 +1,71 @@
import gleam/map
import gleam/list
import gleam/string_builder
import glemplate/assigns
import glemplate/html
import glemplate/ast
import glemplate/renderer
import trelixir/tpls
import trelixir/events/types
pub fn render(
template: ast.Template,
template_cache: renderer.TemplateCache,
data: types.Event,
) {
html.render(template, event_to_assigns(data), template_cache)
}
pub fn event_to_assigns(event: types.Event) -> assigns.Assigns {
assigns.from_list([
#("title", assigns.String(event.title)),
#("description", assigns.String(event.description)),
#("host_name", assigns.String(event.host_name)),
#("host_url", assigns.String(event.host_url)),
#("signup_info", assigns.String(event.signup_info)),
#("talks", assigns.List(list.map(event.talks, talk_to_assigns))),
#("is_index", assigns.Bool(False)),
])
}
pub fn talk_to_assigns(talk: types.Talk) -> assigns.AssignData {
case talk {
types.Talk(title, speaker) ->
assigns.Map(map.from_list([
#("title", assigns.String(title)),
#("speaker", speaker_to_assigns(speaker)),
#("is_placeholder", assigns.Bool(False)),
]))
types.Placeholder ->
assigns.Map(map.from_list([#("is_placeholder", assigns.Bool(True))]))
}
}
pub fn speaker_to_assigns(speaker: types.Speaker) -> assigns.AssignData {
let company = case speaker.company {
types.NoCompany -> assigns.Bool(False)
types.Company(name) -> assigns.String(name)
}
assigns.Map(map.from_list([
#("name", assigns.String(speaker.name)),
#("img", assigns.String(speaker.img)),
#("company", company),
#("description", assigns.String(speaker.description)),
]))
}
pub fn load_child_tpl(
event_module: String,
child_tpl: String,
data: assigns.Assigns,
) {
let assert Ok(parsed) =
tpls.load_template(
child_tpl <> ".html.glemp",
"src/trelixir/events/" <> event_module <> "-tpls",
)
let assert Ok(rendered) = html.render(parsed, data, map.new())
string_builder.to_string(rendered)
}

View file

@ -0,0 +1,7 @@
<p>
TRElixir will be next held on the 16th of March, 2023, at Vincit office in central Tampere, starting at 18.00 o'clock.
</p>
<p>
<a href="https://trelixir.fi/<%= event_file %>.html">Go to event page.</a>
</p>

View file

@ -0,0 +1,75 @@
import gleam/map
import gleam/string_builder
import gleam/list
import glemplate/html
import glemplate/text
import glemplate/assigns
import glemplate/ast
import glemplate/renderer
import trelixir/tpls
import trelixir/events/event_2023_03_16
pub const tpl = "feed.xml.glemp"
type Item {
Item(
title: String,
link: String,
guid: String,
description: String,
pub_date: String,
)
}
pub fn render(template: ast.Template, template_cache: renderer.TemplateCache) {
text.render(
template,
items()
|> items_to_assigns(),
template_cache,
)
}
fn items() {
[
Item(
title: "Upcoming event: 16.3.2023",
link: "https://trelixir.fi/event-2023-03-16.html",
guid: "cfc19ed7-f46d-45b6-a134-e11df93a864d",
description: load_description(
"cfc19ed7-f46d-45b6-a134-e11df93a864d",
assigns.new()
|> assigns.add_string("event_file", event_2023_03_16.file),
),
pub_date: "Tue, 21 Feb 2023 21:30:00 +0200",
),
]
}
fn items_to_assigns(items: List(Item)) {
let mapped =
list.map(
items,
fn(item) {
let item_map =
assigns.new()
|> assigns.add_string("title", item.title)
|> assigns.add_string("link", item.link)
|> assigns.add_string("guid", item.guid)
|> assigns.add_string("description", item.description)
|> assigns.add_string("pubDate", item.pub_date)
assigns.Map(item_map)
},
)
assigns.new()
|> assigns.add_list("items", mapped)
}
fn load_description(item: String, data: assigns.Assigns) {
let assert Ok(parsed) =
tpls.load_template(item <> "-description.html.glemp", "src/trelixir/feed")
let assert Ok(rendered) = html.render(parsed, data, map.new())
string_builder.to_string(rendered)
}

71
src/trelixir/index.gleam Normal file
View file

@ -0,0 +1,71 @@
import gleam/map
import gleam/list
import glemplate/assigns
import glemplate/renderer
import glemplate/html
import glemplate/ast
import trelixir/events/utils
import trelixir/events/event_2023_03_16
import trelixir/events/event_2017_09_21
pub const tpl = "index.html.glemp"
pub fn assigns() {
assigns.from_list([
#("is_index", assigns.Bool(True)),
#(
"upcoming_event",
assigns.Map(map.from_list([
#("title", assigns.String("Thursday, 16.3.2023, 18.00")),
#("file", assigns.String(event_2023_03_16.file)),
#(
"description",
assigns.String(
"
<p>
TRElixir will be next held on the 16<sup>th</sup> of March, 2023, at Vincit office in central Tampere, starting at 18.00 o'clock.
</p>
",
),
),
#(
"talks",
assigns.List(list.map(
event_2023_03_16.data().talks,
utils.talk_to_assigns,
)),
),
])),
),
#(
"past_events",
assigns.List([
assigns.Map(map.from_list([
#("title", assigns.String("21.9.2017")),
#("file", assigns.String("event-2017-09-21")),
#(
"description",
assigns.String(
"
<p>
The first TRElixir event was on the 21<sup>st</sup> of September at Vincit premises in Hervanta, Tampere.
</p>
",
),
),
#(
"talks",
assigns.List(list.map(
event_2017_09_21.data().talks,
utils.talk_to_assigns,
)),
),
])),
]),
),
])
}
pub fn render(template: ast.Template, template_cache: renderer.TemplateCache) {
html.render(template, assigns(), template_cache)
}

View file

@ -0,0 +1 @@
Janne mainly gets his hands dirty in .NET backend development at his job with <a href="http://headpower.fi">Cybersoft, a subsidiary of Headpower</a>. He feels they get dirtier when fiddling with the frontend. He first learned of Elixir's existence in 2015 and started to fool around with it in 2016.

View file

@ -0,0 +1,12 @@
import glemplate/assigns
import trelixir/events/types
import trelixir/speakers/utils
pub fn data() {
types.Speaker(
name: "Janne Tenhovirta",
img: "jannenaama.jpg",
description: utils.load_description("janne_tenhovirta", assigns.new()),
company: types.Company(name: "Cybersoft"),
)
}

View file

@ -0,0 +1 @@
Mikko works primarily as a full stack developer at <a href="https://vincit.com/en">Vincit</a>, but on his free time has dabbled with Elixir since 2013. He has published <a href="https://hex.pm/users/nicd">some packages</a> on hex.pm and runs <a href="https://codestats.net/">Code::Stats</a> on Phoenix.

View file

@ -0,0 +1,12 @@
import glemplate/assigns
import trelixir/events/types
import trelixir/speakers/utils
pub fn data() {
types.Speaker(
name: "Mikko Ahlroth",
img: "mikkonaama2.webp",
description: utils.load_description("mikko_ahlroth", assigns.new()),
company: types.Company(name: "Vincit Oyj"),
)
}

View file

@ -0,0 +1,15 @@
import gleam/map
import gleam/string_builder
import glemplate/html
import glemplate/assigns
import trelixir/tpls
pub fn load_description(speaker_name: String, assigns: assigns.Assigns) {
let assert Ok(parsed) =
tpls.load_template(
speaker_name <> "-description.html.glemp",
"src/trelixir/speakers",
)
let assert Ok(rendered) = html.render(parsed, assigns, map.new())
string_builder.to_string(rendered)
}

38
src/trelixir/tpls.gleam Normal file
View file

@ -0,0 +1,38 @@
import gleam/list
import gleam/result
import gleam/string
import gleam/map
import gleam/erlang/file
import glemplate/parser
const tpl_dir = "tpl"
pub fn load_templates() {
use tpls <- result.then(
file.list_directory(tpl_dir)
|> result.map_error(fn(err) { "Couldn't list dir: " <> string.inspect(err) }),
)
list.try_fold(
tpls,
map.new(),
fn(cache, tpl_file) {
use parsed <- result.then(load_template(tpl_file, tpl_dir))
Ok(map.insert(cache, tpl_file, parsed))
},
)
}
pub fn load_template(tpl_file: String, tpl_dir: String) {
let tpl_path = tpl_dir <> "/" <> tpl_file
use tpl <- result.then(
file.read(tpl_path)
|> result.map_error(fn(err) {
"Couldn't read tpl file " <> tpl_path <> ": " <> string.inspect(err)
}),
)
parser.parse_to_template(tpl, tpl_file)
|> result.map_error(fn(err) {
"Couldn't parse template " <> tpl_file <> ": " <> string.inspect(err)
})
}

32
tpl/body_top.html.glemp Normal file
View file

@ -0,0 +1,32 @@
<header class="header">
<div class="home-menu pure-menu pure-menu-horizontal pure-menu-fixed">
<% if is_index %>
<a class="pure-menu-heading" href="#top">TRElixir</a>
<% else %>
<a class="pure-menu-heading" href="/">⬅ TRElixir</a>
<% end %>
<% if is_index %>
<ul class="pure-menu-list">
<li class="pure-menu-item"><a class="pure-menu-link" href="#event">Events</a></li>
<li class="pure-menu-item"><a class="pure-menu-link" href="#contact">Contact</a></li>
<li class="pure-menu-item"><a class="pure-menu-link" href="#sponsors">Sponsored by</a></li>
<li class="pure-menu-item"><a class="pure-menu-link" href="#top">Top</a></li>
</ul>
<% end %>
</div>
</header>
<section class="splash-container <% if is_index %><% else %>event-splash<% end %>">
<% if is_index %>
<header class="splash">
<h1 class="splash-head">TRElixir</h1>
<p class="splash-subhead">
Tampere 💜 BEAM!
</p>
<p class="splash-subhead">
TRElixir is a developer meetup focusing on the <abbr title="Elixir, Erlang, Gleam, etc.">BEAM</abbr> ecosystem.
</p>
</header>
<% end %>
</section>

37
tpl/feed.xml.glemp Normal file
View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
<title>TRElixir</title>
<link>https://trelixir.fi/</link>
<description>TRElixir newsletter feed.</description>
<generator>Glemplate</generator>
<copyright>© Mikko Ahlroth</copyright>
<% for item in items %>
<item>
<title>
<![CDATA[<%= item.title %>]]>
</title>
<dc:creator>Mikko Ahlroth</dc:creator>
<link>
<%= item.link %>
</link>
<guid>
<%= item.guid %>
</guid>
<description>
<![CDATA[
<%= raw item.description %>
]]>
</description>
<pubDate>
<%= item.pubDate %>
</pubDate>
</item>
<% end %>
</channel>
</rss>

3
tpl/foot.html.glemp Normal file
View file

@ -0,0 +1,3 @@
<footer class="footer l-box is-center">
Tampere background © Tampereen kaupunki, licensed under CC-BY-NC.
</footer>

14
tpl/head.html.glemp Normal file
View file

@ -0,0 +1,14 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>
<% if is_index %><% else %><%= title %> <% end %>
TRElixir
</title>
<meta name="description" content="TRElixir is a developer meetup in Tampere focusing on the BEAM ecosystem.">
<link rel="alternate" type="application/rss+xml" href="feed.xml" title="TRElixir RSS feed">
<link rel="stylesheet" href="https://unpkg.com/purecss@0.6.2/build/pure-min.css" integrity="sha384-UQiGfs9ICog+LwheBSRCt1o5cbyKIHbwjWscjemyBMT9YCUMZffs6UqUTd0hObXD" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/purecss@0.6.2/build/grids-responsive-min.css">
<link rel="stylesheet" href="marketing.css">
</head>

58
tpl/index.html.glemp Normal file
View file

@ -0,0 +1,58 @@
<!DOCTYPE html>
<html>
<% render head.html.glemp is_index: is_index %>
<body>
<% render body_top.html.glemp is_index: is_index %>
<main class="content-wrapper">
<section id="event" class="ribbon l-box-lrg contentpadding">
<h2 class="content-head content-head-ribbon is-center">Upcoming event</h2>
<% render index_event.html.glemp event: upcoming_event %>
<h2 class="content-head content-head-ribbon is-center">Past events</h2>
<% for event in past_events %>
<% render index_event.html.glemp event: event %>
<% end %>
</section>
<section id="contact" class="content contentpadding">
<h2 class="content-head is-center">Contact</h2>
<div class="pure-g">
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<h3 class="content-subhead">RSS</h3>
<p>
Follow our <a rel="alternate" type="application/rss+xml" href="feed.xml">RSS feed</a> for the latest updates. We will post any upcoming meetups in the RSS feed.
</p>
</div>
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<h3 class="content-subhead">IM</h3>
<p>
Currently the most relevant discussion can be found on the <a href="https://koodiklinikka.fi">Koodiklinikka.fi Slack server</a>, on the channel <kbd>#elixir</kbd>.
</p>
<p>
You can also shoot Mikko Ahlroth a message <a href="https://fosstodon.org/@nicd">on Fosstodon</a>, on IRC (Nicd @ Libera.chat), or <a href="mailto:trelixir@nytsoi.net">by email</a>.
</p>
</div>
</div>
</section>
<section id="sponsors" class="ribbon l-box-lrg contentpadding">
<h2 class="content-head content-head-ribbon is-center">Sponsored by</h2>
<div class="pure-g">
<div class="l-box-lrg pure-u-1 sponsorlogos">
<a href="https://vincit.com/" target="_blank"><img class="pure-img" src="vincitlogo.jpg" alt="Vincit" title="Vincit"></a>
</div>
</div>
</section>
<% render foot.html.glemp %>
</main>
</body>
</html>

View file

@ -0,0 +1,29 @@
<h3 class="content-subhead"><%= event.title %></h3>
<div class="pure-g">
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<%= raw event.description %>
<% if event.talks %>
<p>
Presentations:
</p>
<ul>
<% for talk in event.talks %>
<li>
<% if talk.is_placeholder %>
More talks TBA
<% else %>
<%= talk.title %> (<%= talk.speaker.name %>)
<% end %>
</li>
<% end %>
</ul>
<% end %>
</div>
<div class="l-box-lrg pure-u-1 pure-u-md-1-2 signupform">
<a href="<%= event.file %>.html" class="pure-button">Go to event page</a>
</div>
</div>

56
tpl/page.html.glemp Normal file
View file

@ -0,0 +1,56 @@
<!DOCTYPE html>
<html>
<% render head.html.glemp is_index: is_index, title: title %>
<body>
<% render body_top.html.glemp is_index: is_index %>
<main class="content-wrapper event-content">
<section id="event" class="content contentpadding">
<h2 class="content-head is-center"><%= title %></h2>
<div class="pure-g">
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<%= raw description %>
<p>
The event is graciously hosted by <a href="<%= host_url %>"><%= host_name %></a>.
</p>
</div>
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<%= raw signup_info %>
</div>
</div>
</section>
<section id="event" class="content contentpadding">
<h2 class="content-head is-center">Talks</h2>
<div class="pure-g">
<% for talk in talks %>
<div class="l-box-lrg pure-u-1 pure-u-md-1-2">
<% if talk.is_placeholder %>
<h3 class="talk-title">More talks TBA</h3>
<% else %>
<h3 class="talk-title"><%= talk.title %></h3>
<img src="<%= talk.speaker.img %>" alt="<%= talk.speaker.name %>" title="<%= talk.speaker.name %>" class="speaker-image pure-img" />
<p class="speaker-name">
<%= talk.speaker.name %><% if talk.speaker.company %>, <%= talk.speaker.company %><% end %>
</p>
<p class="speaker-description">
<%= raw talk.speaker.description %>
</p>
<% end %>
</div>
<% end %>
</div>
</section>
<% render foot.html.glemp %>
</main>
</body>
</html>