osheet 0.1.0 → 0.2.0

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 (71) hide show
  1. data/.bundle/config +2 -0
  2. data/.gitignore +6 -0
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +33 -0
  5. data/README.rdoc +103 -61
  6. data/Rakefile +5 -56
  7. data/examples/basic.rb +73 -0
  8. data/examples/formats.rb +366 -0
  9. data/examples/trivial.rb +21 -0
  10. data/lib/osheet/associations.rb +62 -0
  11. data/lib/osheet/cell.rb +44 -10
  12. data/lib/osheet/column.rb +34 -7
  13. data/lib/osheet/format/accounting.rb +21 -0
  14. data/lib/osheet/format/currency.rb +23 -0
  15. data/lib/osheet/format/custom.rb +13 -0
  16. data/lib/osheet/format/datetime.rb +13 -0
  17. data/lib/osheet/format/fraction.rb +33 -0
  18. data/lib/osheet/format/general.rb +9 -0
  19. data/lib/osheet/format/number.rb +13 -0
  20. data/lib/osheet/format/numeric.rb +113 -0
  21. data/lib/osheet/format/percentage.rb +25 -0
  22. data/lib/osheet/format/scientific.rb +25 -0
  23. data/lib/osheet/format/special.rb +28 -0
  24. data/lib/osheet/format/text.rb +11 -0
  25. data/lib/osheet/format.rb +30 -0
  26. data/lib/osheet/row.rb +32 -7
  27. data/lib/osheet/style.rb +65 -0
  28. data/lib/osheet/style_set.rb +39 -0
  29. data/lib/osheet/styled_element.rb +18 -0
  30. data/lib/osheet/template.rb +32 -0
  31. data/lib/osheet/template_set.rb +57 -0
  32. data/lib/osheet/version.rb +1 -11
  33. data/lib/osheet/workbook.rb +32 -16
  34. data/lib/osheet/workbook_element.rb +21 -0
  35. data/lib/osheet/worksheet.rb +18 -20
  36. data/lib/osheet/worksheet_element.rb +15 -0
  37. data/lib/osheet/xmlss_writer/base.rb +42 -0
  38. data/lib/osheet/xmlss_writer/elements.rb +68 -0
  39. data/lib/osheet/xmlss_writer/styles.rb +176 -0
  40. data/lib/osheet/xmlss_writer.rb +1 -0
  41. data/lib/osheet.rb +7 -27
  42. data/osheet.gemspec +26 -0
  43. data/test/cell_test.rb +83 -0
  44. data/test/column_test.rb +81 -0
  45. data/test/env.rb +9 -0
  46. data/test/format/accounting_test.rb +158 -0
  47. data/test/format/currency_test.rb +158 -0
  48. data/test/format/custom_test.rb +22 -0
  49. data/test/format/datetime_test.rb +22 -0
  50. data/test/format/fraction_test.rb +84 -0
  51. data/test/format/general_test.rb +21 -0
  52. data/test/format/number_test.rb +93 -0
  53. data/test/format/percentage_test.rb +116 -0
  54. data/test/format/scientific_test.rb +116 -0
  55. data/test/format/special_test.rb +51 -0
  56. data/test/format/text_test.rb +18 -0
  57. data/test/format_test.rb +35 -0
  58. data/test/helper.rb +101 -0
  59. data/test/osheet_test.rb +14 -0
  60. data/test/row_test.rb +78 -0
  61. data/test/style_set_test.rb +50 -0
  62. data/test/style_test.rb +92 -0
  63. data/test/template_set_test.rb +76 -0
  64. data/test/template_test.rb +63 -0
  65. data/test/workbook_test.rb +142 -0
  66. data/test/worksheet_test.rb +77 -0
  67. data/test/xmlss_writer/base_test.rb +92 -0
  68. data/test/xmlss_writer/elements_test.rb +168 -0
  69. data/test/xmlss_writer/styles_test.rb +253 -0
  70. metadata +141 -30
  71. data/lib/osheet/base.rb +0 -95
