
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
 <channel>
   <title>israel on Vinay Gopinath</title>
   <link>https://vinaygopinath.me/tags/israel/</link>
   <description>Recent content in israel on Vinay Gopinath</description>
   <generator>Hugo -- gohugo.io</generator>
   <language>en-gb</language>
   <lastBuildDate>Sun, 08 Jun 2025 10:36:43 -0600</lastBuildDate>
   
       <atom:link href="https://vinaygopinath.me/tags/israel/index.xml" rel="self" type="application/rss+xml" />
   
   
     <item>
       <title>Heartbreak</title>
       <link>https://vinaygopinath.me/2025/06/heartbreak/</link>
       <pubDate>Sun, 08 Jun 2025 10:36:43 -0600</pubDate>
       
       <guid>https://vinaygopinath.me/2025/06/heartbreak/</guid>
       <description>&lt;h1 id=&#34;heartbreak&#34;&gt;Heartbreak&lt;/h1&gt;&lt;p&gt;Wake up. Scroll through the news. Find out how many more innocent people were killed by Israeli Occupation Forces at a food collection point. Cry.A faint glimmer of hope. A ceasefire, they say? Get excited. Sure, it&amp;rsquo;s not international acknowledgement of statehood but at least the murdering might stop for a moment. Find out the talks have broken down. Return to gloom. You know Israel wasn&amp;rsquo;t going to honour the ceasefire anyway.&lt;/p&gt;&lt;p&gt;Spend hours watching interviews of the Freedom Flotilla Coalition activists aboard the &lt;em&gt;Madleen&lt;/em&gt;. &lt;a href=&#34;https://www.democracynow.org/2025/6/4/greta_thunberg_gaza_aid_flotilla&#34;&gt;Listen to Greta&lt;/a&gt; speak with a clarity of thought that lifts your spirits even as tears well up in your eyes. Notice that she&amp;rsquo;s making it very clear that there are no weapons onboard, that we&amp;rsquo;re peaceful volunteers carrying baby formula and medicine. Almost as if she expects Israel to attack, kill and justify its actions by claiming weapons were found, which for some reason the world would just believe. Cry at the thought of Greta being aware that she might be killed in this &lt;a href=&#34;https://www.aljazeera.com/news/liveblog/2025/6/4/madleen-gaza-flotilla-live-greta-thunberg-activists-to-arrive-on-june-7&#34;&gt;attempt to reach Gaza&lt;/a&gt; but going ahead anyway probably because she figures her reputation as an activist will coerce governments into action if she&amp;rsquo;s killed. Cry at her love for humanity.&lt;/p&gt;&lt;p&gt;Bemoan your inaction. Bemoan your silence. What can I do? What is there to do to dismantle this seemingly impenetrable war machine?Deep breaths. Try to redirect your thoughts towards things that are within your reach, within your ability to influence. You understand tech. Tech is an integral, toxic part of this machine - the eyes and ears of the beast. Helping people reduce their dependence on big tech weakens its ability to aid genocide. Assert to yourself, yes, it&amp;rsquo;s not much and it does not make a difference in the current situation, but it&amp;rsquo;s a step in the right direction, long-term.&lt;/p&gt;&lt;p&gt;Realise that you want it all to be over. Just darkness and silence, please. You want to look away, you realise, and you do, skipping past &lt;em&gt;those&lt;/em&gt; videos and articles. No more visuals of crying, emaciated children with their ribs jutting out. You can&amp;rsquo;t bear it. Especially not when you&amp;rsquo;re tucking into your organic lentil soup. You&amp;rsquo;re looking for something &amp;ldquo;light&amp;rdquo; to keep you company as you eat.&lt;/p&gt;&lt;p&gt;A break from the news. But you know it&amp;rsquo;s there, you know it&amp;rsquo;s happening. Your friends tell you of their grief, the latest round of slaughter. At the supermarket, you notice a gloved employee nonchalantly and briskly unloading large chunks of meat. Your scarred mind immediately leaps to visuals of a &amp;ldquo;refugee&amp;rdquo; camp in Gaza. Is that what it looks like in the aftermath of an &amp;ldquo;air raid&amp;rdquo;, you wonder?&lt;/p&gt;&lt;p&gt;Your social interactions are fraught. Tense. A new acquaintance merely alluding to Israel&amp;rsquo;s claims over Palestinian land makes you recoil. Disbelief. Are there really people out there who can find a way to rationalise Israel&amp;rsquo;s actions? Withdraw from the conversation and the acquaintance. Engaging in conversation and countering their claims would be the right thing to do, you agree, but you need what&amp;rsquo;s left of your will and resolve to function.&lt;/p&gt;&lt;p&gt;Rage. Rage against others who don&amp;rsquo;t seem to care. Rage against the apathy and cruelty. But you&amp;rsquo;re familiar with this rage, of course. You&amp;rsquo;re vegan, and most of your friends are not. Every dinner plate is a demonstration of apathy. But ultimately, you recognise that the rage is just a front for the deep disappointment and frustration with yourself and your impotence. Your inability, like the inability of millions around the world, to stop the genocide.&lt;/p&gt;&lt;p&gt;How much longer? What has this done to our humanity? How many millions of people around the world are devastated by living, continuing to live, in the epoch of a televised genocide?&lt;/p&gt;&lt;p&gt;What is the collective weight of this grief, this heartbreak?&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Nonviolent communication</title>
       <link>https://vinaygopinath.me/2025/05/nonviolent-communication/</link>
       <pubDate>Sun, 18 May 2025 21:44:37 -0600</pubDate>
       
       <guid>https://vinaygopinath.me/2025/05/nonviolent-communication/</guid>
       <description>&lt;p&gt;Alt title: How I learned to feel and embraced conflict as an opportunity for growth&lt;/p&gt;&lt;h3 id=&#34;whats-your-earliest-memory-of-being-frustrated-or-angry-but-not-being-able-or-knowing-how-to-express-yourself&#34;&gt;What’s your earliest memory of being frustrated or angry but not being able or knowing how to express yourself?&lt;/h3&gt;&lt;p&gt;Mine is probably after I said something to my sister, or did something to her things (that part is blurry, but I remember as a child I had a phase when I revelled in smashing/throwing/tearing up the things that meant something to her, like an Archie’s greeting card (remember those? Is that still a thing?) or gift given to her by a friend), immediately recognising at the sight of her tears that I’d done something “wrong”, finding a place to hide (sometimes literally in a closet 🤪), and feeling a wave of intense emotions I didn’t have words for at the time, trembling with the intensity of what I now recognise to be regret and embarrassment for the harm I’d caused, and shame and anger at my own behaviour.&lt;/p&gt;&lt;p&gt;But then, growing up in a South Asian working single-parent-ish household, there was never a conversation about what I’d done, why I’d done it, or how I felt about it later. Only condemnation and judgement that I’d been “bad” and “cruel”. It became the norm to simply dismiss how I felt and pretend like the incident had never happened, even if I was a bit sheepish and unwilling to make eye contact when I eventually emerged from the closet and saw my sister at dinnertime. And, over time, an internalisation of some of the judgements. Perhaps I &lt;em&gt;was&lt;/em&gt; a bad person, child-me concluded.&lt;/p&gt;&lt;h3 id=&#34;toxic-masculinity-queer-edition&#34;&gt;Toxic masculinity, queer edition&lt;/h3&gt;&lt;p&gt;Cut to being a young adult, and its distinct swirl of confusion and anxieties over one’s role in the world, gender identity, masculinity and sexuality. There is a strain of masculinity out there, certainly widely prevalent in India, that identifies itself with being emotionless. As in, completely devoid of emotions. All emotions are the realm of women. Men, &lt;em&gt;real&lt;/em&gt; men (by which I mean &lt;em&gt;straight&lt;/em&gt;, of course), feel no emotion and are driven purely by reason.&lt;/p&gt;&lt;p&gt;When you’re overwhelmed and unable to cope with the immensity of the weight of the world and the questions it’s throwing at you, a supposed pathway of escaping emotions altogether can be very tempting. Emotions are for other, lesser, people, not me, I decided. People who cry at airports as they say goodbye to a loved one? Weak.&lt;/p&gt;&lt;p&gt;If you’re inclined towards intellectualising your choices, you might even reach for the self-description “stoic”, in a terribly misguided (but somewhat common?) interpretation of stoicism.&lt;/p&gt;&lt;h3 id=&#34;the-cost-of-avoiding-your-feelings&#34;&gt;The cost of avoiding your feelings&lt;/h3&gt;&lt;p&gt;Given that it’s something we do every day, you’d think we’d be taught how to communicate with each other, but sadly no, that’s not the kind of schooling most of us go through. And so it is that you learn to keep your discomfort to yourself, instead indulging in heaping judgements upon the “perpetrator”, the person “responsible” for your frustrations.&lt;/p&gt;&lt;p&gt;If you’re conditioned to be “nice”, you might keep the judgements to yourself, just swilling them about in your mind. But if you’ve fully internalised the judgement that you’re a “bad” person as I had, you might choose to air them out (which dovetails neatly with the “masculine” trait of being “direct” regardless of the consequences), or worse, stockpile them and weaponise them during fights.&lt;/p&gt;&lt;p&gt;No surprise then that my interpersonal relationships suffered greatly in my twenties, people taken aback by the harshness of my judgements while receiving no feedback whatsoever how/whether I valued their presence in my life, words that perhaps might have offset the judgements.&lt;/p&gt;&lt;p&gt;But I reserved the harshest judgements for a special recipient - me. Try as I did to convince myself that I felt no emotions, that I was an automaton, my bipolar depressive episodes came and went, and a nasty inner voice unleashed a vortex of judgements upon myself. On the days when I was a crying mess, unable to get out of bed, I&amp;rsquo;d tell myself that I was being “lazy”, “ungrateful” for all the choices, comforts and things I had that others did not (third-world survivor’s guilt much?)&lt;/p&gt;&lt;h3 id=&#34;enter-nvc&#34;&gt;Enter NVC&lt;/h3&gt;&lt;p&gt;A few years ago, I joined a group that met up weekly to practise &lt;a href=&#34;https://puddledancer.bookstore.ipgbook.com/nonviolent-communication--a-language-of-life--3rd-edition-products-9781892005281.php&#34;&gt;nonviolent communication (NVC)&lt;/a&gt;, the communication framework developed by Marshall Rosenberg.&lt;/p&gt;&lt;p&gt;NVC prescribes a &lt;a href=&#34;https://allpoetry.com/out-beyond-ideas&#34;&gt;Rumi-esque&lt;/a&gt; judgement-free approach to communication that goes beyond right or wrong, asking its practitioners to instead focus on&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Observations - Given a situation, who said/did what?&lt;/strong&gt;&lt;br/&gt;If the incident were on video, what would it show, making sure to avoid interpretations/evaluations of the incident (“He arrived 30 minutes after the agreed-upon meeting time” vs “He disrespected me”).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Feelings - What do you feel?&lt;/strong&gt;&lt;br/&gt;Without &lt;em&gt;resisting&lt;/em&gt; or &lt;em&gt;judging&lt;/em&gt; your feelings as positive, negative, productive or unhelpful, acknowledge the feelings you&amp;rsquo;re experiencing, whether that’s happiness, anger, jealousy, excitement, regret, or disappointment. NVC calls out false feelings such as “disrespected” as interpretations. See &lt;a href=&#34;https://casra.org/cms/upload/eventdatesdetail/docs/208/nvc-feelingslist.pdf&#34;&gt;feelings inventory&lt;/a&gt; if you need a little prompting.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Needs - Taking stock of your feelings, what needs of yours were met or not met?&lt;/strong&gt;&lt;br/&gt;NVC posits that underneath each feeling lies a set of needs that is met or not met. In case of a person showing up 30 minutes after the agreed-upon meeting time, you might feel angry because your needs for consideration and communication were not met. And/or disappointed because your needs for (self-)organisation and structure were not met. See &lt;a href=&#34;https://casra.org/cms/upload/eventdatesdetail/docs/312/needs-inventory.pdf&#34;&gt;needs list&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Requests/actions - Based on the awareness of your unmet neds, are there any requests you’d like to make of the other person to have your needs met?&lt;/strong&gt;&lt;br/&gt;Knowing your unmet needs, are there requests you’d like to make of the other person so that your needs can be met?&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;(I’ve described these steps from the perspective of one’s own feelings and needs, but the process involves listening to everyone involved with empathy, devoid of preconceptions and judgements, and acknowledging their feelings and needs).&lt;/p&gt;&lt;p&gt;At first, I felt awkward (and often struggled) to talk about my feelings in the group, let alone talk about my feelings and needs with others who were unaware of NVC (Try being the only one on Slack typing out sentences in the format &amp;ldquo;When you did X, I felt Y because my need for Z was [not] met&amp;rdquo; 😅). But over months and years, the awkwardness has faded away and in its place is a deep sense of gratitude for this communication framework. A friend once joked that I sounded like I&amp;rsquo;d joined a cult because of the number of times I mentioned NVC in one conversation 😅&lt;/p&gt;&lt;h3 id=&#34;conflict-as-opportunity&#34;&gt;Conflict as opportunity&lt;/h3&gt;&lt;p&gt;Now that I am no longer running away from my feelings, I look at conflict as an opportunity to deepen interpersonal relationships and work together to meet our needs, whether with friends, family or at work.&lt;/p&gt;&lt;p&gt;To use a recent example, I&amp;rsquo;m currently volunteering at a farm. I arrived here after establishing with the owner that I have a theoretical understanding of permaculture and its concepts from watching videos and reading books, and want to gain practical experience.&lt;/p&gt;&lt;p&gt;When we were working on a swale (a permaculture technique of digging a trench on a slope to capture rainwater and prevent soil erosion), I said to the owner, &amp;ldquo;Hey, my understanding from watching &lt;a href=&#34;https://www.youtube.com/watch?v=W-f2genlUZI&#34;&gt;Geoff Lawton&amp;rsquo;s videos on swales&lt;/a&gt; is that trees are essential to swales and yet we don&amp;rsquo;t seem to have any here. Why are we doing things differently?&amp;rdquo;&lt;/p&gt;&lt;p&gt;She replied, &amp;ldquo;Open your mind! The design of swales varies depending on the terrain and the elements involved&amp;rdquo;.&lt;/p&gt;&lt;p&gt;The words &amp;ldquo;Open your mind&amp;rdquo; rang in my ears, and I wasn&amp;rsquo;t sure how I felt in the moment. But I stayed silent, continued to work on the swale according to her instructions, checked in with myself and recognised that I felt&amp;hellip;.irritated. Why? Because her words &amp;ldquo;Open your mind&amp;rdquo; carried the implication that I wasn&amp;rsquo;t open to swale designs other than the ones I was familiar with. I thought about it and realised that I was irritated because my need for clarity (over her swale design) was unmet.&lt;/p&gt;&lt;p&gt;Some fifteen minutes after the incident, I decided to address it. I followed the four steps of NVC and said, &amp;ldquo;When you said &amp;lsquo;open your mind&amp;rsquo; in response to my question about why we aren&amp;rsquo;t planting any trees, I felt irritated because my need for clarity was not met. I still don&amp;rsquo;t know why we didn&amp;rsquo;t plant trees. Are you willing to explain that?&amp;rdquo;. To her credit, she acknowledged that she&amp;rsquo;s open to questions, that saying &amp;ldquo;open your mind&amp;rdquo; didn&amp;rsquo;t add to the discussion, and proceeded to explain her thinking behind the swale design! I felt relieved that the conversation had met my need for clarity, and happy and reassured that she was willing to explain her thinking in detail. This factored into my decision to stay on at the farm longer than I&amp;rsquo;d initially planned.&lt;/p&gt;&lt;h3 id=&#34;bonus-no-more-self-flagellation&#34;&gt;Bonus: No more self-flagellation&lt;/h3&gt;&lt;p&gt;Another profound change since I started incorporating NVC into how I talk with others is that I (mostly) no longer have a nasty inner voice. Even when I’m the midst of a depressive episode and not quite myself, I can acknowledge what I’m feeling, enumerate my needs and give myself grace to rest, or entertain myself with mindless TV. Gone are the days of wielding the whip of judgements!&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Travelling while brown: Mexico visa edition</title>
       <link>https://vinaygopinath.me/2022/03/travelling-while-brown-mexico-visa-edition/</link>
       <pubDate>Tue, 29 Mar 2022 12:37:16 +0300</pubDate>
       
       <guid>https://vinaygopinath.me/2022/03/travelling-while-brown-mexico-visa-edition/</guid>
       <description>&lt;p&gt;Wondering how to apply for a temporary resident visa to Mexico as a person of colour in a &amp;ldquo;third world country&amp;rdquo; (Kenya) as a citizen of a &lt;em&gt;different&lt;/em&gt; third world country (India)? Buckle up! This guide will lay out the dos and donts, with detailed instructions on how best to avoid rejection. Failing that, it will remind you your place in the world.&lt;/p&gt;&lt;h2 id=&#34;shed-your-self-respect&#34;&gt;Shed your self-respect&lt;/h2&gt;&lt;p&gt;This is the most important step in this (and any) visa application process.Find a small cardboard box (A dirty shoebox will do) and place your self-respect and expectations of civility and equality in it. If it&amp;rsquo;s been a while since your last visa application, your self-respect may have regrown and it may be hard to fit it in the box. You can reclaim your self-respect from the box when the visa process is done, but it&amp;rsquo;s best if you just throw away the box.&lt;/p&gt;&lt;h2 id=&#34;read-the-visa-rules-carefully&#34;&gt;Read the visa rules carefully&lt;/h2&gt;&lt;p&gt;Go to the consulate website and read the visa page. Take care to note the requirements for your type of visa. In case of the temporary resident visa, the consulate requires&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Proof of residency in the country you&amp;rsquo;re applying from&lt;/li&gt;&lt;li&gt;Bank account statements for the last six months&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;double-check-with-the-consulate&#34;&gt;Double-check with the consulate&lt;/h2&gt;&lt;p&gt;Just to be sure you don&amp;rsquo;t get your hopes up of getting a visa after reviewing the website, contact the consulate, stating the visa you&amp;rsquo;re interested in and confirming the documents you plan to apply with. This will almost certainly be met with an automated reply linking you to the very website you got all the information from but if you&amp;rsquo;re lucky, you might get a passive-aggressive response from a human linking you to the website instead! 😍&lt;/p&gt;&lt;p&gt;This is a good reminder to temper your expectations and practise stoicism - remember, nothing in this world is for sure, not least the possibility of your getting a visa despite meeting all the requirements.&lt;/p&gt;&lt;h2 id=&#34;you-want-privacy-what-are-you-a-terrorist&#34;&gt;You want privacy? What are you, a terrorist?!&lt;/h2&gt;&lt;p&gt;Take a stab in the dark, and send an email to the consulate with all your documents and ask if they&amp;rsquo;re in order.You may not know who (or how many people) have access to the email, but it doesn&amp;rsquo;t matter - people who have nothing to hide don&amp;rsquo;t expect their bank statements and the personal transactions they contain to be private. Privacy is for &lt;em&gt;other&lt;/em&gt; people who distinctly don&amp;rsquo;t look like you.&lt;/p&gt;&lt;h2 id=&#34;expect-the-unexpected&#34;&gt;Expect the unexpected&lt;/h2&gt;&lt;p&gt;You didn&amp;rsquo;t think that the requirements you saw on the website would be everything, did you? No, of course not. You know better than that - There are always hidden requirements! It would be no fun if consulates listed out all of them on their websites or responded to your emails/phone calls with the real list of requirements.&lt;/p&gt;&lt;p&gt;I followed the requirements on the website and emailed the consulate a bank statement for the last six months, generated by the online statement generation tool of my international bank (Standard Chartered) only to be told that I need to submit a statement for the last &lt;em&gt;twelve&lt;/em&gt; months. Surprise!&lt;/p&gt;&lt;p&gt;What if you switched banks eight months ago and cannot obtain a bank statement from your previous bank? No problem, the consulate has you covered - Just submit a letter from your employer instead, as well as your payslips for the last six months. Just to be sure that you&amp;rsquo;re not making up an entire organization, its payroll system, contact information, and address, make sure to include the passport bio-data page of the &lt;em&gt;person who signs the letter&lt;/em&gt; confirming that you work for the organization. Easy peasy!&lt;/p&gt;&lt;h2 id=&#34;strengthen-your-bond-with-your-boss&#34;&gt;Strengthen your bond with your boss&lt;/h2&gt;&lt;p&gt;You may not have read this in your run-of-the-mill &amp;ldquo;How to be a manager&amp;rdquo; book, but the true sign of a healthy relationship with your manager is having a copy of their passport bio-data page! After all, nothing says &amp;ldquo;I trust you&amp;rdquo; than sharing personal IDs with your reports.&lt;/p&gt;&lt;p&gt;I reached out to my boss and explained my predicament and how I was running out of time before some upcoming travel plans that would take me out of Kenya, and in the course of a day, she shared a signed letter with the organization letterhead stating my job role and tenure at the org, as well as my salary and &lt;em&gt;her&lt;/em&gt; passport details. She also included a photo of passport bio-data page! In unrelated news, my current role at Maisha Meds has been the longest of my career.&lt;/p&gt;&lt;h2 id=&#34;embrace-radical-candour&#34;&gt;Embrace radical candour&lt;/h2&gt;&lt;p&gt;Think you&amp;rsquo;re an open book? Consulates have a new free service to aid you in your journey towards simple, transparent living - Doxxing your salary!&lt;/p&gt;&lt;p&gt;Remember that signed letter from my boss, with the organization letterhead? It contained an email. A public-facing email address that we share with external parties, which forwards all emails to five people across the org. See where I&amp;rsquo;m going with this?&lt;/p&gt;&lt;p&gt;To make sure that I did not make up an organization, payslips from the org, a signed letter from my boss and a photo of her passport, the consulate decided to contact that public email address. It included my payslips for the last six months in the very first email and asked for verification.&lt;/p&gt;&lt;p&gt;Understandable, really. You can never be too sure of the legitimacy of documents these days.&lt;/p&gt;&lt;h2 id=&#34;encryption-you-say-stamps-and-signatures-on-paper-are-the-true-sign-of-security&#34;&gt;Encryption, you say? Stamps and signatures on paper are the true sign of security&lt;/h2&gt;&lt;p&gt;Speaking of legitimate documents, don&amp;rsquo;t forget to get your bank statements and your payslips &lt;del&gt;doodled on&lt;/del&gt; &lt;del&gt;beautified&lt;/del&gt; &lt;del&gt;bedazzled&lt;/del&gt; certified by professionals - bank managers and your org&amp;rsquo;s finance team respectively. Everyone knows that even a truly hardened criminal who has found a way to forge the bio-data page of a passport &lt;em&gt;and&lt;/em&gt; set up a fake organization website and Google Workspace to intercept emails sent to its public-facing email address to confirm the legitimacy of their payslips cannot crack the certification rubberstamps of a bank.&lt;/p&gt;&lt;p&gt;To the consulate&amp;rsquo;s credit, it always referred to &amp;ldquo;certified bank statements&amp;rdquo; and &amp;ldquo;certified payslips&amp;rdquo;, but I did not pick up on that until it was explicitly flagged, in the seventh round of correspondence with the consulate. No problem though, all it took was a visit to a bank branch and reaching out to my very accommodating mostly-remote colleague who, recognizing the limited time I had to get a visa before my travel, invited me to drop by their house with printouts of my payslips so they could quickly sign the documents. Download a scanner app, take photos of a lot of pages adorned with signatures and stamps and voilà, certification complete!&lt;/p&gt;&lt;h2 id=&#34;practise-patience&#34;&gt;Practise patience&lt;/h2&gt;&lt;p&gt;So you&amp;rsquo;ve submitted all your documents, and the consulate has finally confirmed that they&amp;rsquo;re all in order, but there has been radio silence since then. Time for another reminder that the only thing you can control in this world is your mind (and some of us need medication to have some semblance of control). Never mind that it has been more than a month since you established contact with the consulate. Or that you only have two weeks before your travel plans that will keep you out of the country for a month.&lt;/p&gt;&lt;p&gt;Don&amp;rsquo;t even think about emailing the consulate informing them that you don&amp;rsquo;t live in Nairobi, the city the consulate is located in, and that you would appreciate their accommodating a visa appointment before your upcoming travel plans. Not unless you want a generic response with a link and a quote from the website stating that the processing of documents can take up to 10 days from the submission of all documents. &lt;em&gt;This&lt;/em&gt; time, the word on the website is the law.&lt;/p&gt;&lt;h2 id=&#34;know-your-priorities&#34;&gt;Know your priorities&lt;/h2&gt;&lt;p&gt;What would you do if a consulate emails you at 1pm &lt;em&gt;informing&lt;/em&gt; you that your appointment had been scheduled for 9am the following day? If you answered, &amp;ldquo;I&amp;rsquo;d emphatically accept and show up the next day, because I&amp;rsquo;d put the rest of my life on hold for the visa appointment anyway&amp;rdquo;, you are clearly a person who has accepted your place in the world. I had not, especially as I had already travelled to Rwanda at the time, so I replied that &amp;ldquo;9am tomorrow would not work for me&amp;rdquo; and requested an appointment at the end of March, when I expected to be in Nairobi for a few days.&lt;/p&gt;&lt;h2 id=&#34;are-we-sure-that-samsara-is-_not_-about-obtaining-a-visa&#34;&gt;Are we sure that samsara is &lt;em&gt;not&lt;/em&gt; about obtaining a visa?&lt;/h2&gt;&lt;p&gt;Maybe Lumbini had restrictive visa policies?&lt;/p&gt;&lt;p&gt;After following up a second time, the consulate wrote back to inform me that my visa appointment had been scheduled for the first week of &lt;em&gt;April&lt;/em&gt;. You&amp;rsquo;d think seeking an appointment a full 25+ days in advance for the end of March would ensure that appointments were still open and you&amp;rsquo;d be able to get the date that works best for you (hard to imagine a clamour among folks in Kenya to travel to Mexico), but it is not your allotment in life to question the decisions beyond your control. Well, maybe sometimes it is, as I did, asking if the consulate could accommodate my preference for end of March. It worked - I was informed that my appointment had been set for the date I requested.&lt;/p&gt;&lt;p&gt;Was it that &lt;em&gt;simple&lt;/em&gt;, you ask? No, of course not. The consulate informed me that because my visa appointment would be a full month after the date it had initially &lt;em&gt;set&lt;/em&gt; for me, I would need to submit fresh bank statements and payslips. All certified of course.&lt;/p&gt;&lt;h2 id=&#34;will-this-ever-end&#34;&gt;Will this ever end?&lt;/h2&gt;&lt;p&gt;We&amp;rsquo;re getting there, I promise. Long story short, I made it to my appointment with freshly signed and stamped payslips and bank statements, passport copies, signed letter from my employer (needed a new one, just in case I&amp;rsquo;d been fired in the month since I&amp;rsquo;d last emailed the consulate), visa application form, passport photos in a specific dimension (which weren&amp;rsquo;t needed in the end because a digital photo was taken at the consulate instead).&lt;/p&gt;&lt;p&gt;After two in-person visits to the consulate and additional documents to prove my &amp;ldquo;economic solvency&amp;rdquo;, I finally got a shiny temporary resident visa sticker in my passport!&lt;/p&gt;&lt;p&gt;I hope I&amp;rsquo;ve demonstrated how easy it is to apply for this visa! All it takes is a little bit of patience.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Goodbye Kisumu</title>
       <link>https://vinaygopinath.me/2022/03/goodbye-kisumu/</link>
       <pubDate>Fri, 04 Mar 2022 16:12:13 +0300</pubDate>
       
       <guid>https://vinaygopinath.me/2022/03/goodbye-kisumu/</guid>
       <description>&lt;a href=&#34;https://vinaygopinath.me/images/blog/2022-03-04-goodbye-kisumu/lake-victoria.jpg&#34;&gt;  &lt;img src=&#34;https://vinaygopinath.me/images/blog/2022-03-04-goodbye-kisumu/lake-victoria.jpg&#34; alt=&#34;Early morning Lake Victoria boat ride&#34;&gt;&lt;/a&gt;&lt;p&gt;After two years of (mostly) staying put in Kisumu, I&amp;rsquo;m getting back on the road!&lt;/p&gt;&lt;p&gt;Of all the places in the world, I would not have guessed that I&amp;rsquo;d break my nomadic streak for life in Kisumu. I&amp;rsquo;m glad to have spent all this time in the slow pace of a small city - definitely dodged the intense lockdowns around the world.&lt;/p&gt;&lt;h2 id=&#34;things-ill-miss-from-my-life-in-kisumu&#34;&gt;Things I&amp;rsquo;ll miss from my life in Kisumu&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;walking down the quiet Okore Road or Ridoch Road as the sun sets and the heat of the day lets up&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;cycling or running along the Lake Victoria shore in the direction of Dunga beach, noticing the water hyacinths surrendering or gaining turf as the &amp;ldquo;seasons&amp;rdquo; change.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;grabbing a booth and working from Java House at West End Mall in the afternoons - Going to Java was the one COVID risk I allowed myself for the sake of keeping my sanity and having a reason to get out of the apartment. Debatable if I held on to my sanity, but it became such a comfort that I&amp;rsquo;d visit Java at least once a week, and get one of the few vegan/vegatarian options in the menu, so much so that the waiters knew exactly what I&amp;rsquo;d get. &amp;ldquo;Green smoothie and a garden salad?&amp;rdquo;. All I had to do was nod.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;the stunning views of the hills around Kisumu - I was lucky to be in a fifth floor apartment, and seeing the orange and purple hues of dawn over the hills was an experience.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;the one-hour (&lt;em&gt;minimum&lt;/em&gt;) wait times at most restaurants and how it teaches you to embrace Kisumu&amp;rsquo;s quirks and work with it - I&amp;rsquo;d call in my order an hour in advance and would have to wait &lt;em&gt;only&lt;/em&gt; 5 - 10 minutes when I got there 😛&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;rushing to Chandarana/Food Plus before closing time for groceries that cannot be found elsewhere in Kisumu (hummus, kombucha, vegan pesto/ice cream/wine, fresh tofu)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;being accosted by the same group of boda drivers outside West End Mall who know exactly where to drop you off, all jostling to be the one to make 50 shillings. I&amp;rsquo;ve been told that &lt;em&gt;real&lt;/em&gt; Kisumu folks pay only 20 shillings to go to most places, but 50 is the minimum price for foreigners.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;the long rains - the howling winds, the lightning storms over the lake, the sheer intensity of rain.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;the dodgy instant electric heaters - Can you really say you&amp;rsquo;ve lived life on the edge if you haven&amp;rsquo;t showered under an electric showerhead that instantly heats water, as you contemplate high-voltage electrocution and bodies charred beyond recognition? Just kidding, this probably belongs in the next section.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;the elaborate greetings. As someone familiar and very comfortable in the brusque, utilitarian, seemingly rude (by Western standards, not mine) ways of urban Indian customer service at grocery stores, and small shops and restaurants, the social ritual of greeting absolutely everyone before engaging with them initially drove me crazy (So &lt;em&gt;inefficient&lt;/em&gt;). This is a real transcript of a phone call from an unknown number&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Me: &amp;#34;Hello?&amp;#34;Person: &amp;#34;Hello, how are you?&amp;#34;Me: &amp;#34;I&amp;#39;m fine, how are you?&amp;#34;Person: &amp;#34;I&amp;#39;m fine too&amp;#34;*Awkward pauseMe: &amp;#34;Yes?&amp;#34;Person: &amp;#34;I&amp;#39;m calling you from DHL. There is a package at our office....&amp;#34;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But now? Kisumu has done a number on me. I was in India briefly last year, and I got strange looks because I started all interactions with &amp;ldquo;Hi, how are you?&amp;rdquo;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;making small talk with a stranger and discovering that they run a &amp;ldquo;ministry&amp;rdquo; on weekends and collections have been down in recent months. The odds that that has happened to me 2 - 3 times!&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;watching the sunset in the weirdly segregationist, melanin-free environs of Dunga Hill Camp on a Friday night, and greeting folks whom you&amp;rsquo;ve only ever seen with a bottle of beer in their hands (and presumably not their first for the evening). Alcoholism is a real thing, Kisumu!&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;taking a boda to the airport, and catching your flight even when you get to the airport 15 minutes before takeoff.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;a href=&#34;https://vinaygopinath.me/images/blog/2022-03-04-goodbye-kisumu/kisumu-kibos-road-hills.jpg&#34;&gt;  &lt;img src=&#34;https://vinaygopinath.me/images/blog/2022-03-04-goodbye-kisumu/kisumu-kibos-road-hills.jpg&#34; alt=&#34;View of the hills near Kibos Road, Kisumu&#34;&gt;&lt;/a&gt;&lt;h2 id=&#34;things-i-_wont_-miss&#34;&gt;Things I &lt;em&gt;won&amp;rsquo;t&lt;/em&gt; miss&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;The garbage burning and the filthy air (sometimes)! Don&amp;rsquo;t get me started (I&amp;rsquo;ve written about it before).&lt;/li&gt;&lt;li&gt;The drunk-driving! If you see someone driving past midnight on a Friday, I promise you they&amp;rsquo;re not sober. It blew my mind when I learnt that drunk-driving was just the norm in Kisumu.&lt;/li&gt;&lt;li&gt;Maybe it&amp;rsquo;s just me and my complete lack of social media and/or social skills, but it felt like there was absolutely nothing to do in Kisumu but to go to church or drink to get hammered. At this point in my life (and, I hope, always), I don&amp;rsquo;t need someone shouting at me that &amp;ldquo;the fiery depths of hell await you for your sinful ways&amp;rdquo; in the hope of scaring me into committing to a tithe, nor do I want to run away from an existential crisis by numbing my mind with alcohol (I prefer travelling, i.e, literally running away, instead). That meant..spending a lot of time reading and watching stuff online. And resentfully finding meetup.com and &lt;a href=&#34;http://afkenya.org/&#34;&gt;Alliance Française&lt;/a&gt; events in Nairobi&lt;ul&gt;&lt;li&gt;That said, I&amp;rsquo;m leaving with a tinge of regret that I didn&amp;rsquo;t use my time in Kisumu to travel to the places around it. I&amp;rsquo;ve been learning more about permaculture and natural farming lately, and after spending days trying to find a permaculture farm accessible to me that would be willing to give me a tour, I found one an hour&amp;rsquo;s drive away from Kisumu 🤦. I also don&amp;rsquo;t feel like I&amp;rsquo;ve seen much of Kakamega and beyond, not to mention Mount Elgon National Park!&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;The madenning chorus of church loudspeakers on the weekends, starting as early as 8AM and stretching all the way to about 6PM.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;exciting-times-for-kisumu&#34;&gt;Exciting times for Kisumu&lt;/h2&gt;&lt;p&gt;I&amp;rsquo;m cautiously optimistic about all the upcoming changes in Kisumu - A waterfront promenade is under construction, the Kisumu port is being revived, many parts of the city have been spruced up and new bicycle lanes created in preparation for the Africities summit in a few months, and the colonial-era train is finally functional again, if your definition of functional accommodates a train that averages about 20 km/h and takes 12 hours from Nairobi to Kisumu.&lt;/p&gt;&lt;p&gt;Depending on how the election in August ~is decided~ goes, the next President may have strong ties to Kisumu - Hopefully that translates to a greater focus on the region, if only for the purposes of voter appeasement.&lt;/p&gt;&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s next?&lt;/h2&gt;&lt;p&gt;I&amp;rsquo;m looking forward to doing a bit of travelling - Yes, it&amp;rsquo;s inconvenient to look up COVID restrictions and take PCR tests, but after a long time in Kisumu, the scale has tipped in favour of travelling despite the inconveniences. I feel silly that I haven&amp;rsquo;t seen much of east Africa in all this time in the region, so I have Rwanda on my mind.&lt;/p&gt;&lt;p&gt;What I&amp;rsquo;d really like from this year is to find a small house on a permaculture farm, in a part of the world with a mild climate, spending hours outdoors everyday, working on the farm alongside people willing to teach me their farming practices and put up with my remote work, somewhere I can go cycling or wandering off every weekend.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Larynx - A viable Linux TTS</title>
       <link>https://vinaygopinath.me/2021/06/larynx-a-viable-linux-tts/</link>
       <pubDate>Wed, 09 Jun 2021 10:44:19 +0300</pubDate>
       
       <guid>https://vinaygopinath.me/2021/06/larynx-a-viable-linux-tts/</guid>
       <description>&lt;p&gt;I spent some time over the weekend experimenting with &lt;a href=&#34;https://voice2json.org/&#34;&gt;voice2json&lt;/a&gt; and &lt;a href=&#34;https://rhasspy.readthedocs.io/en/latest/&#34;&gt;rhasspy&lt;/a&gt;, trying to set up a fully offline voice assistant system using &lt;a href=&#34;https://github.com/mozilla/DeepSpeech&#34;&gt;Mozilla DeepSpeech&lt;/a&gt; for speech recognition, a template file containing all known phrases and their mappings to intents, an intent recognizer and a local shell script to parse the recognized intent and invoke commands (think opening a website or folder when an intent is recognized). Rhasspy was easy to use and really fun. It&amp;rsquo;s amazing how far we&amp;rsquo;ve come in terms of open-source tools in the TTS/speech recognition space.&lt;/p&gt;&lt;p&gt;Along the way, I discovered &lt;a href=&#34;https://github.com/rhasspy/larynx&#34;&gt;Larynx&lt;/a&gt;, a TTS system for Linux with high-quality voices from &lt;a href=&#34;https://github.com/jaywalnut310/glow-tts&#34;&gt;Glow-TTS&lt;/a&gt; and others, with intonations that sound human. I&amp;rsquo;ve kept an eye on the &lt;a href=&#34;https://askubuntu.com/questions/53896/natural-sounding-text-to-speech&#34;&gt;Linux TTS&lt;/a&gt; space for years and have been disappointed by the limited consumer-use options. Often, the pre-trained TTS voices sound all too robotic for everyday use. I suppose that&amp;rsquo;s understandable given the dearth of open-source voice datasets (which is why projects like &lt;a href=&#34;https://commonvoice.mozilla.org&#34;&gt;Mozilla CommonVoice&lt;/a&gt; are so exciting!). It&amp;rsquo;s nice to have a pleasant pre-trained TTS model natively available on Linux.&lt;/p&gt;&lt;p&gt;My use case is to copy text in a browser/Thunderbird RSS article, hit a shortcut and have the TTS system read the selected text aloud so I can look away from the screen and just listen.&lt;/p&gt;&lt;h2 id=&#34;setup&#34;&gt;Setup&lt;/h2&gt;&lt;p&gt;I followed the &lt;a href=&#34;https://github.com/rhasspy/larynx#debian-installation&#34;&gt;Debian installation instructions&lt;/a&gt;, and downloaded and installed the &lt;code&gt;tts&lt;/code&gt;, &lt;code&gt;lang-en_us&lt;/code&gt; and &lt;code&gt;Harvard Glow TTS&lt;/code&gt; files.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# cd Downloads # (or /path/to/downloaded/deb/files)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt install ./larynx*.deb&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;tts-shortcut&#34;&gt;TTS Shortcut&lt;/h2&gt;&lt;p&gt;To create a shortcut that invokes Larynx on selected text, I added aliases in my &lt;code&gt;~/.bash_aliases&lt;/code&gt; file. They use &lt;code&gt;xclip&lt;/code&gt; to access clipboard and selection data. On Debian-based systems, you should be able to install it with &lt;code&gt;sudo apt install xclip&lt;/code&gt;.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Speak text passed as argument&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Usage: speak &amp;#34;This is a test&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;alias speak&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;larynx --voice harvard-glow_tts --interactive&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Speak clipboard text&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Usage: speak-clipboard&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;alias speak-clipboard&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;xclip -out -selection clipboard | speak&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Speak currently selected text&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Usage: speak-selection&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;alias speak-selection&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;xclip -out -selection primary | speak&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Under Settings &amp;ndash;&amp;gt; Keyboard on GNOME, I added a custom keybinding for Super+S to invoke &lt;code&gt;bash -i -c &amp;quot;speak-selection&amp;quot;&lt;/code&gt;. This lets me select any text and hit Super+S to invoke larynx&lt;/p&gt;&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://github.com/rhasspy/larynx&#34;&gt;Larynx [GitHub]&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://askubuntu.com/questions/167969/can-i-use-a-bash-alias-in-a-keyboard-shortcut/1087823#1087823&#34;&gt;Assign a keyboard shortuct to a bash alias [Stack Overflow]&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://askubuntu.com/questions/573663/command-to-copy-currently-selected-text/573688#573688&#34;&gt;Read the currently selected text using xclip [Stack Overflow]&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</description>
     </item>
   
     <item>
       <title>Jodi Sudoku</title>
       <link>https://vinaygopinath.me/2021/04/jodi-sudoku/</link>
       <pubDate>Tue, 13 Apr 2021 23:02:22 +0300</pubDate>
       
       <guid>https://vinaygopinath.me/2021/04/jodi-sudoku/</guid>
       <description>&lt;p&gt;Happy to share that I&amp;rsquo;m working on &lt;a href=&#34;https://jodisudoku.app&#34;&gt;Jodi Sudoku&lt;/a&gt;, an open-source (&lt;a href=&#34;https://choosealicense.com/licenses/agpl-3.0/&#34;&gt;AGPLv3&lt;/a&gt;), Sudoku progressive web app with the goal of implementing multiplayer support using WebRTC!&lt;/p&gt;&lt;p&gt;I&amp;rsquo;ve been following the adoption of &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API&#34;&gt;WebRTC&lt;/a&gt; (now supported on &lt;a href=&#34;http://iswebrtcreadyyet.com/legacy.html&#34;&gt;all major platforms&lt;/a&gt; except Safari, &lt;a href=&#34;https://onezero.medium.com/apple-is-trying-to-kill-web-technology-a274237c174d&#34;&gt;infamous for dragging its feet&lt;/a&gt;) and the decentralized web for years, but haven&amp;rsquo;t had the opportunity to work with it, so I decided to use it in a hobby project.&lt;/p&gt;&lt;h2 id=&#34;frontend&#34;&gt;Frontend&lt;/h2&gt;&lt;p&gt;The &lt;a href=&#34;https://github.com/vinaygopinath/jodi-sudoku&#34;&gt;frontend code&lt;/a&gt; uses the standard React + Typescript setup, with React Router and Redux. So far, the web app features&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Starting a new game with a level of difficulty of your choice&lt;/li&gt;&lt;li&gt;Keyboard and mouse/touch input&lt;ul&gt;&lt;li&gt;Changing the mode of entering values into cells - Choose a digit first, and then click a cell to enter the digit, or vice versa&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Responsive cross-platform layout, tested on Firefox and Chrome (desktop and mobile)&lt;/li&gt;&lt;li&gt;Undo and redo using redux store history&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I&amp;rsquo;m a big fan of internationalization and &lt;a href=&#34;https://www.vinaygopinath.me/2019/06/hey-big-tech-support-regional-languages/&#34;&gt;supporting regional languages&lt;/a&gt;, so the basic one-player implementation currently supports Kannada, English and Polish using react-i18n.&lt;/p&gt;&lt;h2 id=&#34;backend&#34;&gt;Backend&lt;/h2&gt;&lt;p&gt;The &lt;a href=&#34;https://github.com/vinaygopinath/jodi-sudoku-api&#34;&gt;backend&lt;/a&gt; is more esoteric - I&amp;rsquo;m interested in using Rails + WebSockets. While I primarily wear the Android hat at &lt;a href=&#34;https://maishameds.org/&#34;&gt;work&lt;/a&gt;, our backend is built with Rails (a framework I had not worked with before) and I&amp;rsquo;m interested in improving my Rails fu through this project and contributing more than just the occasional pull request. Rails has a thin wrapper over WebSockets called &lt;a href=&#34;https://guides.rubyonrails.org/action_cable_overview.html&#34;&gt;Action Cable&lt;/a&gt;, and I&amp;rsquo;m curious to see how it fares compared to Node ( + socket.io), which seems to be the internet&amp;rsquo;s go-to recommendation for scalable WebSockets. WebSockets is a great way to achieve peer discovery (your browser tab discovering peers and finding a way to &lt;a href=&#34;https://www.html5rocks.com/en/tutorials/webrtc/infrastructure/&#34;&gt;establish a direct connection despite NAT traversal&lt;/a&gt;), required for WebRTC. If there&amp;rsquo;s a lot of traffic and Action Cable/Rails struggles under the load, I&amp;rsquo;m aware I might need to swap out Action Cable for Node someday, but I think the Rails experimentation will be worth it. Besides, Node feels like home turf and it&amp;rsquo;ll be fun to try something different :)&lt;/p&gt;&lt;h2 id=&#34;multiplayer&#34;&gt;Multiplayer&lt;/h2&gt;&lt;p&gt;What&amp;rsquo;s with the idea of multiplayer Sudoku, you ask? Is there any interest in multiplayer Sudoku? I&amp;rsquo;m not sure - I found a few multiplayer games on different app stores, but I don&amp;rsquo;t think there&amp;rsquo;s a big community. This project is honestly just an excuse to find a way to play Sudoku with my mum - Playing Sudoku together is a ritual whenever I&amp;rsquo;m in Bangalore. With mugs of basil and ginger tea after dinner, and armed with pens at the kitchen table, we pair up (Jodi (ಜೋಡಿ) is the Kannada word for &amp;ldquo;pair&amp;rdquo;) on solving the Sudoku puzzle in the daily newspaper (remember those?), trying to get as many digits as possible but also explaining to each other how we &amp;ldquo;unlocked&amp;rdquo; the right digits. I miss that, and I see no reason why that should stop when I&amp;rsquo;m not in Bangalore :) I&amp;rsquo;d be happy if others find it useful as well.&lt;/p&gt;&lt;p&gt;The idea is to implement URL-based discovery of peers. Like &lt;a href=&#34;https://meet.jit.si/some-room-name&#34;&gt;Jitsi&lt;/a&gt; calls, users who open the same link will be able to connect to the server using WebSockets, receive information from the server on the users in the &amp;ldquo;room&amp;rdquo;, and then establish browser-to-browser WebRTC peer connections, making the server connection theoretically unnecessary after that point.&lt;/p&gt;&lt;p&gt;I&amp;rsquo;m thinking of building different multiplayer modes&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Cooperative: all players in a room collaboratively solve the same puzzle together&lt;/li&gt;&lt;li&gt;Challenge (same puzzle): all players in a room &lt;em&gt;individually&lt;/em&gt; solve the same puzzle, only aware of the number of empty/filled cells of other players.&lt;/li&gt;&lt;li&gt;Challenge (puzzle of same difficulty): All players in a room solve different puzzles of the same difficulty level, only aware of the number of empty/filled cells of other players. Could be fun to also add a &amp;ldquo;Peek player&amp;rsquo;s board&amp;rdquo; feature&lt;/li&gt;&lt;li&gt;Time challenge? Turn-based games? Some dastardly variant of Sudoku?&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&#34;q-privacy-and-fault-tolerance&#34;&gt;Q: Privacy and fault tolerance&lt;/h3&gt;&lt;p&gt;I&amp;rsquo;ve encountered a lot of flaky internet connections and I&amp;rsquo;d like Jodi Sudoku to be tolerant of and handle edge cases related to flaky connections, users having to refresh their tabs, abrupt drop-offs etc. Given that exploring WebRTC and peer-to-peer systems is one of the goals, it&amp;rsquo;s imperative that the server know as little as possible about users, rooms or the type and state of the game in a room.&lt;/p&gt;&lt;p&gt;If the server knows nothing about the state of the game in a room, who is the source of truth in case of failure? Should all players in a room store the game state locally (in the browser local/session storage), and send the most recent state (via WebRTC) to any player who re/joins a room? What happens if two players disagree on the most recent state of the game? Should rooms have a leader who is responsible for storing the game state and sharing it with users who enter the room? What happens if the leader&amp;rsquo;s connection goes down? I have struggled to square the two seemingly contradictory goals of privacy/decentralization, and maintaining a source of truth for fault recovery, and decided to move forward with making the server aware of users, rooms, and game states. Please reach out if you have any suggestions that would keep the server out of the loop after peer discovery.&lt;/p&gt;&lt;p&gt;My current approach is:&lt;/p&gt;&lt;h4 id=&#34;peer-discovery&#34;&gt;Peer discovery&lt;/h4&gt;&lt;ol&gt;&lt;li&gt;Abbi enters a room (a room is identified by the link, i.e, judisudoku.app/some-room)&lt;/li&gt;&lt;li&gt;Abbi establishes a WebSocket connection with the server.&lt;/li&gt;&lt;li&gt;Abbi receives the list of users in the room via WebSocket. The list does not have anyone except Abbi. The WebSocket connection with the server is kept open.&lt;/li&gt;&lt;li&gt;Ilana enters the room, connects to the server and receives the list of users. The WebSocket connection with the server is kept open.&lt;/li&gt;&lt;li&gt;Abbi and Ilana use a STUN server, bypass their NAT, and establish a peer-to-peer connection.&lt;/li&gt;&lt;/ol&gt;&lt;h4 id=&#34;gameplay&#34;&gt;Gameplay&lt;/h4&gt;&lt;ol start=&#34;6&#34;&gt;&lt;li&gt;Abbi and Ilana begin a cooperative game, notifying each other via WebRTC and notifying the server via WebSocket.&lt;/li&gt;&lt;li&gt;Abbi and Ilana send messages directly to each other via WebRTC when they enter values into cells. They would also need to passively notify the server of the state changes via WebSocket&lt;/li&gt;&lt;li&gt;The server passively stores the game state, doing nothing with it.&lt;/li&gt;&lt;/ol&gt;&lt;h4 id=&#34;new-playerrejoin&#34;&gt;New player/rejoin&lt;/h4&gt;&lt;ol start=&#34;9&#34;&gt;&lt;li&gt;Jaime joins the game late, and is announced to the other users by the server (the updated list of users is shared) via WebSocket&lt;/li&gt;&lt;li&gt;The server shares the latest state of the game to Jaime via WebSocket&lt;/li&gt;&lt;li&gt;Once connected to the other users via WebRTC p2p connections, Jaime follows step 7 to participate in the game.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Using the server as the single source of truth allows users who join a game midway or experience connection issues and rejoin the game to continue the (collaborative) game from the latest state (if other users have made changes). Other multiplayer modes may need to handle failure/rejoin scenarios differently.&lt;/p&gt;&lt;h2 id=&#34;next-steps&#34;&gt;Next steps&lt;/h2&gt;&lt;p&gt;I realise I&amp;rsquo;ve defined a lot of lofty technical ambitions for a simple Sudoku webapp, and I&amp;rsquo;d like to take this one step at a time. I&amp;rsquo;m planning to make small releases to keep up my motivation and get feedback from my target demographic of one - When it comes to hobby projects, I tend to be very excited during the design phase and in the early days of implementation, and lose interest and move on to the next shiny puzzle that needs solving when the problem is more or less &amp;ldquo;solved&amp;rdquo; in my head :)&lt;/p&gt;&lt;p&gt;The next release will have peer discovery and cooperative game mode.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>European fantasy</title>
       <link>https://vinaygopinath.me/2021/04/european-fantasy/</link>
       <pubDate>Sun, 11 Apr 2021 22:14:36 +0300</pubDate>
       
       <guid>https://vinaygopinath.me/2021/04/european-fantasy/</guid>
       <description>&lt;p&gt;For the first time in ten years, I marked one whole year (and counting) living in the same country thanks to Covid, and while I’ve enjoyed liberties in Kenya (open restaurants, only one major lockdown and few restrictions, few cases until recently) that many around the world are still waiting to get back, I find myself longing to escape to Europe.&lt;/p&gt;&lt;p&gt;Of course, the Europe in my mind is completely untouched by Covid, there are no restrictions, all art spaces are active, you can drink a beer by the river, it’s springtime and sunny, and you can travel as you please, because that’s how I last saw it :)&lt;/p&gt;&lt;p&gt;&lt;u&gt;Trigger warning&lt;/u&gt;: Privileged person whining about the hardships of others&lt;/p&gt;&lt;p&gt;The relentlessness of Kenyan inequality can be hard to bear at times - I’ve been trying to explore the streets of Kisumu by bike, venturing into neighbourhoods and streets I don’t usually take. Cycling by the lake wearing my $30 helmet and seeing parts of people’s houses flooded after heavy rains, and kids collecting water from filthy ditches or the lake in distinct yellow jerry cans while someone else washes their motorbike a few metres away makes me want to give up.&lt;/p&gt;&lt;p&gt;You’d think India would’ve inured me to the injustices of poverty and poor infrastructure, but I’ve realised India is quite good at ghettoising its poor, politely tucking them away in long-forgotten parts of towns and cities that are avoided by anyone who can afford to do so, certainly politicians. Or perhaps it’s more accurate to say the wealthy have managed to outspend, gentrify and shut out the poor from “their” neighbourhoods. The segregation seems less stark to me here in Kenya.&lt;/p&gt;&lt;p&gt;A few days after I moved into my apartment, someone rang the doorbell - It was a shy young woman, probably in her late teens. She seemed very uncomfortable and struggled to say, “I would be grateful to you if I could have any work to do in your house - cleaning the floor, and doing your dishes and laundry?”.&lt;/p&gt;&lt;p&gt;What was I to say? It looks like it’s common to have housekeepers in Kenya, but it makes me very uncomfortable, not just because of the risks of catching Covid and the dubious ethics of paying someone to clean up after me, but also I get nervous at the very thought of strangers coming into my private space and having nowhere to hide while they’re around, possibly for hours (The joys of living with anxiety!). With a lot of guilt and fully aware that I might be cutting off an opportunity for her to make a few hundred Kenyan shillings a week, I declined.&lt;/p&gt;&lt;p&gt;Such moral conundrums are an everyday occurrence - Last month, I stepped out to get some bananas from a nearby supermarket, and as I walked past an empty plot, I saw a woman sitting at a small folding table with no shade, selling eggs, fried meat and bananas. A child played in the dirt nearby. The sun was setting after a hot day, and the bananas were well on their way to brown. I felt torn - I had stepped out just to get bananas, and I could support the woman and her child by buying bananas from her knowing that the bananas would go bad within a day, or go to the supermarket and buy less ripe ones. I kept walking (with only a tiny bit of hyperventilating), but it was a tough choice. Everyday, very real tests of Kant’s categorial imperative take their toll. All the bananas I&amp;rsquo;ve bought since then have been from her though (and the nearly brown bananas must have just been due to a bad day - the bananas have been great since then)! :)&lt;/p&gt;&lt;p&gt;Is it possible to respect your own values and preferences and still find a way to help others that doesn’t involve paying for things or services you don’t need, or simply handing people a wad of cash?&lt;/p&gt;&lt;p&gt;I know it’s pure escapism, but I’m craving a respite from guilt for a bit. It would be a dream to be in a place where there might still be plenty of inequality but not the kind that leads to extreme starvation or deprives people of basic infrastructure or access to healthcare. Where the crunch of a snail on a bike path getting crushed under my bike wheels despite my best efforts to avoid it is the only source of guilt pangs. Where I’m not constantly aware of my privilege.&lt;/p&gt;&lt;p&gt;I never thought I’d think fondly of places where, among many xenophobic incidents, someone called me a terrorist, pointed at my blinking red bike light and made the gesture of a bomb blast :D&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>An organisation next door stopped burning garbage!</title>
       <link>https://vinaygopinath.me/2021/03/an-organisation-next-door-stopped-burning-garbage/</link>
       <pubDate>Tue, 09 Mar 2021 13:26:39 +0300</pubDate>
       
       <guid>https://vinaygopinath.me/2021/03/an-organisation-next-door-stopped-burning-garbage/</guid>
       <description>&lt;p&gt;I&amp;rsquo;ve written about the &lt;a href=&#34;https://www.vinaygopinath.me/2021/02/hey-kisumu-clean-up-your-air/&#34;&gt;pollution caused by poor waste management and garbage burning&lt;/a&gt; before. That was spurred by an organisation I live near that burnt all kinds of stuff (dry leaves, carton boxes, and sometimes industrial waste I&amp;rsquo;m sure) everyday!&lt;a href=&#34;https://vinaygopinath.me/images/blog/2021-03-09-organisation-garbage-burning/smoke.gif&#34;&gt;&lt;img src=&#34;https://vinaygopinath.me/images/blog/2021-03-09-organisation-garbage-burning/smoke.gif&#34; alt=&#34;Garbage burning in a nearby compound&#34;&gt;&lt;/a&gt;I was sick of having to keep my windows shut all the time, so I called them one day and reminded them that it was illegal to burn garbage according to &lt;a href=&#34;https://www.nema.go.ke/index.php?option=com_content&amp;amp;view=article&amp;amp;id=8&amp;amp;Itemid=142&#34;&gt;NEMA regulations&lt;/a&gt;. The person I spoke to claimed ignorance of any garbage burning within their compound, wanted to know how I knew of their (non-existent) burning, was I perhaps mistaken in thinking it was their compound burning garbage and not the next one, and eventually wanted to give me someone else&amp;rsquo;s phone number so I could talk to them about this issue. I refused, saying it wasn&amp;rsquo;t any of my business to talk to someone else, and that I just wanted to remind them that they shouldn&amp;rsquo;t be exposing everyone around them to fumes everyday. We exchanged phone numbers, and that was it. I wasn&amp;rsquo;t expecting much from the call, but&amp;hellip;it worked! They have since stopped burning garbage. I&amp;rsquo;m not sure what&amp;rsquo;s happening to all of it now (I sincerely hope they&amp;rsquo;re using a garbage service that recycles and/or uses an incinerator and that I haven&amp;rsquo;t just demonstrated NIMBY behaviour), but there&amp;rsquo;s one fewer compound I have to worry about as a source of smoke.&lt;/p&gt;&lt;p&gt;I&amp;rsquo;m surprised that all it took was a phone call! Had I known that before, I wouldn&amp;rsquo;t have spent weeks psyching myself up to call them, arming myself with facts and preparing responses to imagined questions, not to mention how to put pressure on them (social media? legal notice?) should the call fail to do anything :) Now that I know it can be as simple as making a phone call, I feel unleashed, looking for other opportunities to get organisations, as a start, in Kisumu to do better!&lt;/p&gt;&lt;p&gt;That said, I was somewhat ashamed of myself at one point during the call - The person on the phone wanted to know my phone number (I was passed through to them via the receptionist), and the first thought that came to my mind was &amp;ldquo;Am I putting myself in danger by sharing my phone number?&amp;rdquo;. Thanks to mpesa, it&amp;rsquo;s easy to look up anyone&amp;rsquo;s full name if you have their Kenyan phone number. Would the organisation find it easier or more cost-effective to shut me up than to deal with the daily garbage burning? I went ahead and gave them my phone number, but I had to remind myself that if it comes to it, I have the resources to deal with any problems the organisation might try to create for me. Made me think of environmentalists who risk their lives to wage year-long campaigns against &lt;a href=&#34;https://www.theguardian.com/world/2020/oct/23/south-african-environmental-activist-shot-dead-in-her-home&#34;&gt;mining giants&lt;/a&gt; and &lt;a href=&#34;https://www.reuters.com/article/us-brazil-indigenous-logging-trfn-idUSKBN27H16P&#34;&gt;illegal loggers&lt;/a&gt; - If a simple thing like calling someone out on garbage burning can make me feel at risk of harm (it&amp;rsquo;s hard to say if that was paranoia or a legitimate threat), the folks fighting the real battles must be constantly aware of the danger they&amp;rsquo;re in and yet find a way to put their fears aside and continue to do the work they do ♥&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Hey Kisumu, clean up your air!</title>
       <link>https://vinaygopinath.me/2021/02/hey-kisumu-clean-up-your-air/</link>
       <pubDate>Sun, 07 Feb 2021 20:09:12 +0300</pubDate>
       
       <guid>https://vinaygopinath.me/2021/02/hey-kisumu-clean-up-your-air/</guid>
       <description>&lt;p&gt;Kisumu is undergoing a major transformation - Preparing to host the Africities summit in &lt;strike&gt;November 2021&lt;/strike&gt; April 2022, the county government has been setting up public infrastructure, building footpaths (with rumours of bike paths! 😍), the construction of a new market complex (although the &lt;a href=&#34;https://www.kenyans.co.ke/news/58737-governor-gives-10000-traders-notice-vacate-public-land&#34;&gt;demolition of open-air markets&lt;/a&gt; before the launch of the new market has not gone down well with small-scale traders already hit hard by COVID) and even the revival of a colonial-era &lt;a href=&#34;https://www.standardmedia.co.ke/nyanza/article/2001399706/plans-to-revive-kisumu-railway-line-gather-pace&#34;&gt;railway line&lt;/a&gt;. I&amp;rsquo;m excited to see what the city will look like later this year!&lt;/p&gt;&lt;h3 id=&#34;the-air-pollution-problem&#34;&gt;The air pollution problem&lt;/h3&gt;&lt;p&gt;Kisumu&amp;rsquo;s air quality has gone completely unnoticed in this effort to &amp;ldquo;upgrade&amp;rdquo; the city. As I write this, &lt;a href=&#34;https://air.plumelabs.com/air-quality-in-Kisumu-NKB#ae15&#34;&gt;Kisumu&amp;rsquo;s air quality index&lt;/a&gt; is hovering at 99, considered poor in most parts of the world. I wish I had known when I was moving into an apartment building that with the nice views of the hills in the distance, I&amp;rsquo;d also have to deal with the noxious fumes of burning garbage from nearby compounds burning dry leaves, or a few days&amp;rsquo; worth of food, plastic and other waste. Running to the windows and closing them shut at the first whiff of burning garbage has become an unfortunate necessity. I&amp;rsquo;ve noticed I wake up with a sore throat or stuffy nose if I leave the bedroom window open all night.&lt;/p&gt;&lt;p&gt;Burning is the predominant form of waste management in Kenya, and notwithstanding &lt;a href=&#34;https://www.nema.go.ke/index.php?option=com_content&amp;amp;view=article&amp;amp;id=8&amp;amp;Itemid=114&#34;&gt;National Environment Management Authority&amp;rsquo;s claim&lt;/a&gt; that burning of waste must be undertaken in a licenced incinerator, dumping garbage in a heap (either in a designated corner within one&amp;rsquo;s own compound or in a public field) and setting fire to it is a common sight in the evenings (Not wanting to breathe in the fumes is my current excuse for giving up on running in the evenings). It&amp;rsquo;s not much better using a garbage collection service - The building uses one, and chatting with the garbage collector, I found out that the collected garbage does not undergo segregation of any kind and all of it is burnt (I have no reason to think he meant incinerated and not just burnt in an open field on the outskirts of town).&lt;/p&gt;&lt;p&gt;At a time when WHO estimates that &lt;a href=&#34;https://www.who.int/airpollution/infographics/Air-pollution-INFOGRAPHICS-English-1.1200px.jpg&#34;&gt;7 million people die every year due to air pollution&lt;/a&gt;, of which 1 million are from the African continent, it doesn&amp;rsquo;t seem to me like there&amp;rsquo;s a national conversation on air pollution. The adverse &lt;a href=&#34;https://academic.oup.com/bmb/article/68/1/143/421247&#34;&gt;effects of ambient air pollution&lt;/a&gt; are well established, and I think Kisumu has a great opportunity to address the pollution issue as the city continues to grow.&lt;/p&gt;&lt;h3 id=&#34;reality&#34;&gt;Reality&lt;/h3&gt;&lt;p&gt;According to the City of Kisumu&amp;rsquo;s own &lt;a href=&#34;https://www.kisumu.go.ke/wp-content/uploads/2019/08/Updated-KISWAMP-Feb-2018-.pdf&#34;&gt;Kisumu Integrated Solid Waste Management Plan (KISWaMP) strategy document (PDF)&lt;/a&gt;, garbage collection rates are abysmal - In a city that produces 385 tons of waste every day, only 25% is effectively collected. The figures are worse for household waste - Only 3.1% of household waste is collected for garbage disposal. While the strategy document defines lofty goals for 2020 and 2030 including planning ahead for the growth of the city and decommissioning the &lt;a href=&#34;https://www.standardmedia.co.ke/nyanza/article/2001355809/riddle-of-kachok-dumpsite-as-heaps-of-garbage-re-emerge&#34;&gt;Kachok dumpsite&lt;/a&gt;, its implementation is far from reality.&lt;/p&gt;&lt;h3 id=&#34;heres-an-idea&#34;&gt;Here&amp;rsquo;s an idea&lt;/h3&gt;&lt;p&gt;With a little financial help from international partners, I think Kisumu county could set up its own &lt;strong&gt;free&lt;/strong&gt; citywide garbage collection service. On its own, or with the help of community based organisations and nonprofits, the county administration can widen access to proper garbage disposal to all parts of the city. The strategy document reveals willingness to use a disposal service and to learn segregation methods, which can be taught through community outreach programs.&lt;/p&gt;&lt;p&gt;Given that &lt;a href=&#34;https://link.springer.com/article/10.1007/s12132-017-9316-1&#34;&gt;65 - 70% of waste in Kisumu is estimated to be decomposable, organic materials&lt;/a&gt;, composting would significantly reduce the volume of garbage to be processed and transported to incinerators or landfills.&lt;/p&gt;&lt;p&gt;Why?&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Clean air, water and soil: Improvement in the air quality, and reduction in the pollutants in the water (due to open dumping in the lake) and leachates in the soil would lead to better health outcomes and a lower healthcare expenditure burden on the county government over time.&lt;/li&gt;&lt;li&gt;Employment: 41% of people aged between 18 and 34 in Kisumu were unemployed, according to the &lt;a href=&#34;https://www.businessdailyafrica.com/bd/economy/nyeri-and-embu-top-list-of-counties-with-lowest-unemployment-rates-2281860&#34;&gt;2019 census data&lt;/a&gt;. That was &lt;em&gt;before&lt;/em&gt; COVID, so I can&amp;rsquo;t imagine how dire the situation must be nowadays (A few months ago, I saw about forty people queueing outside an office to apply for a security guard position!). Setting up a new waste management program, or supporting community based organisations in various estates/settlements of Kisumu could &lt;a href=&#34;https://www.myclimate.org/information/carbon-offset-projects/detail-carbon-offset-projects/kenya-waste-management-7190/&#34;&gt;create hundreds of jobs&lt;/a&gt;!&lt;/li&gt;&lt;li&gt;Fertilizer: Compost is a great fertilizer. Composting organic waste and selling the compost to farmers at a nominal rate and conducting programs to increase awareness of compost use would be a self-sustaining source of revenue for the county&amp;rsquo;s garbage collection service and hopefully lead to better yield for farmers?&lt;/li&gt;&lt;li&gt;Aesthetically pleasing: Assuming improved access leads to a reduction in illegal dumping of waste in public fields, trenches and highways, Kisumu would be able to position itself as a clean, progressive city with a long-term vision.&lt;/li&gt;&lt;/ul&gt;</description>
     </item>
   
     <item>
       <title>Hike for Mental Health</title>
       <link>https://vinaygopinath.me/2020/11/hike-for-mental-health/</link>
       <pubDate>Sat, 14 Nov 2020 19:16:42 +0300</pubDate>
       
       <guid>https://vinaygopinath.me/2020/11/hike-for-mental-health/</guid>
       <description>&lt;p&gt;&lt;a href=&#34;https://vinaygopinath.me/images/blog/nov-2020-hike-for-mental-health/hike-for-mental-health.jpg&#34;&gt;&lt;img src=&#34;https://vinaygopinath.me/images/blog/nov-2020-hike-for-mental-health/hike-for-mental-health.jpg&#34; alt=&#34;Western Kenya LBQT Feminist Forum meetup in Kakamega forest&#34;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;I attended the &amp;ldquo;Hike for mental health&amp;rdquo; event organised by Western Kenya LBQT Feminist Forum today.&lt;/p&gt;&lt;p&gt;I didn&amp;rsquo;t know Kisumu had an active grassroots-led LGBTQ group! When I came across the event on their &lt;a href=&#34;https://www.facebook.com/WkenyaLBQTfem/&#34;&gt;Facebook page&lt;/a&gt; announcing a hike in Kakamega forest followed by a &amp;ldquo;vent session&amp;rdquo;, I immediately reached out to them.&lt;/p&gt;&lt;p&gt;As a cis Indian man in Kisumu, I was a bit worried I would come across as an intruder at an event led by Kenyan women when I met the group at the pickup point, but everyone was welcoming and I felt comfortable pretty quickly. I talked to many folks as we hiked through the forest, falling back from the group as people started opening up about themselves. I&amp;rsquo;m so overwhelmed and touched by everyone&amp;rsquo;s openness and willingness to be vulnerable in the company of strangers - People spoke of taking in many queer people who were kicked out of their homes when their families found out that they were queer during COVID lockdown, even though they were financially stretched thin themselves, reaching out to willing pastors to encourage them to avoid preaching against homosexuality and alienating (or worse, vilifying) vulnerable individuals in their congregations, being accused of trying to &amp;ldquo;convert&amp;rdquo; a heterosexual adult and dragged to a police station (It breaks me to think of the violence they must have experienced &lt;a href=&#34;https://www.standardmedia.co.ke/nairobi/article/2000179966/i-was-raped-thrown-out-of-police-station-nairobi-victim-says&#34;&gt;at the hands of the police&lt;/a&gt;), the everyday brutality of verbal abuse from Internet strangers on social media for being unapologetically queer, and the desire to create a safe space, both physical and mental, and using hikes as a way to allow queer people to speak freely as the small group hopes to mobilise funds to rent a physical space for a safe space and LGBTQ wellness centre.&lt;/p&gt;&lt;p&gt;On a personal note, for as long as I remember, I have struggled with my mental state, experiencing anxiety attacks, extreme mood swings, and withdrawing from the world for weeks at a time. Early this year however, I finally met with a psychiatrist, received a diagnosis and have been on medication that has made me feel like I&amp;rsquo;ve unlocked what I imagine &amp;ldquo;others&amp;rdquo; must have enjoyed throughout their lives - a stable state of mind! In a way, it feels like I&amp;rsquo;ve spent this year in isolation, looking inward and building up my strength to engage with the world. I could not have come across Kisumu&amp;rsquo;s fledgling LGBTQ group at a better time - I feel ready to put my energy to use for causes that are important to me, and the prospect of joining and supporting people who have been able to do so much with limited resources makes me happy.&lt;/p&gt;&lt;p&gt;P.S: If you&amp;rsquo;re able to support the organisation financially or otherwise, please get in touch with the organisers through the contact information on the &lt;a href=&#34;https://www.facebook.com/WkenyaLBQTfem/&#34;&gt;Facebook page&lt;/a&gt;.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Fever Pitch</title>
       <link>https://vinaygopinath.me/2020/10/fever-pitch/</link>
       <pubDate>Sun, 25 Oct 2020 16:35:03 +0300</pubDate>
       
       <guid>https://vinaygopinath.me/2020/10/fever-pitch/</guid>
       <description>&lt;p&gt;I spent a couple of blissful months unaware of the happenings of the world, cut off from my muscle-memory visits to The Guardian, Hacker News, Google News and a dozen other websites as I wait for my code changes to build. When I first stopped reading the news, I thought I&amp;rsquo;d miss being out of the loop and &amp;ldquo;relapse&amp;rdquo; pretty quickly, but I managed to hold off for about three months. Towards the end of it, I dipped back every week or two, mostly to know how COVID was ravaging the world and whether I&amp;rsquo;d be allowed back into India (Has any other country ever shut down all international flights and refused entry to its own citizens?), just in case that became a necessity. I even managed to read more long-form articles (Thanks &lt;a href=&#34;https://aeon.co/&#34;&gt;Aeon&lt;/a&gt;!) and books, since there was a surprisingly big chunk of free time that I would have otherwise filled with news.&lt;/p&gt;&lt;p&gt;All of that came crashing down with Ruth Bader Ginsburg&amp;rsquo;s death last month, and the imminent US election. The anxiety of not knowing what was happening out there quickly exceeded the anxiety I felt when I tried to keep up with the news. I can&amp;rsquo;t think of a time when so much has hung in the balance of an election - The US is set to withdraw from the (non-binding!) Paris climate agreement in November, and those who wish to trample upon women&amp;rsquo;s rights are surely &lt;a href=&#34;https://www.theguardian.com/world/2020/oct/22/poland-rules-abortion-due-to-foetal-defects-unconstitutional&#34;&gt;emboldened&lt;/a&gt; by the recent &lt;a href=&#34;https://www.theguardian.com/world/2020/oct/22/us-trump-administration-signs-anti-abortion-declaration&#34;&gt;anti-choice declaration&lt;/a&gt; led by the US and of course, the coup de grâce, RBG&amp;rsquo;s replacement.&lt;/p&gt;&lt;p&gt;I&amp;rsquo;m really looking forward to cutting news out of my life again, but I cannot imagine skipping the news first thing in the morning for the next two weeks.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Ruth Bader Ginsburg</title>
       <link>https://vinaygopinath.me/2020/09/ruth-bader-ginsburg/</link>
       <pubDate>Thu, 24 Sep 2020 01:31:53 +0300</pubDate>
       
       <guid>https://vinaygopinath.me/2020/09/ruth-bader-ginsburg/</guid>
       <description>&lt;p&gt;I struggled with last weekend&amp;rsquo;s news of Ruth Bader Ginsburg&amp;rsquo;s death.&lt;/p&gt;&lt;p&gt;Even though I have nothing whatsoever to do with the US, I remember feeling grateful and wildly optimistic as I followed along RBG&amp;rsquo;s championing of &lt;a href=&#34;https://www.theguardian.com/us-news/2015/apr/28/ruth-bader-ginsburg-gay-marriage-arguments-supreme-court&#34;&gt;same-sex marriage rights&lt;/a&gt; at the US Supreme Court in 2015. For better or worse, America (&lt;a href=&#34;https://www.denverpost.com/2019/12/08/america-influence-waning-under-trump/&#34;&gt;yet?&lt;/a&gt;) wields enormous influence over the rest of the world, and that verdict seemed to pave the way for a lot of queer activism around the world. India&amp;rsquo;s Supreme Court striking down a section of a British-era law in 2018 that criminalized homosexual activities seemed to be a continuation of that wave of progress.&lt;/p&gt;&lt;p&gt;Reading RBG&amp;rsquo;s powerful dissents have often been a reprieve in an otherwise bleak decade, especially when I was in Poland as the &lt;a href=&#34;https://www.reuters.com/article/us-poland-abortion-idUSKBN1GZ2LP&#34;&gt;Polish government tried to pass a total ban&lt;/a&gt; on abortion. Sisyphean though they might have been given the conservative majority in the US Supreme Court in recent years, it was refreshing to know that there was still room in the world for well-reasoned counterarguments and respect for diversity.&lt;/p&gt;&lt;p&gt;I can&amp;rsquo;t help but wonder how devastating it must be to pass away knowing that everything you stood for, everything you worked towards for decades, was never at greater risk of being destroyed at the hands of totalitarians in all but name. Is that when you truly grasp that &amp;ldquo;every generation must fight the same battles again and again&amp;rdquo; and hope that you&amp;rsquo;ve nudged the needle of progress forward at least a little bit? I just read the progressive Kannada author U R Ananthamurthy&amp;rsquo;s &lt;a href=&#34;https://www.nytimes.com/2016/07/19/books/in-hindutva-or-hind-swaraj-a-warning-against-hindu-nationalism.html&#34;&gt;ಹಿಂದುತ್ವ ಅಥವಾ ಹಿಂದ್ ಸ್ವರಾಜ್?&lt;/a&gt;, his final work before his death in 2014 that chronicles the secular or religious Indian identity that newly independent India had to choose between in the last century, and how that planted the seed for Hindu nationalism in present-day Indian society. Written at a time when Modi&amp;rsquo;s election-winning vision of an unabashedly &lt;em&gt;Hindu&lt;/em&gt; nation found many takers, it reads like a breathless lament for the hard-fought progress that might soon be dismantled.&lt;/p&gt;&lt;p&gt;I know, &lt;em&gt;I know&lt;/em&gt; that we&amp;rsquo;re making progress on a lot of fronts, even if that sometimes just means having conversations about things that might have gone unnoticed a few years ago, but when I hear of the death of someone as pivotal as RBG to social change in recent memory, I find myself thinking that the walls are closing in as the steady drumbeat of fascism&amp;rsquo;s march to power across the world gets louder, and can&amp;rsquo;t help but feel despondent.&lt;/p&gt;&lt;p&gt;I suppose all you can do is pick your battles, stay the course and hope that it makes a difference.&lt;/p&gt;&lt;p&gt;Thank you, &lt;a href=&#34;https://www.youtube.com/watch?v=QvBdsFWUZrE&#34;&gt;Duchess of Krakenthorp&lt;/a&gt;&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Gardening during COVID</title>
       <link>https://vinaygopinath.me/2020/09/gardening-during-covid/</link>
       <pubDate>Tue, 22 Sep 2020 09:30:16 +0300</pubDate>
       
       <guid>https://vinaygopinath.me/2020/09/gardening-during-covid/</guid>
       <description>&lt;p&gt;Hello!&lt;/p&gt;&lt;p&gt;It&amp;rsquo;s been a while, and I&amp;rsquo;m trying to get back to blogging.&lt;/p&gt;&lt;p&gt;I&amp;rsquo;m alive and well, and I&amp;rsquo;ve spent most of this year in Kisumu. I was in Kisumu when countries started restricting international flights, and I just couldn&amp;rsquo;t decide whether to go to India or elsewhere, so I stayed put.&lt;/p&gt;&lt;p&gt;In hindsight, it was a great decision. I took over the office compound, which felt like an island, cut off from the realities and rapidly rising daily statistics of the outside world. The compound also has lots of space, and since I&amp;rsquo;ve always harboured ideas of living on a self-sustaining farm one day, I took up gardening.&lt;/p&gt;&lt;p&gt;The first order of business was to eliminate food waste from garbage. &lt;a href=&#34;https://www.standardmedia.co.ke/commentary/article/2001305795/why-we-must-urgently-stop-burning-our-waste&#34;&gt;Burning&lt;/a&gt; is the predominant method of waste management in Kenya, and it&amp;rsquo;s sadly quite common to see plumes of smoke in the evenings as people set their garbage on fire.&lt;/p&gt;&lt;p&gt;I remember reading about compost circles (but I can&amp;rsquo;t seem to find any reference to it online!) and decided to use one for composting. A compost circle is a circle that&amp;rsquo;s about half or one-foot deep (depending on the amount of food waste your household produces) that you fill with your food waste. If the circle is big enough, it should take you about 6 weeks to complete a full circle, by which time the first section of the circle should have turned into fresh compost. It&amp;rsquo;s a great, continuous source of compost for your plants&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://vinaygopinath.me/images/blog/2020-09-22-gardening/compost-circle.jpg&#34;&gt;&lt;img src=&#34;https://vinaygopinath.me/images/blog/2020-09-22-gardening/compost-circle.jpg&#34; alt=&#34;Digging a compost circle&#34;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;With food waste covered, it was time to make sure I had a source of fresh vegetables - I dug two patches of about 2m X 3m, and planted green beans (I found a dried-up plant elsewhere within the compound and used the dry, pearly black seeds in pods, unsure if they would grow, but grow they did), cucumbers and tomatoes. It&amp;rsquo;s astonishing how quickly beans grow and how resilient they are to poor watering and intense sunlight - While the tomato plants drooped and wilted if they weren&amp;rsquo;t watered abundantly everyday, the beans seemed to flourish. Within a month, the bean plants had flowered and produced sweet, fresh beans. I&amp;rsquo;d take a break after work meetings in the evenings to water the plants and snack on young beans fresh off the plant 😍&lt;/p&gt;&lt;p&gt;The cucumber plants started off well, but the leaves turned yellow and the tiny cucumbers whose growth I was so excited to monitor everyday died on the vine before they could ripen. I realised it was a mistake to plant them in a spot that received sunlight for at least eight hours everyday - While the beans and tomato plants loved the direct sunlight, I believe cucumbers like the shade. Talking to a friend about the cucumber situation, I learnt there are insects that burrow into the soil and destroy the roots of cucumber plants. I did not get to confirm this because the cucumber plants were already too far gone.&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://vinaygopinath.me/images/blog/2020-09-22-gardening/beans.jpg&#34;&gt;&lt;img src=&#34;https://vinaygopinath.me/images/blog/2020-09-22-gardening/beans.jpg&#34; alt=&#34;Fresh green beans and the late office dog Kike, patiently waiting for treats&#34;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;I purchased tomato seeds of a variety called &amp;ldquo;Rio Grande&amp;rdquo;. I&amp;rsquo;m not sure if it&amp;rsquo;s true of tomatoes in general or the variety I tried, but tomato plants need a lot of love! Careful, plentiful watering at a consistent schedule, trellis/support, and more susceptible to diseases.&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://vinaygopinath.me/images/blog/2020-09-22-gardening/tomatoes.jpg&#34;&gt;&lt;img src=&#34;https://vinaygopinath.me/images/blog/2020-09-22-gardening/tomatoes.jpg&#34; alt=&#34;Fresh tomatoes&#34;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Gardening is such a joy, and I&amp;rsquo;m so glad I had the opportunity and the space to experiment with it. For a few years now, I&amp;rsquo;ve been thinking about finding a piece of land somewhere, growing my own food, if only just to feed myself, and living the life of a hermit in an environmentally sustainable way. COVID lockdowns gave me an opportunity to test that lifestyle, and I&amp;rsquo;m more confident now that I can swing it!&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Hey Big Tech, Support Regional Languages!</title>
       <link>https://vinaygopinath.me/2019/06/hey-big-tech-support-regional-languages/</link>
       <pubDate>Sat, 15 Jun 2019 11:05:57 +0300</pubDate>
       
       <guid>https://vinaygopinath.me/2019/06/hey-big-tech-support-regional-languages/</guid>
       <description>&lt;p&gt;A few months ago, I bumped into someone who was using &lt;a href=&#34;https://en.wikipedia.org/wiki/Kannada&#34;&gt;Kannada&lt;/a&gt; as their system language on their iPhone. Curious to find out Android&amp;rsquo;s support for languages that are not English, I switched my system language to Kannada as well and was pleasantly surprised to find nearly everything on stock Android in Kannada. The app ecosystem on the other hand is a different story.&lt;/p&gt;&lt;p&gt;Uber and Ola, India&amp;rsquo;s leading taxi apps, don&amp;rsquo;t seem to care about regional language support - While Ola makes no attempt to support use in Kannada, Uber&amp;rsquo;s attempt at Kannada internationalization seems to be an afterthought, with entirely broken screens.&lt;/p&gt;&lt;div class=&#34;img-mobile&#34;&gt;  &lt;a href=&#34;https://vinaygopinath.me/images/blog/regional-lang/uber.png&#34;&gt;    &lt;img src=&#34;https://vinaygopinath.me/images/blog/regional-lang/uber.png&#34; alt=&#34;Uber Android app screenshot with broken Kannada internationalization&#34;&gt;  &lt;/a&gt;&lt;/div&gt;&lt;h3 id=&#34;why-support-regional-languages&#34;&gt;Why support regional languages?&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Barrier to entry&lt;/strong&gt; - As a person in tech, with friends who work in or take an interest in tech, it&amp;rsquo;s easy to forget that using technology is hard for a lot of people. Adding a language barrier into the mix makes it worse. English is intimidating to a lot of people, especially in India. Anecdotally, I&amp;rsquo;ve had relatives tell me that they&amp;rsquo;d really like to be able to use taxi apps to move around, but they&amp;rsquo;re afraid of selecting the wrong location or ordering the wrong class of taxi (resulting in memorising the flow of taps 🤦). There&amp;rsquo;s an argument to be made about better UI/UX, but I think interfaces that are exclusively in English become a barrier to entry.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;English is not inclusive&lt;/strong&gt; - The history of English in India is steeped in colonialism (well, duh!), &lt;a href=&#34;https://psmag.com/news/how-english-creates-a-new-caste-system-in-india&#34;&gt;casteism, and elitism&lt;/a&gt;. Walk around in any &amp;ldquo;second-tier&amp;rdquo; city in India, and you&amp;rsquo;ll see advertisements for a multitude of companies, schools and websites offering to teach you how to speak &amp;ldquo;fluent English&amp;rdquo;. The clamour to learn English is of course driven by its employment potential, but I&amp;rsquo;d argue that it has to do with caste and elitism as well. Indian society places a high value on English fluency, and apps are silently reinforcing the discrimination that comes with it.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Content availability is a problem&lt;/strong&gt; - There&amp;rsquo;s a classic chicken-and-egg (could not find an equivalent vegan expression!) issue at play - Platforms don&amp;rsquo;t have support for regional languages, and there&amp;rsquo;s not enough regional language content to make it worthwhile for platforms to support them. It looks like Amazon is yet to support &lt;a href=&#34;https://factordaily.com/kannada-books-kindle-vasudhendra/&#34;&gt;Kannada content on Kindles&lt;/a&gt;! For now, you have a choice from as many as TWO Kindle eBooks if you &lt;a href=&#34;https://www.amazon.in/Kannada-Books-Kindle-eBooks/s?i=stripbooks&amp;amp;bbn=5194527031&amp;amp;rh=n%3A5194527031%2Cp_n_binding_browse-bin%3A1634951031%2Cp_n_feature_three_browse-bin%3A9495762031&amp;amp;dc&amp;amp;qid=1560596634&amp;amp;rnid=9141481031&amp;amp;ref=sr_nr_p_n_feature_three_browse-bin_1&#34;&gt;search by language&lt;/a&gt;. TWO!&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;$$$$&lt;/strong&gt; - Okay, fine! I&amp;rsquo;ll stoop to appealing to capitalism - Companies are leaving money on the table by making their products and services inaccessible to a lot of people! Think about the growth potential, the happy investors, those beautiful graphs in upswing!&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Because squiggly letters are awesome! ದಯವಿಟ್ಟು ನಿಮ್ಮ ಫೋನ್ ಹಾಗು ಕಂಪ್ಯೂಟರ್-ಗಳಲ್ಲಿ ನಿಮ್ಮ ಭಾಷೆಯನ್ನು ಉಪಯೋಗಿಸಿ&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&#34;what-can-you-do&#34;&gt;What can you do?&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Strength in numbers - Don&amp;rsquo;t be an elitist, prop up regional language numbers by switching your system language on your device(s)&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://www.wikihow.tech/Change-the-Language-in-Android&#34;&gt;Change the language - Android&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://support.apple.com/en-us/HT204031&#34;&gt;Change the language - iPhone, iPad, or iPod Touch&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://support.google.com/accounts/answer/32047?co=GENIE.Platform%3DDesktop&amp;amp;hl=en&#34;&gt;Change the language - Google account&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Contribute translation strings to open-source projects! I started contributing Kannada translations to Signal Messenger&amp;rsquo;s &lt;a href=&#34;https://www.transifex.com/signalapp/signal-android/&#34;&gt;Android app&lt;/a&gt;, and it&amp;rsquo;s surprisingly fun and very satisfying to see the number of untranslated strings go down on every submit :)&lt;ul&gt;&lt;li&gt;Find open-source projects that could use your help at &lt;a href=&#34;https://hosted.weblate.org/projects/&#34;&gt;Weblate&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Join (or create) a GNOME internationalization team &lt;a href=&#34;https://l10n.gnome.org/teams/&#34;&gt;here&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Contribute to Mozilla&amp;rsquo;s &lt;a href=&#34;https://github.com/mozilla-l10n&#34;&gt;apps and websites&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Lobby governments to mandate the availability of regional languages (perhaps in addition to other languages?) on government websites. In 2019, governments paying third parties taxpayer money to build websites that lack regional language support is like building footpaths that are not wheelchair-friendly - egregious and just plain criminally negligent!&lt;/li&gt;&lt;/ul&gt;</description>
     </item>
   
     <item>
       <title>Commit to Master Branch on GitHub Using Travis CI</title>
       <link>https://vinaygopinath.me/2018/04/commit-to-master-branch-on-github-using-travis-ci/</link>
       <pubDate>Tue, 24 Apr 2018 11:57:08 +0200</pubDate>
       
       <guid>https://vinaygopinath.me/2018/04/commit-to-master-branch-on-github-using-travis-ci/</guid>
       <description>&lt;p&gt;I&amp;rsquo;m trying to turn my pet project to find visa requirements for couples, &lt;a href=&#34;https://nomadcouple.vinaygopinath.me&#34;&gt;Nomad Couple&lt;/a&gt;, into a full-fledged progressive web app (PWA) that has regularly updated visa requirements data (from Wikipedia) through a Travis CI cron job.&lt;/p&gt;&lt;p&gt;As part of that, I&amp;rsquo;ve set up the &lt;a href=&#34;https://github.com/vinaygopinath/visa-req-wiki-scraper&#34;&gt;wiki scraper repo&lt;/a&gt; to be rebuilt every month and any changes in the visa requirements for citizens of different countries (available in &lt;code&gt;dist/output&lt;/code&gt;) to be automatically committed back to the &lt;code&gt;master&lt;/code&gt; branch.&lt;/p&gt;&lt;h3 id=&#34;attempt-1---travis-github-pages-deployment&#34;&gt;Attempt #1 - Travis&amp;rsquo; Github Pages deployment&lt;/h3&gt;&lt;p&gt;I noticed that Travis CI offers automated &lt;a href=&#34;https://docs.travis-ci.com/user/deployment/pages/&#34;&gt;deployment to GitHub Pages&lt;/a&gt; via configuration in &lt;code&gt;.travis.yml&lt;/code&gt;. By default, it commits code to the &lt;code&gt;gh-pages&lt;/code&gt; branch, but the configuration has a &lt;code&gt;target_branch&lt;/code&gt; property to customize it. &lt;a href=&#34;https://github.com/vinaygopinath/visa-req-wiki-scraper/blob/698b7617bbc4ba2382efe57f263c428a5a685c87/.travis.yml&#34;&gt;This&lt;/a&gt; is the configuration I tried&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;language&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;node_js&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;node_js&lt;/span&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lts/*&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Unrelated configuration for node.js/electron project&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#ae81ff&#34;&gt;npm run scrape&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;deploy&lt;/span&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;provider&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;pages&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;skip-cleanup&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;target-branch&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;master&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# Commit to master instead of gh-pages&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;github-token&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;$GH_TOKEN&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;keep-history&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# By default, Travis uses push --force and wipes out commit history&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;verbose&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;on&lt;/span&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;branch&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;master&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;GH_TOKEN&lt;/code&gt; mentioned in the config refers to a token generated (with &lt;code&gt;public_repo&lt;/code&gt; permission) on the &lt;a href=&#34;https://github.com/settings/tokens&#34;&gt;GitHub personal access tokens page&lt;/a&gt; that I&amp;rsquo;ve saved on Travis CI as an &lt;a href=&#34;https://docs.travis-ci.com/user/environment-variables#Defining-Variables-in-Repository-Settings&#34;&gt;environment variable&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;While this was super easy to set up, it has a few issues.&lt;/p&gt;&lt;h4 id=&#34;drawbacks&#34;&gt;Drawbacks&lt;/h4&gt;&lt;ol&gt;&lt;li&gt;This approach is dependent on Travis CI&amp;rsquo;s GitHub Pages support. You&amp;rsquo;re out of luck if you&amp;rsquo;re using a different Git hosting provider or your own Git server.&lt;/li&gt;&lt;li&gt;Travis listens for commits on the &lt;code&gt;master&lt;/code&gt; branch and triggers a build. Since the commit at build time is pushed back to &lt;code&gt;master&lt;/code&gt;, this triggers an infinite build loop&lt;sup&gt;&lt;a href=&#34;https://github.com/travis-ci/travis-ci/issues/9329&#34;&gt;[1]&lt;/a&gt;&lt;/sup&gt;!&lt;/li&gt;&lt;li&gt;It&amp;rsquo;s &lt;a href=&#34;https://github.com/travis-ci/travis-ci/issues/9287&#34;&gt;not possible&lt;/a&gt; (as of Apr 2018) to customize the commit message. This rules out adding &lt;a href=&#34;https://docs.travis-ci.com/user/customizing-the-build#Skipping-a-build&#34;&gt;&amp;quot;[skip ci]&amp;quot;&lt;/a&gt; to the commit message to avoid the infinite loop.&lt;/li&gt;&lt;/ol&gt;&lt;h3 id=&#34;attempt-2---good-ol-shell-script&#34;&gt;Attempt #2 - Good ol&amp;rsquo; shell script&lt;/h3&gt;&lt;p&gt;Travis supports &lt;code&gt;after_success&lt;/code&gt;, a hook that is called when the build succeeds. I replaced the &lt;code&gt;deploy&lt;/code&gt; section in &lt;code&gt;.travis.yml&lt;/code&gt; above with:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;after_success&lt;/span&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#ae81ff&#34;&gt;sh .travis-push.sh&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;travis-push.sh&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/sh&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Credit: https://gist.github.com/willprice/e07efd73fb7f13f917ea&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;setup_git&lt;span style=&#34;color:#f92672&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  git config --global user.email &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;travis@travis-ci.org&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  git config --global user.name &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Travis CI&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;commit_country_json_files&lt;span style=&#34;color:#f92672&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  git checkout master&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# Current month and year, e.g: Apr 2018&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  dateAndMonth&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;date &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;+%b %Y&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# Stage the modified files in dist/output&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  git add -f dist/output/*.json&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# Create a new commit with a custom build message&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# with &amp;#34;[skip ci]&amp;#34; to avoid a build loop&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# and Travis build number for reference&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  git commit -m &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Travis update: &lt;/span&gt;$dateAndMonth&lt;span style=&#34;color:#e6db74&#34;&gt; (Build &lt;/span&gt;$TRAVIS_BUILD_NUMBER&lt;span style=&#34;color:#e6db74&#34;&gt;)&amp;#34;&lt;/span&gt; -m &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;[skip ci]&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;upload_files&lt;span style=&#34;color:#f92672&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# Remove existing &amp;#34;origin&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  git remote rm origin&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# Add new &amp;#34;origin&amp;#34; with access token in the git URL for authentication&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  git remote add origin https://vinaygopinath:&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;GH_TOKEN&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;@github.com/vinaygopinath/visa-req-wiki-scraper.git &amp;gt; /dev/null 2&amp;gt;&amp;amp;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  git push origin master --quiet&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;setup_git&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;commit_country_json_files&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Attempt to commit to git only if &amp;#34;git commit&amp;#34; succeeded&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; $? -eq &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;A new commit with changed country JSON files exists. Uploading to GitHub&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  upload_files&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;No changes in country JSON files. Nothing to do&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;TODO: If you&amp;rsquo;re using Travis on public GitHub repositories, your build log is publicly visible. If there are any Git related errors, it is possible that the origin URL (with your GitHub personal access token with access to ALL your public repositories) may be logged, which is a huge security risk. It is strongly recommended to redirect the output of all git commands to &lt;code&gt;/dev/null&lt;/code&gt; (e.g, &lt;code&gt;git push origin master --quiet &amp;gt; /dev/null 2&amp;gt;&amp;amp;1&lt;/code&gt;) once you&amp;rsquo;ve verified that the script works for your repo.&lt;/p&gt;&lt;p&gt;References:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Will Price&amp;rsquo;s &lt;a href=&#34;https://gist.github.com/willprice/e07efd73fb7f13f917ea&#34;&gt;GitHub Gist&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Justin Ellis&amp;rsquo; &lt;a href=&#34;https://jellis18.github.io/post/2017-12-03-continuous-integration-hugo/&#34;&gt;post&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</description>
     </item>
   
     <item>
       <title>Social media rich snippets with AngularJS and ngMeta</title>
       <link>https://vinaygopinath.me/2016/11/social-media-rich-snippets-with-angularjs-and-ngmeta/</link>
       <pubDate>Sat, 26 Nov 2016 12:22:06 +0100</pubDate>
       
       <guid>https://vinaygopinath.me/2016/11/social-media-rich-snippets-with-angularjs-and-ngmeta/</guid>
       <description>&lt;p&gt;&lt;a href=&#34;https://github.com/vinaygopinath/ngMeta&#34;&gt;ngMeta&lt;/a&gt;, my Angular1 SEO meta tags library, has frequently seen issues related to the preview snippets or rich snippets generated by Facebook, Twitter, Skype, Whatsapp and others when a URL of an Angular site using ngMeta is shared. I&amp;rsquo;ve &lt;a href=&#34;https://github.com/vinaygopinath/ngMeta/issues/16&#34;&gt;addressed it&lt;/a&gt; on Github &lt;a href=&#34;https://github.com/vinaygopinath/ngMeta/issues/20&#34;&gt;several times&lt;/a&gt;, so I thought I would explain the issue in greater detail here.&lt;/p&gt;&lt;p&gt;Sites like Facebook and Twitter use crawlers to fetch URLs and extract the &lt;a href=&#34;http://ogp.me/&#34;&gt;Open Graph&lt;/a&gt; meta values. When Open Graph data is not available, they fall back on the basic meta tags (&lt;code&gt;title&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt; and others). Social media crawlers generally do not execute Javascript, meaning that they pick up the values provided in meta tags as-is. For sites that use ngMeta, meta tags might look like this:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;title&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ng-bind&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ngMeta.title&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;title&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{{ngMeta.description}}&amp;#34;&lt;/span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;og:title&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{{ngMeta.title}}&amp;#34;&lt;/span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;og:description&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{{ngMeta.description}}&amp;#34;&lt;/span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As a result, the rich snippet generated by social media sites is not pleasant (Remember, no Javascript!):&lt;img src=&#34;https://i.imgur.com/wSNMYNF.png&#34; alt=&#34;Facebook rich snippet&#34;&gt;&lt;/p&gt;&lt;p&gt;However, Google search&amp;rsquo;s crawlers &lt;em&gt;do&lt;/em&gt; execute Javascript and the search result snippet uses the title, description and other tags set through ngMeta, as expected.&lt;/p&gt;&lt;p&gt;It is technically impossible for a front-end framework library like ngMeta to force crawlers to execute Javascript and pick up the meta content values set through Javascript. Instead, solutions include serving pre-rendered pages, or redirecting requests by crawlers to a specific service that serves a static page with meta tags relevant to the requested URL. For more on the latter, check out &lt;a href=&#34;https://github.com/michaelbromley/angular-social-demo&#34;&gt;angular-social-demo&lt;/a&gt; on GitHub.&lt;/p&gt;&lt;p&gt;Another option: If you&amp;rsquo;re willing to forsake the customization of meta tags for different pages to get rid of uninterpolated Angular expressions like &lt;code&gt;ngMeta.title&lt;/code&gt; in your social media snippets, consider adding fallback meta tags that provide a title, description and image for your site. These fallback meta tags must have &lt;code&gt;ng-if=&amp;quot;false&amp;quot;&lt;/code&gt; so that they are removed in a Javascript-enabled environment:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;title&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ng-bind&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ngMeta.title&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;title&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{{ngMeta.description}}&amp;#34;&lt;/span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;og:title&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Site name or other general title&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ng-if&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;og:description&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Site description&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ng-if&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;property&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;og:image&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://your-site.com/fallback-snippet-image.jpg&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ng-if&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Feel free to check out the source code of the &lt;a href=&#34;https://vinaygopinath.github.io/ngMeta/#/&#34;&gt;ngMeta demo site&lt;/a&gt;. I&amp;rsquo;ve just updated it with fallback meta tags.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Android app for Torun&#39;s city bike system</title>
       <link>https://vinaygopinath.me/2016/10/android-app-for-toruns-city-bike-system/</link>
       <pubDate>Fri, 21 Oct 2016 22:17:40 +0200</pubDate>
       
       <guid>https://vinaygopinath.me/2016/10/android-app-for-toruns-city-bike-system/</guid>
       <description>&lt;p&gt;I&amp;rsquo;m a big fan of Toruń, and I&amp;rsquo;ve spent a lot of time exploring the city over the last year. I&amp;rsquo;m also a big fan of cycling, so when Toruń&amp;rsquo;s city bike system relaunched in March after its winter hiatus, I took to cycling around the city. As an outsider, it wasn&amp;rsquo;t always easy locating the nearest bike station. Sometimes, I&amp;rsquo;d walk to a known station, only to find that there were no bikes. I couldn&amp;rsquo;t find an app to help me out with the city bike system, so I &lt;a href=&#34;https://play.google.com/store/apps/details?id=com.vinay.trm&#34;&gt;made one&lt;/a&gt; myself that uses the data available on the website.&lt;/p&gt;&lt;p&gt;The app uses the Google Maps Android API to show all the bike stations in the city as well as your current location. Using the Locate icon floating action button, you can locate the bike station that&amp;rsquo;s nearest to you from any location in the city.&lt;/p&gt;&lt;div class=&#34;img-mobile&#34;&gt;  &lt;a href=&#34;https://vinaygopinath.me/images/blog/trm-torun-app/home.png&#34;&gt;    &lt;img src=&#34;https://vinaygopinath.me/images/blog/trm-torun-app/home.png&#34; alt=&#34;TRM Torun Android app home screen&#34;&gt;  &lt;/a&gt;&lt;/div&gt;&lt;p&gt;Once you&amp;rsquo;ve identified a bike station, you can check the number of available bikes, and use the Navigate icon floating action button to get walking directions to the station from Google Maps. The app is capable of working offline, using a cache of bike stations, but, needless to say, realtime bike availability information needs a working internet connection.&lt;/p&gt;&lt;div class=&#34;img-mobile&#34;&gt;  &lt;a href=&#34;https://vinaygopinath.me/images/blog/trm-torun-app/bike-info.png&#34;&gt;    &lt;img src=&#34;https://vinaygopinath.me/images/blog/trm-torun-app/bike-info.png&#34; alt=&#34;TRM Torun Android app bike station information&#34;&gt;  &lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Demo&lt;/strong&gt;&lt;/p&gt;&lt;iframe class=&#34;embed-center&#34; width=&#34;450&#34; height=&#34;800&#34; src=&#34;https://www.youtube.com/embed/5bBT3z-xNIk&#34; frameborder=&#34;0&#34; allow=&#34;autoplay; encrypted-media&#34; allowfullscreen&gt;&lt;/iframe&gt;&lt;p&gt;&lt;strong&gt;Technical details&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;The app supports Android 4.0.3 (API Level 15 - Ice cream sandwich) and above, covering 98.6%+ of all Android devices &lt;sup&gt;&lt;a href=&#34;https://developer.android.com/about/dashboards/index.html&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. The context-aware floating action button, sliding bottom sheet and the themed action bar are based on &lt;a href=&#34;https://developer.android.com/design/material/index.html&#34;&gt;material design&lt;/a&gt; guidelines. The app is compatible with the &amp;ldquo;new&amp;rdquo; permission system applicable to Android 6 and above: Devices running Android 6 (Marshmallow) and above use runtime permission requests, while all permissions must be granted at installation time on devices running older versions of Android.&lt;/p&gt;&lt;p&gt;Check out TRM Torun on &lt;a href=&#34;https://play.google.com/store/apps/details?id=com.vinay.trm&#34;&gt;Google Play&lt;/a&gt;&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Overheard in Łódź</title>
       <link>https://vinaygopinath.me/2016/08/overheard-in-%C5%82%C3%B3d%C5%BA/</link>
       <pubDate>Thu, 25 Aug 2016 19:59:36 +0200</pubDate>
       
       <guid>https://vinaygopinath.me/2016/08/overheard-in-%C5%82%C3%B3d%C5%BA/</guid>
       <description>&lt;blockquote&gt;&lt;p&gt;We buy things we don&amp;rsquo;t need, with money we don&amp;rsquo;t have, to impress people we don&amp;rsquo;t like.&lt;br/&gt;&amp;ndash; Piotrkowska Street, Łódź&lt;/p&gt;&lt;/blockquote&gt;</description>
     </item>
   
     <item>
       <title>SEO in Angular with ng2-meta</title>
       <link>https://vinaygopinath.me/2016/07/seo-in-angular-with-ng2-meta/</link>
       <pubDate>Mon, 25 Jul 2016 20:02:22 +0200</pubDate>
       
       <guid>https://vinaygopinath.me/2016/07/seo-in-angular-with-ng2-meta/</guid>
       <description>&lt;p&gt;Updating meta tags such as title and description as the route changes is essential for SEO in any single page application. While building &lt;a href=&#34;http://vinaygopinath.me/tech/2016/07/25/nomad-couple/&#34;&gt;Nomad Couple&lt;/a&gt;, an Angular2 site, I realized that there weren&amp;rsquo;t any Angular2 meta-tags libraries, so I decided to build one. I&amp;rsquo;ve released it as &lt;a href=&#34;https://github.com/vinaygopinath/ng2-meta&#34;&gt;ng2-meta&lt;/a&gt; on npm.&lt;/p&gt;&lt;p&gt;ng2-meta listens to router changes and updates &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tags with the values provided in the route&amp;rsquo;s configuration.&lt;/p&gt;&lt;p&gt;To get started, install ng2-meta&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm install --save ng2-meta&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Add a &lt;code&gt;data&lt;/code&gt; object to each of your routes and define meta tags (title, description, url etc) relevant to the route.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;routes&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;RouterConfig&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;home&amp;#39;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;component&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;HomeComponent&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;meta&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;title&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Home page&amp;#39;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;description&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Description of the home page&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;dashboard&amp;#39;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;component&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;DashboardComponent&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;meta&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;title&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Dashboard&amp;#39;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;description&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Description of the dashboard page&amp;#39;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;og:image&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;http://example.com/dashboard-image.png&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Inject Angular2&amp;rsquo;s &lt;code&gt;Title&lt;/code&gt; service as well as ng2-meta&amp;rsquo;s &lt;code&gt;MetaService&lt;/code&gt; into your app. ng2-meta uses the Title service internally to automatically change the page title when the &lt;code&gt;title&lt;/code&gt; meta tag is updated.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;Title&lt;/span&gt; } &lt;span style=&#34;color:#a6e22e&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;@angular/platform-browser&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;MetaService&lt;/span&gt; } &lt;span style=&#34;color:#a6e22e&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ng2-meta&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;bootstrap&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;AppComponent&lt;/span&gt;, [&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;HTTP_PROVIDERS&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ...&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;Title&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;MetaService&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Add MetaService as a provider to your root component&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;MetaService&lt;/span&gt; } &lt;span style=&#34;color:#a6e22e&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ng2-meta&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;@&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Component&lt;/span&gt;({&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ...&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;providers&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [&lt;span style=&#34;color:#a6e22e&#34;&gt;MetaService&lt;/span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you&amp;rsquo;d like to update the page title and meta tags from a component or service, say, after receiving data from a HTTP call, you can use &lt;code&gt;MetaService&lt;/code&gt;.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ProductComponent&lt;/span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ...&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;constructor&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;metaService&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MetaService&lt;/span&gt;) {}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;ngOnInit&lt;/span&gt;() {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;product&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;//HTTP GET for product in catalogue&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;metaService&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;setTitle&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Product page for &amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;product&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;metaService&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;setTag&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;og:image&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;product&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;imageURL&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s a good idea to add some fallback meta tags for use by crawlers that don&amp;rsquo;t execute Javascript, like Facebook and Twitter.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;html&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;head&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Website Name&amp;#34;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;og:title&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Website Name&amp;#34;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;General site description&amp;#34;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;og:description&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;General site description&amp;#34;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;og:image&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://abc.com/general-image.png&amp;#34;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;head&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;html&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The fallback meta tags are used to generate the rich snippet shown when your website is shared on Facebook (Just make sure to add &lt;a href=&#34;http://ogp.me/&#34;&gt;Open graph&lt;/a&gt; meta tags).&lt;a href=&#34;https://vinaygopinath.me/images/blog/ng2-meta/facebook-share.png&#34;&gt;&lt;img src=&#34;https://vinaygopinath.me/images/blog/ng2-meta/facebook-share.png&#34; alt=&#34;Nomad Couple - Facebook share rich snippet&#34;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Check out &lt;a href=&#34;https://nomadcouple.vinaygopinath.me&#34;&gt;Nomad Couple&lt;/a&gt; as a demo of ng2-meta. Its source code is available &lt;a href=&#34;https://github.com/vinaygopinath/NomadCouple&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I&amp;rsquo;m currently investigating server-side rendering using &lt;a href=&#34;https://github.com/angular/universal&#34;&gt;angular/universal&lt;/a&gt; and plan to update ng2-meta to support it.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Nomad Couple</title>
       <link>https://vinaygopinath.me/2016/07/nomad-couple/</link>
       <pubDate>Mon, 25 Jul 2016 16:31:07 +0200</pubDate>
       
       <guid>https://vinaygopinath.me/2016/07/nomad-couple/</guid>
       <description>&lt;p&gt;I&amp;rsquo;m Indian, and my girlfriend is Polish. We love travelling and we&amp;rsquo;d like to visit a lot of places, but there are &lt;a href=&#34;https://en.wikipedia.org/wiki/Visa_requirements_for_Indian_citizens#Visa_requirements_map&#34;&gt;surprisingly few countries&lt;/a&gt; that I can visit without a visa. Getting a visa can be tedious. (Quite often, I&amp;rsquo;ve had to  personally visit consulates and submit bank account statements, travel insurance and cover letters along with my visa application form). Finding countries that allow both of us to visit without a visa or obtain a visa on arrival is hard. That&amp;rsquo;s why it occurred to me to build &lt;a href=&#34;https://nomadcouple.vinaygopinath.me&#34;&gt;Nomad Couple&lt;/a&gt;, a website that provides information on visa requirements for couples.&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://vinaygopinath.me/images/blog/nomad-couple/home.png&#34;&gt;&lt;img src=&#34;https://vinaygopinath.me/images/blog/nomad-couple/home.png&#34; alt=&#34;Nomad Couple home page&#34;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://vinaygopinath.me/images/blog/nomad-couple/search.png&#34;&gt;&lt;img src=&#34;https://vinaygopinath.me/images/blog/nomad-couple/search.png&#34; alt=&#34;Nomad Couple search results&#34;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The site was built as an experiment with Angular2. I set up the &lt;a href=&#34;https://github.com/vinaygopinath/NomadCouple&#34;&gt;repo&lt;/a&gt; using &lt;a href=&#34;https://github.com/angular/angular-cli&#34;&gt;angular-cli&lt;/a&gt;, a wonderful tool that makes it easy to build an Angular2 site and deploy it on GitHub Pages. Having spent some time in the &amp;ldquo;Javascript fatigue&amp;rdquo;-inducing React ecosystem, it&amp;rsquo;s refreshing to be able to set up a project and get going quickly with angular-cli. (Side note: It looks like Facebook is finally acknowledging how complex it is to get started with a React app by creating its &lt;a href=&#34;https://facebook.github.io/react/blog/2016/07/22/create-apps-with-no-configuration.html&#34;&gt;own CLI tool&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;At the moment, the site groups countries based on visa requirements and links to WikiVoyage pages. I&amp;rsquo;d like to provide more information on each country through multiple data sources, the abilitiy to add links and pictures, and a Disqus comments section.&lt;/p&gt;&lt;p&gt;The data for the site was scraped from Wikipedia&amp;rsquo;s &amp;ldquo;Visa requirements for X citizens&amp;rdquo; pages. The source code for the scraper is available &lt;a href=&#34;https://github.com/vinaygopinath/visa-req-wiki-scraper&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Sending emails from Ghost with Nodemailer and SparkPost</title>
       <link>https://vinaygopinath.me/2016/06/sending-emails-from-ghost-with-nodemailer-and-sparkpost/</link>
       <pubDate>Tue, 14 Jun 2016 01:08:22 +0200</pubDate>
       
       <guid>https://vinaygopinath.me/2016/06/sending-emails-from-ghost-with-nodemailer-and-sparkpost/</guid>
       <description>&lt;p&gt;While I personally prefer Jekyll to Ghost in the battle of minimalistic CMS platforms, I recently set up a &lt;a href=&#34;https://ghost.org/&#34;&gt;Ghost&lt;/a&gt; blog for work and connected the built-in &lt;a href=&#34;https://github.com/nodemailer/nodemailer&#34;&gt;Nodemailer&lt;/a&gt; email module with &lt;a href=&#34;https://www.sparkpost.com&#34;&gt;SparkPost&lt;/a&gt;, an email service. Here&amp;rsquo;s how:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Set up a SparkPost account and verify your sending domain.&lt;/li&gt;&lt;li&gt;Go to SparkPost &lt;strong&gt;Accounts&lt;/strong&gt; &amp;ndash;&amp;gt; &lt;strong&gt;SMTP Relay&lt;/strong&gt; and copy the host, port and username.&lt;/li&gt;&lt;li&gt;Under &lt;strong&gt;Accounts&lt;/strong&gt; &amp;ndash;&amp;gt; &lt;strong&gt;API Keys&lt;/strong&gt;, select the &lt;strong&gt;Send via SMTP&lt;/strong&gt; permission and generate a new API key. The generated API key will be your SMTP password.&lt;/li&gt;&lt;li&gt;Open up your Ghost blog&amp;rsquo;s config.js, and modify the production section.&lt;/li&gt;&lt;/ol&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;production&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://example.com/blog&amp;#39;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;mail&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;from&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;Example.com Blog&amp;#34; &amp;lt;no-reply@example.com&amp;gt;&amp;#39;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;transport&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;SMTP&amp;#39;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;host&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;smtp.sparkpostmail.com&amp;#39;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;port&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;587&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;auth&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;user&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;SPARKPOST_SMTP_USERNAME&amp;#39;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;pass&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;SPARKPOST_SMTP_API_KEY&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;database&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ....&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can test your configuration by inviting someone to your Ghost installation. The invitee should receive an email from &lt;em&gt;Example.com blog&lt;/em&gt;.&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Setting up CORS with pre-flight request handling for development use in Node &#43; Express</title>
       <link>https://vinaygopinath.me/blog/tech/enable-cors-with-pre-flight/</link>
       <pubDate>Mon, 23 May 2016 23:20:37 +0200</pubDate>
       
       <guid>https://vinaygopinath.me/blog/tech/enable-cors-with-pre-flight/</guid>
       <description>&lt;p&gt;&amp;ldquo;Cross-Origin Resource Sharing&amp;rdquo; (CORS)-related errors are a common occurrence while developing a site with a separate frontend and API backend. While the &lt;a href=&#34;https://github.com/expressjs/cors&#34;&gt;cors module&lt;/a&gt; can set headers and respond to pre-flight requests, I didn&amp;rsquo;t find any documentation to set it up only in the Node development environment (assuming you don&amp;rsquo;t want to expose your APIs to requests from multiple domains).&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;express&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;express&amp;#39;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;app&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;express&lt;/span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;app&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;env&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;development&amp;#39;&lt;/span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Enabling CORS&amp;#39;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;app&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;use&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;res&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;res&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;header&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Access-Control-Allow-Origin&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;*&amp;#39;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;res&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;header&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Access-Control-Allow-Methods&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;GET,PUT,POST,DELETE,PATCH,OPTIONS&amp;#39;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;res&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;header&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Access-Control-Allow-Headers&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Content-Type, Authorization, Content-Length, X-Requested-With&amp;#39;&lt;/span&gt;); &lt;span style=&#34;color:#75715e&#34;&gt;//Add other headers used in your requests&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;OPTIONS&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;method&lt;/span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;res&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;sendStatus&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//Set up express routes here&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This enables CORS for all Express routes and responds to pre-flight OPTIONS requests with HTTP status OK (200) only during development. If you haven&amp;rsquo;t already set your &lt;code&gt;NODE_ENV&lt;/code&gt; to &lt;code&gt;development&lt;/code&gt;, you can do so and start your server like so&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NODE_ENV&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;development node index.js&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>URL Redirection With BrowserSync</title>
       <link>https://vinaygopinath.me/2016/05/url-redirection-with-browsersync/</link>
       <pubDate>Tue, 17 May 2016 16:57:05 +0200</pubDate>
       
       <guid>https://vinaygopinath.me/2016/05/url-redirection-with-browsersync/</guid>
       <description>&lt;p&gt;I started working on &lt;a href=&#34;https://www.gapsquad.com&#34;&gt;GapSquad&amp;rsquo;s website&lt;/a&gt; recently, and I had to redirect some URL requests in the Gulp+Browsersync dev environment to alternate paths in the project directory (to recreate the behaviour of the nginx production server). This can be done using &lt;a href=&#34;https://www.browsersync.io/docs/options/&#34;&gt;Browsersync options&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The project structure is organized like this&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;app- - - pages- - - - - - jobs.html- - - - - - pricing.html- - - - - - how-it-works.html- - - index.html&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;index.html has references to &lt;code&gt;/pricing&lt;/code&gt;, &lt;code&gt;/jobs&lt;/code&gt; and &lt;code&gt;/how-it-works&lt;/code&gt;, all of which are redirected to the corresponding HTML files in the &lt;code&gt;pages&lt;/code&gt; directory on the production server using nginx configuration.&lt;/p&gt;&lt;p&gt;I wanted to use Browsersync locally and recreate the behaviour of the server, without rewriting links specifically for the dev environment.&lt;/p&gt;&lt;h3 id=&#34;option-1---use-a-middleware&#34;&gt;Option 1 - Use a middleware&lt;/h3&gt;&lt;p&gt;Browsersync lets you define middleware to manipulate request and response data. I used it to rewrite &lt;code&gt;/pricing&lt;/code&gt; to &lt;code&gt;/pages/pricing.html&lt;/code&gt; in my Browsersync Gulp task.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;browserSync&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;({&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;server&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#75715e&#34;&gt;//Middleware paths are relative to the base directory&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;baseDir&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;app&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;middleware&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;,&lt;span style=&#34;color:#a6e22e&#34;&gt;res&lt;/span&gt;,&lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/pricing&amp;#39;&lt;/span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/pages/pricing.html&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/how-it-works&amp;#39;&lt;/span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/pages/how-it-works.html&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/jobs&amp;#39;&lt;/span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/pages/jobs.html&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When the request URL matches the ones that need to be redirected, I&amp;rsquo;ve simply replaced the request URL with the file path. You can also use JS regular expressions to match and redirect URLs.&lt;/p&gt;&lt;h3 id=&#34;option-2---define-server-routes&#34;&gt;Option 2 - Define server routes&lt;/h3&gt;&lt;p&gt;The more straightforward option is to define routes in the server config.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;browserSync&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;({&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;server&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;baseDir&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;app&amp;#39;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#75715e&#34;&gt;//Route paths are not relative to the base directory&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;routes&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/pricing&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;app/pages/pricing.html&amp;#39;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/how-it-works&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;app/pages/how-it-works.html&amp;#39;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/jobs&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;app/pages/jobs.html&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>Jekyll updates</title>
       <link>https://vinaygopinath.me/2016/04/jekyll-updates/</link>
       <pubDate>Thu, 14 Apr 2016 11:11:49 +0200</pubDate>
       
       <guid>https://vinaygopinath.me/2016/04/jekyll-updates/</guid>
       <description>&lt;p&gt;I&amp;rsquo;ve been exploring the Jekyll ecosystem over the past couple of weeks, and consider me sold - I really like the simplicity of building a static site with pages, posts and collections. I know that neither Jekyll nor Ghost will dethrone WordPress as the layman&amp;rsquo;s choice of CMS, but given all of the work coming out of the node.js and Ruby camps, WordPress has got some stiff competition, and deservedly so.&lt;/p&gt;&lt;h3 id=&#34;project-structure&#34;&gt;Project Structure&lt;/h3&gt;&lt;p&gt;While setting up a new Jekyll 3.0-powered site based on the Lanyon theme wasn&amp;rsquo;t hard, choosing a project structure that works for me took some time. I wanted my posts to be grouped within folders based on the category name, so I use this structure&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;  _posts    - tech      - 2016-04-14-tech-post-title.md      - 2016-03-29-another-tech-post-title.md    - travel      - 2016-04-10-travel-post-title.md      - 2016-03-09-another-travel-post-title.md&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you use Octopress, it&amp;rsquo;s easy to create a new post in a category with this command&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;#Create a new post with the title &amp;#34;Post Title&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;#in the &amp;#34;tech&amp;#34; category with the current date&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   octopress new post &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Post Title&amp;#34;&lt;/span&gt; -D tech&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To maintain consistency, my &lt;code&gt;_pages&lt;/code&gt; folder is organized like this:&lt;/p&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;   _pages     - tech       - index.html     - travel       - index.html&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To get Jekyll to process these files, I had to add &lt;code&gt;_pages&lt;/code&gt; to my &lt;code&gt;include&lt;/code&gt; array in &lt;code&gt;_config.yml&lt;/code&gt;&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;include&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_pages&amp;#34;&lt;/span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;pagination&#34;&gt;Pagination&lt;/h3&gt;&lt;p&gt;I found that I needed Octopress and octopress-paginate to set up paginated categories. I created a new file in &lt;code&gt;_layouts&lt;/code&gt; called category-page.html with this content&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;layout: default&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% raw %}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;page&amp;#34;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;h1&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;page-title&amp;#34;&lt;/span&gt;&amp;gt;{{ page.paginate.category }}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {% if paginator.page &amp;gt; 1 %}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - Page {{paginator.page}}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {% endif %}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;h1&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;posts&amp;#34;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {% assign index = true %}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {% for post in paginator.posts %}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {% assign content = post.content %}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;post&amp;#34;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;h2&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;post-title&amp;#34;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{{ post.url }}&amp;#34;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {{ post.title }}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;h2&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;span&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;post-date&amp;#34;&lt;/span&gt;&amp;gt;{{ post.date | date_to_string }}&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;span&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {{ post.excerpt }}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {% unless post == paginator.posts.last %}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;hr&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {% endunless %}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {% endfor %}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pagination&amp;#34;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {% if paginator.next_page %}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pagination-item older&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{{paginator.next_page_path}}&amp;#34;&lt;/span&gt;&amp;gt;Older&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {% else %}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;span&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pagination-item older&amp;#34;&lt;/span&gt;&amp;gt;Older&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;span&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {% endif %}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {% if paginator.previous_page %}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {% if paginator.page == 2 %}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pagination-item newer&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/{{page.paginate.category}}&amp;#34;&lt;/span&gt;&amp;gt;Newer&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {% else %}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pagination-item newer&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{{paginator.previous_page_path}}&amp;#34;&lt;/span&gt;&amp;gt;Newer&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {% endif %}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {% else %}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;span&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pagination-item newer&amp;#34;&lt;/span&gt;&amp;gt;Newer&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;span&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {% endif %}&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% endraw %}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Setting up a paginated category page is simply a matter of using &lt;code&gt;layout: category-page&lt;/code&gt;. For example, this is my &lt;code&gt;_pages/tech/index.html&lt;/code&gt;&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;layout&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;category-page&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;title&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Tech&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;permalink&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/tech/&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;paginate&lt;/span&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;category&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tech&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;sass&#34;&gt;SASS&lt;/h3&gt;&lt;p&gt;I&amp;rsquo;m a big fan of SASS, and I was happy to discover that Jekyll has &lt;a href=&#34;https://jekyllrb.com/docs/assets/#sassscss&#34;&gt;built-in support for SASS&lt;/a&gt;. I&amp;rsquo;ve also taken to inlining critical CSS (&lt;a href=&#34;https://www.youtube.com/watch?v=FEs2jgZBaQA&#34;&gt;this long and insightful talk&lt;/a&gt; by Addy Osmani helped me see the light) and I wanted to set it up for my site. A little bit of Google-fu led me to this &lt;a href=&#34;https://gist.github.com/benedfit/46da533805566141c42f&#34;&gt;gist&lt;/a&gt; and this &lt;a href=&#34;http://www.kevinsweet.com/inline-scss-jekyll-github-pages&#34;&gt;post&lt;/a&gt; that solved my problem without having to use Grunt or Gulp.&lt;/p&gt;&lt;h3 id=&#34;deploying-to-github-pages&#34;&gt;Deploying to Github Pages&lt;/h3&gt;&lt;p&gt;Lastly, since this site is hosted on Github Pages and Github approves a limited set of plugins (Octopress not being one of them), I had to resort to manually building and deploying my site. This can be done by setting up a &lt;code&gt;_deploy.yml&lt;/code&gt; according to the specification &lt;a href=&#34;https://github.com/octopress/octopress#git-deployment-configuration&#34;&gt;here&lt;/a&gt; and running&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   octopress deploy&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Warning: Be sure to have a clean working environment by stashing/committing your files before running this command. I accidentally published a bunch of temp posts I&amp;rsquo;d created to test pagination because they were present (untracked , not stashed or committed) in the &lt;code&gt;_posts&lt;/code&gt; folder and they ended up in the resulting Jekyll build. That&amp;rsquo;s what you get for not reading the &lt;a href=&#34;https://github.com/octopress/octopress#isolate&#34;&gt;manual&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://developers.google.com/speed/pagespeed/insights/?url=vinaygopinath.me&#34;&gt;Pagespeed insights score&lt;/a&gt; for the site:&lt;img src=&#34;https://i.imgur.com/iJhrRzN.png&#34; alt=&#34;99/100 on both mobile and desktop&#34;&gt;&lt;/p&gt;</description>
     </item>
   
     <item>
       <title>Hello</title>
       <link>https://vinaygopinath.me/2016/02/hello/</link>
       <pubDate>Wed, 17 Feb 2016 10:41:00 +0100</pubDate>
       
       <guid>https://vinaygopinath.me/2016/02/hello/</guid>
       <description>&lt;p&gt;Hello,&lt;/p&gt;&lt;p&gt;Ghost is just not enough! I&amp;rsquo;d heard good things about Jekyll for sometime now, so I decided to check things out and host my blog on Github Pages.&lt;/p&gt;&lt;p&gt;I ran into a few issues&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Github Pages &lt;a href=&#34;https://github.com/isaacs/github/issues/547&#34;&gt;does not support&lt;/a&gt; setting a custom domain for a user site (&lt;code&gt;master&lt;/code&gt; branch) without affecting all project sites (&lt;code&gt;gh-pages&lt;/code&gt; branch) of that account. I wanted to added the custom domain &lt;a href=&#34;http://blog.vinaygopinath.me&#34;&gt;blog.vinaygopinath.me&lt;/a&gt; to the blog repository while keeping the &lt;code&gt;username.github.io/project&lt;/code&gt; demo site for &lt;a href=&#34;https://github.com/vinaygopinath/ngMeta&#34;&gt;ngMeta&lt;/a&gt;, but apparently that is not possible with GitHub. A support member suggested that I set up my blog as a project repo, as a workaround for the all-or-nothing redirection.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Jekyll does not seem to have support for paginated categories. I intend to split this blog into categories like travel, tech and other. While it is possible to list out posts in a category, &lt;a href=&#34;http://jekyllrb.com/docs/pagination/&#34;&gt;pagination does not support categories&lt;/a&gt; just yet. I hope a future release adds this feature. I&amp;rsquo;m not sure about the Jekyll community ecosystem - perhaps there&amp;rsquo;s a plugin that already does this.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;</description>
     </item>
   
     <item>
       <title>Previous Blog</title>
       <link>https://vinaygopinath.me/2016/02/previous-blog/</link>
       <pubDate>Wed, 17 Feb 2016 10:40:00 +0100</pubDate>
       
       <guid>https://vinaygopinath.me/2016/02/previous-blog/</guid>
       <description>&lt;p&gt;The previous incarnation of this blog lives in WordPress land and has my posts about AngularJS, Android, Ubuntu and other tools and frameworks I&amp;rsquo;ve worked with. You can check it out &lt;a href=&#34;https://vinaygopinath.wordpress.com/category/tech/&#34;&gt;here&lt;/a&gt;&lt;/p&gt;</description>
     </item>
   
 </channel>
</rss>