<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="http://artz.to/blog/feed.xml" rel="self" type="application/atom+xml" /><link href="http://artz.to/blog/" rel="alternate" type="text/html" /><updated>2026-06-17T21:44:56+00:00</updated><id>http://artz.to/blog/feed.xml</id><title type="html">arthurmolina.com</title><subtitle>I&apos;m a software developer and I love to looking for solutions to problems.</subtitle><author><name>Arthur Molina</name></author><entry><title type="html">Migration to Jekyll</title><link href="http://artz.to/blog/blog/migration-to-jekyll/" rel="alternate" type="text/html" title="Migration to Jekyll" /><published>2024-03-22T00:00:00+00:00</published><updated>2024-03-22T23:00:00+00:00</updated><id>http://artz.to/blog/blog/migration-to-jekyll</id><content type="html" xml:base="http://artz.to/blog/blog/migration-to-jekyll/"><![CDATA[<p>Little by little I am migrating and translating all my texts from old blogs and articles spread across the internet to my old new corner. Many texts are not worth transplanting, others lose their meaning when translated. But with baby steps I will do this work and try to maintain at least their chronology. I hope it’s worth reading. Thanks new/old/ghost reader for stopping by.</p>]]></content><author><name>Arthur Molina</name></author><category term="Blog" /><category term="nonsense" /><summary type="html"><![CDATA[Little by little I am migrating and translating all my texts from old blogs and articles spread across the internet to my old new corner. Many texts are not worth transplanting, others lose their meaning when translated. But with baby steps I will do this work and try to maintain at least their chronology. I hope it’s worth reading. Thanks new/old/ghost reader for stopping by.]]></summary></entry><entry><title type="html">Minimizing Gems/Libraries in Ruby on Rails: Keeping Code Within the Repository</title><link href="http://artz.to/blog/blog/minimizing-gems-libraries-in-ruby-on-rails/" rel="alternate" type="text/html" title="Minimizing Gems/Libraries in Ruby on Rails: Keeping Code Within the Repository" /><published>2024-01-11T00:00:00+00:00</published><updated>2024-01-11T19:00:00+00:00</updated><id>http://artz.to/blog/blog/minimizing-gems-libraries-in-ruby-on-rails</id><content type="html" xml:base="http://artz.to/blog/blog/minimizing-gems-libraries-in-ruby-on-rails/"><![CDATA[<p>When we are developing web systems it is easy to add a new gem or library without thinking much about it. It does what we need, if I’m going to have to create that functionality, I might need a long time. It will save me a lot of work. The code is already there ready to be used. It has an entire documentation, tests and (sometimes) a whole community providing support and developing new features.</p>

<p>So… Why not?</p>

<p>However, while gems and libraries can offer many benefits, there are compelling reasons to exercise caution and minimize their usage in favor of keeping code within the repository.</p>

<h1 id="version-incompatibilities">Version Incompatibilities</h1>

<p>One of the foremost concerns when relying heavily on external gems and libraries is the risk of version incompatibilities. As Rails and its associated gems evolve, newer versions may introduce breaking changes or conflicts with existing code. This can lead to painstaking troubleshooting, dependency resolution, and potential downtime. By minimizing external dependencies, developers can mitigate the risk of version conflicts and maintain greater control over their codebase’s stability.</p>

<p>I ran into a problem like this recently: I was working on an application that was many years old with an outdated version of Rails and Ruby. Many gems were old and needed to be updated. What happened was that one of the gems needed to be updated anyway because it was integrated with a third party service. To be able to carry out the update, it was necessary to update several other gems together, causing uncertainty as to whether everything would work out. Good thing we had acceptable test coverage and a wonderful QA team.</p>

<h1 id="code-maintenance">Code Maintenance</h1>

<p>Every gem or library added to a Rails project represents another piece of code that must be monitored, updated, and maintained. As time passes, maintaining compatibility with evolving dependencies becomes increasingly challenging, particularly if certain gems fall out of active development or lack adequate documentation. By keeping code within the repository, developers can simplify maintenance efforts, ensuring that all components of the application remain up-to-date and cohesive.</p>

<p>In the same code mentioned above we have several challenges that for now are just technical debts but that at an inopportune moment we will have to deal with. For example, this application is using a gem that is already deprecated, when we need to update another gem that it depends on, we will need to modify all the code where it is used to place a replacement. That time will come, one way or another.</p>

<h1 id="security-risks">Security Risks</h1>

<p>External dependencies can introduce security vulnerabilities into an application. While reputable gems are typically maintained with security best practices in mind, vulnerabilities may still arise, especially in lesser-known or unmaintained libraries. By minimizing the number of external dependencies, developers reduce the attack surface and can more effectively implement security measures tailored to their specific application.</p>

<p>At the same time, we are targets of hackers who can deliberately add backdoors and malicious codes to the packages downloaded for our applications. The media has already recorded several cases of security breaches in libraries, mainly from <a href="https://www.securityweek.com/dozens-of-malicious-npm-packages-steal-user-system-data/">NPM</a>. But RubyGems is also not safe from <a href="https://www.securityweek.com/two-malware-laced-gems-found-rubygems-repository/">malicious</a> <a href="https://www.theregister.com/2019/08/20/ruby_gem_hacked/">code</a> insertion.</p>

<h1 id="performance-overhead-and-dependency-bloat">Performance Overhead and Dependency Bloat</h1>

<p>Every gem/library included in a Rails project incurs a performance overhead, however minor. While individual performance impacts may seem negligible, they can accumulate over time, particularly in large-scale applications with numerous dependencies. Minimizing external dependencies allows developers to optimize performance by reducing unnecessary overhead and streamlining the application’s execution.</p>

<p>Over-reliance on gems/libraries can lead to dependency bloat, wherein a Rails project becomes burdened with an excessive number of external dependencies. This can impede development agility, increase deployment complexity, and hinder scalability. By prioritizing self-contained code within the repository, developers can maintain a leaner, more manageable codebase that is easier to maintain and scale over time.</p>

<h1 id="practices">Practices</h1>

<p>The author of the book <a href="https://sustainable-rails.com/">Sustainable Web Development with Ruby on Rails</a>, David Copeland, suggests that we should update dependencies Early and Often. His advice is to define one day per month for dependencies to be updated. In other words: “we’d run bundle update in our Rails apps, run the tests, fix what was broken, and then be up to date” (page 380). Another piece of advice from the author is to keep a Versioning Policy and Automate Dependecy Updates.</p>

<p>Another good way to be prepared for package updates, in addition to using minimal libraries, is to have good automated test coverage. And never forget integrity testing!</p>

<p>I worked on a system whose Ruby was on version 2.6.7 and needed to be updated to at least the next version because it was hosted on Heroku and the container that worked with this version would no longer be supported in 2 months (and in a few months more it would no longer be available for deployment). At the time I joined this project there was not one line of test written, and we didn’t have a QA team yet. It took 2 complicated months to write a minimum of test coverage so that we would be able to carry out the version update the Ruby and Rails versions with more confidence.</p>

<h1 id="conclusion">Conclusion</h1>

<p>While gems/libraries undoubtedly offer valuable functionality and convenience in Ruby on Rails development, their indiscriminate usage can introduce numerous challenges and risks. By prioritizing self-contained code within the repository, developers can minimize version incompatibilities, simplify maintenance efforts, mitigate security risks, optimize performance, and avoid dependency bloat. Ultimately, exercising restraint in the adoption of external dependencies promotes greater code stability, security, and maintainability, ensuring the long-term success of Rails applications.</p>]]></content><author><name>Arthur Molina</name></author><category term="Blog" /><category term="code" /><category term="electronics" /><category term="arduino" /><summary type="html"><![CDATA[When we are developing web systems it is easy to add a new gem or library without thinking much about it. It does what we need, if I’m going to have to create that functionality, I might need a long time. It will save me a lot of work. The code is already there ready to be used. It has an entire documentation, tests and (sometimes) a whole community providing support and developing new features.]]></summary></entry><entry><title type="html">Better ways to identify individual records in Ruby on Rails</title><link href="http://artz.to/blog/blog/better-ways-to-identify-individual-records-in-ruby-on-rails/" rel="alternate" type="text/html" title="Better ways to identify individual records in Ruby on Rails" /><published>2023-06-04T00:00:00+00:00</published><updated>2023-06-04T23:00:00+00:00</updated><id>http://artz.to/blog/blog/better-ways-to-identify-individual-records-in-ruby-on-rails</id><content type="html" xml:base="http://artz.to/blog/blog/better-ways-to-identify-individual-records-in-ruby-on-rails/"><![CDATA[<p>When we want to access a specific model record, the easiest way is to use the unique identifier as an access key. By default in SQL databases we have a sequential integer primary key. But this is not the only possible option. We also have the possibility of using UUID, Slug and Hash ID or Token. Each method offers distinct advantages and considerations, impacting factors like security, readability, and database performance. Drawing insights from real-world implementations and industry best practices, we explore the nuances of each method and their implications for modern applications. Let’s compare these approaches and explore why platforms like YouTube opt for Hash IDs.</p>

<h1 id="sequence-ids-embracing-predictability-and-efficiency">Sequence IDs: Embracing Predictability and Efficiency</h1>

<p>This is the easiest option to implement as it is the one available in most cases. When we create a new model or generate a scaffold, the Sequence ID is already the established standard. Sequence IDs maintain a predictable numerical sequence, facilitating database operations like range queries and indexing.</p>

<h2 id="advantages">Advantages</h2>

<ol>
  <li><strong>Sequentiality</strong>: Sequence IDs maintain a predictable numerical sequence, facilitating database operations like range queries and indexing.</li>
  <li><strong>Simplicity</strong>: Sequence IDs align with conventional database practices, requiring minimal configuration or external dependencies.</li>
  <li><strong>Efficiency</strong>: Sequential IDs promote efficient database indexing and storage, optimizing performance in certain use cases.</li>
</ol>

<h2 id="disadvantages">Disadvantages</h2>

<ol>
  <li><strong>Predictability</strong>: Sequential IDs expose database sequence information, potentially enabling attackers to infer patterns or exploit vulnerabilities.</li>
  <li><strong>Scalability</strong>: In distributed environments or high-throughput applications, managing sequential IDs across multiple nodes can pose scalability challenges.</li>
  <li><strong>Readability</strong>: Numeric IDs lack the human-readable qualities of hashed strings, potentially impacting user experience and URL aesthetics.</li>
</ol>

<h2 id="potentialities">Potentialities</h2>

<ol>
  <li><strong>Performance Optimization</strong>: Sequence IDs excel in scenarios where sequential access or range queries are prevalent, leveraging database indexing for enhanced performance.</li>
  <li><strong>Compatibility</strong>: Sequence IDs seamlessly integrate with existing database schemas and workflows, minimizing migration efforts and compatibility concerns.</li>
</ol>

<h2 id="how-to-do-this">How to do this</h2>

<p>You don’t need to do anything beyond the basics. This is already the standard.</p>

<h1 id="uuids-universally-unique-identifiers-ensuring-global-uniqueness-and-interoperability">UUIDs (Universally Unique Identifiers): Ensuring Global Uniqueness and Interoperability</h1>

<p>Universally Unique Identifiers (UUIDs) provide unparalleled guarantees of global uniqueness across distributed systems and time. This decentralized approach to identifier generation alleviates concerns about collision risks, making UUIDs ideal for large-scale deployments and decentralized architectures. However, the very long IDs and the lack of connection with more human information end up making their use difficult. It is a good option for those who have distributed databases or want to merge databases. I wouldn’t use it if I didn’t have one of these reasons.</p>

<h2 id="advantages-1">Advantages</h2>

<ol>
  <li><strong>Uniqueness</strong>: UUIDs guarantee global uniqueness across distributed systems and time, mitigating collision risks even in large-scale deployments.</li>
  <li><strong>Decentralization</strong>: UUIDs can be generated without centralized coordination, making them suitable for decentralized or distributed architectures.</li>
  <li><strong>Standardization</strong>: UUIDs adhere to established standards like RFC 4122, ensuring interoperability and compatibility across platforms and frameworks.</li>
</ol>

<h2 id="disadvantages-1">Disadvantages</h2>

<ol>
  <li><strong>Length</strong>: UUIDs are longer than sequential or hashed IDs, potentially impacting database storage and network transmission overhead.</li>
  <li><strong>Readability</strong>: UUIDs lack human-friendly qualities, often appearing as random strings of characters, which can diminish URL aesthetics and user experience.</li>
  <li><strong>Indexing Complexity</strong>: Indexing UUIDs in databases may incur performance penalties due to increased storage requirements and index fragmentation.</li>
</ol>

<h2 id="potentialities-1">Potentialities</h2>

<ol>
  <li><strong>Global Uniqueness</strong>: UUIDs provide unparalleled uniqueness guarantees, making them ideal for scenarios where collision avoidance is paramount.</li>
  <li><strong>Decentralized Generation</strong>: UUIDs facilitate distributed system architectures by enabling decentralized ID generation without centralized coordination.</li>
  <li><strong>Interoperability</strong>: Standardized UUID formats promote interoperability and compatibility across diverse platforms, frameworks, and technologies.</li>
</ol>

<h2 id="how-to-do-this-1">How to do this</h2>

<p>You’ve probably already come across several articles explaining how to use UUID in Rails so I’ll just put the summary version. So far I’ve only used UUID in Postgres database, so if you use MySQL I’ll be in debt.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># /db/migrations/0000000000_enable_pgcrypto_extension.rb</span>
<span class="k">class</span> <span class="nc">EnablePgcryptoExtension</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span><span class="p">[</span><span class="mf">6.1</span><span class="p">]</span>
  <span class="k">def</span> <span class="nf">change</span>
    <span class="n">enable_extension</span> <span class="s1">'pgcrypto'</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="c1"># /config/initializers/generators.rb</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">config</span><span class="p">.</span><span class="nf">generators</span> <span class="k">do</span> <span class="o">|</span><span class="n">g</span><span class="o">|</span>
  <span class="n">g</span><span class="p">.</span><span class="nf">orm</span> <span class="ss">:active_record</span><span class="p">,</span> <span class="ss">primary_key_type: :uuid</span>
