scruffy 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGES CHANGED
@@ -1,5 +1,13 @@
1
1
  = Scruffy Changelog
2
2
 
3
+ == Version 0.2.1
4
+ (August 18th, 2006)
5
+
6
+ * Mostly documentation.
7
+ * Added Builder 2.0 dependency to gem spec.
8
+ * Removed minimum size hack in RMagickRasterizer, for now.
9
+
10
+
3
11
  == Version 0.2.0
4
12
  (August 14th, 2006)
5
13
 
data/Rakefile CHANGED
@@ -39,11 +39,14 @@ Rake::RDocTask.new { |rdoc|
39
39
  rdoc.title = "Scruffy - Graphing Library for Ruby"
40
40
  # rdoc.options << '--line-numbers --inline-source --main README --accessor adv_attr_accessor=M'
41
41
  # rdoc.template = "#{ENV['template']}.rb" if ENV['template']
42
- rdoc.rdoc_files.include('README', 'CHANGES')
42
+ rdoc.rdoc_files.include('README', 'CHANGES', 'MIT-LICENSE')
43
43
  rdoc.rdoc_files.include('lib/scruffy.rb')
44
44
  rdoc.rdoc_files.include('lib/scruffy/*.rb')
45
45
  rdoc.rdoc_files.include('lib/scruffy/layers/*.rb')
46
46
  rdoc.rdoc_files.include('lib/scruffy/renderers/*.rb')
47
+ rdoc.rdoc_files.include('lib/scruffy/components/*.rb')
48
+ rdoc.rdoc_files.include('lib/scruffy/helpers/*.rb')
49
+ rdoc.rdoc_files.include('lib/scruffy/rasterizers/*.rb')
47
50
  }
48
51
 
49
52
  spec = Gem::Specification.new do |s|
@@ -53,6 +56,7 @@ spec = Gem::Specification.new do |s|
53
56
  s.email = "brasten@nagilum.com"
54
57
  s.homepage = "http://scruffy.rubyforge.org"
55
58
  s.summary = "A powerful, clean graphing library for Ruby."
59
+ s.add_dependency('builder', '>= 2.0')
56
60
  s.description = <<-EOF
57
61
  Scruffy is a Ruby library for generating powerful graphs. It is based on
58
62
  SVG, allowing for powerful, clean code, as well as a good foundation for
@@ -84,7 +88,10 @@ task :publish_packages => [:verify_user, :verify_password, :package] do
84
88
  release_files = FileList[
85
89
  "pkg/#{PKG_FILE_NAME}.gem",
86
90
  "pkg/#{PKG_FILE_NAME}.tgz",
87
- "pkg/#{PKG_FILE_NAME}.zip"
91
+ "pkg/#{PKG_FILE_NAME}.zip",
92
+ "pkg/#{PKG_FILE_NAME}.gem.md5",
93
+ "pkg/#{PKG_FILE_NAME}.tgz.md5",
94
+ "pkg/#{PKG_FILE_NAME}.zip.md5"
88
95
  ]
89
96
 
90
97
  Rake::XForge::Release.new(MetaProject::Project::XForge::RubyForge.new(PKG_NAME)) do |xf|
data/lib/scruffy.rb CHANGED
@@ -1,3 +1,16 @@
1
+ # ===Scruffy Graphing Library for Ruby
2
+ #
3
+ # Author:: Brasten Sager
4
+ # Date:: August 5th, 2006
5
+ #
6
+ # For information on generating graphs using Scruffy, see the
7
+ # documentation in Scruffy::Graph.
8
+ #
9
+ # For information on creating your own graph types, see the
10
+ # documentation in Scruffy::Layers::Base.
11
+ module Scruffy; end
12
+
13
+
1
14
  $:.unshift(File.dirname(__FILE__)) unless
2
15
  $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
16
 
@@ -1,3 +1,13 @@
1
+ # ===Scruffy Components
2
+ #
3
+ # Author:: Brasten Sager
4
+ # Date:: August 16th, 2006
5
+ #
6
+ # Components make up the visual elements of a Scruffy graph.
7
+ #
8
+ # For examples, see Scruffy::Components::Base.
9
+ module Scruffy::Components; end
10
+
1
11
  require 'scruffy/components/base'
