Today I Learned Notes to self about software development

    Truncate HTML without cutting off HTML tags

    I ran into an issue trying to truncate a users post in a notification email. Posts can contain markdown and thus HTML so it was difficult to style the truncated text because if the post contained HTML I could cut off a closing tag which would mess up the formatting of the rest of the email.

    I wanted a way to truncate just the text part of the String and not the HTML tags.

    The only gem I found that worked was Truncato. See this SO question.

    In the end, I created a view helper that also sanitized the HTML so I could create an “allowlist” of tags that I could keep in the output. Truncato has effectively a “denylist” option, but it felt like that could get too large.

    It looked like this:

    def truncated_sanitized_html(html, **options)
      sanitized_html  = sanitize(html, tags: options[:tags])
      Truncato.truncate(sanitized_html, max_length: options[:max_length]).html_safe
    end
    
    
    truncated_sanitized_html(post.to_html, tags: %w(p div span code table td tbody tr pre), max_length: 1000)
    

    Skip Git Commit Hooks

    Occasionally I find myself in situations where I want to make a commit, but I don’t want git commit hooks to run because it will format the code and I don’t want it to.

    (I plan to eventually format the code, but sometimes, especially when the commit is a work in progress, I don’t want to format anything since I haven’t solidified what I want to do yet)

    The easiest way to do this is to use the --no-verify flag.

    git commit -m "WIP" --no-verify
    

    Soure

    Ruby Array Intersection

    TIL you can find the intersection of two Arrays easily with &.

    a = [18, 22, 33, 4, 5, 6]
    
    b = [5, 4, 22, 1, 88, 9]
    
    c = [18, 22, 33, 40, 50, 6]
    
    # a intersecting b
    puts "intersection of a and b : #{a & b}\n\n"
    # => intersection of a and b : [22, 4, 5]
    
    # a intersecting c
    puts "intersection of a and c : #{a & c}\n\n"
    # =>intersection of a and c : [18, 22, 33, 6]
    
    # b intersecting c
    puts "intersection of b and c : #{b & c}\n\n"
    # => intersection of b and c : [22]
    

    Source

    Beware— Default Headless Browser Size

    Ran into a hard-to-debug error where a nav link was unable to be clicked on because it wasn’t “visible”. Printing the HTML of the page and turning off headless proved to me that the link was both present and visible, so I was confused for a while.

    Apparently, the default browser size for headless chrome is Something like 800, 600 which almost certainly will activate a responsive layout that could cause some elements (like navbar links) to become “invisible”.

    The browser has a wider default size in non-headless mode, so it was not immediately obvious to me that an element could be invisible when the browser is headless.

    This makes me think that it should be best practice to always specify a default browser size for tests 🤔

    Active Record methods for associations

    I learned about a few useful methods that I hadn’t heard of before.

    reflect_on_association

    This method allows you to check if an association exists, without making a database query.

    • Will return nil if no associated records are found.
    • Will return an AggregateReflection object if records are found.
    class Post < ActiveRecord::Base
      has_many :upvotes
    end
    class Upvote < ActiveRecord::Base
      belongs_to :post
    end
    
    Post.reflect_on_association(:upvotes)
    

    extract_associated

    Useful if you need to get all associated objects from an ActiveRecord::Relation.

    Assuming:

    class Author < ActiveRecord::Base
      has_many :books
    end
    
    class Book < ActiveRecord::Base
      belongs_to :author
    end
    

    and we want to get all the books from a list of authors…

    Normally, my first thought we be to do something like this:

    Author.all.includes(:books).map(&:books) 
    

    but there is a more readable way to do this with extract_associated.

    Author.all.extract_associated(:books)
    

    which actually does something similar, so the benefit is mostly the readability.

    missing

    Ever need to find records with no associated records?

    For example:

    class City < ActiveRecord::Base
      has_many :photos
    end
    
    class Photo < ActiveRecord::Base
      belongs_to :city
    end
    

    if I want to find all cities that have no photos, previously, I thought you needed to do something like this:

    City.includes(:photos).where(photos: { city_id: nil })
    

    missing does this.

    City.where.missing(:photos)
    

    associated

    New in Rails 7. Sort of the inverse of missing, but associated checks for the presence of an association.

    Finding all the cities that have photos is as simple as:

    City.where.associated(:photos)