<span class="k">end</span>

<span class="c1"># /db/migrations/000000_create_users.rb</span>
<span class="k">class</span> <span class="nc">CreateUsers</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span><span class="p">[</span><span class="mf">6.1</span><span class="p">]</span>
  <span class="k">def</span> <span class="nf">change</span>
    <span class="n">create_table</span> <span class="ss">:users</span><span class="p">,</span> <span class="ss">id: :uuid</span> <span class="k">do</span> <span class="o">|</span><span class="n">t</span><span class="o">|</span>
      <span class="n">t</span><span class="p">.</span><span class="nf">string</span> <span class="ss">:name</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h1 id="hash-ids-balancing-security-and-readability">Hash IDs: Balancing Security and Readability</h1>

<p>Hash IDs represent a cryptographic approach to generating unique identifiers by transforming numerical values into hashed strings. This technique offers several advantages, including enhanced security, improved readability, and collision avoidance. By obscuring database sequences, Hash IDs mitigate the risk of enumeration attacks and unauthorized access. Platforms like YouTube leverage Hash IDs to create user-friendly URLs, enhancing usability and brand recognition. However, Hash IDs also pose challenges, such as non-sequentiality and dependency on hashing algorithms. Despite these considerations, Hash IDs remain a popular choice for applications prioritizing security and user experience.</p>

<h2 id="advantages-2">Advantages</h2>

<ol>
  <li><strong>Security</strong>: Hash IDs obscure the underlying database sequence, enhancing privacy and deterring malicious activities such as data scraping and enumeration attacks. By transforming numerical IDs into hashed strings, sensitive information remains hidden from unauthorized access.</li>
  <li><strong>Readability</strong>: Hash IDs can be tailored to generate human-readable strings, enhancing user experience and URL aesthetics. Platforms like YouTube leverage Hash IDs to create user-friendly URLs, improving usability and brand recognition.</li>
  <li><strong>Collision Avoidance</strong>: Hash algorithms strive to minimize the likelihood of collisions, ensuring the uniqueness of generated IDs within practical constraints. While collisions can still occur, modern hash algorithms maintain a high level of collision resistance, reducing the risk of duplicate IDs.</li>
  <li><strong>Customization</strong>: Hash IDs offer flexibility in tailoring encoding schemes and incorporating application-specific logic. Platforms like YouTube can customize Hash IDs to incorporate additional metadata or encoding schemes, optimizing resource management and user experience.</li>
</ol>

<h2 id="disadvantages-2">Disadvantages</h2>

<ol>
  <li><strong>Non-Sequentiality</strong>: Hash IDs are non-sequential, which may complicate certain database operations like range queries or sorting. While platforms like YouTube prioritize security and user experience over sequentiality, developers must carefully consider the impact of non-sequential IDs on database performance and query optimization.</li>
  <li><strong>Dependency on Algorithms</strong>: The effectiveness of Hash IDs hinges on the cryptographic robustness of underlying hashing algorithms. Weak hash algorithms or improper configuration may compromise security and expose vulnerabilities to attacks like hash collisions or brute force decryption.</li>
  <li><strong>Limited Encoding Space</strong>: Depending on configuration, Hash IDs may have a finite encoding space, potentially leading to collisions in high-throughput environments. Balancing encoding space and collision resistance is essential to ensure the scalability and reliability of Hash IDs in large-scale deployments.</li>
</ol>

<h2 id="potentialities-2">Potentialities</h2>

<ol>
  <li><strong>Enhanced Security</strong>: With proper configuration, Hash IDs can bolster data security and mitigate risks associated with exposing numerical IDs. By obscuring database sequences and deterring enumeration attacks, Hash IDs contribute to overall system security and integrity.</li>
  <li><strong>Improved User Experience</strong>: Hash IDs enable the creation of user-friendly, readable URLs, enhancing usability and brand recognition. Platforms like YouTube leverage Hash IDs to optimize user experience and facilitate content discovery through intuitive, descriptive URLs.</li>
  <li><strong>Scalability and Performance</strong>: Hash IDs promote efficient indexing and retrieval in high-throughput environments, ensuring optimal performance and scalability. By balancing collision resistance and encoding space, Hash IDs facilitate seamless resource management and query optimization in large-scale deployments.</li>
  <li><strong>Customization and Optimization</strong>: Hash IDs offer flexibility, enabling tailored encoding schemes and metadata incorporation. Platforms can optimize Hash IDs to meet specific application requirements, enhancing resource management, and user engagement.</li>
</ol>

<h2 id="youtubes-hash-id-implementation-a-case-study">YouTube’s Hash ID Implementation: A Case Study</h2>

<p>YouTube, one of the world’s largest video-sharing platforms, utilizes Hash IDs extensively for various reasons:</p>

<ol>
  <li><strong>Security</strong>: Hash IDs obscure the underlying database sequence, enhancing privacy and deterring malicious activities such as data scraping or enumeration attacks.</li>
  <li><strong>Readability</strong>: Hash IDs allow YouTube to generate user-friendly video URLs, enhancing usability and brand recognition.</li>
  <li><strong>Consistency</strong>: Hash IDs provide a consistent format for video identifiers, simplifying resource management and URL generation across the platform.</li>
  <li><strong>Scalability</strong>: With millions of videos uploaded daily, Hash IDs facilitate efficient indexing and retrieval, ensuring optimal performance and scalability.</li>
  <li><strong>Customization</strong>: YouTube can customize Hash IDs to incorporate additional metadata or encoding schemes, further optimizing resource management and user experience.</li>
</ol>

<p>By leveraging Hash IDs, YouTube maintains a balance between security, scalability, and user experience, reinforcing its position as a leading video-sharing platform in the digital landscape.</p>

<h2 id="how-to-do-this-2">How to do this</h2>

<p>We have the perfect hashing algorithm for this work: Hashids. There are several different Gems that do the same work as the code above, but I have a philosophy that says that if a file solves the problem there is no need to add another external library resulting in more complexity and problems when updating. There is no need to <a href="/blog/minimizing-gems-libraries-in-ruby-on-rails/">have a gem and have to deal with the hassles of maintaining it</a>. But the same way that I did for Gravatar, I opened <a href="https://github.com/jcypret/hashid-rails">hashid-rails gem</a> and took what is important:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># /app/models/concerns/hashable.rb</span>
<span class="c1"># frozen_string_literal: true</span>

<span class="c1"># Hashids - Change the numeric identifications to hash identifiers</span>
<span class="c1"># Based on a simplification of 'hashid-rails gem' but avoiding yet another gem that could be a concern file.</span>
<span class="k">module</span> <span class="nn">Hashable</span>
  <span class="kp">extend</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">Concern</span>

  <span class="k">def</span> <span class="nf">hashid</span>
    <span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="p">.</span><span class="nf">encode_id</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">to_param</span>
    <span class="n">hashid</span>
  <span class="k">end</span>

  <span class="c1"># Methods for the Model Class</span>
  <span class="k">module</span> <span class="nn">ClassMethods</span>
    <span class="k">def</span> <span class="nf">encode_id</span><span class="p">(</span><span class="n">ids</span><span class="p">)</span>
      <span class="k">if</span> <span class="n">ids</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="no">Array</span><span class="p">)</span>
        <span class="n">ids</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="nb">id</span><span class="o">|</span> <span class="n">hashid_encode</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span> <span class="p">}</span>
      <span class="k">else</span>
        <span class="n">hashid_encode</span><span class="p">(</span><span class="n">ids</span><span class="p">)</span>
      <span class="k">end</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">decode_id</span><span class="p">(</span><span class="n">ids</span><span class="p">)</span>
      <span class="k">if</span> <span class="n">ids</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="no">Array</span><span class="p">)</span>
        <span class="n">ids</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="nb">id</span><span class="o">|</span> <span class="n">hashid_decode</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span> <span class="p">}</span>
      <span class="k">else</span>
        <span class="n">hashid_decode</span><span class="p">(</span><span class="n">ids</span><span class="p">).</span><span class="nf">first</span>
      <span class="k">end</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">find</span><span class="p">(</span><span class="o">*</span><span class="n">ids</span><span class="p">)</span>
      <span class="n">expects_array</span> <span class="o">=</span> <span class="n">ids</span><span class="p">.</span><span class="nf">first</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="no">Array</span><span class="p">)</span>

      <span class="n">uniq_ids</span> <span class="o">=</span> <span class="n">ids</span><span class="p">.</span><span class="nf">flatten</span><span class="p">.</span><span class="nf">compact</span><span class="p">.</span><span class="nf">uniq</span>
      <span class="n">uniq_ids</span> <span class="o">=</span> <span class="n">uniq_ids</span><span class="p">.</span><span class="nf">first</span> <span class="k">unless</span> <span class="n">expects_array</span> <span class="o">||</span> <span class="n">uniq_ids</span><span class="p">.</span><span class="nf">size</span> <span class="o">&gt;</span> <span class="mi">1</span>

      <span class="k">if</span> <span class="n">uniq_ids</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="no">Array</span><span class="p">)</span>
        <span class="n">uniq_ids</span><span class="p">.</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="nb">id</span><span class="o">|</span>
          <span class="nb">id</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="no">Integer</span><span class="p">)</span> <span class="p">?</span> <span class="k">super</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span> <span class="p">:</span> <span class="k">super</span><span class="p">(</span><span class="n">decode_id</span><span class="p">(</span><span class="nb">id</span><span class="p">))</span>
        <span class="k">end</span>
      <span class="k">else</span>
        <span class="n">uniq_ids</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="no">Integer</span><span class="p">)</span> <span class="p">?</span> <span class="k">super</span><span class="p">(</span><span class="n">uniq_ids</span><span class="p">)</span> <span class="p">:</span> <span class="k">super</span><span class="p">(</span><span class="n">decode_id</span><span class="p">(</span><span class="n">uniq_ids</span><span class="p">))</span>
      <span class="k">end</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">find_by_hashid</span><span class="p">(</span><span class="n">hashid</span><span class="p">)</span>
      <span class="n">find_by</span> <span class="ss">id: </span><span class="n">decode_id</span><span class="p">(</span><span class="n">hashid</span><span class="p">)</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">find_by_hashid!</span><span class="p">(</span><span class="n">hashid</span><span class="p">)</span>
      <span class="n">find</span><span class="p">(</span><span class="n">decode_id</span><span class="p">(</span><span class="n">hashid</span><span class="p">))</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">hashid</span>
      <span class="n">encode_id</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
    <span class="k">end</span>

    <span class="kp">private</span>

    <span class="k">def</span> <span class="nf">hashids</span>
      <span class="no">Hashids</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="p">.</span><span class="nf">name</span> <span class="o">+</span> <span class="no">ENV</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="s1">'HASHID_TOKEN'</span><span class="p">,</span> <span class="s1">''</span><span class="p">),</span> <span class="mi">8</span><span class="p">)</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">hashid_encode</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
      <span class="k">return</span> <span class="kp">nil</span> <span class="k">if</span> <span class="nb">id</span><span class="p">.</span><span class="nf">nil?</span>

      <span class="n">hashids</span><span class="p">.</span><span class="nf">encode</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">hashid_decode</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
      <span class="n">hashids</span><span class="p">.</span><span class="nf">decode</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
    <span class="k">rescue</span> <span class="no">Hashids</span><span class="o">::</span><span class="no">InputError</span>
      <span class="nb">id</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Now we just have to add a line to the model that we want to use hashids and it’s all done:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
  <span class="kp">include</span> <span class="no">Hashable</span>
<span class="k">end</span>
</code></pre></div></div>

<h1 id="slugs-human-readability-url-friendly">Slugs: Human readability, URL-friendly</h1>

<p>Slugs represent a human-readable, URL-friendly string derived from a source text, typically used to represent titles, names, or other descriptive elements.</p>

<h2 id="advantages-3">Advantages</h2>

<ol>
  <li><strong>Readability</strong>: Slugs create human-friendly URLs that are easy to read and understand, improving the overall user experience. Descriptive slugs provide users with valuable context about the content they are accessing.</li>
  <li><strong>SEO Optimization</strong>: By including relevant keywords and descriptive elements in the URL, slugs contribute to better search engine visibility and ranking. Search engines often use URLs as part of their ranking algorithms, making SEO-friendly slugs essential for driving organic traffic.</li>
  <li><strong>Brand Consistency</strong>: Consistently formatted slugs contribute to brand recognition and consistency across web pages. Users can easily identify content from the URL, reinforcing brand identity and credibility.</li>
  <li><strong>Compatibility</strong>: Slugs adhere to web standards and are compatible with various web browsers and platforms. They facilitate easy sharing and linking of content across different channels, including social media and email.</li>
</ol>

<h2 id="disadvantages-3">Disadvantages</h2>

<ol>
  <li><strong>Uniqueness</strong>: Ensuring the uniqueness of slugs across a large dataset can be challenging, especially in environments where multiple users can create content simultaneously. Conflicting slugs may lead to URL collisions and accessibility issues.</li>
  <li><strong>Character Limitations</strong>: URLs have character limitations, and slugs must adhere to these constraints to ensure compatibility and avoid truncation issues. Long titles or descriptive elements may require truncation or abbreviation, potentially affecting readability.</li>
  <li><strong>Text Processing Complexity</strong>: Generating slugs involves processing and sanitizing text inputs to remove special characters, whitespace, and other non-URL-friendly characters. This text processing complexity can introduce performance overhead and require careful handling of edge cases.</li>
  <li><strong>Internationalization</strong>: Supporting multilingual content poses challenges for slug generation, as different languages may have unique character sets and conventions. Ensuring consistent and SEO-friendly slugs across languages requires careful consideration and possibly language-specific handling.</li>
</ol>

<h2 id="potentialities-3">Potentialities</h2>

