<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jorge Alvarez</title>
    <description>The latest articles on DEV Community by Jorge Alvarez (@jorgealvarez).</description>
    <link>https://dev.to/jorgealvarez</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F104867%2F0bf1d775-7adf-43ba-8bad-0922400b52b2.jpg</url>
      <title>DEV Community: Jorge Alvarez</title>
      <link>https://dev.to/jorgealvarez</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jorgealvarez"/>
    <language>en</language>
    <item>
      <title>Developers will become pilots</title>
      <dc:creator>Jorge Alvarez</dc:creator>
      <pubDate>Fri, 29 Mar 2024 12:29:56 +0000</pubDate>
      <link>https://dev.to/jorgealvarez/developers-will-become-pilots-4c0</link>
      <guid>https://dev.to/jorgealvarez/developers-will-become-pilots-4c0</guid>
      <description>&lt;p&gt;20 or 30 years ago pilots where actually flying airliners. They were in charge of all aspects of the fly, taking all decisions themselves.&lt;/p&gt;

&lt;p&gt;Today it's the plane itself that takes care of everything and pilots are just supervisors that only take control when there are unexpected conditions.&lt;/p&gt;

&lt;p&gt;And that is exactly what is going to happen to developers. To me, that's the perfect analogy.&lt;/p&gt;

&lt;p&gt;The AI will take care of most of the work and the developer will be supervising everything.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://unsplash.com/photos/black-vehicle-control-panel-REZp_5-2wzA"&gt;Image credits&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>development</category>
      <category>software</category>
    </item>
    <item>
      <title>When AI writes code, all code will be open source.</title>
      <dc:creator>Jorge Alvarez</dc:creator>
      <pubDate>Thu, 14 Mar 2024 10:01:26 +0000</pubDate>
      <link>https://dev.to/jorgealvarez/when-ai-writes-code-all-code-will-be-open-source-20gc</link>
      <guid>https://dev.to/jorgealvarez/when-ai-writes-code-all-code-will-be-open-source-20gc</guid>
      <description>&lt;p&gt;There is a lot of noise lately about Devin, an AI that can write code with a high degree of accuracy.&lt;/p&gt;

&lt;p&gt;We know that AI will eventually write most of the code that companies produce. The only question is not if but when that will happen on a big scale.&lt;/p&gt;

&lt;p&gt;The most important change that this will bring is that all code will be open source code.&lt;/p&gt;

&lt;p&gt;If I can use an AI to, for instance, create an invoicing software it would make sense that I release that code as open source, and if I don't, somebody else will. Then we enter the same cycle as current open source projects. People add changes, that would be AI generated, or request features that we would ask the AI to implement.&lt;/p&gt;

&lt;p&gt;Everybody will have access to any code because code now is a commodity.&lt;/p&gt;

&lt;p&gt;We will see that companies that now offer SaaS products will be doing less Software and much more Service.&lt;/p&gt;

&lt;p&gt;There will be still plenty of companies that will gladly pay to other companies to review, secure and deploy that open source code for them. But that's old news we currently have many companies that provide services based on open source projects. The difference is that we will be able to generate more open source code and faster.&lt;/p&gt;

&lt;p&gt;It is also a big opportunity for small, agile companies that can now offer services that before where only offered by big companies with hundreds of employees. No more you would need 100s of persons to develop and maintain a crm or an erp software, small boutiques will be able to offer those services much more cheaper and efficiently.&lt;/p&gt;

&lt;p&gt;As for us developers, there will still be a lot of work for us. It will just be more specialised. No more a 6 months boot-camp will grant you a job as a developer. I think the most demanded role will be someone with a mix of development, dev-ops and product skills. Those profiles will be able to gather good requirements, generate code using the AI, validating it and deploying it in a secure environment.&lt;/p&gt;

&lt;p&gt;People who are capable of perform well on all those tasks will be in high demand and less specialised people will have to improve their skills.&lt;/p&gt;