2
12
  require 'scruffy/components/title'
3
13
  require 'scruffy/components/background'
@@ -1,21 +1,21 @@
1
- module Scruffy
2
- module Components
3
- # Component used to limit other visual components to a certain area on the graph.
4
- class Viewport < Base
5
- include Scruffy::Helpers::Canvas
1
+ module Scruffy::Components
2
+ # Component used to limit other visual components to a certain area on the graph.
3
+ class Viewport < Base
4
+ include Scruffy::Helpers::Canvas
5
+
6
+ attr_accessor :components
7
+
8
+ def initialize(*args, &block)
9
+ super(*args)
6
10
 
7
- attr_accessor :components
8
-
9
- def initialize(*args, &block)
10
- super(*args)
11
-
12
- self.components = []
13
- if block
14
- block.call(self.components)
15
- end
11
+ self.components = []
12
+ if block
13
+ block.call(self.components)
16
14
  end
15
+ end
17
16
 
18
- def draw(svg, bounds, options={})
17
+ def draw(svg, bounds, options={})
18
+ svg.g(options_for) {
19
19
  self.components.each do |component|
20
20
  component.render(svg,
21
21
  bounds_for( [bounds[:width], bounds[:height]],
@@ -23,8 +23,20 @@ module Scruffy
23
23
  component.size ),
24
24
  options)
25
25
  end
26
-
27
- end
26
+ }
28
27
  end
28
+
29
+ private
30
+ def options_for
31
+ options = {}
32
+ %w(skewX skewY).each do |option|
33
+ if @options[option.to_sym]
34
+ options[:transform] ||= ''
35
+ options[:transform] = options[:transform] + "#{option.to_s}(#{@options[option.to_sym]})"
36
+ end
37
+ end
38
+
39
+ options
40
+ end
29
41
  end
30
42
  end
@@ -1,111 +1,176 @@
1
- module Scruffy
2
- module Formatters
3
- class Base
4
- def route_format(target, idx, options = {})
5
- args = [target, idx, options]
6
- if respond_to?(:format)
7
- send :format, *args[0...self.method(:format).arity]
8
- elsif respond_to?(:format!)
9
- send :format!, *args[0...self.method(:format!).arity]
10
- target
11
- else
12
- raise NameError, "Formatter subclass must container either a format() method or format!() method."
13
- end
1
+ # ===Scruffy Formatters
2
+ #
3
+ # Author:: Brasten Sager
4
+ # Date:: August 16th, 2006
5
+ #
6
+ # Formatters are used to format the values displayed on the y-axis by
7
+ # setting graph.value_formatter.
8
+ #
9
+ # Example:
10
+ #
11
+ # graph.value_formatter = Scruffy::Formatters::Currency.new(:precision => 0)
12
+ #
13
+ module Scruffy::Formatters
14
+
15
+ # == Scruffy::Formatters::Base
16
+ #
17
+ # Author:: Brasten Sager
18
+ # Date:: August 16th, 2006
19
+ #
20
+ # Formatters are used to format the values displayed on the y-axis by
21
+ # setting graph.value_formatter.
22
+ class Base
23
+
24
+ # Called by the value marker component. Routes the format call
25
+ # to one of a couple possible methods.
26
+ #
27
+ # If the formatter defines a #format method, the returned value is used
28
+ # as the value. If the formatter defines a #format! method, the value passed is
29
+ # expected to be modified, and is used as the value. (This may not actually work,
30
+ # in hindsight.)
31
+ def route_format(target, idx, options = {})
32
+ args = [target, idx, options]
33
+ if respond_to?(:format)
34
+ send :format, *args[0...self.method(:format).arity]
35
+ elsif respond_to?(:format!)
36
+ send :format!, *args[0...self.method(:format!).arity]
37
+ target
38
+ else
39
+ raise NameError, "Formatter subclass must container either a format() method or format!() method."
14
40
  end
41
+ end
15
42
 
