<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title><![CDATA[Luis Cipriani - Tall Eye]]></title>
        <description><![CDATA[My Personal website with projects and articles about technology and computer science.]]></description>
        <link>https://www.talleye.com</link>
        <generator>RSS for Node</generator>
        <lastBuildDate>Sat, 14 Mar 2026 11:59:45 GMT</lastBuildDate>
        <atom:link href="https://www.talleye.com/posts-rss.xml" rel="self" type="application/rss+xml"/>
        <pubDate>Mon, 01 Jan 2007 00:00:00 GMT</pubDate>
        <copyright><![CDATA[https://creativecommons.org/licenses/by-nc/4.0/]]></copyright>
        <language><![CDATA[en]]></language>
        <ttl>60</ttl>
        <item>
            <title><![CDATA[How to deal with teams falling behind]]></title>
            <description><![CDATA[<p>It can be for many reasons: your business was impacted by Covid and had to lay off people, or your company grew too fast and recruiting could not keep up, or you received an unusual amount of resignation letters in a short period of time. Suddenly your team has not enough talent to keep the rhythm and is falling behind.</p>
<p><strong>Teams at a low capacity lose their ability to contribute fast and well</strong>, morale will go down because they see less work being delivered, more reports of regression bugs and the feeling that there's no time to work on ideal solutions. In the end, most of the work is to fulfill the pressure of the business.</p>
<p>In the book "The elegant puzzle", <a href="https://twitter.com/lethain" target="_blank">Will Larson</a> introduces <a href="https://lethain.com/durably-excellent-teams/" target="_blank">the four stages of a team</a> and what they need to be able to increase performance. He shares that for teams that are falling behind, the fix is to hire more people while providing tactical support, which is a logical step given the low stock of talent. Furthermore, he emphasizes that changing a team from one state to the other is a slow process, which demands patience and a solid reaction from the manager.</p>
<p>I've been working for the past 6 years in startups and this scenario is very familiar to me, including situations where not even hiring was an option. I would like to share what's on my playbook to keep a team stable. And I hope these tips help your teams as well.</p>
<p><img src="https://www.talleye.com/img-posts/htdwtfb-thumb.png" alt=""></p>
<h2>Adapt the workflow to a new reality</h2>
<p>The first hard truth to swallow is that <strong>your team won't be able to handle too many parallel initiatives as before</strong>. The reason being that working in simultaneous different tasks demands switching contexts frequently. This will, as a consequence, increase the stress in the development team and reflect poorly on the code being released to customers.</p>
<p><strong>They also will be taking longer to complete their tasks</strong> because you don't have the collaborative environment you had before. No pair programming, not enough peers to review code, less people to share ideas and discuss problems.</p>
<p>Find the balance by making your development process sustainable again:</p>
<ul>
<li><strong>Tame the backlog</strong>: the tendency is that it will only grow. Backlogs always get messy with time anyway and you should review it and remove any tasks that are not a top priority. Don't underestimate the power of simply hiding it from the team and keeping tasks in a backlog of the backlog.</li>
<li><strong>Start less, finish more</strong>: finishing a feature brings more value to customers than starting new ones. Make sure every epic or initiative that's started has an end before picking the next big thing. Even if to make it happen we concentrate the whole team on it. This will help to decrease context switching.</li>
<li><strong>Shutdown external requests channels</strong>: I'm all in for software engineers collaborating with customers and business teams, but embrace the reality that this can drain their focus now. It might be a good moment to put someone (or yourself) in between representing the team and optimizing their time.</li>
<li><strong>Never trade off quality over speed</strong>: it can be contradictory, but you need to empower your team to accept the lower speed of delivery. It's easy to sacrifice quality over speed in these situations, but the pay back is expensive and it comes in the form of regression bugs and unexpected fires to put out. It's better to do it right, instead of faster.</li>
</ul>
<p>These tips are targeted to help us to deal with the work in progress, but remember that the backlog will continue to grow. Let's see how we can take care of that.</p>
<h2>Focus!</h2>
<p>Given that hiring is a slow process, the only option we have to avoid the team burning out is to narrow the focus of work.</p>
<p><strong>Break it down, work on smaller things</strong>. Developing a feature with large scope and complexity increases the chance that the team will spend a long time on it. It can steal time that would be used to solve tech debts or even fix a critical issue in production. Work with the product team to create smaller iterations that can be released as early as possible to users. As a consequence, your team will feel that they are always progressing and delivering often.</p>
<p>This exercise can also be applied to tasks. Small tasks are easy to code, review and release. It will pump up the cycle time and throughput metrics.</p>
<p>It might be good to even stop feature development at all and focus only on tech debts. It is hard to negotiate with the product team, but worth a try, especially if you can demonstrate that fixing them will improve users happiness more than any other new feature they propose.</p>
<p><strong>Be rigorous in the refinement process</strong>. When a cross-functional team is working together for a long time, the specifications tend to omit details and cover only the surface of the requirements. It's understandable, teams involved trust each other and the fine details are sorted out during development. Now, with low capacity, people are not as available as before and clarifying topics will take longer.</p>
<p>I'm assuming that at least product research and discovery is done well, if that's not the case, challenge every product assumption that's not backed by irrefutable evidence. And of course don't turn this into a petty revenge.</p>
<p>Being more criterious over the specification input will shift the workload back to the product team, decreasing the overhead of communication once the feature is started. This can even have a positive effect on the test results and numbers of regressions.</p>
<p><strong>Avoid committing to deadlines or estimations</strong>. The reason is that now there's too many uncontrollable variables influencing how long a task will take. With fewer people to work on the project, every interruption, incident or meeting will have a greater influence on the deadline compared to a team with an adequate size. If a company wants to get serious about deadlines, then get serious about building great teams first.</p>
<p>Estimating well is an important skill to have, don't get me wrong, especially when the deadline is defined by an external factor. You should still make the effort to have at least a baseline to share with your team and compare with the final result. Then use these outcomes to negotiate scope of the next features.</p>
<p><strong>Organize special activities</strong>. We call it Hackday, in your company you might use a different name for it. Software engineers have a whole day to work on whatever project they want. It may sound counterintuitive, but allowing them to relax from the daily pressure and work on their favorite initiatives, develop themselves and accomplish personal career goals is a great way to keep a good cadence during a crisis.</p>
<p>In more than 10 years doing these hackdays, I never had the feeling that I was wasting company money or time. There were always good outcomes from it, being a fix to an old tech debt or bringing joy and satisfaction to the teams.</p>
<h2>Care about people</h2>
<p>The level of stress will increase when teams have a shortage of talents. People will start to question themselves whether they should find some other place to work. It's your responsibility as a manager to know how this situation is affecting the team and help them to overcome the problems.</p>
<p><strong>Ensure you have a psychologically safe environment</strong>. There's a good chance that failures will happen more often now. It's your job to encourage your team to speak up about their ideas, concerns and failures without fearing retaliation or bad judgment. This behavior will promote more trust between the team and also increase transparency in the environment.</p>
<p><strong>Be honest</strong>. The worst that you can do is to try to hide from the team the reality when the side effects are so perceptible. Sharing the problem with them opens the door to discussions that can lead to solutions, put them in control of the situation and promote an inclusive environment. In addition, be calm and be pragmatic about the actions you take because if the team perceive you as impatient, irritated or desperate then you can transfer these emotions to them.</p>
<p><strong>Pay attention to burnout</strong>. Keep an eye on whether people are overworking, being invited to too many meetings, getting interrupted a lot during work or getting requests outside of work hours. Use 1:1 meetings well so you and them can be open about the main issues that are leading them to stress out.</p>
<p>If you see strong signs of burnout, act to improve their work life on whatever is in your power and advise them to look for external help if necessary.</p>
<p><strong>If you plan well, you can decrease the impact of a person leaving</strong>. The transition process will be smooth if you have a clear plan. You should look to hire a replacement, define how the communication to the team will be done and how the handover will be.</p>
<h2>Setting expectations</h2>
<p>I have been so far sharing tips on how to take some pressure off the team and create a sustainable environment. However, <strong>don't forget that you are running a business</strong>. While it's inevitable that there will be an impact on performance, it's crucial to stay in tune with business demands and set clear expectations.</p>
<p>In a healthy organization we expect a good collaboration among all the cross-functional teams to solve the low capacity problem, but let's not be naive, they will come for you once the releases are getting late or customers are complaining.</p>
<p><strong>Match the scope of your team's ownership to the people available</strong>. I do that with a table:</p>
<ul>
<li>Columns are all components of the product the team takes care;</li>
<li>Rows are team members;</li>
<li>Values are the percentage of rough estimation of allocation of a given person, a row of values should always sum to 100%, because no overwork;</li>
<li>The last row is a sum of values in the column, representing how much allocation a given component usually has;</li>
<li>If you are negotiating headcount, add another row to indicate what's the ideal number of people in a given topic.</li>
</ul>
<p>This approach makes it very easy to visualize which components we are ignoring due to having fewer people in the team. In my experience this table always helped business teams and top management to understand the situation. In addition, it's a great tool to bring to their awareness that software development is not only about building new features, but it requires maintenance, quality and infrastructure management.</p>
<p><strong>Have your metrics in hand</strong>. The allocation table, despite being helpful, is a bit subjective. Nowadays it is not hard to extract metrics from the software development tools we use. The key takeaway is to have a baseline to compare previous performance with the current one.</p>
<ul>
<li>Cycle time, the time from when the first commit is done till the task is released in production, tends to increase. Applies also to Pull Request merge time or any duration-based metric;</li>
<li>Throughput or number of deploys/releases tend to get lower. Burndown charts get flatter;</li>
<li>The size of the backlog increases. You can also observe task columns in the board having more tickets than normal;</li>
<li>Ratio of failed builds or regressions also tend to increase;</li>
<li>If you have any product or business metric that's impacted by the low capacity, it can make even a better case for investing in the team.</li>
</ul>
<p><strong>Celebrate wins</strong>. I mean, this should be done independently of having a team falling behind or not. But any injection of happiness and optimism is always good to keep them motivated. Plus it will show to the rest of the company how valuable your team contributions are.</p>
<h2>Now what?</h2>
<p><em>Could it happen that by trying all these tips my team starts to perform better and better to the point that it's not necessary to hire anymore?</em></p>
<p>The answer is... <strong>No</strong>.</p>
<p>Just like the <a href="https://www.who.int/emergencies/diseases/novel-coronavirus-2019/covid-19-vaccines/advice" target="_blank">Covid vaccine</a> won't be protecting you 100% from infection, but will definitely increase your chances of having just mild symptoms and surviving. These tips will help your team to go through a tough time with a solid foundation to rely on.</p>
<p>Nevertheless, don't fool yourself. <strong>Hire as soon as you can!</strong> There's always a limit for patience and it will be a bumpy road. I myself collect some successes but also failures trying to keep a falling behind team together.</p>
<hr>
<p>A short appendix:</p>
<p>Is your company trying to shut down your project or department? It can be pretty unpleasant to realize this when you're the non informed.</p>
<p>In that case the tips in this article won't be that helpful. I recommend planning a transition with your manager and helping your team to do the same, being in the same company or outside of it.</p>
<hr>
<p><em>Thanks to <a href="https://twitter.com/lsdr" target="_blank">Luiz Rocha</a> for reviewing this post.</em></p>]]></description>
            <link>https://www.talleye.com/posts/how-to-deal-with-teams-falling-behind</link>
            <guid isPermaLink="true">https://www.talleye.com/posts/how-to-deal-with-teams-falling-behind</guid>
            <dc:creator><![CDATA[Luis Cipriani]]></dc:creator>
            <pubDate>Fri, 04 Feb 2022 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Beginner's Blender setup for 3D print modeling]]></title>
            <description><![CDATA[<p>One of the distractions I decided to get since the pandemic started was a 3D printer. I was always curious about them but never got a strong reason to buy until I had to find ways to get my son and me entertained at home. And I must say, 3D printers are fun and opens a world of possibilities of toys, tools, home appliances and <a href="https://www.thingiverse.com/" target="_blank">many more</a>.</p>
<p>In the first weeks with a 3D printer you usually rely on pre-designed models in the several 3D model repositories available for free, but it's inevitable that at some point you will want to design your own models. And among the <a href="https://all3dp.com/1/best-free-cad-software-2d-3d-cad-programs-design/" target="_blank">several tools available</a>, I chose Blender to learn and use.</p>
<p><a href="https://www.blender.org/" target="_blank">Blender</a> is an ultra complete, professional, free and open source 3D creation suite. I personally like the combo made by free software, big community support and a super professional tool. In case I get myself too involved at least I don't need to move to a new tool, because Blender has no limits!</p>
<p>I'll show you how to setup a boilerplate Blender file that has everything you need to start 3D modeling and avoid the common pitfalls that can affect your print later. This article won't teach you how to model or how to get your model in the printer, but I'll give some tips and directions on how to get there.</p>
<h2>Learning Blender</h2>
<p>First you need to install it: <a href="https://www.blender.org/download/" target="_blank">www.blender.org/download/</a>. For this post I'm using Blender 2.90.1, but even if you have a newer version you should be able to follow it.</p>
<p>Being the popular tool that Blender is, there's plenty of online resources to learn it. I'll save your time and recommend the one I watched:</p>
<p><a href="https://www.youtube.com/playlist?list=PLgO2ChD7acqH5S3fCO1GbAJC55NeVaCCp" target="_blank"><img src="https://www.talleye.com/img-posts/bbs3pm-tutorial.jpg" alt=""></a></p>
<p>Ram Singh is the author of this video series where he <a href="https://www.youtube.com/playlist?list=PLgO2ChD7acqH5S3fCO1GbAJC55NeVaCCp" target="_blank">teaches Blender in 7 days</a>. You can easily see how dexterous he is in the tool after a few minutes watching it and I personally liked the pace of the course which is not slow and you can learn a lot in a short period of time. For 3D printing you will need at least to watch the series until day 3, covering the basics, editing objects and modifiers.</p>
<h2>3D Printing setup</h2>
<p>From now on, if you wish, you can open Blender and follow along the setup of this boilerplate file. Feel free to save the file to use as a template in the future. You can also override the default startup file with File > Defaults > Save Startup File.</p>
<h3>1. Cleaning</h3>
<p>When you start Blender, you get the following screen:</p>
<p><img src="https://www.talleye.com/img-posts/bbs3pm-blender01.jpg" alt=""></p>
<p>The first step is to get rid of elements we don't need in 3D printing, which are the <strong>camera</strong> and <strong>light</strong>. To delete them, just select them in the right sidebar, the one with the Scene Collection, and press <code>X</code> while selecting camera and light or use the context menu.</p>
<h3>2. What you see should be what you will get</h3>
<p>3D printers are precise instruments and knowing the exact size of objects when modeling is crucial to have a printed object in the dimensions you planned. With an object selected in Blender, if you press <code>N</code> you can see the dimensions of the object, presented in the image below.</p>
<p><img src="https://www.talleye.com/img-posts/bbs3pm-dimensions-min.jpg" alt=""></p>
<p>The cube in the image has an edge of 2 meters and the grid where the cube lies has 1 meter of distance between each line. While I wished we had a printer big enough to print objects like that, we need to scale down the units to be able to handle smaller objects and we also need a grid compatible with our printer capable volume.</p>
<h4>Units</h4>
<p>To have better units to work on your model, open the Scene window as the image shows below and change the following settings:</p>
<ul>
<li><strong>Unit scale</strong>: from <code>1</code> to <code>0.001</code>. It defines the scale factor to use when converting between internal units and values displayed in the user interface.</li>
<li><strong>Length</strong>: from <code>meters</code> to <code>millimeters</code></li>
</ul>
<p><img src="https://www.talleye.com/img-posts/bbs3pm-scene.jpg" alt=""></p>
<p>If you see the object dimensions above, you will realize that now it shows as having an edge of 2 millimeters instead of 2 meters.</p>
<h4>Grid</h4>
<p>The grid is still showing 1 meter between each line. Having a properly sized grid is very important because it helps us to quickly check sizes when modeling and also to align different objects. Ideally the grid should have a 1 millimeter granularity.</p>
<p>To set a new grid size you click in the Overlays option as the image shows below and change the scale of the grid to <code>0.001</code>.</p>
<p><img src="https://www.talleye.com/img-posts/bbs3pm-grid.jpg" alt=""></p>
<p>Now the grid in the image is showing a 1 millimeter granularity.</p>
<h4>Working Area</h4>
<p>Next step is to make visible the boundaries you should be constrained so the object fits in your 3D printer range. This step is optional because usually you can change the object scale in the Slicer software (the tool that converts the 3D object in instructions for your printer), but if you want to have that control, do the following:</p>
<ol>
<li>Select the cube with mouse <code>right-button</code> click</li>
<li>Set its dimensions to the dimensions of your 3D printer (press <code>N</code> to see the Item tab)</li>
<li>Set its location to <code>x = 0</code>, <code>y = 0</code> and <code>z = height/2</code> (z is actually in the center of mass, not in the base, that's why it's not zero)</li>
<li>Click on the Object properties tab as indicated in the image below</li>
<li>In the "Visibility" untick <code>Selectable</code></li>
<li>In the "Viewport Display" tab, choose "Display As" to <code>Bounds</code></li>
</ol>
<p>Then you should see the Working Area as the image below. I renamed the object name to "Working Area" so you don't mix with other objects in the collection.</p>
<p><img src="https://www.talleye.com/img-posts/bbs3pm-workingarea.jpg" alt=""></p>
<h3>3. Getting the details right</h3>
<h4>Edge measurement</h4>
<p>It's common to have parts of your model that you need to ensure precise measurements, such as holes, gaps, parts that will be attached to others, like a screw, for example. To help you to know exact edge measurements, Blender has an option in the Viewport Overlay, as shown below:</p>
<p><img src="https://www.talleye.com/img-posts/bbs3pm-measurement.jpg" alt=""></p>
<p>Once enabled, it will show the measurement of the selected edges. There's an important catch, if you scale an object, this measurement might be "wrong" because it only gets updated if you Apply the scale transformation by selecting the object and pressing <code>CMD + A</code> or <code>Ctrl + A</code> and choosing "Scale" transform to be applied.</p>
<h4>3D print add-on</h4>
<p>Finally, there's an essential Blender add-on that helps us to check if the object we will be exporting is "healthy", i.e. doesn't have any problem like being non-manifold, super thin surfaces, too sharp edges or face intersections. The great thing about this add-on is that it can fix your model with a single click in most of the cases.</p>
<p>To enable it go to Edit > Preferences, then select Add-ons, search for "3D-print" and enable the add-on. You now have a side tab when you press <code>N</code> named "3D-Print". Then with an object selected, try to click the "Check All" button to see the results.</p>
<p><img src="https://www.talleye.com/img-posts/bbs3pm-addon.jpg" alt=""></p>
<p>Instead of explaining the details of this add-on, I recommend you to explore the features with a cube object or try to "break" your object to see how the add-on reports the problems.</p>
<p>Once your object passes all checks, you can use the Export button to save it in the desired format for processing in the Slicer software.</p>
<h2>Now you build</h2>
<p>That's it! With these steps you will be able to start building your object ensuring that all edges are in the size you want and that your model is printable without issues. We just scratched the surface of Blender and there's still a lot to learn, but I can attest that this setup helped me a lot since I started to play with 3D modeling.</p>
<p>Here's a <a href="https://www.thingiverse.com/thing:4785703" target="_blank">magnetic key holder</a> I designed and printed to use at home:</p>
<p><a href="https://www.thingiverse.com/thing:4785703" target="_blank"><img src="https://www.talleye.com/img-posts/bbs3pm-keyholderpic.png" alt=""></a></p>
<p><img src="https://www.talleye.com/img-posts/bbs3pm-final.jpg" alt=""></p>
<p>As a suggestion for further reading, these topics might interest you and can help to avoid other pitfalls:</p>
<ul>
<li>Learn how to use the <a href="https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/booleans.html" target="_blank">Boolean</a> modifier or use <a href="https://docs.blender.org/manual/en/latest/addons/object/bool_tools.html" target="_blank">BoolTool</a> add-on.</li>
<li>Learn how to use <a href="https://docs.blender.org/manual/en/latest/modeling/meshes/mesh_analysis.html" target="_blank">Mesh Analysis</a>.</li>
</ul>]]></description>
            <link>https://www.talleye.com/posts/beginner-blender-setup-for-3d-print-modeling</link>
            <guid isPermaLink="true">https://www.talleye.com/posts/beginner-blender-setup-for-3d-print-modeling</guid>
            <dc:creator><![CDATA[Luis Cipriani]]></dc:creator>
            <pubDate>Sat, 13 Mar 2021 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Cross platform productivity with Terminal]]></title>
            <description><![CDATA[<p>At work I use a Mac, at home I have a Lenovo notebook with <a href="https://www.archlinux.org/" target="_blank">ArchLinux</a> installed. There's several reasons why people, and specially developers, adopt different platforms simultaneously and there's nothing wrong with that, in fact, we are just adopting the right tool for the right job… or budget.</p>
<p>However, it can be annoying when you depend on productivity tools that are not available on all platforms. You also start to think twice before buying a license for a tool that won't be available on one of the platforms you adopt. Cross platform consistent user experience is hard, but we can find ways to solve this issue. This article will focus on how to improve the experience for one kind of tool:</p>
<h2>Keyboard triggered Productivity and Assistant tool</h2>
<p>The title above is my tentative to classify tools like <a href="https://en.wikipedia.org/wiki/Alfred_%28software%29" target="_blank">Mac Alfred</a>, <a href="https://en.wikipedia.org/wiki/Unity_%28user_interface%29#Dash" target="_blank">Ubuntu Unity Dash</a>, <a href="https://en.wikipedia.org/wiki/GNOME" target="_blank">Gnome Shell overview</a> and <a href="http://alternativeto.net/software/alfred/" target="_blank">others</a>.</p>
<p><img src="https://www.talleye.com/img-posts/cppwt-alfred.png" alt="">
<em>Above a screenshot of Alfred in action.</em></p>
<p>The way they work is simple. After a global keyboard shortcut is hit by the user, they can type commands to:</p>
<ul>
<li>Launch applications</li>
<li>Search desktop</li>
<li>Open URLs and search specific sites</li>
<li>Calculate something</li>
<li>Manage clipboard</li>
<li>Even trigger more complex workflows that can be customized</li>
</ul>
<p>It's a true Swiss-army tool that saves a lot of time, but given that most of them are not cross-platform because of their deep relationship with the operating system, it's really hard to find a consistent experience. If we had a tool that was as powerful as these ones, reachable from a simple keyboard shortcut and available on all these platforms would be nice, right? Right?</p>
<h2>Introducing… Terminal</h2>
<p>Nothing new here, just the plain and simple terminal you are used to have available in any operating system. Basically we just need to find a way to have the same experience as these keyboard triggered productivity and assistant tools. As I'll show below, it's totally possible to achieve something similar, sometimes even more powerful. Let's do it!</p>
<h3><strong>Keyboard Shortcut</strong></h3>
<p>First important thing is to have a terminal that pops up by hitting a simple keyboard shortcut, luckily this is a feature that can be easily found in the most popular Terminal apps.</p>
<p>On Mac, I use <a href="http://iterm2.com/features.html" target="_blank"><strong>iTerm2</strong></a> support for Hotkey window and <code>CMD+space</code> to trigger that terminal.</p>
<p>On Linux, I use <a href="http://guake.org/" target="_blank"><strong>Guake</strong></a> with <code>Super+space</code> shortcut.</p>
<p>Usually I configure these terminals to hide when losing focus so you can have a better experience when executing a command that opens the browser, for example. You can also tweak the terminal fonts and color the way you want.</p>
<p><img src="https://www.talleye.com/img-posts/cppwt-terminal.png" alt="">
<em>Above: Guake terminal screen on ArchLinux with a Timezone custom command I created.</em></p>
<p>So far so good, we have a quick way of opening a Terminal, now we need a good set of commands and packages that reproduces the nice behavior we get with the other tools.</p>
<h3>Unix Shell</h3>
<p>On both Mac and Linux I use <a href="http://www.zsh.org/" target="_blank"><strong>zsh</strong></a> and <a href="http://ohmyz.sh/" target="_blank"><strong>oh-my-zsh</strong></a> and this combination is perfect because already comes with a pretty good way of organizing the features I want to have on my productivity tool set. By creating a <a href="https://github.com/robbyrussell/oh-my-zsh/wiki/Customization" target="_blank">oh-my-zsh plugin</a> you keep all configurations in one place and can easily edit them to tweak existing or add new features.</p>
<p>I have this file on my oh-my-zsh install (with a quite suggestive name):</p>
<pre class="language-bash"><code class="language-bash">$ ~/.oh-my-zsh/custom/plugins/terminalfred/terminalfred.plugin.zsh
</code></pre>
<p>All aliases and functions you add to that file will be available in your <code>PATH</code> when you open the terminal. In the next sections I'll show what I have on this file. If you are not patient, you can take a look on this <a href="https://gist.github.com/lfcipriani/f6baa463fa1c600a5bf7ce3ecf764321#file-terminalfred-plugin-zsh" target="_blank">Gist</a>.</p>
<p>What I also do is to keep that file on Dropbox and symlink to it from all machines I use. It's okay if you don't have any sensitive information in its content.</p>
<p>I'm confident that you can find a similar solution no matter the Unix Shell you use because in the end, what you need is only <strong>installed packages, aliases and functions</strong>.</p>
<h3>Searching the web</h3>
<p>This is the most used feature and oh-my-zsh has a perfect plugin, called <a href="https://github.com/robbyrussell/oh-my-zsh/blob/master/plugins/web-search/web-search.plugin.zsh" target="_blank">web-search</a>, that allows me to do this:</p>
<pre class="language-bash"><code class="language-bash">$ ddg how to ride a bicycle   <span class="token comment"># search duckduckgo</span>
$ image beautiful bicycle     <span class="token comment"># search for images</span>
$ map bicycle store           <span class="token comment"># search on a map</span>
</code></pre>
<p>These commands will open a browser and do the search, more straightforward impossible. If you want to add your own custom searches, my approach to that was to copy that function in my custom plugin and add them manually:</p>
<pre class="language-shell"><code class="language-shell"><span class="token builtin class-name">alias</span> <span class="token assign-left variable">urban</span><span class="token operator">=</span>’terminalfred urban’
<span class="token builtin class-name">alias</span> <span class="token assign-left variable">regex</span><span class="token operator">=</span>’terminalfred regex’
<span class="token builtin class-name">alias</span> <span class="token assign-left variable">shell</span><span class="token operator">=</span>’terminalfred shell’
<span class="token builtin class-name">alias</span> <span class="token assign-left variable">jsonb</span><span class="token operator">=</span>’terminalfred jsonb’
<span class="token builtin class-name">alias</span> <span class="token assign-left variable">aur</span><span class="token operator">=</span>’terminalfred aur’

<span class="token keyword">function</span> <span class="token function-name function">terminalfred</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	emulate -L <span class="token function">zsh</span>

	<span class="token comment"># define search engine URLS</span>
	<span class="token builtin class-name">typeset</span> -A urls
	<span class="token assign-left variable">urls</span><span class="token operator">=</span><span class="token punctuation">(</span>
		urban <span class="token string">"http://www.urbandictionary.com/define.php?term="</span>
		regex <span class="token string">"https://regex101.com/library?orderBy=RELEVANCE&#x26;search="</span>
		shell <span class="token string">"http://explainshell.com/explain?cmd="</span>
		jsonb <span class="token string">"http://codebeautify.org/jsonviewer"</span>
		aur <span class="token string">"https://www.archlinux.org/packages/?q="</span>
	<span class="token punctuation">)</span>

	<span class="token keyword">if</span> <span class="token punctuation">[</span><span class="token punctuation">[</span> -z <span class="token string">"<span class="token variable">$urls</span>[<span class="token variable">$1</span>]"</span> <span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span>
		<span class="token builtin class-name">echo</span> <span class="token string">"Check available searches"</span>
		<span class="token builtin class-name">return</span> <span class="token number">1</span>
	<span class="token keyword">fi</span>
	<span class="token keyword">if</span> <span class="token punctuation">[</span><span class="token punctuation">[</span> <span class="token variable">$#</span> -gt <span class="token number">1</span> <span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span>
		<span class="token assign-left variable">url</span><span class="token operator">=</span><span class="token string">"<span class="token variable">${urls<span class="token punctuation">[</span>$1<span class="token punctuation">]</span>}</span><span class="token variable">${(j<span class="token operator">:+</span><span class="token operator">:</span>)@<span class="token punctuation">[</span>2<span class="token operator">,</span>-1<span class="token punctuation">]</span>}</span>"</span>
	<span class="token keyword">else</span>
		<span class="token assign-left variable">url</span><span class="token operator">=</span><span class="token string">"<span class="token variable">${(j<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">/</span><span class="token operator">:</span>)${(s<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">:</span>)urls<span class="token punctuation">[</span>$1<span class="token punctuation">]</span>}</span>[1,2]}"</span>
	<span class="token keyword">fi</span>
	open_command <span class="token string">"<span class="token variable">$url</span>"</span>
<span class="token punctuation">}</span>
</code></pre>
<h3>Quick calculator</h3>
<p>From time to time I need to do quick calculations, for that one I use <a href="http://www.isthe.com/chongo/tech/comp/calc/" target="_blank">Calc — C-style arbitrary precision calculator</a>:</p>
<pre class="language-shell"><code class="language-shell">$ calc <span class="token number">24</span> * <span class="token number">4</span>
     <span class="token number">96</span>
$ calc
C-style arbitrary precision calculator <span class="token punctuation">(</span>version <span class="token number">2.12</span>.5.0<span class="token punctuation">)</span>
Calc is <span class="token function">open</span> software. For license details type:  <span class="token builtin class-name">help</span> copyright
<span class="token punctuation">[</span>Type <span class="token string">"exit"</span> to exit, or <span class="token string">"help"</span> <span class="token keyword">for</span> help.<span class="token punctuation">]</span>

<span class="token punctuation">;</span> <span class="token number">365</span>/12
 ~30.41666666666666666667
<span class="token punctuation">;</span> .*12
 <span class="token number">365</span>
<span class="token punctuation">;</span> <span class="token builtin class-name">exit</span>
</code></pre>
<h3>Dictionary</h3>
<p>Few people know <a href="https://en.wikipedia.org/wiki/DICT" target="_blank">DICT</a>, a dictionary network protocol and its command line companion <a href="http://linuxcommand.org/man_pages/dictd8.html" target="_blank">dictd</a>, a dictionary database server. After installing dictionaries, it's fairly simple to use and the result is formidable:</p>
<pre class="language-shell"><code class="language-shell">$ dict formidable
<span class="token number">3</span> definitions found

From WordNet <span class="token punctuation">(</span>r<span class="token punctuation">)</span> <span class="token number">3.0</span> <span class="token punctuation">(</span><span class="token number">2006</span><span class="token punctuation">)</span> <span class="token punctuation">[</span>wn<span class="token punctuation">]</span>:

formidable
      adj <span class="token number">1</span>: extremely impressive <span class="token keyword">in</span> strength or excellence<span class="token punctuation">;</span> <span class="token string">"a
             formidable opponent"</span><span class="token punctuation">;</span> <span class="token string">"the challenge was formidable"</span><span class="token punctuation">;</span>
             <span class="token string">"had a formidable array of compositions to his credit"</span><span class="token punctuation">;</span>
             <span class="token string">"the formidable army of brains at the Prime Minister's
             disposal"</span>
      <span class="token number">2</span>: inspiring fear<span class="token punctuation">;</span> <span class="token string">"the formidable prospect of major surgery"</span><span class="token punctuation">;</span>
         <span class="token string">"a tougher and more redoubtable adversary than the heel-
         clicking, jackbooted fanatic"</span>- G.H.Johnston<span class="token punctuation">;</span> <span class="token string">"something
         unnerving and prisonlike about high grey wall"</span> <span class="token punctuation">[</span>syn:
         <span class="token punctuation">{</span>formidable<span class="token punctuation">}</span>, <span class="token punctuation">{</span>redoubtable<span class="token punctuation">}</span>, <span class="token punctuation">{</span>unnerving<span class="token punctuation">}</span><span class="token punctuation">]</span>
<span class="token punctuation">..</span>.
</code></pre>
<p>To make it easy to use a few dictionaries and make the result more brief, I have the following aliases:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token builtin class-name">alias</span> <span class="token operator">|</span> <span class="token function">grep</span> dict
<span class="token assign-left variable">define</span><span class="token operator">=</span><span class="token string">'dict -d gcide'</span>          <span class="token comment"># english definitions</span>
<span class="token assign-left variable">dicts</span><span class="token operator">=</span><span class="token string">'dict -d moby-thesaurus'</span>  <span class="token comment"># synonym dictionary</span>
</code></pre>
<p>Follow <a href="https://www.unixmen.com/look-dictionary-definitions-via-terminal/" target="_blank">these instructions</a> if you want to install it.</p>
<h3>Translations</h3>
<p>Being a Brazilian living in Berlin makes me a heavy user of translation services. <a href="https://github.com/soimort/translate-shell" target="_blank">Translate Shell</a> is the best tool for that.</p>
<pre class="language-bash"><code class="language-bash">$ trans -brief ‘Super fácil traduzir com essa ferramenta<span class="token operator">!</span>’
Super easy to translate with this tool<span class="token operator">!</span>
</code></pre>
<p>Of course having some alias to make commands shorter is always good:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token builtin class-name">alias</span> <span class="token operator">|</span> <span class="token function">grep</span> trans
<span class="token assign-left variable">tre</span><span class="token operator">=</span>’trans -b’                 <span class="token comment"># translate with brief output</span>
<span class="token assign-left variable">trep</span><span class="token operator">=</span>’trans -b :pt’            <span class="token comment"># translate to Portuguese</span>
<span class="token assign-left variable">tres</span><span class="token operator">=</span>’trans -shell -brief’     <span class="token comment"># open a REPL to translate</span>
<span class="token assign-left variable">trev</span><span class="token operator">=</span>’trans -v’                <span class="token comment"># view translation in terminal pager</span>
</code></pre>
<h3>Task management</h3>
<p>I use <a href="https://www.trello.com" target="_blank">Trello</a> as task manager and I have a simple board to track my daily TODOs, tasks that I should be focusing during the day. Another wanted featured was to be able to add tasks easily to that board.</p>
<p><img src="https://www.talleye.com/img-posts/cppwt-trello.png" alt="">
<em>Above: Sample execution, now on Mac.</em></p>
<p>Implementation is done using the <a href="https://developers.trello.com/apis" target="_blank">API</a>. I could have installed a Trello command line tool, but I decided to keep it simple and I have the following:</p>
<pre class="language-shell"><code class="language-shell"><span class="token builtin class-name">alias</span> <span class="token assign-left variable">tasks</span><span class="token operator">=</span><span class="token string">'curl -s -X GET -H "Cache-Control: no-cache" -H "Terminalfred" "https://api.trello.com/1/lists/569623d6blablabla309c4d301?cards=open&#x26;card_fields=name&#x26;fields=cards&#x26;key=`cat ~/.secrets/trello.key.secret`&#x26;token=`cat ~/.secrets/trello.token.secret`" | jq ".cards[].name"'</span>

<span class="token keyword">function</span> <span class="token function-name function">taskadd</span> <span class="token punctuation">{</span>
    <span class="token assign-left variable">str</span><span class="token operator">=</span><span class="token string">"<span class="token variable">$*</span>"</span>
    <span class="token keyword">if</span> <span class="token punctuation">[</span> -z <span class="token string">"<span class="token variable">$str</span>"</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span>
        <span class="token builtin class-name">echo</span> usage: <span class="token variable">$0</span> task
    <span class="token keyword">else</span>
        <span class="token function">curl</span> -s -X POST -H <span class="token string">"Cache-Control: no-cache"</span> --data-urlencode <span class="token string">"name=<span class="token variable">${str}</span>"</span> -H <span class="token string">"Terminalfred"</span> <span class="token string">"https://api.trello.com/1/cards/?idList=569623d660blablablad301&#x26;key=<span class="token variable"><span class="token variable">`</span><span class="token function">cat</span> ~/.secrets/trello.key.secret<span class="token variable">`</span></span>&#x26;token=<span class="token variable"><span class="token variable">`</span><span class="token function">cat</span> ~/.secrets/trello.token.secret<span class="token variable">`</span></span>"</span> <span class="token operator">|</span> jq <span class="token string">".url"</span>
    <span class="token keyword">fi</span>
<span class="token punctuation">}</span>
</code></pre>
<p>In the code above I have one alias that basically do a request to fetch cards from a specific list on a board of mine. The result is in JSON format, then I use <a href="https://stedolan.github.io/jq/" target="_blank">jq tool</a>, a json command line processor to have a nice output like the snapshot shows above. The function to add a task is basically doing another request to create a card on the same list and as a result, I just add the link on the response.</p>
<p>Given that I want to sync zsh plugin file on Dropbox, the code expects to fetch Trello API secrets from a specific folder in my machines with the right permissions. Be safe!</p>
<h3>Launching applications</h3>
<p>To mention one limitation of using exclusively the terminal, I didn't find a good way to replace GUI application launch and sometimes I still rely on Alfred, Gnome Dash or simply global shortcuts to open them. Even so, I have found one function in a <a href="http://stackoverflow.com/questions/13384139/elegant-and-efficient-way-to-start-gui-programs-from-terminal-without-spamming-i" target="_blank">StackOverflow thread</a> that works if the GUI app command is available on the PATH:</p>
<pre class="language-shell"><code class="language-shell"><span class="token function-name function">o</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token variable">$#</span> -gt <span class="token number">0</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token keyword">then</span>
        <span class="token comment"># Expand if $1 is an alias</span>
        <span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">alias</span> <span class="token operator">|</span> <span class="token function">awk</span> -F <span class="token string">"[ =]"</span> <span class="token string">'{print $2}'</span> <span class="token operator">|</span> <span class="token function">grep</span> -x $1<span class="token variable">)</span></span> <span class="token operator">></span> <span class="token number">0</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token keyword">then</span>
            <span class="token builtin class-name">set</span> -- <span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">alias</span> $1 <span class="token operator">|</span> <span class="token function">awk</span> -F <span class="token string">"['']"</span> <span class="token string">'{print $2}'</span><span class="token variable">)</span></span> <span class="token string">"<span class="token variable">${@<span class="token operator">:</span>2}</span>"</span>
        <span class="token keyword">fi</span>
        <span class="token punctuation">(</span><span class="token variable">$@</span> <span class="token operator">&#x26;</span><span class="token punctuation">)</span> <span class="token operator">&#x26;></span>/dev/null
    <span class="token keyword">else</span>
        <span class="token builtin class-name">echo</span> <span class="token string">"missing argument"</span>
    <span class="token keyword">fi</span>
<span class="token punctuation">}</span>
</code></pre>
<p>The main benefit is that you start the GUI app detached from terminal and avoiding <code>stdout</code> flooding your shell. However, to be honest, I don't use that much.</p>
<h2>Everything is possible</h2>
<p>If you achieved that point in the article I believe you get the point, we just scratched the surface of what can be done with a Terminal and basically there's no limit on what you can do to automate tasks and be more productive. You can find other tools I use, such as consulting timezones or finding files, in <a href="https://gist.github.com/lfcipriani/f6baa463fa1c600a5bf7ce3ecf764321#file-terminalfred-plugin-zsh" target="_blank"><strong>my plugin file</strong></a>.</p>
<p>Hope the lives of cross platform users are improved a little bit with that approach. As a last note, I purposely ignored Windows because I'm not an active user, but if you are able to have a similar setup using it let me know and I can update this article or link to yours.</p>
<p><em>This post was originally published on <a href="https://hackernoon.com/cross-platform-productivity-tool-with-terminal-7dd0487ead93#.z4ve1oeuo" target="_blank">Hackernoon</a>.</em></p>]]></description>
            <link>https://www.talleye.com/posts/cross-platform-productivity-with-terminal</link>
            <guid isPermaLink="true">https://www.talleye.com/posts/cross-platform-productivity-with-terminal</guid>
            <dc:creator><![CDATA[Luis Cipriani]]></dc:creator>
            <pubDate>Tue, 07 Feb 2017 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Project: Pinboogle]]></title>
            <description><![CDATA[<p>When bootstrapping <a href="https://www.nestpick.com/" target="_blank">Nestpick</a> in 2016, I decided to work on a web scraping and search playground so I could learn and prototype the features that we would be adding to the product later. Nestpick is an apartment mid-term rental aggregator that allow users to search on several providers at once. The technical solution relies on scraping these partners via API or web and indexing this data on a search engine component.</p>
<p>I partnered again with <a href="https://twitter.com/lsdr" target="_blank">Luiz Rocha</a>, a long time friend, to build this playground using <a href="https://scrapy.org/" target="_blank">Scrapy</a> and <a href="https://solr.apache.org/" target="_blank">Solr</a>.</p>
<blockquote>
<p><a href="https://github.com/spare-time/pinboogle#pinboogle" target="_blank">The code is available at Github</a></p>
</blockquote>
<p>The project is structured in X parts:</p>
<ul>
<li>Web scraper that fetches the content of all bookmarked links in <a href="https://pinboard.in/" target="_blank">Pinboard.in</a>, using Scrapy.</li>
<li>Index that is stored in Solr search engine</li>
<li>A search frontend built in python Flask framework that work as a Solr client.</li>
</ul>
<p>The project README shows you how to setup it yourself so feel free to use it in case you need to play around search engines and web scraping.</p>]]></description>
            <link>https://www.talleye.com/posts/project-pinboogle</link>
            <guid isPermaLink="true">https://www.talleye.com/posts/project-pinboogle</guid>
            <dc:creator><![CDATA[Luis Cipriani]]></dc:creator>
            <pubDate>Sat, 12 Nov 2016 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Twitter Fabric evangelism and sample projects]]></title>
            <description><![CDATA[<p>From 2013 to 2015 I worked at Twitter as a developer advocate in the International developer relations team, presenting Twitter platforms to developers in Latin America and the rest of the world. One of these platforms was Fabric, a mobile platform with several solutions to speed up mobile development and that was later acquired by Google and merged with Firebase.</p>
<p><img src="https://www.talleye.com/img-posts/tfesp-flock-seoul.jpg" alt=""></p>
<p><em>Twitter Flock event in Seoul, South Korea</em></p>
<p>Beyond <a href="https://www.infoq.com/br/presentations/fabric-na-pratica/" target="_blank">doing talks</a> in several events, I also built some sample projects that were used as canonical examples of the platform or simply to show off the platform capabilities.</p>
<h2>Live Event Android App</h2>
<p>A sample Android Live Event app using Fabric to help your participants to follow what's happening around an event at Twitter.</p>
<p><img src="https://www.talleye.com/img-posts/tfesp-live-event-android-thumb.jpg" alt=""></p>
<p><a href="https://github.com/lfcipriani/live-event-android" target="_blank">Source code available in Github</a>.</p>
<h2>Cannonball Android App</h2>
<p>In October 2014 Twitter launched <a href="https://get.fabric.io/" title="visit the website" target="_blank">Fabric</a>, a platform for mobile developers that provides several tools to improve their mobile apps. To showcase the tools this platform provided we built Cannonball demo app, a word game similar to magnetic poetry, in which the user can create poems, share with friends, see the popular ones all using the platform features such as: digits, social login, twitter kit, mopub, crashlytics and answers.</p>
<p><img src="https://www.talleye.com/img-posts/tfesp-cannonball-android-app-thumb.jpg" alt=""></p>
<p><a href="https://github.com/lfcipriani/cannonball-android" target="_blank">Source code available in Github</a>.</p>]]></description>
            <link>https://www.talleye.com/posts/twitter-fabric-evangelism-and-sample-projects</link>
            <guid isPermaLink="true">https://www.talleye.com/posts/twitter-fabric-evangelism-and-sample-projects</guid>
            <dc:creator><![CDATA[Luis Cipriani]]></dc:creator>
            <pubDate>Fri, 24 Jul 2015 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Project: Tweet Jukebox]]></title>
            <description><![CDATA[<p>At Twitter, every Friday we have Tea Time, which is a team event in the office to wrap up the week and hangout with colleagues. Usually we have music playing in the background and this gave me the idea to build a Jukebox powered by tweets, but it had to be something that I just plug in the power outlet and work out of the box.</p>
<p>This is the result after a few weeks of work:</p>
<p><img src="https://www.talleye.com/img-posts/ptj-tweetjukebox.png" alt=""></p>
<h2>Architecture</h2>
<p>Under the hood this is what's happening:</p>
<pre class="language-js"><code class="language-js">                <span class="token operator">+</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">-</span><span class="token operator">+</span>
                <span class="token operator">|</span>                                 <span class="token operator">|</span>
<span class="token maybe-class-name">Tweet</span>           <span class="token operator">|</span> <span class="token maybe-class-name">Raspberry</span> <span class="token maybe-class-name">Pi</span>                    <span class="token operator">|</span>
<span class="token punctuation">(</span>music request<span class="token punctuation">)</span> <span class="token operator">|</span>                                 <span class="token operator">|</span>
   <span class="token operator">+</span>            <span class="token operator">|</span> <span class="token operator">+</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">-</span><span class="token operator">+</span>      <span class="token operator">+</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">+</span> <span class="token operator">|</span>       <span class="token maybe-class-name">Music</span> powered by
   <span class="token operator">|</span>            <span class="token operator">|</span> <span class="token operator">|</span> <span class="token maybe-class-name">Twitter</span>   <span class="token operator">|</span> <span class="token operator">+</span><span class="token operator">--</span><span class="token operator">></span> <span class="token operator">|</span> <span class="token maybe-class-name">Mopidy</span>   <span class="token operator">|</span> <span class="token operator">|</span>             <span class="token maybe-class-name">Spotify</span>
   <span class="token operator">+</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">></span> <span class="token maybe-class-name">Streaming</span> <span class="token operator">|</span>      <span class="token operator">|</span> <span class="token maybe-class-name">Server</span>   <span class="token operator">+</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">></span>  <span class="token maybe-class-name">Youtube</span>
     network    <span class="token operator">|</span> <span class="token operator">|</span> <span class="token constant">API</span>       <span class="token operator">|</span> <span class="token operator">&#x3C;</span><span class="token operator">--</span><span class="token operator">+</span> <span class="token operator">|</span>          <span class="token operator">|</span> <span class="token operator">|</span>  audio      <span class="token maybe-class-name">Soundcloud</span>
                <span class="token operator">|</span> <span class="token operator">+</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">-</span><span class="token operator">+</span>      <span class="token operator">+</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">+</span> <span class="token operator">|</span>
                <span class="token operator">|</span>                                 <span class="token operator">|</span>
                <span class="token operator">+</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">-</span><span class="token operator">+</span>
</code></pre>
<p>This is what's running on the Raspberry Pi:</p>
<ul>
<li><a href="https://www.mopidy.com/" target="_blank">Mopidy</a> python open source music server</li>
<li>Mopidy <a href="https://github.com/mopidy/mopidy-spotify" target="_blank">Spotify</a>, <a href="https://github.com/dz0ny/mopidy-youtube" target="_blank">Youtube</a>, <a href="https://github.com/mopidy/mopidy-soundcloud" target="_blank">Soundcloud</a> plugins that sources audio from these services.</li>
<li>Written in Node.js</li>
<li>UsesTwitter <a href="https://dev.twitter.com/rest/public" target="_blank">REST</a> and <a href="https://dev.twitter.com/streaming/overview" target="_blank">Streaming</a> API to monitor mentions to the bot username requesting music.</li>
</ul>
<h2>Twitter Bot syntax</h2>
<p><img src="https://www.talleye.com/img-posts/ptj-tweet.png" alt=""></p>
<p>The syntax is based on a simple rule:</p>
<pre><code>@jukebox_user song name [by artist] [#spotify|#youtube|#soundcloud]
</code></pre>
<p>or</p>
<pre><code>@jukebox_user [link to a spotify, youtube or soundcloud music]
</code></pre>
<ul>
<li>bot will play the first result found for the query sent to Mopidy</li>
<li>artist is optional, but can help to search the right song</li>
<li>you can restrict the search to a specific source by using the hashtag with source name</li>
<li>if no music #source is specified, it will play the first result found on Spotify, then Youtube, then Soundcloud</li>
</ul>
<p>A pre-configured admin user can send DM messages to jukebox user to #play, #pause, #next (skip song).</p>
<h2>Hardware</h2>
<p>To be able to run out of the box, I improvised a case with a display and some buttons to control the music, the skip button is especially important to avoid some unwanted songs 😁.</p>
<p>The schematic below shows the connections from raspberry PI GPIO and the buttons and displays.</p>
<p><img src="https://www.talleye.com/img-posts/ptj-schematic.png" alt=""></p>
<h2>Source Code</h2>
<p>You can find the <a href="https://github.com/lfcipriani/tweet-jukebox" target="_blank">source code in Github</a>. Hope you have as much fun as I did running this jukebox in the office.</p>]]></description>
            <link>https://www.talleye.com/posts/project-tweet-jukebox</link>
            <guid isPermaLink="true">https://www.talleye.com/posts/project-tweet-jukebox</guid>
            <dc:creator><![CDATA[Luis Cipriani]]></dc:creator>
            <pubDate>Fri, 14 Nov 2014 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[API Rosetta Code]]></title>
            <description><![CDATA[<div class="float-right">
    <img src="https://www.talleye.com/img-posts/arc-thumb.jpg" width="400" height="308" loading="lazy" alt="Screenshot">
</div>
<p>Sometimes a good API documentation or playground is enough to understand and grow adoption of a platform. To help developers to cope very easily with an API, providing integration code in the language they are familiar is a good way to drive a faster adoption, because it will be clear how to use that API on code and not simply a bunch of isolated requests and responses.</p>
<p><a href="http://rosettacode.org/wiki/Rosetta_Code" target="_blank">Rosetta Code</a> is an website that allow you to compare certain algorithm implementations in several languages. Inspired on that, API Rosetta Code goal is to allow developers to have certain use cases of the API implemented in several languages and libraries.</p>
<p>By simply following a directory organization and running a generator, you can have a website with all the samples built by your software engineers or by the community. To launch this experiment on Twitter, I organized 3 coding dojos to estimulate contributions from the developer community in Brazil.</p>
<p>If you want to know how it works to use in your platform ecosystem, check the <a href="https://github.com/lfcipriani/api-rosetta-code" target="_blank">project source code</a> in Github.</p>]]></description>
            <link>https://www.talleye.com/posts/api-rosetta-code</link>
            <guid isPermaLink="true">https://www.talleye.com/posts/api-rosetta-code</guid>
            <dc:creator><![CDATA[Luis Cipriani]]></dc:creator>
            <pubDate>Tue, 11 Nov 2014 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Goal! Detecting the most important World Cup moments]]></title>
            <description><![CDATA[<p><em>This article was originally published in <a href="https://blog.twitter.com/developer/en_us/a/2014/goal-detecting-the-most-important-world-cup-moments.html" target="_blank">Twitter Developer Blog</a> during my time there as Developer Advocate.</em></p>
<p>Billions of fans around the world are tuned in to the World Cup matches, and many use Twitter to support their teams and join in the global public conversation about the games.</p>
<p>At the end of May, we held a World Cup-themed hackathon in São Paulo, Brazil. Between helping to organize the event and mentoring some of the teams, I played a little bit with <a href="http://www.raspberrypi.org/" target="_blank">Raspberry Pi</a> and the <a href="https://dev.twitter.com/docs/api/streaming" target="_blank">Twitter Streaming API</a>. My idea was to build a goal bell that would ring when the volume of Tweets around a specific goal grew very quickly. In order to do this, I had to connect to the Streaming API so as to calculate the frequency of Tweets mentioning “goal”, and create a device that would ring a bell when a spike in that frequency was detected.</p>
<p>Spoiler alert: if you are curious about how I built the bell before hearing the specifics, take a look at this video:</p>
<div class="media">
<video controls loop playsinline preload="auto" class="media-asset">
    <source src="/img-posts/gdmiwcm-tilingol.m4v" type="video/mp4">
    <p>Your browser doesn't support HTML5 video. Here is
     a <a href="https://www.talleye.com/img-posts/gdmiwcm-tilingol.m4v">link to the video</a> instead.</p>
</video>
</div>
<h2>Detecting spikes in Tweet volume</h2>
<p>Detecting relevant events in time-series data is not exactly rocket science, and there are <a href="http://scholar.google.com.br/scholar?hl=en&#x26;q=event+detection+twitter&#x26;btnG=&#x26;as_sdt=1%2C5&#x26;as_sdtp=" target="_blank">plenty of research articles on that topic</a>. But sometimes the implementation of these techniques is too complex to be applied in short period of time. I decided to build a simpler, less-precise solution that would work well enough for my needs. I started with the hypothesis that during any given game, lots of fans would Tweet when a goal was scored.</p>
<p>Thankfully, some simulations I did demonstrated that this pulse can be easily detected. Take a look at this data from the second half of the first World Cup match between Brazil and Croatia:</p>
<p><img src="https://www.talleye.com/img-posts/gdmiwcm-tps01.png" alt="">
<em>Image 1: Tweets per second mentioning “gol copa, gool copa, goool, golaço” during the match June 12th, 2014.</em></p>
<p>To get this data, I simply connected to the <a href="https://dev.twitter.com/docs/api/1.1/post/statuses/filter" target="_blank">POST status/filter</a> endpoint of the <a href="https://dev.twitter.com/docs/api/streaming" target="_blank">Streaming API</a>. There I monitored for mentions of the Brazilian Portuguese words for “goal”, “world cup” and their variations, and then calculated their frequency in Tweets per second (TPS). Looking at the resulting graph above it is easy to identify when the goals occured.</p>
<p>The next step was to convert this noisy data into a baseline value that could serve as a trigger for the bell when that threshold was reached. To achieve this, I used a simple statistical tool: <a href="http://en.wikipedia.org/wiki/Exponential_smoothing" target="_blank">exponential moving average (EMA)</a>. Rather than showing you all the formulas, let’s instead visualize what EMA does:</p>
<p><img src="https://www.talleye.com/img-posts/gdmiwcm-tps02.png" alt="">
<em>Image 2: EMA calculated based on Tweets per second around the first goal during the second half of Brazil vs. Croatia.</em></p>
<p>As you can see, EMA smoothed the noisy Tweets-per-second data and gave us a cleaner time-series data set. To detect the goal spike, I then calculated a simple growth value relative to 10 seconds prior: <strong>(EMA now - EMA 10 sec ago) / EMA 10 sec ago</strong>. Let’s again use a visualization to see how this formula helped to determine the baseline trigger:</p>
<p><img src="https://www.talleye.com/img-posts/gdmiwcm-tps03.png" alt="">
<em>Image 3: Spike detection using relative growth around the first goal during the second half of Brazil vs. Croatia.</em></p>
<p>When the growth calculation results were greater than 1.5 (which meant that we saw a 150% growth rate in Tweets per second between present and the previous 10 seconds) the algorithm fired the trigger that happily rang the goal bell. Let’s zoom out and see the whole second half, removing the EMA curve:</p>
<p><img src="https://www.talleye.com/img-posts/gdmiwcm-tps04.png" alt="">
<em>Image 4: Spike detection using relative growth during the second half of Brazil vs. Croatia. Yes, we had two goals scored during this period.</em></p>
<p>It was interesting to see that the first goal had stronger growth. In my opinion, Twitter users were ready to tweet as soon as the goal was scored (because it was a penalty kick by <a href="https://twitter.com/neymarjr" target="_blank">Neymar</a>), which resulted in more people tweeting over a shorter period of time. As you can also see on the last graph, the way growth was calculated helped to identify only when the Tweets-per-second frequency varied more quickly and in a larger amplitude (i.e., when a very exciting event happened during a game).</p>
<p>There are implementations of these calculations in <a href="https://github.com/lfcipriani/tilingol/blob/master/python/peak_detection.py" target="_blank">Python</a> and <a href="https://github.com/lfcipriani/tilingol/blob/master/peak_detection.rb" target="_blank">Ruby</a> if you want to try them out yourself. The code for connecting to the Streaming API is relatively short and simple:</p>
<pre class="language-ruby"><code class="language-ruby"><span class="token variable">@client</span> <span class="token operator">=</span> TweetStream<span class="token double-colon punctuation">::</span><span class="token class-name">Client</span><span class="token punctuation">.</span><span class="token keyword">new</span>

<span class="token variable">@client</span><span class="token punctuation">.</span>on_error <span class="token keyword">do</span> <span class="token operator">|</span>message<span class="token operator">|</span>
  puts <span class="token string-literal"><span class="token string">"ERROR: </span><span class="token interpolation"><span class="token delimiter punctuation">#{</span><span class="token content">message</span><span class="token delimiter punctuation">}</span></span><span class="token string">"</span></span>
<span class="token keyword">end</span>
<span class="token variable">@client</span><span class="token punctuation">.</span>on_enhance_your_calm <span class="token keyword">do</span>
  puts <span class="token string-literal"><span class="token string">"Calm down"</span></span>
<span class="token keyword">end</span>
<span class="token variable">@client</span><span class="token punctuation">.</span>on_limit <span class="token keyword">do</span> <span class="token operator">|</span>skip_count<span class="token operator">|</span>
  puts <span class="token string-literal"><span class="token string">"You lost </span><span class="token interpolation"><span class="token delimiter punctuation">#{</span><span class="token content">skip_count</span><span class="token delimiter punctuation">}</span></span><span class="token string"> tweets"</span></span>
<span class="token keyword">end</span>

puts <span class="token string-literal"><span class="token string">"Starting to track: </span><span class="token interpolation"><span class="token delimiter punctuation">#{</span><span class="token content"><span class="token variable">@keywords</span></span><span class="token delimiter punctuation">}</span></span><span class="token string">...\nLanguages: </span><span class="token interpolation"><span class="token delimiter punctuation">#{</span><span class="token content"><span class="token variable">@languages</span></span><span class="token delimiter punctuation">}</span></span><span class="token string">"</span></span>
<span class="token variable">@client</span><span class="token punctuation">.</span>filter<span class="token punctuation">(</span><span class="token symbol">:track</span> <span class="token operator">=></span> <span class="token variable">@keywords</span><span class="token punctuation">,</span> <span class="token symbol">:language</span> <span class="token operator">=></span> <span class="token variable">@languages</span><span class="token punctuation">)</span> <span class="token keyword">do</span> <span class="token operator">|</span>status<span class="token operator">|</span>
  <span class="token variable">@peak_detector</span><span class="token punctuation">.</span>collect_frequency

  <span class="token variable">@bell</span><span class="token punctuation">.</span>ring<span class="token operator">!</span> <span class="token keyword">if</span> <span class="token variable">@peak_detector</span><span class="token punctuation">.</span>is_this_a_peak<span class="token operator">?</span> <span class="token operator">||</span> status<span class="token punctuation">.</span>text<span class="token punctuation">.</span>index<span class="token punctuation">(</span><span class="token variable">@magic_word</span><span class="token punctuation">)</span>
<span class="token keyword">end</span>
</code></pre>
<p><a href="https://github.com/lfcipriani/tilingol/blob/master/tilingol.rb#L40" target="_blank">https://github.com/lfcipriani/tilingol/blob/master/tilingol.rb#L40</a></p>
<p>You can also <a href="https://github.com/lfcipriani/tilingol/blob/master/data/TweetsPerSecond_BrazilvsCroatia.csv" target="_blank">download the data</a> captured during the game if you want to try plotting it in other ways.</p>
<h2>Ringing the bell</h2>
<p>Once you have the implemented goal-detection algorithm, your device should then ring the goal bell. I decided to use a <a href="http://www.raspberrypi.org/" target="_blank">Raspberry Pi</a> for this. This was the first time I played with it, and I found its potential to be amazing. I built the bell using materials I had at home and borrowed some other items from friends. Here’s the parts list with a brief explanation on how each piece was used:</p>
<ul>
<li>Raspberry Pi rev 2: The computer unit that ran the Streaming API connection, detected the spikes and controlled ringing of the bell;</li>
<li><a href="http://en.wikipedia.org/wiki/Servomotor" target="_blank">Servo motor</a>: A tiny motor that allowed precise control of position, velocity and acceleration. It was used to shake the bells;</li>
<li><a href="http://en.wikipedia.org/wiki/Jump_wire" target="_blank">Jumper wires</a>: These were used to connect the Raspberry Pi and the servo motor;</li>
<li><a href="https://www.google.com/search?q=jingle%20bells&#x26;gws_rd=ssl&#x26;tbm=isch" target="_blank">Christmas jingle bells</a>: An appropriately exciting sound for goal notifications;</li>
<li>Plastic coffee mixer: This was used to increase the movement amplitude of the servo motor;</li>
<li>Paper clips: These held the bells;</li>
<li>Lego blocks: These were used to build the structure that held the motor and the Raspberry Pi… not to mention making the project look cool and nerdy.</li>
</ul>
<p>Here’s a graphic representation of the connections, all fairly simple:</p>
<p><img src="https://www.talleye.com/img-posts/gdmiwcm-schematic.png" alt="">
<em>Image 5: Schematic of the hardware project. Use at your own risk!</em></p>
<p>The code that activates the pin and does the shaking is below:</p>
<pre class="language-python"><code class="language-python"><span class="token keyword">import</span> RPi<span class="token punctuation">.</span>GPIO <span class="token keyword">as</span> GPIO
<span class="token keyword">import</span> time
<span class="token keyword">import</span> sys

<span class="token keyword">class</span> <span class="token class-name">JingleBells</span><span class="token punctuation">:</span>

    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> pin<span class="token punctuation">)</span><span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>pin <span class="token operator">=</span> pin
        GPIO<span class="token punctuation">.</span>setmode<span class="token punctuation">(</span>GPIO<span class="token punctuation">.</span>BCM<span class="token punctuation">)</span>
        GPIO<span class="token punctuation">.</span>setup<span class="token punctuation">(</span>self<span class="token punctuation">.</span>pin<span class="token punctuation">,</span> GPIO<span class="token punctuation">.</span>OUT<span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">shake</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> iterations<span class="token punctuation">,</span> speed<span class="token punctuation">)</span><span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>pwm <span class="token operator">=</span> GPIO<span class="token punctuation">.</span>PWM<span class="token punctuation">(</span>self<span class="token punctuation">.</span>pin<span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>pwm<span class="token punctuation">.</span>start<span class="token punctuation">(</span><span class="token number">19</span><span class="token punctuation">)</span>
        time<span class="token punctuation">.</span>sleep<span class="token punctuation">(</span>speed<span class="token punctuation">)</span>
        <span class="token keyword">for</span> n <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span>iterations<span class="token punctuation">)</span><span class="token punctuation">:</span>
            self<span class="token punctuation">.</span>pwm<span class="token punctuation">.</span>ChangeDutyCycle<span class="token punctuation">(</span><span class="token number">16</span><span class="token punctuation">)</span>
            time<span class="token punctuation">.</span>sleep<span class="token punctuation">(</span>speed<span class="token punctuation">)</span>
            self<span class="token punctuation">.</span>pwm<span class="token punctuation">.</span>ChangeDutyCycle<span class="token punctuation">(</span><span class="token number">19</span><span class="token punctuation">)</span>
            time<span class="token punctuation">.</span>sleep<span class="token punctuation">(</span>speed<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>pwm<span class="token punctuation">.</span>stop<span class="token punctuation">(</span><span class="token punctuation">)</span>

    <span class="token comment">#def __del__(self):</span>
    <span class="token comment">#    GPIO.cleanup()</span>

<span class="token keyword">if</span> __name__ <span class="token operator">==</span> <span class="token string">'__main__'</span><span class="token punctuation">:</span>
    jb <span class="token operator">=</span> JingleBells<span class="token punctuation">(</span><span class="token number">18</span><span class="token punctuation">)</span>
    jb<span class="token punctuation">.</span>shake<span class="token punctuation">(</span><span class="token builtin">int</span><span class="token punctuation">(</span>sys<span class="token punctuation">.</span>argv<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token builtin">float</span><span class="token punctuation">(</span>sys<span class="token punctuation">.</span>argv<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
    GPIO<span class="token punctuation">.</span>cleanup<span class="token punctuation">(</span><span class="token punctuation">)</span>
    exit<span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre>
<p><a href="https://github.com/lfcipriani/tilingol/blob/master/jinglebells.py" target="_blank">https://github.com/lfcipriani/tilingol/blob/master/jinglebells.py</a></p>
<p>You just need to execute the Python file and decide how many times the bell will shake (iterations parameter) and at what speed. Then we just call this program from the goal detection algorithm we previously implemented. The only important thing is to call it asynchronously, because we don’t want to enqueue the flow of Tweets coming through the Streaming connection (see <a href="https://github.com/lfcipriani/tilingol/blob/master/jingle_bell.rb" target="_blank">https://github.com/lfcipriani/tilingol/blob/master/jingle_bell.rb</a>).</p>
<p>It’s out of scope of this article to explain what <a href="http://en.wikipedia.org/wiki/Pulse-width_modulation" target="_blank">PWM</a> is and how to use <a href="https://pypi.python.org/pypi/RPi.GPIO" target="_blank">GPIO library</a>, but know that there are many good resources about that on <a href="http://www.raspberrypi.org/" target="_blank">Raspberry Pi’s website</a>.</p>
<h2>Wrapping up</h2>
<p>I just showed you how easily you can detect goals using the volume of Tweets coming from the Streaming API. After identifying these goals, you can decide the best way to notify someone that it happened… but I must say, using a Raspberry Pi is a really fun way to do it. When talking about the Twitter platform, we always say that the API is a great way to connect to the pulse of the planet and here we’ve proven that it can be tapped and interpreted in fun, interesting ways!</p>
<p>All the code built to make this happen is available at <a href="https://github.com/lfcipriani/tilingol" target="_blank">GitHub</a>. Also I want to thank <a href="https://twitter.com/intent/user?screen_name=luisleao" target="_blank">@luisleao</a> for helping with the hardware part during the hackathon.</p>
<h2>One more thing</h2>
<p>This blog post was later turned into a tech talk where I detailed a bit more the hardware part and some challenges I faced when building this project. The slides are embedded below:</p>
<p><a href="https://www.slideshare.net/lfcipriani/adventures-with-raspberry-pi-and-twitter-api" title="Open Slideshare" target="_blank"><img src="https://www.talleye.com/img-posts/gdmiwcm-slides.jpg" alt=""></a></p>]]></description>
            <link>https://www.talleye.com/posts/goal-detecting-the-most-important-world-cup-moments</link>
            <guid isPermaLink="true">https://www.talleye.com/posts/goal-detecting-the-most-important-world-cup-moments</guid>
            <dc:creator><![CDATA[Luis Cipriani]]></dc:creator>
            <pubDate>Mon, 23 Jun 2014 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Twitter Realtime Voting]]></title>
            <description><![CDATA[<div class="float-right">
    <img src="https://www.talleye.com/img-posts/trv-thumb.jpg" width="400" height="308" loading="lazy" alt="Screenshot">
</div>
<p>In 2014 I was asked to do a Workshop at QCon in São Paulo about Twitter Streaming APIs and I decided to build a website that would allow people to evaluate the talks just like QCon usually do for their events (green, yellow, red cards) but with tweets instead.</p>
<p>I built it using Twitter Streaming API and monitoring specific hashtags to increment the counter and a ruby stack: Sinatra and faye-websocket as backend, Redis as storage and jQuery and Bootstrap for the frontend.</p>
<p>If you interested on how to implement real time counting, how to monitor hashtags in Twitter or how to update web user interfaces using websockets you can check the <a href="https://github.com/lfcipriani/twitter-realtime-voting" target="_blank">source code</a>.</p>
<h2>Technologies used</h2>
<ul>
<li>Twitter Streaming API and tweetstream gem</li>
<li>ruby 2.x</li>
<li>Sinatra (web dashboard)</li>
<li>faye-websocket (Websocket middleware)</li>
<li>redis (storage)</li>
<li>jQuery and Bootstrap (frontend)</li>
</ul>
<h2>Installing and running</h2>
<ol>
<li>Clone <a href="https://github.com/lfcipriani/twitter-realtime-voting" target="_blank">the repo</a></li>
<li>Set up your Twitter credentials in <code>config/credentials.yml</code> with your app tokens obtained at apps.twitter.com (see <code>config/credentials.yml.sample</code>)</li>
<li>Set up your <code>config/agenda.yml</code> file with all talks, the hashtag that will trigger a vote for each one and also their attributes (see <code>config/agenda.yml.sample</code>)</li>
<li>Open <code>config/initializer.rb</code> and define:
<ul>
<li>Your Event hashtag (used by Twitter tracker)</li>
<li>Which words represent each level of quality (see <code>EVENT_REVIEW_GRADES</code>)</li>
<li>If you will be accepting only one vote per user (see <code>ACCEPT_ONLY_UNIQUE_VOTES</code>)</li>
</ul>
</li>
<li>Install and start redis</li>
<li>Run <code>bundle install</code> to set up environment</li>
<li>Run <code>foreman start</code></li>
<li>Access <code>http://localhost:3000</code> in your browser</li>
<li>Vote! ex.: <em>"#qconsp #twitterapi #good awesome talk"</em></li>
</ol>
<p>Try to vote to a talk while having it's permalink page open to see the page updating the count</p>
<p>Have fun!</p>]]></description>
            <link>https://www.talleye.com/posts/twitter-realtime-voting</link>
            <guid isPermaLink="true">https://www.talleye.com/posts/twitter-realtime-voting</guid>
            <dc:creator><![CDATA[Luis Cipriani]]></dc:creator>
            <pubDate>Fri, 09 May 2014 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Bootstrapping Sign in with Twitter]]></title>
            <description><![CDATA[<p><em>This article was originally published in <a href="https://blog.twitter.com/developer/en_us/a/2014/bootstrapping-sign-in-with-twitter.html" target="_blank">Twitter Develop Blog</a> during my time there as Developer Advocate.</em></p>
<p>Implementing <a href="https://dev.twitter.com/docs/auth/sign-twitter" target="_blank">Sign in with Twitter</a> is a great way to seamlessly integrate the user experience of your app or site with the Twitter platform and enable users to share content generated in your app with their followers.</p>
<p>The first step to implementing Sign in with Twitter is using OAuth, a widely-adopted standard that enables third parties to access resources without having to exchange passwords. Even when the <a href="https://dev.twitter.com/docs/auth/implementing-sign-twitter" target="_blank">specifications on how to implement OAuth</a> are detailed and clear, it can still be a complex and frustrating task.</p>
<p>That’s why we built some bootstrapping code in Ruby and Python to help you get started quickly and confidently, giving you more time to focus on your product. The examples are easy to install and try. Let’s do a walkthrough in the <a href="https://github.com/lfcipriani/sign_in_with_twitter_sample" target="_blank">Ruby implementation</a>, a lightweight Sinatra app. Note that the code follows the process outlined in the <a href="https://dev.twitter.com/docs/auth/implementing-sign-twitter" target="_blank">Sign in with Twitter documentation</a>.</p>
<h2>Step 1: Obtain the request token</h2>
<p>When the user clicks “Sign in” in your application, the first step is to obtain a request token:</p>
<p><a href="https://github.com/lfcipriani/sign_in_with_twitter_sample/blob/master/app.rb#L48" target="_blank">Listing 1</a>:</p>
<pre class="language-ruby"><code class="language-ruby">get <span class="token string-literal"><span class="token string">'/signin'</span></span> <span class="token keyword">do</span>
  <span class="token comment"># After hitting Sign in link, first thing your app must do</span>
  <span class="token comment"># is to get a request token.</span>
  <span class="token comment"># See https://dev.twitter.com/docs/auth/implementing-sign-twitter (Step 1)</span>
  token <span class="token operator">=</span> TwitterSignIn<span class="token punctuation">.</span>request_token

  <span class="token comment"># With request token in hands, you will just redirect</span>
  <span class="token comment"># the user to authenticate at Twitter</span>
  <span class="token comment"># See https://dev.twitter.com/docs/auth/implementing-sign-twitter (Step 2)</span>
  redirect TwitterSignIn<span class="token punctuation">.</span>authenticate_url<span class="token punctuation">(</span>token<span class="token punctuation">)</span>
<span class="token keyword">end</span>
</code></pre>
<p>The <code>request_token</code> method is implemented in the TwitterSignIn class like so:</p>
<p><a href="https://github.com/lfcipriani/sign_in_with_twitter_sample/blob/master/lib/twitter_sign_in.rb#L15" target="_blank">Listing 2</a>:</p>
<pre class="language-ruby"><code class="language-ruby"><span class="token keyword">def</span> <span class="token method-definition"><span class="token function">request_token</span></span>

    <span class="token comment"># The request to get request tokens should only</span>
    <span class="token comment"># use consumer key and consumer secret, no token</span>
    <span class="token comment"># is necessary</span>
    response <span class="token operator">=</span> TwitterSignIn<span class="token punctuation">.</span>request<span class="token punctuation">(</span>
    <span class="token symbol">:post</span><span class="token punctuation">,</span>
    <span class="token string-literal"><span class="token string">"https://api.twitter.com/oauth/request_token"</span></span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token variable">@oauth</span>
    <span class="token punctuation">)</span>

    obj <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
    vars <span class="token operator">=</span> response<span class="token punctuation">.</span>body<span class="token punctuation">.</span>split<span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"&#x26;"</span></span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token keyword">each</span> <span class="token keyword">do</span> <span class="token operator">|</span>v<span class="token operator">|</span>
    obj<span class="token punctuation">[</span>v<span class="token punctuation">.</span>split<span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"="</span></span><span class="token punctuation">)</span><span class="token punctuation">.</span>first<span class="token punctuation">]</span> <span class="token operator">=</span> v<span class="token punctuation">.</span>split<span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"="</span></span><span class="token punctuation">)</span><span class="token punctuation">.</span>last
    <span class="token keyword">end</span>

    <span class="token comment"># oauth_token and oauth_token_secret should</span>
    <span class="token comment"># be stored in a database and will be used</span>
    <span class="token comment"># to retrieve user access tokens in next requests</span>
    db <span class="token operator">=</span> Daybreak<span class="token double-colon punctuation">::</span><span class="token class-name">DB</span><span class="token punctuation">.</span><span class="token keyword">new</span> <span class="token constant">DATABASE</span>
    db<span class="token punctuation">.</span>lock <span class="token punctuation">{</span> db<span class="token punctuation">[</span>obj<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"oauth_token"</span></span><span class="token punctuation">]</span><span class="token punctuation">]</span> <span class="token operator">=</span> obj <span class="token punctuation">}</span>
    db<span class="token punctuation">.</span>close

    <span class="token keyword">return</span> obj<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"oauth_token"</span></span><span class="token punctuation">]</span>