@@ -0,0 +1,113 @@
1
+ require 'enumeration'
2
+
3
+ module Osheet::Format
4
+
5
+ class Numeric
6
+ include Enumeration
7
+
8
+ attr_accessor :decimal_places, :comma_separator
9
+ enum :symbol, [:none, :dollar, :euro]
10
+ enum :negative_numbers, [:black, :black_parenth, :red, :red_parenth]
11
+
12
+ def initialize(opts={})
13
+ self.symbol = opts[:symbol] || :none
14
+ self.decimal_places = opts[:decimal_places] || 0
15
+ self.comma_separator = opts.has_key?(:comma_separator) ? opts[:comma_separator] : false
16
+ self.negative_numbers = opts[:negative_numbers] || :black
17
+ end
18
+
19
+ def decimal_places=(value)
20
+ if !value.kind_of?(::Fixnum) || value < 0 || value > 30
21
+ raise ArgumentError, ":decimal_places must be a Fixnum between 0 and 30."
22
+ end
23
+ @decimal_places = value
24
+ end
25
+
26
+ def comma_separator=(value)
27
+ @comma_separator = !!value
28
+ end
29
+
30
+ def style
31
+ negative_numbers_style
32
+ end
33
+
34
+ def key
35
+ "#{key_prefix}_#{symbol_key}_#{decimal_places_key}_#{comma_separator_key}_#{negative_numbers_key}"
36
+ end
37
+
38
+ protected
39
+
40
+ # used by 'key' in Numeric base class, override as necessary
41
+ def key_prefix
42
+ "number"
43
+ end
44
+
45
+ # used by 'symbol_style' in Numeric base class, override as necessary
46
+ def symbol_suffix
47
+ ""
48
+ end
49
+
50
+ # used by 'decimal_places_style' in Numeric base class, override as necessary
51
+ def decimal_places_suffix
52
+ ""
53
+ end
54
+
55
+ private
56
+
57
+
58
+ def numeric_style
59
+ symbol_style +
60
+ comma_separator_style +
61
+ decimal_places_style
62
+ end
63
+
64
+
65
+ def negative_numbers_style
66
+ case @negative_numbers
67
+ when :black
68
+ numeric_style
69
+ when :red
70
+ "#{numeric_style};[Red]#{numeric_style}"
71
+ when :black_parenth
72
+ "#{numeric_style}_);\(#{numeric_style}\)"
73
+ when :red_parenth
74
+ "#{numeric_style}_);[Red]\(#{numeric_style}\)"
75
+ end
76
+ end
77
+ def negative_numbers_key
78
+ @negative_numbers.to_s.gsub('_', '')
79
+ end
80
+
81
+
82
+ def decimal_places_style
83
+ "0#{'.'+'0'*@decimal_places if @decimal_places > 0}#{decimal_places_suffix}"
84
+ end
85
+ def decimal_places_key
86
+ @decimal_places.to_s
87
+ end
88
+
89
+
90
+ def comma_separator_style
91
+ @comma_separator == true ? '#,##' : ''
92
+ end
93
+ def comma_separator_key
94
+ @comma_separator == true ? 'comma' : 'nocomma'
95
+ end
96
+
97
+
98
+ def symbol_style
99
+ case @symbol
100
+ when :dollar
101
+ "\"$\"#{symbol_suffix}"
102
+ when :euro
103
+ "[$€-2]\ #{symbol_suffix}"
104
+ when :none
105
+ ''
106
+ end
107
+ end
108
+ def symbol_key
109
+ @symbol.to_s.gsub('_', '')
110
+ end
111
+
112
+ end
113
+ end
@@ -0,0 +1,25 @@
1
+ require 'enumeration'
2
+ require 'osheet/format/numeric'
3
+
4
+ module Osheet::Format
5
+
6
+ class Percentage < Osheet::Format::Numeric
7
+ def initialize(opts={})
8
+ super({
9
+ :decimal_places => 2
10
+ }.merge(opts))
11
+ end
12
+
13
+ protected
14
+
15
+ # used by 'key' in Numeric base class
16
+ def key_prefix
17
+ "percentage"
18
+ end
19
+
20
+ # used by 'decimal_places_style' in Numeric base class
21
+ def decimal_places_suffix
22
+ "%"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ require 'enumeration'
2
+ require 'osheet/format/numeric'
3
+
4
+ module Osheet::Format
5
+
6
+ class Scientific < Osheet::Format::Numeric
7
+ def initialize(opts={})
8
+ super({
9
+ :decimal_places => 2
10
+ }.merge(opts))
11
+ end
12
+
13
+ protected
14
+
15
+ # used by 'key' in Numeric base class
16
+ def key_prefix
17
+ "scientific"
18
+ end
19
+
20
+ # used by 'decimal_places_style' in Numeric base class
21
+ def decimal_places_suffix
22
+ "E+00"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ require 'enumeration'
2
+
3
+ module Osheet::Format
4
+
5
+ class Special
6
+ include Enumeration
7
+
8
+ enum(:type, {
9
+ :zip_code => '00000',
10
+ :zip_code_plus_4 => '00000-0000',
11
+ :phone_number => '[<=9999999]###-####;(###) ###-####',
12
+ :social_security_number => '000-00-0000'
13
+ })
14
+
15
+ def initialize(opts={})
16
+ self.type = opts[:type]
17
+ end
18
+
19
+ def style
20
+ type
21
+ end
22
+
23
+ def key
24
+ "special_#{type_key.to_s.gsub('_', '')}"
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,11 @@
1
+ module Osheet::Format
2
+
3
+ class Text
4
+
5
+ def initialize(opts={}); end
6
+
7
+ def style; '@'; end
8
+ def key; 'text'; end
9
+
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ require 'osheet/format/general'
2
+ require 'osheet/format/number'
3
+ require 'osheet/format/currency'
4
+ require 'osheet/format/accounting'
5
+ require 'osheet/format/datetime'
6
+ require 'osheet/format/percentage'
7
+ require 'osheet/format/fraction'
8
+ require 'osheet/format/scientific'
9
+ require 'osheet/format/text'
10
+ require 'osheet/format/special'
11
+ require 'osheet/format/custom'
12
+
13
+ module Osheet::Format
14
+
15
+ VALUES = [
16
+ :general,:number, :currency, :accounting,
17
+ :datetime, :percentage, :fraction, :scientific,
18
+ :text, :special, :custom
19
+ ]
20
+
21
+ class << self
22
+ def new(type, opts={})
23
+ unless VALUES.include?(type)
24
+ raise ArgumentError, "'#{type.inspect}' is not a valid format type"
25
+ end
26
+ self.const_get(type.to_s.capitalize).new(opts)
27
+ end
28
+ end
29
+
30
+ end
data/lib/osheet/row.rb CHANGED
@@ -1,14 +1,39 @@
1
- require 'osheet/base'
2
1
  require 'osheet/cell'