&lt;p&gt;In summary: AI will take care of the Software and small boutiques will take care of the Service. SaaS is here to stay.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>development</category>
      <category>software</category>
      <category>saas</category>
    </item>
    <item>
      <title>Find in batches and pluck</title>
      <dc:creator>Jorge Alvarez</dc:creator>
      <pubDate>Tue, 02 Jan 2024 16:34:00 +0000</pubDate>
      <link>https://dev.to/jorgealvarez/find-in-batches-and-pluck-4apb</link>
      <guid>https://dev.to/jorgealvarez/find-in-batches-and-pluck-4apb</guid>
      <description>&lt;p&gt;How to use find in batches in Ruby on Rails along with pluck.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NewYearJob&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform&lt;/span&gt;
    &lt;span class="no"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;in_batches&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;rotate_jobs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="no"&gt;RotateYearJob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="no"&gt;ActiveJob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform_all_later&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rotate_jobs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>rails</category>
      <category>performance</category>
      <category>ruby</category>
      <category>development</category>
    </item>
    <item>
      <title>Normalise array in Rails 7.1</title>
      <dc:creator>Jorge Alvarez</dc:creator>
      <pubDate>Tue, 10 Oct 2023 06:07:32 +0000</pubDate>
      <link>https://dev.to/jorgealvarez/normalise-array-in-rails-71-afd</link>
      <guid>https://dev.to/jorgealvarez/normalise-array-in-rails-71-afd</guid>
      <description>&lt;p&gt;One of the (many) good features of Rails 7.1 is the &lt;code&gt;normalizes&lt;/code&gt; method. This is how you can use it with fields of type array. &lt;/p&gt;

&lt;p&gt;In this example there is a field called folders that we want to normalise removing blank entries and ensuring all entries are down cased.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;normalizes&lt;/span&gt; &lt;span class="ss"&gt;:folders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;with: &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:downcase&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:blank?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>ruby</category>
      <category>rails</category>
      <category>postgres</category>
      <category>programming</category>
    </item>
    <item>
      <title>Requesting a technical challenge is useless and rude</title>
      <dc:creator>Jorge Alvarez</dc:creator>
      <pubDate>Thu, 28 Sep 2023 08:43:34 +0000</pubDate>
      <link>https://dev.to/jorgealvarez/requesting-a-technical-challenge-is-useless-and-rude-1hbi</link>
      <guid>https://dev.to/jorgealvarez/requesting-a-technical-challenge-is-useless-and-rude-1hbi</guid>
      <description>&lt;p&gt;These are the main reasons why your company should remove the technical challenge step from the hiring process:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1) It's rude.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Requesting people to perform unpaid work in their free time for you is just impolite. Do Not Do That.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2) There is a better way to validate the technical abilities of a candidate: A personal interview.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Talking to candidates not only will help you detect their expertise and knowledge about any technical area but it will also help you detect if that person is a good team player, someone that can work nicely with others. You want to hire people that will get along well with the rest of your coworkers. &lt;em&gt;You don't want to hire divas, no matter how skilled they are.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;You can teach people best practices, patterns, technologies.... but you can't teach them manners, attitude, politeness.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;3) You will discard good candidates.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Usually the technical challenge is employed as a first filter to remove candidates.  Again that is not a good way of doing it. How somebody solves a specific problem is not a very good indicator. Someone that produces a mediocre solution to the challenge may still be a good hiring because they are a good fit for the team (culturally, soft skills...). Maybe the had time constraints and they couldn't deliver the best solution. Probably they are applying to other positions, their current job... Assuming that all candidates can allocate a few hours is just something you should not do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4) It's them that will change, not you&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It doesn't matter how well they organise their code or what standards they use because they will have to adapt and use your company's standards and best practices. You can teach people best practices, patterns, technologies.... but you can't teach them manners, attitude, politeness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5) That code might not be theirs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;People can cheat in the technical challenge but they can't cheat face to face. You never know if that code was actually produced by them or they got help from friends or other sources.  When they are in an interview with you there is no way they can cheat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6) Show them your code.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Instead of asking the candidate to write some code so you can discuss about it, just show them your own code. The same code they will have to work with if they get hired. Ask them about it, what they like, what they don't, what they will change... You will get much more useful insights than reviewing some arbitrary code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Unless you want to hire robots that work in isolation you probably want to hire people that are skilled, have good technical knowledge but also good attitude, open, friendly and capable of working on a team. The technical challenge can't help you in any of those areas, on the contrary, it will eliminate good potential candidates.&lt;/p&gt;