16
- protected
17
- def number_with_precision(number, precision=3)
18
- sprintf("%01.#{precision}f", number)
19
- end
43
+ protected
44
+ def number_with_precision(number, precision=3) #:nodoc:
45
+ sprintf("%01.#{precision}f", number)
46
+ end
47
+ end
48
+
49
+ # Default number formatter.
50
+ # Limits precision, beautifies numbers.
51
+ class Number < Base
52
+ attr_accessor :precision, :separator, :delimiter, :precision_limit
53
+
54
+ # Returns a new Number formatter.
55
+ #
56
+ # Options:
57
+ # precision:: precision to use for value. Can be set to an integer, :none or :auto.
58
+ # :auto will use whatever precision is necessary to portray all the numerical
59
+ # information, up to :precision_limit.
60
+ #
61
+ # Example: [100.1, 100.44, 200.323] will result in [100.100, 100.440, 200.323]
62
+ #
63
+ # separator:: decimal separator. Defaults to '.'
64
+ # delimiter:: delimiter character. Defaults to ','
65
+ # precision_limit:: upper limit for auto precision.
66
+ def initialize(options = {})
67
+ @precision = options[:precision] || :none
68
+ @separator = options[:separator] || '.'
69
+ @delimiter = options[:delimiter] || ','
70
+ @precision_limit = options[:precision_limit] || 4
20
71
  end
21
72
 
22
- # Default number formatter. Limits precision, beautifies numbers.
23
- class Number < Base
24
- attr_accessor :precision, :separator, :delimiter, :precision_limit
73
+ # Formats the value.
74
+ def format(target, idx, options)
75
+ my_precision = @precision
25
76
 
26
- def initialize(options = {})
27
- @precision = options[:precision] || :none
28
- @separator = options[:separator] || '.'
29
- @delimiter = options[:delimiter] || ','
30
- @precision_limit = options[:precision_limit] || 4
77
+ if @precision == :auto
78
+ my_precision = options[:all_values].inject(0) do |highest, current|
79
+ cur = current.to_f.to_s.split(".").last.size
80
+ cur > highest ? cur : highest
81
+ end
82
+
83
+ my_precision = @precision_limit if my_precision > @precision_limit
84
+ elsif @precision == :none
85
+ my_precision = 0
31
86
  end
32
87
 
33
- def format(target, idx, options)
34
- my_precision = @precision
88
+ my_separator = @separator
89
+ my_separator = "" unless my_precision > 0
90
+ begin
91
+ parts = number_with_precision(target, my_precision).split('.')
35
92
 
36
- if @precision == :auto
37
- my_precision = options[:all_values].inject(0) do |highest, current|
38
- cur = current.to_f.to_s.split(".").last.size
39
- cur > highest ? cur : highest
40
- end
41
-
42
- my_precision = @precision_limit if my_precision > @precision_limit
43
- elsif @precision == :none
44
- my_precision = 0
45
- end
46
-
47
- my_separator = @separator
48
- my_separator = "" unless my_precision > 0
49
- begin
50
- parts = number_with_precision(target, my_precision).split('.')
51
-
52
- number = parts[0].to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{@delimiter}") + my_separator + parts[1].to_s
53
- number
54
- rescue StandardError => e
55
- target
56
- end
93
+ number = parts[0].to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{@delimiter}") + my_separator + parts[1].to_s
94
+ number
95
+ rescue StandardError => e
96
+ target
57
97
  end
58
98
  end
99
+ end
100
+
101
+ # Currency formatter.
102
+ #
103
+ # Provides formatting for currencies.
104
+ class Currency < Base
59
105
 