3
2
 
4
3
  module Osheet
5
- class Row < Osheet::Base
6
-
7
- has :cells => "Cell"
4
+ class Row
5
+ include Associations
6
+ include WorkbookElement
7
+ include WorksheetElement
8
+ include StyledElement
8
9
 
9
- def initialize(args={})
10
- super(args)
10
+ hm :cells
11
+
12
+ def initialize(workbook=nil, worksheet=nil, *args, &block)
13
+ @workbook = workbook
14
+ @worksheet = worksheet
15
+ @height = nil
16
+ @autofit = false
17
+ @hidden = false
18
+ instance_exec(*args, &block) if block_given?
19
+ end
20
+
21
+ def height(value=nil)
22
+ !value.nil? ? @height = value : @height
11
23
  end
12
-
24
+ def autofit(value); @autofit = !!value; end
25
+ def autofit?; @autofit; end
26
+ def hidden(value); @hidden = !!value; end
27
+ def hidden?; @hidden; end
28
+
29
+ def attributes
30
+ {
31
+ :style_class => @style_class,
32
+ :height => @height,
33
+ :autofit => @autofit,
34
+ :hidden => @hidden
35
+ }
36
+ end
37
+
13
38
  end
14
39
  end
@@ -0,0 +1,65 @@
1
+ module Osheet
2
+ class Style
3
+
4
+ # this class is essentially a set of collectors for style settings
5
+ # each setting collects any arguments passed to it and
6
+ # it is up to the drivers to determine how to use the settings
7
+
8
+ BORDER_POSITIONS = [:top, :right, :bottom, :left]
9
+ BORDERS = BORDER_POSITIONS.collect{|p| "border_#{p}".to_sym}
10
+ SETTINGS = [:align, :font, :bg] + BORDERS
11
+
12
+ attr_reader :selectors
13
+
14
+ def initialize(*selectors, &block)
15
+ @selectors = verify(selectors)
16
+ SETTINGS.each do |setting|
17
+ instance_variable_set("@#{setting}", [])
18
+ end
19
+ instance_eval(&block) if block_given?
20
+ end
21
+
22
+ SETTINGS.each do |setting|
23
+ define_method(setting) do |*args|
24
+ instance_variable_set("@#{setting}", instance_variable_get("@#{setting}") + args)
25
+ end
26
+ end
27
+
28
+ def border(*args)
29
+ BORDERS.each do |border|
30
+ send(border, *args)
31
+ end
32
+ end
33
+
34
+ def attributes
35
+ SETTINGS.inject({}) do |attrs, s|
36
+ attrs[s] = instance_variable_get("@#{s}")
37
+ attrs
38
+ end
39
+ end
40
+
41
+ def match?(style_class)
42
+ selectors.inject(false) do |match, s|
43
+ match ||= s.split('.').inject(true) do |result, part|
44
+ result && (part.empty? || style_class.include?(part))
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def verify(selectors)
52
+ selectors.each do |selector|
53
+ if !selector.kind_of?(::String) || invalid?(selector)
54
+ raise ArgumentError, "invalid selector: '#{selector}', selectors must be strings that begin with '.' and con't have spaces or '>'."
55
+ end
56
+ end
57
+ end
58
+
59
+ def invalid?(selector)
60
+ selector =~ /\s+/ ||
61
+ selector =~ /^[^.]/ ||
62
+ selector =~ />+/
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,39 @@
1
+ require 'osheet/style'
2
+
3
+ module Osheet
4
+ class StyleSet < ::Array
5
+
6
+ # this class is an Array has some helper methods. I want to
7
+ # push styles into the set using the '<<' operator, only allow
8
+ # Osheet::Style objs to be pushed, and then be able to reference
9
+ # a particular set of styles using a style class.
10
+
11
+ def initialize
12
+ super
13
+ end
14
+
15
+ def <<(value)
16
+ super if verify(value)
17
+ end
18
+
19
+ # return the style set for the style class
20
+ def for(style_class)
21
+ self.select{|s| s.match?(style_class)}
22
+ end
23
+
24
+ private
25
+
26
+ def styles(class_str)
27
+ end
28
+
29
+ # verify the style, otherwise ArgumentError it up
30
+ def verify(style)
31
+ if style.kind_of?(Style)
32
+ true
33
+ else
34
+ raise ArgumentError, 'you can only push Osheet::Style objs to the style set'
35
+ end
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,18 @@
1
+ module Osheet::StyledElement
2
+ def style_class(value); @style_class = verify_style_class(value); end
3
+
4
+ private
5
+
6
+ def verify_style_class(style_class)
7
+ if !style_class.kind_of?(::String) || invalid_style_class?(style_class)
8
+ raise ArgumentError, "invalid style_class: '#{style_class}', cannot contain '.' or '>'"
9
+ else
10
+ style_class
11
+ end
12
+ end
13
+
14
+ def invalid_style_class?(style_class)
15
+ style_class =~ /\.+/ ||
16
+ style_class =~ />+/
17
+ end
18
+ end
@@ -0,0 +1,32 @@
1
+ module Osheet
2
+ class Template < ::Proc
3
+
4
+ # this class is essentially a way to define a named initializer
5
+ # block and associate that with an osheet element
6
+ # if an element is initialized from a template, the template
7
+ # block will be instance_eval'd for the element being initialized
8
+
9
+ ELEMENTS = ['worksheet', 'column', 'row', 'cell']
10
+
11
+ attr_accessor :element, :name
12
+
13
+ def initialize(element, name)
14
+ verify(element, name)
15
+ @element = element.to_s
16
+ @name = name.to_s
17
+ super()
18
+ end
19
+
20
+ private
21
+
22
+ def verify(element, name)
23
+ unless element.respond_to?(:to_s) && ELEMENTS.include?(element.to_s)
24
+ raise ArgumentError, "you can only define a template for #{ELEMENTS.join(', ')} elements."
25
+ end
26
+ unless name.kind_of?(::String) || name.kind_of?(::Symbol)
27
+ raise ArgumentError, "please use a string or symbol for the template name."
28
+ end
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,57 @@
1
+ require 'osheet/template'
2
+
3
+ module Osheet
4
+ class TemplateSet < ::Hash
5
+
6
+ # this class is a Hash that behaves kinda like a set. I want to
7
+ # push templates into the set using the '<<' operator, only allow
8
+ # Osheet::Template objs to be pushed, and then be able to reference
9
+ # a particular set of templates using a key
10
+
11
+ def initialize
12
+ super
13
+ end
14
+
15
+ def <<(template)
16
+ if (key = verify(template))
17
+ push(key, template)
18
+ end
19
+ end
20
+
21
+ # return the template set for the named element
22
+ def get(element, name)
23
+ lookup(key(element.to_s, name.to_s))
24
+ end
25
+
26
+ private
27
+
28
+ def lookup(key)
29
+ self[key.first][key.last] if self[key.first]
30
+ end
31
+
32
+ # push the template onto the key
33
+ def push(key, template)
34
+ self[key.first][key.last] = template
35
+ end
36
+
37
+ # verify the template, init the key set, and return the key string
38
+ # otherwise ArgumentError it up
39
+ def verify(template)
40
+ unless template.kind_of?(Template)
41
+ raise ArgumentError, 'you can only push Osheet::Template objs to the template set'
42
+ end
43
+ key = template_key(template)
44
+ self[key.first] ||= {}
45
+ self[key.first][key.last] ||= nil
46
+ key
47
+ end
48
+
49
+ def template_key(template)
50
+ key(template.element, template.name)
51
+ end
52
+
53
+ def key(element, name)
54
+ [element, name]
55
+ end
56
+ end
57
+ end
@@ -1,13 +1,3 @@
1
1
  module Osheet
