A Ruby client for the zettel.box REST API.
Add this line to your application's Gemfile:
gem "zettel_box"And then execute:
bundle installrequire "zettel_box"
client = ZettelBox::Client.new(
token: "your_api_token",
box_slug: "bA1b2C3d4E5f6G7h8I9j0K1"
)
# List all zettels in a box
client.zettels.list
# Filter by type and visibility
client.zettels.list(type: "article", public: "true")
# Find a single zettel
client.zettels.find(slug: "zA1b2C3...")
# Search across boxes
client.search.zettels(q: "quantum biology", limit: 10)
# Box operations
client.boxes.list
client.boxes.find(slug: "bA1b2C3...")ZettelBox::Client.configure do |config|
config.token = "your_api_token"
config.box_slug = "bA1b2C3d4E5f6G7h8I9j0K1"
config.base_url = "https://app.zettel.box/api/v1"
config.timeout = 30
config.retries = 3
end
# All subsequent clients use the global configuration
client = ZettelBox::Client.newThe client raises specific exceptions for different HTTP status codes:
| Exception | HTTP Status | Description |
|---|---|---|
ZettelBox::AuthenticationError |
401 | Invalid or missing API token |
ZettelBox::ReadOnlyError |
403 (read_only) |
Token has read-only access |
ZettelBox::PermissionError |
403 | Insufficient permissions |
ZettelBox::NotFoundError |
404 | Resource not found |
ZettelBox::ValidationError |
422 | Validation failed (includes .errors hash) |
ZettelBox::ParameterMissingError |
422 (parameter_missing) |
Required parameter missing |
ZettelBox::ServerError |
5xx | Server-side error |
begin
client.zettels.find(slug: "unknown")
rescue ZettelBox::NotFoundError
# handle not found
endThe client supports pluggable caching via any object that responds to fetch(key, expires_in:, &block) and delete_matched(pattern).
The easiest way to add caching in a Rails application is with the Redis cache store.
1. Add Redis to your Gemfile:
gem "redis", "~> 5.0"2. Configure config.cache_store:
# config/environments/development.rb
config.cache_store = :redis_cache_store, {
url: "redis://localhost:6379/1",
expires_in: 5.minutes
}
# config/environments/production.rb
config.cache_store = :redis_cache_store, {
url: ENV["REDIS_URL"],
expires_in: 1.hour
}3. Wire the client to Rails.cache in an initializer:
# config/initializers/zettel_box.rb
require "zettel_box"
ZettelBox::Client.configure do |config|
config.token = Rails.application.credentials.dig(:zettel_box, :api_key)
config.box_slug = Rails.application.credentials.dig(:zettel_box, :box_slug)
config.base_url = Rails.application.credentials.dig(:zettel_box, :api_url)
config.cache_store = Rails.cache
config.cache_ttl = Rails.env.development? ? 5.minutes : 1.hour
end4. Use the client in your models or controllers:
class Article
def self.all
client.zettels.list(type: "article", public: "true")
end
def self.find(slug)
client.zettels.find(slug: slug)
end
private
def self.client
@client ||= ZettelBox::Client.new(
skip_cache: Rails.env.development?
)
end
endRails 8 ships with Solid Cache, a database-backed cache that does not require Redis.
1. Add Solid Cache to your Gemfile:
gem "solid_cache"2. Run the install generator:
bin/rails solid_cache:install3. Configure the cache store:
# config/environments/production.rb
config.cache_store = :solid_cache_store4. Wire the client as shown above.
Note: Solid Cache does not implement
delete_matchedby default. If you rely onclient.clear_cache!, you may need to use Redis or implement a custom cache wrapper.
All read methods accept a skip_cache: true option to bypass the cache for a single request:
client.zettels.list(skip_cache: true)
client.zettels.find(slug: "z1", skip_cache: true)
client.boxes.list(skip_cache: true)Invalidate all cached entries with the clear_cache! method:
client.clear_cache!# List zettels
client.zettels.list # all
client.zettels.list(type: "article") # filter by type
client.zettels.list(public: "true") # filter by visibility
client.zettels.list(q: "quantum", limit: 10) # search within box
# Find a zettel
client.zettels.find(slug: "zA1b2C3...")
# Create a zettel
client.zettels.create(title: "My Zettel", content_markdown: "# Hello")
# Update a zettel
client.zettels.update(slug: "zA1b2C3...", title: "Updated Title")
# Delete a zettel
client.zettels.delete(slug: "zA1b2C3...")
# Restore a soft-deleted zettel
client.zettels.restore(slug: "zA1b2C3...")# List boxes
client.boxes.list
# Find a box
client.boxes.find(slug: "bA1b2C3...")
# Create a box
client.boxes.create(name: "My Box", description: "A description")
# Update a box
client.boxes.update(slug: "bA1b2C3...", name: "New Name")
# Delete a box
client.boxes.delete(slug: "bA1b2C3...")# Search zettels across all accessible boxes
client.search.zettels(q: "grounding", limit: 5)Run the test suite:
bundle exec rake testThe gem is available as open source under the terms of the MIT License.