<ol>
  <li><strong>Customization</strong>: Slugs offer flexibility in customization, allowing developers to incorporate branding elements, keywords, and relevant metadata into URLs. Customizable slugs enable tailored SEO strategies and branding initiatives.</li>
  <li><strong>SEO Enhancement</strong>: Optimizing slugs for SEO can significantly impact search engine visibility and ranking, driving organic traffic and user engagement. Strategic use of keywords and descriptive elements in slugs improves content discoverability and relevance.</li>
  <li><strong>User Engagement</strong>: User-friendly slugs enhance user engagement by providing clear and descriptive URLs that users can easily remember and share. Improved usability and readability contribute to positive user experiences and higher retention rates.</li>
  <li><strong>Localization</strong>: Tailoring slugs to different languages and regions facilitates localization efforts, ensuring that content remains accessible and relevant to diverse audiences. Localized slugs contribute to global brand reach and engagement.</li>
</ol>

<h2 id="how-to-do-this-3">How to do this</h2>

<p>You could use a script similar to the one I provided above to override the find method and use the slug as the main parameter or simply change the call in the controller, which would be:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:id</span><span class="p">])</span>
</code></pre></div></div>

<p>To:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">find_by</span><span class="p">(</span><span class="ss">slug: </span><span class="n">params</span><span class="p">[</span><span class="ss">:id</span><span class="p">])</span>
</code></pre></div></div>

<h1 id="conclusion-my-opinion">Conclusion: My Opinion</h1>

<p>So which option should I use? As always the answer is…it depends.</p>

<p>Firstly, sequential ID is rarely a good option unless you want the sequential ID to be part of the functionality, for example you want to show the user that one ID is older than another. At a time when there were few messaging applications and ICQ dominated the world, having an ICQ number with less than 6 digits was a sign of status, that you were “old” on the platform.</p>

<p>As I said in the same session, UUID only makes sense if you want to manipulate decentralized database systems. Keep in mind that a UUID takes up a little more space than an integer, but not that much.</p>

<p>My preferred option is Hash ID, this solution works for 99% of cases. It does not take up extra space in the database, on the other hand it requires a little more encryption/decryption processing. Do I have a user model? Are they invoices? Are they work orders? Throws a Hash ID. Everything’s solved.</p>

<p>Finally, if I need the ID to be part of a URL and need to work SEO or have some meaning, the solution is slug. The slug will result in a significant increase in database space and even more creating an index just for it, but in terms of being something more user-friendly it has no equal. Is my user model for sharing with other people (like on Linkedin)? Am I creating IDs for blog posts? There’s nothing to think about, it’s Slug.</p>

<p>I hope this collection of information about the use of ID has been useful.</p>]]></content><author><name>Arthur Molina</name></author><category term="Blog" /><category term="nonsense" /><summary type="html"><![CDATA[When we want to access a specific model record, the easiest way is to use the unique identifier as an access key. By default in SQL databases we have a sequential integer primary key. But this is not the only possible option. We also have the possibility of using UUID, Slug and Hash ID or Token. Each method offers distinct advantages and considerations, impacting factors like security, readability, and database performance. Drawing insights from real-world implementations and industry best practices, we explore the nuances of each method and their implications for modern applications. Let’s compare these approaches and explore why platforms like YouTube opt for Hash IDs.]]></summary></entry><entry><title type="html">About Gravatar</title><link href="http://artz.to/blog/blog/about-gravatar/" rel="alternate" type="text/html" title="About Gravatar" /><published>2023-04-16T00:00:00+00:00</published><updated>2023-04-16T23:00:00+00:00</updated><id>http://artz.to/blog/blog/about-gravatar</id><content type="html" xml:base="http://artz.to/blog/blog/about-gravatar/"><![CDATA[<p>Gravatar, short for “Globally Recognized Avatar,” is a service that allows users to create a profile picture that follows them across the web. Launched in 2007 by Tom Preston-Werner, Gravatar quickly became a popular choice for online identity management.</p>

<p>At its core, Gravatar assigns a unique avatar to each user’s email address. When users comment on blogs, participate in forums, or interact on various websites, their Gravatar image automatically appears alongside their contributions. This consistent visual representation fosters a sense of identity and community across different online platforms.</p>

<p>One of the key advantages of Gravatar is its convenience. Users can create or update their avatar once, and it will automatically propagate across all Gravatar-enabled sites, eliminating the need to upload a profile picture individually on each platform. This streamlined process saves time and ensures consistency in online branding.</p>

<p>Additionally, Gravatar offers users control over their privacy settings, allowing them to choose between public, private, or rated content for their avatars.</p>

<p>In summary, Gravatar simplifies online identity management, fosters community engagement, and provides users with a convenient and customizable way to represent themselves across the web.</p>

<p>Furthermore, there is no need to create a feature in your application to save your user’s photo, we don’t need to have storage space for these photos, the user doesn’t need to go through the trouble of sending their photo to each application they want to use, and if he doesn’t want to be identified, we can still leave a drawing of a funny robot or alien that changes and maintains the same pattern by email.</p>

<h1 id="how-to-do-it">How to do it</h1>

<p>There are <a href="https://rubygems.org/search?query=gravatar">several Gems</a> that can do the task. But this is a very easy functionality to have and a simple file added to the /app/models/concerns folder solves the problem. There is no need to <a href="/blog/minimizing-gems-libraries-in-ruby-on-rails/">have a gem and have to deal with the hassles of maintaining it</a>. Especially because many of the gravatar gems I’ve seen are already very out of date.</p>

<p>So just add the file below:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># frozen_string_literal: true</span>

<span class="c1"># /app/models/concerns/gravatable.rb</span>

<span class="c1"># From:</span>
<span class="c1"># https://github.com/chrislloyd/gravtastic/blob/master/lib/gravtastic.rb</span>
<span class="c1"># https://chrislloyd.github.io/gravtastic/</span>

<span class="nb">require</span> <span class="s1">'digest/md5'</span>

<span class="c1"># Module to generate Gravatar URL</span>
<span class="k">module</span> <span class="nn">Gravatable</span>
  <span class="kp">extend</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">Concern</span>

  <span class="c1"># The raw MD5 hash of the users' email. Gravatar is particularly tricky as</span>
  <span class="c1"># it downcases all emails. This is really the guts of the module,</span>
  <span class="c1"># everything else is just convenience.</span>
  <span class="k">def</span> <span class="nf">gravatar_id</span>
    <span class="no">Digest</span><span class="o">::</span><span class="no">MD5</span><span class="p">.</span><span class="nf">hexdigest</span><span class="p">(</span><span class="nb">send</span><span class="p">(</span><span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="p">.</span><span class="nf">gravatar_source</span><span class="p">).</span><span class="nf">to_s</span><span class="p">.</span><span class="nf">downcase</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="c1"># Constructs the full Gravatar url.</span>
  <span class="k">def</span> <span class="nf">gravatar_url</span><span class="p">(</span><span class="n">options</span> <span class="o">=</span> <span class="p">{})</span>
    <span class="n">options</span> <span class="o">=</span> <span class="p">{</span>
      <span class="ss">rating: </span><span class="s1">'PG'</span><span class="p">,</span>
      <span class="ss">secure: </span><span class="kp">true</span><span class="p">,</span>
      <span class="ss">filetype: :png</span><span class="p">,</span>
      <span class="ss">default: :robohash</span><span class="p">,</span> <span class="c1"># options: 404, mp (mystery person), identicon, monsterid, wavatar, retro, robohash, blank</span>
      <span class="ss">size: </span><span class="mi">200</span>
    <span class="p">}.</span><span class="nf">merge</span><span class="p">(</span><span class="n">options</span><span class="p">)</span>
    <span class="n">gravatar_hostname</span><span class="p">(</span><span class="n">options</span><span class="p">.</span><span class="nf">delete</span><span class="p">(</span><span class="ss">:secure</span><span class="p">))</span> <span class="o">+</span>
      <span class="n">gravatar_filename</span><span class="p">(</span><span class="n">options</span><span class="p">.</span><span class="nf">delete</span><span class="p">(</span><span class="ss">:filetype</span><span class="p">))</span> <span class="o">+</span>
      <span class="s2">"?</span><span class="si">#{</span><span class="n">url_params_from_hash</span><span class="p">(</span><span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="p">.</span><span class="nf">process_options</span><span class="p">(</span><span class="n">options</span><span class="p">))</span><span class="si">}</span><span class="s2">"</span>
  <span class="k">end</span>

  <span class="c1"># private</span>

  <span class="c1"># Creates a params hash like "?foo=bar" from a hash like {'foo' =&gt; 'bar'}.</span>
  <span class="c1"># The values are sorted so it produces deterministic output (and can</span>
  <span class="c1"># therefore be tested easily).</span>
  <span class="k">def</span> <span class="nf">url_params_from_hash</span><span class="p">(</span><span class="nb">hash</span><span class="p">)</span>
    <span class="nb">hash</span><span class="p">.</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">key</span><span class="p">,</span> <span class="n">val</span><span class="o">|</span>
      <span class="p">[</span><span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="p">.</span><span class="nf">gravatar_abbreviations</span><span class="p">[</span><span class="n">key</span><span class="p">.</span><span class="nf">to_sym</span><span class="p">]</span> <span class="o">||</span> <span class="n">key</span><span class="p">.</span><span class="nf">to_s</span><span class="p">,</span> <span class="n">val</span><span class="p">.</span><span class="nf">to_s</span><span class="p">].</span><span class="nf">join</span><span class="p">(</span><span class="s1">'='</span><span class="p">)</span>
    <span class="k">end</span><span class="p">.</span><span class="nf">sort</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s1">'&amp;'</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="c1"># Returns either Gravatar's secure hostname or not.</span>
  <span class="k">def</span> <span class="nf">gravatar_hostname</span><span class="p">(</span><span class="n">secure</span><span class="p">)</span>
    <span class="s2">"http</span><span class="si">#{</span><span class="n">secure</span> <span class="p">?</span> <span class="s1">'s://secure.'</span> <span class="p">:</span> <span class="s1">'://'</span><span class="si">}</span><span class="s2">gravatar.com/avatar/"</span>
  <span class="k">end</span>

  <span class="c1"># Munges the ID and the filetype into one. Like "abc123.png"</span>
  <span class="k">def</span> <span class="nf">gravatar_filename</span><span class="p">(</span><span class="n">filetype</span><span class="p">)</span>
    <span class="s2">"</span><span class="si">#{</span><span class="n">gravatar_id</span><span class="si">}</span><span class="s2">.</span><span class="si">#{</span><span class="n">filetype</span><span class="si">}</span><span class="s2">"</span>
  <span class="k">end</span>

  <span class="c1"># Methods for the Model Class</span>
  <span class="k">module</span> <span class="nn">ClassMethods</span>
    <span class="nb">attr_accessor</span> <span class="ss">:gravatar_source</span>

    <span class="k">def</span> <span class="nf">gravatar_field</span><span class="p">(</span><span class="n">source</span> <span class="o">=</span> <span class="ss">:emailx</span><span class="p">)</span>
      <span class="vi">@gravatar_source</span> <span class="o">=</span> <span class="n">source</span>
    <span class="k">end</span>

    <span class="c1"># Some options need to be processed before becoming URL params</span>
    <span class="k">def</span> <span class="nf">process_options</span><span class="p">(</span><span class="n">options</span><span class="p">)</span>
      <span class="n">processed_options</span> <span class="o">=</span> <span class="p">{}</span>
      <span class="n">options</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">key</span><span class="p">,</span> <span class="n">val</span><span class="o">|</span>
        <span class="k">case</span> <span class="n">key</span>
        <span class="k">when</span> <span class="ss">:forcedefault</span>
          <span class="n">processed_options</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'y'</span> <span class="k">if</span> <span class="n">val</span>
        <span class="k">else</span>
          <span class="n">processed_options</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">val</span>
        <span class="k">end</span>
      <span class="k">end</span>
      <span class="n">processed_options</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">gravatar_abbreviations</span>
      <span class="p">{</span> <span class="ss">size: </span><span class="s1">'s'</span><span class="p">,</span>
        <span class="ss">default: </span><span class="s1">'d'</span><span class="p">,</span>
        <span class="ss">rating: </span><span class="s1">'r'</span><span class="p">,</span>
        <span class="ss">forcedefault: </span><span class="s1">'f'</span> <span class="p">}</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>In any model that has the email field, just add the Gravatable as below:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">User</span>
  <span class="kp">include</span> <span class="no">Gravatable</span>

  <span class="n">gravatar_field</span> <span class="ss">:email</span>
<span class="k">end</span>

<span class="no">User</span><span class="p">.</span><span class="nf">last</span><span class="p">.</span><span class="nf">gravatar_url</span><span class="p">({</span> <span class="ss">size: </span><span class="mi">40</span> <span class="p">})</span>
</code></pre></div></div>

<p>As you can see in the code, it is not mine originally. I created a version from the <a href="https://github.com/chrislloyd/gravtastic/commits/master/">Gravtastic gem</a> that has not been updated since 2016.</p>]]></content><author><name>Arthur Molina</name></author><category term="Blog" /><category term="nonsense" /><summary type="html"><![CDATA[Gravatar, short for “Globally Recognized Avatar,” is a service that allows users to create a profile picture that follows them across the web. Launched in 2007 by Tom Preston-Werner, Gravatar quickly became a popular choice for online identity management.]]></summary></entry><entry><title type="html">When we need to search or replace a string inside the DB</title><link href="http://artz.to/blog/blog/when-we-need-to-search-or-replace-a-string-inside-the-db/" rel="alternate" type="text/html" title="When we need to search or replace a string inside the DB" /><published>2014-11-05T00:00:00+00:00</published><updated>2014-08-17T23:00:00+00:00</updated><id>http://artz.to/blog/blog/when-we-need-to-search-or-replace-a-string-inside-the-db</id><content type="html" xml:base="http://artz.to/blog/blog/when-we-need-to-search-or-replace-a-string-inside-the-db/"><![CDATA[<p>What about when I need to find a specific string within a database but I don’t know in which table or column?</p>