<span class="token keyword">end</span>
</code></pre>
<p>Basically, this request will return <code>oauth_token</code> and <code>oauth_token_secret</code> and these are your temporary credentials given by Twitter to request a user access token.</p>
<h2>Step 2: Redirect the user</h2>
<p>Now we can redirect the user to /oauth/authenticate, passing the oauth_token obtained in the previous step.</p>
<p><a href="https://github.com/lfcipriani/sign_in_with_twitter_sample/blob/master/lib/twitter_sign_in.rb#L43" target="_blank">Listing 3</a>:</p>
<pre class="language-ruby"><code class="language-ruby"><span class="token keyword">def</span> <span class="token method-definition"><span class="token function">authenticate_url</span></span><span class="token punctuation">(</span>query<span class="token punctuation">)</span>
    <span class="token comment"># The redirection need to be done with oauth_token</span>
    <span class="token comment"># obtained in request_token request</span>
    <span class="token string-literal"><span class="token string">"https://api.twitter.com/oauth/authenticate?oauth_token="</span></span> <span class="token operator">+</span> query
<span class="token keyword">end</span>
</code></pre>
<p>At this moment, the user will be redirected to Twitter and will be asked to authorize your app to have access to user resources. If the user grants access to your app, Twitter will redirect her or him to an URL set by you as a callback in your app configurations.</p>
<p><a href="https://github.com/lfcipriani/sign_in_with_twitter_sample/blob/master/app.rb#L60" target="_blank">Listing 4</a>:</p>
<pre class="language-ruby"><code class="language-ruby"><span class="token comment"># This callback will be called by user browser after</span>
<span class="token comment"># being redirect by Twitter with successful authentication</span>
<span class="token comment"># See https://dev.twitter.com/docs/auth/implementing-sign-twitter (end of Step 2)</span>
get <span class="token string-literal"><span class="token string">'/callback'</span></span> <span class="token keyword">do</span>

  <span class="token comment"># Given that the user authorized us, we now</span>
  <span class="token comment"># need to get its Access Token.</span>
  <span class="token comment"># See https://dev.twitter.com/docs/auth/implementing-sign-twitter (Step 3)</span>
  token <span class="token operator">=</span> TwitterSignIn<span class="token punctuation">.</span>access_token<span class="token punctuation">(</span>params<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"oauth_token"</span></span><span class="token punctuation">]</span><span class="token punctuation">,</span> params<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"oauth_verifier"</span></span><span class="token punctuation">]</span><span class="token punctuation">)</span>
