Jekyll2017-07-13T20:24:55-05:00https://jeremykratz.com/Jeremy KratzFull-Stack Web Developer using Ruby on Rails, C#, and JavaScriptUsing React with Rails and Webpack2017-07-13T00:00:00-05:002017-07-13T00:00:00-05:00https://jeremykratz.com/blog/using-react-with-rails-and-webpack<p><img src="/img/posts/2017-07-13/using-react-with-rails-and-webpack-800.png" srcset="/img/posts/2017-07-13/using-react-with-rails-and-webpack-800.png 1x, /img/posts/2017-07-13/using-react-with-rails-and-webpack-1600.png 2x" alt="Using React with Rails and Webpack" /></p>
<p>I first got started with <a href="https://facebook.github.io/react/">React</a> back in 2015, and my initial experience was pretty negative. I had no experience using <a href="https://webpack.github.io/">Webpack</a> or <a href="https://babeljs.io/">Babel</a>, so setting up my local dev environment was an error-filled nightmare. It probably took a solid week before I was able to run the code samples in React’s getting started guide.</p>
<p>Luckily, it’s much easier to get up and running with React since the release of <a href="https://github.com/facebookincubator/create-react-app">create-react-app</a>, a Node package that hides away all the tedious configuration that was previously required. New React devs can start coding their first apps in mere minutes instead of days.</p>
<p>It’s also been difficult to integrate React and Webpack into a traditional Rails project, as Rails has its own built-in asset pipeline for managing front-end resources. But this is no longer the case - Webpack is now a first-class citizen in <a href="http://edgeguides.rubyonrails.org/5_1_release_notes.html">Rails 5.1</a>, which means that using React with Rails is now easier than ever. Let’s take a look at how to configure Rails with Webpack and React.</p>
<h2 id="update-your-system">Update Your System</h2>
<p>First, make sure the following tools are up-to-date on your local development machine:</p>
<ul>
<li><a href="https://rvm.io/rubies/installing">Ruby</a> (2.2.5+)</li>
<li><a href="https://rubygems.org/pages/download">RubyGems</a> (2.6.4+)</li>
<li><a href="http://guides.rubyonrails.org/getting_started.html">Rails</a> (5.1.2+)</li>
<li><a href="https://nodejs.org/en/download/">Node.js</a> (6.11.1+)</li>
<li><a href="https://yarnpkg.com/en/docs/install">Yarn</a> (0.27.5+)</li>
</ul>
<h2 id="create-or-update-your-project">Create or Update Your Project</h2>
<p>Get started by creating a new Rails project with the <code class="highlighter-rouge">--webpack=react</code> option:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">rails new sampleapp --webpack<span class="o">=</span>react</code></pre></figure>
<p>Or, if you’d like to add React to an existing Rails app, add <code class="highlighter-rouge">gem 'webpacker'</code> to your <code class="highlighter-rouge">Gemfile</code>, then run:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">bundle install
rails webpacker:install
rails webpacker:install:react</code></pre></figure>
<p>You’ll notice a ton of packages being installed in your command window - these are all development tools required by Webpack and React. When the install process finishes, Webpack and React will be ready to use in your Rails app!</p>
<h2 id="start-the-development-server">Start the Development Server</h2>
<p>You’d typically use <code class="highlighter-rouge">rails server</code> to start a local Rails dev server to view your app in a web browser. When using Webpack, you’ll actually need to spin up two servers - one for Rails and one for the Webpack build process. This adds a bit of overhead, but in return you get nice features such as live code reloading in your browser as you make changes to your front-end code.</p>
<p>We’ll use <a href="https://ddollar.github.io/foreman">Foreman</a> to start both servers with a single command. First, install the Foreman gem:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">gem install foreman</code></pre></figure>
<p>Next, create a new file in your Rails app root named <code class="highlighter-rouge">Procfile</code> with this content:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">web: bundle <span class="nb">exec </span>rails s
webpacker: ./bin/webpack-dev-server</code></pre></figure>
<p>After saving your <code class="highlighter-rouge">Procfile</code>, run the Foreman command in your project root directory:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">foreman start</code></pre></figure>
<p>Foreman will run both the commands listed in your <code class="highlighter-rouge">Procfile</code>. Two local servers should start up: your Rails dev server at <code class="highlighter-rouge">http://localhost:5000</code> and your Webpack dev server at <code class="highlighter-rouge">http://localhost:8080</code> (check the output of the <code class="highlighter-rouge">foreman start</code> command to see if your dev servers are running on different port numbers).</p>
<p>You should now be able to open <code class="highlighter-rouge">http://localhost:5000</code> in a web browser and see your Rails app.</p>
<h2 id="add-a-sample-controller-and-view">Add a Sample Controller and View</h2>
<p>If you created a brand new Rails app at the beginning of this exercise, go ahead and create a new controller and view:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">rails generate controller Home index</code></pre></figure>
<p>This will create a new <code class="highlighter-rouge">HomeController</code> with an <code class="highlighter-rouge">index</code> action, accessible at <code class="highlighter-rouge">GET /home/index</code>. Let’s simply make this the root route by editing <code class="highlighter-rouge">/config/routes.rb</code>:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">routes</span><span class="p">.</span><span class="nf">draw</span> <span class="k">do</span>
<span class="n">root</span> <span class="ss">to: </span><span class="s1">'home#index'</span>
<span class="k">end</span></code></pre></figure>
<p>Refresh your browser and you’ll see your <code class="highlighter-rouge">home#index</code> view.</p>
<p>Note: If you’re adding React to an existing Rails app, you can use any of your existing <code class="highlighter-rouge">GET</code> routes for the rest of this exercise.</p>
<h2 id="add-a-pack-tag-to-your-layout">Add a Pack Tag to Your Layout</h2>
<p>You’ll notice a new directory in your Rails app named <code class="highlighter-rouge">/app/javascript/</code> - any files in this folder will be compiled automatically by Webpack, and it’s where you’ll create your React components.</p>
<p>You’ll notice a sample file in <code class="highlighter-rouge">/app/javascript/packs</code> named <code class="highlighter-rouge">application.js</code> (if you created a brand new app, you’ll also see <code class="highlighter-rouge">hello_react.jsx</code>). Any JS files in your <code class="highlighter-rouge">packs</code> folder are referred to as “packs,” and they serve as the entry points for your front-end code. Make sure you have an <code class="highlighter-rouge">application.js</code> file with the following content:</p>
<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'Hello World from Webpacker'</span><span class="p">);</span></code></pre></figure>
<h3 id="important-note-on-javascript-folders">Important Note on JavaScript Folders</h3>
<p>If you take a look in <code class="highlighter-rouge">/app/assets/</code>, you’ll notice that your app still contains the standard Rails <code class="highlighter-rouge">javascripts</code> folder, which also has a file named <code class="highlighter-rouge">application.js</code>. So why is your JavaScript now split between two separate folders?</p>
<p>The answer is due to the fact that we now have two build processes that compile our JavaScript code:</p>
<ul>
<li><code class="highlighter-rouge">/app/assets/</code> - <strong>Rails</strong> will compile any resources in this folder. JavaScript files in this folder can be included in your views with <code class="highlighter-rouge"><%= javascript_include_tag %></code>.</li>
<li><code class="highlighter-rouge">/app/javascript/</code> - <strong>Webpack</strong> will compile any resources in this folder. JavaScript files in this folder can be included in your views with <code class="highlighter-rouge"><%= javascript_pack_tag %></code>.</li>
</ul>
<p>Simply put, the scripts are kept in separate folders to avoid confusion and to prevent Rails and Webpack from stepping on each other’s toes.</p>
<p>By default, none of your packs are being rendered to your views. Open up <code class="highlighter-rouge">/app/views/layouts/application.html.erb</code> and add the following to the bottom of your <code class="highlighter-rouge"><head></code> tag:</p>
<figure class="highlight"><pre><code class="language-erb" data-lang="erb"><span class="cp"><%=</span> <span class="n">javascript_pack_tag</span> <span class="s1">'application'</span> <span class="cp">%></span></code></pre></figure>
<p>Open your web browser’s development console, then refresh your app. You should now see <code class="highlighter-rouge">Hello World from Webpacker</code> displayed in the console, which means that the Webpack bundler is working with Rails!</p>
<p>Just for fun, open up <code class="highlighter-rouge">/app/javascript/packs/application.js</code> and change the <code class="highlighter-rouge">console.log()</code> message to <code class="highlighter-rouge">'Ready for React!'</code>. As soon as you save the file, your web browser window should automatically refresh, showing the updated message in your console. Our Webpack live reload server is working too!</p>
<h2 id="add-a-react-component">Add a React Component</h2>
<p>We’ve verified that Webpack is working with our <code class="highlighter-rouge">console.log()</code> message, so now let’s verify that React is also working by building a simple component.</p>
<p>Create a new folder under <code class="highlighter-rouge">/app/javascript</code> named <code class="highlighter-rouge">components</code>, then create a new file inside named <code class="highlighter-rouge">SampleComponent.js</code>. Add the following code to the file:</p>
<figure class="highlight"><pre><code class="language-jsx" data-lang="jsx">import React from 'react';
export default class SampleComponent extends React.Component {
render() {
return (
<h1>Hi, I'm SampleComponent!</h1>
);
}
}</code></pre></figure>
<p>This component simply renders a heading tag with text, but note that the component name is exported so it can be imported by other JavaScript files.</p>
<p>Why place components in a separate folder? This is mainly for cleanliness - I recommend separating your React components from your packs based on these rules:</p>
<ul>
<li><code class="highlighter-rouge">/app/javascript/components/</code> - Place your components here.</li>
<li><code class="highlighter-rouge">/app/javascript/packs/</code> - Place your DOM rendering scripts here. This folder should only contain scripts which will be rendered using <code class="highlighter-rouge"><%= javascript_pack_tag %></code>.</li>
</ul>
<p>Think of your packs as bridges between your Rails views and your React components. If you’re building a single-page application, you’ll likely only have a single pack, which handles the rendering of your components with something like React Router.</p>
<p>Now that you have a component, it’s time to render it to the DOM with your pack. Update <code class="highlighter-rouge">/app/javascript/packs/application.js</code> to the following:</p>
<figure class="highlight"><pre><code class="language-jsx" data-lang="jsx">import React from 'react';
import { render } from 'react-dom';
import SampleComponent from '../components/SampleComponent';
document.addEventListener('DOMContentLoaded', () => {
const container = document.body.appendChild(document.createElement('div'));
render(<SampleComponent/>, container);
});</code></pre></figure>
<p>All right! Your pack file is now importing the new <code class="highlighter-rouge">SampleComponent</code> as a module, waiting for the DOM to load, then rendering the component to a new <code class="highlighter-rouge"><div></code> element at the bottom of the page. As soon as you saved <code class="highlighter-rouge">application.js</code>, your browser should have reloaded to show the component:</p>
<p><img src="/img/posts/2017-07-13/screenshot-800.png" srcset="/img/posts/2017-07-13/screenshot-800.png 1x, /img/posts/2017-07-13/screenshot-1600.png 2x" alt="Screenshot of React component" /></p>
<p>If you have React Developer Tools installed as a browser extension, you can also use it to inspect the React component.</p>
<h2 id="learn-more-about-rails-and-webpacker">Learn More about Rails and Webpacker</h2>
<p>You can learn more about the Webpacker gem on GitHub: <a href="https://github.com/rails/webpacker">https://github.com/rails/webpacker</a></p>Monkey Patching Griddler to Add Mailgun Properties2017-07-06T00:00:00-05:002017-07-06T00:00:00-05:00https://jeremykratz.com/blog/monkey-patching-griddler-to-add-mailgun-properties<p><img src="/img/posts/2017-07-06-monkey-patching-griddler-to-add-mailgun-properties-800.png" srcset="/img/posts/2017-07-06-monkey-patching-griddler-to-add-mailgun-properties-800.png 1x, /img/posts/2017-07-06-monkey-patching-griddler-to-add-mailgun-properties-1600.png 2x" alt="Monkey Patching Griddler to Add Mailgun Properties" /></p>
<p><a href="http://griddler.io/">Griddler</a> is a great gem to consider if your Rails application needs to receive email messages. Griddler works by creating an endpoint to receive webhooks from email services like SendGrid and Postmark. Since each email service formats their webhook payloads differently, Griddler adapts each incoming message into a generic <code class="highlighter-rouge">Email</code> class instance.</p>
<p>While this generic class makes it really easy to switch email services without rewriting your code, it does require you to discard any data from incoming messages that Griddler doesn’t include in its <code class="highlighter-rouge">Email</code> class. For example, I prefer to use <a href="https://www.mailgun.com/">Mailgun</a> due to its addition a <a href="http://mailgun-documentation.readthedocs.io/en/latest/user_manual.html#parsed-messages-parameters"><code class="highlighter-rouge">stripped-text</code></a> parameter, which automatically removes quoted text and signature blocks from emails.</p>
<p>When using Griddler out of the box, messages received from Mailgun don’t include the <code class="highlighter-rouge">stripped-text</code> parameter since it’s not a common email field. Luckily, we can use <a href="https://en.wikipedia.org/wiki/Monkey_patch">monkey patching</a> to cleanly extend Griddler and add this functionality just for our app.</p>
<h2 id="install-griddler">Install Griddler</h2>
<p>Let’s first install Griddler in our Rails app by adding the <a href="https://github.com/bradpauly/griddler-mailgun"><code class="highlighter-rouge">griddler-mailgun</code></a> gem to the end of the <code class="highlighter-rouge">Gemfile</code>:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">gem</span> <span class="s1">'griddler-mailgun'</span></code></pre></figure>
<p>Run <code class="highlighter-rouge">bundle install</code> to install the Griddler gem with the Mailgun adapter. Next, add the default Griddler endpoint to <code class="highlighter-rouge">app/config/routes.rb</code>:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">routes</span><span class="p">.</span><span class="nf">draw</span> <span class="k">do</span>
<span class="n">mount_griddler</span>
<span class="k">end</span></code></pre></figure>
<p>This will create an endpoint at <code class="highlighter-rouge">/email_processor</code> - Mailgun will send emails to this route, and they’ll be processed by Griddler.</p>
<p>We also need to add an initializer file to configure Griddler, so create a new file named <code class="highlighter-rouge">app/config/initializers/griddler.rb</code> with the following code:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Griddler</span><span class="p">.</span><span class="nf">configure</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
<span class="n">config</span><span class="p">.</span><span class="nf">email_service</span> <span class="o">=</span> <span class="ss">:mailgun</span>
<span class="k">end</span></code></pre></figure>
<p>Finally, we’ll need to create a class named <code class="highlighter-rouge">EmailProcessor</code> - Griddler needs this to be defined in our app, and it’s where you’ll eventually process email messages after they arrive. You can place this file wherever you prefer in your Rails project, but let’s create it at <code class="highlighter-rouge">lib/email_processor.rb</code>:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">EmailProcessor</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">email</span><span class="p">)</span>
<span class="vi">@email</span> <span class="o">=</span> <span class="n">email</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">process</span>
<span class="c1"># do something with @email here</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></figure>
<p>In the <code class="highlighter-rouge">process</code> method above, the <code class="highlighter-rouge">@email</code> object will contain an email message that was sent from Mailgun, arrived at your app’s <code class="highlighter-rouge">/email_processor</code> endpoint, and was processed by Griddler. You can easily access the contents of the message as attributes, such as <code class="highlighter-rouge">@email.from</code> and <code class="highlighter-rouge">@email.body</code>.</p>
<p>Now let’s extend Griddler to handle Mailgun’s <code class="highlighter-rouge">stripped-text</code> attribute, which is currently simply dropped when an email is processed.</p>
<h2 id="extending-the-mailgun-adapter">Extending the Mailgun Adapter</h2>
<p>Let’s first take a look at the source code of the <a href="https://github.com/bradpauly/griddler-mailgun/blob/master/lib/griddler/mailgun/adapter.rb">Griddler Mailgun adapter class</a>, which processes webhook payloads as they arrive from Mailgun:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">module</span> <span class="nn">Griddler</span>
<span class="k">module</span> <span class="nn">Mailgun</span>
<span class="k">class</span> <span class="nc">Adapter</span>
<span class="kp">attr_reader</span> <span class="ss">:params</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">params</span><span class="p">)</span>
<span class="vi">@params</span> <span class="o">=</span> <span class="n">params</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">normalize_params</span><span class="p">(</span><span class="n">params</span><span class="p">)</span>
<span class="n">adapter</span> <span class="o">=</span> <span class="kp">new</span><span class="p">(</span><span class="n">params</span><span class="p">)</span>
<span class="n">adapter</span><span class="p">.</span><span class="nf">normalize_params</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">normalize_params</span>
<span class="p">{</span>
<span class="ss">to: </span><span class="n">to_recipients</span><span class="p">,</span>
<span class="ss">cc: </span><span class="n">cc_recipients</span><span class="p">,</span>
<span class="ss">bcc: </span><span class="no">Array</span><span class="p">.</span><span class="nf">wrap</span><span class="p">(</span><span class="n">param_or_header</span><span class="p">(</span><span class="ss">:Bcc</span><span class="p">)),</span>
<span class="ss">from: </span><span class="n">determine_sender</span><span class="p">,</span>
<span class="ss">subject: </span><span class="n">params</span><span class="p">[</span><span class="ss">:subject</span><span class="p">],</span>
<span class="ss">text: </span><span class="n">params</span><span class="p">[</span><span class="s1">'body-plain'</span><span class="p">],</span>
<span class="ss">html: </span><span class="n">params</span><span class="p">[</span><span class="s1">'body-html'</span><span class="p">],</span>
<span class="ss">attachments: </span><span class="n">attachment_files</span><span class="p">,</span>
<span class="ss">headers: </span><span class="n">serialized_headers</span>
<span class="p">}</span>
<span class="k">end</span>
<span class="c1"># ...</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></figure>
<p>We’re mainly interested in <code class="highlighter-rouge">normalize_params</code> - this method maps the <code class="highlighter-rouge">params</code> values to a new hash that can be parsed into a Griddler::Email instance. Since we know <code class="highlighter-rouge">stripped-text</code> is included in the params sent from Mailgun, we simply need to add a new key/value pair to the hash returned from <code class="highlighter-rouge">normalize_params</code>.</p>
<p>Let’s create a new module in our app to extend <code class="highlighter-rouge">Griddler::Mailgun::Adapter</code> - add the following code to <code class="highlighter-rouge">app/config/initializers/griddler.rb</code>:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># Add Mailgun stripped-text to normalized params</span>
<span class="k">module</span> <span class="nn">GriddlerMailgunAdapterExtensions</span>
<span class="k">def</span> <span class="nf">normalize_params</span>
<span class="n">normalized_params</span> <span class="o">=</span> <span class="k">super</span>
<span class="n">normalized_params</span><span class="p">[</span><span class="ss">:stripped_text</span><span class="p">]</span> <span class="o">=</span> <span class="n">params</span><span class="p">[</span><span class="s1">'stripped-text'</span><span class="p">]</span>
<span class="n">normalized_params</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="c1"># Prepend custom extensions</span>
<span class="no">Griddler</span><span class="o">::</span><span class="no">Mailgun</span><span class="o">::</span><span class="no">Adapter</span><span class="p">.</span><span class="nf">class_eval</span> <span class="k">do</span>
<span class="n">prepend</span> <span class="no">GriddlerMailgunAdapterExtensions</span>
<span class="k">end</span></code></pre></figure>
<p>Our module contains its own <code class="highlighter-rouge">normalize_params</code> method, which will be used to override the <code class="highlighter-rouge">normalize_params</code> method in <code class="highlighter-rouge">Griddler::Mailgun::Adapter</code>. The <code class="highlighter-rouge">super</code> method calls the original <code class="highlighter-rouge">normalize_params</code> method, which returns a hash object. A new <code class="highlighter-rouge">:stripped_text</code> key is added to the hash, with the value of <code class="highlighter-rouge">params['stripped-text']</code> from the Mailgun payload. The modified hash is then returned.</p>
<p>Our extension module is then applied to <code class="highlighter-rouge">Griddler::Mailgun::Adapter</code> by using <code class="highlighter-rouge">class_eval</code> to “open” the original class. The <code class="highlighter-rouge">prepend</code> method is then used to “mix in” our extension module method.</p>
<p>Now our new <code class="highlighter-rouge">normalize_params</code> code will execute in place of the original method in <code class="highlighter-rouge">Griddler::Mailgun::Adapter</code>, and the resulting hash output will include our new key/value pair.</p>
<h2 id="extending-griddlers-email-class">Extending Griddler’s Email Class</h2>
<p>We also need to monkey patch <code class="highlighter-rouge">Griddler::Email</code> to account for the updates we made to the Adapter. Here’s the relevant <a href="https://github.com/thoughtbot/griddler/blob/master/lib/griddler/email.rb">source code</a>:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">require</span> <span class="s1">'htmlentities'</span>
<span class="k">module</span> <span class="nn">Griddler</span>
<span class="k">class</span> <span class="nc">Email</span>
<span class="kp">include</span> <span class="no">ActionView</span><span class="o">::</span><span class="no">Helpers</span><span class="o">::</span><span class="no">SanitizeHelper</span>
<span class="kp">attr_reader</span> <span class="ss">:to</span><span class="p">,</span>
<span class="ss">:from</span><span class="p">,</span>
<span class="ss">:cc</span><span class="p">,</span>
<span class="ss">:bcc</span><span class="p">,</span>
<span class="ss">:original_recipient</span><span class="p">,</span>
<span class="ss">:reply_to</span><span class="p">,</span>
<span class="ss">:subject</span><span class="p">,</span>
<span class="ss">:body</span><span class="p">,</span>
<span class="ss">:raw_body</span><span class="p">,</span>
<span class="ss">:raw_text</span><span class="p">,</span>
<span class="ss">:raw_html</span><span class="p">,</span>
<span class="ss">:headers</span><span class="p">,</span>
<span class="ss">:raw_headers</span><span class="p">,</span>
<span class="ss">:attachments</span><span class="p">,</span>
<span class="ss">:vendor_specific</span><span class="p">,</span>
<span class="ss">:spam_report</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">params</span><span class="p">)</span>
<span class="vi">@params</span> <span class="o">=</span> <span class="n">params</span>
<span class="vi">@to</span> <span class="o">=</span> <span class="n">recipients</span><span class="p">(</span><span class="ss">:to</span><span class="p">)</span>
<span class="vi">@from</span> <span class="o">=</span> <span class="n">extract_address</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:from</span><span class="p">])</span>
<span class="vi">@subject</span> <span class="o">=</span> <span class="n">extract_subject</span>
<span class="vi">@body</span> <span class="o">=</span> <span class="n">extract_body</span>
<span class="vi">@raw_text</span> <span class="o">=</span> <span class="n">params</span><span class="p">[</span><span class="ss">:text</span><span class="p">]</span>
<span class="vi">@raw_html</span> <span class="o">=</span> <span class="n">params</span><span class="p">[</span><span class="ss">:html</span><span class="p">]</span>
<span class="vi">@raw_body</span> <span class="o">=</span> <span class="vi">@raw_text</span><span class="p">.</span><span class="nf">presence</span> <span class="o">||</span> <span class="vi">@raw_html</span>
<span class="vi">@headers</span> <span class="o">=</span> <span class="n">extract_headers</span>
<span class="vi">@cc</span> <span class="o">=</span> <span class="n">recipients</span><span class="p">(</span><span class="ss">:cc</span><span class="p">)</span>
<span class="vi">@bcc</span> <span class="o">=</span> <span class="n">recipients</span><span class="p">(</span><span class="ss">:bcc</span><span class="p">)</span>
<span class="vi">@original_recipient</span> <span class="o">=</span> <span class="n">extract_address</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:original_recipient</span><span class="p">])</span>
<span class="vi">@reply_to</span> <span class="o">=</span> <span class="n">extract_address</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:reply_to</span><span class="p">])</span>
<span class="vi">@raw_headers</span> <span class="o">=</span> <span class="n">params</span><span class="p">[</span><span class="ss">:headers</span><span class="p">]</span>
<span class="vi">@attachments</span> <span class="o">=</span> <span class="n">params</span><span class="p">[</span><span class="ss">:attachments</span><span class="p">]</span>
<span class="vi">@vendor_specific</span> <span class="o">=</span> <span class="n">params</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:vendor_specific</span><span class="p">,</span> <span class="p">{})</span>
<span class="vi">@spam_report</span> <span class="o">=</span> <span class="n">params</span><span class="p">[</span><span class="ss">:spam_report</span><span class="p">]</span>
<span class="k">end</span>
<span class="c1"># ...</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></figure>
<p>We need to add <code class="highlighter-rouge">:stripped_text</code> to the <code class="highlighter-rouge">attr_reader</code> declaration, and make sure it’s parsed in the <code class="highlighter-rouge">initialize</code> method.</p>
<p>Add this code to <code class="highlighter-rouge">app/config/initializers/griddler.rb</code>:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># Assign Mailgun stripped-text to attribute</span>
<span class="k">module</span> <span class="nn">GriddlerEmailExtensions</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">params</span><span class="p">)</span>
<span class="k">super</span>
<span class="vi">@stripped_text</span> <span class="o">=</span> <span class="n">params</span><span class="p">[</span><span class="ss">:stripped_text</span><span class="p">]</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="c1"># Add attribute for Mailgun stripped-text and prepend custom extensions</span>
<span class="no">Griddler</span><span class="o">::</span><span class="no">Email</span><span class="p">.</span><span class="nf">class_eval</span> <span class="k">do</span>
<span class="kp">attr_reader</span> <span class="ss">:stripped_text</span>
<span class="n">prepend</span> <span class="no">GriddlerEmailExtensions</span>
<span class="k">end</span></code></pre></figure>
<p>We’ve now defined a module with an <code class="highlighter-rouge">initialize</code> method, which uses <code class="highlighter-rouge">super</code> to call the original overridden method. An instance variable named <code class="highlighter-rouge">@stripped_text</code> is declared to store the value of <code class="highlighter-rouge">params[:stripped_text]</code> from our modified adapter (see previous section).</p>
<p>Finally, <code class="highlighter-rouge">class_eval</code> is used to modify the <code class="highlighter-rouge">Griddler::Email</code> class, where we add <code class="highlighter-rouge">:stripped_text</code> to the <code class="highlighter-rouge">attr_reader</code> declarations, then prepend our extension module method.</p>
<h2 id="wrapping-it-up">Wrapping it Up</h2>
<p>Here’s the final version of <code class="highlighter-rouge">app/config/initializers/griddler.rb</code>:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Griddler</span><span class="p">.</span><span class="nf">configure</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
<span class="n">config</span><span class="p">.</span><span class="nf">email_service</span> <span class="o">=</span> <span class="ss">:mailgun</span>
<span class="k">end</span>
<span class="c1"># Add Mailgun stripped-text to normalized params</span>
<span class="k">module</span> <span class="nn">GriddlerMailgunAdapterExtensions</span>
<span class="k">def</span> <span class="nf">normalize_params</span>
<span class="n">normalized_params</span> <span class="o">=</span> <span class="k">super</span>
<span class="n">normalized_params</span><span class="p">[</span><span class="ss">:stripped_text</span><span class="p">]</span> <span class="o">=</span> <span class="n">params</span><span class="p">[</span><span class="s1">'stripped-text'</span><span class="p">]</span>
<span class="n">normalized_params</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="c1"># Prepend custom extensions</span>
<span class="no">Griddler</span><span class="o">::</span><span class="no">Mailgun</span><span class="o">::</span><span class="no">Adapter</span><span class="p">.</span><span class="nf">class_eval</span> <span class="k">do</span>
<span class="n">prepend</span> <span class="no">GriddlerMailgunAdapterExtensions</span>
<span class="k">end</span>
<span class="c1"># Assign Mailgun stripped-text to attribute</span>
<span class="k">module</span> <span class="nn">GriddlerEmailExtensions</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">params</span><span class="p">)</span>
<span class="k">super</span>
<span class="vi">@stripped_text</span> <span class="o">=</span> <span class="n">params</span><span class="p">[</span><span class="ss">:stripped_text</span><span class="p">]</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="c1"># Add attribute for Mailgun stripped-text and prepend custom extensions</span>
<span class="no">Griddler</span><span class="o">::</span><span class="no">Email</span><span class="p">.</span><span class="nf">class_eval</span> <span class="k">do</span>
<span class="kp">attr_reader</span> <span class="ss">:stripped_text</span>
<span class="n">prepend</span> <span class="no">GriddlerEmailExtensions</span>
<span class="k">end</span></code></pre></figure>
<p>To recap, we’ve added a small amount of functionality to Griddler. When an email arrives from Mailgun, the <code class="highlighter-rouge">stripped-text</code> parameter is now processed, and is included as an attribute of the resulting <code class="highlighter-rouge">Griddler::Email</code> object. We’re now free to use <code class="highlighter-rouge">stripped-text</code> anywhere in our application!</p>
<h2 id="source-code-available-on-github">Source Code Available on GitHub</h2>
<p>You can find the full source code for this example here: <a href="https://github.com/jwkratz/griddler-stripped-text">https://github.com/jwkratz/griddler-stripped-text</a></p>Going Native with System Fonts and Form Fields2016-04-26T00:00:00-05:002016-04-26T00:00:00-05:00https://jeremykratz.com/blog/going-native-with-system-fonts-and-form-fields<p class="center"><img src="/img/posts/2016-04-26-going-native-with-system-fonts-and-form-fields.png" alt="Going Native with System Fonts and Form Fields" width="400" height="250" /></p>
<p>Designing an application interface in today’s world is difficult. Many products offer native implementations for iOS and Android, as well as cross-browser HTML versions. Native platforms each offer their own user interface guidelines, which outline some best-practice approaches to navigation, controls, animations, and more. Apps that follow these guidelines are instantly understandable and usable by users. The best apps mix their own visual style into these guidelines, which makes the software feel like a natural extension of both the product’s brand and the host operating system.</p>
<p><a href="https://www.getdonedone.com/going-native-with-system-fonts-and-form-fields/">Read the full post on the DoneDone Blog</a></p>Debugging Like a Detective2015-10-06T00:00:00-05:002015-10-06T00:00:00-05:00https://jeremykratz.com/blog/debugging-like-a-detective<p class="center"><img src="/img/posts/2015-10-06-debugging-like-a-detective.png" alt="Debugging Like a Detective" width="400" height="250" /></p>
<p>Last week we received an email from a DoneDone customer who was experiencing a strange file upload error. We attempted to reproduce the error with no luck, so we asked the user to notify us if it happened again. They replied that the error had disappeared. Case closed.</p>
<p><a href="https://www.getdonedone.com/debugging-like-a-detective/">Read the full post on the DoneDone Blog</a></p>Writing for Non-Writers2015-09-14T00:00:00-05:002015-09-14T00:00:00-05:00https://jeremykratz.com/blog/writing-for-non-writers<p class="center"><img src="/img/posts/2015-09-14-writing-for-non-writers.png" alt="Writing for Non-Writers" width="400" height="250" /></p>
<p>Over a year ago we decided to focus on writing higher-quality articles for the DoneDone blog. Previously, the blog was used mainly to announce new DoneDone features and updates. The new goal was to write more editorial-style posts related to software development, bug tracking, and managing a SaaS business.</p>
<p><a href="https://www.getdonedone.com/writing-for-non-writers/">Read the full post on the DoneDone Blog</a></p>Debugging Remote Webhooks with Ngrok and Visual Studio2015-08-19T00:00:00-05:002015-08-19T00:00:00-05:00https://jeremykratz.com/blog/debugging-remote-webhooks-with-ngrok-and-visual-studio<p class="center"><img src="/img/posts/2015-08-19-debugging-remote-webhooks-with-ngrok-and-visual-studio.png" alt="Debugging Remote Webhooks with Ngrok and Visual Studio" width="400" height="250" /></p>
<p>Over the past few years, we’ve regularly built new integrations into DoneDone. Most of our integrations use webhooks, which are callbacks sent from a remote third-party service to your application. Webhooks are simple yet powerful, and allow us to easily automate tasks like processing incoming emails and updating issues from Git or SVN commits.</p>
<p><a href="https://www.getdonedone.com/debugging-remote-webhooks-with-ngrok-and-visual-studio/">Read the full post on the DoneDone Blog</a></p>The Taboo of Burnout2015-06-18T00:00:00-05:002015-06-18T00:00:00-05:00https://jeremykratz.com/blog/the-taboo-of-burnout<p class="center"><img src="/img/posts/2015-06-18-the-taboo-of-burnout.png" alt="The Taboo of Burnout" width="400" height="250" /></p>
<p>Last week, Ka Wai wrote a great article about the benefits of keeping busy with a healthy variety of work. This seemed like a great time to publish a companion piece about the dangers of becoming overworked and stressed due to little variety – so let’s talk about burnout.</p>
<p><a href="https://www.getdonedone.com/the-taboo-of-burnout/">Read the full post on the DoneDone Blog</a></p>Accessing your Windows Development Environment from OSX2015-05-12T00:00:00-05:002015-05-12T00:00:00-05:00https://jeremykratz.com/blog/accessing-your-windows-development-environment-from-osx<p class="center"><img src="/img/posts/2015-05-12-accessing-your-windows-development-environment-from-osx.png" alt="Accessing your Windows Development Environment from OSX" width="400" height="250" /></p>
<p>DoneDone is built using ASP.NET, meaning our development environment is Visual Studio on Windows. Ka Wai and I both use MacBook Pros with Parallels installed because it gives us easy access to all major browsers on OSX and Windows.</p>
<p><a href="https://www.getdonedone.com/accessing-your-windows-development-environment-from-osx/">Read the full post on the DoneDone Blog</a></p>Silence in the Workplace2015-04-20T00:00:00-05:002015-04-20T00:00:00-05:00https://jeremykratz.com/blog/silence-workplace<p class="center"><img src="/img/posts/2015-04-20-silence-workplace.png" alt="Silence in the Workplace" width="400" height="250" /></p>
<p>Lately at DoneDone and our partner companies We Are Mammoth and Kin, we’ve been thinking a lot about work environments. In March, we read Peopleware as part of our #MindMatters book series. This month, we’ve started sharing our own home offices and explaining the thought that went into each of them.</p>
<p><a href="https://www.getdonedone.com/silence-workplace/">Read the full post on the DoneDone Blog</a></p>Developers vs Sales2015-03-19T00:00:00-05:002015-03-19T00:00:00-05:00https://jeremykratz.com/blog/developers-vs-sales<p class="center"><img src="/img/posts/2015-03-19-developers-vs-sales.png" alt="Developers vs Sales" width="400" height="250" /></p>
<p>Hi, my name is Jeremy, and I used to be anti-sales. I think this is mainly because I worked in advertising for a few years. As a web developer at an agency, it was easy to feel less important than your colleagues in sales and accounts. It always seemed to me that the relationship between sales and developers was disproportionate. The sales team continued to grow and the executives celebrated them internally, while the development team began to shrink and received the most attention only when we made mistakes.</p>
<p><a href="https://www.getdonedone.com/developers-vs-sales/">Read the full post on the DoneDone Blog</a></p>