xmlss 0.0.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.
Files changed (52) hide show
  1. data/.gitignore +6 -0
  2. data/Gemfile +4 -0
  3. data/Gemfile.lock +28 -0
  4. data/README.rdoc +154 -0
  5. data/Rakefile +7 -0
  6. data/examples/example_workbook.rb +19 -0
  7. data/examples/layout.rb +81 -0
  8. data/examples/simple.rb +30 -0
  9. data/examples/styles.rb +85 -0
  10. data/examples/text.rb +36 -0
  11. data/lib/xmlss/cell.rb +38 -0
  12. data/lib/xmlss/column.rb +28 -0
  13. data/lib/xmlss/data.rb +67 -0
  14. data/lib/xmlss/enum.rb +56 -0
  15. data/lib/xmlss/item_set.rb +17 -0
  16. data/lib/xmlss/row.rb +34 -0
  17. data/lib/xmlss/style/alignment.rb +47 -0
  18. data/lib/xmlss/style/base.rb +63 -0
  19. data/lib/xmlss/style/border.rb +43 -0
  20. data/lib/xmlss/style/font.rb +41 -0
  21. data/lib/xmlss/style/interior.rb +41 -0
  22. data/lib/xmlss/style/number_format.rb +20 -0
  23. data/lib/xmlss/style/protection.rb +18 -0
  24. data/lib/xmlss/table.rb +22 -0
  25. data/lib/xmlss/version.rb +3 -0
  26. data/lib/xmlss/workbook.rb +33 -0
  27. data/lib/xmlss/worksheet.rb +37 -0
  28. data/lib/xmlss/xml.rb +55 -0
  29. data/lib/xmlss.rb +31 -0
  30. data/test/cell_test.rb +86 -0
  31. data/test/column_test.rb +46 -0
  32. data/test/data_test.rb +75 -0
  33. data/test/enum_test.rb +63 -0
  34. data/test/env.rb +10 -0
  35. data/test/helper.rb +49 -0
  36. data/test/item_set_test.rb +26 -0
  37. data/test/row_test.rb +67 -0
  38. data/test/style/alignment_test.rb +107 -0
  39. data/test/style/base_test.rb +126 -0
  40. data/test/style/border_test.rb +114 -0
  41. data/test/style/font_test.rb +100 -0
  42. data/test/style/interior_test.rb +87 -0
  43. data/test/style/number_format_test.rb +50 -0
  44. data/test/style/protection_test.rb +45 -0
  45. data/test/table_test.rb +53 -0
  46. data/test/thing.rb +5 -0
  47. data/test/workbook_test.rb +59 -0
  48. data/test/worksheet_test.rb +62 -0
  49. data/test/xml_test.rb +61 -0
  50. data/test/xmlss_test.rb +29 -0
  51. data/xmlss.gemspec +23 -0
  52. metadata +184 -0
