Rails Tricks

Archive

15 Aug

Rails static pages - Rails Tricks Issue 15


Hi there,


This week I want to show you how I add static pages in a Rails application, since I just did this recently.


I like to put all of them into the same controller so they are nicely separated, so I generate a new controller called StaticPagesController. Then I add my routes:

cddf7ec0-3085-40df-98f7-8327175a2ff6.png 66 KB And I create my views in app/views/static_pages/. Since Rails 7.0, I can omit the controller methods, Rails will render the appropriate views regardless. That’s it. This would be today’s Rails trick, but since it would be super short, let’s dig into how Rails renders the views without the controller methods being defined.

As I found out, this feature called “implicit rendering”. What happens is, when Action Controller is processing a request, it is trying to find the appropriate method for the action it receives from the router:

4c4b8fa7-47e5-4c71-9c73-586be33505bc.png 99.2 KB This method is overridden in action_pack/lib/action_controller/metal.rb to check if there is any template available for the action, and if so, it renders the template, if not, it just renders a head: :no_content: 10246ea8-6ba3-41de-adeb-b6601c050338.png 637 KB Now you know how Rails can render implicit actions in your controller. Until next time!

11 Jul

Modifying Action Text markup - Rails Tricks Issue 14


Hi there,


I am working on a newsletter tool(Pombo) and this week, I want to share how I solved a problem I came across last week while working on it.


The problem: the newsletter tool needs to support importing past issues from other tools. These imported issues might contain references to images hosted at the other provider, and during the import, I need to move those images into Pombo because once the account on the old service is deleted, the images won’t be accessible anymore.


In Pombo, I use Action Text to store the body of a newsletter issue, and I use Active Storage for the images. My initial plan was to parse the HTML output of the Action Text field with Nokogiri, extract the image tags, create an Active Storage blob from them, and replace the image tag’s src in the markup to the blob’s URL.
Then I decided to look at Action Text’s source code to see how it works under the hood, and it turned out it will be way easier to do what I need than I expected.
An Action Text field has a “body” attribute, which is an instance of ActionText::Content which has a fragment method, which returns an instance of ActionText::Fragment and that’s a wrapper around the Nokogiri parsed markup.
Now putting this all together, to find all image tags on a content attribute backed by Action Text, we can just do the following:

9fdfbd14-0fd2-45d6-830b-3958d416bd5c.png 13.9 KB The next part is to download the image and create an ActiveStorage::Blob: 85ee56bd-e94a-4bcd-a3b5-8a96be1f0391.png 104 KB Next thing to figure out is how to replace the old image with the new one.
Digging a bit more into Action Text, I figured out I need to create an ActionText::Attachment from the blob and replace the nokogiri node with that: e622f146-580d-402c-af29-893dc75c21fd.png 53.6 KB And finally, we need to save the changes on the content. Here is the full snippet: fc440b3a-40ed-4930-a1df-364f698d2c8a.png 150 KB Based on what I learned from figuring out this solution, it will be pretty easy to add an outgoing link validator to make sure a newsletter doesn’t contain broken links.

I hope you enjoyed this, until next time!

04 Jul

Automate some of your security - Rails Tricks Issue 13

958e2d19-fc34-4eaf-965d-16fb83092b2f.jpg 54.9 KB Hi there,

This week, I will show you how you can automate some of the security necessities of a Rails application. If you follow this guide, you will be safe from one of the OWASP Top 10 security issues(A9-Using Components with Known Vulnerabilities) and lower the chances of having other vulnerabilities in your codebase. Let’s get into it.


Using Components with Known Vulnerabilities is in the OWASP Top 10, but automating a notification about gems with known vulnerabilities is very easy. The bundler-audit gem covers us there with the bundle-audit command:

[~/] bundle-audit --help
Commands:
  bundler-audit check [DIR]     # Checks the Gemfile.lock for insecure dependencies
  bundler-audit download        # Downloads ruby-advisory-db
  bundler-audit help [COMMAND]  # Describe available commands or one specific command
  bundler-audit stats           # Prints ruby-advisory-db stats
  bundler-audit update          # Updates the ruby-advisory-db
  bundler-audit version         # Prints the bundler-audit version

If you put a check for bundle audit --update to your CI workflow, it will check your app for vulnerable dependencies and your pipeline will fail. Additionally, if you use yarn to manage your javascript dependencies, you can use yarn audit to check your dependencies for any known vulnerability.


Here is an example GitHub Action file to do this:

# .github/workflows/bundle-audit.yml
name: Bundle Audit
on:
  pull_request:
  schedule:
    - cron: "0 0 * * *"
jobs:
  base:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
      - name: Setup Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: 3.2
          bundler-cache: true
      - name: Install bundler-audit
        run: gem install bundler-audit
      - name: Check dependencies with known vulnerabilities
        run: bundle-audit --update
      - name: Check javascript dependencies
        run: yarn audit

The above action runs bundle audit and yarn audit on every pull request and at midnight every day. You might need to adjust the Ruby version above to the one you are on.


