<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Tomás Müller]]></title><description><![CDATA[Tomás Müller]]></description><link>https://logs.tomasmuller.dev</link><image><url>https://logs.tomasmuller.dev/img/substack.png</url><title>Tomás Müller</title><link>https://logs.tomasmuller.dev</link></image><generator>Substack</generator><lastBuildDate>Tue, 21 Apr 2026 10:16:28 GMT</lastBuildDate><atom:link href="https://logs.tomasmuller.dev/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Tomás Müller]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[tomasmuller@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[tomasmuller@substack.com]]></itunes:email><itunes:name><![CDATA[Tomás Müller]]></itunes:name></itunes:owner><itunes:author><![CDATA[Tomás Müller]]></itunes:author><googleplay:owner><![CDATA[tomasmuller@substack.com]]></googleplay:owner><googleplay:email><![CDATA[tomasmuller@substack.com]]></googleplay:email><googleplay:author><![CDATA[Tomás Müller]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[There is no single `bundle update` task]]></title><description><![CDATA[A simple way to fix vulnerable gems, update dependencies in stages, and avoid turning bundle update into a gamble.]]></description><link>https://logs.tomasmuller.dev/p/bundle-update</link><guid isPermaLink="false">https://logs.tomasmuller.dev/p/bundle-update</guid><dc:creator><![CDATA[Tomás Müller]]></dc:creator><pubDate>Sun, 29 Mar 2026 20:13:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Ku9s!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a8cb482-c220-49e4-a198-cb4c78f17cc1_1024x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ku9s!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a8cb482-c220-49e4-a198-cb4c78f17cc1_1024x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ku9s!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a8cb482-c220-49e4-a198-cb4c78f17cc1_1024x1536.png 424w, https://substackcdn.com/image/fetch/$s_!Ku9s!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a8cb482-c220-49e4-a198-cb4c78f17cc1_1024x1536.png 848w, https://substackcdn.com/image/fetch/$s_!Ku9s!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a8cb482-c220-49e4-a198-cb4c78f17cc1_1024x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!Ku9s!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a8cb482-c220-49e4-a198-cb4c78f17cc1_1024x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ku9s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a8cb482-c220-49e4-a198-cb4c78f17cc1_1024x1536.png" width="1024" height="1536" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8a8cb482-c220-49e4-a198-cb4c78f17cc1_1024x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1536,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3609270,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://logs.tomasmuller.dev/i/192534940?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a8cb482-c220-49e4-a198-cb4c78f17cc1_1024x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Ku9s!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a8cb482-c220-49e4-a198-cb4c78f17cc1_1024x1536.png 424w, https://substackcdn.com/image/fetch/$s_!Ku9s!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a8cb482-c220-49e4-a198-cb4c78f17cc1_1024x1536.png 848w, https://substackcdn.com/image/fetch/$s_!Ku9s!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a8cb482-c220-49e4-a198-cb4c78f17cc1_1024x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!Ku9s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a8cb482-c220-49e4-a198-cb4c78f17cc1_1024x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><code>bundle update</code> gets scary when a single command carries too much weight.</p><p>A CVE fix, a <code>rubocop</code> bump, a <code>sidekiq</code> minor release, and a <code>rails</code> upgrade do not belong in the same conversation. But they often land in the same lockfile diff anyway. I do not trust that kind of PR, and I do not expect anyone else to trust it either.</p><p>I do not think the problem is Bundler. The problem is treating dependency maintenance like one big chore instead of a few smaller jobs.</p><p>Here is the split I prefer.</p><p>Nothing clever. Just separate work with separate risk.</p><h2>Pull security fixes out of the pile</h2><p>If a gem is vulnerable, handle that first.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;85896d81-b4b9-4360-b802-576d3c49c08c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">gem install bundler-audit
bundle audit --update</code></pre></div><p>Then update only the gem you actually need to move:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;805d0618-4afe-4360-bd3e-428382817aa1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">bundle update --conservative --patch &lt;gem-name&gt;</code></pre></div><p>I like doing this in isolation because the goal is obvious. You are not &#8220;cleaning up dependencies.&#8221; You are removing a known problem.</p><p>That distinction matters in review and when something goes wrong. A security fix should not sit around waiting for someone to debug a broken formatter or a noisy minor update in a completely different part of the tree.</p><h2>Move dev and test tooling on its own</h2><p>After that, I usually take the lower-risk bundle next:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;be48eb6f-5457-498c-97e5-34ac10082410&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">bundle update --conservative --group=development
bundle update --conservative --group=test

# Or:
bundle update --conservative --group test development</code></pre></div><p>If your <code>Gemfile</code> groups things differently, adjust the commands to match. The point is to keep tooling updates away from runtime updates.</p><p>This is often the easiest place to make progress. If something breaks here, it usually breaks in CI, in local scripts, or in editor tooling. That is still annoying, but it is a very different kind of risk from changing gems that sit in the request path or talk to production services.</p><p>I also like that it gives you a smaller PR with a clear story: we refreshed the tools around the app, not the app itself.</p><h2>Do runtime updates without the big gamble</h2><p>This is where people reach for one big <code>bundle update</code> and hope for the best.</p><p>I would not.</p><p>Start with the cheapest pass:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;53d51e9f-229b-4c79-8ae9-4854c90da492&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">bundle update --patch --strict</code></pre></div><p>That usually clears a decent amount of drift without rewriting the whole dependency graph.</p><p>After that, slow down a bit. Run the test suite. Check the areas that matter. If the app has a few dependencies that tend to cause trouble, read their changelogs before you keep going.</p><p>Then use <code>bundle outdated</code> and decide what the next small batch should be. Maybe that is one background job stack. Maybe one API client. Maybe one gem that has been behind for a while and deserves its own PR.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;19bb807e-a6ed-4129-8340-dd5276a094fa&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">bundle update --minor --strict &lt;gem-name&gt;
bundle update --major &lt;gem-name&gt;</code></pre></div><p>There is no prize for updating everything in one shot. I would rather review a few boring dependency pull requests than stare at one giant lockfile diff and guess.</p><h2>Treat <code>Gemfile</code> constraints as real upgrade work</h2><p>At some point, Bundler stops because <code>Gemfile</code> says no.</p><p>This is where I switch mental models:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;20cf352a-d2a4-4f9a-9d81-150692905954&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">bundle outdated --groups</code></pre></div><p>If a gem is blocked by a version pin or an upper bound, you are no longer doing routine maintenance. You are changing an explicit decision in the application.</p><p>That deserves more attention.</p><p>Sometimes the change is easy. Sometimes it needs code changes, rollout notes, or a dedicated ticket. Either way, I would not hide that work inside a general dependency refresh.</p><p>I care even more about this with gems close to the heart of the app: <code>rails</code>, database adapters, job processing, auth, anything that can quietly change behavior under load.</p><h2>What this changes</h2><p>The main benefit is not elegance. It is control.</p><p>Each diff has one reason to exist. That makes review less tiring, and when something breaks, you usually know where to look first.</p><p>That is the part people actually want from dependency maintenance. Not a perfectly current lockfile. Just a process that does not feel reckless.</p><h2>Quick reference</h2><ul><li><p>Fix vulnerable gems first.</p></li><li><p>Update <code>test</code> and <code>development</code> gems separately.</p></li><li><p>Use a patch pass for runtime dependencies before picking off smaller batches.</p></li><li><p>Treat <code>Gemfile</code> constraint changes and major upgrades as deliberate work, not lockfile churn.</p></li></ul>]]></content:encoded></item><item><title><![CDATA[Lazy-evaluated filter map]]></title><description><![CDATA[(1..).lazy.filter_map { |i| i * 2 if i.even? }.first(5) => [4, 8, 12, 16, 20]]]></description><link>https://logs.tomasmuller.dev/p/lazy-evaluated-filter-map</link><guid isPermaLink="false">https://logs.tomasmuller.dev/p/lazy-evaluated-filter-map</guid><dc:creator><![CDATA[Tomás Müller]]></dc:creator><pubDate>Tue, 15 Nov 2022 13:57:59 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/2f21367c-ff72-4621-a7da-0ae49838e90e_2000x1341.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HPVa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51f6ad52-330e-4f4c-a8d4-9e9ed23411d3_2000x1341.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HPVa!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51f6ad52-330e-4f4c-a8d4-9e9ed23411d3_2000x1341.jpeg 424w, https://substackcdn.com/image/fetch/$s_!HPVa!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51f6ad52-330e-4f4c-a8d4-9e9ed23411d3_2000x1341.jpeg 848w, https://substackcdn.com/image/fetch/$s_!HPVa!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51f6ad52-330e-4f4c-a8d4-9e9ed23411d3_2000x1341.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!HPVa!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51f6ad52-330e-4f4c-a8d4-9e9ed23411d3_2000x1341.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HPVa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51f6ad52-330e-4f4c-a8d4-9e9ed23411d3_2000x1341.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/51f6ad52-330e-4f4c-a8d4-9e9ed23411d3_2000x1341.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HPVa!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51f6ad52-330e-4f4c-a8d4-9e9ed23411d3_2000x1341.jpeg 424w, https://substackcdn.com/image/fetch/$s_!HPVa!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51f6ad52-330e-4f4c-a8d4-9e9ed23411d3_2000x1341.jpeg 848w, https://substackcdn.com/image/fetch/$s_!HPVa!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51f6ad52-330e-4f4c-a8d4-9e9ed23411d3_2000x1341.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!HPVa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51f6ad52-330e-4f4c-a8d4-9e9ed23411d3_2000x1341.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p><strong>Problem</strong>: calculate and return the current step of a user under a very complex onboarding process, which depends on a myriad of conditions. The implementation should be:</p><ul><li><p>Easy to introduce new steps in-between existing ones;</p></li><li><p>Easy to remove existing steps;</p></li><li><p>Easy to maintain, iterating over the current behavior of any existing steps;</p></li></ul><h3>The idea</h3><pre><code>class CurrentOnboardingStepCalculator
  def initialize(user)
    @user = user
  end
  
  def self.call(user)
    new(user).call
  end
  
  private
  
  attr_reader :user
    
  def call
    steps.lazy.filter_map { |step| step.call(user) }.first
  end
  
  # Keep the steps in the order you want the process to happen.
  def steps
    [
      MissingRequirementStep,
      MissingIdentityStep,
      MissingAddressStep,
      MissingBusinessStructureStep,
      MissingPaymentInformationStep,
      MissingTermsAcceptanceStep,
      # And the list goes on.
    ]
  end
end</code></pre><p>Pretty simple, right? Not a single <code>if</code> statement nor a <code>case</code> switch. We are telling this class to return the first non-nil value from the array. The <code>lazy.filter_map</code> chains the operation to be lazy-evaluated.</p><p>In other words, consider a hypothetical execution:<br>- <code>MissingRequirementStep</code> returns <code>nil</code>;<br>- <code>MissingIdentityStep</code> returns <code>nil</code>;<br>- <code>MissingAddressStep</code> returns <code>:missing_region</code>;</p><p>The execution will stop there and return the symbol without realizing the following steps. Then, an API could return this symbol to the application frontend, which is prepared to handle the situation.</p><p>Interesting! Now we are free to implement the rules that determine each step we need, keeping them well encapsulated using separate classes for each one.</p><p>Let's see how we could implement the <code>MissingAddressStep</code> class to calculate and return the possible values which belong to this step.</p><pre><code>class MissingAddressStep
  delegate :address, :subscription, :suspect_fraud?, to: :user, private: true
  delegate :city, :region, :country, to: :address, private: true
  delegate :expired_trial?, to: :subscription, private: true
  
  def initialize(user)
    @user = user
  end
  
  def self.call(user)
    new(user).call
  end
  
  private
  
  attr_reader :user
    
  def call
    return unless request_address?
    
    [
      missing_city,
      missing_region,
      missing_country
    ].lazy.filter_map(&amp;:call).first
  end
  
  def request_address?
    [
      expired_trial?,
      suspect_fraud?
    ].any?
  end
    
  def missing_city
    -&gt; { :missing_city if city.blank? }
  end
  
  def missing_region
    -&gt; { :missing_region if region.blank? }
  end
  
  def missing_country
    -&gt; { :missing_country if country.blank? }
  end
end</code></pre><p>Again the <code>lazy.filter_map</code> pattern is applied, mixed with a few lambda objects responsible for checking in each address field of our theoretical example.</p><p>To spice things up, I'm illustrating how we could delay this specific onboarding step by early returning if all preconditions are met. Following the same idea, we could apply an exclusive logic to defer or early request a given field within the lambda implementation.</p><blockquote><p>&#128134; Ah! Please, don't focus on how I named things here. This is just an example. I'm not trying to convey a naming pattern but instead an idea to solve a problem. Given your context, you will likely apply different names, structures, and so forth. <a href="https://twitter.com/jorgemanru">Jorge Manrubia</a> put out a nice write-up called <a href="https://dev.37signals.com/vanilla-rails-is-plenty?utm_source=tomasmuller.dev&amp;utm_medium=link&amp;utm_campaign=Lazy-evaluated%20filter%20map">"Vanilla Rails is plenty"</a>. You should check it out if you are using Rails and want some inspiration to name and organize things within your project.</p></blockquote><p>See you around! &#128075;</p>]]></content:encoded></item><item><title><![CDATA[Record and monitor deployments using GitHub Actions and New Relic]]></title><description><![CDATA[In my previous post, we saw how to use GitHub Actions to deploy your application manually. Now let's learn how to keep a record of your production deployments.]]></description><link>https://logs.tomasmuller.dev/p/record-and-monitor-deployments-using-github-actions-and-newrelic</link><guid isPermaLink="false">https://logs.tomasmuller.dev/p/record-and-monitor-deployments-using-github-actions-and-newrelic</guid><dc:creator><![CDATA[Tomás Müller]]></dc:creator><pubDate>Fri, 07 Oct 2022 10:45:49 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/e7281a09-fe9c-4189-9ee4-246752cff214_1727x1104.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-xoG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F998d7817-8325-4f96-92f1-e07c0918f35e_1727x1104.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-xoG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F998d7817-8325-4f96-92f1-e07c0918f35e_1727x1104.jpeg 424w, https://substackcdn.com/image/fetch/$s_!-xoG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F998d7817-8325-4f96-92f1-e07c0918f35e_1727x1104.jpeg 848w, https://substackcdn.com/image/fetch/$s_!-xoG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F998d7817-8325-4f96-92f1-e07c0918f35e_1727x1104.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!-xoG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F998d7817-8325-4f96-92f1-e07c0918f35e_1727x1104.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-xoG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F998d7817-8325-4f96-92f1-e07c0918f35e_1727x1104.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/998d7817-8325-4f96-92f1-e07c0918f35e_1727x1104.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-xoG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F998d7817-8325-4f96-92f1-e07c0918f35e_1727x1104.jpeg 424w, https://substackcdn.com/image/fetch/$s_!-xoG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F998d7817-8325-4f96-92f1-e07c0918f35e_1727x1104.jpeg 848w, https://substackcdn.com/image/fetch/$s_!-xoG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F998d7817-8325-4f96-92f1-e07c0918f35e_1727x1104.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!-xoG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F998d7817-8325-4f96-92f1-e07c0918f35e_1727x1104.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>In my previous post, we saw how to use GitHub Actions to deploy your application manually. Now let's learn how to keep a record of your production deployments.</p><blockquote><h4><a href="https://tomasmuller.dev/manual-application-deployment-using-github-actions/">Manual application deployment using GitHub Actions</a></h4></blockquote><p>As you may already know, you can define <a href="https://docs.github.com/en/actions/using-workflows/about-workflows#creating-dependent-jobs">dependent jobs</a> within your GitHub Actions workflow. So, now let's define a <code>release</code> phase for our <code>deploy</code> job, which will run only when a production deployment is triggered.</p><pre><code>name: Deploy

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Environment to deploy'
        required: true
        default: 'staging'

