Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add components #3

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
language: ruby
rvm:
- 2.2.2
notifications:
email: true
matrix:
include:
- rvm: 2.0.0
gemfile: gemfiles/Gemfile.activesupport-3.x
- rvm: 2.2.2
gemfile: gemfiles/Gemfile.activesupport-4.x
4 changes: 4 additions & 0 deletions gemfiles/Gemfile.activesupport-3.x
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
source 'http://rubygems.org'

gem 'activesupport', '~> 3.0'
gemspec path: '../'
4 changes: 4 additions & 0 deletions gemfiles/Gemfile.activesupport-4.x
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
source 'http://rubygems.org'

gem 'activesupport', '~> 4.0'
gemspec path: '../'
5 changes: 4 additions & 1 deletion lib/squid.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
require 'prawn'

require 'active_support/core_ext/string/inflections' # for titleize

require 'squid/config'
require 'squid/interface'

module Squid
end
end
23 changes: 23 additions & 0 deletions lib/squid/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
require 'squid/settings'

module Squid
# Abstract class that delegates unhandled calls to a +pdf+ object which
# is convenient when working with Prawn methods
class Base
extend Settings

attr_reader :pdf, :data

def initialize(document, data = {}, settings = {})
@pdf = document
@data = data
@settings = settings
end

# Delegates all unhandled calls to object returned by +pdf+ method.
def method_missing(method, *args, &block)
return super unless pdf.respond_to?(method)
pdf.send method, *args, &block
end
end
end
2 changes: 1 addition & 1 deletion lib/squid/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ def initialize
@height = ENV.fetch('SQUID_HEIGHT', '200').to_f
end
end
end
end
30 changes: 12 additions & 18 deletions lib/squid/graph.rb
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
require 'squid/settings'
require 'squid/base'
require 'squid/graph/legend'

module Squid
class Graph
extend Settings
class Graph < Base
has_settings :height

attr_reader :pdf

def initialize(document, settings = {})
@pdf = document
@settings = settings
end

# Draws the graph.
def draw
bounding_box [0, pdf.cursor], width: bounds.width, height: height do
stroke_bounds
bounding_box [0, cursor], width: bounds.width, height: height do
Legend.new(pdf, data.keys).draw

# The baseline is last so it’s drawn on top of any graph element.
stroke_horizontal_line 0, bounds.width, at: cursor - height
end
end

# Delegates all unhandled calls to object returned by +pdf+ method.
def method_missing(method, *args, &block)
return super unless pdf.respond_to?(method)
pdf.send method, *args, &block
end
private

end
end
end
77 changes: 77 additions & 0 deletions lib/squid/graph/legend.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
require 'squid/base'

module Squid
# Adds a legend to the graph. The legend is drawn in a floating bounding box
# at the top of the graph, in the right side. The dimensions of the legend
# are fixed: height, width and font size cannot be customized.
# When multiple series are provided, the legend includes all of them, ordered
# from the right to the left.
class Legend < Base

def draw
float do
bounding_box [width, cursor+height*2], width: width, height: height do
right_margin = bounds.right
data.each do |series|
right_margin = draw_label series, right_margin
right_margin = draw_square series, right_margin
right_margin -= label_padding
end
end
end
end

private

# Writes the name of the series, left-aligned, with a small font size.
# @param [Symbol, String] series The series to add to the legend
# @param [Integer] x The current right-margin of the legend
# @return [Integer] The updated right-margin (after adding the label)
def draw_label(series, x)
label = series.to_s.titleize
x -= width_of label, size: font_size
text_box label, at: [x, bounds.top], size: font_size, height: height, valign: :center
x
end

# Draws a square with the same color of the series (next to the label).
# @param [Symbol, String] series The series to add to the legend
# @param [Integer] x The current right-margin of the legend
# @return [Integer] The updated right-margin (after adding the label)
def draw_square(series, x)
x -= square_size + square_padding
fill_rectangle [x, bounds.height - square_size], square_size, square_size
x
end

# Restrict the legend to the right part of the graph
def width
bounds.width/2
end

# Restrict the legend to a specific vertical space
def height
15
end

# Ensure the label fits in the height of the legend
def font_size
height/2
end

# Ensure the square fits in the height of the legend
def square_size
height/3
end

# The horizontal distance left between the labels of two series
def label_padding
height
end

# The horizontal distance left between the squares of two series
def square_padding
height/5
end
end
end
4 changes: 2 additions & 2 deletions lib/squid/interface.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

module Squid
module Interface
def chart(settings = {})
Graph.new(self, settings).draw
def chart(data = {}, settings = {})
Graph.new(self, data, settings).draw
end
end
end
Expand Down
8 changes: 5 additions & 3 deletions manual/squid/height.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
#
filename = File.basename(__FILE__).gsub('.rb', '.pdf')
Prawn::ManualBuilder::Example.generate(filename) do
data = {views: {2013 => 182, 2014 => 46, 2015 => 102}}

text 'Default height:'
chart
move_down 20
chart data
move_down 30