</code></pre>
<p>This redirection has a parameter called <code>oauth_verifier</code>, which is a confirmation that the user, still identified by the <code>oauth_token</code> obtained in the first step, has granted you access.</p>
<h2>Step 3: Convert the request token</h2>
<p>Now you’re going to use all the tokens obtained so far to get the access token. The access token is what’s necessary in order to — as its name suggests — access Twitter on behalf of a user. Let’s take a look at the implementation of that step:</p>
<p><a href="https://github.com/lfcipriani/sign_in_with_twitter_sample/blob/master/lib/twitter_sign_in.rb#L49" target="_blank">Listing 5</a>:</p>
<pre class="language-ruby"><code class="language-ruby"><span class="token keyword">def</span> <span class="token method-definition"><span class="token function">access_token</span></span><span class="token punctuation">(</span>oauth_token<span class="token punctuation">,</span> oauth_verifier<span class="token punctuation">)</span>

    <span class="token comment"># To request access token, you need to retrieve</span>
    <span class="token comment"># oauth_token and oauth_token_secret stored in</span>
    <span class="token comment"># database</span>
    db <span class="token operator">=</span> Daybreak<span class="token double-colon punctuation">::</span><span class="token class-name">DB</span><span class="token punctuation">.</span><span class="token keyword">new</span> <span class="token constant">DATABASE</span>
    <span class="token keyword">if</span> dbtoken <span class="token operator">=</span> db<span class="token punctuation">[</span>oauth_token<span class="token punctuation">]</span>

    <span class="token comment"># now the oauth signature variables should be</span>
    <span class="token comment"># your app consumer keys and secrets and also</span>
    <span class="token comment"># token key and token secret obtained in request_token</span>
    oauth <span class="token operator">=</span> <span class="token variable">@oauth</span><span class="token punctuation">.</span>dup
    oauth<span class="token punctuation">[</span><span class="token symbol">:token</span><span class="token punctuation">]</span> <span class="token operator">=</span> oauth_token
    oauth<span class="token punctuation">[</span><span class="token symbol">:token_secret</span><span class="token punctuation">]</span> <span class="token operator">=</span> dbtoken<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"oauth_token_secret"</span></span><span class="token punctuation">]</span>

    <span class="token comment"># oauth_verifier got in callback must</span>
    <span class="token comment"># to be passed as body param</span>
    response <span class="token operator">=</span> TwitterSignIn<span class="token punctuation">.</span>request<span class="token punctuation">(</span>
        <span class="token symbol">:post</span><span class="token punctuation">,</span>
        <span class="token string-literal"><span class="token string">"https://api.twitter.com/oauth/access_token"</span></span><span class="token punctuation">,</span>
        <span class="token punctuation">{</span><span class="token symbol">:oauth_verifier</span> <span class="token operator">=></span> oauth_verifier<span class="token punctuation">}</span><span class="token punctuation">,</span>
        oauth
    <span class="token punctuation">)</span>

    obj <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
    vars <span class="token operator">=</span> response<span class="token punctuation">.</span>body<span class="token punctuation">.</span>split<span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"&#x26;"</span></span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token keyword">each</span> <span class="token keyword">do</span> <span class="token operator">|</span>v<span class="token operator">|</span>
        obj<span class="token punctuation">[</span>v<span class="token punctuation">.</span>split<span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"="</span></span><span class="token punctuation">)</span><span class="token punctuation">.</span>first<span class="token punctuation">]</span> <span class="token operator">=</span> v<span class="token punctuation">.</span>split<span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"="</span></span><span class="token punctuation">)</span><span class="token punctuation">.</span>last
    <span class="token keyword">end</span>

    <span class="token comment"># now the we got the access tokens, store it safely</span>
    <span class="token comment"># in database, you're going to use it later to</span>
    <span class="token comment"># access Twitter API in behalf of logged user</span>
    dbtoken<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"access_token"</span></span><span class="token punctuation">]</span> <span class="token operator">=</span> obj<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"oauth_token"</span></span><span class="token punctuation">]</span>
    dbtoken<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"access_token_secret"</span></span><span class="token punctuation">]</span> <span class="token operator">=</span> obj<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"oauth_token_secret"</span></span><span class="token punctuation">]</span>
    db<span class="token punctuation">.</span>lock <span class="token punctuation">{</span> db<span class="token punctuation">[</span>oauth_token<span class="token punctuation">]</span> <span class="token operator">=</span> dbtoken <span class="token punctuation">}</span>

    <span class="token keyword">else</span>
    oauth_token <span class="token operator">=</span> <span class="token keyword">nil</span>
    <span class="token keyword">end</span>

    db<span class="token punctuation">.</span>close
    <span class="token keyword">return</span> oauth_token