<p>Today I had a need that I keep having and I always used another way to solve it. I had never used this bank before, but they came to me asking to change the contract number to, say, ‘666/2014’ where before it was ‘333/2012’. How to make? Normally I dump the database and search the text in text mode (using an editor or the grep command). But talking to my friend DBA (thanks Adolfho Lopes!) we decided to do it another way.</p>

<p>Below is the result, but be careful: Use in moderation! These functions are to be used in times of need and in a hurry. Never in production. Don’t do that, otherwise the bank would collapse! :)</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">TYPE</span> <span class="n">table_master_search_table</span> <span class="k">AS</span> <span class="p">(</span>
    <span class="n">tname</span> <span class="nb">varchar</span><span class="p">,</span>
    <span class="n">cname</span> <span class="nb">varchar</span><span class="p">,</span>
    <span class="k">found</span> <span class="nb">varchar</span>
<span class="p">);</span>

<span class="k">CREATE</span> <span class="k">OR</span> <span class="k">REPLACE</span> <span class="k">FUNCTION</span> <span class="n">master_search_table</span><span class="p">(</span><span class="n">p_search_string</span> <span class="nb">varchar</span><span class="p">,</span> <span class="n">p_tablecatalog</span> <span class="nb">varchar</span><span class="p">,</span> <span class="n">p_tableschema</span> <span class="nb">varchar</span><span class="p">,</span> <span class="n">p_tablename</span> <span class="nb">varchar</span><span class="p">)</span>
<span class="k">RETURNS</span> <span class="k">SETOF</span> <span class="n">table_master_search_table</span> <span class="k">AS</span>
<span class="err">$$</span>
<span class="k">DECLARE</span> <span class="n">r</span> <span class="n">record</span><span class="p">;</span>
   <span class="n">r2</span> <span class="n">table_master_search_table</span><span class="o">%</span><span class="n">rowtype</span><span class="p">;</span>
   <span class="n">t1</span> <span class="n">table_master_search_table</span><span class="p">;</span>
   <span class="n">command</span> <span class="nb">varchar</span><span class="p">;</span>
<span class="k">BEGIN</span>
  <span class="k">FOR</span> <span class="n">r</span> <span class="k">IN</span> <span class="k">SELECT</span> <span class="k">table_name</span><span class="p">,</span> <span class="k">column_name</span><span class="p">,</span> <span class="n">data_type</span> <span class="k">from</span> <span class="n">information_schema</span><span class="p">.</span><span class="n">columns</span>
	<span class="k">where</span> <span class="n">table_catalog</span> <span class="o">=</span> <span class="n">p_tablecatalog</span> <span class="k">and</span> <span class="n">table_schema</span> <span class="o">=</span> <span class="n">p_tableschema</span> <span class="k">and</span> <span class="k">table_name</span> <span class="o">=</span> <span class="n">p_tablename</span> <span class="n">LOOP</span>
	<span class="n">command</span> <span class="p">:</span><span class="o">=</span> <span class="s1">'SELECT </span><span class="se">''</span><span class="s1">'</span><span class="o">||</span> <span class="n">r</span><span class="p">.</span><span class="k">table_name</span> <span class="o">||</span> <span class="s1">'</span><span class="se">''</span><span class="s1"> tname, </span><span class="se">''</span><span class="s1">'</span><span class="o">||</span> <span class="n">r</span><span class="p">.</span><span class="k">column_name</span> <span class="o">||</span> <span class="s1">'</span><span class="se">''</span><span class="s1"> cname, '</span> <span class="o">||</span> <span class="n">r</span><span class="p">.</span><span class="k">column_name</span><span class="o">||</span> <span class="s1">' found FROM '</span> <span class="o">||</span> <span class="n">r</span><span class="p">.</span><span class="k">table_name</span> <span class="o">||</span> <span class="s1">' WHERE cast('</span> <span class="o">||</span> <span class="n">r</span><span class="p">.</span><span class="k">column_name</span> <span class="o">||</span> <span class="s1">' as varchar) like </span><span class="se">''</span><span class="s1">%'</span> <span class="o">||</span> <span class="n">p_search_string</span> <span class="o">||</span> <span class="s1">'%</span><span class="se">''</span><span class="s1">'</span><span class="p">;</span>
	<span class="n">raise</span> <span class="n">notice</span> <span class="s1">'%'</span><span class="p">,</span><span class="n">command</span><span class="p">;</span>
    <span class="k">FOR</span> <span class="n">r2</span> <span class="k">IN</span> <span class="k">EXECUTE</span> <span class="n">command</span> <span class="n">LOOP</span>
     <span class="k">return</span> <span class="k">NEXT</span> <span class="n">r2</span><span class="p">;</span>
    <span class="k">END</span> <span class="n">LOOP</span><span class="p">;</span>

  <span class="k">END</span> <span class="n">LOOP</span><span class="p">;</span>
<span class="k">END</span>
<span class="err">$$</span> <span class="k">LANGUAGE</span> <span class="n">plpgsql</span><span class="p">;</span>

<span class="k">CREATE</span> <span class="k">TYPE</span> <span class="n">table_master_search</span> <span class="k">AS</span> <span class="p">(</span><span class="n">tname</span> <span class="nb">varchar</span><span class="p">,</span> <span class="n">quant</span> <span class="nb">integer</span><span class="p">);</span>
<span class="k">CREATE</span> <span class="k">OR</span> <span class="k">REPLACE</span> <span class="k">FUNCTION</span> <span class="n">master_search</span><span class="p">(</span><span class="n">p_search_string</span> <span class="nb">varchar</span><span class="p">,</span> <span class="n">p_tablecatalog</span> <span class="nb">varchar</span><span class="p">,</span> <span class="n">p_tableschema</span> <span class="nb">varchar</span><span class="p">)</span>
<span class="k">RETURNS</span> <span class="k">SETOF</span> <span class="n">table_master_search</span> <span class="k">AS</span>
<span class="err">$$</span>
<span class="k">declare</span>
  <span class="n">num</span> <span class="nb">int</span><span class="p">:</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span>
  <span class="n">col</span> <span class="n">record</span><span class="p">;</span>
  <span class="n">tab</span> <span class="n">record</span><span class="p">;</span>
  <span class="n">comando</span> <span class="nb">varchar</span><span class="p">;</span>
  <span class="n">r</span> <span class="n">table_master_search</span><span class="o">%</span><span class="n">rowtype</span><span class="p">;</span>
<span class="k">BEGIN</span>
  <span class="k">FOR</span> <span class="n">tab</span> <span class="k">in</span> <span class="k">select</span> <span class="k">table_name</span> <span class="k">from</span> <span class="n">information_schema</span><span class="p">.</span><span class="n">tables</span> <span class="k">where</span> <span class="n">table_schema</span><span class="o">=</span><span class="n">p_tableschema</span> <span class="k">and</span> <span class="n">table_type</span> <span class="o">=</span> <span class="s1">'BASE TABLE'</span> <span class="n">LOOP</span>
   <span class="n">comando</span> <span class="o">=</span> <span class="s1">'select </span><span class="se">''</span><span class="s1">'</span><span class="o">||</span> <span class="n">tab</span><span class="p">.</span><span class="k">table_name</span> <span class="o">||</span> <span class="s1">'</span><span class="se">''</span><span class="s1"> tname, count(1) quant from '</span><span class="o">||</span><span class="n">tab</span><span class="p">.</span><span class="k">table_name</span><span class="o">||</span><span class="s1">' where '</span><span class="p">;</span>
   <span class="k">FOR</span> <span class="n">col</span> <span class="k">in</span> <span class="k">select</span> <span class="k">count</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="n">over</span> <span class="p">(</span><span class="k">partition</span> <span class="k">by</span> <span class="k">table_name</span><span class="p">)</span> <span class="n">numcols</span><span class="p">,</span> <span class="n">row_number</span><span class="p">()</span> <span class="n">over</span> <span class="p">(</span><span class="k">partition</span> <span class="k">by</span> <span class="k">table_name</span><span class="p">)</span> <span class="n">seq</span><span class="p">,</span> <span class="o">*</span> <span class="k">from</span> <span class="n">information_schema</span><span class="p">.</span><span class="n">columns</span>
	<span class="k">where</span> <span class="n">table_schema</span> <span class="o">=</span> <span class="n">p_tableschema</span> <span class="k">and</span> <span class="k">table_name</span> <span class="o">=</span> <span class="n">tab</span><span class="p">.</span><span class="k">table_name</span> <span class="n">LOOP</span>
       <span class="n">comando</span> <span class="p">:</span><span class="o">=</span>  <span class="n">comando</span> <span class="o">||</span> <span class="s1">'cast('</span> <span class="o">||</span> <span class="n">col</span><span class="p">.</span><span class="k">column_name</span> <span class="o">||</span><span class="s1">' as varchar) like </span><span class="se">''</span><span class="s1">%'</span> <span class="o">||</span> <span class="n">p_search_string</span> <span class="o">||</span><span class="s1">'%</span><span class="se">''</span><span class="s1">'</span><span class="p">;</span>
       <span class="n">IF</span> <span class="n">col</span><span class="p">.</span><span class="n">seq</span> <span class="o">&lt;&gt;</span> <span class="n">col</span><span class="p">.</span><span class="n">numcols</span> <span class="k">then</span>
         <span class="n">comando</span> <span class="p">:</span><span class="o">=</span>  <span class="n">comando</span> <span class="o">||</span> <span class="s1">' or '</span><span class="p">;</span>
       <span class="k">END</span> <span class="n">IF</span><span class="p">;</span>
   <span class="k">END</span> <span class="n">LOOP</span><span class="p">;</span>
   <span class="n">RAISE</span> <span class="n">notice</span> <span class="s1">'%'</span><span class="p">,</span><span class="n">comando</span><span class="p">;</span>
   <span class="k">EXECUTE</span> <span class="n">comando</span> <span class="k">into</span> <span class="n">r</span><span class="p">;</span>
   <span class="n">IF</span> <span class="n">r</span><span class="p">.</span><span class="n">quant</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="k">then</span>
     <span class="k">RETURN</span> <span class="k">next</span> <span class="n">r</span><span class="p">;</span>
   <span class="k">END</span> <span class="n">IF</span><span class="p">;</span>
  <span class="k">END</span> <span class="n">LOOP</span><span class="p">;</span>
<span class="k">END</span>
<span class="err">$$</span> <span class="k">LANGUAGE</span> <span class="n">plpgsql</span><span class="p">;</span>

<span class="k">CREATE</span> <span class="k">OR</span> <span class="k">REPLACE</span> <span class="k">FUNCTION</span> <span class="n">master_search_full</span><span class="p">(</span><span class="n">p_search_string</span> <span class="nb">varchar</span><span class="p">,</span> <span class="n">p_tablecatalog</span> <span class="nb">varchar</span><span class="p">,</span> <span class="n">p_tableschema</span> <span class="nb">varchar</span><span class="p">)</span>
<span class="k">RETURNS</span> <span class="k">SETOF</span> <span class="n">table_master_search_table</span> <span class="k">AS</span>
<span class="err">$$</span>
<span class="k">declare</span>
  <span class="n">num</span> <span class="nb">int</span><span class="p">:</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span>
  <span class="n">col</span> <span class="n">record</span><span class="p">;</span>
  <span class="n">tab</span> <span class="n">record</span><span class="p">;</span>
  <span class="n">comando</span> <span class="nb">varchar</span><span class="p">;</span>
  <span class="n">r</span> <span class="n">table_master_search_table</span><span class="o">%</span><span class="n">rowtype</span><span class="p">;</span>
<span class="k">BEGIN</span>
  <span class="k">FOR</span> <span class="n">tab</span> <span class="k">IN</span> <span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">master_search</span><span class="p">(</span><span class="n">p_search_string</span><span class="p">,</span> <span class="n">p_tablecatalog</span><span class="p">,</span> <span class="n">p_tableschema</span><span class="p">)</span> <span class="n">LOOP</span>
    <span class="k">FOR</span> <span class="n">r</span> <span class="k">IN</span> <span class="k">SELECT</span> <span class="o">*</span> <span class="k">from</span> <span class="n">master_search_table</span><span class="p">(</span><span class="n">p_search_string</span><span class="p">,</span> <span class="n">p_tablecatalog</span><span class="p">,</span> <span class="n">p_tableschema</span><span class="p">,</span> <span class="n">tab</span><span class="p">.</span><span class="n">tname</span><span class="p">)</span> <span class="n">LOOP</span>
     <span class="k">RETURN</span> <span class="k">next</span> <span class="n">r</span><span class="p">;</span>
    <span class="k">END</span> <span class="n">LOOP</span><span class="p">;</span>
  <span class="k">END</span> <span class="n">LOOP</span><span class="p">;</span>
<span class="k">END</span>
<span class="err">$$</span> <span class="k">LANGUAGE</span> <span class="n">plpgsql</span><span class="p">;</span>