text 'Custom height:'
chart height: 100
chart data, height: 100
end
15 changes: 15 additions & 0 deletions manual/squid/legend.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# By default, <code>chart</code> adds a legend in the top-right corner of the chart,
# listing the labels of all the series in the graph.
#
filename = File.basename(__FILE__).gsub('.rb', '.pdf')
Prawn::ManualBuilder::Example.generate(filename) do
views = {2013 => 182, 2014 => 46, 2015 => 102}
uniques = {2013 => 110, 2014 => 30, 2015 => 88}

text 'One series:'
chart views: views
move_down 30

text 'Two series:'
chart views: views, uniques: uniques
end
1 change: 1 addition & 0 deletions manual/squid/squid.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

p.section 'Basics' do |s|
s.example 'basic'
s.example 'legend'
end

p.section 'Styling' do |s|
Expand Down
10 changes: 10 additions & 0 deletions spec/graph/baseline_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
require 'spec_helper'

describe 'Graph baseline', inspect: true do
let(:baseline) { inspected_line.points.map &:first }

it 'spans the whole width of the page' do
pdf.chart
expect(baseline.last - baseline.first).to eq pdf.bounds.width
end
end
29 changes: 14 additions & 15 deletions spec/graph/height_spec.rb
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
require 'spec_helper'

describe 'Graph height' do
let(:default_height) { Squid.configuration.height }
let(:options) { {} }
let(:height) {
pdf = Prawn::Document.new
describe 'Graph height', inspect: true do
before do
pdf.stroke_horizontal_rule
pdf.chart options
pdf.chart data, options
pdf.stroke_horizontal_rule
output = pdf.render
line = PDF::Inspector::Graphics::Line.analyze output
y = line.points.each_slice(2).map{|x| x.first.last}
height = y.first - y.last
}
end

let(:y) { inspected_line.points.each_slice(2).map{|x| x.first.last} }
let(:height) { y.first - y.last }

it 'uses the Squid.configuration value by default' do
expect(height).to eq default_height
context 'uses the Squid.configuration value by default' do
it { expect(height).to eq Squid.configuration.height }
end

context 'can be set calling chart with the :height option' do
Expand All @@ -24,8 +20,11 @@
end

context 'can be set with Squid.config' do
before { Squid.configure {|config| config.height = 400} }
after { Squid.configure {|config| config.height = default_height } }
before(:all) do
@original_height = Squid.configuration.height
Squid.configure {|config| config.height = 400}
end
after(:all) { Squid.configure {|config| config.height = @original_height} }
it { expect(height).to eq 400.0 }
end
end
36 changes: 36 additions & 0 deletions spec/graph/legend_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
require 'spec_helper'

describe 'Graph legend', inspect: true do
before { pdf.chart data, options }

context 'given no series, does not have any text' do
it { expect(inspected_text.strings).to be_empty }
end

context 'given one series' do
let(:data) { {views: views} }

it 'includes the titleized name of the series' do
expect(inspected_text.strings).to eq ['Views']
end

it 'draws a small square representing the series' do
square = inspected_rectangle.rectangles.first
expect(square[:width]).to be 5.0
expect(square[:height]).to be 5.0
end
end

context 'given two series' do
let(:data) { {views: views, uniques: uniques} }

it 'includes the titleized names of both series' do
expect(inspected_text.strings).to eq ['Views', 'Uniques']
end

it 'prints both names on the same text line' do
lines = inspected_text.positions.map(&:last).uniq
expect(lines).to be_one
end
end
end
15 changes: 0 additions & 15 deletions spec/graph/width_spec.rb

This file was deleted.

13 changes: 13 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,16 @@

require 'pdf/inspector'
require 'squid'

RSpec.shared_context 'PDF Inspector', inspect: true do
let(:pdf) { Prawn::Document.new }
let(:output) { pdf.render }
let(:inspected_line) { PDF::Inspector::Graphics::Line.analyze output }
let(:inspected_text) { PDF::Inspector::Text.analyze output }
let(:inspected_rectangle) { PDF::Inspector::Graphics::Rectangle.analyze output }
let(:data) { {} }
let(:options) { {} }

let(:views) { {2013 => 182, 2014 => 46, 2015 => 102} }
let(:uniques) { {2013 => 110, 2014 => 30, 2015 => 88} }
end
3 changes: 2 additions & 1 deletion squid.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ Gem::Specification.new do |spec|
spec.require_paths = ['lib']

spec.add_dependency 'prawn', '~> 2.0'
spec.add_dependency 'activesupport' # 3 or 4, see gemfiles/

spec.add_development_dependency 'pdf-inspector', '~> 1.2'
spec.add_development_dependency 'prawn-manual_builder', '~> 0.2.0'
spec.add_development_dependency 'bundler', '~> 1.9'
spec.add_development_dependency 'rake', '~> 10.0'
spec.add_development_dependency 'rspec', '~> 3.3'
spec.add_development_dependency 'coveralls', '~> 0.8.2'
Expand Down