<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: John Jacob</title>
    <description>The latest articles on DEV Community by John Jacob (@johnsalzarulo).</description>
    <link>https://dev.to/johnsalzarulo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F68209%2F47aaa53b-eac8-4253-b894-7ed402f520ef.jpg</url>
      <title>DEV Community: John Jacob</title>
      <link>https://dev.to/johnsalzarulo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/johnsalzarulo"/>
    <language>en</language>
    <item>
      <title>3 Levels of Developer</title>
      <dc:creator>John Jacob</dc:creator>
      <pubDate>Tue, 24 Nov 2020 20:04:38 +0000</pubDate>
      <link>https://dev.to/johnsalzarulo/3-levels-of-developer-4bci</link>
      <guid>https://dev.to/johnsalzarulo/3-levels-of-developer-4bci</guid>
      <description>&lt;p&gt;Software development is easy. It's 2020. We can build an app. We can store and mutate data. It's all solved. Within reason if you can explain what you want a computer to do, I can make it do the thing. &lt;/p&gt;

&lt;p&gt;Now: "The thing". That's the hard problem. What in the world are you building? What does it do? Why does it do it? Will anyone care that your thing does the thing it does? &lt;/p&gt;

&lt;p&gt;I've been realizing a few phases between developers I've worked with in my career. It's three main levels of professional developers: &lt;/p&gt;

&lt;h3&gt;
  
  
  Level One — You can ship a feature
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Within existing primitives, they can ship a new set of functionality on top.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;For Example: "Add a way for users to like messages". &lt;/p&gt;

&lt;p&gt;You need these people on your team. These kind of developers are incredible for continuous improvements and bug fixes. &lt;/p&gt;

&lt;h3&gt;
  
  
  Level Two — You can ship a product
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;You can build a product that's well scoped and well defined.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;For Example: "Build a messaging app" &lt;/p&gt;

&lt;p&gt;Good level two developers can take a well defined scope of work and ship maintainable code on time. They can define technical primitives from a non-technical design. They know how to break up work into executable deliverables and iterate until "the thing" is shipped. They can maintain and bugfix at the product level autonomously. &lt;/p&gt;

&lt;h3&gt;
  
  
  Level Three — You can deliver a solution
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;You can build a solution to a problem.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example: "I need more customers"&lt;/p&gt;

&lt;p&gt;Level Three developers are like Unicorns. They are designers, product managers, business analysts and full-stack coders all wrapped into one. Level three coders take abstract problems and ship concrete solutions to them. Level three coders don't ask "Should I use React or Vue?" They ask: "Should we build this at all?". A level three coder approaches the world with a ruthless bias toward shipping. Shipping small, iterating, getting their hands dirty,  and pushing until their technology feels like magic to people that use it. &lt;/p&gt;