</description>
      <category>development</category>
      <category>management</category>
      <category>hiring</category>
      <category>career</category>
    </item>
    <item>
      <title>Integration tests with minitest, Sorcery and Rails 7</title>
      <dc:creator>Jorge Alvarez</dc:creator>
      <pubDate>Wed, 16 Aug 2023 13:02:44 +0000</pubDate>
      <link>https://dev.to/jorgealvarez/integration-tests-with-minitest-sorcery-and-rails-7-2ejk</link>
      <guid>https://dev.to/jorgealvarez/integration-tests-with-minitest-sorcery-and-rails-7-2ejk</guid>
      <description>&lt;p&gt;The example app at &lt;a href="https://github.com/Sorcery/sorcery-example-app/tree/master"&gt;https://github.com/Sorcery/sorcery-example-app/tree/master&lt;/a&gt; does not work for integration tests. Here is a configuration that helps you log in using Sorcery.&lt;/p&gt;

&lt;p&gt;Edit your &lt;code&gt;test_helper.rb file&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ActionController::TestCase&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Sorcery&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TestHelpers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Controller&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ActionDispatch::IntegrationTest&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Sorcery&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TestHelpers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Integration&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Sorcery&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;TestHelpers&lt;/span&gt;
    &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Rails&lt;/span&gt;
      &lt;span class="c1"&gt;# Passwords? Where we are going, we don't need passwords.&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;login_user_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'top-secret'&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="vi"&gt;@user&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;password_confirmation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save!&lt;/span&gt;
        &lt;span class="n"&gt;where&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;presence&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;user_sessions_path&lt;/span&gt;
        &lt;span class="nb"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="ss"&gt;password: &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now in your integration files you can log in using the method &lt;code&gt;login_user_post&lt;/code&gt; with a user's email.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tests/integration/admin/settings_test.rb&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Admin::SettingsTest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;IntegrationTest&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotManager&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Admin&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SettingsTest&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;
      &lt;span class="vi"&gt;@user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;login_user_post&lt;/span&gt; &lt;span class="vi"&gt;@user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s1"&gt;'can not access admin area'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;admin_settings_url&lt;/span&gt;

      &lt;span class="n"&gt;assert_response&lt;/span&gt; &lt;span class="ss"&gt;:redirect&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Manager&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Admin&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SettingsTest&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;
      &lt;span class="vi"&gt;@user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:manager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;login_user_post&lt;/span&gt; &lt;span class="vi"&gt;@user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s1"&gt;'can access admin area'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;admin_settings_url&lt;/span&gt;

      &lt;span class="n"&gt;assert_response&lt;/span&gt; &lt;span class="ss"&gt;:success&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>rails</category>
      <category>ruby</category>
      <category>minitest</category>
      <category>testing</category>
    </item>
    <item>
      <title>Write code that helps your users</title>
      <dc:creator>Jorge Alvarez</dc:creator>
      <pubDate>Tue, 15 Aug 2023 14:34:03 +0000</pubDate>
      <link>https://dev.to/jorgealvarez/write-code-that-helps-your-users-1eh9</link>
      <guid>https://dev.to/jorgealvarez/write-code-that-helps-your-users-1eh9</guid>
      <description>&lt;p&gt;We all know web developers that are so busy doing &lt;em&gt;"technical"&lt;/em&gt; things that they don't have time to think about end users.&lt;/p&gt;

&lt;p&gt;For them development is all about patterns, abstractions, refactors, technical debt (in another post I'll explain why technical debt doesn't really exist)...&lt;/p&gt;

&lt;p&gt;Don't be one of those developers.&lt;/p&gt;

&lt;p&gt;You write code to solve problems to the users of your application. Every time you are working on something that adds no value to those users &lt;strong&gt;you are doing it wrong&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It's been a productive day of work if you have made things faster for your users, if they need to click one less thing to perform their job, if a dashboard has the new data they've been requesting, better accessibility...&lt;/p&gt;

