Rails is a great framework, once you stop thinking in Java patterns and embraice DHH way of writing web apps.
Interestingly, Basecamp uses exactly the same ActiveStorage implementation, but shows full-size and download links alone with the PDF attachment. But if you only follow the guide, it’s hard to implement by your own without some gotchas.
These gotchas I want to capture here in this how-to article. It assumes reader followed official guide and stuck with PDF thumbnails preview implementation.
As many of us, I was writing data migrations as a part of database schema migrations.
That’s a bad practice, and here is how I perform the same nowadays.
Migration from the plain text fields to rich text fields with ActiveText. How?
Steps 1-2-3 are done within a single new PR. Once the schema is in the new state, I perform rake tasks for data movement.
Argh, some things I should have read earlier! One of these is this brilliant blog post Vanilla Rails is plenty had a great influence on me.
Here is the list of gems i’m ditching out of my Rails projects.
- knockout-rails
- paloma
- simple-form
- rails-timeago
- carrierwave
-- we are here --
- devise
- haml
- resque
- resque-web
- resque-scheduler
- factory-bot
- rspec
- bower-rails
- sprockets
- google-visualr
- recurring-select
- seleect2-rails
- pundit
adding a few
GitHub CLI is a nice way to query GitHub API locally
gh api orgs/<org>/teams | jq '.[] | {name,slug}'
Adding RSpec support to VSCode
in Gemfile, and Ruby-LSP from Shopify
group :development do
gem "ruby-lsp-rspec", require: false
gem "ruby-lsp-rails", require: false
end
include ActiveSupport::Testing::TimeHelpers
travel_to 2.days.ago
One of the projects I’ve started a long-long time ago still powers the business. It was quite a journey starting from Rails 2, than Rails 3 and so on up until currently Rails 7 with plans to bump to Rails 8.
The project has live updates with Server Sent Events and a sidecar microservice to keep the connections and push updates. Quite similar to what DHH have done with ActiveCable, but based on different stack.
I lived on GitHub Pages for static hosting for a long time. For example, this blog been hosted as https://mprokopov.github.io for years, just with the custom domain https://prokopov.me
But last week I finally moved everything to CloudFlare Pages and quite happy about it!
The major driver for the change was a hassle of management github-pages. It’s either your have to keep everything in a separate branch, with the name like github-pages
, or have to use completly different repository.
I chose the latter, and for a long time kept using two repositories, one with Hugo sources, second with rendered HTML. It was a bit painful to manage two git repositories in the same working tree, hugo build
renders the website to the public
folder. In addition, the repository with HTML should be public.
We all love the fast software. Ghostty is crazy fast.
What I use with my macos keyboard comes from Emacs. Namely, Cmd+
Here is an example of a config extracted from this discussion.
Example of the Python code to catch EC2 termination signal and ping Jenkins to run termination job. Useful to execute graceful shutdown if you’re using EC2 spot instaces.
You will need Jenkins Generic Webhook plugin.
import json
import boto3
import urllib3
import os
JENKINS_JOB_TOKEN = os.environ.get('JENKINS_TOKEN')
JENKINS_URL = os.environ.get('JENKINS_URL')
def invoke_jenkins(target):
http = urllib3.PoolManager()
return http.request('POST', f"{JENKINS_URL}/generic-webhook-trigger/invoke?instance={target}", headers={'Authorization': f"Bearer {JENKINS_JOB_TOKEN}"})
def find_target(instance_id):
ec2 = boto3.client("ec2")
tags = ec2.describe_tags(Filters=[{'Name': 'resource-id', 'Values': [instance_id]}])
for tag in tags['Tags']:
if tag['Key'] == "Name":
return tag['Value']
def lambda_handler(event, context):
instance_id = event["detail"]["instance-id"]
target = find_target(instance_id)
if not target:
return {
'statusCode': 422,
'body': f"target for {instance_id} not found"
}
print(f"Jenkins termination job called for instance: {instance_id} target {target}")
resp = invoke_jenkins(target)
print(f"Jenkins response status {resp.status}")
print(f"Jenkins decoded response")
print(resp.data.decode('utf-8'))
return {
'statusCode': resp.status,
'body': json.dumps(resp.data.decode('utf-8'))
}
It’s often the job increases retry counter, but there is no time to waste after the root cause of failing job being removed.
Here is the way to enforce re-run of the job. Unfortunately, I couldn’t find any means to achieve the same from the UI
nomad job eval -force-reschedule rundeck