60
- class Currency < Base
61
- def initialize(options = {})
62
- @precision = options[:precision] || 2
63
- @unit = options[:unit] || '$'
64
- @separator = options[:separator] || '.'
65
- @delimiter = options[:delimiter] || ','
66
- @negative_color = options[:negative_color] || 'red'
67
- @special_negatives = options[:special_negatives] || false
68
- end
69
-
70
- def format(target, idx, options)
71
- @separator = "" unless @precision > 0
72
- begin
73
- parts = number_with_precision(target, @precision).split('.')
74
- if @special_negatives && (target.to_f < 0)
75
- number = "(" + @unit + parts[0].to_i.abs.to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{@delimiter}") + @separator + parts[1].to_s + ")"
76
- else
77
- number = @unit + parts[0].to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{@delimiter}") + @separator + parts[1].to_s
78
- end
79
- if (target.to_f < 0) && @negative_color
80
- options[:marker_color_override] = @negative_color
81
- end
82
- number
83
- rescue
84
- target
106
+ # Returns a new Currency class.
107
+ #
108
+ # Options:
109
+ # precision:: precision of value
110
+ # unit:: Defaults to '$'
111
+ # separator:: Defaults to '.'
112
+ # delimiter:: Defaults to ','
113
+ # negative_color:: Color of value marker for negative values. Defaults to 'red'
114
+ # special_negatives:: If set to true, parenthesizes negative numbers. ie: -$150.50 becomes ($150.50).
115
+ # Defaults to false.
116
+ def initialize(options = {})
117
+ @precision = options[:precision] || 2
118
+ @unit = options[:unit] || '$'
119
+ @separator = options[:separator] || '.'
120
+ @delimiter = options[:delimiter] || ','
121
+ @negative_color = options[:negative_color] || 'red'
122
+ @special_negatives = options[:special_negatives] || false
123
+ end
124
+
125
+ # Formats value marker.
126
+ def format(target, idx, options)
127
+ @separator = "" unless @precision > 0
128
+ begin
129
+ parts = number_with_precision(target, @precision).split('.')
130
+ if @special_negatives && (target.to_f < 0)
131
+ number = "(" + @unit + parts[0].to_i.abs.to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{@delimiter}") + @separator + parts[1].to_s + ")"
132
+ else
133
+ number = @unit + parts[0].to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{@delimiter}") + @separator + parts[1].to_s
134
+ end
135
+ if (target.to_f < 0) && @negative_color
136
+ options[:marker_color_override] = @negative_color
85
137
  end
138
+ number
139
+ rescue
140
+ target
86
141
  end
87
142
  end
143
+ end
144
+
145
+ # Percentage formatter.
146
+ #
147
+ # Provides formatting for percentages.
148
+ class Percentage < Base
149
+
150
+ # Returns new Percentage formatter.
151
+ #
152
+ # Options:
153
+ # precision:: Defaults to 3.
154
+ # separator:: Defaults to '.'
155
+ def initialize(options = {})
156
+ @precision = options[:precision] || 3
157
+ @separator = options[:separator] || '.'
158
+ end
88
159
 
89
- class Percentage < Base
90
- def initialize(options = {})
91
- @precision = options[:precision] || 3
92
- @separator = options[:separator] || '.'
93
- end
94
-
95
- def format(target)
96
- begin
97
- number = number_with_precision(target, @precision)
98
- parts = number.split('.')
99
- if parts.at(1).nil?
100
- parts[0] + "%"
101
- else
102
- parts[0] + @separator + parts[1].to_s + "%"
103
- end
104
- rescue
105
- target
160
+ # Formats percentages.
161
+ def format(target)
162
+ begin
163
+ number = number_with_precision(target, @precision)
164
+ parts = number.split('.')
165
+ if parts.at(1).nil?
166
+ parts[0] + "%"
167
+ else
168
+ parts[0] + @separator + parts[1].to_s + "%"
106
169
  end
170
+ rescue
171
+ target
107
172
  end
108
173
  end
109
-
110
174
  end
175
+
111
176
  end
data/lib/scruffy/graph.rb CHANGED
@@ -1,13 +1,3 @@
1
- # ===Scruffy Graphing Library for Ruby
2
- #
3
- # Author:: Brasten Sager
4
- # Date:: August 5th, 2006
5
- #
6
- # For information on generating graphs using Scruffy, see the
7
- # documentation in Scruffy::Graph.
8
- #
9
- # For information on creating your own graph types, see the
10
- # documentation in Scruffy::Layers::Base.
11
1
  module Scruffy
12
2
 
13
3
  # ==Scruffy Graphs
@@ -21,7 +11,7 @@ module Scruffy
21
11
  # Scruffy::Graph is the primary class you will use to generate your graphs. A Graph does not
22
12
  # define a graph type nor does it directly hold any data. Instead, a Graph object can be thought
23
13
  # of as a canvas on which other graphs are draw. (The actual graphs themselves are subclasses of Scruffy::Layers::Base)