<span class="token keyword">end</span>
</code></pre>
<p>The method <code>access_token</code> do a request to <code>POST /oauth/access_token</code> passing app consumer keys and oauth tokens inside OAuth signature; and the <code>oauth_verifier</code> obtained in Step 2 in the body of request. With that set of information, Twitter can validate this request to the access token as legitimate and return to the app the access tokens. That in turn will enable your app to do requests on behalf of the user who just granted you access. You can now store these tokens safely in your database.</p>
<h2>Step 4: Use the access token</h2>
<p>This app implements a feature that lets you create a friendship between this user and an account you choose as an example of what can be done once you have been granted access.</p>
<p><a href="https://github.com/lfcipriani/sign_in_with_twitter_sample/blob/master/app.rb#L118" target="_blank">Listing 6</a>:</p>
<pre class="language-ruby"><code class="language-ruby"><span class="token comment"># Building oauth signature vars to use in this request</span>
<span class="token comment"># Basically, it's our app consumer vars combined</span>
<span class="token comment"># with user access tokens</span>
oauth <span class="token operator">=</span> <span class="token variable">@oauth</span><span class="token punctuation">.</span>dup
oauth<span class="token punctuation">[</span><span class="token symbol">:token</span><span class="token punctuation">]</span> <span class="token operator">=</span> dbtoken<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"access_token"</span></span><span class="token punctuation">]</span>
oauth<span class="token punctuation">[</span><span class="token symbol">:token_secret</span><span class="token punctuation">]</span> <span class="token operator">=</span> dbtoken<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"access_token_secret"</span></span><span class="token punctuation">]</span>

