# Vega.rb
Interactive charts for Ruby, powered by [Vega](https://vega.github.io/vega/) and [Vega-Lite](https://vega.github.io/vega-lite/)
[See it in action](https://vega.dokkuapp.com)
Works with Rails, iRuby, and other frameworks
[![Build Status](https://github.com/ankane/vega-ruby/workflows/build/badge.svg?branch=master)](https://github.com/ankane/vega-ruby/actions)
## Installation
Add this line to your application’s Gemfile:
```ruby
gem "vega"
```
Then follow the instructions for how you plan to use it:
- [Rails 7 / Importmap](#rails-7--importmap)
- [Rails 7 / esbuild or Webpack](#rails-7--esbuild-or-webpack)
- [Rails 6 / Webpacker](#rails-6--webpacker)
- [Rails 5 / Sprockets](#rails-5--sprockets)
- [iRuby](#iruby)
- [Other](#other)
### Rails 7 / Importmap
Add to `config/importmap.rb`:
```ruby
pin "vega", to: "vega.js"
pin "vega-lite", to: "vega-lite.js"
pin "vega-embed", to: "vega-embed.js"
```
And add to `app/javascript/application.js`:
```js
import "vega"
import "vega-lite"
import "vega-embed"
window.dispatchEvent(new Event("vega:load"))
```
### Rails 7 / esbuild or Webpack
Run:
```sh
yarn add vega vega-lite vega-embed
```
And add to `app/javascript/application.js`:
```js
import embed from "vega-embed"
window.vegaEmbed = embed
window.dispatchEvent(new Event("vega:load"))
```
### Rails 6 / Webpacker
Run:
```sh
yarn add vega vega-lite vega-embed
```
And add to `app/javascript/packs/application.js`:
```js
window.vegaEmbed = require("vega-embed").default
```
### Rails 5 / Sprockets
Add to `app/assets/javascripts/application.js`:
```js
//= require vega
//= require vega-lite
//= require vega-embed
```
### iRuby
No additional set up is needed.
### Other
For Sinatra and other web frameworks, download [Vega](https://cdn.jsdelivr.net/npm/vega@5), [Vega-Lite](https://cdn.jsdelivr.net/npm/vega-lite@5), and [Vega-Embed](https://cdn.jsdelivr.net/npm/vega-embed@6) and include them on pages with charts:
```html
```
## Getting Started
Vega is a visualization grammar, and Vega-Lite is a high-level grammar built on top of it. We recommend using Vega-Lite by default and moving to Vega for advanced use cases.
Create visualizations by chaining together methods:
```ruby
Vega.lite.data(data).mark("bar").height(200)
```
There are methods for each of the top-level properties. The docs are a great source of examples:
- [Vega-Lite docs](https://vega.github.io/vega-lite/docs/)
- [Vega docs](https://vega.github.io/vega/docs/)
## Example
Create a bar chart
```ruby
Vega.lite
.data([{city: "A", sales: 28}, {city: "B", sales: 55}, {city: "C", sales: 43}])
.mark(type: "bar", tooltip: true)
.encoding(
x: {field: "city", type: "nominal"},
y: {field: "sales", type: "quantitative"}
)
```
The chart will automatically render in iRuby. For Rails, render it in your view:
```erb
<%= Vega.lite.data(...) %>
```
## Vega-Lite
Start a Vega-Lite chart with:
```ruby
Vega.lite
```
### Data
[Docs](https://vega.github.io/vega-lite/docs/data.html)
Data can be an array
```ruby
data([{x: "A", y: 1}, {x: "B", y: 2}])
```
Or a URL
```ruby
data("https://www.example.com/data.json")
```
Or a Rover data frame
```ruby
data(df)
```
Or a data generator
```ruby
data(sequence: {start: 0, stop: 10, step: 1, as: "x"})
```
### Transforms
[Docs](https://vega.github.io/vega-lite/docs/transform.html)
```ruby
transform(bin: true, field: "a", as: "binned a")
```
### Marks
[Docs](https://vega.github.io/vega-lite/docs/mark.html)
Bar chart
```ruby
mark("bar")
```
Line chart
```ruby
mark("line")
```
Pie chart
```ruby
mark("pie")
```
Area chart
```ruby
mark("area")
```
Enable tooltips
```ruby
mark(type: "bar", tooltip: true)
```
### Encoding
[Docs](https://vega.github.io/vega-lite/docs/mark.html)
```ruby
encoding(x: {field: "a", type: "ordinal"})
```
### Projection
[Docs](https://vega.github.io/vega-lite/docs/projection.html)
```ruby
projection(type: "albersUsa")
```
### View Composition
[Docs](https://vega.github.io/vega-lite/docs/composition.html)
Faceting
```ruby
facet(row: {field: "x"})
```
Layering
```ruby
layer(view)
```
Concatenation
```ruby
hconcat(view)
vconcat(view)
concat(view)
```
Repeating
```ruby
repeat(row: ["a", "b", "c"])
```
Resolving
```ruby
resolve(scale: {color: "independent"})
```
### Selections
[Docs](https://vega.github.io/vega-lite/docs/selection.html)
```ruby
selection(x: {type: "single"})
```
### Config
[Docs](https://vega.github.io/vega-lite/docs/config.html)
Set the font
```ruby
config(font: "Helvetica")
```
### Top-Level Properties
[Docs](https://vega.github.io/vega-lite/docs/spec.html#top-level)
Set width and height
```ruby
width(500).height(300)
```
Set the background color
```ruby
background("#000")
```
Set padding
```ruby
padding(5)
# or
padding(left: 5, top: 5, right: 5, bottom: 5)
```
### Embed Options
[Docs](https://github.com/vega/vega-embed#options)
Set embed options
```ruby
embed_options(actions: true)
```
## Vega
You can also use Vega directly. In this case, you don’t need to include Vega-Lite in the JavaScript files.
Start a Vega chart with:
```ruby
Vega.start
```
## Spec
You can also create a specification by hand
```ruby
spec = {
"$schema" => "https://vega.github.io/schema/vega-lite/v5.json",
"data" => {"url" => "https://www.example.com"},
# ...
}
```
And render it in Rails
```erb
<%= vega_chart spec %>
```
Or display it in iRuby
```ruby
Vega.display(spec)
```
Get the spec for a chart
```ruby
chart.spec
```
## Exporting Charts (experimental)
Export charts to PNG, SVG, or PDF. This requires Node.js and npm 7+. Run:
```sh
yarn add vega-cli vega-lite
```
For PNG, use:
```ruby
File.binwrite("chart.png", chart.to_png)
```
For SVG, use:
```ruby
File.binwrite("chart.svg", chart.to_svg)
```
For PDF, use:
```ruby
File.binwrite("chart.pdf", chart.to_pdf)
```
## Content Security Policy (CSP)
### Styles and Frames
Enable unsafe inline styles and blob frames on actions that have charts
```ruby
class ChartsController < ApplicationController
content_security_policy only: :index do |policy|
policy.style_src :self, :unsafe_inline
policy.frame_src :blob
end
end
```
### Nonce
Automatically add a nonce when configured in Rails with:
```erb
<%= vega_chart chart %>
```
### Interpreter
By default, the Vega parser uses the Function constructor, which [can cause issues with CSP](https://vega.github.io/vega/usage/interpreter/).
For Rails 7 / Importmap, add to `config/importmap.rb`:
```ruby
pin "vega-interpreter", to: "vega-interpreter.js"
```
And add to `app/javascript/application.js`:
```js
import "vega-interpreter"
```
For Rails 6 / Webpacker, run:
```sh
yarn add vega-interpreter
```
For Rails 5 / Sprockets, add to `app/assets/javascripts/application.js`:
```js
//= require vega-interpreter
```
And set embed options for your charts
```ruby
embed_options(ast: true)
```
## History
View the [changelog](https://github.com/ankane/vega-ruby/blob/master/CHANGELOG.md)
## Contributing
Everyone is encouraged to help improve this project. Here are a few ways you can help:
- [Report bugs](https://github.com/ankane/vega-ruby/issues)
- Fix bugs and [submit pull requests](https://github.com/ankane/vega-ruby/pulls)
- Write, clarify, or fix documentation
- Suggest or add new features
To get started with development:
```sh
git clone https://github.com/ankane/vega-ruby.git
cd vega-ruby
bundle install
bundle exec rake test
```
Resources for contributors:
- [Vega specification](https://vega.github.io/vega/docs/specification/)
- [Vega-Lite specification](https://vega.github.io/vega-lite/docs/spec.html)