Professional charts from CSV, XLSX, and YAML data files. Zero dependencies, pure Ruby rendering using ruby-libgd.
RubyCharts.from_csv('sales.csv')
.type(:pie)
.title('Q1 Sales Distribution')
.save('report.png')✅ Multiple Chart Types
- Pie charts
- Vertical bar charts
- Horizontal bar charts
- Line charts
✅ Multiple Data Sources
- CSV files
- XLSX (Excel) spreadsheets
- YAML files
- Ruby hashes (inline data)
✅ Professional Styling
- Custom colors
- Titles and subtitles
- Automatic legends
- Text labels with FreeType rendering
- Grid lines and axes
✅ Performance
- Server-side rendering (100ms per chart)
- Zero external APIs
- Scalable to 1000+ charts/second
- Works offline
✅ Rails Integration
- Perfect for admin dashboards
- Embed in emails
- Generate reports in background jobs
- Serve via API
Add to your Gemfile:
gem 'ruby-charts'Then bundle:
bundle installOr install directly:
gem install ruby-chartsruby-charts requires:
- Ruby 2.7+
- ruby-libgd (automatically installed)
- TrueType fonts (system fonts auto-detected)
Supported on:
- ✅ Linux (Ubuntu, Debian, CentOS)
- ✅ macOS
- ✅ Windows
require 'ruby_charts'
RubyCharts.from_csv('data.csv')
.type(:pie)
.title('Product Distribution')
.save('chart.png')CSV Format:
Category,Value
Apple,150
Banana,200
Orange,120RubyCharts.from_xlsx('data.xlsx', sheet: 'Sales')
.type(:bar)
.title('Monthly Sales')
.save('chart.png')XLSX Format:
| Product | Sales |
|---|---|
| Apple | 150 |
| Banana | 200 |
RubyCharts.from_yaml('data.yml')
.type(:line)
.title('Growth Trend')
.save('chart.png')YAML Format:
data:
- name: January
value: 5000
- name: February
value: 7200
- name: March
value: 6800data = {
labels: ['Q1', 'Q2', 'Q3', 'Q4'],
values: [100, 150, 120, 180]
}
RubyCharts.from_hash(data)
.type(:bar)
.title('Quarterly Performance')
.save('chart.png').type(:pie) # Pie chart
.type(:bar) # Vertical bar chart
.type(:vertical_bar) # Same as :bar
.type(:horizontal_bar) # Horizontal bar chart
.type(:line) # Line chart with pointsRubyCharts.from_csv('sales.csv')
.type(:pie)
.title('Sales Analysis')
.subtitle('Q1 2024')
.colors(['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A'])
.legend(position: :right)
.font('/path/to/font.ttf')
.save('report.png')| Method | Type | Default | Description |
|---|---|---|---|
type() |
Symbol | :bar |
Chart type |
title() |
String | 'Chart' |
Chart title |
subtitle() |
String | nil |
Subtitle text |
colors() |
Array | System colors | RGB color array: ['#FF6B6B', '#4ECDC4'] |
legend() |
Symbol | :right |
Legend position |
font() |
String | Auto-detect | Path to TrueType font |
save() |
String | N/A | Output filename |
require 'ruby_charts'
# CSV with product sales
csv_data = 'product,sales
Apple,15000
Banana,8500
Orange,12000
Mango,9800'
File.write('sales.csv', csv_data)
# Generate charts
RubyCharts.from_csv('sales.csv')
.type(:pie)
.title('Product Sales Distribution')
.subtitle('Month of May 2024')
.colors(['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A'])
.save('sales_pie.png')
RubyCharts.from_csv('sales.csv')
.type(:horizontal_bar)
.title('Product Sales Ranking')
.save('sales_bar.png')# Using CoinGecko to fetch Bitcoin data
require 'ruby_charts'
require 'httparty'
response = HTTParty.get('https://api.coingecko.com/api/v3/coins/bitcoin/market_chart',
query: { vs_currency: 'usd', days: 30 })
prices = response['prices']
# Create dataset
data = {
labels: prices.map { |p| Time.at(p[0]/1000).strftime('%m-%d') },
values: prices.map { |p| p[1] }
}
# Generate chart
RubyCharts.from_hash(data)
.type(:line)
.title('Bitcoin 30-Day Price Trend')
.subtitle('Data from CoinGecko')
.colors(['#F7931A'])
.save('bitcoin_trend.png')# app/controllers/admin/dashboard_controller.rb
class Admin::DashboardController < ApplicationController
def index
@sales = generate_sales_chart
@revenue = generate_revenue_chart
end
private
def generate_sales_chart
data = {
labels: ['Product A', 'Product B', 'Product C'],
values: [450, 320, 280]
}
RubyCharts.from_hash(data)
.type(:pie)
.title('Sales by Product')
.save("tmp/sales_#{Time.now.to_i}.png")
end
def generate_revenue_chart
months = Date.today.last_months(6).reverse
revenue = [5000, 7200, 6800, 8100, 7500, 9200]
data = {
labels: months.map { |d| d.strftime('%b') },
values: revenue
}
RubyCharts.from_hash(data)
.type(:line)
.title('Revenue Trend')
.save("tmp/revenue_#{Time.now.to_i}.png")
end
end# app/mailers/report_mailer.rb
class ReportMailer < ApplicationMailer
def monthly_summary
# Generate chart
chart_file = generate_report_chart
# Attach to email
attachments['report.png'] = File.read(chart_file)
# Send email with chart
mail(to: @user.email, subject: 'Monthly Report')
end
private
def generate_report_chart
data = monthly_data
RubyCharts.from_hash(data)
.type(:bar)
.title("#{@user.name}'s Monthly Summary")
.save("tmp/report_#{@user.id}_#{Date.today}.png")
end
end# app/controllers/api/charts_controller.rb
class Api::ChartsController < ApplicationController
def generate
symbol = params[:symbol]
data = fetch_price_data(symbol)
# Generate chart
filename = RubyCharts.from_hash(data)
.type(:line)
.title("#{symbol} Price Chart")
.save("public/charts/#{symbol}.png")
# Return image
send_file filename, type: 'image/png'
end
endDefault (10 colors):
[
'#FF6B6B', # Red
'#4ECDC4', # Teal
'#45B7D1', # Blue
'#FFA07A', # Orange
'#9AD8C8', # Mint
'#F7DC6F', # Yellow
'#BB8FCE', # Purple
'#85C1E2', # Light Blue
'#F8B88B', # Peach
'#AAC6BA' # Gray
]Use hex colors or RGB arrays:
# Hex colors
.colors(['#FF6B6B', '#4ECDC4', '#45B7D1'])
# RGB arrays
.colors([[255, 107, 107], [78, 205, 196], [69, 183, 209]])
# Mix both
.colors(['#FF6B6B', [78, 205, 196]])| Operation | Time | Notes |
|---|---|---|
| Load CSV (1000 rows) | 15ms | Pandas-like speed |
| Render pie chart | 45ms | Per chart |
| Render bar chart | 35ms | Per chart |
| Render line chart | 50ms | Per chart |
| Save PNG | 20ms | Per file |
| Total (CSV→PNG) | ~110ms | End-to-end |
# Generate 100 charts in ~11 seconds
100.times do |i|
RubyCharts.from_csv("data_#{i}.csv")
.type(:pie)
.save("output_#{i}.png")
endIf text doesn't appear in charts:
# Check available fonts
RubyCharts::Config.font(:regular)
# => "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
# Specify custom font
RubyCharts.from_csv('data.csv')
.type(:pie)
.font('/path/to/your/font.ttf')
.save('chart.png')Font paths by OS:
- Linux:
/usr/share/fonts/truetype/ - macOS:
/Library/Fonts/ - Windows:
C:\Windows\Fonts\
Increase DPI or size:
# Larger chart size = sharper text
RubyCharts.from_csv('data.csv')
.type(:pie)
# Chart is 1200x700 by default
# This is sufficient for most uses
.save('chart.png')Make sure colors are valid hex or RGB:
# ✅ Valid
.colors(['#FF6B6B', '#4ECDC4'])
.colors([[255, 107, 107], [78, 205, 196]])
# ❌ Invalid
.colors(['red', 'blue']) # Color names not supportedEnsure the XLSX file is valid:
# ✅ Correct
RubyCharts.from_xlsx('data.xlsx', sheet: 'Sales')
# ❌ Wrong sheet name
RubyCharts.from_xlsx('data.xlsx', sheet: 'NonExistent')
# Specify correct sheet nameruby-charts depends on:
ruby-libgd # Native graphics binding
roo # XLSX reading
csv # CSV reading (stdlib)
yaml # YAML reading (stdlib)That's it. No heavy frameworks, no external APIs.
Bug reports and pull requests are welcome! Please:
- Fork the repository
- Create a feature branch
- Test your changes
- Submit a pull request
- More chart types (stacked bars, scatter, area)
- Export formats (PDF, SVG)
- Data transformations (aggregation, filtering)
- Theme system
- Documentation improvements
- Bug fixes and performance improvements
See CONTRIBUTING.md for details.
MIT License - see LICENSE file for details.
ruby-charts is part of the ruby-libgd ecosystem:
-
ruby-libgd - Native graphics library for Ruby
-
libgd-gis - Geographic data processing
Germán Silva - @ggerman
- RubyStackNews - Ruby-focused newsletter (2,500+ subscribers)
- GitHub
- Open an issue
- Email: [email protected]
- Discord: Ruby Community
Please open an issue with:
- Ruby version
- OS
- ruby-charts version
- Minimal code to reproduce
Open an issue and describe:
- Your use case
- Example code
- Why it's important
Made with ❤️ by someone who thinks Ruby deserves better graphics.
Questions? Issues? Ideas? Let's talk!