<span class="token comment"># A POST request in https://dev.twitter.com/docs/api/1.1/post/friendships/create</span>
<span class="token comment"># to make the logged user follow the ACCOUNT_TO_FOLLOW</span>
response <span class="token operator">=</span> TwitterSignIn<span class="token punctuation">.</span>request<span class="token punctuation">(</span>
    <span class="token symbol">:post</span><span class="token punctuation">,</span>
    <span class="token string-literal"><span class="token string">"https://api.twitter.com/1.1/friendships/create.json"</span></span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span><span class="token symbol">:screen_name</span> <span class="token operator">=></span> <span class="token constant">ACCOUNT_TO_FOLLOW</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
    oauth
<span class="token punctuation">)</span>
</code></pre>
<h3>Summary</h3>
<p>Now that you know how to implement Sign in with Twitter, check the <a href="https://github.com/lfcipriani/sign_in_with_twitter_sample" target="_blank">full source code</a> to have a big picture of how the app works. If you are a Pythonist, check <a href="https://twitter.com/jaakkosf" target="_blank">@jaakkosf</a>’s Python implementation at <a href="https://github.com/jaakko-sf/twauth-web" target="_blank">https://github.com/jaakko-sf/twauth-web</a>.</p>
<p>Have you built a Sign in with Twitter integration in another language besides English that can easily be converted to standalone code? Share it with us by Tweeting a Github link to your implementation <a href="https://twitter.com/twitterdev" target="_blank">@TwitterDev</a>. We will update this post with implementations in other languages.</p>]]></description>
            <link>https://www.talleye.com/posts/bootstrapping-sign-in-with-twitter</link>
            <guid isPermaLink="true">https://www.talleye.com/posts/bootstrapping-sign-in-with-twitter</guid>
            <dc:creator><![CDATA[Luis Cipriani]]></dc:creator>
            <pubDate>Wed, 07 May 2014 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Projeto: @oquevernaCP]]></title>
            <description><![CDATA[<p>Para a Campus Party Brasil 2014, eu criei um bot do Twitter que ajudava os participantes a descobrir quais palestras acompanhar, dado que no evento acontecem inúmeras palestras simultâneas. Eu também apresentei o projeto em uma palestra no mesmo evento. O bot funcionava mais ou menos assim:</p>
<p><img src="https://www.talleye.com/img-posts/po-tweet.png" alt=""></p>
<p>O texto que segue é de um post compartilhado na Campus Party.</p>
<h2>Quer saber como criar um robô como esse?</h2>
<p>O Twitter fará duas palestras na Campus Party Brasil para mostrar o potencial da Plataforma, suas APIs e o código do @oquevernaCP. A agenda é a seguinte:</p>
<ul>
<li><strong>Quarta, 29/01 as 14:30</strong> - O Twitter e suas APIs de Streaming. <a href="http://www.campus-party.com.br/2014/agenda-pitagoras.html" target="_blank">Palco #pitagoras</a></li>
<li><strong>Quinta, 30/01 as 10:30</strong> - Workshop: Capture o momento com as APIs do Twitter. <a href="http://www.campus-party.com.br/2014/agenda-workshop2.html" target="_blank">Palco #workshop2</a></li>
</ul>
<h2>Como tweetar para @oquevernaCP?</h2>
<p>Se quiser apenas obter uma sugestão rápida, apenas crie um tweet com o texto "<a href="https://twitter.com/intent/tweet?related=TwitterDevBr&#x26;text=%40oquevernaCP" target="_blank">@oquevernaCP</a>" que ele vai retornar a próxima palestra ou evento do calendário. Você também pode usar as hastags <a href="https://twitter.com/intent/tweet?related=TwitterDevBr&#x26;text=%40oquevernaCP+%23agora" target="_blank">#agora</a> ou <a href="https://twitter.com/intent/tweet?related=TwitterDevBr&#x26;text=%40oquevernaCP+%23depois" target="_blank">#depois</a>.</p>
<h2>Tecnologias utilizadas</h2>
<ul>
<li>ruby 2.0.0</li>
<li><a href="https://dev.twitter.com/docs/api/1.1/get/user" target="_blank">API de Streaming do Twitter (GET user)</a></li>
<li><a href="http://redis.io/" target="_blank">Redis</a></li>
<li>gem tweetstream</li>
</ul>
<p>Se estiver interessado em como o bot foi feito, você pode <a href="https://github.com/lfcipriani/oquevernaCP" target="_blank">acessar o código-fonte</a>.</p>]]></description>
            <link>https://www.talleye.com/pt-BR/posts/projeto-oquevernacp</link>
            <guid isPermaLink="true">https://www.talleye.com/pt-BR/posts/projeto-oquevernacp</guid>
            <dc:creator><![CDATA[Luis Cipriani]]></dc:creator>
            <pubDate>Wed, 29 Jan 2014 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[The 4 basic flows of HTTP Caching]]></title>
            <description><![CDATA[<p>Every web developer probably had used cache one day in their web apps or APIs to avoid redundant data traffic, network bottlenecks, protect your server from load spikes or simply long network latencies. The concept of caching is usually well understood and easily applicable in practice thanks to <a href="https://href.li/?http://en.wikipedia.org/wiki/Web_cache#Comparison_of_web_caches" title="comparison of web caches" target="_blank">open source tools</a>. However, to build a good cache strategy, i.e. a strategy that defines what can be cached, how long can you cache something and what policy you’ll follow after some resource is staled, is a hard and incremental process that need:</p>
<ol>
<li>knowledge of how your resources are consumed by users;</li>
<li>understanding of how <a href="https://href.li/?http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html" title="HTTP caching RFC" target="_blank">HTTP caching protocol works</a> (HTTP headers everywhere);</li>
<li>patience to solve problems when tools don’t honor the protocol (believe me, this is very common)</li>
</ol>
<p>The first one is up to you. The third one is an inherently issue of every computer system and you should be used to that too. Time and experience will help you to deal with this burden, but there’s always mailing lists and web search to save the day.</p>
<p>This post is an illustrative contribution to item 2 and presents the four basic flows of HTTP caching that can be implicitly extracted from the RFC. These flows shows how clients, server and caching software will behave depending on the status of the resource being requested and its location in the topology. And by “basic” I mean that they could vary depending on the cache strategy, so use them as a starting point. Let’s go!</p>
<h2>Cache Miss</h2>
<div class="media">
<video controls loop playsinline preload="auto" class="media-asset">
    <source src="/img-posts/t4bfhc-cache01-miss.m4v" type="video/mp4">
    <p>Your browser doesn't support HTML5 video. Here is
     a <a href="https://www.talleye.com/img-posts/t4bfhc-cache01-miss.m4v">link to the video</a> instead.</p>
</video>
</div>
<h2>Cache Hit</h2>
<div class="media">
<video controls loop playsinline preload="auto" class="media-asset">
    <source src="/img-posts/t4bfhc-cache02-hit.m4v" type="video/mp4">
    <p>Your browser doesn't support HTML5 video. Here is
     a <a href="https://www.talleye.com/img-posts/t4bfhc-cache02-hit.m4v">link to the video</a> instead.</p>
</video>
</div>
<p><strong>Curious fact</strong>: this animation length is half of the “Cache miss” length. 😉</p>
<h2>Cache Revalidation (Condition False)</h2>
<div class="media">
<video controls loop playsinline preload="auto" class="media-asset">
    <source src="/img-posts/t4bfhc-cache03-revfalse.m4v" type="video/mp4">
    <p>Your browser doesn't support HTML5 video. Here is
     a <a href="https://www.talleye.com/img-posts/t4bfhc-cache03-revfalse.m4v">link to the video</a> instead.</p>
</video>
</div>
<h2>Cache Revalidation (Condition True)</h2>
<div class="media">
<video controls loop playsinline preload="auto" class="media-asset">
    <source src="/img-posts/t4bfhc-cache04-revtrue.m4v" type="video/mp4">
    <p>Your browser doesn't support HTML5 video. Here is
     a <a href="https://www.talleye.com/img-posts/t4bfhc-cache04-revtrue.m4v">link to the video</a> instead.</p>
</video>
</div>
<p>If you want to dig deeper on the subject, this content above was included in a talk I <a href="https://www.eventials.com/locaweb/caching-de-apis-na-pratica-porque-seu-servidor-merece-um-descanso/" target="_blank">presented at Rubyconf Brazil 2013</a> about HTTP Caching (intro and good practices). The slides are embedded below:</p>
<p><a href="https://www.slideshare.net/lfcipriani/api-caching-why-your-server" title="Open Slideshare" target="_blank"><img src="https://www.talleye.com/img-posts/t4bfhc-slides.png" alt=""></a></p>
<p>You might also like to explore the <strong><a href="https://href.li/?http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14" title="Headers in HTTP RFC" target="_blank">HTTP headers RFC</a></strong>.</p>]]></description>
            <link>https://www.talleye.com/posts/the-4-basic-flows-of-http-caching</link>
            <guid isPermaLink="true">https://www.talleye.com/posts/the-4-basic-flows-of-http-caching</guid>
            <dc:creator><![CDATA[Luis Cipriani]]></dc:creator>
            <pubDate>Sun, 03 Nov 2013 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[An easy way to build an autocomplete search for API endpoint docs]]></title>
            <description><![CDATA[<p>There are some APIs that we use often and having easy and fast access to them can save us from repetitive tasks. You know, as software engineers, we love to automate micro tasks to save that precious seconds of our lives :-).</p>
<p>The solution described in this post is simple, but uses a lot of tools and services. If you have patience, the final result is very useful. Here’s the list of things we use:</p>
<ul>
<li><a href="http://apify.heroku.com/resources" title="APIfy website" target="_blank">APIfy</a> to scrape the docs page (<a href="https://github.com/sathish316/scrapify#usage" title="Github page" target="_blank">Scrapify</a> is used by APIfy)</li>
<li><a href="http://dl.dropboxusercontent.com/u/848981/it/xp/xp.html" title="XCPath website" target="_blank">XCPath Bookmarklet</a> to don’t make us learn XPath again</li>
<li>curl to retrieve the json version of the API docs</li>
<li><a href="http://stedolan.github.io/jq/" title="jq website" target="_blank">jq</a> to scrape this json and generate XML \o/</li>
<li>grep to filter based on our search query</li>
<li><a href="http://www.alfredapp.com/" title="Alfred website" target="_blank">Alfred</a> workflows to implement autocomplete (Powerpack needed)</li>
</ul>
<p>There are some very known tools in this list, and maybe some new to you. My recommendation is to keep attention and perceive the real value of APIfy and jq. These are really useful tools that can be used for a lot of other applications. Of course the other ones are interesting as well.</p>
<p><strong>My wish was:</strong> be able to trigger a search in Alfred that shows me a list of <a href="https://dev.twitter.com/docs/api/1.1" title="twitter api site" target="_blank">Twitter REST API endpoints</a> and when hitting enter, the browser would open the API endpoint doc.</p>
<p>The result was:</p>
<p><img src="https://www.talleye.com/img-posts/ewbasaed-thumb.png" alt=""></p>
<p>#productivity #win</p>
<h2>Getting the data we need</h2>
<p>APIfy is a really great tool to easily scrape an HTML page and exposes the relevant content as a json representation. The website has very <a href="http://apify.heroku.com/tutorial/create" title="tutorials!" target="_blank">descriptive tutorials</a> that show how to create an API, I recommend them. But the process it’s uses is very simple:</p>
<p>For each field your API returns:</p>
<ul>
<li>Use XCPath Bookmarklet to get the XPATH or CSS of the element value you want;</li>
</ul>
<p><img src="https://www.talleye.com/img-posts/ewbasaed-01.png" alt=""></p>
<ul>
<li>Do a mapping between the field name and value in APIfy editor;</li>
</ul>
<p><img src="https://www.talleye.com/img-posts/ewbasaed-02.png" alt=""></p>
<p>You can check the brand new API created to expose all Twitter API endpoints as json at <a href="http://apify.heroku.com/resources/5238b24bb7ea5d000200001f" target="_blank">TwitterRESTAPIv1\ APIfy page</a>. And of course, check the <a href="http://apify.heroku.com/api/twitter_rest_api_v11_docs.json" target="_blank">json representation returned by the API</a>.</p>
<p>It’s very very useful. Make sure to check other APIs and how they are done, or try the Scrapify gem used to build APIfy.</p>
<h2>Converting and filtering the data</h2>
<p>Now that we have a json representation with each API endpoint, description and link to the docs, we can manipulate this json to be in the format needed to the next step in our flow.</p>
<p>jq, as website says, it is a lightweight and flexible command-line JSON processor. It’s a mandatory tool if you deal with a lot of json APIs, since it could transform raw API responses to beautified colorful representations and also allow you to filter, process, transform the data the way you want it. There’s a <a href="http://stedolan.github.io/jq/tutorial/" target="_blank">very good tutorial</a> on the website.</p>
<p>In our case, we need to convert the json to XML… XML??? Why?!?</p>
<p>Well, this is the format chosen by Alfred to show autocomplete results. Basically, anytime a user trigger the workflow, a script is executed with the query and the response should be a XML with items that match the query and have other metadata such as what URL should be open if the user choose that item.</p>
<p>Let’s show the jq command we use:</p>
<p>And now let’s break it in pieces to understand what it does.</p>
<p>Surrounding the commands are two echo commands to add the root XML element needed to be input in Alfred. The second command is the request to the API we created that is pipelined to jq.</p>
<p>The jq command iterates over all entries in the response and do a string interpolation setting the resource of the endpoint and the link to it to some xml values and properties. It’s important to note that jq isn’t just to convert json to XML, it has a very powerful query language that enable you to do practically anything you want with your json. Check their tutorial.</p>
<p>The XML generated by jq is pipelined to a grep command, that filters for “search” using a regular expression. The final result is the following XML (already filtered by endpoints that matches the “search” keyword):</p>
<p>This is the input Alfred needs to show the items in autocomplete, as we saw in the beginning of this post. After testing the command in the terminal, then we need to use a Script Filter module of Alfred Workflows to run that code after triggering the keyword:</p>
<p><img src="https://www.talleye.com/img-posts/ewbasaed-03.png" alt=""></p>
<p>Note that instead of doing the request everytime, I just saved the json on my computer since I’m not expecting that the docs will be changed so frequently, and also because I don’t want to abuse the APIfy service.</p>
<p>The final version of the workflow will look like this:</p>
<p><img src="https://www.talleye.com/img-posts/ewbasaed-04.png" alt=""></p>
<p>That’s it! Now we have everything working:</p>
<p><img src="https://www.talleye.com/img-posts/ewbasaed-thumb.png" alt=""></p>
<p>This combination of tools could help you to build any search to your most used APIs. And of course that there are some minor improvements to the command showed above, but the beauty of this is what we could accomplish with a short period of time thanks to these awesome tools.</p>]]></description>
            <link>https://www.talleye.com/posts/an-easy-way-to-build-autocomplete-search-for-api-docs</link>
            <guid isPermaLink="true">https://www.talleye.com/posts/an-easy-way-to-build-autocomplete-search-for-api-docs</guid>
            <dc:creator><![CDATA[Luis Cipriani]]></dc:creator>
            <pubDate>Fri, 27 Sep 2013 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Segurança em APIs HTTP]]></title>
            <description><![CDATA[<p>Essa semana eu apresentei uma palestra na QCon São Paulo sobre segurança em APIs HTTP. A palestra é um resultado de um trabalho feito na Abril Mídia quando estávamos desenvolvendo a plataforma Alexandria, que era o gerenciador de conteúdo digital da Abril. Ela é um guia sobre as possibilidades de implementação de segurança em APIs HTTP. Abaixo você encontra:</p>
<ul>
<li><a href="https://www.infoq.com/br/presentations/seguranca-apis-http/" target="_blank">Vídeo da apresentação e slides no site da QCon</a></li>
<li><a href="https://www.infoq.com/br/interviews/seguranca-apis-http/" target="_blank">Uma entrevista que eu dei pra eles sobre o assunto</a></li>
</ul>
<p>E os slides:</p>
<p><a href="https://www.slideshare.net/lfcipriani/segurana-de-p" title="Abrir no Slideshare" target="_blank"><img src="https://www.talleye.com/img-posts/sah-slides.png" alt=""></a></p>]]></description>
            <link>https://www.talleye.com/pt-BR/posts/seguranca-em-apis-http</link>
            <guid isPermaLink="true">https://www.talleye.com/pt-BR/posts/seguranca-em-apis-http</guid>
            <dc:creator><![CDATA[Luis Cipriani]]></dc:creator>
            <pubDate>Sat, 31 Aug 2013 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[A System of systems to Publish Digital Content]]></title>
            <description><![CDATA[<p>From 2009 to 2013 I worked as software engineer, lead software architect at Abril in the team that built this platform, we were a team of approximately 40 software engineers.</p>
<p>Abril Mídia is one of the largest media companies in Brazil. In 2011 it published 52 titles and is a leader in 22 out of 26 segments in which it operates. In the digital world, the big challenge Abril faces is deliver a large volume of information that it generates in a high diversity of digital channels, such as websites, mobile sites and TVs. The chosen solution was the building of a distributed platform, named Alexandria, that allows the content to be added and handled by separated information domains, implemented with REST constraints and with a hypermedia API, that is exposed to publishers and end users.</p>
<p>The result of this work became a paper published by Springer in the book "<a href="http://link.springer.com/chapter/10.1007%2F978-1-4614-9299-3_10" title="go to Sringer website" target="_blank">Rest: Advanced Research Topics and Practical Applications</a>" and it was written by <a href="https://twitter.com/lsdr" target="_blank">Luiz Rocha</a> and me.</p>
<p>I spoke in QCon Conference São Paulo in 2012 about all the technical aspects of Alexandria, you can find links to <a href="https://www.slideshare.net/lfcipriani/como-um-verdadeiro-sistema-rest-funciona-arquitetura-e-performance-na-abril" target="_blank">slides (in Portuguese)</a> and the <a href="https://www.infoq.com/br/presentations/rest-arquitetura-abril" target="_blank">talk video</a> (in Portuguese).</p>
<p><a href="https://www.slideshare.net/lfcipriani/como-um-verdadeiro-sistema-rest-funciona-arquitetura-e-performance-na-abril" title="Open Slideshare" target="_blank"><img src="https://www.talleye.com/img-posts/sospdc-slides.jpg" alt=""></a></p>]]></description>
            <link>https://www.talleye.com/posts/alexandria-platform</link>
            <guid isPermaLink="true">https://www.talleye.com/posts/alexandria-platform</guid>
            <dc:creator><![CDATA[Luis Cipriani]]></dc:creator>
            <pubDate>Sun, 12 Aug 2012 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Explaining Semantic Web]]></title>
            <description><![CDATA[<div class="float-right">
    <img src="https://www.talleye.com/img-posts/esw-thumb.jpg" width="400" height="308" loading="lazy" alt="Linked data graph">
</div>
<p>Few weeks ago I gave an introduction to Semantic Web at the Sao Paulo Ruby User Group (GURU, in portuguese). Since Semantic Web is a very broader term that entail tons of technologies and tools, I decided to focus more on explaining the single main reason that make companies apply it in their projects:</p>
<h2>Data Integration</h2>
<p>We, as software engineers and product managers, face a lot of problems, specially in big companies, when we need to integrate data coming from several sources. Anytime I needed to do this, I realize that I kept rumbling to myself: "Why we don’t have only one universal metadata for our data? How we let this happen?"</p>
<p>The big triumph provided by the Semantic Web set of technologies, specifications and tools is to enable you, as owner of highly variable data and metadata, to organize it in a way that you can find any information and derive knowledge as easily as a SQL-like query. This is possible because we start to represent the data in the simplest way possible: <strong>a triple</strong> (subject -> predicate -> object). For example:</p>
<ul>
<li>Abril_Engineering_blog > is_owned_by > Abril</li>
<li>Abril_Engineering_blog > is_hosted_by > Tumblr</li>
<li>Luis_Cipriani > work_at > Abril</li>
</ul>
<p>By establishing relationships with all your data based on a controlled and known metadata (better known as ontology), you create the most flexible format for representing a data, and from this point, you can derive anything the quality of your data would allow.</p>
<p>At Abril we have a perfect environment to use this strategy for data integration, since we produce a lot of content that we are able to extract structure, such as, people, events, venues, articles, user comments, etc. Some projects are on their way and others are about to start. We hope to be talking about it sooner.</p>
<p>Meanwhile, check the presentation slides below, it has more detail about the technologies and cases (in Portuguese):</p>
<p><a href="https://www.slideshare.net/lfcipriani/explaining-semantic-web" title="Open Slideshare" target="_blank"><img src="https://www.talleye.com/img-posts/esw-slides.jpg" alt=""></a></p>]]></description>
            <link>https://www.talleye.com/posts/explaining-semantic-web</link>
            <guid isPermaLink="true">https://www.talleye.com/posts/explaining-semantic-web</guid>
            <dc:creator><![CDATA[Luis Cipriani]]></dc:creator>
            <pubDate>Fri, 01 Jun 2012 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Introduction to HBase]]></title>
            <description><![CDATA[<p>One of the services we had in the <a href="https://www.talleye.com/posts/alexandria-platform">Alexandria Platform</a> at Abril was the Social Core. Its domain covered any social functionality that a web property in the company could have, such as user timelines, user graphs and follow actions.</p>
<p>Given the large audience of the websites at Abril, we evaluated and adopted HBase as the storage for the user timelines. We later shared our experiences in some tech events in São Paulo.</p>
<p>The slides are available in the link below:</p>
<p><a href="https://www.slideshare.net/lfcipriani/hbase-introduction-to-column-oriented-databases" target="_blank"><img src="https://www.talleye.com/img-posts/ith-slides.jpg" alt=""></a></p>]]></description>
            <link>https://www.talleye.com/posts/introduction-to-hbase</link>
            <guid isPermaLink="true">https://www.talleye.com/posts/introduction-to-hbase</guid>
            <dc:creator><![CDATA[Luis Cipriani]]></dc:creator>
            <pubDate>Sat, 25 Feb 2012 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Tracking real time clicks on website home pages]]></title>
            <description><![CDATA[<p>During my time at Abril, the second largest media company in Brazil, we worked in a project to track the user clicks in home pages so the editors could change the home page of the website properties to more popular news articles and consequently, make the landing pages more attractive content-wise.</p>
<p>We built a simple Sinatra ruby web app, with Redis as persistent storage, to track these user clicks and in 1 month we served around 1 million hits per day for 17 different websites from Abril.</p>
<p>Later I did a presentation about the project in a Ruby User Group Meetup in Brazil, the slides are available in Slideshare:</p>
<p><a href="https://www.slideshare.net/lfcipriani/case-abril-tracking-real-time-user-behavior-in-websites-homes-with-ruby-sinatra-heroku-redis" title="Open Slideshare" target="_blank"><img src="https://www.talleye.com/img-posts/trtcwhp-slides.png" alt=""></a></p>]]></description>
            <link>https://www.talleye.com/posts/tracking-real-time-clicks-on-home-pages</link>
            <guid isPermaLink="true">https://www.talleye.com/posts/tracking-real-time-clicks-on-home-pages</guid>
            <dc:creator><![CDATA[Luis Cipriani]]></dc:creator>
            <pubDate>Sat, 28 Jan 2012 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Rubygem: cheatorious]]></title>
            <description><![CDATA[<blockquote>
<p>Being victorious through a means so amazing it cannot possibly be achieved without cheating.</p>
<p><em>- Urban Dictionary</em></p>
</blockquote>
<p><a href="https://github.com/lfcipriani/cheatorious" target="_blank">cheatorious</a> is a generator of simple, searchable, shareable, modular command-line cheatsheets.</p>
<p>Cheatsheets are very good to start learning or keep knowledge of some language or tool, but very often you just don't have the proper cheatsheet at hand when you need to remember that cryptic keyboard shortcut. Furthermore, wouldn't be nice to keep or create your own cheatsheets in a neat ruby DSL? Wouldn't be great if you have easy access to them, such as via command-line interface?</p>
<h2>TL;DR;</h2>
<ul>
<li>Install Cheatorious: gem install cheatorious</li>
<li>Import a cheatsheet from <a href="https://github.com/lfcipriani/cheatorious-cheatsheets" target="_blank">cheatorious cheatsheet repository</a>.</li>
<li>Create your cheatsheet</li>
<li>Check examples folder in repository</li>
<li>Type <code>cheatorious search CHEATSHEETNAME KEYWORD</code> or <code>cheatorious view</code> to access your cheatsheets</li>
<li>If you use Alfred 2, download the Cheatorious workflow in examples folder</li>
</ul>
<h2>Install</h2>
<pre><code>gem install cheatorious
</code></pre>
<p>Cheatorious is compatible with ruby 1.8.x, 1.9.x, 2.0.x, rubinius, jruby, ree and more.</p>
<h2><a href="https://github.com/lfcipriani/cheatorious#creating-your-cheatsheet" target="_blank"></a></h2>
<h2>Creating your Cheatsheet</h2>
<p>Create a file and use the following syntax, shown in the example below:</p>
<pre class="language-ruby"><code class="language-ruby">cheatsheet_for <span class="token string-literal"><span class="token string">"Simple VIM"</span></span> <span class="token keyword">do</span>
    <span class="token comment"># put some info about you, if you want to share this later</span>
    description <span class="token string-literal"><span class="token string">"A simple VIM cheatsheet for tests"</span></span>
    author      <span class="token string-literal"><span class="token string">"Luis Cipriani"</span></span><span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">"github.com/lfcipriani"</span></span>
    version     <span class="token string-literal"><span class="token string">"1.0.0"</span></span>

    <span class="token comment"># you can configure some keyboard keys variables, you can change it later to fit your personal preferences</span>
    key <span class="token symbol">:control</span><span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">"^"</span></span>
    key <span class="token symbol">:esc</span>    <span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">"&#x3C;esc>"</span></span>
    key <span class="token symbol">:leader</span> <span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">","</span></span>

    <span class="token comment"># your prefered keyboard keys separators (default is empty string)</span>
    key_separator <span class="token string-literal"><span class="token string">" "</span></span>

    <span class="token comment"># this is an cheatsheet entry, the first parameter is what the entry does, the other are the shortcuts or descriptions</span>
    __ <span class="token string-literal"><span class="token string">"Enter insertion mode"</span></span><span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">"i"</span></span>
    __ <span class="token string-literal"><span class="token string">"Exit insertion mode"</span></span> <span class="token punctuation">,</span> _esc

    <span class="token comment"># you can create sections, that will be searchable</span>
    section <span class="token string-literal"><span class="token string">"Basic Movement"</span></span> <span class="token keyword">do</span>
        __ <span class="token string-literal"><span class="token string">"character left, right, line up, line down"</span></span><span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">"h"</span></span><span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">"l"</span></span><span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">"k"</span></span><span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">"j"</span></span>
        __ <span class="token string-literal"><span class="token string">"word/token left, right"</span></span>                   <span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">"b"</span></span><span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">"w"</span></span>
    <span class="token keyword">end</span>

    section <span class="token string-literal"><span class="token string">"Scrolling"</span></span> <span class="token keyword">do</span>
        <span class="token comment"># this is how you use the pre-configured keyboard keys</span>
        __ <span class="token string-literal"><span class="token string">"scroll line up, down"</span></span><span class="token punctuation">,</span> <span class="token punctuation">(</span>_control <span class="token string-literal"><span class="token string">"E"</span></span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>_control <span class="token string-literal"><span class="token string">"Y"</span></span><span class="token punctuation">)</span>
        __ <span class="token string-literal"><span class="token string">"scroll page up, down"</span></span><span class="token punctuation">,</span> <span class="token punctuation">(</span>_control <span class="token string-literal"><span class="token string">"F"</span></span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>_control <span class="token string-literal"><span class="token string">"B"</span></span><span class="token punctuation">)</span>
        __ <span class="token string-literal"><span class="token string">"crazy scroll"</span></span><span class="token punctuation">,</span> <span class="token punctuation">(</span>_leader _control <span class="token string-literal"><span class="token string">"A"</span></span><span class="token punctuation">)</span> <span class="token comment"># this is just to show you can combine keys \o/</span>
    <span class="token keyword">end</span>

    section <span class="token string-literal"><span class="token string">"Files"</span></span> <span class="token keyword">do</span>
        __ <span class="token string-literal"><span class="token string">"Open file"</span></span><span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">":e"</span></span>

        <span class="token comment"># you can go infinitely deep with sections (seriously)</span>
        section <span class="token string-literal"><span class="token string">"Saving"</span></span> <span class="token keyword">do</span>
            __ <span class="token string-literal"><span class="token string">"Save file"</span></span><span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">":w"</span></span>
        <span class="token keyword">end</span>
    <span class="token keyword">end</span>
<span class="token keyword">end</span>
</code></pre>
<h2>Using cheatorious</h2>
<p>The basic CLI usage goes as follows:</p>
<pre class="language-bash"><code class="language-bash">$ cheatorious                                        <span class="token comment"># get help</span>
$ cheatorious view CHEATSHEET <span class="token punctuation">[</span>OPTIONS<span class="token punctuation">]</span>              <span class="token comment"># view a CHEATSHEET. The CHEATSHEET variable could be a name (for imported cheatsheets) or a file that describes a cheatsheet.</span>
$ cheatorious search CHEATSHEET <span class="token punctuation">[</span>KEYWORD<span class="token punctuation">]</span> <span class="token punctuation">[</span>OPTIONS<span class="token punctuation">]</span>  <span class="token comment"># search for KEYWORD in CHEATSHEET entries only. The CHEATSHEET variable could be a name (for imported cheatsheets) or a file that describes a che...</span>
$ cheatorious <span class="token builtin class-name">alias</span> NAME CHEATSHEET                  <span class="token comment"># return a shell alias command with NAME for easy access to searching a CHEATSHEET. The CHEATSHEET variable must be an imported cheatsheet. Exampl...</span>
$ cheatorious edit CHEATSHEET                        <span class="token comment"># edit an existent CHEATSHEET. Will open it in the default terminal editor, use CHEATORIOUS_EDITOR environment variable to use another.</span>
$ cheatorious <span class="token function">import</span> FILE                            <span class="token comment"># import a cheatsheet description FILE. Check https://github.com/lfcipriani/cheatorious to learn how to create your own cheatsheets. Check https:/...</span>
$ cheatorious list                                   <span class="token comment"># lists the available cheatsheets. See 'import' command.</span>
$ cheatorious reload CHEATSHEET                      <span class="token comment"># reload a CHEATSHEET after editing it with 'edit' command</span>
$ cheatorious remove CHEATSHEET                      <span class="token comment"># remove a CHEATSHEET. The original file is kept for later recovering</span>
$ cheatorious writers <span class="token punctuation">[</span>OPTIONS<span class="token punctuation">]</span>                      <span class="token comment"># lists the available writers or set a default</span>
$ cheatorious <span class="token builtin class-name">help</span> <span class="token punctuation">[</span>COMMAND<span class="token punctuation">]</span>                         <span class="token comment"># Describe available commands or one specific command</span>
</code></pre>
<h3>Searching</h3>
<p>To search on your cheatsheets:</p>
<pre class="language-bash"><code class="language-bash">$ cheatorious <span class="token builtin class-name">help</span> search
Usage:
  cheatorious search CHEATSHEET <span class="token punctuation">[</span>KEYWORD<span class="token punctuation">]</span> <span class="token punctuation">[</span>OPTIONS<span class="token punctuation">]</span>

Options:
  -s, <span class="token punctuation">[</span>--section<span class="token punctuation">]</span>        <span class="token comment"># matches KEYWORD only on section names, returning all entries and sections inside it.</span>
  -r, <span class="token punctuation">[</span>--reverse<span class="token punctuation">]</span>        <span class="token comment"># reverse means to search only the values of a cheatsheet (and not entries, as usual). For example, search by shortcuts.</span>
  -S, <span class="token punctuation">[</span>--sensitive<span class="token punctuation">]</span>      <span class="token comment"># case sensitive search (insensitive is default).</span>
  -w, <span class="token punctuation">[</span>--writer<span class="token operator">=</span>WRITER<span class="token punctuation">]</span>  <span class="token comment"># writer to use for the output. If not set, uses the default.</span>

search <span class="token keyword">for</span> <span class="token for-or-select variable">KEYWORD</span> <span class="token keyword">in</span> CHEATSHEET entries only.
The CHEATSHEET variable could be a name <span class="token punctuation">(</span>for imported cheatsheets<span class="token punctuation">)</span> or a <span class="token function">file</span> that describes a cheatsheet.
Omit KEYWORD to view the full cheatsheet.
</code></pre>
<h3>Alias</h3>
<p>If you are tired to type everything above to do a simple search, use the alias command:</p>
<pre class="language-bash"><code class="language-bash">$ bin/cheatorious <span class="token builtin class-name">help</span> <span class="token builtin class-name">alias</span>
Usage:
  cheatorious <span class="token builtin class-name">alias</span> NAME CHEATSHEET

<span class="token builtin class-name">return</span> a shell <span class="token builtin class-name">alias</span> <span class="token builtin class-name">command</span> with NAME <span class="token keyword">for</span> easy access to searching a CHEATSHEET.
The CHEATSHEET variable must be an imported cheatsheet.
Example: cheatorious <span class="token builtin class-name">alias</span> svim simple_vim <span class="token operator">>></span> ~/.bashrc
         next <span class="token function">time</span> just use: svim KEYWORD <span class="token punctuation">[</span>OPTIONS<span class="token punctuation">]</span>
</code></pre>
<p>It will just <strong>show</strong> the alias command, it's up to you to decide where to put it.</p>
<h3>Editing, reloading, removing</h3>
<pre class="language-bash"><code class="language-bash">$ cheatorious edit CHEATSHEET    <span class="token comment"># edit an existent CHEATSHEET. Will open it in the default terminal editor, use CHEATORIOUS_EDITOR environment variable to use another.</span>
$ cheatorious reload CHEATSHEET  <span class="token comment"># reload a CHEATSHEET after editing it with 'edit' command</span>
$ cheatorious remove CHEATSHEET  <span class="token comment"># remove a CHEATSHEET. The original file is kept for later recovering</span>
</code></pre>
<h2>Writers</h2>
<p>Cheatorious has a default Text writer to give a fairly nice output for your cheatsheet. Since we can have multiple writers avaiable, you can list and set a default writer through the command <em>writers</em>:</p>
<pre class="language-bash"><code class="language-bash">$ bin/cheatorious <span class="token builtin class-name">help</span> writers
Usage:
  cheatorious writers <span class="token punctuation">[</span>OPTIONS<span class="token punctuation">]</span>

Options:
  -d, <span class="token punctuation">[</span>--default<span class="token operator">=</span>DEFAULT<span class="token punctuation">]</span>  <span class="token comment"># set a default writer for next searches.</span>

lists the available writers or <span class="token builtin class-name">set</span> a default
</code></pre>
<p>I'll work to provide more options of output as soon as possible (markdown, colored, HTML, etc), but you can also contribute writing your own writers and sending a pull request to this project. Check the examples folder to see a Writer sample, you just need to implement that interface.</p>
<h2>Tips to improve the experience!</h2>
<ul>
<li>Create aliases to save typings in the cheatsheets you use more</li>
<li>Sync your cheatsheets with Dropbox by creating a symlink for the <code>~/.cheatorious</code> folder</li>
</ul>]]></description>
            <link>https://www.talleye.com/posts/rubygem-cheatorious</link>
            <guid isPermaLink="true">https://www.talleye.com/posts/rubygem-cheatorious</guid>
            <dc:creator><![CDATA[Luis Cipriani]]></dc:creator>
            <pubDate>Thu, 19 Jan 2012 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Fearless HTTP requests abuse]]></title>
            <description><![CDATA[<p>This talk was a result of all the research done when building <a href="https://www.talleye.com/posts/alexandria-platform">Alexandria Platform</a> at Abril. Every web developer probably had used cache one day in their web apps or APIs to avoid redundant data traffic, network bottlenecks, protect your server from load spikes or simply long network latencies.</p>
<p>The slides below were done basically after reading Caching RFC, so if you want a visual reference of web caching, here it goes:</p>
<p><a href="https://www.slideshare.net/lfcipriani/fearless-http-requests-abuse" title="Open Slideshare" target="_blank"><img src="https://www.talleye.com/img-posts/fhra-slides.jpg" alt=""></a></p>
<p>Later in 2013 I presented another talk at Rubyconf Brazil giving more context about caching strategies, you can find <a href="https://www.talleye.com/posts/the-4-basic-flows-of-http-caching">the article in this website as well</a>.</p>]]></description>
            <link>https://www.talleye.com/posts/fearless-http-requests-abuse</link>
            <guid isPermaLink="true">https://www.talleye.com/posts/fearless-http-requests-abuse</guid>
            <dc:creator><![CDATA[Luis Cipriani]]></dc:creator>
            <pubDate>Sat, 26 Nov 2011 00:00:00 GMT</pubDate>
        </item>
    </channel>
</rss>