<span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="n">master_search_table</span><span class="p">(</span><span class="s1">'string'</span><span class="p">,</span> <span class="s1">'catalogo'</span><span class="p">,</span> <span class="s1">'schema'</span><span class="p">,</span> <span class="s1">'usuarios'</span><span class="p">);</span>
<span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="n">master_search</span><span class="p">(</span><span class="s1">'string'</span><span class="p">,</span> <span class="s1">'catalogo'</span><span class="p">,</span> <span class="s1">'schema'</span><span class="p">);</span>
<span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="n">master_search_full</span><span class="p">(</span><span class="s1">'string'</span><span class="p">,</span> <span class="s1">'catalogo'</span><span class="p">,</span> <span class="s1">'schema'</span><span class="p">);</span>
</code></pre></div></div>]]></content><author><name>Arthur Molina</name></author><category term="Blog" /><category term="nonsense" /><summary type="html"><![CDATA[What about when I need to find a specific string within a database but I don’t know in which table or column?]]></summary></entry><entry><title type="html">The Social Function of Domain Names for Dummies or “Someone registered the domain name that I wanted and don’t use it… Now what?”</title><link href="http://artz.to/blog/blog/the-social-function-of-domain-names-for-dummies/" rel="alternate" type="text/html" title="The Social Function of Domain Names for Dummies or “Someone registered the domain name that I wanted and don’t use it… Now what?”" /><published>2014-08-17T00:00:00+00:00</published><updated>2014-08-17T23:00:00+00:00</updated><id>http://artz.to/blog/blog/the-social-function-of-domain-names-for-dummies</id><content type="html" xml:base="http://artz.to/blog/blog/the-social-function-of-domain-names-for-dummies/"><![CDATA[<p>The Internet is a space. Virtual, but it’s a space. It has domains, neighbors, addresses… Until recently, we faced a serious problem: the scarcity of IP addresses. That would be the virtual version of an address. IP is the address we give to a specific computer. And as I said, until recently, we had a problem because only the IPv4 format was used to define machine addresses. An address like 200.124.22.11 is in this IPv4 format. It has a limit of 4.3 billion combinations, and we reached that mark. So we started using IPv6, which has a limit of 340 undecillion (or 3.4×10^38) combinations. This means that while before the “lands” of the Internet were the size of, let’s say, the moon, now we have the size of Jupiter. It turns out that if it was already a problem to have an address of 4 sequences of digits up to 254, it would be much more difficult to inform 2001:0db8:85a3:08d3:1319:8a2e:0370:7344 (an example of IPv6). Even before, with only the use of IPv4, we had the service of assigning specific names to IP: DNS or Domain Name Server.</p>

<p><img src="/assets/images/2014/jupiter-earth-moon.jpg" alt="image-right" class="align-right" />
The Internet basically self-regulates. There are some organizations that try to establish some rules, but they are very few and have almost negligible power. Who determines most of the network rules are the companies themselves that provide access to anyone who wants to use the Internet (that is, almost everyone). It’s a libertarian’s paradise. The few organizations managed in part by governments are the ones that manage domain names. You can buy a domain name of any type just by looking for the organization responsible for that nationality. In other words, if I want to buy a .com.br, I need to go to registro.br, which is the Brazilian Internet Steering Committee. By paying only R$ 30.00, you become the happy owner of yourdomain.com.br. So far so good. Or you can use a company that registers in various country organizations, like name.com and godaddy.com.</p>

<p>Just like physical property, domain names have a significant social function on the internet. They are like addresses on a busy street, indicating the location of services, companies, and information. However, the proliferation of registered but unused domains presents a dilemma similar to real estate speculation.
There are many cases where I am creating a service, have the perfect name to call my clientele, but the domain is already registered. I accept that someone else had the same idea as me for a domain name. As they say, ideas are in the air, and I don’t own them. However, it makes me very angry when the domain I thought of with so much care and has already been registered is a page like this:</p>

<p><img src="/assets/images/2014/banzo.png" alt="image-center" class="align-center" /></p>

<p>This happened to me when I tried to create an e-commerce about regional articles with my group in a Startup Weekend (promoted by Google). We had the whole concept of longing for the homeland and we thought of the name “banzo” that refers to that. However, besides the name being registered, it was empty. A virtual void waiting for speculation. Yes, because besides the page only having advertisements, it has a link to buy this domain for the tidy sum of $2,000.00 (a .com domain normally costs around $10.00).</p>

<p>In many cases, domains are acquired solely for future resale, without contributing to the development or utility of the web. This practice, known as cybersquatting, creates a scenario where valuable domain names are registered and kept idle, waiting for profit opportunities. It’s a huge market of thousands of domains available for auctions at very high prices.
The phenomenon of “domain parking,” where empty pages are maintained only with ads or related links, reflects a lack of social responsibility in the use of these resources. Just waiting for someone to pay a fortune or even not, just because the owner wants to have this domain and it’s his and no one else’s. Screw you if you have a disadvantaged idea and could make some money. I don’t care if my domain has no function other than me saying it’s mine.</p>

<p>Just as physical property must fulfill a social function, be it residential, commercial, or public, domain names should also actively contribute to the online community.
In Brazil, the 1988 Constitution established the principle of the social function of property, aiming to prevent speculation and promote the effective use of resources. Contrary to what some say, the social function does not mean that it should be available to society as if your property were not yours. It means that it should be there performing some function.</p>

<p>In Brazilian cities, there are thousands of speculators who buy land at very low prices and leave them there without any function, just waiting for the city to grow around them and the value of the land to skyrocket. This is one of the ways to promote real estate speculation. This practice is extremely harmful to the city as it forces the government to expand its water, electricity, and sewage services (the latter is very expensive) to increasingly distant areas without real necessity because in between are large urban voids with no function at all.</p>

<p>The City Statute (law of 2001) defined some tools to avoid this speculative practice and to compel property owners to fulfill the social function of property. The tools range from progressive property tax (where the tax increases annually as the owner does not give a function to it) to expropriation. Are these tools being used in the city? I don’t see it.</p>

<p>Analogously, it would be beneficial for the organizations responsible for managing domain names to introduce similar policies. A system of “virtual progressive property tax” could encourage the active use of domains, penalizing owners who keep them idle. This could help mitigate the problem of registered but unused domains, ensuring that these resources are allocated more efficiently and for the benefit of the online community as a whole.</p>

<p>How about creating the PVPT (Progressive Virtual Property Tax)?</p>]]></content><author><name>Arthur Molina</name></author><category term="Blog" /><category term="nonsense" /><summary type="html"><![CDATA[The Internet is a space. Virtual, but it’s a space. It has domains, neighbors, addresses… Until recently, we faced a serious problem: the scarcity of IP addresses. That would be the virtual version of an address. IP is the address we give to a specific computer. And as I said, until recently, we had a problem because only the IPv4 format was used to define machine addresses. An address like 200.124.22.11 is in this IPv4 format. It has a limit of 4.3 billion combinations, and we reached that mark. So we started using IPv6, which has a limit of 340 undecillion (or 3.4×10^38) combinations. This means that while before the “lands” of the Internet were the size of, let’s say, the moon, now we have the size of Jupiter. It turns out that if it was already a problem to have an address of 4 sequences of digits up to 254, it would be much more difficult to inform 2001:0db8:85a3:08d3:1319:8a2e:0370:7344 (an example of IPv6). Even before, with only the use of IPv4, we had the service of assigning specific names to IP: DNS or Domain Name Server.]]></summary></entry><entry><title type="html">Why ruby? Por que Ruby?</title><link href="http://artz.to/blog/blog/why-ruby-porque-ruby/" rel="alternate" type="text/html" title="Why ruby? Por que Ruby?" /><published>2013-11-03T00:00:00+00:00</published><updated>2013-11-03T19:00:00+00:00</updated><id>http://artz.to/blog/blog/why-ruby-porque-ruby</id><content type="html" xml:base="http://artz.to/blog/blog/why-ruby-porque-ruby/"><![CDATA[<p>This is the first time I’m taking a blog article and translating it into Portuguese. I did this because I consider the reasons listed to be very relevant and can serve as a basis for debate. Here is just the translated version, if you want to read the original, <a href="https://blog.codinghorror.com/why-ruby/">click here</a>.</p>

<p>É a primeira vez que estou pegando um artigo de blog e traduzindo para português. Fiz isso porque considero as razões elencadas muito pertinentes e podem servir como base de debates. Aqui está apenas a versão traduzida, se quiser ler o original, <a href="https://blog.codinghorror.com/why-ruby/">clique aqui</a>.</p>

<p>Com a palavra, o sr. Atwood do CodingHorror:</p>

<p><strong>Por que Ruby?</strong></p>

<p>Tenho sido desenvolvedor Microsoft por algumas décadas até hoje. Eu dei meus primeiros passos em vários sabores do <a href="http://www.codinghorror.com/blog/2008/04/everything-i-needed-to-know-about-programming-i-learned-from-basic.html">Microsoft Basic para PC</a>, e recebi meu primeiro pagamento programando em Microsoft FoxPro, Microsoft Access, e Microsoft Visual Basic.Eu vi o futuro da programação, meus amigos, e ele é terriveis aplicações de CRUD rodando em maquinas <a href="http://pt.wikipedia.org/wiki/Wintel">Wintel</a>!</p>

<p>É claro, construimos o Stack Overflow em Microsoft.NET. Este é o grande motivo de ele ainda ser tão rápido quanto é. Então uma das perguntas mais frequentemente feitas depois que anunciamos o <a href="http://www.codinghorror.com/blog/2013/02/civilized-discourse-construction-kit.html">Discourse</a> foi:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Por que vocês não desenvolveram o Discourse em .NET também?
</code></pre></div></div>

<p>Deixemos claro uma coisa: Eu amo .NET. Uma das grandes emoções da minha carreira profissional foi ter a oportunidade de colocar um adesivo do Coding Horror nas mãos de <a href="http://en.wikipedia.org/wiki/Anders_Hejlsberg">Anders Hejlsberg</a>. Perdoe meu fanboy interior por um momento, mas caramba, eu ainda me arrepio. Devem ter talvez uns cinquenta projetistas de linguagem de classes de computador pelo mundo. Anders é apenas um deles que criou o Turbo Pascal e o Delphi. É graças à experiência de Anders que o .NET começou com uma linguagem tão bem projetada – literalmente o que o Java deveria ter sido em todo o nível concebível – e tem evoluído extraordinariamente de modo continuo em meios práticos pelos últimos 10 anos, aproveitando os pontos fortes de outras linguagens dinamicamente influentes.</p>

<p><img src="/assets/images/2013/por-que-ruby1.png" alt="image-center" class="align-center" /></p>

<p>Dito tudo isso, é verdade que eu intencionalmente escolhi não usar .NET para o meu próximo projeto. Então você pode esperar encontrar alguém bravo arengando aqui sobre o quão mais feliz estou deixando as amarras opressoras dos meus senhores Microsoft para trás. Livre afinal, livre, pelo menos, graças a Deus todo-poderoso eu sou livre enfim!</p>

<p>Sinto muito. <a href="http://www.codinghorror.com/blog/2007/05/giving-up-on-microsoft.html">Já escrevi este post cinco anos atrás</a>.</p>

<p>Como qualquer programador pragmático, eu escolhi a ferramenta apropriada para o trabalho em mãos. E por mais que eu ame o .NET, ele seria uma escolha extraordinariamente pobre para um projeto 100% software livre como o Discourse. Por que? Três razões em especial:</p>

<p>1 O licenciamento. Meu Deus, o licenciamento. Nem é tanto pelo dinheiro, como é infernal o alucinante nível de complexidade em ter certeza de que todo o seu software é licenciado apropriadamente: determinando em qual nível e edição você está licenciado, quem é licenciado para usar o que, qual servidores são licenciados… Han? Que? Desculpa, passei lá por um minuto, quando fui atacado por <a href="http://www.codinghorror.com/blog/2009/07/oh-you-wanted-awesome-edition.html">doninhas raivosas do licenciamento</a>.</p>

<p>Eu não estou inclinado a fazer grandes declarações sobre o futuro do software, mas se há algo que vai mata o software comercial, deixe-me dizer-lhe, não será um software de código aberto.</p>

<p>2 O atrito. Se você quer construir um software open source verdadeiramente viável, você precisa de pessoas para contribuir com o seu projeto, de modo que se trate de uma coisa vivente, respirando e crescendo. E a não ser que você possa baixar todo o software que precisa para hackear o seu projeto de forma livre por toda a Internet, sem amarras, haverá atrito demais.</p>

<p>Se Stack Overflow me ensinou algo, é que hoje nós vivemos num mundo onde o próximo engenheiro de software brilhante pode vir de qualquer lugar do planeta. Estou falando de lugares que esse <a href="http://www.codinghorror.com/blog/2009/03/the-ugly-american-programmer.html">programador americano e feio</a> nunca escutou falar, onde se fala línguas doidas e sem sentido que eu não entendo. Mas entenda. Afaste-se um pouco enquanto eu explodo seus cérebros, gente: esses programadores brilhantes ainda desenvolvem usando as mesmas palavras-chave que nós! Eu sei, louco né?</p>

<p>Levantar e rodar em um ambiente Microsoft é simplesmente muito difícil para um desenvolvedor, digamos, na Argentina, ou no Nepal, ou na Bulgária. Cadeias de sistemas Operacionais, linguagens e ferramentas livres são os grandes equalizadores, a base para a nova grande geração de programadores por todo o mundo onde estão em vias de nos ajudar a mudar o mundo.</p>

<p>3 O Ecosistema. Quando eu estava no <a href="http://blog.stackoverflow.com/2012/02/stack-exchange-open-source-projects/">Stack Exchange</a>, nós brigamos para fazer o máximo que podiamos para que a nossa infraestrutura fosse open source. Foi algo que deixamos explícito nas nossas orientações de compensação, essa ideia de que nós seriamos (parcialmente) julgados pelo quanto poderíamos fazer em público, e tentar deixar o quanto de artefatos públicos e úteis do nosso trabalho quanto podíamos. Mas por que todo o Stack Exchange não foi criado sob <a href="http://blog.stackoverflow.com/2009/06/stack-overflow-creative-commons-data-dump/">Creative Commons contributions</a> desde o primeiro dia?</p>

<p>Você pode certamente desenvolver software código aberto em .Net. E muitos o fazem. Mas isto nunca parece natural. Isto nunca parece correto. Ninguém aceita uma correção para uma biblioteca de classe do core do .Net, não importa o quanto você tente. parece que você está nadando contra a corrente, em um mundo de pequenas e grandes empresas usando .Net onde elas não estão de fato interessadas em compartilhar o códigos com o mundo – provavelmente porque eles sabem que seriam ruim se o fizessem de qualquer forma. É que não faz parte da cultura do Microsoft .Net fazer coisas open source, especialmente coisas ruins. Se você tem medo de que as coisas que você compartilha sejam ruins, este medo vai te tornar incapaz de realmente e profundamente retribuir. O aspecto mais, digamos delicioso… das comunidades de código aberto é como eles não têm medo de deixar “tudo para fora”, por assim dizer.</p>