&lt;p&gt;You have a job because your application has users. It's those users that pay your salary, not your company.&lt;/p&gt;

&lt;p&gt;Prioritise work that improves the life of your users over work that only helps you and your fellow developers. Your priority is to make things easy for the users not for yourself. If you can have both then that's perfect but if you need to choose there is only one right choice.&lt;/p&gt;

&lt;p&gt;In an ideal world we have unlimited time and resources but more often than not you need to take decisions. There are trade-offs to be made. &lt;/p&gt;

&lt;p&gt;One of things that differentiates a senior developer from the rest is their ability to make those trade-offs. Another one is that they used to have user metrics in their dashboards. Application and server monitoring is absolutely necessary and so it is knowing what users do on your platform.  How many times they access the application? What sections do they visit? What actions they perform the most? What reports they use?&lt;/p&gt;

&lt;p&gt;Remember, the code you write is just a means to an end: Helping users on their daily tasks. Everything else can wait.&lt;/p&gt;

&lt;p&gt;Cover image by: &lt;a href="https://unsplash.com/@nikkotations"&gt;Nikkotations&lt;/a&gt;&lt;/p&gt;

</description>
      <category>development</category>
      <category>productivity</category>
      <category>discuss</category>
      <category>saas</category>
    </item>
    <item>
      <title>How to create a secure password that you can remember</title>
      <dc:creator>Jorge Alvarez</dc:creator>
      <pubDate>Mon, 18 Jan 2021 11:47:00 +0000</pubDate>
      <link>https://dev.to/jorgealvarez/how-to-create-a-secure-password-that-you-can-remember-74e</link>
      <guid>https://dev.to/jorgealvarez/how-to-create-a-secure-password-that-you-can-remember-74e</guid>
      <description>&lt;p&gt;It's the eternal struggle, creating a strong password that is hard to crack yet easy to remember.&lt;/p&gt;

&lt;p&gt;We are usually forced to use random letters uppercase and downcase, numbers and symbols and the password should be at least 8 characters long. &lt;/p&gt;

&lt;p&gt;All of that is very good for security but very bad for our brains.  If you try to remember: &lt;em&gt;HP2Epzo&amp;amp;BTPuyQV&lt;/em&gt; chances are that you will end up writing it down on a piece of paper.  &lt;/p&gt;

&lt;p&gt;So here is the trick I use to generate passwords that are easy to memorize.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use letters from a sentence
&lt;/h2&gt;

&lt;p&gt;It has the perfect balance between security and rememberability.&lt;/p&gt;

&lt;p&gt;Think of a sentence that you remember from a movie or from a song that you like and then use the first letter of each word to create the password. &lt;/p&gt;

&lt;p&gt;To add an extra level of security you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the last letter from each word.&lt;/li&gt;
&lt;li&gt;Alternate between the first and the last word.&lt;/li&gt;
&lt;li&gt;Substitute one letter with a symbol like: # $ % &amp;amp; * . ,&lt;/li&gt;
&lt;li&gt;Add numbers to the sentence if there are none.&lt;/li&gt;
&lt;li&gt;Think of a song you don't like yet you know the lyrics instead of one that you like.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;... be creative.&lt;/p&gt;

&lt;p&gt;These are a couple of examples using Iron Maiden songs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fly on you way like an eagle, fly as high as the sun.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The resulting password is: &lt;em&gt;foywlaefahats&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's improve it a bit:&lt;/p&gt;

&lt;p&gt;With an ampersand to join the two sentences: &lt;em&gt;foywlae&amp;amp;fahats&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Adding the year that the album was released: &lt;em&gt;foywlae&amp;amp;fahats1983&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Another example:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Oh Well, wherever, wherever you are, Iron Maiden's gonna get you, no matter how far.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The resulting password is: &lt;em&gt;owwwyaimiggynmhf&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That's a very good password by itself but we can spice it up.&lt;/p&gt;

&lt;p&gt;Put Iron Maiden in uppercase: &lt;em&gt;owwwyaIMiggynmhf&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Add some numbers and symbols. I'm adding the year that the album was released and changing the first O with an asterisk.&lt;/p&gt;