</description>
      <category>career</category>
    </item>
    <item>
      <title>0 to Dev in 6 months — Recommended Syllabus </title>
      <dc:creator>John Jacob</dc:creator>
      <pubDate>Mon, 07 Sep 2020 04:03:44 +0000</pubDate>
      <link>https://dev.to/johnsalzarulo/0-to-dev-in-6-months-recommended-syllabus-52mk</link>
      <guid>https://dev.to/johnsalzarulo/0-to-dev-in-6-months-recommended-syllabus-52mk</guid>
      <description>&lt;p&gt;Take it with a grain of Salt — but if I was starting out trying to be a web dev today this is the syllabus / path I would take. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Other devs — comment below what path you would take if you were starting from scratch today.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Month 1 (HTML + CSS + Basics)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Week 1 HTML  (Build and deploy a website using ugly plain css) (Deploy using &lt;a href="https://thenextweb.com/google/2013/02/05/google-drive-now-lets-developers-share-hosted-websites-by-storing-html-javascript-and-css-files/#:~:text=Create%20a%20new%20folder%20in,can%20view%20your%20web%20page."&gt;google drive&lt;/a&gt; or &lt;a href="https://www.netlify.com/"&gt;Netlify&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Week 2 HTML + CSS  again Deploy using Google drive or netlify&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Bonus points for:&lt;br&gt;
        - Google Analytics&lt;br&gt;
        - Custom Webfonts&lt;br&gt;
        - Images in your website&lt;br&gt;
        - Mutli-page website&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First 2 weeks is about getting something on the screen, learning editors, and basics of the internet HTML + CSS is the easiest way to do it.&lt;/li&gt;
&lt;li&gt;Last 2 weeks: Rebuilding one of those sites with a CSS framework as a starting point. Bootstrap or Tailwind (Just using a CDN style tag)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By month one you should be pretty confident in &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTML&lt;/li&gt;
&lt;li&gt;CSS&lt;/li&gt;
&lt;li&gt;The terminal&lt;/li&gt;
&lt;li&gt;Git + Github&lt;/li&gt;
&lt;li&gt;Basics of hosting and domains&lt;/li&gt;
&lt;li&gt;Basics of file structures and relative paths&lt;/li&gt;
&lt;li&gt;Basics of style tags and using a front end library&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;At this point you can probably make $15-20/hr freelance if you position yourself right.&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You could also do a lot customizing things like Squarespace or Shopify&lt;/strong&gt; &lt;/p&gt;


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

&lt;h3&gt;
  
  
  Month 2  (JavaScript)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Keep it Vanilla JavaScript&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoid JQuery or any other libraries&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.beginnerjavascript.com/"&gt;https://www.beginnerjavascript.com/&lt;/a&gt;  or any other course for Vanila JavaScript&lt;/li&gt;
&lt;li&gt;Add interactivity to your site — Again deploy it&lt;/li&gt;
&lt;li&gt;Maybe do a couple hours of algorithm bullshit — not until month 2&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;By month two you should be confident in:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing and reading JavaScript functions&lt;/li&gt;
&lt;li&gt;Selecting and interacting with HTML elements on screen&lt;/li&gt;
&lt;li&gt;Handling forms and inputs&lt;/li&gt;
&lt;li&gt;Some API interactivity — Maybe a Stripe Credit card collection form or the Pokemon API&lt;/li&gt;
&lt;li&gt;Strong recommend to try to play some some "low code" stuff and make something that actually does something. Not just a "Static" site.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;At this point you can probably make $20-25/hr freelance if you position yourself right.&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;You could now build an MVP using "low code / no code tools — Strongly encouraged &lt;/p&gt;


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

&lt;h3&gt;
  
  
  Month 3 (React — if you want to be a "Front end dev")
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;This is going to take all month.&lt;/li&gt;
&lt;li&gt;Build at least 2 projects start to finish and DEPLOY them.&lt;/li&gt;
&lt;li&gt;Bonus points for interactivity or interacting with any kind of low code backend — &lt;a href="https://upmostly.com/tutorials/create-simple-web-app-react-airtable"&gt;airtable&lt;/a&gt; or &lt;a href="https://firebase.google.com/"&gt;Firebase&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;At this point you can probably make $25-30/hr freelance if you position yourself right.&lt;/strong&gt;  &lt;/p&gt;

&lt;h3&gt;
  
  
  Month 4 (Gatsby + CMS Systems)
&lt;/h3&gt;

&lt;p&gt;(Get freelance work or do favors for people now)&lt;br&gt;
    - Build a static site using React + Gatsby&lt;br&gt;
    - Always be deploying&lt;br&gt;
    - Bonus points for actually getting paid to develop a website&lt;br&gt;
    - Understand and explore Content Management Systems&lt;br&gt;
        - Headless Wordpress, Contentful, Sanity Etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;At this point you can probably make $25-30/hr freelance if you position yourself right.&lt;/strong&gt;  &lt;/p&gt;

&lt;h3&gt;
  
  
  Month 5 (Backend basics — Next.js  or Rails)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You can start with Next.js&lt;/li&gt;
&lt;li&gt;Even though you want to be a front end developer you'll need knowledge of the back end&lt;/li&gt;
&lt;li&gt;You should now build an app that does "Something" with a backend you've built.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;At this point you can probably make $30-35/hr freelance if you position yourself right and be up for internships / JR JR positions at agencies or firms&lt;/strong&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Month 6 (Build something real using everything you know)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ABD — ALWAYS BE DEPLOYING — Shipping things, get paid early on if you can.

&lt;ul&gt;
&lt;li&gt;Build small and complete&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Random note: &lt;a href="https://teamtreehouse.com/tracks/front-end-web-development"&gt;This "Front End Web Development" course from Treehouse seems pretty solid&lt;/a&gt; and my recommended path kind of follows this&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>beginners</category>
      <category>career</category>
    </item>
    <item>
      <title>Simple Full Text Search in Rails</title>
      <dc:creator>John Jacob</dc:creator>
      <pubDate>Sat, 22 Aug 2020 03:05:55 +0000</pubDate>
      <link>https://dev.to/johnsalzarulo/simple-full-text-search-in-rails-l7n</link>
      <guid>https://dev.to/johnsalzarulo/simple-full-text-search-in-rails-l7n</guid>
      <description>&lt;p&gt;&lt;strong&gt;No gems, no dependencies, no complexity. Here’s a basic full text search to use on simpler, smaller rails apps.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Don’t get me wrong, I love ElasticSearch, or Ransack for larger more complex projects. But sometimes you just need something simple. This website for example.&lt;/p&gt;

&lt;p&gt;At the time of writing this I’ve implemented search using plain old Ruby. This example not only does “full text” search, but it also searches the full text across multiple attributes, lastly, it also supports searching within ActionText rich text blocks as well.&lt;/p&gt;

&lt;p&gt;Here's the basics for Searching the Post model within my blog: &lt;/p&gt;

&lt;p&gt;I have a before_action for the controller to a set_scope method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;before_action :set_scope, only: :index
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here's that set_scope method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  def set_scope
    @posts = Post.all
    if params[:search]
      basics = search_basics
      body = search_body
      tags = search_tags
      post_ids = basics | body | tags
      @posts = @posts.find(post_ids)
    end
  end

  def search_basics
    @posts.where(
      "title ilike ? OR description ilike ?",
      "%#{params[:search]}%","%#{params[:search]}%"
    ).pluck(:id)
  end

  def search_body
    @posts.joins(:action_text_rich_text)
      .where(
        "action_text_rich_texts.body ilike ?",
        "%#{params[:search]}%"
      ).pluck(:id)
  end

  def search_tags
    tags = params[:search].split(" ")
    Post.tagged_with(tags, :any =&amp;gt; true).pluck(:id)
  end

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here's how I render my basic search bar at the top of my posts index to wire it up in the view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;form id="search" action="&amp;lt;%= posts_path %&amp;gt;"&amp;gt;
  &amp;lt;div class="input-group"&amp;gt;
    &amp;lt;%= text_field_tag :search, params[:search], id: 'content_search_term',class: 'form-control', placeholder:'Search...' %&amp;gt;
    &amp;lt;div class="input-group-append"&amp;gt;
      &amp;lt;button class="btn btn-primary" type="submit" id="button-addon2"&amp;gt;Search&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;% unless params[:search].blank? %&amp;gt;
    &amp;lt;small&amp;gt;&amp;lt;%= pluralize(@posts.count, "post", "posts") %&amp;gt; found for search "&amp;lt;%= params[:search] %&amp;gt;" &amp;lt;a href="&amp;lt;%= posts_path %&amp;gt;" class="text-muted pl-2 text-decoration-none"&amp;gt;&amp;lt;i class="fas fa-times"&amp;gt;&amp;lt;/i&amp;gt; Clear Search&amp;lt;/a&amp;gt;&amp;lt;/small&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Bottom Line
&lt;/h3&gt;

&lt;p&gt;This is not the cleanest or most performant way to do search in rails, but sometimes simpler is better. If you only have a few dozen records to search and don't need "sloppy" search, this hack will probably suffice. Otherwise, there's plenty of resources out there on Elastic search and search-kick. &lt;/p&gt;

</description>
      <category>rails</category>
    </item>
    <item>
      <title>My Problem with HTML Tables</title>
      <dc:creator>John Jacob</dc:creator>
      <pubDate>Fri, 21 Aug 2020 20:59:51 +0000</pubDate>
      <link>https://dev.to/johnsalzarulo/my-problem-with-html-tables-570c</link>
      <guid>https://dev.to/johnsalzarulo/my-problem-with-html-tables-570c</guid>
      <description>&lt;p&gt;I feel like no css framework has really solved the problem of HTML tables. &lt;/p&gt;

&lt;p&gt;Take this basic bootstrap table for example: &lt;/p&gt;

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

&lt;h2&gt;
  
  
  "Responsive" Tables On Mobile 🙄
&lt;/h2&gt;

&lt;p&gt;Even the &lt;code&gt;table-responsive&lt;/code&gt; class is hardly "responsive" it just makes the table scroll sideways off the screen. This is not an ideal solution. &lt;/p&gt;

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

&lt;h2&gt;
  
  
  My Alternative
&lt;/h2&gt;

&lt;p&gt;I string together &lt;code&gt;div&lt;/code&gt;'s with &lt;code&gt;col&lt;/code&gt; classes to make sure the interface responds well on mobile devices, so instead of scrolling offscreen, content flows into nice rows: &lt;/p&gt;

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

&lt;h2&gt;
  
  
  Codepen
&lt;/h2&gt;

&lt;p&gt;Open this up and drag the preview window to get a gist of what I mean: &lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/johnsalzarulo/embed/PoNNzgY?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  How does everyone else solve this problem?
&lt;/h1&gt;

&lt;p&gt;I like my solution because it is one piece of code that carries into mobile and desktop, however it's just so incredibly verbose. &lt;/p&gt;

&lt;p&gt;What CSS frameworks handle this well? How else have people solved this? &lt;/p&gt;

</description>
      <category>html</category>
      <category>css</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Solutions for Staging / Demo Account Data?</title>
      <dc:creator>John Jacob</dc:creator>
      <pubDate>Fri, 07 Aug 2020 22:40:27 +0000</pubDate>
      <link>https://dev.to/johnsalzarulo/solutions-for-staging-demo-account-data-13gn</link>
      <guid>https://dev.to/johnsalzarulo/solutions-for-staging-demo-account-data-13gn</guid>
      <description>&lt;p&gt;Every software product I've ever built always has this same problem. Sales, marketing / product need a repeatable demo account populated with best case scenario data that doesn't take manual reset and doesn't have any side effects. I was listening to &lt;a href="https://rework.fm/an-email-account-is-born/"&gt;this episode&lt;/a&gt; of the Re-work podcast from Basecamp and it was good to know that I'm not totally crazy in this being a pain point. &lt;/p&gt;

&lt;h3&gt;
  
  
  💬🙏 comment below — how are you solving this?
&lt;/h3&gt;

&lt;p&gt;I would love to know, don't limit answers to Rails! I'm sure we can be inspired based on whatever framework people are in. &lt;/p&gt;

&lt;h2&gt;
  
  
  Our Current solution — very "Rails Specific"
&lt;/h2&gt;

&lt;p&gt;I use Rails fixtures and pre-load those fixtures in a bin/setup script. If you take your time to pre-populate all this fixture data it works really well. You've got that data for testing (Which is usually a win) and then it's easy to set up staging or localhost to just reset back to this state with a simple command. &lt;/p&gt;

&lt;p&gt;However — especially with ActionText and ActiveStorage it gets clunky and you end up with these huge fixture files. I have found some solutions to limit the pain here, you can &lt;a href="https://stackoverflow.com/questions/6591722/how-to-generate-fixtures-based-on-my-development-database/35394335#35394335"&gt;pull down production data&lt;/a&gt; and then write it to fixtures, then update it. &lt;/p&gt;

&lt;h3&gt;
  
  
  Other Misc Rails Thoughts / Tips
&lt;/h3&gt;

&lt;p&gt;I've recently learned you can do this &lt;a href="https://dzone.com/articles/drying-yaml-fixtures"&gt;quasi-inheritance&lt;/a&gt; thing in your fixture files to DRY them up. And of course, &lt;a href="https://flylib.com/books/en/4.185.1.105/1/"&gt;you can write ERB&lt;/a&gt; in fixtures. (Things like Faker can help this) but even with all this, there's got to be a better way. &lt;/p&gt;

&lt;h1&gt;
  
  
  Again — would love to hear from the Dev.to community
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;How do you deal with "Demo" account data? &lt;/li&gt;
&lt;li&gt;How does your marketing and sales team are well supported on this? What if they need to make changes or updates? &lt;/li&gt;
&lt;li&gt;How do you keep this whole thing from not being such a pain in the ass? &lt;/li&gt;
&lt;li&gt;How do you keep this in sync with new feature rollouts? &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rails</category>
      <category>discuss</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Tables and other Advanced Formatting in Action Text (Kind of) </title>
      <dc:creator>John Jacob</dc:creator>
      <pubDate>Fri, 07 Aug 2020 22:12:06 +0000</pubDate>
      <link>https://dev.to/mainstreet/tables-and-other-advanced-formatting-in-action-text-kind-of-1dbn</link>
      <guid>https://dev.to/mainstreet/tables-and-other-advanced-formatting-in-action-text-kind-of-1dbn</guid>
      <description>&lt;p&gt;I work on an in house Learning Management system built in Rails. This Learning Management system allows our content team to create articles, videos and quizzes. This content is delivered through lessons for new business owners. Through these tracks they learn all they need to learn to start and run new service businesses.&lt;/p&gt;

&lt;p&gt;Since were on rails and we needed rich text editing we reached for &lt;a href="https://edgeguides.rubyonrails.org/action_text_overview.html"&gt;Action Text&lt;/a&gt;. Action Text gives you the solid &lt;a href="https://trix-editor.org/"&gt;Trix editor&lt;/a&gt; plug and play in your app. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Rails Action Text in Practice in our Content Library Back-end&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  However: Action Text / Trix doesn't support tables.
&lt;/h3&gt;

&lt;p&gt;Our content team had lots of tables of information they wanted to be able to communicate. Sometimes it's hard to get across nuanced information for estimating and process without a table. We realized we needed table support in Action Text / Trix. &lt;/p&gt;

&lt;h3&gt;
  
  
  First Direction — Action Text Attachments
&lt;/h3&gt;

&lt;p&gt;My first path was to extend the Action Text attachment model. This is what the &lt;a href="https://readtimelapse.com/how-we-built-table-support-for-trix-editor-cd4f14c03463"&gt;team at Timelapse documented&lt;/a&gt;. Although they provided some good direction, there was nothing I could directly leverage. Also, this seems like a lot of front end complexity for a v1 of a product. Was looking for something similar. In this research I found this great &lt;a href="https://www.youtube.com/watch?v=2iGBuLQ3S0c"&gt;Rails Conf video&lt;/a&gt; from Chris Oliver of Go Rails, a strongly recommended resource on Action Text attachments in general. However, decided against doing something so front end heavy (for now). &lt;/p&gt;

&lt;h2&gt;
  
  
  The Crazy Idea that worked: Render Code-blocks as HTML
&lt;/h2&gt;

&lt;p&gt;Since this project didn't ever need a "Code Block" these articles render to a non-technical audience, why not allow any valid HTML written in a code block to render as HTML.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No extra front end work&lt;/li&gt;
&lt;li&gt;Simple Extension of the Article Decorator&lt;/li&gt;
&lt;li&gt;Very maintainable portable simple content (Just HTML)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your users have to be reasonably technical to update HTML code-blocks&lt;/li&gt;
&lt;li&gt;You don't get a WYSIWYG interface when editing content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I put a rough prototype and did a &lt;a href="https://www.loom.com/share/004c75360fe94207b00cb4f38371c753"&gt;screen recording&lt;/a&gt; to get my colleagues thoughts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Execution
&lt;/h2&gt;

&lt;p&gt;All credit goes to the great &lt;a href="https://dev.to/ortegacmanuel"&gt;Manuel Ortega&lt;/a&gt; for actually making my crazy idea work, he was able to build a clean implementation that solved many of the pitfalls my prototype was riddled with.&lt;/p&gt;

&lt;p&gt;It all essentially boils down to one plain ol' ruby object — this &lt;code&gt;Richtext::CodeBlocks::HtmlService&lt;/code&gt; object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Richtext::CodeBlocks::HtmlService&lt;/span&gt;
  &lt;span class="nc"&gt;ALLOWED_HTML_TAGS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"table"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"tr"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"td"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"th"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"col"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"pre"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"p"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"h1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"h2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"h3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"summary"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"row"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="no"&gt;ALLOWED_HTML_ATTRIBUTES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

  &lt;span class="c1"&gt;# To Validate HTML tags and protect from bad formatted input&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Nokogiri&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTML&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DocumentFragment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"pre"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pre_tag&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;pre_tag_html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pre_tag_errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ensure_well_formed_markup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pre_tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pre_tag_errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;pre_tag_errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;
      &lt;span class="n"&gt;inner_html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extract_inner_html_from_pre_tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pre_tag_html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;inner_html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove_not_allowed_tags_and_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inner_html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;pre_tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Nokogiri&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;XML&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;inner_html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;pre_tag&lt;/span&gt;
      &lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ActionText&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Fragment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniq&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# To parse each code block tag and render it to HTML&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rich_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Nokogiri&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTML&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DocumentFragment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rich_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"pre"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pre_tag&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;inner_html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Nokogiri&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTML&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DocumentFragment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pre_tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;inner_html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;add_styles_to_tables&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inner_html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;advanced_code_block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;div class='advanced-code-block'&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;inner_html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_html&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/div&amp;gt;"&lt;/span&gt;
      &lt;span class="n"&gt;pre_tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;advanced_code_block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_html&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ensure_well_formed_markup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Nokogiri&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;XML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;pre&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/pre&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# To add our bootstrap specific classes to table elements&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_styles_to_tables&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"table"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"table"&lt;/span&gt;
      &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;div class='table-responsive'&amp;gt;&amp;lt;/div&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;html&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extract_inner_html_from_pre_tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Nokogiri&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;XML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"pre"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;inner_html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# To add error messages to mis-formatted HTML&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error_messages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;readable_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;":"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt; &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;readable_message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;uniq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;", "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Using the HtmlService
&lt;/h3&gt;

&lt;p&gt;You can just add a method in the article model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;formatted_body&lt;/span&gt;
  &lt;span class="no"&gt;Richtext&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CodeBlocks&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HtmlService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;html_safe&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and then call it in the view&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="vi"&gt;@article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formatted_body&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Screenshots
&lt;/h3&gt;

&lt;p&gt;Here's the example of the editor with code blocks:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nyaOXCo---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/b8ldei9o4dbu2wdxbz4r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nyaOXCo---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/b8ldei9o4dbu2wdxbz4r.png" alt="Example input"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the example output of that article:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rcKigWCf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/58y51xkjtubr6hlivj4a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rcKigWCf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/58y51xkjtubr6hlivj4a.png" alt="Rendered output"&gt;&lt;/a&gt;&lt;br&gt;
Here's example validation being thrown to prevent you from saving bad HTML.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tq_qwpYv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/lsbvwedrclxfn0iib36w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tq_qwpYv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/lsbvwedrclxfn0iib36w.png" alt="Validation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  In Production
&lt;/h2&gt;

&lt;p&gt;Obviously this repo is a very limited example, but used well in production with an extended CSS framework this can be pretty powerful in practice:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0LfD3v62--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rw5pifmgid8g9vdjkku5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0LfD3v62--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rw5pifmgid8g9vdjkku5.png" alt="Production Example 1"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xIVLkMr2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/sfzt3di1gdeb8auqfpp0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xIVLkMr2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/sfzt3di1gdeb8auqfpp0.png" alt="Production example 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/Johnsalzarulo/action_text_code_blocks"&gt;Github Repo&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This is just a basic demo rails app using this approach. Excited to get feedback from anyone else looking to explore a feature like this. &lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Developer with New Baby Coming Soon — HELP! </title>
      <dc:creator>John Jacob</dc:creator>
      <pubDate>Wed, 29 Jul 2020 19:12:50 +0000</pubDate>
      <link>https://dev.to/johnsalzarulo/developer-with-new-baby-coming-soon-help-58l3</link>
      <guid>https://dev.to/johnsalzarulo/developer-with-new-baby-coming-soon-help-58l3</guid>
      <description>&lt;p&gt;I'm incredibly excited that my wife and I will be having our first baby girl in a month or so. &lt;/p&gt;

&lt;p&gt;Also, pretty scared. &lt;/p&gt;

&lt;p&gt;Looking to get a good thread going with practical tips and tricks. &lt;/p&gt;

&lt;p&gt;My top questions / concerns: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do I balance supporting being there for family and pushing my career through ongoing learning? &lt;/li&gt;
&lt;li&gt;Am I really going to be able to balance my Engineering Manager job and a new baby? &lt;/li&gt;
&lt;li&gt;My job is remote, how do I make myself available for wife and baby and also have clear work time? &lt;/li&gt;
&lt;li&gt;Will I die of lack of sleep? &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other parents — Please help! &lt;/p&gt;

</description>
      <category>discuss</category>
      <category>career</category>
      <category>help</category>
    </item>
    <item>
      <title>Abbreviations in Software Development</title>
      <dc:creator>John Jacob</dc:creator>
      <pubDate>Sun, 26 Jul 2020 17:43:38 +0000</pubDate>
      <link>https://dev.to/johnsalzarulo/abbreviations-in-software-development-4jjf</link>
      <guid>https://dev.to/johnsalzarulo/abbreviations-in-software-development-4jjf</guid>
      <description>&lt;p&gt;One of my pet peeve's in the software world is how much we all use abbreviations. Technical or non-technical. Seems half the time we don't even know what they mean. &lt;/p&gt;

&lt;p&gt;I've put together a reasonably complete list here of the most common ones the industry seems to use too much. &lt;/p&gt;

&lt;p&gt;We actually walk through and explain a bunch of these in the &lt;a href="https://iteration.simplecast.com/episodes/essential-abbreviations-in-software-development"&gt;most recent episode of our podcast&lt;/a&gt; — Iteration, a podcast about software development and design. &lt;/p&gt;

&lt;p&gt;Comment below with essential ones I've missed or gotten wrong. Might turn this into a Github repo so we can try to get a source of truth around this insanity. &lt;/p&gt;

&lt;h3&gt;
  
  
  Technical / General Software
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;CROn: Command ron on&lt;/li&gt;
&lt;li&gt;URL: Uniform Resource Locator&lt;/li&gt;
&lt;li&gt;TTL: Time to Live   — Example a cache's TTL&lt;/li&gt;
&lt;li&gt;DOM: Document Object Model&lt;/li&gt;
&lt;li&gt;HTML: Hypertext Markup Language&lt;/li&gt;
&lt;li&gt;CLI: Command Line Interface (CLI)&lt;/li&gt;
&lt;li&gt;API: Application Program Interface (API)&lt;/li&gt;
&lt;li&gt;TDD: Test Driven Development&lt;/li&gt;
&lt;li&gt;CI: Continuous Integration (CI)&lt;/li&gt;
&lt;li&gt;UX: User Experience&lt;/li&gt;
&lt;li&gt;UI: User Interface&lt;/li&gt;
&lt;li&gt;CSS: Cascading Style Sheets&lt;/li&gt;
&lt;li&gt;IDE: Integrated Development Environment&lt;/li&gt;
&lt;li&gt;FTP (File Transfer Protocol)&lt;/li&gt;
&lt;li&gt;TLD (Top Level Domain) (.com, .net, .car)&lt;/li&gt;
&lt;li&gt;SSO   Single Sign On&lt;/li&gt;
&lt;li&gt;JS: JavaScript&lt;/li&gt;
&lt;li&gt;AJAX  Asynchronous JavaScript and XML&lt;/li&gt;
&lt;li&gt;GUID  Global Unique Identifier&lt;/li&gt;
&lt;li&gt;GUI Graphical User Interface&lt;/li&gt;
&lt;li&gt;OOP   Object Oriented Programming&lt;/li&gt;
&lt;li&gt;ORM   Object Relational Mapping&lt;/li&gt;
&lt;li&gt;DSL: Domain Specific Language&lt;/li&gt;
&lt;li&gt;REGEX Regular Expression&lt;/li&gt;
&lt;li&gt;PHP   Personal Home Page/PHP Hypertext Preprocessor&lt;/li&gt;
&lt;li&gt;REST  Representational State Transfer&lt;/li&gt;
&lt;li&gt;SDK   Software Development Kit&lt;/li&gt;
&lt;li&gt;SQL   Structured Query Language&lt;/li&gt;
&lt;li&gt;XML   Extensible Markup Language&lt;/li&gt;
&lt;li&gt;2FA: Two factor authentication&lt;/li&gt;
&lt;li&gt;DNS: Domain Name Servers&lt;/li&gt;
&lt;li&gt;Web RTC — web real time communications&lt;/li&gt;
&lt;li&gt;FOSS — Free Open Source Software&lt;/li&gt;
&lt;li&gt;WYSIWYG: What you see is what you get&lt;/li&gt;
&lt;li&gt;JSON - JavaScript Object Notation&lt;/li&gt;
&lt;li&gt;TS: Type Script &lt;/li&gt;
&lt;li&gt;POC : Proof of Concept&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Marketing  / Sales
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;CTA: CTA: Call-to-Action&lt;/li&gt;
&lt;li&gt;CTR: Click-through Rate&lt;/li&gt;
&lt;li&gt;CPL: Cost Per Lead&lt;/li&gt;
&lt;li&gt;CPA: Cost per Action&lt;/li&gt;
&lt;li&gt;CPC: Cost per click&lt;/li&gt;
&lt;li&gt;PPC Pay per click
&lt;/li&gt;
&lt;li&gt;CR: Conversion Rate&lt;/li&gt;
&lt;li&gt;CRO: Conversion Rate Optimization&lt;/li&gt;
&lt;li&gt;CMS: Content Management System&lt;/li&gt;
&lt;li&gt;PDP: Product Detail Page&lt;/li&gt;
&lt;li&gt;PLP: Product Listing Page&lt;/li&gt;
&lt;li&gt;SEO: Search Engine Optimization&lt;/li&gt;
&lt;li&gt;SEM: Search Engine Marketing&lt;/li&gt;
&lt;li&gt;SMM: Social Media Marketing&lt;/li&gt;
&lt;li&gt;MAP (Marketing Automation Platform)&lt;/li&gt;
&lt;li&gt;MQL: Marketing Qualified Leads&lt;/li&gt;
&lt;li&gt;LPO (Landing Page Optimization)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Business Terms
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;KPI: Key Performance Indicators&lt;/li&gt;
&lt;li&gt;OKR: Objective Key Results&lt;/li&gt;
&lt;li&gt;RFP: Request for Proposal&lt;/li&gt;
&lt;li&gt;IC: Individual contributor&lt;/li&gt;
&lt;li&gt;P/E - Price-to-earnings ratio (How much does the stock cost vs how much does the company earn?)&lt;/li&gt;
&lt;li&gt;P&amp;amp;L - Profit and Loss — How much money comes in the door and what they lost to get it&lt;/li&gt;
&lt;li&gt;ROI - Return on Investment&lt;/li&gt;
&lt;li&gt;WC - Working Capital (Liquid money to spend)&lt;/li&gt;
&lt;li&gt;B2B - Business to Business&lt;/li&gt;
&lt;li&gt;B2C - Business to Consumer&lt;/li&gt;
&lt;li&gt;D2C - direct to consumer&lt;/li&gt;
&lt;li&gt;BD - Business Development&lt;/li&gt;
&lt;li&gt;QA - Quality Assurance&lt;/li&gt;
&lt;li&gt;YTD: Year to Date&lt;/li&gt;
&lt;li&gt;CRM: Customer Relationship Management&lt;/li&gt;
&lt;li&gt;MOM: Month over Month (compares a metric over to previous month)&lt;/li&gt;
&lt;li&gt;SAAS  Software as a Service&lt;/li&gt;
&lt;li&gt;ERP   Enterprise Resource Planning&lt;/li&gt;
&lt;li&gt;EPD — Engineering Product and Design&lt;/li&gt;
&lt;li&gt;NDA   Non-Disclosure Agreement&lt;/li&gt;
&lt;li&gt;YoY   Year On Year (compares a metric over to previous year)&lt;/li&gt;
&lt;li&gt;RDD   Requirements Definition Document&lt;/li&gt;
&lt;li&gt;PRD Product Requirements Definition&lt;/li&gt;
&lt;li&gt;RFQ : Request for Quotation&lt;/li&gt;
&lt;li&gt;RFI : Request for Information&lt;/li&gt;
&lt;li&gt;PO : Purchase Order&lt;/li&gt;
&lt;li&gt;WO : Work Order&lt;/li&gt;
&lt;li&gt;SKU : Stock Keeping Unit&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>discuss</category>
      <category>tips</category>
    </item>
  </channel>
</rss>