jobs:
  deploy:
    name: Deploy to ${{ github.event.inputs.environment }}
    runs-on: ubuntu-latest
    env:
      echo "Ommited for brevity (see: https://tomasmuller.dev/manual-application-deployment-using-github-actions/)."

    steps:
      echo "Ommited for brevity (see: https://tomasmuller.dev/manual-application-deployment-using-github-actions/)."
      
  release:
    needs: deploy
    if: github.event.inputs.environment == 'production'
    runs-on: ubuntu-latest
    name: Release
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Get last commit message
        run: |
          LAST_COMMIT_MESSAGE=$(git log -1 --pretty=format:"%s")
          echo "LAST_COMMIT_MESSAGE=$LAST_COMMIT_MESSAGE" &gt;&gt; $GITHUB_ENV
      - name: Create New Relic deployment marker
        run: |
          curl -X POST "https://api.newrelic.com/v2/applications/${{ secrets.NEW_RELIC_APP_ID }}/deployments.json" \
            -H "X-Api-Key:${{ secrets.NEW_RELIC_API_KEY }}" \
            -i \
            -H 'Content-Type: application/json' \
            -d \
            '{
              "deployment": {
                "revision": "${{ github.sha }}",
                "description": "${{ env.LAST_COMMIT_MESSAGE }}",
                "user": "${{ github.actor }}"
              }
            }'</code></pre><p>Use <a href="https://docs.github.com/en/actions/security-guides/encrypted-secrets">GitHub Actions secrets</a> to store your <a href="https://docs.newrelic.com/docs/apis/rest-api-v2/get-started/get-app-other-ids-new-relic-one/"><code>NEW_RELIC_APP_ID</code></a> and <a href="https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/"><code>NEW_RELIC_API_KEY</code></a>.</p><p>Deploying an app can be an exciting event, but often your app breaks because of bad changes. A record of your application deployments can guide you to the root cause.</p><p><a href="https://newrelic.com/?utm_source=tomasmuller-dot-dev&amp;utm_medium=blog&amp;utm_campaign=record-and-monitor-deployments-using-github-actions-and-newrelic">New Relic</a> allows you to track deployments to correlate any deployment to your app's performance and anomalies. Also, tracking deployments create deployment markers that appear in APM charts.</p><div class="captioned-image-container"><figure><div id="youtube2-HPeXZubcZ7o" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;HPeXZubcZ7o&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/HPeXZubcZ7o?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div></figure></div><p>After the deployment is recorded using the REST API, you can optionally notify a webhook endpoint of the deployment.</p><p>The destination of the webhook can be your <a href="https://slack.com/?utm_source=tomasmuller-dot-dev&amp;utm_medium=post&amp;utm_campaign=record-and-monitor-deployments-using-github-actions-and-newrelic">Slack</a> instance. To use webhooks to set up a deployment notification for a Slack channel:</p><ol><li><p>Log in to your Slack account as an admin, then go to <strong>App directory &gt; Manage &gt; Apps</strong>.</p></li><li><p>Search for your New Relic app, then select <strong>Add configuration</strong>.</p></li><li><p>From <strong>Post to channel</strong>, select an existing Slack channel or add a new channel, then <strong>Add configuration</strong>.</p></li><li><p>From the list of options, copy the webhook URL.</p></li><li><p>Go to <strong><a href="https://one.newrelic.com/">one.newrelic.com</a> &gt; (<a href="https://docs.newrelic.com/docs/using-new-relic/welcome-new-relic/get-started/glossary#account-dropdown">account dropdown</a>) &gt; Account settings &gt; Integrations &gt; Deploy notifications &gt; Webhook</strong>.</p></li><li><p>Paste the Slack webhook URL, then save.</p></li><li><p>Optional: Send a test message.</p></li></ol><p>That's all. Simple and effective. Now, it's your time! Go ahead and ship something!</p>]]></content:encoded></item><item><title><![CDATA[Manual application deployment using GitHub Actions]]></title><description><![CDATA[Did you know that you can configure your workflows to run only when manually triggered through the Actions tab on GitHub, GitHub CLI, or the REST API?]]></description><link>https://logs.tomasmuller.dev/p/manual-application-deployment-using-github-actions</link><guid isPermaLink="false">https://logs.tomasmuller.dev/p/manual-application-deployment-using-github-actions</guid><dc:creator><![CDATA[Tomás Müller]]></dc:creator><pubDate>Fri, 30 Sep 2022 10:54:25 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/8b0fc397-10ed-449e-9455-fc459aee4856_2308x1048.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WhTQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632bee15-6b07-473b-995a-5e3437cd8ac9_2308x1048.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WhTQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632bee15-6b07-473b-995a-5e3437cd8ac9_2308x1048.png 424w, https://substackcdn.com/image/fetch/$s_!WhTQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632bee15-6b07-473b-995a-5e3437cd8ac9_2308x1048.png 848w, https://substackcdn.com/image/fetch/$s_!WhTQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632bee15-6b07-473b-995a-5e3437cd8ac9_2308x1048.png 1272w, https://substackcdn.com/image/fetch/$s_!WhTQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632bee15-6b07-473b-995a-5e3437cd8ac9_2308x1048.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WhTQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632bee15-6b07-473b-995a-5e3437cd8ac9_2308x1048.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/632bee15-6b07-473b-995a-5e3437cd8ac9_2308x1048.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!WhTQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632bee15-6b07-473b-995a-5e3437cd8ac9_2308x1048.png 424w, https://substackcdn.com/image/fetch/$s_!WhTQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632bee15-6b07-473b-995a-5e3437cd8ac9_2308x1048.png 848w, https://substackcdn.com/image/fetch/$s_!WhTQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632bee15-6b07-473b-995a-5e3437cd8ac9_2308x1048.png 1272w, https://substackcdn.com/image/fetch/$s_!WhTQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F632bee15-6b07-473b-995a-5e3437cd8ac9_2308x1048.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>Did you know that you can configure your workflows to run only when manually triggered through the Actions tab on GitHub, GitHub CLI, or the REST API?</p><p>An everyday use case for this feature is when you have multiple environments running your application and wants to release a branch to a specific environment.</p><p>Here is a basic recipe for that purpose, making use of <code>workflow_dispatch</code> and event inputs.</p><pre><code>name: Deploy

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Environment to deploy'
        required: true
        default: 'staging'
        