&lt;p&gt;The resulting password is: &lt;em&gt;1980*wwwyaIMiggynmhf&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;As you can see generating secure passwords that are easy to remember is not as hard as it may seems if you are creative.&lt;/p&gt;

&lt;p&gt;Anyway what I would recommend you is to use a password manager if you can. Let them generate the passwords for you.&lt;/p&gt;

&lt;p&gt;But even a password manager needs a password to open it and also there are other situations like starting a session in your computer, that requires you to enter a password.&lt;/p&gt;

&lt;p&gt;With the help of your favorite movie/song and a bit of creativity you can create good passwords that are hard to crack and yet rememberable. &lt;/p&gt;

</description>
      <category>security</category>
      <category>developers</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Open Source event tracking and gamification engine</title>
      <dc:creator>Jorge Alvarez</dc:creator>
      <pubDate>Fri, 08 Jan 2021 14:43:37 +0000</pubDate>
      <link>https://dev.to/jorgealvarez/open-source-event-tracking-and-gamification-engine-25dn</link>
      <guid>https://dev.to/jorgealvarez/open-source-event-tracking-and-gamification-engine-25dn</guid>
      <description>&lt;h2&gt;
  
  
  Siete Valles
&lt;/h2&gt;

&lt;p&gt;The goal of &lt;a href="https://github.com/jorgegorka/siete-valles" rel="noopener noreferrer"&gt;this application&lt;/a&gt; is to provide an easy way to integrate user engagement schemas and event tracking into your existent applications.&lt;/p&gt;

&lt;p&gt;You can create specific schemas like an onboarding process and keep track of the progress of each individual employee. Or you can implement generic long term schemas and event tracking like tracking the activity of users on a website (articles, comments, likes, etc.).&lt;/p&gt;

&lt;h2&gt;
  
  
  Use cases and examples
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Track important application events.&lt;/li&gt;
&lt;li&gt;Measure user's engagement.&lt;/li&gt;
&lt;li&gt;Lead users conducting important events like onboarding, placing an order...&lt;/li&gt;
&lt;li&gt;Etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are a few &lt;a href="https://github.com/jorgegorka/siete-valles/wiki#examples" rel="noopener noreferrer"&gt;implementation examples&lt;/a&gt; available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical details
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/jorgegorka/siete-valles" rel="noopener noreferrer"&gt;Siete Valles&lt;/a&gt; has been developed with Ruby on Rails and it has a full featured Graphql A.P.I.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jorgegorka/siete-valles/wiki" rel="noopener noreferrer"&gt;Detailed documentation available&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technology
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Rails 6.1&lt;/li&gt;
&lt;li&gt;Graphql&lt;/li&gt;
&lt;li&gt;Stimulus JS&lt;/li&gt;
&lt;li&gt;TailwindCSS&lt;/li&gt;
&lt;li&gt;Mysql&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can check all my &lt;a href="https://www.alvareznavarro.es/open-source" rel="noopener noreferrer"&gt;open source projects&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Siete Valles means Seven Valleys and this is where &lt;a href="https://en.wikipedia.org/wiki/La_Rioja#Hydrography" rel="noopener noreferrer"&gt;the name comes from&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Faltqc15um5le4tuaerlt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Faltqc15um5le4tuaerlt.png" alt="Siete valles dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gamification</category>
      <category>rails</category>
      <category>analytics</category>
      <category>graphql</category>
    </item>
    <item>
      <title>Svelte Router SPA now supports route localisation</title>
      <dc:creator>Jorge Alvarez</dc:creator>
      <pubDate>Wed, 15 Jan 2020 15:15:29 +0000</pubDate>
      <link>https://dev.to/jorgealvarez/svelte-router-spa-now-supports-route-localisation-14i0</link>
      <guid>https://dev.to/jorgealvarez/svelte-router-spa-now-supports-route-localisation-14i0</guid>
      <description>&lt;p&gt;Today I've released version 5.2.0 of &lt;a href="https://github.com/jorgegorka/svelte-router"&gt;Svelte Router&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;It's an easy to use routing library for Single Page Applications developed with Svelte JS.&lt;/p&gt;

