Skip to content

Instantly share code, notes, and snippets.

View forsbergplustwo's full-sized avatar

Bjørn Forsberg forsbergplustwo

View GitHub Profile
@forsbergplustwo
forsbergplustwo / ferrum.md
Last active May 15, 2023 18:46
Generating PDFs in Ruby, using Ferrum gem on Heroku

Generating PDFs in Ruby, using Ferrum gem on Heroku

by Bjorn Forsberg

image

Ferrum is a pure Ruby gem, providing a high-level API to control Chrome, which runs headless by default. Ferrum is a great alternative to Node's Puppeteer library, which is a pain to use with Ruby and Heroku.

Ferrum can be used for any browser scripting work, as well as for generating screenshots and PDFs. We use it mainly for PDF generation, hosted on Heroku, and the setup flow goes like this:

  1. Add Chromium locally if not already done: brew install chromium
@forsbergplustwo
forsbergplustwo / 1_my_engine.gemspec
Last active October 25, 2021 08:55
Adding Javascript from a Rails Engine, using importmap-rails
# my_engine/my_engine.gemspec
spec.add_dependency "importmap-rails", "~> 0.7.6"
# # USAGE
#
# # Include the concern in the class
# class Settings::CreateSetting
# include SimpleServiceObject
#
# # optional: include validation
# validates :shop, presence: true
#
# # mandatory: initialize the command with keyword arguments
@forsbergplustwo
forsbergplustwo / graphql_response_rubyfier.rb
Last active August 24, 2021 05:57
Proof of concept! Transforms Shopify GraphQL API responses to remove the need to type "edges" and "node", while also adding support for Ruby dot notation. We could also transform createAt to created_at.. but I think that will be "too much magic", as it won't match docs etc.
require 'json'
class GraphqlResponseRubyfier
def initialize(response)
@response = JSON.parse(response)
end
def rubyfy
rubyfied_response = to_o(@response)
puts(rubyfied_response)
require "ruby-boolean"
class TypeValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
value_to_test = type_before_coercion(record, attribute, value)
expected_type = type_class(options[:with])
record.errors.add attribute, (options[:message] || "argument of #{record.class} is not of class: #{options[:with].to_s.camelize}. It is class: #{value.class} with value of #{value}") unless value_to_test.is_a?(expected_type)
end
private
@forsbergplustwo
forsbergplustwo / gist:ee60395c51607eb847916d7caee85137
Created November 6, 2019 10:22 — forked from sebboh/gist:f1dfe4f096746c45f3e9ea06a09743a0
Installing a Gem on Heroku from a Private GitHub Repo

Installing a Gem on Heroku from a Private GitHub Repo

Sometimes you want to use a gem on Heroku that is in a private repository on GitHub.

Using git over http you can authenticate to GitHub using basic authentication. However, we don't want to embed usernames and passwords in Gemfiles. Instead, we can use authentication tokens.

This method does not add your OAuth token to Gemfile.lock. It uses bundle config to store your credentials, and allows you to configure Heroku to use environment variables when deploying.

  1. Generate an OAuth token from GitHub
CollectionsQuery = <<~GRAPHQL
{
shop {
name
}
collections(first: 15, sortKey: UPDATED_AT, reverse: true) {
edges {
node {
id
title
@forsbergplustwo
forsbergplustwo / spr.js
Created July 11, 2018 17:15
Shopify Reviews App - Currently it's not possible to reveal the "Write a review" form when linking to a product page. This would be useful for asking customers to provide a review of the product via email. Revision 1 is the original code from Shopify (spr.js), Revision 2 is my (very inexperienced) suggested change.
!function() {
var e = function(e) {
var t = {
exports: {}
};
return e.call(t.exports, t, t.exports),
t.exports
};
e(function() {
"use strict";
@forsbergplustwo
forsbergplustwo / source-works-does-not-contain-body-html.txt
Last active April 6, 2017 14:20
This is the exact same email code as the the one that is failing, only difference is that the HTML is not added to the body-html header attribute.
Return-Path: <[email protected]>
Delivered-To: [email protected]
Received: from director1.mail.unoeuro.com ([172.17.165.33])
by worker6.mail.unoeuro.com (Dovecot) with LMTP id +ZWcANlN5lhr2wAAao8G7A
for <[email protected]>; Thu, 06 Apr 2017 16:16:57 +0200
Received: from worker3.mail.unoeuro.com ([172.17.165.33])
by director1.mail.unoeuro.com (Dovecot) with LMTP id 6YBwF9lN5lgFOAAA1VGibg
; Thu, 06 Apr 2017 16:16:57 +0200
Received: from localhost (localhost [127.0.0.1])
by worker3.mail.unoeuro.com (Postfix) with ESMTP id 57C01160074
Return-Path: <[email protected]>
Delivered-To: [email protected]
Received: from director1.mail.unoeuro.com ([172.17.165.10])
by worker6.mail.unoeuro.com (Dovecot) with LMTP id y97tLkpO5lg93gAAao8G7A
for <[email protected]>; Thu, 06 Apr 2017 16:18:50 +0200
Received: from worker14.mail.unoeuro.com ([172.17.165.10])
by director1.mail.unoeuro.com (Dovecot) with LMTP id G5uTLEpO5lgPOAAA1VGibg
; Thu, 06 Apr 2017 16:18:50 +0200
Received: from localhost (localhost [127.0.0.1])
by worker14.mail.unoeuro.com (Postfix) with ESMTP id AB088E0070