jobs:
  deploy:
    name: Deploy to ${{ github.event.inputs.environment }}
    runs-on: ubuntu-latest
    env:
      FLY_APP_CONFIG: ${{ github.workspace }}/${{ fromJSON('{"staging":"fly.staging.toml","production":"fly.production.toml"}')[github.event.inputs.environment] }}
      FLY_APP_NAME: ${{ fromJSON('{"staging":"myapp-staging","production":"myapp-production"}')[github.event.inputs.environment] }}

    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Install Fly.io CLI
        uses: superfly/flyctl-actions/setup-flyctl@master

      - name: Run Fly Doctor
        run: flyctl doctor

      - name: Deploy the application to Fly.io
        run: |
          echo "Deploying to $FLY_APP_NAME with config $FLY_APP_CONFIG"
          flyctl deploy --remote-only</code></pre><p>Here I'm giving you an illustration of what could be a very basic workflow to deploy an app to <a href="https://fly.io/?utm_source=tomasmuller-dot-dev&amp;utm_medium=link">Fly.io</a>.</p><p>Also, as food for thought, I'm showing how you could grab different configuration files or values (<code>env</code> section) using the environment input. Then, you can elaborate on the deploy command, applying these values to build the command line and ship your software to the right environment using the correct configuration.</p><p>Since it is possible to trigger workflows like this <a href="https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow">using REST API</a>, you can go above and beyond, connecting with your existing tools and processes.</p>]]></content:encoded></item><item><title><![CDATA[Clojure’s main rudiment: Macros]]></title><description><![CDATA[The following is a recording of my detailed walkthrough of Clojure's macros system on Clojure Brazil Meetup.]]></description><link>https://logs.tomasmuller.dev/p/clojure-main-rudiment-macros</link><guid isPermaLink="false">https://logs.tomasmuller.dev/p/clojure-main-rudiment-macros</guid><dc:creator><![CDATA[Tomás Müller]]></dc:creator><pubDate>Sat, 06 Aug 2016 03:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/10fd0cac-a827-49ea-9ed7-c5464dcdb56f_1906x1416.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QCJU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0e1d430-ca6a-493b-97e8-f83f8971e841_1906x1416.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QCJU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0e1d430-ca6a-493b-97e8-f83f8971e841_1906x1416.png 424w, https://substackcdn.com/image/fetch/$s_!QCJU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0e1d430-ca6a-493b-97e8-f83f8971e841_1906x1416.png 848w, https://substackcdn.com/image/fetch/$s_!QCJU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0e1d430-ca6a-493b-97e8-f83f8971e841_1906x1416.png 1272w, https://substackcdn.com/image/fetch/$s_!QCJU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0e1d430-ca6a-493b-97e8-f83f8971e841_1906x1416.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QCJU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0e1d430-ca6a-493b-97e8-f83f8971e841_1906x1416.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c0e1d430-ca6a-493b-97e8-f83f8971e841_1906x1416.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Clojure's main rudiment: macros - building the foundation&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Clojure's main rudiment: macros - building the foundation" title="Clojure's main rudiment: macros - building the foundation" srcset="https://substackcdn.com/image/fetch/$s_!QCJU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0e1d430-ca6a-493b-97e8-f83f8971e841_1906x1416.png 424w, https://substackcdn.com/image/fetch/$s_!QCJU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0e1d430-ca6a-493b-97e8-f83f8971e841_1906x1416.png 848w, https://substackcdn.com/image/fetch/$s_!QCJU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0e1d430-ca6a-493b-97e8-f83f8971e841_1906x1416.png 1272w, https://substackcdn.com/image/fetch/$s_!QCJU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0e1d430-ca6a-493b-97e8-f83f8971e841_1906x1416.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>The following is a recording of my detailed walkthrough of Clojure's macros system on Clojure Brazil Meetup. <a href="https://speakerdeck.com/tomasmuller/clojures-main-rudiment-macros-construindo-a-fundacao">Slides</a>.</p><div class="captioned-image-container"><figure><div id="youtube2-BGdfOGT4_HE" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;BGdfOGT4_HE&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/BGdfOGT4_HE?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div></figure></div>]]></content:encoded></item><item><title><![CDATA[AWS Opsworks Deployment]]></title><description><![CDATA[Back in 2013, AWS announced a new service called OpsWorks. Let's look at its actual state and how you can deploy your Ruby on Rails applications there.]]></description><link>https://logs.tomasmuller.dev/p/aws-opsworks-deployment</link><guid isPermaLink="false">https://logs.tomasmuller.dev/p/aws-opsworks-deployment</guid><dc:creator><![CDATA[Tomás Müller]]></dc:creator><pubDate>Tue, 25 Aug 2015 03:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/5972ba15-ee38-4df2-abfa-7cfeb37db6ce_2000x1333.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!54J5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50afa992-b0e3-47a9-8fd8-3ecd0194b40b_2000x1333.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!54J5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50afa992-b0e3-47a9-8fd8-3ecd0194b40b_2000x1333.jpeg 424w, https://substackcdn.com/image/fetch/$s_!54J5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50afa992-b0e3-47a9-8fd8-3ecd0194b40b_2000x1333.jpeg 848w, https://substackcdn.com/image/fetch/$s_!54J5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50afa992-b0e3-47a9-8fd8-3ecd0194b40b_2000x1333.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!54J5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50afa992-b0e3-47a9-8fd8-3ecd0194b40b_2000x1333.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!54J5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50afa992-b0e3-47a9-8fd8-3ecd0194b40b_2000x1333.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/50afa992-b0e3-47a9-8fd8-3ecd0194b40b_2000x1333.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!54J5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50afa992-b0e3-47a9-8fd8-3ecd0194b40b_2000x1333.jpeg 424w, https://substackcdn.com/image/fetch/$s_!54J5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50afa992-b0e3-47a9-8fd8-3ecd0194b40b_2000x1333.jpeg 848w, https://substackcdn.com/image/fetch/$s_!54J5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50afa992-b0e3-47a9-8fd8-3ecd0194b40b_2000x1333.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!54J5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50afa992-b0e3-47a9-8fd8-3ecd0194b40b_2000x1333.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>Back in 2013, AWS announced a new service called OpsWorks. Let's look at its actual state and how you can deploy your Ruby on Rails applications there.</p><p>OpsWorks is an application management service for managing applications of any scale or complexity on the AWS cloud. You can think of it as something between Heroku or Beanstalk and a manually configured server environment.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KC_d!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cda8741-2a54-41c4-8d23-232de5cb114e_747x682.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KC_d!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cda8741-2a54-41c4-8d23-232de5cb114e_747x682.png 424w, https://substackcdn.com/image/fetch/$s_!KC_d!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cda8741-2a54-41c4-8d23-232de5cb114e_747x682.png 848w, https://substackcdn.com/image/fetch/$s_!KC_d!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cda8741-2a54-41c4-8d23-232de5cb114e_747x682.png 1272w, https://substackcdn.com/image/fetch/$s_!KC_d!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cda8741-2a54-41c4-8d23-232de5cb114e_747x682.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!KC_d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cda8741-2a54-41c4-8d23-232de5cb114e_747x682.png" width="747" height="682" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2cda8741-2a54-41c4-8d23-232de5cb114e_747x682.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:682,&quot;width&quot;:747,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!KC_d!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cda8741-2a54-41c4-8d23-232de5cb114e_747x682.png 424w, https://substackcdn.com/image/fetch/$s_!KC_d!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cda8741-2a54-41c4-8d23-232de5cb114e_747x682.png 848w, https://substackcdn.com/image/fetch/$s_!KC_d!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cda8741-2a54-41c4-8d23-232de5cb114e_747x682.png 1272w, https://substackcdn.com/image/fetch/$s_!KC_d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cda8741-2a54-41c4-8d23-232de5cb114e_747x682.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Opsworks Sample Architecture</figcaption></figure></div><ul><li><p>AWS Elastic Beanstalk is an easy-to-use solution for building web apps and web services with popular application containers such as Java, PHP, Python, Ruby, and .NET. You upload your code, and Elastic Beanstalk automatically does the rest. Elastic Beanstalk supports the most common web architectures, application containers, and frameworks.</p></li><li><p>AWS OpsWorks is a robust end-to-end solution that gives you an easy way to manage applications of nearly any scale and complexity without sacrificing control. You model, customize, and automate the entire application throughout its lifecycle. OpsWorks provides integrated experiences for IT administrators and ops-minded developers who want a high degree of productivity and control over operations</p></li><li><p>AWS CloudFormation is a building block service that enables customers to provision and manage almost any AWS resource via a domain-specific language. You define JSON templates and use them to provision and manage AWS resources, operating systems, and application code.</p></li></ul><p>The main concepts of OpsWorks are:</p><ul><li><p>Stacks: a stack represents a collection of EC2 instances and related AWS resources that have a common purpose and that you want to manage collectively. Within a stack, you use layers to define the configuration of your instances and use apps to specify the code you want to deploy.</p></li><li><p>Layers: a layer is a blueprint for a set of EC2 instances. It defines the instance's settings, resources, installed packages, profiles, and security groups. OpsWorks provides a set of layers for standard use cases such as application servers, which you can customize as needed. You can also create a custom layer whose configuration you can define from the ground up.</p></li><li><p>Applications: an application represents code stored in a repository that you want to install on application server instances. When you deploy the application, OpsWorks downloads the code from the repository to the specified server instances.</p></li><li><p>Instances: an instance represents a server. It can belong to one or more layers, that define the instance's settings, resources, installed packages, profiles and security groups. When you start the instance, OpsWorks uses the associated layer's blueprint to create and configure a corresponding EC2 instance.</p></li></ul><p>There's no charge for using OpsWorks. You pay only for AWS resources being used like EC2 instances, RDS databases, etc.</p><p>OpsWorks uses <a href="http://www.opscode.com/chef/">Chef</a> to configure the layer's EC2 instances. Chef turns infrastructure into code. With Chef, you can automate how you build, deploy, and manage your infrastructure. Your infrastructure becomes as versionable, testable, and repeatable as application code. A Chef recipe defines everything that is required to configure part of a system. And a Chef Cookbook is a collection of recipes.</p><p>You don't have to be a <a href="https://www.google.com.br/search?q=masterchef+brasil&amp;source=lnms&amp;tbm=isch&amp;sa=X&amp;ved=0CAgQ_AUoAmoVChMIiM3Mn4ezxwIVAh2QCh3FnQrK&amp;biw=1920&amp;bih=978">MasterChef</a> (oh, &#129318;&#127995;&#8205;) to use AWS OpsWorks. There is a good set of predefined layer blueprints for standard use cases, all of them are open-source, available on <a href="http://github.com/aws/opsworks-cookbooks">opsworks-cookbooks</a> Github repository.</p><p>This post focuses on how you can deploy Ruby on Rails applications on AWS OpsWorks. There are fantastic resources on the Internet explaining OpsWorks philosophy and how it works under the hood, so I won't repeat them here. You're invited to check the curated references at the end of this post.</p><p>Here is a collection of tips and tricks to help you deliver Ruby on Rails applications using AWS OpsWorks. Enjoy!</p><p>Log in to your AWS account. Select OpsWorks on AWS console. It can be found under the <strong>Deployment &amp; Management</strong> product family.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YHlJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20d3c2bc-8645-49c3-a61c-cbb779226b6b_1904x660.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YHlJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20d3c2bc-8645-49c3-a61c-cbb779226b6b_1904x660.png 424w, https://substackcdn.com/image/fetch/$s_!YHlJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20d3c2bc-8645-49c3-a61c-cbb779226b6b_1904x660.png 848w, https://substackcdn.com/image/fetch/$s_!YHlJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20d3c2bc-8645-49c3-a61c-cbb779226b6b_1904x660.png 1272w, https://substackcdn.com/image/fetch/$s_!YHlJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20d3c2bc-8645-49c3-a61c-cbb779226b6b_1904x660.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YHlJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20d3c2bc-8645-49c3-a61c-cbb779226b6b_1904x660.png" width="1904" height="660" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/20d3c2bc-8645-49c3-a61c-cbb779226b6b_1904x660.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:660,&quot;width&quot;:1904,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!YHlJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20d3c2bc-8645-49c3-a61c-cbb779226b6b_1904x660.png 424w, https://substackcdn.com/image/fetch/$s_!YHlJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20d3c2bc-8645-49c3-a61c-cbb779226b6b_1904x660.png 848w, https://substackcdn.com/image/fetch/$s_!YHlJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20d3c2bc-8645-49c3-a61c-cbb779226b6b_1904x660.png 1272w, https://substackcdn.com/image/fetch/$s_!YHlJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20d3c2bc-8645-49c3-a61c-cbb779226b6b_1904x660.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Tip N&#186;1</strong>: you can have many stacks as you want. Doing an analogy with Heroku, you can think of a stack as a new Heroku application. A completely new and isolated environment. Talking about environments, a common practice is to have multiple stacks that represent different environments, like staging, production, and so on.</p><p><strong>Tip N&#186;2</strong>: when you are creating a stack, you have the option to set a <strong>Default SSH key</strong> for all future instances within the stack. So, a common practice is to create or import a key pair <strong>before</strong> creating a new stack. To do that, first, go to EC2 dashboard, click on <strong>Key Pairs</strong> option, and create or import your key pair. By doing so, all layers instances will have it configured and you'll be able to ssh those instances.</p><p><strong>Tip N&#186;3</strong>: you can customize layer cookbooks using the <strong>custom JSON</strong> textarea. Chef cookbooks accept parameters in the form of node attributes. You need to check what parameters you can send to each cookbook (<a href="http://github.com/aws/opsworks-cookbooks">use the source</a>). Check below one useful example:</p><pre><code>{
  "opsworks_initial_setup": {
    "swapfile_size_mb": "512"
  },
  "unicorn": {
    "worker_processes": "2",
    "version": "4.9.0"
  },
  "nginx": {
    "worker_processes": "1"
  },
  "deploy": {
    "application_short_name": {
      "database": {
        "adapter": "postgresql",
        "type":"postrgesql",
        "encoding": "unicode",
        "min_messages": "warning"
      }
    }
  }
}</code></pre><p><strong>Tip N&#186;4</strong>: the <code>swapfile_size_mb</code> parameter above is especially useful when deploying Rails applications on EC2 micro instances (free tier) and use a deploy hook (that I'll share in minutes with you) to precompile your assets (imitating Heroku deploys). The <code>assets:precompile</code> command eats a lot of memory, causing the instance to run out of memory on every deployment if you don't increase the swap file size.</p><p><strong>Tip N&#186;5</strong>: during the stack configuration you have the option to use <strong>custom Chef cookbooks</strong>. This is useful when setting node attributes aren't sufficient. By setting custom cookbooks you can go further and override some files written out by your layer's recipes, or even build a completely new and customized layer.</p><p>Every stack contains one or more layers, each of which represents a stack component, such as a load balancer, application servers, application databases, workers, and so on.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jHz3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb055cda-0b04-4351-888b-4cf7a6179ab4_1926x994.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jHz3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb055cda-0b04-4351-888b-4cf7a6179ab4_1926x994.png 424w, https://substackcdn.com/image/fetch/$s_!jHz3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb055cda-0b04-4351-888b-4cf7a6179ab4_1926x994.png 848w, https://substackcdn.com/image/fetch/$s_!jHz3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb055cda-0b04-4351-888b-4cf7a6179ab4_1926x994.png 1272w, https://substackcdn.com/image/fetch/$s_!jHz3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb055cda-0b04-4351-888b-4cf7a6179ab4_1926x994.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jHz3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb055cda-0b04-4351-888b-4cf7a6179ab4_1926x994.png" width="1926" height="994" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fb055cda-0b04-4351-888b-4cf7a6179ab4_1926x994.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:994,&quot;width&quot;:1926,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!jHz3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb055cda-0b04-4351-888b-4cf7a6179ab4_1926x994.png 424w, https://substackcdn.com/image/fetch/$s_!jHz3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb055cda-0b04-4351-888b-4cf7a6179ab4_1926x994.png 848w, https://substackcdn.com/image/fetch/$s_!jHz3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb055cda-0b04-4351-888b-4cf7a6179ab4_1926x994.png 1272w, https://substackcdn.com/image/fetch/$s_!jHz3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb055cda-0b04-4351-888b-4cf7a6179ab4_1926x994.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I can't do a good analogy this time with Heroku. However, you can think of a layer as a custom add-on, or your Heroku Postgres (which is an add-on in fact).</p><p><strong>Tip N&#186;1</strong>: currently AWS OpsWorks provides a set of layers for standard use cases such as application servers, databases, etc., which you can customize as needed. Also, there is a special kind of layer to connect your <strong>RDS databases</strong> to your stack. This is perfect if you are coming from Heroku. First, go to RDS dashboard, create a new RDS Postgres database and then go to OpsWorks, select your stack, add a layer, and select the RDS layer pointing to the recently created database.</p><p><strong>Tip N&#186;2</strong>: a typical Rails Application will have three or more layers:</p><ul><li><p>A Rails App Server layer</p></li><li><p>A Load Balancer layer</p></li><li><p>A Database layer</p></li><li><p>N worker layers (yes, like your Heroku workers)</p></li></ul><p><strong>Tip N&#186;3</strong>: when you create the <strong>Rails App Server layer</strong> you have the option to select the Ruby version and other common settings. If you intend to use a <strong>load balancer</strong> for your App Server instances (which is highly recommended!), first go to EC2 dashboard and create a Load Balancer <strong>before</strong> creating the Rails App Server layer. A common practice is to name the load balancer with the <code>stack-name</code> plus the <code>-lb</code> suffix, resulting in <code>your-app-short-name-lb</code>.</p><p><strong>Tip N&#186;4</strong>: create and attach a security group for your load balancer configuring the inbound and outbound traffic as follows:</p><p><strong>Inbound:</strong></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ptaF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bdba35d-1838-4292-ba5c-6db2d3376eaa_610x83.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ptaF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bdba35d-1838-4292-ba5c-6db2d3376eaa_610x83.png 424w, https://substackcdn.com/image/fetch/$s_!ptaF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bdba35d-1838-4292-ba5c-6db2d3376eaa_610x83.png 848w, https://substackcdn.com/image/fetch/$s_!ptaF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bdba35d-1838-4292-ba5c-6db2d3376eaa_610x83.png 1272w, https://substackcdn.com/image/fetch/$s_!ptaF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bdba35d-1838-4292-ba5c-6db2d3376eaa_610x83.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ptaF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bdba35d-1838-4292-ba5c-6db2d3376eaa_610x83.png" width="610" height="83" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3bdba35d-1838-4292-ba5c-6db2d3376eaa_610x83.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:83,&quot;width&quot;:610,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!ptaF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bdba35d-1838-4292-ba5c-6db2d3376eaa_610x83.png 424w, https://substackcdn.com/image/fetch/$s_!ptaF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bdba35d-1838-4292-ba5c-6db2d3376eaa_610x83.png 848w, https://substackcdn.com/image/fetch/$s_!ptaF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bdba35d-1838-4292-ba5c-6db2d3376eaa_610x83.png 1272w, https://substackcdn.com/image/fetch/$s_!ptaF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bdba35d-1838-4292-ba5c-6db2d3376eaa_610x83.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><strong>Outbound:</strong></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zZss!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b25240-6ed6-4d48-bc34-26d85c9f8906_627x51.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zZss!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b25240-6ed6-4d48-bc34-26d85c9f8906_627x51.png 424w, https://substackcdn.com/image/fetch/$s_!zZss!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b25240-6ed6-4d48-bc34-26d85c9f8906_627x51.png 848w, https://substackcdn.com/image/fetch/$s_!zZss!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b25240-6ed6-4d48-bc34-26d85c9f8906_627x51.png 1272w, https://substackcdn.com/image/fetch/$s_!zZss!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b25240-6ed6-4d48-bc34-26d85c9f8906_627x51.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zZss!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b25240-6ed6-4d48-bc34-26d85c9f8906_627x51.png" width="627" height="51" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a7b25240-6ed6-4d48-bc34-26d85c9f8906_627x51.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:51,&quot;width&quot;:627,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!zZss!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b25240-6ed6-4d48-bc34-26d85c9f8906_627x51.png 424w, https://substackcdn.com/image/fetch/$s_!zZss!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b25240-6ed6-4d48-bc34-26d85c9f8906_627x51.png 848w, https://substackcdn.com/image/fetch/$s_!zZss!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b25240-6ed6-4d48-bc34-26d85c9f8906_627x51.png 1272w, https://substackcdn.com/image/fetch/$s_!zZss!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7b25240-6ed6-4d48-bc34-26d85c9f8906_627x51.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><strong>Tip N&#186;5</strong>: if you use HTTPS for your application, you must upload the SSL certificate files to AWS IAM. An SSL Certificate allows you to configure the HTTPS/SSL listeners of your load balancer. You may select a previously uploaded certificate, or define a new SSL Certificate. To upload your certificate to AWS, ensure that all your certificate files are in PEM format. Follow the steps below if your certificate was issued by <a href="https://www.ssls.com/">Comodo</a>:</p><p>Extract the certificate files to a folder and <code>cd</code> to that folder:</p><pre><code>cd /path/to/certificates/folder
mkdir out</code></pre><p>Convert all certificates and private key:</p><pre><code>openssl x509 -in ./AddTrustExternalCARoot.crt -outform pem -out ./out/AddTrustExternalCARoot.pem
openssl x509 -in ./COMODORSAAddTrustCA.crt -outform pem -out ./out/COMODORSAAddTrustCA.pem
openssl x509 -in ./COMODORSADomainValidationSecureServerCA.crt -outform pem -out ./out/COMODORSADomainValidationSecureServerCA.pem
openssl x509 -in ./your_issued_certificate_name.crt -outform pem -out ./out/your_issued_certificate_name.pem
openssl rsa -in ./server.key -outform pem -out ./out/server.key.pem</code></pre><p>Create the CAChain:</p><pre><code>cat ./out/COMODORSADomainValidationSecureServerCA.pem &gt; ./out/CAChain.pem
cat ./out/COMODORSAAddTrustCA.pem &gt;&gt; ./out/CAChain.pem
cat ./out/AddTrustExternalCARoot.pem &gt;&gt; ./out/CAChain.pem</code></pre><p>Finally, upload it to AWS IAM:</p><pre><code>cd out
aws iam upload-server-certificate --server-certificate-name YourIssuedCertificateName --certificate-body file://your_issued_certificate_name.pem --private-key file://server.key.pem --certificate-chain file://CAChain.pem</code></pre><p><strong>Tip N&#186;6</strong>: if you use HTTPS for your application, configure the load balancer health check to use <code>HTTPS</code> for the ping protocol. Set only an <code>/</code> as the ping path.</p><p><strong>Tip N&#186;7</strong>: to use an RDS Postgres database layer along with your Rails application, you need to configure and apply a Security Group to your database instance allowing any source IP to access your database. To do that go to EC2 dashboard, select <strong>Security Groups</strong> option and create a new security group with the following inbound and outbound rules:</p><p><strong>Inbound:</strong></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6NYq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb053d3c9-c476-4110-91cc-ab5c736cc66a_637x62.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6NYq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb053d3c9-c476-4110-91cc-ab5c736cc66a_637x62.png 424w, https://substackcdn.com/image/fetch/$s_!6NYq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb053d3c9-c476-4110-91cc-ab5c736cc66a_637x62.png 848w, https://substackcdn.com/image/fetch/$s_!6NYq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb053d3c9-c476-4110-91cc-ab5c736cc66a_637x62.png 1272w, https://substackcdn.com/image/fetch/$s_!6NYq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb053d3c9-c476-4110-91cc-ab5c736cc66a_637x62.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6NYq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb053d3c9-c476-4110-91cc-ab5c736cc66a_637x62.png" width="637" height="62" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b053d3c9-c476-4110-91cc-ab5c736cc66a_637x62.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:62,&quot;width&quot;:637,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!6NYq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb053d3c9-c476-4110-91cc-ab5c736cc66a_637x62.png 424w, https://substackcdn.com/image/fetch/$s_!6NYq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb053d3c9-c476-4110-91cc-ab5c736cc66a_637x62.png 848w, https://substackcdn.com/image/fetch/$s_!6NYq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb053d3c9-c476-4110-91cc-ab5c736cc66a_637x62.png 1272w, https://substackcdn.com/image/fetch/$s_!6NYq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb053d3c9-c476-4110-91cc-ab5c736cc66a_637x62.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><strong>Outbound:</strong></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QFTD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20bfd7d0-c791-43e7-ba6c-673e16946513_620x50.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QFTD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20bfd7d0-c791-43e7-ba6c-673e16946513_620x50.png 424w, https://substackcdn.com/image/fetch/$s_!QFTD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20bfd7d0-c791-43e7-ba6c-673e16946513_620x50.png 848w, https://substackcdn.com/image/fetch/$s_!QFTD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20bfd7d0-c791-43e7-ba6c-673e16946513_620x50.png 1272w, https://substackcdn.com/image/fetch/$s_!QFTD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20bfd7d0-c791-43e7-ba6c-673e16946513_620x50.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QFTD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20bfd7d0-c791-43e7-ba6c-673e16946513_620x50.png" width="620" height="50" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/20bfd7d0-c791-43e7-ba6c-673e16946513_620x50.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:50,&quot;width&quot;:620,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!QFTD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20bfd7d0-c791-43e7-ba6c-673e16946513_620x50.png 424w, https://substackcdn.com/image/fetch/$s_!QFTD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20bfd7d0-c791-43e7-ba6c-673e16946513_620x50.png 848w, https://substackcdn.com/image/fetch/$s_!QFTD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20bfd7d0-c791-43e7-ba6c-673e16946513_620x50.png 1272w, https://substackcdn.com/image/fetch/$s_!QFTD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20bfd7d0-c791-43e7-ba6c-673e16946513_620x50.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><strong>Tip N&#186;8</strong>: if you already have a database on Heroku Postgres, and want to migrate all existent data to your RDS Postgres instance, do the following:</p><p>Put your Heroku app in maintenance mode:</p><pre><code>heroku maintenance:on --app your_heroku_application</code></pre><p>Capture and download a <a href="https://devcenter.heroku.com/articles/heroku-postgres-backups">backup via command line</a>, or using Heroku Postgres interface.</p><p>Uncompress it to raw SQL:</p><pre><code># the `-n public` option restore only objects that are in the public schema
pg_restore -n public -O latest.dump &gt; heroku_latest.sql</code></pre><p>And then import into your RDS instance:</p><pre><code>psql -f heroku_latest.sql --host=your.rds.database.endpoint --port=5432 --username=your-rds-db-username --password --dbname=your_rds_dbname</code></pre><p><strong>Tip N&#186;9</strong>: after creating the Rails App Server layer, you can edit the layer and add additional operational system packages that will be available on all instances. Again, doing an analogy with Heroku you can think of this as "something like" <a href="https://devcenter.heroku.com/articles/buildpacks">Heroku Buildpacks</a>. I want to reinforce the double quotes here. There's no such thing as Buildpacks on OpsWorks. This is just an analogy. You probably wrote one Buildpack if you already needed to run a package that is not part of the <a href="https://devcenter.heroku.com/articles/cedar-ubuntu-packages">default set of installed packages</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!O8Uj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c4d2873-786a-4019-a14b-3caab668a1c8_829x133.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!O8Uj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c4d2873-786a-4019-a14b-3caab668a1c8_829x133.png 424w, https://substackcdn.com/image/fetch/$s_!O8Uj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c4d2873-786a-4019-a14b-3caab668a1c8_829x133.png 848w, https://substackcdn.com/image/fetch/$s_!O8Uj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c4d2873-786a-4019-a14b-3caab668a1c8_829x133.png 1272w, https://substackcdn.com/image/fetch/$s_!O8Uj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c4d2873-786a-4019-a14b-3caab668a1c8_829x133.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!O8Uj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c4d2873-786a-4019-a14b-3caab668a1c8_829x133.png" width="829" height="133" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8c4d2873-786a-4019-a14b-3caab668a1c8_829x133.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:133,&quot;width&quot;:829,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!O8Uj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c4d2873-786a-4019-a14b-3caab668a1c8_829x133.png 424w, https://substackcdn.com/image/fetch/$s_!O8Uj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c4d2873-786a-4019-a14b-3caab668a1c8_829x133.png 848w, https://substackcdn.com/image/fetch/$s_!O8Uj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c4d2873-786a-4019-a14b-3caab668a1c8_829x133.png 1272w, https://substackcdn.com/image/fetch/$s_!O8Uj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c4d2873-786a-4019-a14b-3caab668a1c8_829x133.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>For instance, if you use the <code>pg</code> gem, add the <code>postgresql93-devel</code> and <code>postgresql93-libs</code> packages. Also, don't forget to add <code>nodejs</code> package. We will use it to precompile application assets.</p><p>We're almost there. At this point, we have configured the software that will run on the server. Now it's time to create the instances themselves which are the servers that will execute and run the application code.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GBE6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2d8d406-1b71-4ec3-b4c0-325cc2ccde9b_2316x1154.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GBE6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2d8d406-1b71-4ec3-b4c0-325cc2ccde9b_2316x1154.png 424w, https://substackcdn.com/image/fetch/$s_!GBE6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2d8d406-1b71-4ec3-b4c0-325cc2ccde9b_2316x1154.png 848w, https://substackcdn.com/image/fetch/$s_!GBE6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2d8d406-1b71-4ec3-b4c0-325cc2ccde9b_2316x1154.png 1272w, https://substackcdn.com/image/fetch/$s_!GBE6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2d8d406-1b71-4ec3-b4c0-325cc2ccde9b_2316x1154.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GBE6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2d8d406-1b71-4ec3-b4c0-325cc2ccde9b_2316x1154.png" width="2000" height="997" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e2d8d406-1b71-4ec3-b4c0-325cc2ccde9b_2316x1154.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:997,&quot;width&quot;:2000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!GBE6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2d8d406-1b71-4ec3-b4c0-325cc2ccde9b_2316x1154.png 424w, https://substackcdn.com/image/fetch/$s_!GBE6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2d8d406-1b71-4ec3-b4c0-325cc2ccde9b_2316x1154.png 848w, https://substackcdn.com/image/fetch/$s_!GBE6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2d8d406-1b71-4ec3-b4c0-325cc2ccde9b_2316x1154.png 1272w, https://substackcdn.com/image/fetch/$s_!GBE6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2d8d406-1b71-4ec3-b4c0-325cc2ccde9b_2316x1154.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Tip N&#186;1</strong>: instances can be started manually, or configured to start and stop on a schedule or in response to load patterns.</p><p><strong>Tip N&#186;2</strong>: a common practice is to spread your instances over subnets in multiple AZs for higher redundancy.</p><p>Adding an app to the stack is the first step to deploying an application to your application servers. An AWS OpsWorks application represents code that you want to run on an application server. The code itself resides in a repository such as an Amazon S3 archive or a GitHub repository.</p><p>With the AWS OpsWorks interface is very easy to register an application. Just click on <strong>Apps</strong> and on the <strong>+add</strong> link. From now on just fill out the form which is pretty straightforward. Bellow are a couple more tips and reminders.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jA1L!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23ad7c34-17ce-4f1d-8123-b50adacdfa54_850x392.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jA1L!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23ad7c34-17ce-4f1d-8123-b50adacdfa54_850x392.png 424w, https://substackcdn.com/image/fetch/$s_!jA1L!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23ad7c34-17ce-4f1d-8123-b50adacdfa54_850x392.png 848w, https://substackcdn.com/image/fetch/$s_!jA1L!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23ad7c34-17ce-4f1d-8123-b50adacdfa54_850x392.png 1272w, https://substackcdn.com/image/fetch/$s_!jA1L!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23ad7c34-17ce-4f1d-8123-b50adacdfa54_850x392.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jA1L!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23ad7c34-17ce-4f1d-8123-b50adacdfa54_850x392.png" width="850" height="392" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/23ad7c34-17ce-4f1d-8123-b50adacdfa54_850x392.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:392,&quot;width&quot;:850,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!jA1L!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23ad7c34-17ce-4f1d-8123-b50adacdfa54_850x392.png 424w, https://substackcdn.com/image/fetch/$s_!jA1L!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23ad7c34-17ce-4f1d-8123-b50adacdfa54_850x392.png 848w, https://substackcdn.com/image/fetch/$s_!jA1L!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23ad7c34-17ce-4f1d-8123-b50adacdfa54_850x392.png 1272w, https://substackcdn.com/image/fetch/$s_!jA1L!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23ad7c34-17ce-4f1d-8123-b50adacdfa54_850x392.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Tip N&#186;1</strong>: when you deploy an application, AWS OpsWorks triggers a <strong>deploy event</strong>, which runs each layer's Deploy recipes. Each of the built-in application server layers includes a set of Deploy recipes, which use the data from the stack configuration and deployment JSON to automatically deploy the app's code from the repository to the layer's instances. The Deploy recipes also handle related tasks such as restarting services and setting up database connections.</p><p><strong>Tip N&#186;2</strong>: you can use a <strong>private git repository</strong> as application source. You just have to configure the repository ssh key on both sides (in the OpsWorks application configuration and your repository).</p><p><strong>Tip N&#186;3</strong>: if you have multiple environments (staging, production, etc.) you can configure to source the application from a <strong>specific branch</strong> corresponding to the environment name.</p><p><strong>Tip N&#186;4</strong>: select <strong>RDS</strong> as data source type if you are using an RDS database layer.</p><p><strong>Tip N&#186;5</strong>: not exactly a tip, but a reminder: you can (and should!) use environment variables to store your secret keys and third-party credentials. A common practice is to set at least the <code>RACK_ENV</code> and <code>RAILS_ENV</code> env vars.</p><p><strong>Tip N&#186;6</strong>: add your application domains or subdomains filling in the <strong>Domains</strong> section. Use the DNS manager of your domain to configure the records. You have the following options:</p><ul><li><p>add an A record pointing it to the specific <strong>instance IP address</strong>. This is not good because this IP changes every time you stop and start the instance.</p></li><li><p>grab the <strong>instance Public DNS</strong> and add a <code>CNAME</code> record pointing to it. Again this is not good, because this address also changes after a full stop/start since the IP address composes the name of instance Public DNS endpoint.</p></li><li><p>the correct approach is to use a Load Balancer layer and use the <strong>DNS name of the Elastic Load Balancer</strong> associated with this layer. This endpoint never changes and all requests will be balanced to all available instances.</p></li></ul><p><strong>Tip N&#186;7</strong>: if your application uses HTTPS you have to configure the SSL certificates. You just have to enable the SSL switch and fill the textareas with the contents of your purchased certificate files as follows:</p><ul><li><p>SSL certificate: enter the SSL certificate (usually: <code>-----BEGIN CERTIFICATE-----</code> ... <code>-----END CERTIFICATE-----</code>). Comodo issued certificates can do the following:</p></li></ul><pre><code>cat your_issued_certificate_name.crt COMODORSAAddTrustCA.crt COMODORSADomainValidationSecureServerCA.crt AddTrustExternalCARoot.crt &gt; bundle.crt
# copy the bundle contents and paste into the SSL certificate textarea
pbcopy &lt; bundle.crt</code></pre><ul><li><p>SSL certificate key: copy and paste the contents of the key generated with your certificate (normally this file is called <code>server.key</code>).</p></li></ul><p>The <strong>SSL certificates of Certification Authorities</strong> textarea is optional, used for intermediate CA key or Client Authentication.</p><p><strong>Tip N&#186;1</strong>: write your own deploy scripts and use the <code>aws</code> command line tool to automate the application deployment of your OpsWorks applications. The following command creates the deployment of an AWS OpsWorks application:</p><pre><code>aws --region='&lt;stack-region&gt;' opsworks create-deployment --stack-id='&lt;stack-opsworks-id&gt;' --app-id='&lt;app-opsworks-id&gt;' --command='{\"Name\":\"deploy\"}'</code></pre><p>Just replace the <code>&lt;stack-region&gt;</code>, <code>&lt;stack-opsworks-id&gt;</code> and <code>&lt;app-opsworks-id&gt;</code> with the corresponding values for your stack and application. These ID's you find viewing the stack and the app respectively.</p><p><strong>Tip N&#186;2</strong>: you probably configured a default SSH Key for your instances (tip number 2, under adding stacks). Having a configured SSH key allows you to SSH directly an instance by its IP address or Public DNS. As one example, if you want to manually check the Rails application logs on a running instance:</p><pre><code># Attention: the default username can change depending on instance's operational system.
# Open the Instances list under OpsWorks and check how to connect to a specific instance clicking on its ssh link.
ssh -i ~/.ssh/your_ssh_key ec2-user@instance.ip.address
sudo su deploy
cd /srv/www/your_app_short_name/current/log
tail -f staging.log</code></pre><p><strong>Tip N&#186;3</strong>: <a href="https://docs.chef.io/resource_deploy.html#callbacks">Chef deploy</a> callbacks can help you to automate your deployment scripts. Just create a directory in your app called <code>deploy</code> and add files named for the appropriate callbacks. For example, here's how you can handle assets pre-compilation, expose environment variables during the deployment, and perform common database tasks like running migrations and seeds.</p><p><strong>deploy/before_restart.rb</strong></p><pre><code>rails_env = new_resource.environment["RAILS_ENV"]

Chef::Log.info("Mapping the environment_variables node for RAILS_ENV=#{rails_env}...")
node[:deploy].each do |application, deploy|
  deploy[:environment_variables].each do |key, value|
    ENV[key] = value
  end
end

Chef::Log.info("Precompiling assets for RAILS_ENV=#{rails_env}...")
execute "rake assets:precompile" do
  cwd release_path
  command "bundle exec rake assets:precompile"
  environment "RAILS_ENV" =&gt; rails_env
end</code></pre><p><strong>deploy/after_restart.rb</strong></p><pre><code>rails_env = new_resource.environment["RAILS_ENV"]

Chef::Log.info("Migrating database for RAILS_ENV=#{rails_env}...")
execute "rake db:migrate" do
  cwd release_path
  command "bundle exec rake db:migrate"
  environment "RAILS_ENV" =&gt; rails_env
end

Chef::Log.info("Seeding database for RAILS_ENV=#{rails_env}...")
execute "rake db:seed" do
  cwd release_path
  command "bundle exec rake db:seed"
  environment "RAILS_ENV" =&gt; rails_env
end</code></pre><p>I encourage you to play with AWS OpsWorks. With a very simple interface, almost any developer can model and map the components of their applications. Plus you can take advantage and use other services available on AWS Cloud. For example, <strong>CloudWatch</strong> to monitor your applications and resources, creating custom alarms, or using hundreds of metrics and graphs. Also, take a look at RDS database monitoring options &#8212; you will be amazed. Also, there are many other services available. I can't list all of them here.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0Z_B!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31c8dda5-0229-4a17-85f7-b67bc555b78f_1104x783.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0Z_B!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31c8dda5-0229-4a17-85f7-b67bc555b78f_1104x783.png 424w, https://substackcdn.com/image/fetch/$s_!0Z_B!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31c8dda5-0229-4a17-85f7-b67bc555b78f_1104x783.png 848w, https://substackcdn.com/image/fetch/$s_!0Z_B!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31c8dda5-0229-4a17-85f7-b67bc555b78f_1104x783.png 1272w, https://substackcdn.com/image/fetch/$s_!0Z_B!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31c8dda5-0229-4a17-85f7-b67bc555b78f_1104x783.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0Z_B!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31c8dda5-0229-4a17-85f7-b67bc555b78f_1104x783.png" width="1104" height="783" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/31c8dda5-0229-4a17-85f7-b67bc555b78f_1104x783.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:783,&quot;width&quot;:1104,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!0Z_B!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31c8dda5-0229-4a17-85f7-b67bc555b78f_1104x783.png 424w, https://substackcdn.com/image/fetch/$s_!0Z_B!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31c8dda5-0229-4a17-85f7-b67bc555b78f_1104x783.png 848w, https://substackcdn.com/image/fetch/$s_!0Z_B!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31c8dda5-0229-4a17-85f7-b67bc555b78f_1104x783.png 1272w, https://substackcdn.com/image/fetch/$s_!0Z_B!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31c8dda5-0229-4a17-85f7-b67bc555b78f_1104x783.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>And don't get me wrong: I'm not comparing OpsWorks with Heroku in any way. For me, they are entirely different things. Seriously, I already tested a lot of services out there, and I continue checking every time something new appears on the scene. However, I was not able to find a competitor to beat Heroku's ease of use. All analogies in this post are to help "Heroku-only" developers to understand OpsWorks vocabulary and how it works.</p><p>The simplicity of OpsWorks makes it a reliable option for delivering your applications. Highly productive, powerful, and flexible, supporting any software that you would like to use with scripted installations.</p><p>If you are looking for another choice to deliver your applications, definitely it's time to give AWS OpsWorks a chance.</p>]]></content:encoded></item><item><title><![CDATA[Ruby GC Tuning Parameters]]></title><description><![CDATA[Quick question: how many GC tuning environment variables have been available for you since Ruby 2.1.1?]]></description><link>https://logs.tomasmuller.dev/p/ruby-gc-tuning-parameters</link><guid isPermaLink="false">https://logs.tomasmuller.dev/p/ruby-gc-tuning-parameters</guid><dc:creator><![CDATA[Tomás Müller]]></dc:creator><pubDate>Thu, 18 Dec 2014 16:27:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/2bea98e1-9170-4030-8468-bda84b537bb6_2000x1333.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Fg2P!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cbe0b4d-f316-4d04-89eb-08c7b4f7e5c8_2000x1333.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Fg2P!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cbe0b4d-f316-4d04-89eb-08c7b4f7e5c8_2000x1333.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Fg2P!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cbe0b4d-f316-4d04-89eb-08c7b4f7e5c8_2000x1333.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Fg2P!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cbe0b4d-f316-4d04-89eb-08c7b4f7e5c8_2000x1333.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Fg2P!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cbe0b4d-f316-4d04-89eb-08c7b4f7e5c8_2000x1333.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Fg2P!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cbe0b4d-f316-4d04-89eb-08c7b4f7e5c8_2000x1333.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8cbe0b4d-f316-4d04-89eb-08c7b4f7e5c8_2000x1333.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Fg2P!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cbe0b4d-f316-4d04-89eb-08c7b4f7e5c8_2000x1333.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Fg2P!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cbe0b4d-f316-4d04-89eb-08c7b4f7e5c8_2000x1333.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Fg2P!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cbe0b4d-f316-4d04-89eb-08c7b4f7e5c8_2000x1333.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Fg2P!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cbe0b4d-f316-4d04-89eb-08c7b4f7e5c8_2000x1333.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>Eleven. This is the right answer. At least for now where the latest Ruby version is 2.1.5 this number remains the same. <a href="https://github.com/ruby/ruby/blob/v2_1_5/gc.c#L5702">Check the source for yourself</a>.</p><p>So what?</p><p>Now and then I keep hearing Ruby programmers complaining about the memory usage increase when switching from Ruby 2.0 to Ruby 2.1. The objective of this blog post is to help those people who are stuck on MRI 2.0, giving an overview of each one of the GC tuning parameters, and explaining how you can benefit your app by using them.</p><p>Sorry, this kind of thing does not exist when the subject is memory profiling or performance tuning. There's no silver bullet or one answer for all. <a href="https://www.youtube.com/watch?v=wBoRkg5-Ieg">Just profile, experiment, analyze and repeat</a>. A lot.</p><p>"Everything in Ruby is an object" - you probably already heard this statement. So, a running Ruby program is made up of objects. Thousands of them. Thanks to the <strong>G</strong>arbage <strong>C</strong>ollector you don't have to manage memory allocation manually.</p><p>Each major release of Ruby has included improvements to the garbage collector, most of them about speed. This is because for the GC to do its work it needs to pause the program execution while it is running.</p><p>Since the birth of Ruby its garbage collector is based on the Mark &amp; Sweep algorithm, which is made up of two phases (Effective Ruby, 2014):</p><ol><li><p>Traverse the object graph (the Ruby heap). Objects that are still reachable from your code are considered alive and subsequently <strong>marked</strong>.</p></li><li><p>Next, any objects that were not marked in the first phase are considered garbage and <em>swept</em> away, releasing memory back to Ruby and possibly to the operating system.</p></li></ol><p>Now, can you imagine how expensive is to traverse the object graph of a running program with thousands and thousands of objects? That's why the team behind this complex piece of software engineering concentrates so much effort on seeking quality and performance improvements.</p><p>Here's the history of CRuby's GC evolution (Incremental GC for Ruby interpreter, K.Sasada, 2014):</p><ul><li><p>1993/12 Ruby 0.9: Conservative mark and sweep GC<br>&#8195;- Simple algorithm<br>&#8195;- Easy to implement C extensions</p></li><li><p>2011/10 Ruby 1.9.3: Lazy sweep To reduce pause time on sweep</p></li><li><p>2013/02 Ruby 2.0: Bitmap marking</p></li><li><p>2013/12 Ruby 2.1: RGenGC</p></li></ul><p>According to with K.Sasada work, we can expect another major update on Ruby 2.2 GC which among other things, will introduce incremental GC algorithm into major GC to reduce long pause time (RincGC: Restricted Incremental GC algorithms).</p><p>The garbage collector in Ruby 2.1 introduced the Generational GC, using Mark &amp; Sweep algorithm to maintain the Ruby heap classifying objects into two categories (generations).</p><p>This technique treats new objects differently than older ones. A new (young) object is one that has just been created by your program. An old object (also referenced as a <em>mature</em> object) is one that will continue to be used by your program.</p><p>The reason behind this categorization is based on the weak generational hypothesis, which means: "most objects die young". In other words, new objects will probably die young. Having this "life expectancy" differences between new and old objects, different GC algorithms and strategies can be applied to each <em>generation</em>.</p><p>Enter the <em>minor</em> and <em>major</em> GC runs:</p><ul><li><p>Minor GC: should be faster than major GC, run often, traversing only the young objects.</p></li><li><p>Major GC: traverses the whole object graph, including the old generation.</p></li></ul><p>To classify an object as young or old the GC does the following:</p><ol><li><p>On a minor GC, when it marks an object in the mark phase, it promotes the object to the old generation (remember: when an object is marked, means that the object will survive the current GC run).</p></li><li><p>Sweep away unmarked objects.</p></li><li><p>During the next minor GC, the entire old generation section will be ignored by the GC, traversing only the young generation.</p></li></ol><p>Above is just the basics for you to understand the GC tuning parameters topic below. The entire garbage collection process implemented in Ruby 2.1 (RGenGC) is much more complex in order to solve all existing problems. For a detailed view of how it works take a look at this post's references.</p><p>When Ruby 2.2 goes out we can expect another major update to the garbage collector, which will probably introduce a three-generation GC.</p><p>Before we continue let's see some key points about the Ruby heap:</p><ul><li><p>The heap is the memory managed by Ruby and to which we have access.</p></li><li><p>The heap is divided into pages and slots.</p></li><li><p>Each heap <strong>page</strong> holds 408 slots (<code>GC::INTERNAL_CONSTANTS[:HEAP_OBJ_LIMIT]</code>).</p></li><li><p>Internally, a <a href="https://github.com/ruby/ruby/blob/v2_1_5/gc.c#L330">Ruby object is represented as a struct called RVALUE</a></p></li><li><p>Each heap <strong>slot</strong> has the size of one RVALUE, which is 40 bytes (<code>GC::INTERNAL_CONSTANTS[:RVALUE_SIZE]</code>). So, sufficient size to hold one Ruby object.</p></li><li><p>The size of each heap <strong>page</strong> is around 16kb (<code>408 * 40 = 16320 bytes</code>).</p></li></ul><p>Ok, now we have the minimum amount of information to understand the existent GC tuning environment variables.</p><p>Following the idea of each application should be treated as a unique case, there are defaults set on Ruby VM that you have to know and likely want to adjust according to the app weight, pondering with the computing resources at your disposal.</p><p>Just to be clear, each one of these GC tuning parameters has a default value, meaning that if you don't set this variable on your application environment, the default value will take place.</p><p>So, if your app starts running out of memory after upgrading to Ruby 2.1, this is a good place to start.</p><p>In the case of a Rails application, there are a lot of other things that you can do (and probably will have to), most of them related to the application server. Later in this post, I'll give a bonus topic for those who are using Unicorn as an application server.</p><p>Roll up your sleeves. Below is the explanation behind each GC tuning variable available today.</p><ol><li><p><strong>RUBY_GC_HEAP_INIT_SLOTS</strong>: Initial number of slots allocated on Ruby's heap. Increasing this value from its default can reduce GC activity during application boot. Default value: 10000</p></li><li><p><strong>RUBY_GC_HEAP_FREE_SLOTS</strong>: After a GC execution, the minimum number of free slots that should be available. Default value: 4096</p></li><li><p><strong>RUBY_GC_HEAP_GROWTH_FACTOR</strong> (new from 2.1): When the heap needs to be expanded, allocate slots by this factor: <code>(next slots number) = (current slots number) * (this factor)</code>. Default value: 1.8</p></li><li><p><strong>RUBY_GC_HEAP_GROWTH_MAX_SLOTS</strong> (new from 2.1): Set the maximum number of slots that Ruby is allowed to add to the heap at once. When disabled, Ruby uses the heap growth factor to determine by how much to grow the heap. Default value: 0 (meaning that this limit is disabled)</p></li><li><p><strong>RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR</strong> (new from 2.1.1): Factor to control major GC timing, given by: <code>control threshold = this factor * number of old objects from the last major GC</code>. The <code>control threshold</code> is compared with the current number of old objects. If the <code>control threshold</code> is exceeded by the number of old objects since the last major marking phase, another major GC will happen. Special note: if you want to disable generational garbage collection, you can specify 0.9 (any number lesser than 1.0). Default value: 2.0 (meaning that old objects have to double to trigger another major GC)</p></li><li><p><strong>RUBY_GC_MALLOC_LIMIT</strong>:<strong> </strong>Internally the VM keeps track of <code>malloc_increase</code>, which is the number of bytes that have been allocated but not yet freed. This is effectively the memory growth of the process. This parameter holds the minimum value for <code>GC.stat[:malloc_limit]</code>. If <code>malloc_increase</code> exceeds the <code>malloc_limit</code> a minor GC is triggered. Default value: 16777216 (16MB)</p></li><li><p><strong>RUBY_GC_MALLOC_LIMIT_MAX</strong> (new from 2.1): Sets the maximum value for <code>GC.stat[:malloc_limit]</code> from going too high (<code>malloc_limit</code> changes dynamically by the growth factor below, tuning number 8). To remove this upper limit set the value 0. Default value: 33554432 (32MB)</p></li><li><p><strong>RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR</strong> (new from 2.1): The growth factor that controls the <code>GC.stat[:malloc_limit]</code> value grows over time. Given by: <code>new malloc_limit = current malloc_limit * this factor</code>. Default value: 1.4</p></li><li><p><strong>RUBY_GC_OLDMALLOC_LIMIT</strong> (new from 2.1): The old generation memory growth is tracked separately by the VM in <code>oldmalloc_increase</code>. This parameter holds the minimum value for <code>GC.stat[:oldmalloc_limit]</code>. If <code>oldmalloc_increase</code> exceeds the <code>oldmalloc_limit</code> a major GC is triggered. Default value: 16777216 (16MB)</p></li><li><p><strong>RUBY_GC_OLDMALLOC_LIMIT_MAX</strong> (new from 2.1): Sets the maximum value for <code>GC.stat[:oldmalloc_limit]</code> from going to high (<code>oldmalloc_limit</code> changes dynamically by the growth factor below, tuning number 11). Default value: 134217728 (128MB)</p></li><li><p><strong>RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR</strong> (new from 2.1): The growth factor that controls the <code>GC.stat[:oldmalloc_limit]</code> value grows over time. Given by: <code>new oldmalloc_limit = current oldmalloc_limit * this factor</code>. Default value: 1.2</p></li></ol><p><strong>Obsolete:</strong></p><ul><li><p><code>RUBY_FREE_MIN -&gt; RUBY_GC_HEAP_FREE_SLOTS</code> (from 2.1)</p></li><li><p><code>RUBY_HEAP_MIN_SLOTS -&gt; RUBY_GC_HEAP_INIT_SLOTS</code> (from 2.1)</p></li></ul><p>Again: all listed environment variables above are for Ruby 2.1. Ruby 2.2 will probably have an impact on these settings. Tune them only if you really know what you are doing.</p><blockquote><p>"One thing I have learned in a long life: that all our science, measured against reality, is primitive and childlike - and yet it is the most precious thing we have." &#8213; Albert Einstein</p></blockquote><p>Think about. This Einstein's quote fits perfectly here.</p><p>In order to accomplish this hard work surround yourself with great tools. A few examples below:</p><ul><li><p>Ruby <code>GC::stat</code> - Return a hash with interesting information about the garbage collector.</p></li><li><p>Ruby <code>ObjectSpace</code> - The ObjectSpace module contains a number of routines that interact with the garbage collection facility and allow you to traverse all living objects with an iterator. The objspace library extends the ObjectSpace module and adds several methods to get internal statistical information about object/memory management.</p></li><li><p><a href="https://newrelic.com/">NewRelic</a> - The Ruby VMs tab under monitoring menu gives a good set of graphics about the situation of memory and garbage collection.</p></li><li><p><a href="https://www.librato.com/">Librato</a></p></li><li><p><a href="https://www.skylight.io/">Skylight</a></p></li><li><p><a href="https://devcenter.heroku.com/articles/metrics">Heroku App Dashboard Metrics tab</a></p></li><li><p><a href="https://github.com/ko1/gc_tracer">gc_tracer</a></p></li><li><p><a href="https://github.com/SamSaffron/memory_profiler">memory_profiler</a></p></li><li><p><a href="http://rbkit.codemancers.com/">Rbkit - Ruby Profiler</a></p></li><li><p><a href="https://github.com/schneems/derailed_benchmarks">Derailed Benchmarks</a></p></li></ul><p>Also, the <a href="http://blog.skylight.io/hunting-for-leaks-in-ruby/">What I Learned About Hunting Memory Leaks in Ruby 2.1</a> blog post from Peter W. describes an approach to collect and analyze heap dumps from your running application. Heap dumps are the devil's house. Precious raw data lives there.</p><p>There's a technique called Out-of-band GC (OOBGC) created to optimize the GC process for web applications. The main idea is to run the GC in-between requests rather than during requests. This technique was first introduced by <a href="http://unicorn.bogomips.org/Unicorn/OobGC.html">Unicorn</a>.</p><p>Traditionally the OOBGC implementations force a GC run every <strong>N requests</strong>. This works well however, it does not observe if the GC actually is needed.</p><p><a href="https://github.com/tmm1">Aman Gupta</a> created a more intelligent OOBGC implementation built on new APIs and events offered in Ruby 2.1 and its RGenGC implementation, allowing the VM to make the best decision about when a garbage collection is required.</p><p>The installation is pretty straightforward:</p><ol><li><p>Add the <a href="https://github.com/tmm1/gctools">gctools</a> gem to your Gemfile and bundle install it.</p></li><li><p>Edit the <code>config.ru</code> file adding lines 3 and 4:</p></li></ol><pre><code># (...)
require 'gctools/oobgc'
use(GC::OOB::UnicornMiddleware)
run Rails.application</code></pre><p><strong>Note for Ruby 2.2:</strong></p><p>If you are using Ruby 2.2 you don't need OOBGC, since it includes the RIncGC (Incremental GC) and <a href="https://github.com/tmm1/gctools/issues/11">will not pause the world for a long time</a>.</p><p>Here we scratched the surface of the most important part of Ruby internals - the Garbage Collection. This invention first appeared <a href="http://en.wikipedia.org/wiki/Garbage_collection_%28computer_science%29">around 1959</a>, and since then it continues to be heavily researched, evolving over time through the development of complex algorithms and techniques.</p><p>We also saw how the existent GC tuning environment variables can impact your application performance, but more important than blindly trying random values on these parameters is to know your application and the environment where it is running.</p><p>That said, in this context, you can't manage what you don't measure and monitor. That's why you have to configure a good set of profiling tools, monitor the memory growth of your application processes, discover the pain points of your application, fix what can be fixed, and repeat.</p><p>I hope this rundown can be useful for you.</p>]]></content:encoded></item><item><title><![CDATA[Starting with graph databases using Neo4j and Rails]]></title><description><![CDATA[Graphs are data structures that are highly useful for understanding and represent many real-world problems in all areas, such as business, government, and science.]]></description><link>https://logs.tomasmuller.dev/p/starting-with-graph-databases-using-neo4j-and-rails</link><guid isPermaLink="false">https://logs.tomasmuller.dev/p/starting-with-graph-databases-using-neo4j-and-rails</guid><dc:creator><![CDATA[Tomás Müller]]></dc:creator><pubDate>Thu, 24 Jul 2014 16:21:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/a338fe64-a0a8-476c-a55f-8bb3163d0a8b_2000x2000.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0J6q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf2f8289-00ec-4d08-8996-53fdcd5261ba_2000x2000.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0J6q!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf2f8289-00ec-4d08-8996-53fdcd5261ba_2000x2000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!0J6q!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf2f8289-00ec-4d08-8996-53fdcd5261ba_2000x2000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!0J6q!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf2f8289-00ec-4d08-8996-53fdcd5261ba_2000x2000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!0J6q!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf2f8289-00ec-4d08-8996-53fdcd5261ba_2000x2000.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0J6q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf2f8289-00ec-4d08-8996-53fdcd5261ba_2000x2000.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cf2f8289-00ec-4d08-8996-53fdcd5261ba_2000x2000.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0J6q!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf2f8289-00ec-4d08-8996-53fdcd5261ba_2000x2000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!0J6q!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf2f8289-00ec-4d08-8996-53fdcd5261ba_2000x2000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!0J6q!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf2f8289-00ec-4d08-8996-53fdcd5261ba_2000x2000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!0J6q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf2f8289-00ec-4d08-8996-53fdcd5261ba_2000x2000.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>Graphs are data structures that are highly useful for understanding and represent many real-world problems in all areas, such as business, government, and science.</p><p>To take advantage of graph databases we don't need to take a master's in Graph Theory. Instead of that, we must understand what a graph is, and be able to build one by drawing it on paper.</p><p>Mathematically speaking, a graph is just a collection of vertices and edges. Or if you don't like math, a set of nodes and relationships that connect them. Graphs represent entities with nodes (vertices), and the way that entities relate to each other is expressed by relationships (edges).</p><p>If you stop now and think about it, this structure allows us to model countless scenarios, from commercial systems to more complex problems such as optimization algorithms.</p><p>This graph model is formally known as a Property Graph. A property graph has the following characteristics:</p><ul><li><p>It contains nodes and relationships.</p></li><li><p>Nodes contain properties (key-value pairs).</p></li><li><p>Relationships are named and directed, and <strong>always</strong> have a start and end node.</p></li><li><p>Relationships can also contain properties (key-value pairs).</p></li></ul><p>Despite being intuitive and easy to understand, the property graph model can be used to describe almost all graph use cases.</p><p>As you probably are thinking, graph databases use the graph model to store data as a graph, with a structure consisting of vertices and edges, the two entities used to model any graph. In addition, you can use all the algorithms from the long history of graph theory to solve graph problems in less time than using relational database queries. I'll be covering some of them in my next posts.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-E7k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84c2b8d7-e300-46a4-8e7f-ac4761939f4e_1425x1800.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-E7k!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84c2b8d7-e300-46a4-8e7f-ac4761939f4e_1425x1800.png 424w, https://substackcdn.com/image/fetch/$s_!-E7k!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84c2b8d7-e300-46a4-8e7f-ac4761939f4e_1425x1800.png 848w, https://substackcdn.com/image/fetch/$s_!-E7k!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84c2b8d7-e300-46a4-8e7f-ac4761939f4e_1425x1800.png 1272w, https://substackcdn.com/image/fetch/$s_!-E7k!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84c2b8d7-e300-46a4-8e7f-ac4761939f4e_1425x1800.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-E7k!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84c2b8d7-e300-46a4-8e7f-ac4761939f4e_1425x1800.png" width="1425" height="1800" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/84c2b8d7-e300-46a4-8e7f-ac4761939f4e_1425x1800.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1800,&quot;width&quot;:1425,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!-E7k!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84c2b8d7-e300-46a4-8e7f-ac4761939f4e_1425x1800.png 424w, https://substackcdn.com/image/fetch/$s_!-E7k!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84c2b8d7-e300-46a4-8e7f-ac4761939f4e_1425x1800.png 848w, https://substackcdn.com/image/fetch/$s_!-E7k!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84c2b8d7-e300-46a4-8e7f-ac4761939f4e_1425x1800.png 1272w, https://substackcdn.com/image/fetch/$s_!-E7k!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84c2b8d7-e300-46a4-8e7f-ac4761939f4e_1425x1800.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">What is a graph database?</figcaption></figure></div><p>Beyond the image above, and now talking specifically about Neo4j, it is an <a href="https://github.com/neo4j">open-source</a> graph database supported by <a href="http://www.neotechnology.com/">Neo Technology</a>, that stores data using the Property Graph model. It is reliable, with full ACID transactions, expressive, with a powerful, human-readable graph query language called Cypher, and simple, accessible by a convenient REST interface or an object-oriented Java API.</p><p>Enough theory and talking for now. Let's prepare our environment to play a little with Neo4j, and build a simple Rails application.</p><p>Installing Neo4j on development machines is very easy. If you are on OSX and is using brew, go ahead and issue <code>brew install neo4j</code> on a terminal window.</p><p>Or, if you prefer, follow these five steps:</p><ol><li><p><a href="http://www.neo4j.org/download">Download the Neo4j Community package</a>.</p></li><li><p>Unzip on your installations folder, let's say <code>~/Applications/</code>.</p></li><li><p>Create a symbolic link named <code>neo4j</code> to the unzipped folder. For instance: <code>ln -s ~/Applications/neo4j-community-2.1.2 ~/Applications/neo4j</code>.</p></li><li><p>Create an environment variable named <code>NEO4J_HOME</code> pointing to this symbolic link.</p></li><li><p>Change the <code>PATH</code> environment variable, adding the <code>NEO4J_HOME/bin</code> to it.</p></li></ol><p>This way, in the future, when you want to update the Neo4j database on your machine, you can just download the new version, unpack, and update the symbolic link pointing it to the new version.</p><p>When you have it installed, open a terminal window and type: <code>neo4j start</code>. This command will start the Neo4j server on your machine. Now go check it on your browser accessing <code>http://localhost:7474/</code>. You'll be presented with a super nice administration panel, where you can visualize the data stored on your Neo4j instance, manipulate data using the Cypher Query Language, check all instance configurations, and more.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RPhI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06484dca-9863-4dfb-9225-02737b5bf1ce_968x774.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RPhI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06484dca-9863-4dfb-9225-02737b5bf1ce_968x774.png 424w, https://substackcdn.com/image/fetch/$s_!RPhI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06484dca-9863-4dfb-9225-02737b5bf1ce_968x774.png 848w, https://substackcdn.com/image/fetch/$s_!RPhI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06484dca-9863-4dfb-9225-02737b5bf1ce_968x774.png 1272w, https://substackcdn.com/image/fetch/$s_!RPhI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06484dca-9863-4dfb-9225-02737b5bf1ce_968x774.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RPhI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06484dca-9863-4dfb-9225-02737b5bf1ce_968x774.png" width="968" height="774" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/06484dca-9863-4dfb-9225-02737b5bf1ce_968x774.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:774,&quot;width&quot;:968,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!RPhI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06484dca-9863-4dfb-9225-02737b5bf1ce_968x774.png 424w, https://substackcdn.com/image/fetch/$s_!RPhI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06484dca-9863-4dfb-9225-02737b5bf1ce_968x774.png 848w, https://substackcdn.com/image/fetch/$s_!RPhI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06484dca-9863-4dfb-9225-02737b5bf1ce_968x774.png 1272w, https://substackcdn.com/image/fetch/$s_!RPhI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06484dca-9863-4dfb-9225-02737b5bf1ce_968x774.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Neo4j Admin - Fresh Install</figcaption></figure></div><p>Neo4j is built on top of Java and the rock-solid JVM. As we want to use (MRI) Ruby on Rails here, let's connect our app using Neo4j's REST API.</p><p>To make things simpler, we'll use the awesome gem (surprisingly) called <a href="http://rubygems.org/gems/neo4j">neo4j</a> from <a href="https://github.com/andreasronge/">@andreasronge</a>. Version 2.x is the stable version. But here we will use it directly from the master branch where version three is <a href="https://github.com/andreasronge/neo4j/wiki/Neo4j-v3">under active development</a>, which enables us to use the MRI Ruby connecting to Neo4j via its REST interface. If you are into JRuby, you can even use the stable version and connect using the embedded DB (by filesystem), which means a Neo4j instance running on the same JVM of your app.</p><p>Another option to connect an MRI Ruby application to Neo4j using the REST interface is the <a href="https://github.com/maxdemarzi/neography/">Neography</a> gem from <a href="https://github.com/maxdemarzi">@maxdemarzi</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3KG9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F871240b4-1c2f-4c4b-b12c-ca7703ad7e40_1208x832.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3KG9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F871240b4-1c2f-4c4b-b12c-ca7703ad7e40_1208x832.png 424w, https://substackcdn.com/image/fetch/$s_!3KG9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F871240b4-1c2f-4c4b-b12c-ca7703ad7e40_1208x832.png 848w, https://substackcdn.com/image/fetch/$s_!3KG9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F871240b4-1c2f-4c4b-b12c-ca7703ad7e40_1208x832.png 1272w, https://substackcdn.com/image/fetch/$s_!3KG9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F871240b4-1c2f-4c4b-b12c-ca7703ad7e40_1208x832.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3KG9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F871240b4-1c2f-4c4b-b12c-ca7703ad7e40_1208x832.png" width="1208" height="832" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/871240b4-1c2f-4c4b-b12c-ca7703ad7e40_1208x832.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:832,&quot;width&quot;:1208,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!3KG9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F871240b4-1c2f-4c4b-b12c-ca7703ad7e40_1208x832.png 424w, https://substackcdn.com/image/fetch/$s_!3KG9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F871240b4-1c2f-4c4b-b12c-ca7703ad7e40_1208x832.png 848w, https://substackcdn.com/image/fetch/$s_!3KG9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F871240b4-1c2f-4c4b-b12c-ca7703ad7e40_1208x832.png 1272w, https://substackcdn.com/image/fetch/$s_!3KG9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F871240b4-1c2f-4c4b-b12c-ca7703ad7e40_1208x832.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Neo4j Admin - With data</figcaption></figure></div><p>This blog post is intended to introduce you to the world of Graph Databases, giving some basic theory about graphs and a practical hands-on using Neo4j and Rails.</p><p>For future posts expect to read more about Neo4j, Cypher Query Language, and traversal algorithms.</p><p>So, what about learning by doing? And remember: graphs are everywhere!</p>]]></content:encoded></item><item><title><![CDATA[Neo4j Heroku Challenge Winner and Finalists]]></title><description><![CDATA[The entries have been entered, the votes have been cast and now it is time to announce the winners of the Neo4j Heroku Challenge!]]></description><link>https://logs.tomasmuller.dev/p/neo4j-heroku-challenge-winner-and-finalists</link><guid isPermaLink="false">https://logs.tomasmuller.dev/p/neo4j-heroku-challenge-winner-and-finalists</guid><dc:creator><![CDATA[Tomás Müller]]></dc:creator><pubDate>Mon, 19 Mar 2012 03:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/45e97422-592b-499f-a03d-d9b85485953a_2000x1333.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qtXx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e11ca5-1baa-45a0-8f69-924892cacb38_2000x1333.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qtXx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e11ca5-1baa-45a0-8f69-924892cacb38_2000x1333.jpeg 424w, https://substackcdn.com/image/fetch/$s_!qtXx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e11ca5-1baa-45a0-8f69-924892cacb38_2000x1333.jpeg 848w, https://substackcdn.com/image/fetch/$s_!qtXx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e11ca5-1baa-45a0-8f69-924892cacb38_2000x1333.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!qtXx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e11ca5-1baa-45a0-8f69-924892cacb38_2000x1333.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qtXx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e11ca5-1baa-45a0-8f69-924892cacb38_2000x1333.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/14e11ca5-1baa-45a0-8f69-924892cacb38_2000x1333.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qtXx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e11ca5-1baa-45a0-8f69-924892cacb38_2000x1333.jpeg 424w, https://substackcdn.com/image/fetch/$s_!qtXx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e11ca5-1baa-45a0-8f69-924892cacb38_2000x1333.jpeg 848w, https://substackcdn.com/image/fetch/$s_!qtXx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e11ca5-1baa-45a0-8f69-924892cacb38_2000x1333.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!qtXx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e11ca5-1baa-45a0-8f69-924892cacb38_2000x1333.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>We had <a href="http://neo4j.com/blog/heroku-challengers-vote-now/">some amazing entries from all over the globe</a> and from a diverse set of languages, and we thank everyone for participating. The overall winner will receive the new iPad, and the language winners receive <a href="http://www.thinkgeek.com/">$100 to ThinkGeek</a>.<br><br>The proud new owner of a new iPad, and our overall winner of the Neo4j Heroku Challenge is&#8230;<br><br><strong>Luanne Misquitta, Flavorwocky</strong></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BdmG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74bb209f-af73-4e69-9533-2e71535aed9f_964x634.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BdmG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74bb209f-af73-4e69-9533-2e71535aed9f_964x634.png 424w, https://substackcdn.com/image/fetch/$s_!BdmG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74bb209f-af73-4e69-9533-2e71535aed9f_964x634.png 848w, https://substackcdn.com/image/fetch/$s_!BdmG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74bb209f-af73-4e69-9533-2e71535aed9f_964x634.png 1272w, https://substackcdn.com/image/fetch/$s_!BdmG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74bb209f-af73-4e69-9533-2e71535aed9f_964x634.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BdmG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74bb209f-af73-4e69-9533-2e71535aed9f_964x634.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/74bb209f-af73-4e69-9533-2e71535aed9f_964x634.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!BdmG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74bb209f-af73-4e69-9533-2e71535aed9f_964x634.png 424w, https://substackcdn.com/image/fetch/$s_!BdmG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74bb209f-af73-4e69-9533-2e71535aed9f_964x634.png 848w, https://substackcdn.com/image/fetch/$s_!BdmG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74bb209f-af73-4e69-9533-2e71535aed9f_964x634.png 1272w, https://substackcdn.com/image/fetch/$s_!BdmG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74bb209f-af73-4e69-9533-2e71535aed9f_964x634.png 1456w" sizes="100vw"></picture><div></div></div></a></figure></div><p>Flavorwocky won the most votes among the Neo4j community and highest ranking on Gensen, recognized for creativity, usability, and all around awesomeness. A big congrats from the whole Neo4j community to you Luanne &#8211; we are trying to get that iPad to India as soon as possible!</p><h3><strong>Language Winners</strong></h3><p><br><strong>FlattrWhat &#8211; Ruby by <a href="https://github.com/simon">Simon Gate</a></strong><br>The &#8220;Flattr what?&#8221; application is a super simple recommendation engine for Flattr users, a micro-payment/tipping system for bloggers and other content creators.<br><br><strong><a href="https://frostymug.herokuapp.com/">FrostyMug</a> &#8211; PHP by <a href="http://blog.everymansoftware.com/">Josh Adell</a></strong><br>Frosty Mug helps navigate the vast ocean of beers on tap at a proper brew pub. Start with a beer you like, then Frosty Mug will recommend similar beers.<br><br><strong><a href="http://neoflix.herokuapp.com/">Neoflix: Browser</a> &#8211; Javascript by <a href="http://maxdemarzi.com/">Max de Marzi</a></strong><br>In a great spin on the classic Neo4j dataset, Neoflix explores a movie database with D3-powered visualizations to make film recommendations.<br><br><strong>Node Neo4j Template &#8211; Node.js by <a href="https://github.com/aseemk/node-neo4j-template">Aseem Kishore</a></strong><br>The Node template demonstrates how easily Node.js developers can tap into the power of Neo4j for creating a social graph.<br><br><strong>Flask Demo App &#8211; Python by <a href="http://www.ioncannon.net/">Carson McDonald</a></strong><br>The app is a simple data explorer with bookmarking ability, using user-generated content from Stack Exchange.<br><br><strong><a href="http://scala-plays-with-neo4j.herokuapp.com/">Scala Plays with Neo4j</a> &#8211; Scala by <a href="http://ska-la.blogspot.com/">Andy Petrella</a></strong><br>With this through-the-web edited social graph, Andy demonstrates how easy it is to access Neo4j from Scala while running on the Play framework.<br><br><strong>GraphTag &#8211; Clojure by Aran Elkington</strong><br>Graphtag is a Clojure application which monitors Twitter mentions against a custom account &#8220;GraphTag&#8221; and inputs the content of those tweets into Neo4j as a node.<br><br><strong>Neo4jOrbust &#8211; C# by <a href="http://romikoderbynew.com/">Romiko Derbynew</a></strong><br>Neo4j or Bust is an impressive mashup of Heroku (to access Neo4j) and AppHarbor (to host the .NET application), to discover and follow interesting people.</p><blockquote><p>&#127942; <strong>NeoQuotes &#8211; Java by <a href="https://tomasmuller.dev/talking-to-neo4j-graphs/">Tom&#225;s M&#252;ller</a></strong><br>NeoQuotes delivers a stock-exchange symbol lookup application, with integrated voice search!</p></blockquote><p><strong><a href="http://bulbflow.herokuapp.com/">Lightbulb</a> &#8211; Python by James Thornton</strong><br>Lightbulb is a a Git-powered, Neo4j-backed blog engine for Heroku, using James&#8217; own excellent <a href="http://bulbflow.com/">Bulbs</a> framework for <a href="http://neo4j.com/blog/why-graph-databases-are-the-future/?ref=blog/#definition">graph databases</a>.</p><p>I have to give a shout-out to the Neo4j Community Team and our friends at Heroku for this awesome contest, and for all of the great work put in.</p><p>Till next time! &#128075;</p>]]></content:encoded></item><item><title><![CDATA[Talking to Neo4j graphs]]></title><description><![CDATA[In this post, I will cover all the main details regarding developing my entry for the Neo4j Challenge.]]></description><link>https://logs.tomasmuller.dev/p/talking-to-neo4j-graphs</link><guid isPermaLink="false">https://logs.tomasmuller.dev/p/talking-to-neo4j-graphs</guid><dc:creator><![CDATA[Tomás Müller]]></dc:creator><pubDate>Sun, 12 Feb 2012 16:14:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/48276625-c867-4aef-8e20-7af483cb7982_2000x1500.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oeZ-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44573f82-0118-4937-ac19-a429de0236a4_2000x1500.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oeZ-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44573f82-0118-4937-ac19-a429de0236a4_2000x1500.jpeg 424w, https://substackcdn.com/image/fetch/$s_!oeZ-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44573f82-0118-4937-ac19-a429de0236a4_2000x1500.jpeg 848w, https://substackcdn.com/image/fetch/$s_!oeZ-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44573f82-0118-4937-ac19-a429de0236a4_2000x1500.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!oeZ-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44573f82-0118-4937-ac19-a429de0236a4_2000x1500.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oeZ-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44573f82-0118-4937-ac19-a429de0236a4_2000x1500.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/44573f82-0118-4937-ac19-a429de0236a4_2000x1500.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!oeZ-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44573f82-0118-4937-ac19-a429de0236a4_2000x1500.jpeg 424w, https://substackcdn.com/image/fetch/$s_!oeZ-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44573f82-0118-4937-ac19-a429de0236a4_2000x1500.jpeg 848w, https://substackcdn.com/image/fetch/$s_!oeZ-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44573f82-0118-4937-ac19-a429de0236a4_2000x1500.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!oeZ-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44573f82-0118-4937-ac19-a429de0236a4_2000x1500.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>In this post, I will cover all the main details regarding developing my entry for the <a href="http://neo4j-challenge.herokuapp.com/">Neo4j Challenge</a>.</p><p>The main objective of this challenge is to create a Heroku-ready template or demo application using Neo4j. So, I thought to myself: &#8212; what kind of application would be nice to show up in this contest?</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tTUh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8e579c9-ca9b-4c51-8b4a-6f294f734cae_90x29.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tTUh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8e579c9-ca9b-4c51-8b4a-6f294f734cae_90x29.png 424w, https://substackcdn.com/image/fetch/$s_!tTUh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8e579c9-ca9b-4c51-8b4a-6f294f734cae_90x29.png 848w, https://substackcdn.com/image/fetch/$s_!tTUh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8e579c9-ca9b-4c51-8b4a-6f294f734cae_90x29.png 1272w, https://substackcdn.com/image/fetch/$s_!tTUh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8e579c9-ca9b-4c51-8b4a-6f294f734cae_90x29.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tTUh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8e579c9-ca9b-4c51-8b4a-6f294f734cae_90x29.png" width="90" height="29" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c8e579c9-ca9b-4c51-8b4a-6f294f734cae_90x29.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:29,&quot;width&quot;:90,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Neoquotes Logo&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Neoquotes Logo" title="Neoquotes Logo" srcset="https://substackcdn.com/image/fetch/$s_!tTUh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8e579c9-ca9b-4c51-8b4a-6f294f734cae_90x29.png 424w, https://substackcdn.com/image/fetch/$s_!tTUh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8e579c9-ca9b-4c51-8b4a-6f294f734cae_90x29.png 848w, https://substackcdn.com/image/fetch/$s_!tTUh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8e579c9-ca9b-4c51-8b4a-6f294f734cae_90x29.png 1272w, https://substackcdn.com/image/fetch/$s_!tTUh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8e579c9-ca9b-4c51-8b4a-6f294f734cae_90x29.png 1456w" sizes="100vw"></picture><div></div></div></a><figcaption class="image-caption">Neoquotes Logo</figcaption></figure></div><p>After many ideas, here it is! In short, the application is a Stock Exchange symbol lookup using Neo4j and your voice.</p><p>Ok! Let me explain what I did.</p><p>My main objective was to try to combine all the <a href="http://rubykaigi.org/2011/en/schedule/details/16M01">joy of Ruby programming</a>, with the power and ease of use provided by the awesome <a href="http://www.springsource.org/spring-data/neo4j">Spring Data Neo4j</a>, Spring Framework, and JVM, to this amazing piece of software called Neo4j which let me curious since 2011 in the Jim Webber talk at QCON conference of that year in S&#227;o Paulo.</p><p>So, to start things up, <a href="http://jruby.org/">JRuby</a> comes in as a perfect fit. It also allowed me to use <a href="http://www.sinatrarb.com/">Sinatra</a> to easily develop the user interface (both, voice interface and GUI). I choose Sinatra in this case because of its simplicity and as long the application stays small, it beat Rails (<a href="http://rubysource.com/rails-or-sinatra-the-best-of-both-worlds/">as said by @DHH</a>).</p><p>Jetty is another very nice (and old) project, which provides a Web server and javax.servlet container, plus support for Web Sockets, OSGi, JMX, JNDI, JASPI, AJP, and many other integrations, and can be used to develop self-contained applications. In <a href="http://blog.heroku.com/archives/2011/8/25/java/">this article</a> from Heroku blog, we have a nice introduction to creating a Java Web Application using Jetty. Its integration with Maven was also another motivation point.</p><p>Talking about Maven, possibly in <code>POM.xml</code> resides one of the trickiest parts of the application.</p><p>After configuring all the dependencies, <code>aspectj-maven-plugin</code>, <code>jetty-maven-plugin</code>, let's look at the configuration of <code>appassembler-maven-plugin</code> and <code>jruby-rake-plugin</code>:</p><h4>jruby-rake-plugin:<a href="https://www.tomasmuller.dev/posts/talking-to-neo4j-graphs#jruby-rake-plugin">&#8203;</a></h4><pre><code>&lt;code&gt;
  &lt;executions&gt;
    &lt;execution&gt;
      &lt;id&gt;install-bundler&lt;/id&gt;
      &lt;phase&gt;process-resources&lt;/phase&gt;
      &lt;goals&gt;
        &lt;goal&gt;jruby&lt;/goal&gt;
      &lt;/goals&gt;
      &lt;configuration&gt;
        &lt;args&gt;-S gem install bundler --no-ri --no-rdoc --install-dir vendor/bundle&lt;/args&gt;
      &lt;/configuration&gt;
    &lt;/execution&gt;
    &lt;execution&gt;
      &lt;id&gt;bundle-install&lt;/id&gt;
      &lt;phase&gt;process-resources&lt;/phase&gt;
      &lt;goals&gt;
        &lt;goal&gt;jruby&lt;/goal&gt;
      &lt;/goals&gt;
      &lt;configuration&gt;
        &lt;args&gt;script/bundle install --without development:test&lt;/args&gt;
      &lt;/configuration&gt;
    &lt;/execution&gt;
  &lt;/executions&gt;