24
- # Despite the technical distinction, we will refer to Scruffy::Graph objects as 'graphs' and Scruffy::GraphLayers as
14
+ # Despite the technical distinction, we will refer to Scruffy::Graph objects as 'graphs' and Scruffy::Layers as
25
15
  # 'layers' or 'graph types.'
26
16
  #
27
17
  #
@@ -34,7 +24,7 @@ module Scruffy
34
24
  #
35
25
  # OR
36
26
  #
37
- # graph = Scruffy::Graph.new(:title => "Monthly Profits", :theme => Scruffy::Themes::RUBY_BLOG)
27
+ # graph = Scruffy::Graph.new(:title => "Monthly Profits", :theme => Scruffy::Themes::RubyBlog.new)
38
28
  #
39
29
  # Once you have a Graph object, you can set any Graph-level properties (title, theme, etc), or begin adding
40
30
  # graph layers. You can add a graph layer to a graph by using the Graph#add or Graph#<< methods. The two
@@ -49,13 +39,21 @@ module Scruffy
49
39
  # graph << Scruffy::Layers::Line.new(:title => 'Sara', :points => [120, 50, -80, 20])
50
40
  #
51
41
  # Now that we've created our graph and added a layer to it, we're ready to render! You can render the graph
52
- # directly to SVG with the Graph#render method:
42
+ # directly to SVG or any other image format (supported by RMagick) with the Graph#render method:
53
43
  #
54
44
  # graph.render # Renders a 600x400 SVG graph
55
45
  #
56
46
  # OR
57
47
  #
58
- # graph.render(:size => [1500, 1000])
48
+ # graph.render(:width => 1200)
49
+ #
50
+ # # For image formats other than SVG:
51
+ # graph.render(:width => 1200, :as => 'PNG')
52
+ #
53
+ # # To render directly to a file:
54
+ # graph.render(:width => 5000, :to => '<filename>')
55
+ #
56
+ # graph.render(:width => 700, :as => 'PNG', :to => '<filename>')
59
57
  #
60
58
  # And that's your basic Scruffy graph! Please check the documentation for the various methods and
61
59
  # classes you'll be using, as there are a bunch of options not demonstrated here.
@@ -93,7 +91,7 @@ module Scruffy
93
91
  # Options:
94
92
  #
95
93
  # title:: Graph's title
96
- # theme:: A theme hash to use when rendering graph
94
+ # theme:: A theme object to use when rendering graph
97
95
  # layers:: An array of Layers for this graph to use
98
96
  # default_type:: A symbol indicating the default type of Layer for this graph
99
97
  # value_formatter:: Sets a formatter used to modify marker values prior to rendering
@@ -128,7 +126,7 @@ module Scruffy
128
126
  #
129
127
  # For other image formats:
130
128
  # as:: File format to render to ('PNG', 'JPG', etc)
131
- # to:: Name of file to save graph to, if desired. If not provided, image is returned as blob.
129
+ # to:: Name of file to save graph to, if desired. If not provided, image is returned as blob/string.
132
130
  def render(options = {})
133
131
  options[:theme] ||= theme
134
132
  options[:value_formatter] ||= value_formatter
@@ -140,16 +138,16 @@ module Scruffy
140
138
  options[:max_value] ||= top_value
141
139
  options[:graph] ||= self
142
140
 
143
-
144
- if options[:as] && (options[:size][0] <= 300 || options[:size][1] <= 200)
145
- options[:actual_size] = options[:size]
146
- options[:size] = [800, (800.to_f * (options[:actual_size][1].to_f / options[:actual_size][0].to_f))]
147
- puts options[:size].inspect
148
- end
141
+
142
+ # Removed for now.
143
+ # Added for making smaller fonts more legible, but may not be needed after all.
144
+ #
145
+ # if options[:as] && (options[:size][0] <= 300 || options[:size][1] <= 200)
146
+ # options[:actual_size] = options[:size]
147
+ # options[:size] = [800, (800.to_f * (options[:actual_size][1].to_f / options[:actual_size][0].to_f))]
148
+ # end
149
149
 
150
150
  svg = ( options[:renderer].nil? ? self.renderer.render( options ) : options[:renderer].render( options ) )
151
-
152
- options[:size] = options[:actual_size] if options[:actual_size]
153
151
 
154
152
  # SVG to file.
155
153
  if options[:to] && options[:as].nil?