<p>Então como resultado, para qualquer tarefa em .Net você deve ter – se tiver sorte – de escolher entre talvez duas bibliotecas talvez decentes. Por outro lado, em qualquer linguagem open source popular, você terá facilmente uma dúzia de alternativas para a mesma função. Sim, talvez seis delas quebrem, seja obsoletas, não funcionem, ou sejam absolutamente loucas. Mas ei, mesmo levando em conta algumas deteriorações naturais de fonte aberta, você ainda está à frente por um fator de três! Você que sai ganhando!</p>

<p>Eu escrevi há <a href="http://www.codinghorror.com/blog/2007/05/giving-up-on-microsoft.html">cinco anos atrás</a>:</p>

<p>“Eu sou pragmático. No momento, eu escolho viver no universo Microsoft. Mas isso não significa que sou ignorante de como os outros vivem. Sempre há mais de um jeito de fazer, e só porque eu escolhi uma em particular não quer dizer que seja a certa – nem mesmo uma maneira particularmente boa. Escolher ser provincial e insular é um caminho infalível para a ignorância. aprenda como os outros vivem. Tente conhecer alguns desenvolvedores que não vivem exatamente no mesmo mundo que você. Procure as ferramentas que estão usando, e porque. Se depois de molhar o seu pé nos dois lados da cerca, você decidir que o outro lado está vivendo melhor e deseja se juntar a eles, então te desejo um amoroso adeus.”</p>

<p>Eu não vivo mais no universo Microsoft. Certo, errado, bom, mau, é assim que ele saiu para o projeto que queríamos construir.</p>

<p><img src="/assets/images/2013/por-que-ruby2.png" alt="image-center" class="align-center" /></p>

<p>Contudo, eu estaria mentindo se não falasse que eu realmente acredito que o tipo de projeto que estamos construindo no Discourse representa sim o futuro do software. Se você olhar de soslaio um pouco, eu acho que você pode ver um futuro não muito distante onde o .Net é um nicho especializado fora do mainstream.</p>

<p>Mas por que Ruby? Bem, a menor resposta e não muito glamorosa é a de que eu reduzi a lista a Python ou Ruby, e meu co-fundador original <a href="http://eviltrout.com/">Robin Ward</a> vem construindo grandes aplicações em Rails desde 2006. Então isso me conquistou.</p>

<p>Eu sempre fui um pouco intrigado com o Ruby, principalmente por causa do <a href="https://sites.google.com/site/steveyegge2/tour-de-babel">absolutamente transbordante louvor que Steve Yegge fez com a linguagem muito tempo atrás em 2006</a>. Eu nunca esqueci disso.</p>

<p>“Na maior parte, Ruby pegou o processamento de string e integração com o Unix do Perl, o que significa que a sintaxe é idêntica, e assim por ali mesmo, antes de qualquer coisa acontecer, você já tem o melhor de Perl. E isso é um grande começo, especialmente se você não pega o resto do Perl.</p>

<p>Mas então Matz pegou o melhor de processamento de listas do Lisp, o melhor de Orientação a Objetos do Smalltalk e outras linguagens, e o melhor de iterators do CLU, e basicamente o melhor de tudo de todos.</p>

<p>E ele de alguma forma fez com que tudo funcionasse junto tão bem que você nem percebe que é tudo isso. Eu aprendi Ruby mais rápido do que qualquer outra linguagem, de umas 30 ou 40 no total: levou uns 3 dias para que eu me sentisse muito mais confortável usando Ruby, do que em Perl, depois de oito anos de codificação em Perl. Ele é tão consistente que você começa a ser capaz de adivinhar como as coisas vão funcionar, e na maioria das vezes você está certo. É lindo. E divertido. E prático.”</p>

<p>Steve é um desses programadores poliglotas que eu <a href="http://www.codinghorror.com/blog/2012/07/but-you-did-not-persuade-me.html">respeito tanto</a> que eu basicamente só tomo seja qual for a sua opinião, desde que não se trate de algo maluco como o controle de armas ou feminismo ou T’Pau, e aceito como fato.</p>

<p>Peço desculpas, Steve. Sinto muito que me levou 7 anos para dar a volta a Ruby. Mas talvez era melhor esperar um tempo de qualquer maneira:</p>

<ul>
  <li>Ruby tem uma performance decente, mas você realmente precisa jogar uma maquina rápida para ele para ter um bom desempenho. Sim, eu sei, <a href="http://www.codinghorror.com/blog/2006/02/the-day-performance-didnt-matter-any-more.html">linguagens interpretadas são o que são</a>, e cache, banco de dados, rede, blá blá blá. Ainda assim, obtivemos as maquinas mais rápidas que você pode comprar para os servidores Discourse, 4.0 Ghz Ivy Bridge Xeons, e a performance é apenas … boa no hardware mais rápido de hoje. Não muito bom. Bom.</li>
</ul>

<p>Sim, eu vou admitir que estou completamente mimado pela performance do JIT compilado do .Net. É o que eu estou acostumado. Eu faço às vezes definhar para os maus velhos tempos de. NET quando poderíamos construir páginas que servem em bem menos de 50 milissegundos sem pensar muito. Linguagens interpretadas não vão ser capazes de alcançar esses níveis de desempenho. Mas eu só posso imaginar como o desempenho bruto Ruby tinha que estar de volta na idade das trevas de 2006, quando CPUs e servidores eram cinco vezes mais lento do que são hoje! Estou muito feliz que eu estou atacando de Ruby agora, com o forte vento de muitos anos sólidos da lei de Moore em nossas costas.</p>

<ul>
  <li>
    <p>Ruby está amadurecendo muito bem na <a href="http://www.ruby-lang.org/en/news/2013/02/24/ruby-2-0-0-p0-is-released/?ref=blog.codinghorror.com">versão 2.0 da linguagem</a>, o que não aconteceu não mais que um mês depois do Discourse foi anunciado. Então, sim, a desvantagem é que Ruby é lento. Mas o lado positivo é que há um monte de Gems de baixa performance na Rubyland. No Discourse nós temos uma melhoria de desempenho de 20% apenas atualizando para o Ruby 2.0, e nós quase dobramos o nosso desempenho, <a href="http://meta.discourse.org/t/tuning-ruby-and-rails-for-discourse/4126">aumentando o limite de garbage collector padrão do Ruby</a>. De uma perspectiva de desempenho futuro, Ruby é nada mas vantagem.</p>
  </li>
  <li>
    <p>Ruby não é mais cool. Sim, você me escutou. Não é mais legal escrever código Ruby. Todas as pessoas legais se mudaram para Scala e Node.js anos atrás. Nosso projeto não é legal, é só um monte do código Ruby velho e chato. Pessoalmente, estou muito feliz que o Ruby agora é maduro o suficiente para que a comunidade não precise mais se preocupar com a pretensão de ser o garoto mais legal do bloco. Isto significa que o resto de nós que gosta apenas de deixar as coisas prontas (Get Shit Done) pode arregaçar as mangas e se concentrar na missão de construir coisas com os nossos pares, em vez de correr freneticamente tentando <a href="http://www.codinghorror.com/blog/2008/01/the-magpie-developer.html">desvendar a próxima coisa brilhante</a>.</p>
  </li>
</ul>

<p>E é claro que a comunida Ruby é e sempre foi, incrível. Nós nunca queremos para grandes gemas de código aberto e grandes contribuidores de código aberto. Agora é um momento fantástico para começar com o Ruby, na minha opinião, seja qual for o seu background.</p>

<p>(No entanto, é também importante ressaltar que o Discourse é, se alguma coisa, ainda mais um projeto JavaScript do que um projeto Ruby on Rails. Você não acredita em mim? Basta ir ao try.discourse.org e ver o código fonte. Um fórum de Discourse não é tanto de um website, mas uma aplicação bem desenvolvida em JavaScript que acontece de ser executado em seu navegador.)</p>

<p>Mesmo que sendo feito de boa vontade e para os melhores interesses do projeto, ainda é um pouco assustador mudar totalmente sua espécie de programação da noite para o dia depois de duas décadas. Eu sempre acreditei que os grandes programadores aprender a amar mais de uma linguagem e ambiente de programação – e espero que o projeto Discourse seja uma oportunidade para que todos possam aprender e crescer, não só comigo. Então vai e faz um <a href="https://github.com/discourse/discourse">fork no GitHub</a> já!</p>]]></content><author><name>Arthur Molina</name></author><category term="Blog" /><category term="code" /><category term="architecture" /><category term="urban planning" /><category term="statistics" /><summary type="html"><![CDATA[This is the first time I’m taking a blog article and translating it into Portuguese. I did this because I consider the reasons listed to be very relevant and can serve as a basis for debate. Here is just the translated version, if you want to read the original, click here.]]></summary></entry><entry><title type="html">Placing the 2010 Brazilian Census in PostgreSQL</title><link href="http://artz.to/blog/blog/placing-the-2010-brazilian-census-in-postgresql/" rel="alternate" type="text/html" title="Placing the 2010 Brazilian Census in PostgreSQL" /><published>2012-12-04T00:00:00+00:00</published><updated>2012-12-04T19:00:00+00:00</updated><id>http://artz.to/blog/blog/placing-the-2010-brazilian-census-in-postgresql</id><content type="html" xml:base="http://artz.to/blog/blog/placing-the-2010-brazilian-census-in-postgresql/"><![CDATA[<p>There are several reasons for wanting to save IBGE 2010 Universe Census data in the Postgres database. One that is more obvious is being able to generate optimized queries of the data you want to work with. Furthermore, SPSS and ArcGIS have the option to open a Postgres query and many other systems also have it. This whole procedure should be very simple, but the number of errors in the files downloaded from IBGE is so great that this process took me an absurd amount of time for no reason.</p>

<p>So let’s go. The first thing to do is download all the data from the IBGE FTP at ftp://ftp.ibge.gov.br/Censos/Censo_Demografico_2010/Resultados_do_Universo/Agregados_por_Setores_Censitarios/. Prepare time and bandwidth as there are several files and quite large (28 files totaling more than 2.2Gb).</p>

<p>But the problem is not with the download. First, IBGE makes the files available in two formats: XLS and CSV. The problem is that their conversion from XLS to CSV did not come out correctly. Some CSV came with the sector code column in scientific notation format, and they must have gotten confused in the Espirito Santo file as they did not include one of the CSV and replaced it with an XLS… In other words, we cannot count on the saved CSV files by IBGE. Another error that I found later when entering the data into the tables was that the files are not standardized!!! For example, the surrounding files for the states of Ceará, Distrito Federal, Minas Gerais, Pernambuco and Rio Grande do Sul have fewer columns than the others. We will return to this below.</p>

<p>Another issue to think about is the dictionary of variables. Each state has 26 tables. And the dictionary for each table is in a PDF file inside each ZIP called INFORMATION BASE BY CENSUS SECTOR 2010 Census – Universo.pdf. An uninteresting format as it is very difficult to obtain the data in an automated way. The solution I found was to convert the PDF to DOC using this website. And then I had the legwork of converting each one into an <a href="/assets/downloads/DadosCenso2010.xls">XLS table</a>. I made the file available for anyone who doesn’t want to do the work I did.</p>

<p>Returning to the data again. Those who use Linux have an advantage as it has several commands that make it possible to perform a batch conversion. Below is a shell script to run and transform the ZIPs you downloaded from the website into a single file for each of the 26 tables. Before running this script it is important that some programs are installed. To do this, just run:</p>

<p>sudo apt-get install unoconv libreoffice unzip</p>

<p>And the script:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>

<span class="nb">mkdir </span>xls
<span class="k">for </span>i <span class="k">in</span> <span class="si">$(</span><span class="nb">ls</span> <span class="k">*</span>.zip<span class="si">)</span>
<span class="k">do
  </span>unzip <span class="nt">-j</span> <span class="nv">$i</span> <span class="nt">-x</span> <span class="k">*</span>.csv <span class="k">*</span>.pdf <span class="nt">-d</span> xls
<span class="k">done
</span><span class="nb">cd </span>xls
unoconv <span class="nt">-i</span> 59,34,UTF-8 <span class="nt">-f</span> csv <span class="k">*</span>
<span class="nb">cd</span> ..

<span class="k">for </span>i <span class="k">in</span> <span class="si">$(</span><span class="nb">ls</span> ./xls/<span class="k">*</span>.csv | <span class="nb">grep</span> <span class="nt">-v</span> Basico<span class="si">)</span>
<span class="k">do
  </span><span class="nv">lc</span><span class="o">=</span><span class="sb">`</span><span class="nb">echo</span> <span class="nv">$i</span> | <span class="nb">tr</span> <span class="s1">'[A-Z]'</span> <span class="s1">'[a-z]'</span><span class="sb">`</span>
  <span class="nb">sed</span> <span class="s1">'1d'</span> <span class="nv">$i</span> | <span class="nb">sed</span> <span class="s1">'s/,/./g'</span> | <span class="nb">sed</span> <span class="s1">'s/X//g'</span> <span class="o">&gt;</span> <span class="nv">$lc</span>.csv
<span class="k">done

for </span>i <span class="k">in</span> <span class="si">$(</span><span class="nb">ls</span> ./xls/<span class="k">*</span>.csv | <span class="nb">grep </span>Basico<span class="si">)</span>
<span class="k">do
  </span><span class="nv">lc</span><span class="o">=</span><span class="sb">`</span><span class="nb">echo</span> <span class="nv">$i</span> | <span class="nb">tr</span> <span class="s1">'[A-Z]'</span> <span class="s1">'[a-z]'</span><span class="sb">`</span>
  <span class="nb">sed</span> <span class="s1">'1d'</span> <span class="nv">$i</span> | <span class="nb">sed</span> <span class="s1">'s/,/./g'</span> <span class="o">&gt;</span> <span class="nv">$lc</span>.csv
<span class="k">done