2
- module Version
3
-
4
- MAJOR = 0
5
- MINOR = 1
6
- TINY = 0
7
-
8
- def self.to_s # :nodoc:
9
- [MAJOR, MINOR, TINY].join('.')
10
- end
11
-
12
- end
2
+ VERSION = "0.2.0"
13
3
  end
@@ -1,23 +1,39 @@
1
- require 'osheet/base'
1
+ require 'osheet/style_set'
2
+ require 'osheet/template_set'
2
3
  require 'osheet/worksheet'
3
-
4
- # === Usage
5
- #
6
- # require 'osheet'
7
- #
8
- # Osheet::Workbook.configure do |book|
9
- # book.worksheet ...
10
- # book.worksheet ...
11
- # end
4
+ require 'osheet/xmlss_writer'
12
5
 
13
6
  module Osheet
14
- class Workbook < Osheet::Base
15
-
16
- has :worksheets => "Worksheet"
7
+ class Workbook
8
+ include Associations
9
+
10
+ hm :worksheets
11
+ attr_reader :styles, :templates
12
+
13
+ def initialize(&block)
14
+ @title = nil
15
+ @styles = StyleSet.new
16
+ @templates = TemplateSet.new
17
+ instance_eval(&block) if block_given?
18
+ end
19
+
20
+ def title(value=nil)
21
+ !value.nil? ? @title = value : @title
22
+ end
23
+ def style(*selectors, &block); @styles << Style.new(*selectors, &block); end
24
+ def template(element, name, &block); @templates << Template.new(element, name, &block); end
17
25
 
