Hello World
Debug Info:
Slug: No slug provided
Using element ID: blog-content-2
Content length: 5119 characters
Content preview: Hello! This is my first blog post on austn.net. I...
Hello! This is my first blog post on austn.net.
I'm Austin French, a backend developer from California. Lately I've been trying to use AI to learn as much as I possibly can. Part of that means I'll need a place to write my notes!
The Method: Obsidian
Currently I'm using Obsidian for most of my writing. I love the markdown interface, and owning my own files makes it a tempting choice. There's, of course, great tagging system, inter and intra hyperlinks, and a mind-map. I'm less interested in those features -- but there's a lot of reason to pick Obsidian over other tools like Notion or Apple Notes.
Of course, there's a time and a place for everything.
Eventually I will write an in-depth article about how I built this website, but for now I figure this could be a good time to discuss this specific feature!
The code!
A thousand miles in the sky, this website is a simple React on Rails app. I manage it with Hatchbox on my personal DigitalOcean instance.
On the other end, I write in Obsidian:
Then I use this script to automatically check for new markdown files upon deploy:
rubyclass ImportObsidianNotesJob < ApplicationJob
queue_as :default
OBSIDIAN_PUBLIC_PATH = File.join(
ENV["OBSIDIAN_PATH"] || File.join(Dir.home, "PATH_TO_OBSIDIAN_VAULT")
)
CONTENT_BLOG_POSTS_PATH = Rails.root.join("content", "blog_posts")
def perform
FileUtils.mkdir_p(CONTENT_BLOG_POSTS_PATH)
if ENV["OBSIDIAN_PATH"] && Dir.exist?(OBSIDIAN_PUBLIC_PATH)
import_from_obsidian
else
import_from_content_directory
end
end
private
def import_from_obsidian
Rails.logger.info "Importing markdown files from Obsidian at #{OBSIDIAN_PUBLIC_PATH}"
Dir.glob(File.join(OBSIDIAN_PUBLIC_PATH, "**/*.md")).each do |file_path|
filename = File.basename(file_path)
target_path = File.join(CONTENT_BLOG_POSTS_PATH, filename)
FileUtils.cp(file_path, target_path)
Rails.logger.info "Copied #{filename} to content directory"
process_markdown_file(file_path)
end
end
def import_from_content_directory
Rails.logger.info "Importing markdown files from content directory at #{CONTENT_BLOG_POSTS_PATH}"
Dir.glob(File.join(CONTENT_BLOG_POSTS_PATH, "*.md")).each do |file_path|
process_markdown_file(file_path)
end
end
def process_markdown_file(file_path)
content = File.read(file_path)
filename = File.basename(file_path, ".md")
frontmatter, markdown_content = extract_frontmatter(content)
return if frontmatter["draft"] == true
title = frontmatter["title"] || filename.titleize
slug = frontmatter["slug"] || filename.parameterize
blog_post = BlogPost.find_or_initialize_by(slug: slug)
blog_post.assign_attributes(
title: title,
content: markdown_content,
published_at: frontmatter["date"],
metadata: frontmatter
)
if blog_post.save
Rails.logger.info "Imported: #{title}"
else
Rails.logger.error "Failed to import #{file_path}: #{blog_post.errors.full_messages.join(', ')}"
end
end
def extract_frontmatter(content)
frontmatter = {}
markdown_content = content
# Check for YAML frontmatter (between --- markers)
if content.start_with?("---")
parts = content.split("---", 3)
if parts.length >= 3
begin
frontmatter = YAML.safe_load(parts[1])
markdown_content = parts[2..-1].join("---")
rescue => e
Rails.logger.error "Error parsing frontmatter: #{e.message}"
end
end
end
[ frontmatter, markdown_content ]
end
end
Note this has two branches of logic: Locally, we're pulling directly from Obsidian and creating markdown files. Then, in production we're reading from those files to generate BlogPosts that show up at austn.net/blog
We also hooked up a postdeploy script here:
js {
"scripts": {
"postdeploy": "bundle exec rails runner 'PostDeployJob.perform_now'"
}
}
which runs this simple job after each deploy:
rubyclass PostDeployJob < ApplicationJob
queue_as :critical
def perform
ImportObsidianNotesJob.perform_now
end
end
This is, naturally, a super quick overview. But I wanted to show that it works! And, I also wanted to show off some of the other cool markdown features we have like:
- lists
- that
- aren't
- ordered
But also:
- Even
- Ordered
- Lists!
single line of code
works for a single line of code
We have quotes
We also have quotes! And bold and italics!
Heh. Thanks for reading.
Austin