&lt;p&gt;The biggest feature in this version is support for &lt;strong&gt;route localisation&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Define your routes in a single interface&lt;/li&gt;
&lt;li&gt;Layouts global, per page or nested.&lt;/li&gt;
&lt;li&gt;Nested routes.&lt;/li&gt;
&lt;li&gt;Named params.&lt;/li&gt;
&lt;li&gt;Localisation.&lt;/li&gt;
&lt;li&gt;Guards to protect urls. Public and private urls.&lt;/li&gt;
&lt;li&gt;Track pageviews in Google Analytics (optional).&lt;/li&gt;
&lt;li&gt;Use standard &lt;a href="/about-us"&gt;About&lt;/a&gt; elements to navigate between pages (or use  for bonus features).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is an example of how to define routes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PublicIndex&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;es&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;iniciar-sesion&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;signup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SignUp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;es&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;registrarse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AdminLayout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;es&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;administrador&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;nestedRoutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;report&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReportsIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;es&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;informes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;employees&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EmployeesIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;es&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;empleados&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
              &lt;span class="na"&gt;nestedRoutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;show/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ShowEmployeeLayout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;es&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mostrar/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;it&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mostrare/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                  &lt;span class="na"&gt;nestedRoutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                      &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ShowEmployee&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;calendar/:month&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                      &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CalendarEmployee&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                      &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;es&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;calendario/:month&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;de&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;kalender/:month&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                  &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
              &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What started as a small project has become now a full featured routing library for Svelte applications.&lt;/p&gt;

&lt;p&gt;What's next in my TODO is refactoring some parts of the library to make the code easy to read and understand. It has a comprehensive suite of tests so it shouldn't be much of a problem.&lt;/p&gt;

&lt;p&gt;If you use it in a project please send me your comments, suggestions and ideas here: &lt;a href="https://github.com/jorgegorka/svelte-router/issues"&gt;https://github.com/jorgegorka/svelte-router/issues&lt;/a&gt;&lt;/p&gt;

</description>
      <category>svelte</category>
      <category>javascript</category>
      <category>spa</category>
    </item>
    <item>
      <title>Open source e-commerce Rails 6 &amp; GraphQL with JWT</title>
      <dc:creator>Jorge Alvarez</dc:creator>
      <pubDate>Wed, 30 Oct 2019 11:03:04 +0000</pubDate>
      <link>https://dev.to/jorgealvarez/open-source-e-commerce-rails-6-graphql-with-jwt-3h3h</link>
      <guid>https://dev.to/jorgealvarez/open-source-e-commerce-rails-6-graphql-with-jwt-3h3h</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/jorgegorka/demanda"&gt;Demanda&lt;/a&gt; is an open source e-commerce made with Ruby on Rails, GraphQL and JWT.&lt;/p&gt;

&lt;p&gt;It started as a student's material for a web development course I teach but it has evolved since. &lt;/p&gt;

&lt;p&gt;Whether you are looking for a resource to learn more about GraphQL, Ruby on Rails and JWT authentication or just want a basic e-commerce platform that you can evolve to suit your needs &lt;a href="https://github.com/jorgegorka/demanda"&gt;Demanda&lt;/a&gt; can help you.&lt;/p&gt;

&lt;p&gt;Backend is a ruby on rails well tested application using GraphQL and JWT for authentication.&lt;/p&gt;

&lt;p&gt;There is also an example admin interface developed with Svelte JS and Apollo client. It uses TailwindCSS for styling.&lt;/p&gt;

&lt;p&gt;I will post in detail about each aspect of the application like authentication, crud, testing, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jorgegorka/demanda"&gt;https://github.com/jorgegorka/demanda&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>graphql</category>
      <category>svelte</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>Secure Firestore rules for Firebase</title>
      <dc:creator>Jorge Alvarez</dc:creator>
      <pubDate>Tue, 09 Jul 2019 13:02:20 +0000</pubDate>
      <link>https://dev.to/jorgealvarez/secure-firestore-rules-for-firebase-52df</link>
      <guid>https://dev.to/jorgealvarez/secure-firestore-rules-for-firebase-52df</guid>
      <description>&lt;p&gt;Firestore rules give us the possibility to configure and secure a Firebase database. In this article you will learn how to create a set of rules that are easy to read and maintain.&lt;/p&gt;