18
- def initialize(args={})
19
- super(args)
26
+ def attributes
27
+ { :title => @title }
20
28
  end
21
-
29
+
30
+ def writer
31
+ XmlssWriter::Base.new(:workbook => self)
32
+ end
33
+
34
+ [:to_data, :to_file].each do |meth|
35
+ define_method(meth) {|*args| writer.send(meth, *args) }
36
+ end
37
+
22
38
  end
23
39
  end
@@ -0,0 +1,21 @@
1
+ module Osheet::WorkbookElement
2
+ class << self
3
+
4
+ def included(receiver)
5
+
6
+ receiver.send(:attr_reader, :workbook)
7
+
8
+ [:styles, :templates].each do |thing|
9
+ receiver.send(:define_method, thing) do
10
+ if workbook && workbook.respond_to?(thing)
11
+ workbook.send(thing)
12
+ else
13
+ nil
14
+ end
15
+ end
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+ end
@@ -1,29 +1,27 @@
1
- require 'osheet/base'
2
1
  require 'osheet/column'
3
2
  require 'osheet/row'
4
3
 
5
- # === Usage
6
- #
7
- # require 'osheet'
8
- #
9
- # Osheet::Workbook.configure do |book|
10
- # book.worksheet ...
11
- # book.worksheet ...
12
- # end
13
-
14
4
  module Osheet
15
- class Worksheet < Osheet::Base
16
-
17
- attr_reader :name
5
+ class Worksheet
6
+ include Associations
7
+ include WorkbookElement
8
+
9
+ hm :columns
10
+ hm :rows
11
+
12
+ def initialize(workbook=nil, *args, &block)
13
+ @workbook = workbook
14
+ @name = nil
15
+ instance_exec(*args, &block) if block_given?
16
+ end
18
17
 
19
- needs :workbook
20
- has :columns => "Column"
21
- has :rows => "Row"
18
+ def name(value=nil)
19
+ !value.nil? ? @name = value : @name
20
+ end
22
21
 
23
- def initialize(name=nil, args={})
24
- @name = name
25
- super(args)
22
+ def attributes
23
+ { :name => @name }
26
24
  end
27
-
25
+
28
26
  end
29
27
  end
@@ -0,0 +1,15 @@
1
+ module Osheet::WorksheetElement
2
+ class << self
3
+ def included(receiver)
4
+ receiver.send(:attr_reader, :worksheet)
5
+ end
6
+ end
7
+
8
+ def columns
9
+ if worksheet && worksheet.respond_to?(:columns)
10
+ worksheet.columns
11
+ else
12
+ nil
13
+ end
14
+ end
15
+ end