&lt;/code&gt;</code></pre><p>As we can see, the plugin execution is responsible for downloading and installing <a href="http://gembundler.com/">Bundler</a>, then next executing the specified script which lives in the application script folder. This script basically starts the Bundler client (pre-configuring <code>ENV['GEM_HOME']</code>, <code>ENV['GEM_PATH']</code> and <code>ENV['BUNDLE_GEMFILE']</code>), and following the parameters, all gems except the ones for development and test groups, are installed.</p><p>Last, but not least, the <code>appassembler-maven-plugin</code>:</p><pre><code>&lt;code&gt;
  &lt;configuration&gt;
    &lt;assembleDirectory&gt;target&lt;/assembleDirectory&gt;
    &lt;extraJvmArguments&gt;-Djruby.native.enabled=false -Djruby.cext.enabled=false -Djruby.compat.version=1.9 -Xmx256m -Xss2048k&lt;/extraJvmArguments&gt;
    &lt;programs&gt;
      &lt;program&gt;
        &lt;mainClass&gt;org.jruby.Main&lt;/mainClass&gt;
        &lt;name&gt;jruby&lt;/name&gt;
      &lt;/program&gt;
    &lt;/programs&gt;
  &lt;/configuration&gt;
&lt;/code&gt;</code></pre><p>Above we have the configuration for the assemble goal execution. The trick here is to generate the executable with JRuby (<code>org.jruby.Main</code>) as the main class. And you can also set extra JVM arguments, like the version of Ruby language to use.</p><p>After this brief look into <code>POM.xml</code>, let's see how Heroku starts Jetty. This is defined in the <code>Procfile</code>:</p><pre><code>web: sh target/bin/jruby -S config/jetty.rb</code></pre><p>So, that's it! Now our app is running on Heroku Cloud! For more details take a look into <a href="https://github.com/tomasmuller/neoquotes/blob/master/config/jetty.rb">/config/jetty.rb</a> to see how Jetty is being started up and <a href="https://github.com/tomasmuller/neoquotes/tree/master/src/main/webapp/WEB-INF">/src/main/webapp/WEB-INF/web.xml</a> to see the JRuby-Rack configuration. Other technical details you can find in the <a href="https://github.com/tomasmuller/neoquotes">application readme</a> file.</p><p>Now, let's talk about Neo4j.</p><p>Back in 2011, <a href="http://jimwebber.org/">Jim Webber</a> let me very interested in Neo4j with his talk at QCONSP 2011. Maybe because that year I was working on my final graduation project &#8212; a recommender system to support fundamental analysis of the Stock Market using natural language processing, and I never forgot one thing that he said. More or less like this: "MapReduce processing between HTTP requests can be very expensive! Some problems cannot be resolved with this approach.". Well, I did this. And I must confess: a graph database would have changed a lot my project.</p><p>So, when I discovered this challenge, I decided "- ok, now is the time to take a closer look at Neo4j.".</p><p>The first thing I did was access the Neo4j download page and downloaded the 1.6 Community version.</p><p>After that, the server installation. It is super easy. Just follow these instructions.</p><p>So, what's the next step? Learn, learn and learn!</p><p>I was able to make some initial experiments very fast after a <a href="http://en.wikipedia.org/wiki/Speed_reading">dynamic reading</a> of this superb online resource: <a href="http://static.springsource.org/spring-data/data-neo4j/docs/2.0.0.RELEASE/reference/html/">Good Relationships: The Spring Data Neo4j Guide Book</a>. Thank's to <a href="https://twitter.com/mesirii">Michael Hunger</a>, <a href="https://twitter.com/dmontag">David Montag</a>, <a href="https://twitter.com/akollegger">Andreas Kollegger</a> for this material.</p><p>Also, I learned a lot from the following links:</p><p>And that's it. This is the magic of open-source software. Curious about how one given thing works? Go to the source code and read it! All you need to get started with Neo4j is this. Trust me ; )</p><p>I used <a href="http://arborjs.org/">Arbor.js</a> library to display the Neo4j data to the user. But how differently the information can be visualized? Through voice interfaces of course!</p><p>As I already worked with <a href="http://en.wikipedia.org/wiki/Voice_user_interface">Voice Interfaces</a> some years ago (in 2008 actually, when I won the Sun Students Contest. More info <a href="https://www.tomasmuller.dev/posts/grand-prize-winner-sun-student-reviews-contest/">here</a> and <a href="https://www.tomasmuller.dev/posts/powering-what-you-dont-see-with-glassfish-and-mysql/">here</a>), this was not a mystery. Again I used <a href="http://www.w3.org/TR/voicexml21/">VoiceXML</a> markup language, and the free service provided by <a href="http://evolution.voxeo.com/">Voxeo</a>. Refer to the links above for more information about VoiceXML.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!INtf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18eeeace-975e-4a90-b5a1-e97209e3c4d9_621x154.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!INtf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18eeeace-975e-4a90-b5a1-e97209e3c4d9_621x154.png 424w, https://substackcdn.com/image/fetch/$s_!INtf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18eeeace-975e-4a90-b5a1-e97209e3c4d9_621x154.png 848w, https://substackcdn.com/image/fetch/$s_!INtf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18eeeace-975e-4a90-b5a1-e97209e3c4d9_621x154.png 1272w, https://substackcdn.com/image/fetch/$s_!INtf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18eeeace-975e-4a90-b5a1-e97209e3c4d9_621x154.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!INtf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18eeeace-975e-4a90-b5a1-e97209e3c4d9_621x154.png" width="621" height="154" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/18eeeace-975e-4a90-b5a1-e97209e3c4d9_621x154.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:154,&quot;width&quot;:621,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Neoquotes Diagram - Who is talking with who? (a high-level view of NeoQuotes architecture)&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Neoquotes Diagram - Who is talking with who? (a high-level view of NeoQuotes architecture)" title="Neoquotes Diagram - Who is talking with who? (a high-level view of NeoQuotes architecture)" srcset="https://substackcdn.com/image/fetch/$s_!INtf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18eeeace-975e-4a90-b5a1-e97209e3c4d9_621x154.png 424w, https://substackcdn.com/image/fetch/$s_!INtf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18eeeace-975e-4a90-b5a1-e97209e3c4d9_621x154.png 848w, https://substackcdn.com/image/fetch/$s_!INtf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18eeeace-975e-4a90-b5a1-e97209e3c4d9_621x154.png 1272w, https://substackcdn.com/image/fetch/$s_!INtf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18eeeace-975e-4a90-b5a1-e97209e3c4d9_621x154.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption">Neoquotes Diagram - Who is talking with who? (a high-level view of NeoQuotes architecture)</figcaption></figure></div><p>Sinatra eased this part even more, through its easiness of building APIs-like applications. All VXML interfaces and grammars are below the <code>/app/views/voice</code> folder and there are only two voice routes in <code>application.rb</code>.</p><p>Using the Voxeo service is simple. You only need to <a href="https://evolution.voxeo.com/account/register.jsp">create an account</a>, and after that, set up a new application through Voxeo Application Manager, informing the voice entry point of your application (the full URL to your route. In my case to the get <code>/voice/index.vxml</code> route).</p><p>Note that your application needs to be accessible by Voxeo server. So don't forget to git push Heroku master, before setting up your app on Voxeo.</p><p>Finishing, you'll get the numbers for your application. I always tested with a Skype VoIP number. Works very well here in Brazil and is totally free.</p><p>You can try calling to <a href="http://neoquotes.herokuapp.com/">NeoQuotes</a>! This is the Skype VoIP number: <code>+990009369996189207</code> (to see all available numbers for NeoQuotes access the application and click on the respective top nav bar link).</p><p><strong>Important</strong>: When you spell the stock symbol, say one letter, and wait for the voice to repeat what you just said. And go on. When you finish, you can say "Okay!", "Go!" or "Search!" for example. If the symbol has an '.' you have to say "dot". The voice will not repeat this (will stay in silence). Just continue with the letters or numbers.</p><p>This demo application contains an interesting stack of technologies, providing an environment to develop robust and testable Neo4j applications, with a lot of fun. Certainly much more can be done and improved. And you can help <a href="https://github.com/tomasmuller/neoquotes/issues">fill issues</a> and submit <a href="https://github.com/tomasmuller/neoquotes/pulls">pull requests</a>!</p><p>Please, help to vote in this application at <a href="http://gensen.herokuapp.com/show/24">Gensen repository</a> or help with a <a href="https://twitter.com/intent/tweet?hashtags=neo4jchallenge&amp;original_referer=http%3A%2F%2Fneoquotes.herokuapp.com%2F&amp;source=tweetbutton&amp;text=NeoQuotes%20application%2C%20by%20%40tomasmuller%20for%20Neo4j%20Challenge%20-%20Seed%20the%20cloud%20-%20https%3A%2F%2Fgithub.com%2Ftomasmuller%2Fneoquotes&amp;url=http%3A%2F%2Fneoquotes.herokuapp.com%2F">Tweet</a>!</p><p>Thank you Neo4j for running this challenge! It was so enjoyable to participate.</p>]]></content:encoded></item><item><title><![CDATA[Grand prize winner Sun Student Reviews Contest]]></title><description><![CDATA[Yesterday the final results for the MySQL and GlassFish Sun Student Reviews Contest were published! Guess who the grand prize winner for the general students' category is?]]></description><link>https://logs.tomasmuller.dev/p/grand-prize-winner-sun-student-reviews-contest</link><guid isPermaLink="false">https://logs.tomasmuller.dev/p/grand-prize-winner-sun-student-reviews-contest</guid><dc:creator><![CDATA[Tomás Müller]]></dc:creator><pubDate>Mon, 24 Nov 2008 15:06:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/fbe68701-9291-4208-bf64-c090f2432baa_2000x1264.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yIDC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feda2d2ed-636a-41ef-ac09-142a44ea551a_2000x1264.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yIDC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feda2d2ed-636a-41ef-ac09-142a44ea551a_2000x1264.jpeg 424w, https://substackcdn.com/image/fetch/$s_!yIDC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feda2d2ed-636a-41ef-ac09-142a44ea551a_2000x1264.jpeg 848w, https://substackcdn.com/image/fetch/$s_!yIDC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feda2d2ed-636a-41ef-ac09-142a44ea551a_2000x1264.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!yIDC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feda2d2ed-636a-41ef-ac09-142a44ea551a_2000x1264.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yIDC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feda2d2ed-636a-41ef-ac09-142a44ea551a_2000x1264.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/eda2d2ed-636a-41ef-ac09-142a44ea551a_2000x1264.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!yIDC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feda2d2ed-636a-41ef-ac09-142a44ea551a_2000x1264.jpeg 424w, https://substackcdn.com/image/fetch/$s_!yIDC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feda2d2ed-636a-41ef-ac09-142a44ea551a_2000x1264.jpeg 848w, https://substackcdn.com/image/fetch/$s_!yIDC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feda2d2ed-636a-41ef-ac09-142a44ea551a_2000x1264.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!yIDC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feda2d2ed-636a-41ef-ac09-142a44ea551a_2000x1264.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>Hohoho! Christmas is coming early this year!</p><p>Yesterday the <a href="http://blogs.sun.com/students/entry/mysql_and_glassfish_student_reviews">final results</a> for the <a href="http://blogs.sun.com/students/entry/participate_in_mysql_glassfish_student">MySQL and GlassFish Sun Student Reviews Contest</a> were published! Guess who the grand prize winner for the general students' category is? Yes! It's me! Thank you all and congratulations to all participants!</p><p>Below is the winners' list:</p><h3>General students category<a href="https://www.tomasmuller.dev/posts/grand-prize-winner-sun-student-reviews-contest#general-students-category">&#8203;</a></h3><p><strong>Grand Prize Winner: Tomas Augusto Muller</strong>, Universidade de Santa Cruz do Sul, Brazil (<a href="https://www.tomasmuller.dev/posts/powering-what-you-dont-see-with-glassfish-and-mysql/">review</a>, <a href="https://voicehotel.dev.java.net/">project</a>).</p><p>Second Prize Winners:</p><ul><li><p>Ronaldo Prass, Unisinos, Brazil (<a href="http://ronaldoprass.blog.com/4057717/">review</a>, <a href="https://gejun.dev.java.net/source/browse/gejun/">project</a>) (shared with Vitorio Sassi)</p></li><li><p>Tejesh Morla, University of Illinois at Springfield, USA (<a href="http://tejesh-techgeek.blogspot.com/">review</a>, <a href="https://customer.dev.java.net/source/browse/customer/">project</a>)</p></li><li><p>Varun Bhatia, University of Delhi, India (<a href="http://varun-dumca.blogspot.com/">review</a>, <a href="https://stockmarket.dev.java.net/source/browse/stockmarket">project</a>)</p></li></ul><h3>Sun Java Ambassadors category<a href="https://www.tomasmuller.dev/posts/grand-prize-winner-sun-student-reviews-contest#sun-java-ambassadors-category">&#8203;</a></h3><p><strong>Grand Prize Winner: Kolli Bharath</strong>, Dhirubhai Ambani Institute of Information and Communication Technology, India (<a href="http://bharath-studentcontest.blogspot.com/">review</a>, <a href="https://projectroshni.dev.java.net/servlets/ProjectDocumentList">project</a>)</p><p>Second Prize Winners:</p><ul><li><p>Aadhar Mittal, Delhi University, India (<a href="http://blogs.sun.com/aadhar/entry/guide_to_a_first_implementation">review</a>, <a href="https://competitionmanagement.dev.java.net/servlets/ProjectDocumentList">project</a>)</p></li><li><p>Jose Maria Silveira Neto, Universidade Federal do Cear&#225;, Brazil (<a href="http://silveiraneto.net/2008/10/21/short-urls-with-glassfishmysql/">review</a>, <a href="https://xort.dev.java.net/servlets/ProjectDocumentList">project</a>)</p></li><li><p>Qi Cao, Wuhan University, China (<a href="http://blogs.sun.com/greysh/entry/test_management_system">review</a>, <a href="https://greysh.dev.java.net/">project</a>)</p></li><li><p>Vitorio Sassi, UFRGS, Brazil (<a href="http://blogs.sun.com/vitoriosassi/entry/student_reviews_contest">review</a>, <a href="https://gejun.dev.java.net/source/browse/gejun/">project</a>) (shared with Ronaldo Prass)</p></li></ul>]]></content:encoded></item><item><title><![CDATA[Powering what you don't see with Glassfish and Mysql]]></title><description><![CDATA[In this post, I'll give you a general view of how to make a basic hotel reservation application with VoiceXML and how to run it using Glassfish and MySQL.]]></description><link>https://logs.tomasmuller.dev/p/powering-what-you-dont-see-with-glassfish-and-mysql</link><guid isPermaLink="false">https://logs.tomasmuller.dev/p/powering-what-you-dont-see-with-glassfish-and-mysql</guid><dc:creator><![CDATA[Tomás Müller]]></dc:creator><pubDate>Mon, 20 Oct 2008 15:02:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/d12a1278-7ebf-4d23-a653-6084f4250cc4_2000x1333.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!C2W-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84146169-d957-4a2d-8dfe-9c485d652ea8_2000x1333.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!C2W-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84146169-d957-4a2d-8dfe-9c485d652ea8_2000x1333.jpeg 424w, https://substackcdn.com/image/fetch/$s_!C2W-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84146169-d957-4a2d-8dfe-9c485d652ea8_2000x1333.jpeg 848w, https://substackcdn.com/image/fetch/$s_!C2W-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84146169-d957-4a2d-8dfe-9c485d652ea8_2000x1333.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!C2W-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84146169-d957-4a2d-8dfe-9c485d652ea8_2000x1333.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!C2W-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84146169-d957-4a2d-8dfe-9c485d652ea8_2000x1333.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/84146169-d957-4a2d-8dfe-9c485d652ea8_2000x1333.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!C2W-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84146169-d957-4a2d-8dfe-9c485d652ea8_2000x1333.jpeg 424w, https://substackcdn.com/image/fetch/$s_!C2W-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84146169-d957-4a2d-8dfe-9c485d652ea8_2000x1333.jpeg 848w, https://substackcdn.com/image/fetch/$s_!C2W-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84146169-d957-4a2d-8dfe-9c485d652ea8_2000x1333.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!C2W-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84146169-d957-4a2d-8dfe-9c485d652ea8_2000x1333.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>Have you ever heard about VoiceXML? It's a W3C standard that allows you to build web pages that are accessed by voice! It can be used for services like travel tickets selling, hotel reservation, bank account information, and so on. In this post, I'll give you a general view of how to make a basic hotel reservation application with VoiceXML and how to run it using Glassfish and MySQL.</p><p>First of all, for VoiceXML applications, we need a voice gateway. I used <a href="https://evolution.voxeo.com/">Voxeo</a>, it's free and provides you with phone numbers that you can call for free using <a href="https://www.skype.com/">Skype</a>. All you need to do is create a free account in Voxeo and register your application by informing its URL. After registering you'll receive the unique phone number for your application.</p><p>I'll assume you already know the basics of VoiceXML syntax for the rest of this post. If you are completely new to VoiceXML, I recommend you <a href="https://www.w3.org/TR/voicexml21/">check it out first</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!B2LQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe26592ea-8a21-4523-b26f-021f3b9a0c9a_500x400.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!B2LQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe26592ea-8a21-4523-b26f-021f3b9a0c9a_500x400.jpeg 424w, https://substackcdn.com/image/fetch/$s_!B2LQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe26592ea-8a21-4523-b26f-021f3b9a0c9a_500x400.jpeg 848w, https://substackcdn.com/image/fetch/$s_!B2LQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe26592ea-8a21-4523-b26f-021f3b9a0c9a_500x400.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!B2LQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe26592ea-8a21-4523-b26f-021f3b9a0c9a_500x400.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!B2LQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe26592ea-8a21-4523-b26f-021f3b9a0c9a_500x400.jpeg" width="500" height="400" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e26592ea-8a21-4523-b26f-021f3b9a0c9a_500x400.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:400,&quot;width&quot;:500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!B2LQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe26592ea-8a21-4523-b26f-021f3b9a0c9a_500x400.jpeg 424w, https://substackcdn.com/image/fetch/$s_!B2LQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe26592ea-8a21-4523-b26f-021f3b9a0c9a_500x400.jpeg 848w, https://substackcdn.com/image/fetch/$s_!B2LQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe26592ea-8a21-4523-b26f-021f3b9a0c9a_500x400.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!B2LQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe26592ea-8a21-4523-b26f-021f3b9a0c9a_500x400.jpeg 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The user may use his phone to call the voice gateway. The voice gateway will interact as a voice browser, saying the output of the VoiceXML application and waiting for user inputs. It'll use the internet to access the VoiceXML application, which is deployed in a <a href="https://glassfish.dev.java.net/">Glassfish V2 UR2</a> application server for dynamic content generation. The database used to store user information and hotel reservation is a <a href="https://www.mysql.com/">MySQL 5.1 Community edition</a>.</p><p>The Glassfish server receives the voice gateway HTTP requests, queries the MySQL database, and returns a web page with the requested information in VoiceXML syntax. Normally it'll be a form with some input fields that must be filed by the user. The voice gateway then asks for user input and, once it's finished, the information is sent back to the Glassfish server as an HTTP GET or POST.</p><p>Another option for the user is to access it right from the web. In my hotel reservation example, only the query for hotel reservations is allowed.</p><p>On the Glassfish side, I used the Java Server Pages technology with the <a href="http://www.springframework.org/">Spring framework</a> and <a href="http://ibatis.apache.org/">iBATIS</a> as the Data Mapper framework. From Spring, we used <a href="http://static.springframework.org/spring/docs/2.5.x/reference/mvc.html">Spring MVC</a> for the MVC design pattern, <a href="http://static.springframework.org/spring/docs/2.5.x/reference/orm.html#orm-ibatis">Spring ORM</a> for the Spring Dao pattern, and the <a href="http://martinfowler.com/articles/injection.html">dependency injection pattern</a>.</p><p>This allowed me to easily create the application with all the functionality I needed. Note that the MVC pattern was applied in the voice application and the acronym is still valid. Instead of <strong>V</strong>iew, we have <strong>V</strong>oice. For database modeling, <a href="http://dev.mysql.com/workbench/">MySQL Workbench</a> was used, a great free tool for this task.</p><p>The database ended up looking up like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cEBV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9167a782-3129-4c90-8e2c-0ee27d4aac3f_800x600.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cEBV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9167a782-3129-4c90-8e2c-0ee27d4aac3f_800x600.png 424w, https://substackcdn.com/image/fetch/$s_!cEBV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9167a782-3129-4c90-8e2c-0ee27d4aac3f_800x600.png 848w, https://substackcdn.com/image/fetch/$s_!cEBV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9167a782-3129-4c90-8e2c-0ee27d4aac3f_800x600.png 1272w, https://substackcdn.com/image/fetch/$s_!cEBV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9167a782-3129-4c90-8e2c-0ee27d4aac3f_800x600.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cEBV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9167a782-3129-4c90-8e2c-0ee27d4aac3f_800x600.png" width="800" height="600" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9167a782-3129-4c90-8e2c-0ee27d4aac3f_800x600.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:600,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!cEBV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9167a782-3129-4c90-8e2c-0ee27d4aac3f_800x600.png 424w, https://substackcdn.com/image/fetch/$s_!cEBV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9167a782-3129-4c90-8e2c-0ee27d4aac3f_800x600.png 848w, https://substackcdn.com/image/fetch/$s_!cEBV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9167a782-3129-4c90-8e2c-0ee27d4aac3f_800x600.png 1272w, https://substackcdn.com/image/fetch/$s_!cEBV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9167a782-3129-4c90-8e2c-0ee27d4aac3f_800x600.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>another MySQL Workbench screenshot, showing the VoiceHotel Database Model</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JDbv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9ae0eea-9406-40cc-bf6a-10c97584a741_800x808.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JDbv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9ae0eea-9406-40cc-bf6a-10c97584a741_800x808.png 424w, https://substackcdn.com/image/fetch/$s_!JDbv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9ae0eea-9406-40cc-bf6a-10c97584a741_800x808.png 848w, https://substackcdn.com/image/fetch/$s_!JDbv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9ae0eea-9406-40cc-bf6a-10c97584a741_800x808.png 1272w, https://substackcdn.com/image/fetch/$s_!JDbv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9ae0eea-9406-40cc-bf6a-10c97584a741_800x808.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JDbv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9ae0eea-9406-40cc-bf6a-10c97584a741_800x808.png" width="800" height="808" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a9ae0eea-9406-40cc-bf6a-10c97584a741_800x808.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:808,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!JDbv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9ae0eea-9406-40cc-bf6a-10c97584a741_800x808.png 424w, https://substackcdn.com/image/fetch/$s_!JDbv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9ae0eea-9406-40cc-bf6a-10c97584a741_800x808.png 848w, https://substackcdn.com/image/fetch/$s_!JDbv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9ae0eea-9406-40cc-bf6a-10c97584a741_800x808.png 1272w, https://substackcdn.com/image/fetch/$s_!JDbv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9ae0eea-9406-40cc-bf6a-10c97584a741_800x808.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Once the database is created, you just need to generate the SQL script from MySQL Workbench and execute it with MySQL. The CLI command to do this is:</p><pre><code>mysql -u user -p &lt; script_file.sql</code></pre><p>where <code>user</code> is the database user and <code>-p</code> will ask you for the password.</p><p>Other options are using your IDE for doing this or MySQL Workbench. The main Java IDEs are NetBeans and Eclipse. Both IDEs allow the connection with MySQL and the automatic deployment in Glassfish. Using Eclipse the deployment can be done by coding a deploy task in one Ant build file or using another mechanism like Maven. This makes application development even faster.</p><p>With MySQL Workbench we have a set of nice and useful features like Import Reverse Engineer MySQL create a script for importing a database from script, allowing us to edit this new database, and save these modifications. The databases can be exported using a feature called Forward Engineer SQL CREATE/ALTER script. Another nice thing is the visual insert editor, making it a lot easier pre-populate the database with data.</p><p>With the Java and the VoiceXML code ready, we need to put it online. With Glassfish, all you need to do is either, use your IDE to deploy it with one click or put the <code>.war</code> file in the autodeploy folder.</p><p>Another possibility is to make use of Glassfish Admin Console. With one Glassfish "fresh installation" this can be done by accessing <code>http://localhost:4848/</code>. The default administrator username is <code>admin</code> and the default password for admin user is <code>adminadmin</code>.</p><p>After logging in, all you need to do is access the Web Applications section, click on the deploy button, select your <code>war</code> file and that's it.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lRBi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27740d7a-6199-4890-b9d1-e0cda543c9b3_800x704.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lRBi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27740d7a-6199-4890-b9d1-e0cda543c9b3_800x704.png 424w, https://substackcdn.com/image/fetch/$s_!lRBi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27740d7a-6199-4890-b9d1-e0cda543c9b3_800x704.png 848w, https://substackcdn.com/image/fetch/$s_!lRBi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27740d7a-6199-4890-b9d1-e0cda543c9b3_800x704.png 1272w, https://substackcdn.com/image/fetch/$s_!lRBi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27740d7a-6199-4890-b9d1-e0cda543c9b3_800x704.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lRBi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27740d7a-6199-4890-b9d1-e0cda543c9b3_800x704.png" width="800" height="704" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/27740d7a-6199-4890-b9d1-e0cda543c9b3_800x704.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:704,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!lRBi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27740d7a-6199-4890-b9d1-e0cda543c9b3_800x704.png 424w, https://substackcdn.com/image/fetch/$s_!lRBi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27740d7a-6199-4890-b9d1-e0cda543c9b3_800x704.png 848w, https://substackcdn.com/image/fetch/$s_!lRBi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27740d7a-6199-4890-b9d1-e0cda543c9b3_800x704.png 1272w, https://substackcdn.com/image/fetch/$s_!lRBi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27740d7a-6199-4890-b9d1-e0cda543c9b3_800x704.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>VoiceXML applications are a great way to give more than one access possibility to a service and using Glassfish and MySQL for developing this kind of application is incredibly easy. You barely take notice of them :)</p><p>You can access the voice hotel application by calling <code>+990009369996079911</code> from Skype and visualize the data by accessing <a href="http://inf.unisc.br/tmuller/viewDatabaseTableData.voice">the backoffice admin panel</a>.</p><p>The project is located at <a href="http://voicehotel.dev.java.net/">http://voicehotel.dev.java.net</a> and you can download all the source code. More information you can find <a href="https://voicehotel.dev.java.net/source/browse/voicehotel/trunk/artifacts/documentation/">here</a>.</p>]]></content:encoded></item></channel></rss>