&lt;p&gt;All the code mentioned in this article is availaible in the &lt;a href="https://github.com/jorgegorka/svelte-firebase"&gt;Svelte &amp;amp; Firebase repository&lt;/a&gt; and you can download it for free.&lt;/p&gt;

&lt;h3&gt;
  
  
  Table of contents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Some thoughts on security&lt;/li&gt;
&lt;li&gt;Basic rules

&lt;ul&gt;
&lt;li&gt;Grant/Deny access to documents&lt;/li&gt;
&lt;li&gt;Use functions to improve clarity&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Advanced rules

&lt;ul&gt;
&lt;li&gt;Return only a subset of documents&lt;/li&gt;
&lt;li&gt;Allow special permissions to administrators&lt;/li&gt;
&lt;li&gt;Filter by current user&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Some thoughts on security
&lt;/h2&gt;

&lt;p&gt;In a web application we can not trust the client. All the code that is being executed in somebody else's computer can be tampered and hacked.&lt;/p&gt;

&lt;p&gt;If we do not configure our database properly anybody will be able to request any data from our database.&lt;/p&gt;

&lt;p&gt;All the checks in Firestore rules take place in the Firebase servers so there is no chance for users to change them.&lt;/p&gt;

&lt;p&gt;The only information we can trust is the authentication data. After a user successfully log in all comunications between our application and the Firebase database include a token with the session information.&lt;/p&gt;

&lt;p&gt;This token is the only valid piece of information that can not be modified by the user.&lt;/p&gt;

&lt;p&gt;The token gives us the possibility to save some extra information (user claims) that we can use to improve our rules.&lt;/p&gt;

&lt;p&gt;Let's see all this in action:&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic rules
&lt;/h2&gt;