</span><span class="nb">cat</span> ./xls/basico<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Basico.csv
<span class="nb">cat</span> ./xls/domicilio01<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Domicilio01.csv
<span class="nb">cat</span> ./xls/domicilio02<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Domicilio02.csv
<span class="nb">cat</span> ./xls/domiciliorenda<span class="k">*</span>.csv.csv <span class="o">&gt;</span> DomicilioRenda.csv
<span class="nb">cat</span> ./xls/entorno01<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Entorno_01.csv
<span class="nb">cat</span> ./xls/entorno02<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Entorno_02.csv
<span class="nb">cat</span> ./xls/entorno03<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Entorno_03.csv
<span class="nb">cat</span> ./xls/entorno04<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Entorno_04.csv
<span class="nb">cat</span> ./xls/entorno05<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Entorno_05.csv
<span class="nb">cat</span> ./xls/pessoa01<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Pessoa01.csv
<span class="nb">cat</span> ./xls/pessoa02<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Pessoa02.csv
<span class="nb">cat</span> ./xls/pessoa03<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Pessoa03.csv
<span class="nb">cat</span> ./xls/pessoa04<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Pessoa04.csv
<span class="nb">cat</span> ./xls/pessoa05<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Pessoa05.csv
<span class="nb">cat</span> ./xls/pessoa06<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Pessoa06.csv
<span class="nb">cat</span> ./xls/pessoa07<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Pessoa07.csv
<span class="nb">cat</span> ./xls/pessoa08<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Pessoa08.csv
<span class="nb">cat</span> ./xls/pessoa09<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Pessoa09.csv
<span class="nb">cat</span> ./xls/pessoa10<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Pessoa10.csv
<span class="nb">cat</span> ./xls/pessoa11<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Pessoa11.csv
<span class="nb">cat</span> ./xls/pessoa12<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Pessoa12.csv
<span class="nb">cat</span> ./xls/pessoa13<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Pessoa13.csv
<span class="nb">cat</span> ./xls/pessoarenda<span class="k">*</span>.csv.csv <span class="o">&gt;</span> PessoaRenda.csv
<span class="nb">cat</span> ./xls/responsavel01<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Responsavel01.csv
<span class="nb">cat</span> ./xls/responsavel02<span class="k">*</span>.csv.csv <span class="o">&gt;</span> Responsavel02.csv
<span class="nb">cat</span> ./xls/responsavelrenda<span class="k">*</span>.csv.csv <span class="o">&gt;</span> ResponsavelRenda.csv
</code></pre></div></div>

<p>Just put all the ZIP files in a folder together with this script and run. Line 02 creates a folder. Then, in lines 03 to 06, all XLS in that folder are unzipped. Line 08 converts all XLS files to CSV using delimiter ; (whose code is 59) and text delimiter ” (whose code is 34) and still using UTF-8 encoding.</p>

<p>Finally, the subsequent lines use the sed command twice, the first to remove the first line from all files and then to replace the comma with a period in the data and join everything in a single file.</p>

<p>Ready. The files are ready to play in the bank. Then I created a small PHP script that converts the XLS with the XLS table that I mentioned earlier that will generate SQL to execute in PostgreSQL:</p>

<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">passthru</span><span class="p">(</span><span class="s2">"unoconv -i 59,34,UTF-8 -f csv DadosCenso2010.xls"</span><span class="p">);</span>

<span class="nv">$dic</span> <span class="o">=</span> <span class="nb">file</span><span class="p">(</span><span class="s1">'DadosCenso2010.csv'</span><span class="p">);</span>
<span class="nv">$sql_bd</span> <span class="o">=</span> <span class="k">Array</span><span class="p">();</span>
<span class="nv">$sql_banco</span> <span class="o">=</span> <span class="s1">''</span><span class="p">;</span>
<span class="nv">$sql_populate</span> <span class="o">=</span> <span class="s1">''</span><span class="p">;</span>
<span class="nv">$tit</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="nv">$path</span> <span class="o">=</span> <span class="s1">'/home/artz/censo/dados/'</span><span class="p">;</span>

<span class="nv">$sql_dic</span> <span class="o">=</span> <span class="o">&lt;&lt;</span> <span class="no">CREATE</span> <span class="no">TABLE</span> <span class="nf">dicionario</span>
<span class="p">(</span>
  <span class="n">cod</span> <span class="n">serial</span><span class="p">,</span>
  <span class="n">banco</span> <span class="n">character</span> <span class="n">varying</span><span class="p">,</span>
  <span class="n">variavel</span> <span class="n">character</span> <span class="n">varying</span><span class="p">,</span>
  <span class="n">descricao</span> <span class="n">character</span> <span class="n">varying</span>
<span class="p">);</span>

<span class="no">SQL_DIC</span><span class="p">;</span>
<span class="nv">$i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">foreach</span><span class="p">(</span><span class="nv">$dic</span> <span class="k">as</span> <span class="nv">$v</span><span class="p">)</span> <span class="p">{</span>
  <span class="nv">$v</span> <span class="o">=</span> <span class="nb">explode</span><span class="p">(</span><span class="s1">';'</span><span class="p">,</span> <span class="nv">$v</span><span class="p">);</span>

  <span class="k">if</span><span class="p">(</span><span class="nv">$tit</span> <span class="o">==</span> <span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span><span class="p">(</span><span class="nv">$v</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="s1">''</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">if</span><span class="p">(</span><span class="nb">count</span><span class="p">(</span><span class="nv">$sql_bd</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="nv">$sql_banco</span> <span class="mf">.</span><span class="o">=</span> <span class="s2">"CREATE TABLE "</span> <span class="mf">.</span> <span class="nv">$titulo</span> <span class="mf">.</span> <span class="s2">"_2010_SC (</span><span class="se">\n</span><span class="s2">"</span>
          <span class="mf">.</span> <span class="nb">implode</span><span class="p">(</span><span class="s2">",</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span> <span class="nv">$sql_bd</span><span class="p">)</span>
          <span class="mf">.</span> <span class="s2">");</span><span class="se">\n\n</span><span class="s2">"</span><span class="p">;</span>
        <span class="nv">$sql_populate</span> <span class="mf">.</span><span class="o">=</span> <span class="s2">"COPY "</span> <span class="mf">.</span> <span class="nv">$titulo</span> <span class="mf">.</span> <span class="s2">"_2010_SC FROM '"</span> <span class="mf">.</span> <span class="nv">$path</span> <span class="mf">.</span> <span class="nv">$titulo</span> <span class="mf">.</span> <span class="s2">".csv' DELIMITERS ';' CSV;</span><span class="se">\n</span><span class="s2">"</span><span class="p">;</span>
        <span class="nv">$sql_bd</span> <span class="o">=</span> <span class="k">Array</span><span class="p">();</span>
        <span class="k">echo</span> <span class="s2">"Creating </span><span class="nv">$titulo</span><span class="s2">...</span><span class="se">\n</span><span class="s2">"</span><span class="p">;</span>
      <span class="p">}</span>
      <span class="nv">$titulo</span> <span class="o">=</span> <span class="nb">str_replace</span><span class="p">(</span><span class="s1">' '</span><span class="p">,</span> <span class="s1">'_'</span><span class="p">,</span> <span class="nf">tiracento</span><span class="p">(</span><span class="nv">$v</span><span class="p">[</span><span class="mi">0</span><span class="p">]));</span>
      <span class="nv">$desc</span> <span class="o">=</span> <span class="nv">$v</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
      <span class="nv">$tit</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="k">if</span><span class="p">(</span><span class="nv">$v</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s1">''</span><span class="p">)</span> <span class="p">{</span>
      <span class="nv">$tit</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="nv">$v</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="nb">str_replace</span><span class="p">(</span><span class="s1">' '</span><span class="p">,</span> <span class="s1">'_'</span><span class="p">,</span> <span class="nf">tiracento</span><span class="p">(</span><span class="nv">$v</span><span class="p">[</span><span class="mi">0</span><span class="p">]));</span>
      <span class="nv">$sql_dic</span> <span class="mf">.</span><span class="o">=</span> <span class="s2">"INSERT INTO dicionario (banco, variavel, descricao) VALUES ('"</span> <span class="mf">.</span> <span class="nv">$titulo</span> <span class="mf">.</span> <span class="s2">"_2010_SC', '"</span> <span class="mf">.</span><span class="nv">$v</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="mf">.</span> <span class="s2">"', '"</span> <span class="mf">.</span> <span class="nv">$v</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="mf">.</span> <span class="s2">"');</span><span class="se">\n</span><span class="s2">"</span><span class="p">;</span>
      <span class="nv">$sql_bd</span><span class="p">[]</span> <span class="o">=</span> <span class="s2">" "</span> <span class="mf">.</span> <span class="nv">$v</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="mf">.</span> <span class="p">(</span><span class="nv">$v</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="o">==</span><span class="mi">1</span> <span class="o">?</span> <span class="s2">" character varying"</span> <span class="o">:</span> <span class="s2">" numeric"</span><span class="p">);</span>
      <span class="nv">$tit</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="k">if</span><span class="p">(</span><span class="nb">count</span><span class="p">(</span><span class="nv">$sql_bd</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
  <span class="nv">$sql_banco</span> <span class="mf">.</span><span class="o">=</span> <span class="s2">"CREATE TABLE "</span> <span class="mf">.</span> <span class="nv">$titulo</span> <span class="mf">.</span> <span class="s2">"_2010_SC (</span><span class="se">\n</span><span class="s2">"</span>
  <span class="mf">.</span> <span class="nb">implode</span><span class="p">(</span><span class="s2">",</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span> <span class="nv">$sql_bd</span><span class="p">)</span>
  <span class="mf">.</span> <span class="s2">");</span><span class="se">\n\n</span><span class="s2">"</span><span class="p">;</span>
  <span class="nv">$sql_populate</span> <span class="mf">.</span><span class="o">=</span> <span class="s2">"COPY "</span> <span class="mf">.</span> <span class="nv">$titulo</span> <span class="mf">.</span> <span class="s2">"_2010_SC FROM '"</span> <span class="mf">.</span> <span class="nv">$path</span> <span class="mf">.</span> <span class="nv">$titulo</span> <span class="mf">.</span> <span class="s2">".csv' DELIMITERS ';' CSV;</span><span class="se">\n</span><span class="s2">"</span><span class="p">;</span>
  <span class="nv">$sql_bd</span> <span class="o">=</span> <span class="k">Array</span><span class="p">();</span>
  <span class="k">echo</span> <span class="s2">"Creating </span><span class="nv">$titulo</span><span class="s2">...</span><span class="se">\n</span><span class="s2">"</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">function</span> <span class="n">tiracento</span><span class="p">(</span><span class="nv">$texto</span><span class="p">){</span>
  <span class="nv">$trocarIsso</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span><span class="s1">'à'</span><span class="p">,</span><span class="s1">'á'</span><span class="p">,</span><span class="s1">'â'</span><span class="p">,</span><span class="s1">'ã'</span><span class="p">,</span><span class="s1">'ä'</span><span class="p">,</span><span class="s1">'å'</span><span class="p">,</span><span class="s1">'ç'</span><span class="p">,</span><span class="s1">'è'</span><span class="p">,</span><span class="s1">'é'</span><span class="p">,</span><span class="s1">'ê'</span><span class="p">,</span><span class="s1">'ë'</span><span class="p">,</span><span class="s1">'ì'</span><span class="p">,</span><span class="s1">'í'</span><span class="p">,</span><span class="s1">'î'</span><span class="p">,</span><span class="s1">'ï'</span><span class="p">,</span><span class="s1">'ñ'</span><span class="p">,</span><span class="s1">'ò'</span><span class="p">,</span><span class="s1">'ó'</span><span class="p">,</span><span class="s1">'ô'</span><span class="p">,</span><span class="s1">'õ'</span><span class="p">,</span><span class="s1">'ö'</span><span class="p">,</span><span class="s1">'ù'</span><span class="p">,</span><span class="s1">'ü'</span><span class="p">,</span><span class="s1">'ú'</span><span class="p">,</span><span class="s1">'ÿ'</span><span class="p">,</span><span class="s1">'À'</span><span class="p">,</span><span class="s1">'Á'</span><span class="p">,</span><span class="s1">'Â'</span><span class="p">,</span><span class="s1">'Ã'</span><span class="p">,</span><span class="s1">'Ä'</span><span class="p">,</span><span class="s1">'Å'</span><span class="p">,</span><span class="s1">'Ç'</span><span class="p">,</span><span class="s1">'È'</span><span class="p">,</span><span class="s1">'É'</span><span class="p">,</span><span class="s1">'Ê'</span><span class="p">,</span><span class="s1">'Ë'</span><span class="p">,</span><span class="s1">'Ì'</span><span class="p">,</span><span class="s1">'Í'</span><span class="p">,</span><span class="s1">'Î'</span><span class="p">,</span><span class="s1">'Ï'</span><span class="p">,</span><span class="s1">'Ñ'</span><span class="p">,</span><span class="s1">'Ò'</span><span class="p">,</span><span class="s1">'Ó'</span><span class="p">,</span><span class="s1">'Ô'</span><span class="p">,</span><span class="s1">'Õ'</span><span class="p">,</span><span class="s1">'Ö'</span><span class="p">,</span><span class="s1">'O'</span><span class="p">,</span><span class="s1">'Ù'</span><span class="p">,</span><span class="s1">'Ü'</span><span class="p">,</span><span class="s1">'Ú'</span><span class="p">,</span><span class="s1">'Ÿ'</span><span class="p">,);</span>
  <span class="nv">$porIsso</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span><span class="s1">'a'</span><span class="p">,</span><span class="s1">'a'</span><span class="p">,</span><span class="s1">'a'</span><span class="p">,</span><span class="s1">'a'</span><span class="p">,</span><span class="s1">'a'</span><span class="p">,</span><span class="s1">'a'</span><span class="p">,</span><span class="s1">'c'</span><span class="p">,</span><span class="s1">'e'</span><span class="p">,</span><span class="s1">'e'</span><span class="p">,</span><span class="s1">'e'</span><span class="p">,</span><span class="s1">'e'</span><span class="p">,</span><span class="s1">'i'</span><span class="p">,</span><span class="s1">'i'</span><span class="p">,</span><span class="s1">'i'</span><span class="p">,</span><span class="s1">'i'</span><span class="p">,</span><span class="s1">'n'</span><span class="p">,</span><span class="s1">'o'</span><span class="p">,</span><span class="s1">'o'</span><span class="p">,</span><span class="s1">'o'</span><span class="p">,</span><span class="s1">'o'</span><span class="p">,</span><span class="s1">'o'</span><span class="p">,</span><span class="s1">'u'</span><span class="p">,</span><span class="s1">'u'</span><span class="p">,</span><span class="s1">'u'</span><span class="p">,</span><span class="s1">'y'</span><span class="p">,</span><span class="s1">'A'</span><span class="p">,</span><span class="s1">'A'</span><span class="p">,</span><span class="s1">'A'</span><span class="p">,</span><span class="s1">'A'</span><span class="p">,</span><span class="s1">'A'</span><span class="p">,</span><span class="s1">'A'</span><span class="p">,</span><span class="s1">'C'</span><span class="p">,</span><span class="s1">'E'</span><span class="p">,</span><span class="s1">'E'</span><span class="p">,</span><span class="s1">'E'</span><span class="p">,</span><span class="s1">'E'</span><span class="p">,</span><span class="s1">'I'</span><span class="p">,</span><span class="s1">'I'</span><span class="p">,</span><span class="s1">'I'</span><span class="p">,</span><span class="s1">'I'</span><span class="p">,</span><span class="s1">'N'</span><span class="p">,</span><span class="s1">'O'</span><span class="p">,</span><span class="s1">'O'</span><span class="p">,</span><span class="s1">'O'</span><span class="p">,</span><span class="s1">'O'</span><span class="p">,</span><span class="s1">'O'</span><span class="p">,</span><span class="s1">'0'</span><span class="p">,</span><span class="s1">'U'</span><span class="p">,</span><span class="s1">'U'</span><span class="p">,</span><span class="s1">'U'</span><span class="p">,</span><span class="s1">'Y'</span><span class="p">,);</span>
  <span class="nv">$titletext</span> <span class="o">=</span> <span class="nb">str_replace</span><span class="p">(</span><span class="nv">$trocarIsso</span><span class="p">,</span> <span class="nv">$porIsso</span><span class="p">,</span> <span class="nv">$texto</span><span class="p">);</span>
  <span class="k">return</span> <span class="nv">$titletext</span><span class="p">;</span>
<span class="p">}</span>

<span class="nv">$f</span> <span class="o">=</span> <span class="nb">fopen</span><span class="p">(</span><span class="s1">'out.sql'</span><span class="p">,</span> <span class="s1">'w'</span><span class="p">);</span>
<span class="nb">fwrite</span><span class="p">(</span><span class="nv">$f</span><span class="p">,</span> <span class="nv">$sql_dic</span> <span class="mf">.</span> <span class="s2">"</span><span class="se">\n\n</span><span class="s2">"</span> <span class="mf">.</span> <span class="nv">$sql_banco</span> <span class="mf">.</span> <span class="s2">"</span><span class="se">\n\n</span><span class="s2">"</span> <span class="mf">.</span> <span class="nv">$sql_populate</span><span class="p">);</span>
<span class="cp">?&gt;</span>
</code></pre></div></div>

<p>Be assured that the procedure is not that simple. But it’s at least a little help.</p>]]></content><author><name>Arthur Molina</name></author><category term="Blog" /><category term="code" /><category term="architecture" /><category term="urban planning" /><category term="statistics" /><summary type="html"><![CDATA[There are several reasons for wanting to save IBGE 2010 Universe Census data in the Postgres database. One that is more obvious is being able to generate optimized queries of the data you want to work with. Furthermore, SPSS and ArcGIS have the option to open a Postgres query and many other systems also have it. This whole procedure should be very simple, but the number of errors in the files downloaded from IBGE is so great that this process took me an absurd amount of time for no reason.]]></summary></entry><entry><title type="html">Joystick mouse using Teensy</title><link href="http://artz.to/blog/blog/joystick-mouse-using-teensy/" rel="alternate" type="text/html" title="Joystick mouse using Teensy" /><published>2012-11-17T00:00:00+00:00</published><updated>2012-11-17T19:00:00+00:00</updated><id>http://artz.to/blog/blog/joystick-mouse-using-teensy</id><content type="html" xml:base="http://artz.to/blog/blog/joystick-mouse-using-teensy/"><![CDATA[<p>I’ve been playing around with physical computing and accepted my first project for a device using a microprocessor. While the component I need to finish this project isn’t enough (I ordered it through MercadoLivre) I decided to play with other components. As I’m making a device that will simulate some keyboard commands, I decided to use the Teensy 2.0 which allows for very easy HID programming. It’s amazing how easy it is to do this with Teensy.</p>

<p>I took advantage of a joystick module I already had and adapted the mouse control.</p>

<p>So here’s the recipe:</p>

<h1 id="ingredients">Ingredients</h1>

<ul>
  <li>Teensy 2.0</li>
  <li>Joystick KY-023 Module</li>
  <li>some wire</li>
  <li>USB adapter</li>
</ul>

<h1 id="the-code">The Code</h1>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">joyPin1</span> <span class="o">=</span> <span class="n">PIN_F0</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">joyPin2</span> <span class="o">=</span> <span class="n">PIN_F1</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">v1</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">v2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">v1Padrao</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">v2Padrao</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">passo</span> <span class="o">=</span> <span class="mi">20</span><span class="p">;</span>

<span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">Serial</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="mi">9600</span><span class="p">);</span>
  <span class="n">v1Padrao</span> <span class="o">=</span> <span class="n">analogRead</span><span class="p">(</span><span class="n">joyPin1</span><span class="p">);</span>
  <span class="n">delay</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span>
  <span class="n">v2Padrao</span> <span class="o">=</span> <span class="n">analogRead</span><span class="p">(</span><span class="n">joyPin2</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="n">change</span><span class="p">(</span><span class="kt">int</span> <span class="n">v</span><span class="p">,</span> <span class="kt">int</span> <span class="n">unchange</span><span class="p">,</span> <span class="kt">int</span> <span class="n">max_state</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span><span class="p">(</span><span class="n">v</span> <span class="o">&gt;</span> <span class="n">unchange</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="kt">int</span><span class="p">(</span><span class="n">passo</span> <span class="o">*</span> <span class="p">(</span> <span class="n">v</span> <span class="err">–</span> <span class="n">unchange</span> <span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="n">max_state</span> <span class="err">–</span> <span class="n">unchange</span><span class="p">));</span>
  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">v</span> <span class="o">&lt;</span> <span class="n">unchange</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="kt">int</span><span class="p">(</span> <span class="p">(</span><span class="n">v</span> <span class="o">/</span> <span class="n">unchange</span> <span class="o">*</span> <span class="n">passo</span><span class="p">)</span> <span class="err">–</span> <span class="n">passo</span> <span class="p">);</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="n">loop</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">v1</span> <span class="o">=</span> <span class="n">analogRead</span><span class="p">(</span><span class="n">joyPin1</span><span class="p">);</span>
  <span class="n">delay</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span>
  <span class="n">v2</span> <span class="o">=</span> <span class="n">analogRead</span><span class="p">(</span><span class="n">joyPin2</span><span class="p">);</span>
  <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="err">“—</span><span class="o">-</span><span class="err">“</span><span class="p">);</span>
  <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="n">v1</span><span class="p">);</span>
  <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="err">”</span> <span class="err">–</span> <span class="err">“</span><span class="p">);</span>
  <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="n">change</span><span class="p">(</span><span class="n">v1</span><span class="p">,</span> <span class="n">v1Padrao</span><span class="p">,</span> <span class="mi">1023</span><span class="p">));</span>
  <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="n">v2</span><span class="p">);</span>
  <span class="n">Serial</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="err">”</span> <span class="err">–</span> <span class="err">“</span><span class="p">);</span>
  <span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="n">change</span><span class="p">(</span><span class="n">v2</span><span class="p">,</span> <span class="n">v2Padrao</span><span class="p">,</span> <span class="mi">997</span><span class="p">)</span> <span class="p">);</span>
  <span class="n">Mouse</span><span class="p">.</span><span class="n">move</span><span class="p">(</span> <span class="n">change</span><span class="p">(</span><span class="n">v1</span><span class="p">,</span> <span class="mi">506</span><span class="p">,</span> <span class="mi">1023</span><span class="p">)</span> <span class="p">,</span> <span class="n">change</span><span class="p">(</span><span class="n">v2</span><span class="p">,</span> <span class="mi">512</span><span class="p">,</span> <span class="mi">997</span><span class="p">)</span> <span class="p">);</span>
  <span class="n">delay</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h1 id="the-schematics">The Schematics</h1>