Another low-hanging fruit to improve the security posture of a Ruby on Rails application is to set up static code analyses for potential security issues. There are two gems to help with this: brakeman and spektr(DISCLAIMER: I am the author of this gem). These gems analyze your code for potentially vulnerable code and can help to find SQL injections, XSS, and quite a few other issues.
Using on CI brakeman is more ideal, because it supports ignoring false positives out of the box. Spektr is targeted more towards security professionals running it on a codebase during an assessment.
Here is an example GitHub Actions file to run brakeman on your codebase on every pull request and once every day:

# .github/workflows/brakeman-scan.yml
name: Brakeman Scan
on:
 - pull_request:
 - schedule:
   - cron: "0 0 * * *"
jobs:
  base:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
      - name: Setup Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: 3.2
          bundler-cache: true
      - name: Install brakeman
        run: gem install brakeman
      - name: Static code analyses for security
        run: brakeman

That’s it for this week.

27 Jun

Customizing the Rails console


Hi there,


I started to deploy my latest pet project with MRSK and I realized that I miss the customizations from my Rails console on the server. And I thought it might be useful to share how I customize my Rails console, so here we go.


The default Rails console is built on top of IRB, so to customize it, we need to look at IRBs documentation. The docs say that we can either create a global .irbrc in the home directory of the user, or have a project specific one in the project directory, but the home directory one will have a presedence. In my example, I keep it in the project, so I can copy it with docker to the containers when I deploy.


This .irbrc file is a evaluated as ruby, so to do our customizations, we can use our favorite language. The first line in my config is to disable autocompletion because I dislike it:

IRB.conf[:USE_AUTOCOMPLETE] = false

For my dockerized project, I also disable IRB history, since the containers are short-lived, and the history won’t be kept long and it also prevents the exception on the console exit when it fails to write to the file.

IRB.conf[:SAVE_HISTORY] = false if Rails.env.production?

The next thing I have in my file is a prompt customization. I use the pastel gem to colorize the prompt based on the environment:

require "pastel"
pastel = Pastel.new
prompt = case Rails.env
    when "development"
        pastel.black.on_green.bold.dim Rails.env
    when "production"
        pastel.white.on_red.bold.dim Rails.env
    end
# defining custom prompt
IRB.conf[:PROMPT][:CUSTOM] = {   # name of prompt mode
    :PROMPT_I => "#{prompt}>> ", # simple prompt
    :PROMPT_S => "#{prompt}* ",  # prompt for continuated strings
    :PROMPT_C => "#{prompt}? ",  # prompt for continuated statement
    :RETURN   => " => %s\n"      # format to return value
}
IRB.conf[:PROMPT_MODE] = :CUSTOM

As you can see, I don’t do anything fancy in my prompt, just outputting the Rails environment on a green or red background based on the environment. You can get it fancier by using the following special characters in the prompt:

%N    # command name which is running
%m    # to_s of main object (self)
%M    # inspect of main object (self)
%l    # type of string(", ', /, ]), `]' is inner %w[...]
%NNi  # indent level. NN is digits and means as same as printf("%NNd").
      # It can be omitted
%NNn  # line number.
%%    # %

That’s it for this week.

20 Jun

Offline Ruby and Rails documentation

e80bef84-9c82-484c-81da-2c81ac52c6ac.jpg 110 KB

The vacation season is starting, and it can be helpful to have access without the internet to the documentation of Ruby, Rails, and the gems you use. If you are going on a trip and taking your laptop, you can prepare them in advance by following this little guide.


Let’s start with Ruby, that’s the easiest. To browse the API documentation offline, you can download a prebuilt copy from https://ruby-doc.org/downloads/. Extract the downloaded archive, open index.html and you can browse the documentation.


For Rails, you can generate the Rails Guides for local browsing. You need to clone the Rails repository, install some dependencies and run a rake command to generate the guides:

[~/] git clone https://github.com/rails/rails.git
[~/] cd rails/guides
[~/rails/guides] bundle
[~/rails/guides] rake guides:generate:html

When it is done, you will have the files in the output directory. The above will generate the guides for edge Rails, if you want to see the guides for a specific version, you need to checkout that branch. For instance for Rails 7.0:

[~/rails/guides] git checkout 7-0-stable
[~/rails/guides] rake guides:generate:html

You would probably also want to have the API documentation at hand. To generate that, you need to run the following rake command:

[~/rails] rake rdoc

Then open doc/rdoc/index.html in a browser.


For the gems, you have multiple options:


RDoc


First of all, you should make sure you have the rdoc files generated for the gems you have installed on your system. For that you can run:

gem rdoc --all

Once the docs are generated, you need to install the rubygems-server gem and start the server:

gem install rubygems-server
gem server

YARD


YARD is another documentation tool for ruby gem. To run the yard server, you need to install the gem and start the server:

gem install yard
yard gems

RI


If you don’t want to leave your terminal, you can also use ri to browse the documentation. You can run ri Array to view the documentation of the Array class, or ri ActionController::Base to view the rdoc docs for ActionController::Base.


That’s it for the week!