&lt;p&gt;This is an example of the basic structure for securing a document:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  match /teams/{teamId} {
    allow read: if isSignedIn();
    allow create: if userAndAdmin();
    allow update, delete: if companyAdmin()
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://firebase.google.com/docs/firestore/security/rules-structure"&gt;Firestore rules&lt;/a&gt; have basic &lt;em&gt;read&lt;/em&gt; and &lt;em&gt;write&lt;/em&gt; rules. Read rules can be broken into &lt;em&gt;get&lt;/em&gt; and &lt;em&gt;list&lt;/em&gt; while &lt;em&gt;write&lt;/em&gt; rules can be broken into &lt;em&gt;create&lt;/em&gt;, &lt;em&gt;update&lt;/em&gt; and &lt;em&gt;delete&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In the former example we are creating a rule for &lt;em&gt;reads&lt;/em&gt;, another rule for &lt;em&gt;create&lt;/em&gt; and another one for &lt;em&gt;update&lt;/em&gt; and &lt;em&gt;delete&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Grant/Deny access to documents
&lt;/h3&gt;

&lt;p&gt;The way to allow access to a document is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;allow (read/write): if &amp;lt;condition&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We just need to define the operation that we want to allow and add a condition. If the condition is true the rule will succeed and the document will be returned to the client. If the condition fails the document will not be returned to the client.&lt;/p&gt;

&lt;p&gt;If we have more than one rule for a single document Firebase will succedd if &lt;strong&gt;any&lt;/strong&gt; of the rules return true.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use functions to improve clarity
&lt;/h3&gt;

&lt;p&gt;A good tip to help you improve clarity and reuse code is to use functions to define your logic and use that functions in the rule definition.&lt;/p&gt;

&lt;p&gt;Let's create our first rule. We want visitors to be able to read the contents of the teams document only if they are logged in.&lt;/p&gt;

&lt;p&gt;This is how we would create that rule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  match /teams/{teamId} {
    allow read: if isSignedIn();
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and this is the function we create:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  function isSignedIn() {
    return (request.auth.uid != null)
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are checking the &lt;em&gt;request&lt;/em&gt; object, available in all rules, to see if there is an auth uid. If the request has been made by a logged in user auth.uid will return the user id of the user. It will be empty otherwise.&lt;/p&gt;

&lt;p&gt;Now with this rule in place &lt;strong&gt;only&lt;/strong&gt; logged in users will be able to read the teams documents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced rules
&lt;/h2&gt;

&lt;p&gt;Now that we know how to create basic rules let's add some more rules to improve the security of our database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Return only a subset of documents
&lt;/h3&gt;

&lt;p&gt;With the only rule that we've created so far if you are logged in you have access to all the teams in our database. In our application users belong to a company so it makes sense that they can see only teams that belong to their company.&lt;/p&gt;

&lt;p&gt;Let's create a function that checks that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  function userBelongsToCompany() {
    return request.auth.token.companyId == resource.data.companyId
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've mentioned before user claims. Those are pieces of information we can add to the session token with useful data. In our case when we create an employee we add two pieces of information: the Id of the company and the role. Check this code to see &lt;a href="https://github.com/jorgegorka/svelte-firebase/blob/master/functions/index.js#L31"&gt;how to add custom user claims&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We are comparing the &lt;em&gt;request.auth.token.companyId&lt;/em&gt; with the &lt;em&gt;resource.data.companyId&lt;/em&gt;. In &lt;em&gt;resource.data&lt;/em&gt; Firestore gives us access to each document that will be returned. If the companyId of the document does not match the companyId of the user the document won't be returned.&lt;/p&gt;

&lt;p&gt;Now that we have the &lt;em&gt;userBelongsToCompany&lt;/em&gt; function we can change our rule to use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  match /teams/{teamId} {
    allow read: if isSignedIn() &amp;amp;&amp;amp; userBelongsToCompany();
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now in order to read a document, or a list of documents two conditions must be met. The user must be logged in and the companyId of the user must match the companyId of the documents returned.&lt;/p&gt;

&lt;h3&gt;
  
  
  Allow special permission to administrators
&lt;/h3&gt;

&lt;p&gt;Roles are a very common feature in many web applications. This is how we can apply roles to our rules :-).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  function userIsAdmin() {
    return request.auth.token.role == 'admin'
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have another user custom claim defined called &lt;em&gt;role&lt;/em&gt;. It's now very easy for us to check if the user is an admin.&lt;/p&gt;

&lt;p&gt;For the sake of clarity we add another function like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  function userAndAdmin() {
    return isSignedIn() &amp;amp;&amp;amp; userBelongsToCompany() &amp;amp;&amp;amp; userIsAdmin()
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if we want that only admins would be able to create new teams we add a this new rule.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  match /teams/{teamId} {
    allow read: if isSignedIn() &amp;amp;&amp;amp; userBelongsToCompany();
    allow create: if userAndAdmin();
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only admin users that belong to our company can create new teams. Regular users can only read them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Filter by current user
&lt;/h3&gt;

&lt;p&gt;What if we want that regular users can edit their own documents but not others, while admins can edit any document? ... Rules to the rescue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  function adminOrOwner() {
    return userBelongsToCompany() &amp;amp;&amp;amp; (userAndAdmin() || resource.data.employeeId == request.auth.uid)
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I bet you saw that comming, right? We check a field in the data returned called &lt;em&gt;employeeId&lt;/em&gt; and compare it to the id of the logged in user. If they match the rule will be successful. If they don't it would still succedd if the user is an admin. Whether the user is an admin or not they must belong to our company so the first check is the &lt;em&gt;userBelongsToCompany&lt;/em&gt; function.&lt;/p&gt;

&lt;p&gt;This is how we would implement that rule if we want employees (for instace) to be able to edit their own records.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  match /employees/{employeeId} {
    allow update: if adminOrOwner()
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;You need to spend time thinking about who should have access to your Firestore databases. Never trust a client request since it may be compromised. Do all your checkings in the server using Firestore rules and the session information. With the help of custom user claims and functions it should be very easy to secure your database.&lt;/p&gt;

&lt;p&gt;If you want to see these rules in action in a live application download the free &lt;a href="https://github.com/jorgegorka/svelte-firebase"&gt;Svelte and Firebase template&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>firebase</category>
      <category>firestore</category>
      <category>javascript</category>
      <category>security</category>
    </item>
  </channel>
</rss>