data/lib/xmlss/row.rb ADDED
@@ -0,0 +1,34 @@
1
+ require 'xmlss/item_set'
2
+ require 'xmlss/cell'
3
+
4
+ module Xmlss
5
+ class Row
6
+
7
+ include Xmlss::Xml
8
+ def xml
9
+ { :node => :row,
10
+ :attributes => [:style_i_d, :height, :auto_fit_height, :hidden],
11
+ :children => [:cells] }
12
+ end
13
+
14
+ attr_accessor :style_id, :height, :auto_fit_height, :hidden
15
+ attr_accessor :cells
16
+ alias_method :style_i_d, :style_id
17
+
18
+ def initialize(attrs={})
19
+ self.style_id = attrs[:style_id]
20
+ self.height = attrs[:height]
21
+ self.auto_fit_height = attrs[:auto_fit_height] || false
22
+ self.hidden = attrs[:hidden] || false
23
+ self.cells = Xmlss::ItemSet.new(nil, attrs[:cells] || [])
24
+ end
25
+
26
+ def height=(value)
27
+ if value && !value.kind_of?(::Numeric)
28
+ raise ArgumentError, "must specify height as a Numeric"
29
+ end
30
+ @height = value && value < 0 ? nil : value
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,47 @@
1
+ module Xmlss::Style
2
+ class Alignment
3
+ include Xmlss::Xml
4
+ def xml
5
+ { :node => :alignment,
6
+ :attributes => [:horizontal, :vertical, :wrap_text, :rotate] }
7
+ end
8
+
9
+ include Xmlss::Enum
10
+ enum :horizontal, {
11
+ :automatic => "Automatic",
12
+ :left => "Left",
13
+ :center => "Center",
14
+ :right => "Right"
15
+ }
16
+ enum :vertical, {
17
+ :automatic => "Automatic",
18
+ :top => "Top",
19
+ :center => "Center",
20
+ :bottom => "Bottom"
21
+ }
22
+
23
+ attr_accessor :wrap_text, :rotate
24
+
25
+ def initialize(attrs={})
26
+ self.wrap_text = attrs[:wrap_text] || false
27
+ self.rotate = attrs[:rotate]
28
+ self.horizontal = attrs[:horizontal]
29
+ self.vertical = attrs[:vertical]
30
+ end
31
+
32
+ def wrap_text?; !!self.wrap_text; end
33
+
34
+ def rotate=(value)
35
+ @rotate = if value.kind_of?(::Numeric)
36
+ if value <= 90 && value >= -90
37
+ value.round
38
+ else
39
+ nil
40
+ end
41
+ else
42
+ nil
43
+ end
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,63 @@
1
+ require 'xmlss/item_set'
2
+ require 'xmlss/style/alignment'
3
+ require 'xmlss/style/border'
4
+ require 'xmlss/style/font'
5
+ require 'xmlss/style/interior'
6
+ require 'xmlss/style/number_format'
7
+ require 'xmlss/style/protection'
8
+
9
+ module Xmlss::Style
10
+ class Base
11
+ include Xmlss::Xml
12
+ def xml
13
+ { :node => :style,
14
+ :attributes => [:i_d],
15
+ :children => [
16
+ :alignment, :borders, :font, :interior, :number_format, :protection
17
+ ]}
18
+ end
19
+
20
+ attr_reader :id, :borders
21
+ alias_method :i_d, :id
22
+ attr_writer :alignment, :font, :interior, :number_format, :protection
23
+
24
+ def initialize(id, &block)
25
+ raise ArgumentError, "please choose an id for the style" if id.nil?
26
+ @id = id.to_s
27
+ self.borders = Xmlss::ItemSet.new(:borders)
28
+ instance_eval(&block) if block
29
+ end
30
+
31
+ def borders=(value)
32
+ if !value.kind_of? Xmlss::ItemSet
33
+ raise ArgumentError, "must set borders to an Xmlss::ItemSet value"
34
+ end
35
+ @borders = value
36
+ end
37
+
38
+ def border(opts = nil)
39
+ valid_attrs?(opts) do |attrs|
40
+ @borders << Border.new(attrs)
41
+ end
42
+ end
43
+
44
+ [:alignment, :font, :interior, :number_format, :protection].each do |meth|
45
+ define_method(meth) do |*args|
46
+ valid_attrs?(args.first) do |attrs|
47
+ instance_variable_set("@#{meth}", klass(meth).new(attrs))
48
+ end
49
+ instance_variable_get("@#{meth}")
50
+ end
51
+ end
52
+
53
+ protected
54
+
55
+ def valid_attrs?(attrs)
56
+ yield attrs if block_given? && attrs && attrs.kind_of?(::Hash)
57
+ end
58
+
59
+ def klass(method_name)
60
+ Xmlss::Style.const_get(Xmlss.classify(method_name))
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,43 @@
1
+ module Xmlss::Style
2
+ class Border
3
+ include Xmlss::Xml
4
+ def xml
5
+ { :node => :border,
6
+ :attributes => [:color, :position, :weight, :line_style] }
7
+ end
8
+
9
+ include Xmlss::Enum
10
+ enum :position, {
11
+ :left => "Left",
12
+ :top => "Top",
13
+ :right => "Right",
14
+ :bottom => "Bottom",
15
+ :diagonal_left => "DiagonalLeft",
16
+ :diagonal_right => "DiagonalRight"
17
+ }
18
+ enum :weight, {
19
+ :hairline => 0,
20
+ :thin => 1,
21
+ :medium => 2,
22
+ :thick => 3
23
+ }
24
+ enum :line_style, {
25
+ :none => "None",
26
+ :continuous => "Continuous",
27
+ :dash => "Dash",
28
+ :dot => "Dot",
29
+ :dash_dot => "DashDot",
30
+ :dash_dot_dot => "DashDotDot"
31
+ }
32
+
33
+ attr_accessor :color
34
+
35
+ def initialize(attrs={})
36
+ self.color = attrs[:color]
37
+ self.position = attrs[:position]
38
+ self.weight = attrs[:weight] || :thin
39
+ self.line_style = attrs[:line_style] || :continuous
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,41 @@
1
+ module Xmlss::Style
2
+ class Font
3
+ include Xmlss::Xml
4
+ def xml
5
+ { :node => :font,
6
+ :attributes => [
7
+ :bold, :color, :italic, :size,
8
+ :strike_through, :underline, :alignment
9
+ ] }
10
+ end
11
+
12
+ include Xmlss::Enum
13
+ enum :underline, {
14
+ :none => 0,
15
+ :single => 1,
16
+ :double => 2
17
+ }
18
+ enum :alignment, {
19
+ :none => 0,
20
+ :subscript => 1,
21
+ :superscript => 2
22
+ }
23
+
24
+ attr_accessor :bold, :color, :italic, :size, :strike_through
25
+
26
+ def initialize(attrs={})
27
+ self.bold = attrs[:bold] || false
28
+ self.color = attrs[:color]
29
+ self.italic = attrs[:italic] || false
30
+ self.size = attrs[:size]
31
+ self.strike_through = attrs[:strike_through] || false
32
+ self.underline = attrs[:underline]
33
+ self.alignment = attrs[:alignment]
34
+ end
35
+
36
+ def bold?; !!self.bold; end
37
+ def italic?; !!self.italic; end
38
+ def strike_through?; !!self.strike_through; end
39
+
40
+ end
41
+ end
@@ -0,0 +1,41 @@
1
+ module Xmlss::Style
2
+ class Interior
3
+ include Xmlss::Xml
4
+ def xml
5
+ { :node => :interior,
6
+ :attributes => [:color, :pattern, :pattern_color] }
7
+ end
8
+
9
+ include Xmlss::Enum
10
+ enum :pattern, {
11
+ :none => "None",
12
+ :solid => "Solid",
13
+ :gray75 => "Gray75",
14
+ :gray50 => "Gray50",
15
+ :gray25 => "Gray25",
16
+ :gray125 => "Gray125",
17
+ :gray0625 => "Gray0625",
18
+ :horz_stripe => "HorzStripe",
19
+ :vert_stripe => "VertStripe",
20
+ :reverse_diag_stripe => "ReverseDiagStripe",
21
+ :diag_stripe => "DiagStripe",
22
+ :diag_cross => "DiagCross",
23
+ :thick_diag_cross => "ThickDiagCross",
24
+ :thin_horz_stripe => "ThinHorzStripe",
25
+ :thin_vert_stripe => "ThinVertStripe",
26
+ :thin_reverse_diag_stripe => "ThinReverseDiagStripe",
27
+ :thin_diag_stripe => "ThineDiagStripe",
28
+ :thin_horz_cross => "ThinHorzCross",
29
+ :thin_diag_cross => "ThinDiagCross"
30
+ }
31
+
32
+ attr_accessor :color, :pattern_color
33
+
34
+ def initialize(attrs={})
35
+ self.color = attrs[:color]
36
+ self.pattern = attrs[:pattern]
37
+ self.pattern_color = attrs[:pattern_color]
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,20 @@
1
+ module Xmlss::Style
2
+ class NumberFormat
3
+ include Xmlss::Xml
4
+ def xml
5
+ { :node => :number_format,
6
+ :attributes => [:format] }
7
+ end
8
+
9
+ attr_accessor :format
10
+
11
+ def initialize(attrs={})
12
+ self.format = attrs[:format]
13
+ end
14
+
15
+ def format=(value)
16
+ @format = (value && value.respond_to?(:to_s) ? value.to_s : nil)
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ module Xmlss::Style
2
+ class Protection
3
+ include Xmlss::Xml
4
+ def xml
5
+ { :node => :protection,
6
+ :attributes => [:protect] }
7
+ end
8
+
9
+ attr_accessor :protect
10
+
11
+ def initialize(attrs={})
12
+ self.protect = attrs[:protect] || false
13
+ end
14
+
15
+ def protected?; !!self.protect; end
16
+
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ require 'xmlss/item_set'
2
+ require 'xmlss/column'
3
+ require 'xmlss/row'
4
+
5
+ module Xmlss
6
+ class Table
7
+
8
+ include Xmlss::Xml
9
+ def xml
10
+ { :node => :table,
11
+ :children => [:columns, :rows] }
12
+ end
13
+
14
+ attr_accessor :columns, :rows
15
+
16
+ def initialize(attrs={})
17
+ self.columns = Xmlss::ItemSet.new(nil, attrs[:columns] || [])
18
+ self.rows = Xmlss::ItemSet.new(nil, attrs[:rows] || [])
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module Xmlss
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,33 @@
1
+ require 'xmlss/item_set'
2
+ require 'xmlss/xml'
3
+
4
+ require 'xmlss/style/base'
5
+ require 'xmlss/worksheet'
6
+
7
+ module Xmlss
8
+ class Workbook
9
+
10
+ include Xmlss::Xml
11
+ def xml
12
+ { :node => :workbook,
13
+ :children => [:styles, :worksheets] }
14
+ end
15
+
16
+ attr_accessor :styles, :worksheets
17
+
18
+ def initialize(attrs={})
19
+ self.styles = Xmlss::ItemSet.new(:styles, attrs[:styles] || [])
20
+ self.worksheets = Xmlss::ItemSet.new(nil, attrs[:worksheets] || [])
21
+ end
22
+
23
+ def to_xml
24
+ xml_builder do |builder|
25
+ build_node(builder, {
26
+ Xmlss::XML_NS => Xmlss::NS_URI,
27
+ "#{Xmlss::XML_NS}:#{Xmlss::SHEET_NS}" => Xmlss::NS_URI
28
+ })
29
+ end.to_xml.gsub(/#{Xmlss::Data::LB.gsub(/&/, "&amp;")}/, Xmlss::Data::LB)
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,37 @@
1
+ require 'xmlss/table'
2
+
3
+ module Xmlss
4
+ class Worksheet
5
+
6
+ include Xmlss::Xml
7
+ def xml
8
+ { :node => :worksheet,
9
+ :attributes => [:name],
10
+ :children => [:table] }
11
+ end
12
+
13
+ attr_accessor :name, :table
14
+
15
+ def initialize(name, attrs={})
16
+ if name.nil? || name.empty?
17
+ raise ArgumentError, "'#{name.inspect}' is not a good name for a worksheet"
18
+ end
19
+ self.name = sanitized_name(name)
20
+ self.table = attrs[:table] || Table.new
21
+ end
22
+
23
+ def table=(value)
24
+ if value.nil? || !value.kind_of?(Table)
25
+ raise ArgumentError, "you must set table to an actual Table object"
26
+ end
27
+ @table = value
28
+ end
29
+
30
+ private
31
+
32
+ def sanitized_name(value)
33
+ # worksheet name cannot contain: /, \, ?, *, [, ]
34
+ value.to_s.gsub(/[\/|\\|\?|\*|\[|\]]/, '')
35
+ end
36
+ end
37
+ end
data/lib/xmlss/xml.rb ADDED
@@ -0,0 +1,55 @@
1
+ require 'nokogiri'
2
+
3
+ # common methods used for generating XML
4
+ module Xmlss
5
+ XML_NS = "xmlns"
6
+ SHEET_NS = "ss"
7
+ NS_URI = "urn:schemas-microsoft-com:office:spreadsheet"
8
+
9
+ module Xml
10
+
11
+ def xml_builder
12
+ ::Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |builder|
13
+ yield(builder) if block_given?
14
+ end
15
+ end
16
+
17
+ def build_node(builder, attrs={})
18
+ unless xml && xml.kind_of?(::Hash)
19
+ raise ArgumentError, "no xml config provided"
20
+ end
21
+ if xml[:node] && !xml[:node].to_s.empty?
22
+ if xml[:value] && (v = self.send(xml[:value]).to_s)
23
+ builder.send(Xmlss.classify(xml[:node]), v, build_attributes(attrs))
24
+ else
25
+ builder.send(Xmlss.classify(xml[:node]), build_attributes(attrs)) do
26
+ build_children(builder)
27
+ end
28
+ end
29
+ else
30
+ build_children(builder)
31
+ end
32
+ end
33
+
34
+ def build_attributes(attrs={})
35
+ (xml[:attributes] || []).inject({}) do |xattrs, a|
36
+ xattrs.merge(if !(xv = Xmlss.xmlify(self.send(a))).nil?
37
+ {"#{Xmlss::SHEET_NS}:#{Xmlss.classify(a)}" => xv}
38
+ else
39
+ {}
40
+ end)
41
+ end.merge(attrs)
42
+ end
43
+ private :build_attributes
44
+
45
+ def build_children(builder)
46
+ (xml[:children] || []).each do |c|
47
+ if (child = c.kind_of?(::Symbol) ? self.send(c) : c)
48
+ child.build_node(builder) if child.respond_to?(:build_node)
49
+ end
50
+ end
51
+ end
52
+ private :build_children
53
+
54
+ end
55
+ end
data/lib/xmlss.rb ADDED
@@ -0,0 +1,31 @@
1
+ module Xmlss
2
+ # some helpers
3
+ class << self
4
+
5
+ def classify(underscored_string)
6
+ underscored_string.
7
+ to_s.downcase.
8
+ split("_").
9
+ collect{|part| part.capitalize}.
10
+ join('')
11
+ end
12
+
13
+ def xmlify(value)
14
+ if value == true
15
+ 1
16
+ elsif ["",false].include?(value)
17
+ # don't include false or empty string values
18
+ nil
19
+ else
20
+ value
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+
27
+ require 'xmlss/xml'
28
+ require 'xmlss/enum'
29
+ require 'xmlss/style/base'
30
+ require 'xmlss/workbook'
31
+
data/test/cell_test.rb ADDED
@@ -0,0 +1,86 @@
1
+ require "test/helper"
2
+ require 'xmlss/cell'
3
+
4
+ module Xmlss
5
+ class CellTest < Test::Unit::TestCase
6
+
7
+ context "Xmlss::Cell" do
8
+ subject { Cell.new }
9
+
10
+ should_have_accessor :index, :data
11
+ should_have_accessor :formula, :href, :merge_across, :merge_down
12
+ should_have_reader :h_ref
13
+
14
+ should_have_style(Cell)
15
+
16
+ should "set it's defaults" do
17
+ [:data, :formula, :href].each do |a|
18
+ assert_equal nil, subject.send(a)
19
+ end
20
+ assert_equal nil, subject.merge_across
21
+ assert_equal nil, subject.merge_down
22
+ end
23
+
24
+ should "provide alias for :href" do
25
+ c = Cell.new({:href => "http://www.google.com"})
26
+ assert_equal "http://www.google.com", c.href
27
+ assert_equal "http://www.google.com", c.h_ref
28
+ end
29
+
30
+ should "bark when setting non Fixnum indices" do
31
+ assert_raises ArgumentError do
32
+ Cell.new({:index => "do it"})
33
+ end
34
+ assert_raises ArgumentError do
35
+ Cell.new({:index => 2.5})
36
+ end
37
+ assert_nothing_raised do
38
+ Cell.new({:index => 2})
39
+ end
40
+ end
41
+
42
+ should "bark when setting non Fixnum merge attrs" do
43
+ assert_raises ArgumentError do
44
+ Cell.new({:merge_across => "do it"})
45
+ end
46
+ assert_raises ArgumentError do
47
+ Cell.new({:merge_down => 2.5})
48
+ end
49
+ assert_nothing_raised do
50
+ Cell.new({:merge_across => 2})
51
+ end
52
+ assert_nothing_raised do
53
+ Cell.new({:merge_down => 3})
54
+ end
55
+ end
56
+
57
+ should "nil out merge/index values that are <= 0" do
58
+ [:index, :merge_across, :merge_down].each do |a|
59
+ assert_equal nil, Cell.new({a => -1}).send(a)
60
+ assert_equal nil, Cell.new({a => 0}).send(a)
61
+ assert_equal 1, Cell.new({a => 1}).send(a)
62
+ end
63
+ end
64
+
65
+ context "when using cell data" do
66
+ subject do
67
+ Cell.new({
68
+ :data => Data.new(12, {:type => :number})
69
+ })
70
+ end
71
+
72
+ should "should build a data object" do
73
+ assert_kind_of Data, subject.data
74
+ assert_equal 12, subject.data.value
75
+ end
76
+ end
77
+
78
+ context "for generating XML" do
79
+ should_have_reader :xml
80
+ should_build_node
81
+ end
82
+
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,46 @@
1
+ require "test/helper"
2
+ require 'xmlss/column'
3
+
4
+ module Xmlss
5
+ class ColumnTest < Test::Unit::TestCase
6
+
7
+ context "Xmlss::Column" do
8
+ subject { Column.new }
9
+
10
+ should_have_style(Column)
11
+ should_have_accessor :width, :auto_fit_width, :hidden
12
+
13
+ should "set it's defaults" do
14
+ assert_equal nil, subject.width
15
+ assert_equal false, subject.auto_fit_width
16
+ assert_equal false, subject.hidden
17
+ end
18
+
19
+ should "bark when setting non Numeric width" do
20
+ assert_raises ArgumentError do
21
+ Column.new({:width => "do it"})
22
+ end
23
+ assert_nothing_raised do
24
+ Column.new({:width => 2})
25
+ end
26
+ assert_nothing_raised do
27
+ Column.new({:width => 3.5})
28
+ end
29
+ end
30
+
31
+ should "nil out height values that are < 0" do
32
+ assert_equal nil, Column.new({:width => -1.2}).width
33
+ assert_equal nil, Column.new({:width => -1}).width
34
+ assert_equal 0, Column.new({:width => 0}).width
35
+ assert_equal 1.2, Column.new({:width => 1.2}).width
36
+ end
37
+
38
+ context "for generating XML" do
39
+ should_have_reader :xml
40
+ should_build_node
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+ end