<figure class=""><img src="/blog/assets/images/2012/joystick-mouse-using-teensy.png" alt="the Schematics" /></figure>

<h1 id="some-explanations">Some explanations</h1>

<p>The Joystick is nothing more than two potentiometers and a button (in case you press the joystick down). The VRx and VRy terminals are connected to the Teensy’s analog ports while the SW terminal, which is the button, is connected to a digital port (although it is not being used). The analog ports range from Zero to 1023. However, after some tests I discovered that on one axis the values range from zero to 1023, stopping at 506 when it is centered, and the other axis goes from zero to 997, stopping at 512. Certainly other pieces will have different limits. The change function adapts the value returned by the potentiometers to the most interesting number for the mouse function, which should range from -127 to 127.
Problems</p>

<p>I tried to make the button that comes with it work as a mouse click, however I don’t know why sometimes the value is zero and other times it is 1 without even moving the joystick. Another issue is that it doesn’t always move in the right direction. I didn’t understand the reason. If anyone knows, please explain to me.</p>

<h1 id="here-is-the-result">Here is the result</h1>

<figure class=""><img src="/blog/assets/images/2012/joystick-mouse-using-teensy2.png" alt="the results" /></figure>]]></content><author><name>Arthur Molina</name></author><category term="Blog" /><category term="code" /><category term="electronics" /><category term="arduino" /><summary type="html"><![CDATA[I’ve been playing around with physical computing and accepted my first project for a device using a microprocessor. While the component I need to finish this project isn’t enough (I ordered it through MercadoLivre) I decided to play with other components. As I’m making a device that will simulate some keyboard commands, I decided to use the Teensy 2.0 which allows for very easy HID programming. It’s amazing how easy it is to do this with Teensy.]]></summary></entry><entry><title type="html">OpenLayers and Google Maps vs. Internet Explorer</title><link href="http://artz.to/blog/blog/open-layers-and-google-maps-vs-internet-explorer/" rel="alternate" type="text/html" title="OpenLayers and Google Maps vs. Internet Explorer" /><published>2012-08-19T00:00:00+00:00</published><updated>2012-03-19T19:00:00+00:00</updated><id>http://artz.to/blog/blog/open-layers-and-google-maps-vs-internet-explorer</id><content type="html" xml:base="http://artz.to/blog/blog/open-layers-and-google-maps-vs-internet-explorer/"><![CDATA[<p>I was developing my website with maps using <a href="http://www.openlayers.org/">OpenLayers</a> as a framework and, obviously, testing and improving everything in Firefox. The reason is very simple: Firebug. This tool greatly improves the life of a front-end developer! I think that later I will do an evaluation of browser debuggers. But that’s for another post.</p>

<p>Even though Firefox already has a debugger built in and they all work with the same message warning command (console.log and others), I can’t seem to let go of Firebug. This also has its downside because when I need to test my application in another browser I get lost. Even Internet Explorer has already incorporated its debugger tool, and there is no way to include a Firebug Lite like Chrome (or is there?). So I have to use it anyway. Despite having very interesting functions, such as the possibility of testing the page on previous versions, overall I find it very weak…</p>

<p>But back to the subject: OpenLayers. I’m using several Basemaps (which are the maps that are underneath the layers you’re going to work on. It’s only allowed to show one basemap at a time, after all, they take up all the space and it wouldn’t even be possible to see more than one at a time, unless unless we used transparency (I will test this possibility later)…</p>

<p>But here’s the problem. I’m using Google Maps, Google Satellite, Google Hybrid, Google Terrain and OpenStreetMap as a base, the latter being the default layer. In Firefox, they worked fine. When I tested the application in other browsers, they opened in OpenStreetMap (by default) and it also seemed to work.</p>

<p>One fine day (later it wasn’t so beautiful) I’m going to do a demonstration of the application and the machine I was going to show didn’t have Firefox, just IE 9 (at least it was 9!) and the other base layers (those from Google ) simply did not appear. After the embarrassment I experienced in that demonstration, I went looking for the reason for this and couldn’t find anything on the subject. I updated the OpenLayers version, at the time, to 2.11 and nothing. I looked inside the OpenLayers.js, inside the stylesheets and nothing… I gave up after searching so much for the reason.</p>

<p>When I was at the end of the development of this project, about to go into production, I committed myself once again to solving this bug. Version 2.12 of OpenLayers had just come out of the oven and, thinking that this bug would be resolved, I updated once again. And once again, nothing.</p>

<p>Another good time of research when I found the solution on a <a href="https://www.drupal.org/node/1364304">Drupal forum</a>: to fix the problem I just needed to replace the blank.gif file that was defective and prevented Google images from being displayed.</p>

<p>Just replacing this file in the OpenLayers images made Google Maps appear in IE like magic.</p>

<p>But there are some questions:</p>

<ul>
  <li>
    <p>What was in the previous blank.gif that prevented it from being viewed?</p>
  </li>
  <li>
    <p>Why did this problem only occur in IE?</p>
  </li>
  <li>
    <p>How could I waste so much time on something so foolish?</p>
  </li>
</ul>]]></content><author><name>Arthur Molina</name></author><category term="Blog" /><category term="code" /><category term="openlayers" /><category term="php" /><summary type="html"><![CDATA[I was developing my website with maps using OpenLayers as a framework and, obviously, testing and improving everything in Firefox. The reason is very simple: Firebug. This tool greatly improves the life of a front-end developer! I think that later I will do an evaluation of browser debuggers. But that’s for another post.]]></summary></entry></feed>