diff --git a/Gemfile.lock b/Gemfile.lock index 8c49b8a..5ff7561 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,12 +3,12 @@ PATH specs: osheet (0.10.0) enumeration (~> 1.3) - xmlss (~> 0.4) + xmlss (~> 1.0.0.rc) GEM remote: http://rubygems.org/ specs: - ansi (1.4.1) + ansi (1.4.2) assert (0.7.3) assert-view (~> 0.5) assert-view (0.5.0) @@ -17,12 +17,12 @@ GEM enumeration (1.3.1) rake (0.9.2.2) ruby-prof (0.10.8) - undies (2.1.0) + undies (2.2.1) whysoslow (0.0.2) ansi (~> 1.4) - xmlss (0.4.0) + xmlss (1.0.0.rc.1) enumeration (~> 1.3) - undies (~> 2.1) + undies (~> 2.2.1) PLATFORMS ruby diff --git a/bench/bench_runner.rb b/bench/bench_runner.rb index 1d83059..0c66f06 100644 --- a/bench/bench_runner.rb +++ b/bench/bench_runner.rb @@ -25,7 +25,10 @@ def run } outstream = StringIO.new(out = "") - outstream << Osheet::Workbook.new { + outstream << Osheet::Workbook.new(Osheet::XmlssWriter.new(:pp => 2), { + :bench_row_count => @row_count, + :runner => @runner + }) { title "bench" template(:row, :header) { @@ -58,13 +61,13 @@ def run row :header 10.times do |i| - (@row_count / 10).times do + (bench_row_count / 10).times do row :data, data_values end - @runner.snapshot("#{((i+1)*10).to_s.rjust(3)}%") + runner.snapshot("#{((i+1)*10).to_s.rjust(3)}%") end } - }.to_data(:pp => 2) + }.to_data end end diff --git a/examples/basic.rb b/examples/basic.rb index 5585e4f..1db3906 100644 --- a/examples/basic.rb +++ b/examples/basic.rb @@ -17,7 +17,7 @@ puts "building examples/basic.rb ..." -Osheet::Workbook.new { +Osheet::Workbook.new(Osheet::XmlssWriter.new) { title "basic" worksheet { name "Stats: #{fields.join(', ')}" diff --git a/examples/basic.xls b/examples/basic.xls index 97e4bdd..5871623 100644 --- a/examples/basic.xls +++ b/examples/basic.xls @@ -1 +1,2 @@ -Stats: Sex, Age, Height, WeightNameSexAgeHeightWeightSallyF295'3"132 lbs.DickM336'5"243 lbs.TomM526'2"220 lbs.
\ No newline at end of file + +Stats: Sex, Age, Height, WeightNameSexAgeHeightWeightSallyF295'3"132 lbs.DickM336'5"243 lbs.TomM526'2"220 lbs.
\ No newline at end of file diff --git a/examples/basic_with_templates.rb b/examples/basic_with_templates.rb index 792e3d2..ac003f3 100644 --- a/examples/basic_with_templates.rb +++ b/examples/basic_with_templates.rb @@ -17,7 +17,7 @@ puts "building examples/basic_with_templats.rb ..." -Osheet::Workbook.new { +Osheet::Workbook.new(Osheet::XmlssWriter.new(:pp => 2)) { title "basic" template(:column, :data) { |field, index| @@ -82,6 +82,6 @@ row :data, name, stats end } -}.to_file('examples/basic_with_templates.xls', :pp => 2) +}.to_file('examples/basic_with_templates.xls') puts "open examples/basic_with_templates.xls" diff --git a/examples/basic_with_templates.xls b/examples/basic_with_templates.xls index 587dbad..e81668a 100644 --- a/examples/basic_with_templates.xls +++ b/examples/basic_with_templates.xls @@ -48,7 +48,7 @@ 29 - 5'3" + 5'3" 132 lbs. @@ -65,7 +65,7 @@ 33 - 6'5" + 6'5" 243 lbs. @@ -82,7 +82,7 @@ 52 - 6'2" + 6'2" 220 lbs. diff --git a/examples/formats.rb b/examples/formats.rb index a980f99..19cf471 100644 --- a/examples/formats.rb +++ b/examples/formats.rb @@ -8,7 +8,7 @@ puts "building examples/formats.rb ..." -Osheet::Workbook.new { +Osheet::Workbook.new(Osheet::XmlssWriter.new(:pp => 2)) { title "formats" @@ -365,6 +365,6 @@ -}.to_file('examples/formats.xls', :pp => 2) +}.to_file('examples/formats.xls') puts "open examples/formats.xls" diff --git a/examples/formats.xls b/examples/formats.xls index 49a21a5..ff416f3 100644 --- a/examples/formats.xls +++ b/examples/formats.xls @@ -61,6 +61,12 @@ + + @@ -82,12 +88,6 @@ - - @@ -447,6 +447,22 @@ Fraction Example + + + fraction_threedigits + + + 0.01 + + + + + fraction_hundredths + + + 0.01 + + fraction_halves @@ -503,22 +519,6 @@ 0.1 - - - fraction_threedigits - - - 0.01 - - - - - fraction_hundredths - - - 0.01 - - @@ -600,7 +600,7 @@ datetime_m - 2012-01-09T00:00:00 + 2012-03-14T00:00:00 @@ -608,7 +608,7 @@ datetime_d - 2012-01-09T00:00:00 + 2012-03-14T00:00:00 @@ -616,7 +616,7 @@ datetime_y - 2012-01-09T00:00:00 + 2012-03-14T00:00:00 @@ -624,7 +624,7 @@ datetime_mm - 2012-01-09T00:00:00 + 2012-03-14T00:00:00 @@ -632,7 +632,7 @@ datetime_dd - 2012-01-09T00:00:00 + 2012-03-14T00:00:00 @@ -640,7 +640,7 @@ datetime_yy - 2012-01-09T00:00:00 + 2012-03-14T00:00:00 @@ -648,23 +648,23 @@ datetime_yyyy - 2012-01-09T00:00:00 + 2012-03-14T00:00:00 - datetime_mm/dd/yy + datetime_mm/dd/yy - 2012-01-09T00:00:00 + 2012-03-14T00:00:00 - datetime_mm/dd/yyyy + datetime_mm/dd/yyyy - 2012-01-09T00:00:00 + 2012-03-14T00:00:00 @@ -672,7 +672,7 @@ datetime_mmmm - 2012-01-09T00:00:00 + 2012-03-14T00:00:00 @@ -680,7 +680,7 @@ datetime_mmmmm - 2012-01-09T00:00:00 + 2012-03-14T00:00:00 @@ -688,7 +688,7 @@ datetime_mmmmm - 2012-01-09T00:00:00 + 2012-03-14T00:00:00 @@ -696,7 +696,7 @@ datetime_h - 2012-01-09T00:00:00 + 2012-03-14T00:00:00 @@ -704,7 +704,7 @@ datetime_m - 2012-01-09T00:00:00 + 2012-03-14T00:00:00 @@ -712,7 +712,7 @@ datetime_s - 2012-01-09T00:00:00 + 2012-03-14T00:00:00 @@ -720,7 +720,7 @@ datetime_hh - 2012-01-09T00:00:00 + 2012-03-14T00:00:00 @@ -728,7 +728,7 @@ datetime_mm - 2012-01-09T00:00:00 + 2012-03-14T00:00:00 @@ -736,7 +736,7 @@ datetime_ss - 2012-01-09T00:00:00 + 2012-03-14T00:00:00 @@ -744,7 +744,7 @@ datetime_h:mm:ss - 2012-01-09T00:00:00 + 2012-03-14T00:00:00 @@ -752,15 +752,15 @@ datetime_h:mm:ss.0 - 2012-01-09T00:00:00 + 2012-03-14T00:00:00 - datetime_hh:mm:ss AM/PM + datetime_hh:mm:ss AM/PM - 2012-01-09T00:00:00 + 2012-03-14T00:00:00 diff --git a/examples/formula.rb b/examples/formula.rb index 8a2f3ef..d4a0866 100644 --- a/examples/formula.rb +++ b/examples/formula.rb @@ -11,7 +11,7 @@ puts "building examples/formula.rb ..." -Osheet::Workbook.new { +Osheet::Workbook.new(Osheet::XmlssWriter.new(:pp => 2)) { title "formula example" worksheet { name "Formula" @@ -38,6 +38,6 @@ } } } -}.to_file('examples/formula.xls', :pp => 2) +}.to_file('examples/formula.xls') puts "open examples/formula.xls" diff --git a/examples/styles.rb b/examples/styles.rb index d272c7b..c5de794 100644 --- a/examples/styles.rb +++ b/examples/styles.rb @@ -8,7 +8,7 @@ puts "building examples/styles.rb ..." -Osheet::Workbook.new { +Osheet::Workbook.new(Osheet::XmlssWriter.new(:pp => 2)) { title "styles" template(:cell, :styled) { |style, attribute| data attribute == :wrap ? (attribute.to_s+' ')*20 : attribute.to_s @@ -250,6 +250,6 @@ -}.to_file('examples/styles.xls', :pp => 2) +}.to_file('examples/styles.xls') puts "open examples/styles.xls" diff --git a/examples/styles.xls b/examples/styles.xls index c0c5d83..b9294f8 100644 --- a/examples/styles.xls +++ b/examples/styles.xls @@ -293,7 +293,7 @@ - + @@ -302,7 +302,7 @@ top red hairline continuous - + @@ -311,7 +311,7 @@ right green thin dash - + @@ -320,7 +320,7 @@ bottom blue medium dat - + @@ -329,7 +329,7 @@ left yellow thick dast_dot - + diff --git a/examples/trivial.rb b/examples/trivial.rb index bf02b9f..7f5e576 100644 --- a/examples/trivial.rb +++ b/examples/trivial.rb @@ -8,7 +8,7 @@ puts "building examples/trivial.rb ..." -Osheet::Workbook.new { +Osheet::Workbook.new(Osheet::XmlssWriter.new(:pp => 2)) { title "basic" worksheet { name "one dollar" @@ -20,6 +20,6 @@ } } } -}.to_file('examples/trivial.xls', :pp => 2) +}.to_file('examples/trivial.xls') puts "open examples/trivial.xls" diff --git a/lib/osheet.rb b/lib/osheet.rb index 46ee806..dedf940 100644 --- a/lib/osheet.rb +++ b/lib/osheet.rb @@ -1,31 +1,5 @@ -module Osheet - - MIME_TYPE = "application/vnd.ms-excel" - - class << self - # used to register an appropriate view handler - # and register a mime type if necessary - def register - ::Mime::Type.register MIME_TYPE, :xls if defined? ::Mime::Type - require 'osheet/view_handler' - end - end - -end - -require 'osheet/instance' -require 'osheet/partial' -require 'osheet/associations' -require 'osheet/mixin' - -require 'osheet/markup_element' -require 'osheet/styled_element' -require 'osheet/meta_element' -require 'osheet/workbook_element' -require 'osheet/worksheet_element' - -if defined? Rails::Railtie - require 'osheet/railtie' -end +module Osheet; end require 'osheet/workbook' +require 'osheet/xmlss_writer' + diff --git a/lib/osheet/associations.rb b/lib/osheet/associations.rb deleted file mode 100644 index 78478a8..0000000 --- a/lib/osheet/associations.rb +++ /dev/null @@ -1,58 +0,0 @@ -module Osheet::Associations - - def self.included(receiver) - receiver.send(:extend, ClassMethods) - end - - module ClassMethods - - # A has many esque association helper - # - will provide a collection reader - # - will define a 'singular' item method for adding to the collection - # - will support adding to the collection both by black and template - def hm(collection) - unless collection.to_s =~ /s$/ - raise ArgumentError, "association item names must end in 's'" - end - plural = collection.to_s - singular = plural.to_s.sub(/s$/, '') - klass = Osheet.const_get(singular.capitalize) - - # define collection reader - self.send(:define_method, plural, Proc.new do - set_ivar(plural, []) if get_ivar(plural).nil? - get_ivar(plural) - end) - - # define collection item writer - self.send(:define_method, singular) do |*args, &block| - set_ivar(plural, []) if get_ivar(plural).nil? - push_ivar(plural, if self.respond_to?(:workbook) - # on: worksheet, column, row - # creating: column, row, cell - worksheet = self.respond_to?(:worksheet) ? self.worksheet : self - if self.workbook && (template = self.workbook.templates.get(singular, args.first)) - # add by template - klass.new(self.workbook, worksheet, *args[1..-1], &template) - else - # add by block - klass.new(self.workbook, worksheet, &block) - end - else - # on: workbook - # creating: worksheet - if (template = self.templates.get(singular, args.first)) - # add by template - klass.new(self, *args[1..-1], &template) - else - # add by block - klass.new(self, &block) - end - end) - end - - end - - end - -end diff --git a/lib/osheet/cell.rb b/lib/osheet/cell.rb index a9305de..69687b9 100644 --- a/lib/osheet/cell.rb +++ b/lib/osheet/cell.rb @@ -1,62 +1,64 @@ require 'date' + require 'osheet/format' +require 'osheet/meta_element' +require 'osheet/styled_element' module Osheet class Cell - include Instance - include WorkbookElement - include WorksheetElement + + include MetaElement include StyledElement - include MarkupElement - - def initialize(workbook=nil, worksheet=nil, *args, &block) - set_ivar(:workbook, workbook) - set_ivar(:worksheet, worksheet) - set_ivar(:data, nil) - set_ivar(:format, Format.new(:general)) - set_ivar(:rowspan, 1) - set_ivar(:colspan, 1) - set_ivar(:href, nil) - set_ivar(:index, nil) - set_ivar(:formula, nil) - if block_given? - set_binding_ivars(block.binding) - instance_exec(*args, &block) - end + + def initialize(data_value=nil) + @data = cast_data_value(data_value) + @format = Format.new(:general) + @rowspan = 1 + @colspan = 1 + @index = nil + @href = nil + @formula = nil + end + + def data(value=nil) + value.nil? ? @data : @data = cast_data_value(value) + end + + def format(value=nil, opts={}) + value.nil? ? @format : @format = Format.new(value, opts) + end + + def rowspan(value=nil) + value.nil? ? @rowspan : @rowspan = value end - def data(value) - set_ivar(:data, case value - when ::String, ::Numeric, ::Date, ::Time, ::DateTime + def colspan(value=nil) + value.nil? ? @colspan : @colspan = value + end + + def index(value=nil) + value.nil? ? @index : @index = value + end + + def href(value=nil) + value.nil? ? @href : @href = value + end + + def formula(value=nil) + value.nil? ? @formula : @formula = value + end + + private + + def cast_data_value(value) + case value + when ::String, ::Numeric, ::Date, ::Time, ::DateTime, ::NilClass value when ::Symbol value.to_s else value.inspect.to_s - end) - end - - def format(type, opts={}) - set_ivar(:format, Format.new(type, opts)) - end - - def rowspan(value); set_ivar(:rowspan, value); end - def colspan(value); set_ivar(:colspan, value); end - def href(value); set_ivar(:href, value); end - def index(value); set_ivar(:index, value); end - def formula(value); set_ivar(:formula, value); end - - def attributes - { - :style_class => get_ivar(:style_class), - :data => get_ivar(:data), - :format => get_ivar(:format), - :colspan => get_ivar(:colspan), - :rowspan => get_ivar(:rowspan), - :href => get_ivar(:href), - :index => get_ivar(:index), - :formula => get_ivar(:formula) - } + end end end diff --git a/lib/osheet/column.rb b/lib/osheet/column.rb index 4aa366c..47f6b7e 100644 --- a/lib/osheet/column.rb +++ b/lib/osheet/column.rb @@ -1,40 +1,34 @@ +require 'osheet/meta_element' +require 'osheet/styled_element' + module Osheet class Column - include Instance - include WorkbookElement - include WorksheetElement - include StyledElement + include MetaElement - include MarkupElement - - def initialize(workbook=nil, worksheet=nil, *args, &block) - set_ivar(:workbook, workbook) - set_ivar(:worksheet, worksheet) - set_ivar(:width, nil) - set_ivar(:autofit, false) - set_ivar(:hidden, false) - if block_given? - set_binding_ivars(block.binding) - instance_exec(*args, &block) - end + include StyledElement + + attr_reader :format + + def initialize(width=nil) + @width = width + @autofit = false + @hidden = false + @format = Format.new(:general) end def width(value=nil) - !value.nil? ? set_ivar(:width, value) : get_ivar(:width) + value.nil? ? @width : @width = value + end + + def autofit(value=nil) + value.nil? ? @autofit : @autofit = !!value end - def autofit(value); set_ivar(:autofit, !!value); end - def autofit?; get_ivar(:autofit); end - def hidden(value); set_ivar(:hidden, !!value); end - def hidden?; get_ivar(:hidden); end - - def attributes - { - :style_class => get_ivar(:style_class), - :width => get_ivar(:width), - :autofit => get_ivar(:autofit), - :hidden => get_ivar(:hidden) - } + def autofit?; @autofit; end + + def hidden(value=nil) + value.nil? ? @hidden : @hidden = !!value end + def hidden?; @hidden; end end end diff --git a/lib/osheet/format.rb b/lib/osheet/format.rb index bb22840..8b493da 100644 --- a/lib/osheet/format.rb +++ b/lib/osheet/format.rb @@ -19,11 +19,11 @@ module Osheet::Format ] class << self - def new(type, opts={}) + def new(type, *args) unless VALUES.include?(type) raise ArgumentError, "'#{type.inspect}' is not a valid format type" end - self.const_get(type.to_s.capitalize).new(opts) + self.const_get(type.to_s.capitalize).new(*args) end end diff --git a/lib/osheet/instance.rb b/lib/osheet/instance.rb deleted file mode 100644 index 57d7b12..0000000 --- a/lib/osheet/instance.rb +++ /dev/null @@ -1,30 +0,0 @@ -module Osheet::Instance - - private - - OSHEET_IVAR_NS = "_osheet_" - - def get_ivar(name) - instance_variable_get(ivar_name(name)) - end - - def set_ivar(name, value) - instance_variable_set(ivar_name(name), value) - end - - def push_ivar(name, value) - get_ivar(name) << value - end - - def ivar_name(name) - "@#{OSHEET_IVAR_NS}#{name}" - end - - def set_binding_ivars(binding) - binding.eval('instance_variables'). - reject{|ivar| ivar =~ /^@#{OSHEET_IVAR_NS}/}. - each do |ivar| - instance_variable_set(ivar, binding.eval(ivar.to_s)) - end - end -end diff --git a/lib/osheet/markup_element.rb b/lib/osheet/markup_element.rb deleted file mode 100644 index fdc6e56..0000000 --- a/lib/osheet/markup_element.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Osheet - module MarkupElement - - # markup elements can add partial markup to themselves - def add(partial_name, *args) - if self.kind_of?(Workbook) - # on: workbook - if (partial = self.partials.get(partial_name)) - # add partial - instance_exec(*args, &partial) - end - else - # on: worksheet, column, row - if self.workbook && (partial = self.workbook.partials.get(partial_name)) - # add partial - instance_exec(*args, &partial) - end - end - end - - end -end diff --git a/lib/osheet/meta_element.rb b/lib/osheet/meta_element.rb index c5193a6..59b0e70 100644 --- a/lib/osheet/meta_element.rb +++ b/lib/osheet/meta_element.rb @@ -1,7 +1,8 @@ +module Osheet; end module Osheet::MetaElement def meta(value=nil) - value.nil? ? get_ivar(:meta) : set_ivar(:meta, value) + value.nil? ? instance_variable_get("@meta") : instance_variable_set("@meta", value) end end diff --git a/lib/osheet/mixin.rb b/lib/osheet/mixin.rb index 6abcd9a..e896a30 100644 --- a/lib/osheet/mixin.rb +++ b/lib/osheet/mixin.rb @@ -1,36 +1,48 @@ -require 'osheet/style' -require 'osheet/template' - module Osheet::Mixin def self.included(receiver) receiver.send(:extend, ClassMethods) end + class Args + + attr_reader :args, :build + + def initialize(*args, &build) + @args = args + @build = build || Proc.new {} + end + + end + module ClassMethods - def style(*selectors, &block) + + def style(*selectors, &build) instance_variable_set("@s", - (instance_variable_get("@s") || []) << ::Osheet::Style.new(*selectors, &block) + (instance_variable_get("@s") || []) << Args.new(*selectors, &build) ) end + def styles instance_variable_get("@s") || [] end - def template(element, name, &block) + def template(element, name, &build) instance_variable_set("@t", - (instance_variable_get("@t") || []) << ::Osheet::Template.new(element, name, &block) + (instance_variable_get("@t") || []) << Args.new(element, name, &build) ) end + def templates instance_variable_get("@t") || [] end - def partial(name, &block) + def partial(name, &build) instance_variable_set("@p", - (instance_variable_get("@p") || []) << ::Osheet::Partial.new(name, &block) + (instance_variable_get("@p") || []) << Args.new(name, &build) ) end + def partials instance_variable_get("@p") || [] end diff --git a/lib/osheet/partial.rb b/lib/osheet/partial.rb index 38ab418..050a59c 100644 --- a/lib/osheet/partial.rb +++ b/lib/osheet/partial.rb @@ -2,23 +2,19 @@ module Osheet class Partial < ::Proc # this class is essentially a way to define a named definition - # block with arguments. If the partial is added to an element, - # any markup in it's definition block is applied to the element. - # ie. the definition block will be instance_eval'd on the element. - - include Instance + # block with arguments. If the partial is added to an element, + # any markup in it's definition block is applied to the element. + # ie. the definition block will be instance_eval'd on workbook. def initialize(name) unless name.kind_of?(::String) || name.kind_of?(::Symbol) raise ArgumentError, "please use a string or symbol for the partial name." end - set_ivar(:name, name.to_s) + @name = name.to_s super() end - def name; get_ivar(:name); end - def name=(v); set_ivar(:name, v); end - end end + diff --git a/lib/osheet/partial_set.rb b/lib/osheet/partial_set.rb deleted file mode 100644 index 0c51d14..0000000 --- a/lib/osheet/partial_set.rb +++ /dev/null @@ -1,57 +0,0 @@ -require 'osheet/partial' - -module Osheet - class PartialSet < ::Hash - - # this class is a Hash that behaves kinda like a set. I want to - # push partials into the set using the '<<' operator, only allow - # Osheet::Partial objs to be pushed, and then be able to reference - # a particular partial using a key - - def initialize - super - end - - def <<(partial) - if (key = verify(partial)) - push(key, partial) - end - end - - # return the named partial - def get(name) - lookup(key(name.to_s)) - end - - private - - def lookup(key) - self[key] - end - - # push the partial onto the key - def push(key, partial) - self[key] = partial - end - - # verify the partial, init and return the key - # otherwise ArgumentError it up - def verify(partial) - unless partial.kind_of?(Partial) - raise ArgumentError, 'you can only push Osheet::Partial objs to the partial set' - end - pkey = partial_key(partial) - self[pkey] ||= nil - pkey - end - - def partial_key(partial) - key(partial.name) - end - - def key(name) - name.to_s - end - - end -end diff --git a/lib/osheet/railtie.rb b/lib/osheet/railtie.rb deleted file mode 100644 index 7026e34..0000000 --- a/lib/osheet/railtie.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Osheet - class Railtie < Rails::Railtie - - initializer "osheet.register" do |app| - ::Osheet.register - end - - end -end diff --git a/lib/osheet/row.rb b/lib/osheet/row.rb index af22c9d..3137fec 100644 --- a/lib/osheet/row.rb +++ b/lib/osheet/row.rb @@ -1,44 +1,40 @@ +require 'osheet/meta_element' +require 'osheet/styled_element' + require 'osheet/cell' module Osheet class Row - include Instance - include Associations - include WorkbookElement - include WorksheetElement - include StyledElement + include MetaElement - include MarkupElement - - hm :cells - - def initialize(workbook=nil, worksheet=nil, *args, &block) - set_ivar(:workbook, workbook) - set_ivar(:worksheet, worksheet) - set_ivar(:height, nil) - set_ivar(:autofit, false) - set_ivar(:hidden, false) - if block_given? - set_binding_ivars(block.binding) - instance_exec(*args, &block) - end + include StyledElement + + attr_reader :cells, :format + + def initialize(height=nil) + @height = height + @autofit = false + @hidden = false + @cells = [] + @format = Format.new(:general) end def height(value=nil) - !value.nil? ? set_ivar(:height, value) : get_ivar(:height) + value.nil? ? @height : @height = value end - def autofit(value); set_ivar(:autofit, !!value); end - def autofit?; get_ivar(:autofit); end - def hidden(value); set_ivar(:hidden, !!value); end - def hidden?; get_ivar(:hidden); end - - def attributes - { - :style_class => get_ivar(:style_class), - :height => get_ivar(:height), - :autofit => get_ivar(:autofit), - :hidden => get_ivar(:hidden) - } + + def autofit(value=nil) + value.nil? ? @autofit : @autofit = !!value + end + def autofit?; @autofit; end + + def hidden(value=nil) + value.nil? ? @hidden : @hidden = !!value + end + def hidden?; @hidden; end + + def cell(cell_obj) + @cells << cell_obj end end diff --git a/lib/osheet/style.rb b/lib/osheet/style.rb index 7dd17cb..3902161 100644 --- a/lib/osheet/style.rb +++ b/lib/osheet/style.rb @@ -2,45 +2,31 @@ module Osheet class Style # this class is essentially a set of collectors for style settings - # each setting collects any arguments passed to it and - # it is up to the drivers to determine how to use the settings - - include Instance + # each setting collects any arguments passed to it and + # it is up to the writer to determine how to use the settings BORDER_POSITIONS = [:top, :right, :bottom, :left] BORDERS = BORDER_POSITIONS.collect{|p| "border_#{p}".to_sym} SETTINGS = [:align, :font, :bg] + BORDERS - def initialize(*selectors, &block) - set_ivar(:selectors, verify(selectors)) - SETTINGS.each {|setting| set_ivar(setting, [])} - if block_given? - set_binding_ivars(block.binding) - instance_eval(&block) - end - end + attr_reader :selectors, :build - def selectors - get_ivar(:selectors) + def initialize(*selectors, &build) + @selectors = verify(selectors) + @build = build || Proc.new {} + SETTINGS.each { |s| instance_variable_set("@#{s}", []) } end SETTINGS.each do |setting| define_method(setting) do |*args| - set_ivar(setting, get_ivar(setting) + args) + instance_variable_get("@#{setting}").tap do |value| + instance_variable_set("@#{setting}", value + args) if !args.empty? + end end end def border(*args) - BORDERS.each do |border| - send(border, *args) - end - end - - def attributes - SETTINGS.inject({}) do |attrs, s| - attrs[s] = get_ivar(s) - attrs - end + BORDERS.each { |border| send(border, *args) } end def match?(style_class) diff --git a/lib/osheet/style_set.rb b/lib/osheet/style_set.rb deleted file mode 100644 index 0a32def..0000000 --- a/lib/osheet/style_set.rb +++ /dev/null @@ -1,39 +0,0 @@ -require 'osheet/style' - -module Osheet - class StyleSet < ::Array - - # this class is an Array has some helper methods. I want to - # push styles into the set using the '<<' operator, only allow - # Osheet::Style objs to be pushed, and then be able to reference - # a particular set of styles using a style class. - - def initialize - super - end - - def <<(value) - super if verify(value) - end - - # return the style set for the style class - def for(style_class) - self.select{|s| s.match?(style_class)} - end - - private - - def styles(class_str) - end - - # verify the style, otherwise ArgumentError it up - def verify(style) - if style.kind_of?(Style) - true - else - raise ArgumentError, 'you can only push Osheet::Style objs to the style set' - end - end - - end -end diff --git a/lib/osheet/styled_element.rb b/lib/osheet/styled_element.rb index ebe3164..6dbe3fe 100644 --- a/lib/osheet/styled_element.rb +++ b/lib/osheet/styled_element.rb @@ -1,5 +1,13 @@ +module Osheet; end module Osheet::StyledElement - def style_class(value); set_ivar(:style_class, verify_style_class(value)); end + + def style_class(value=nil) + if value.nil? + instance_variable_get("@style_class") + else + instance_variable_set("@style_class", verify_style_class(value)) + end + end private diff --git a/lib/osheet/template.rb b/lib/osheet/template.rb index ecb8973..60d812a 100644 --- a/lib/osheet/template.rb +++ b/lib/osheet/template.rb @@ -4,10 +4,8 @@ module Osheet class Template < Partial # this class is a partial that is associated with an osheet element - # if an element is initialized from a template, the template - # block will be instance_eval'd for the element being initialized - - include Instance + # if an element is initialized from a template, the template + # block will be instance_eval'd for the element being initialized ELEMENTS = ['worksheet', 'column', 'row', 'cell'] @@ -16,12 +14,9 @@ def initialize(element, name) raise ArgumentError, "you can only define a template for #{ELEMENTS.join(', ')} elements." end - set_ivar(:element, element.to_s) + @element = element.to_s super(name) end - def element; get_ivar(:element); end - def element=(v); set_ivar(:element, v); end - end end diff --git a/lib/osheet/template_set.rb b/lib/osheet/template_set.rb deleted file mode 100644 index 0848046..0000000 --- a/lib/osheet/template_set.rb +++ /dev/null @@ -1,51 +0,0 @@ -require 'osheet/partial_set' -require 'osheet/template' - -module Osheet - class TemplateSet < PartialSet - - # this class is a PartialSet that keys off of the template element - # and name. Only Osheet::Template objs can be pushed, and you reference - # a particular template using a key of - - def initialize - super - end - - # return the template set for the named element - def get(element, name) - lookup(key(element.to_s, name.to_s)) - end - - private - - def lookup(key) - self[key.first][key.last] if self[key.first] - end - - # push the template onto the key - def push(key, template) - self[key.first][key.last] = template - end - - # verify the template, init the key set, and return the key string - # otherwise ArgumentError it up - def verify(template) - unless template.kind_of?(Template) - raise ArgumentError, 'you can only push Osheet::Template objs to the template set' - end - key = template_key(template) - self[key.first] ||= {} - self[key.first][key.last] ||= nil - key - end - - def template_key(template) - key(template.element, template.name) - end - - def key(element, name) - [element.to_s, name.to_s] - end - end -end diff --git a/lib/osheet/view_handler.rb b/lib/osheet/view_handler.rb deleted file mode 100644 index b0bfe53..0000000 --- a/lib/osheet/view_handler.rb +++ /dev/null @@ -1,2 +0,0 @@ -require 'osheet/view_handler/tilt' if defined?(Tilt) -require 'osheet/view_handler/rails' if defined?(Rails) diff --git a/lib/osheet/view_handler/rails.rb b/lib/osheet/view_handler/rails.rb deleted file mode 100644 index 3ccafd0..0000000 --- a/lib/osheet/view_handler/rails.rb +++ /dev/null @@ -1,44 +0,0 @@ -# Rails 2.X Template -if Rails.version =~ /^2/ - require 'action_view/base' - require 'action_view/template' - - module ActionView - module TemplateHandlers - class OsheetHandler < TemplateHandler - include Compilable - - def compile(template) - # template.format => 'xls' (or xlsx or whatever they specify) - %{::Osheet::Workbook.new {#{template.source}\n }.to_data} - end - - end - end - end - - ::ActionView::Template.register_template_handler :osheet, ActionView::TemplateHandlers::OsheetHandler -end - -# Rails 3.X Template -if Rails.version =~ /^3/ - require 'action_view/base' - require 'action_view/template' - - module ActionView - module Template::Handlers - class OsheetHandler < Template::Handler - include Compilable - - self.default_format = Osheet::MIME_TYPE - def compile(template) - # template.format => 'xls' (or xlsx or whatever they specify) - %{::Osheet::Workbook.new {\n#{template.source}\n }.to_data} - end - - end - end - end - - ::ActionView::Template.register_template_handler :osheet, ActionView::Template::Handlers::OsheetHandler -end diff --git a/lib/osheet/view_handler/tilt.rb b/lib/osheet/view_handler/tilt.rb deleted file mode 100644 index ce8951e..0000000 --- a/lib/osheet/view_handler/tilt.rb +++ /dev/null @@ -1,42 +0,0 @@ -# TILT/Sinatra template engine -module Osheet - class TiltHandler < Tilt::Template - - def self.engine_initialized? - defined? ::Osheet::Workbook - end - - def initialize_engine - require_template_library 'osheet' - end - - def prepare; end - - def precompiled_preamble(locals) - "::Osheet::Workbook.new {\n#{super}" - end - - def precompiled_template(locals) - data.to_str - end - def precompiled_postamble(locals) - "}.to_data" - end - - end -end - -Tilt.register 'osheet', Osheet::TiltHandler - -if defined?(::Sinatra::Base) - class ::Sinatra::Base - - # use this helper method to render Osheet views in Sinatra - # => view files should be named: "#{template}.osheet" - def osheet(template, options={}, locals={}) - options.merge! :layout => false, :default_content_type => ::Osheet::MIME_TYPE - render :osheet, template, options, locals - end - - end -end diff --git a/lib/osheet/workbook.rb b/lib/osheet/workbook.rb index c5514f1..d049769 100644 --- a/lib/osheet/workbook.rb +++ b/lib/osheet/workbook.rb @@ -1,65 +1,157 @@ -require 'osheet/instance' -require 'osheet/associations' -require 'osheet/markup_element' -require 'osheet/style_set' -require 'osheet/template_set' -require 'osheet/partial_set' -require 'osheet/worksheet' -require 'osheet/xmlss_writer' +require 'osheet/partial' +require 'osheet/template' +require 'osheet/workbook_element' +require 'osheet/workbook_api' module Osheet + + class Workbook - include Instance - include Associations - include MarkupElement - - hm :worksheets - - def initialize(&block) - set_ivar(:title, nil) - set_ivar(:styles, StyleSet.new) - set_ivar(:templates, TemplateSet.new) - set_ivar(:partials, PartialSet.new) - if block_given? - set_binding_ivars(block.binding) - instance_eval(&block) + + # This 'Workbook' class is really just a scope for workbook builds to run + # in. All actually workbook metadata is behavior is handled by the + # 'WorkbookElement' class + + class ElementStack < ::Array; end + + include WorkbookApi + + def initialize(writer=nil, data={}, &build) + # apply :data options to workbook scope + data ||= {} + if (data.keys.map(&:to_s) & self.public_methods.map(&:to_s)).size > 0 + raise ArgumentError, "data conflicts with workbook public methods." end - end + metaclass = class << self; self; end + data.each {|key, value| metaclass.class_eval { define_method(key){value} }} - def title(value=nil) - !value.nil? ? set_ivar(:title, value) : get_ivar(:title) - end + # setup the writer, element stack, and workbook_element + writer.bind(self) if writer + set_ivar(:writer, writer) + set_ivar(:element_stack, Workbook::ElementStack.new) + set_ivar(:workbook_element, WorkbookElement.new) + + # push the workbook element onto the element stack + element_stack.push(workbook) - def style(*selectors, &block); push_ivar(:styles, Style.new(*selectors, &block)); end - def styles - get_ivar(:styles) + # run any instance workbook build given + instance_eval(&build) if build end - def template(element, name, &block); push_ivar(:templates, Template.new(element, name, &block)); end - def templates - get_ivar(:templates) + + def writer + get_ivar(:writer) end - def partial(name, &block); push_ivar(:partials, Partial.new(name, &block)); end - def partials - get_ivar(:partials) + + def element_stack + get_ivar(:element_stack) end - def attributes - { :title => get_ivar(:title) } + def workbook_element + get_ivar(:workbook_element) end + alias_method :workbook, :workbook_element + # use a mixin to define its markup handlers (templates, partials, and styles) + # in your workbook scope + # all blocks in mixins will be instance eval'd in the workbook scope + # and should be written as such def use(mixin) - (mixin.styles || []).each{ |s| push_ivar(:styles, s) } - (mixin.templates || []).each{ |t| push_ivar(:templates, t) } - (mixin.partials || []).each{ |p| push_ivar(:partials, p) } + # templates and partials are just blocks themselves so they just need to + # be added to the workbook element + # they will be instance eval'd when they get used + (mixin.templates || []).each { |mt| template(*mt.args, &mt.build) } + (mixin.partials || []).each { |mp| partial(*mp.args, &mp.build) } + + # styles not only need to be added to the workbook element, but + # any build passed to the style needs to be instance eval'd + (mixin.styles || []).each do |ms| + StyleBuild.new(self, *ms.args, &ms.build).add do |build| + instance_eval(&build) + end + end end - def writer - XmlssWriter::Base.new(:workbook => self) + # add partials to dynamically add markup to your workbook + # note: you can also define element templates to add element specific + # markup to your workbook + def add(partial_name, *args) + workbook.partials.get(partial_name).tap do |p| + instance_exec(*args, &p) if p + end + end + + # overriding to make less noisy + def to_str(*args) + "#" end + alias_method :inspect, :to_str - [:to_data, :to_file].each do |meth| + # writers are responsible for defining what each of these methods do + # and what they return + [:to_s, :to_data, :to_file].each do |meth| define_method(meth) {|*args| writer.send(meth, *args) } end + private + + OSHEET_IVAR_NS = "_osheet_" + + def get_ivar(name) + instance_variable_get(ivar_name(name)) + end + + def set_ivar(name, value) + instance_variable_set(ivar_name(name), value) + end + + def push_ivar(name, value) + get_ivar(name) << value + end + + def ivar_name(name) + "@#{OSHEET_IVAR_NS}#{name}" + end + + end + + + + class Workbook::ElementStack + + # this class is just a wrapper to Array. I want to treat this as a + # stack of objects for the workbook DSL to reference. I need to push + # an object onto the stack, reference it using the 'current' method, + # and pop it off the stack when I'm done. + + def initialize + super + end + + def push(*args) + super + end + + def pop(*args) + super + end + + def current + self.last + end + + def size(*args) + super + end + + def using(obj, &block) + push(obj) + block.call if !block.nil? + pop + end + end + + end diff --git a/lib/osheet/workbook_api.rb b/lib/osheet/workbook_api.rb new file mode 100644 index 0000000..708d741 --- /dev/null +++ b/lib/osheet/workbook_api.rb @@ -0,0 +1,208 @@ +module Osheet; end +module Osheet::WorkbookApi + + # reference API + + [ :templates, + :partials, + :styles + ].each do |meth| + define_method(meth) do |*args| + workbook.send(meth, *args) + end + end + + def worksheets + workbook.worksheets + end + + def columns + worksheets.last.columns + end + + def rows + worksheets.last.rows + end + + def cells + rows.last.cells + end + + # markup handling API + + def template(*args, &build) + Osheet::Template.new(*args, &build).tap do |template| + element_stack.current.template(template) + end + end + + def partial(*args, &build) + Osheet::Partial.new(*args, &build).tap do |partial| + element_stack.current.partial(partial) + end + end + + class StyleBuild + + def initialize(scope, *args, &build) + @scope = scope + @style = Osheet::Style.new(*args, &build) + end + + def add + @scope.element_stack.current.style(@style) + if block_given? + @scope.element_stack.using(@style) { yield @style.build } + end + end + end + + def style(*args, &build) + StyleBuild.new(self, *args, &build).add do |build| + build.call + end + end + + # element handling API + + class TemplatedElement + + # this class is used to create elements that can be templated. + # Arguments are handled differently if building an element from + # a template vs. building from a build block. + # After the element is built, it is added to the current stack element + # and either the build or writer is called. + + ELEMENT_CLASS = { + :worksheet => Osheet::Worksheet, + :column => Osheet::Column, + :row => Osheet::Row, + :cell => Osheet::Cell + } + + def initialize(scope, name, *args, &build) + @scope = scope + @workbook = @scope.workbook + @name = name + @args = args + if (@template = @workbook.templates.get(@name, @args.first)) + @element = ELEMENT_CLASS[@name].new + @build = Proc.new { @scope.instance_exec(*@args[1..-1], &@template) } + else + @element = ELEMENT_CLASS[@name].new(*@args) + @build = build || Proc.new {} + end + end + + def add + @scope.element_stack.current.send(@name, @element) + @scope.element_stack.using(@element) do + if @scope.writer + @scope.writer.send(@name, @element, &@build) + else + @build.call + end + end + end + + end + + # used on: workbook + def worksheet(*args, &block) + if args.empty? && block.nil? + worksheets.last + else + TemplatedElement.new(self, :worksheet, *args, &block).add + end + end + + # used on: worksheet + def column(*args, &block) + TemplatedElement.new(self, :column, *args, &block).add + end + + # used on: worksheet + def row(*args, &block) + if args.empty? && block.nil? + rows.last + else + TemplatedElement.new(self, :row, *args, &block).add + end + end + + # used on: row + def cell(*args, &block) + if args.empty? && block.nil? + cells.last + else + TemplatedElement.new(self, :cell, *args, &block).add + end + end + + # style attribute API + + Osheet::Style::SETTINGS.each do |setting| + define_method(setting) do |*args| + element_stack.current.send(setting, *args) + end + end + + def border(*args) + element_stack.current.border(*args) + end + + # element attribute API + + def meta(*args) + element_stack.current.meta(*args) + end + + # on both style_class and format attribute updates + # call the writer's style hook, passing it the current element's + # style_class and format values. Both are needed to write + # an elements style data and style_id. + + # used by: column, row, cell + def style_class(value) + current_class = element_stack.current.style_class(value) # for self referencing + if self.writer # for writing + self.writer.style(current_class, element_stack.current.format) + end + end + + # used by: cell + def format(*args) + current_format = element_stack.current.format(*args) # for self referencing + if self.writer # for writing + self.writer.style(element_stack.current.style_class, current_format) + end + end + + # used by: workbook_element + def title(*args) + element_stack.current.title(*args) + end + + [ :name, # worksheet + :width, # column + :height, # row + :autofit, # column, row + :autofit?, # column, row + :hidden, # column, row + :hidden?, # column, row + :data, # cell + :href, # cell + :formula, # cell + :index, # cell + :rowspan, # cell + :colspan, # cell + ].each do |meth| + define_method(meth) do |*args| + element_stack.current.send(meth, *args) # for self referencing + if self.writer # for writing + self.writer.send(meth, *args) + end + end + end + +end diff --git a/lib/osheet/workbook_element.rb b/lib/osheet/workbook_element.rb index c9d4561..f949b82 100644 --- a/lib/osheet/workbook_element.rb +++ b/lib/osheet/workbook_element.rb @@ -1,17 +1,234 @@ -module Osheet::WorkbookElement +require 'osheet/style' +require 'osheet/worksheet' + +module Osheet + + + class WorkbookElement + + # This 'WorkbookElement' class handles all workbook state. It is setup + # and referenced by the 'Workbook' class when it runs builds + + class PartialSet < ::Hash; end + class TemplateSet < PartialSet; end + class StyleSet < ::Array; end + class WorksheetSet < ::Array; end + + attr_reader :title + attr_reader :templates, :partials, :styles, :worksheets + + def initialize + @title = nil + + @templates = TemplateSet.new + @partials = PartialSet.new + @styles = StyleSet.new + @worksheets = WorksheetSet.new + end + + def title(value=nil) + value.nil? ? @title : @title = value.to_s + end + + def template(template) + @templates << template + end + + def partial(partial) + @partials << partial + end + + def style(style) + @styles << style + end + + def worksheet(worksheet) + @worksheets << worksheet + end + + def styles(*args) + @styles.for(*args) + end + + def ==(other) + title == other.title && + templates == other.templates && + partials == other.partials && + styles == other.styles && + worksheets == other.worksheets + end + + end + + + + class WorkbookElement::PartialSet + + # this class is a Hash that behaves kinda like a set. I want to + # push partials into the set using the '<<' operator, only allow + # Osheet::Partial objs to be pushed, and then be able to reference + # a particular partial using its name + + def initialize + super + end + + def <<(partial) + if (key = verify(partial)) + push(key, partial) + end + end + + # return the named partial + def get(name) + lookup(key(name.to_s)) + end + + private + + def lookup(key) + self[key] + end + + # push the partial onto the key + def push(key, partial) + self[key] = partial + end + + # verify the partial, init and return the key + # otherwise ArgumentError it up + def verify(partial) + unless partial.kind_of?(Partial) + raise ArgumentError, 'you can only push Osheet::Partial objs to the partial set' + end + pkey = partial_key(partial) + self[pkey] ||= nil + pkey + end + + def partial_key(partial) + key(partial.instance_variable_get("@name")) + end + + def key(name) + name.to_s + end + + end + + + + class WorkbookElement::TemplateSet < WorkbookElement::PartialSet + + # this class is a PartialSet that keys off of the template element + # and name. Only Osheet::Template objs can be pushed, and you reference + # a particular template by its element and name + + def initialize + super + end + + # return the template set for the named element + def get(element, name) + lookup(key(element.to_s, name.to_s)) + end + + private + + def lookup(key) + self[key.first][key.last] if self[key.first] + end + + # push the template onto the key + def push(key, template) + self[key.first][key.last] = template + end + + # verify the template, init the key set, and return the key string + # otherwise ArgumentError it up + def verify(template) + unless template.kind_of?(Template) + raise ArgumentError, 'you can only push Osheet::Template objs to the template set' + end + key = template_key(template) + self[key.first] ||= {} + self[key.first][key.last] ||= nil + key + end + + def template_key(template) + key(template.instance_variable_get("@element"), template.instance_variable_get("@name")) + end + + def key(element, name) + [element.to_s, name.to_s] + end - def workbook - get_ivar(:workbook) end - [:styles, :templates].each do |thing| - define_method(thing) do - if workbook && workbook.respond_to?(thing) - workbook.send(thing) + + + class WorkbookElement::StyleSet < ::Array + + # this class is an Array with some helper methods. I want to + # push styles into the set using the '<<' operator, only allow + # Osheet::Style objs to be pushed, and then be able to reference + # a particular set of styles using a style class. + + def initialize + super + end + + def <<(value) + super if verify(value) + end + + # return the style set for the style class + def for(style_class=nil) + style_class.nil? ? self : self.select{|s| s.match?(style_class)} + end + + private + + # verify the style, otherwise ArgumentError it up + def verify(style) + if style.kind_of?(Style) + true + else + raise ArgumentError, 'you can only push Osheet::Style objs to the style set' + end + end + + end + + + + class WorkbookElement::WorksheetSet < ::Array + + # this class is just a wrapper to Array. I want to push worksheets + # into the set using the '<<' operator, but only allow Worksheet objs + # to be pushed. + + def initialize + super + end + + def <<(value) + super if verify(value) + end + + private + + # verify the worksheet, otherwise ArgumentError it up + def verify(worksheet) + if worksheet.kind_of?(Worksheet) + true else - nil + raise ArgumentError, 'can only push Osheet::Worksheet to the set' end end + end + end diff --git a/lib/osheet/worksheet.rb b/lib/osheet/worksheet.rb index df9b2f3..33b0b5d 100644 --- a/lib/osheet/worksheet.rb +++ b/lib/osheet/worksheet.rb @@ -1,44 +1,38 @@ +require 'osheet/meta_element' require 'osheet/column' require 'osheet/row' +# this class is collects and validates worksheet meta-data. It allows +# for storing a set of columns for referencing when building rows. It is +# up to the writer to take this data and use it as needed. + module Osheet class Worksheet - include Instance - include Associations - include WorkbookElement + include MetaElement - include MarkupElement - - hm :columns - hm :rows - - def initialize(workbook=nil, *args, &block) - set_ivar(:workbook, workbook) - set_ivar(:name, nil) - if block_given? - set_binding_ivars(block.binding) - instance_exec(*args, &block) - end + + attr_reader :columns, :rows + + def initialize(name=nil, *args) + @name = name + @columns = [] + @rows = [] end def name(value=nil) - !value.nil? ? set_ivar(:name, sanitized_name(value)) : get_ivar(:name) + value.nil? ? @name : @name = value.to_s end - def attributes - { :name => get_ivar(:name) } + def column(column_obj) + @columns << column_obj end - private - - def sanitized_name(name_value) - if get_ivar(:workbook) && get_ivar(:workbook).worksheets.collect{|ws| ws.name}.include?(name_value) - raise ArgumentError, "the sheet name '#{name_value}' is already in use. choose a sheet name that is not used by another sheet" - end - if name_value.to_s.length > 31 - raise ArgumentError, "worksheet names must be less than 32 characters long" - end - name_value.to_s + # Osheet only stores the latest row in memory for reference + # memory bloat would be unmanageable in large spreadsheets if + # all rows were stored + def row(row_obj) + @rows.pop + @rows << row_obj end end diff --git a/lib/osheet/worksheet_element.rb b/lib/osheet/worksheet_element.rb deleted file mode 100644 index 7ec6a2f..0000000 --- a/lib/osheet/worksheet_element.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Osheet::WorksheetElement - - def worksheet - get_ivar(:worksheet) - end - - [:columns, :rows].each do |meth| - define_method(meth) do - if worksheet && worksheet.respond_to?(meth) - worksheet.send(meth) - else - nil - end - end - end - -end diff --git a/lib/osheet/xmlss_writer.rb b/lib/osheet/xmlss_writer.rb index 067d464..9fcba0b 100644 --- a/lib/osheet/xmlss_writer.rb +++ b/lib/osheet/xmlss_writer.rb @@ -1 +1,143 @@ -require 'osheet/xmlss_writer/base' +require 'osheet' +require 'xmlss' + +require 'osheet/xmlss_writer/style_cache' + +module Osheet + class XmlssWriter + + # The Osheet::XmlssWriter provides the logic/translation needed to drive + # an xmlss workbook using the Osheet API. The writer creates an xmlss + # workbook and uses the builder approach to drive the workbook creation. + + # The writer maintains a set of used xmlss styles and handles creating + # xmlss style objects as needed and manages style keys + + attr_reader :style_cache, :xmlss_workbook + + def initialize(*args, &block) + @xmlss_workbook = Xmlss::Workbook.new(Xmlss::Writer.new(*args, &block)) + @osheet_workbook = nil + @osheet_worksheet_names = [] + @style_cache = nil + end + + def bind(osheet_workbook) + @osheet_workbook = osheet_workbook + @style_cache = StyleCache.new(@osheet_workbook, @xmlss_workbook) + end + + def to_s + @xmlss_workbook.to_s + end + alias_method :to_data, :to_s + + def to_file(file_path) + @xmlss_workbook.to_file(file_path) + end + + # Element writers + + def worksheet(worksheet, &build) + if @osheet_workbook && @osheet_worksheet_names.include?(worksheet.name.to_s) + raise ArgumentError, "you can't write two worksheets with the same name ('#{worksheet.name}')" + end + @osheet_worksheet_names << worksheet.name.to_s + @xmlss_workbook.worksheet(worksheet.name, &build) + end + + def column(column, &build) + attrs = { + :width => column.width, + :auto_fit_width => column.autofit, + :hidden => column.hidden + } + if s = @style_cache.get(column.style_class, column.format) + attrs[:style_id] = s.id + end + @xmlss_workbook.column(attrs, &build) + end + + def row(row, &build) + attrs = { + :height => row.height, + :auto_fit_height => row.autofit, + :hidden => row.hidden + } + if s = @style_cache.get(row.style_class, row.format) + attrs[:style_id] = s.id + end + @xmlss_workbook.row(attrs, &build) + end + + def cell(cell, &build) + attrs = { + :href => cell.href, + :index => cell.index, + :merge_across => cell_merge(cell.colspan), + :merge_down => cell_merge(cell.rowspan), + :formula => cell.formula + } + if s = @style_cache.get(cell.style_class, cell.format) + attrs[:style_id] = s.id + end + @xmlss_workbook.cell(cell.data, attrs, &build) + end + + # Element style writers + + # given an elements style_class or format attributes: + # 1) write a new xmlss style object and + # 2) set the current element's style_id attribute + + def style(style_class, format=nil) + @style_cache.get( + style_class, + format || Osheet::Format.new(:general) + ).tap do |xmlss_style| + @xmlss_workbook.style_id(xmlss_style.id) + end + end + + def colspan(count) + @xmlss_workbook.merge_across(cell_merge(count)) + end + + def rowspan(count) + @xmlss_workbook.merge_down(cell_merge(count)) + end + + def name(value) + @osheet_worksheet_names.pop + @osheet_worksheet_names << value + @xmlss_workbook.name(value) + end + + # Element attribute writers + + [ :width, # column + :height, # row + :autofit, # column, row + :autofit?, # column, row + :hidden, # column, row + :hidden?, # column, row + :data, # cell + :format, # cell + :href, # cell + :formula, # cell + :index # cell + ].each do |meth| + define_method(meth) do |*args, &block| + @xmlss_workbook.send(meth, *args, &block) + end + end + + protected + + # convert osheet col/row span value to xmlss merge value + def cell_merge(span) + span.kind_of?(::Fixnum) && span > 1 ? span-1 : 0 + end + + end +end diff --git a/lib/osheet/xmlss_writer/base.rb b/lib/osheet/xmlss_writer/base.rb deleted file mode 100644 index bb36910..0000000 --- a/lib/osheet/xmlss_writer/base.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'fileutils' -module Osheet::XmlssWriter; end -require 'xmlss' -require 'osheet/xmlss_writer/elements' -require 'osheet/xmlss_writer/styles' - -module Osheet::XmlssWriter - class Base - include Elements - include Styles - - attr_reader :used_xstyles - - def initialize(opts={}) - @used_xstyles = [] - self.oworkbook = if opts.has_key?(:workbook) - opts[:workbook] - else - ::Osheet::Workbook.new - end - end - - def to_data(xmlss_output_opts={}) - self.xworkbook(xmlss_output_opts).to_s - end - - def to_file(file_path, xmlss_output_opts={}) - self.xworkbook(xmlss_output_opts).to_file(file_path) - end - - - - - def oworkbook=(value) - unless value.kind_of?(::Osheet::Workbook) - raise ArgumentError, "'#{value.inspect}' is not an Osheet::Workbook" - end - @oworkbook = value - end - - def xworkbook(xmlss_output_opts={}) - @used_xstyles = [] - ::Xmlss::Workbook.new(:output => xmlss_output_opts).tap do |xwkbk| - @oworkbook.worksheets.each { |sheet| worksheet(xwkbk, sheet) } - end - end - - end -end diff --git a/lib/osheet/xmlss_writer/elements.rb b/lib/osheet/xmlss_writer/elements.rb deleted file mode 100644 index 8849a44..0000000 --- a/lib/osheet/xmlss_writer/elements.rb +++ /dev/null @@ -1,56 +0,0 @@ -module Osheet::XmlssWriter::Elements - - protected - - def worksheet(xworkbook, oworksheet) - xworkbook.worksheet(oworksheet.attributes[:name]) do - oworksheet.columns.each { |ocolumn| column(xworkbook, ocolumn) } - oworksheet.rows.each { |orow| row(xworkbook, orow) } - end - end - - def column(xworkbook, ocolumn) - xworkbook.column({ - :style_id => style_id(xworkbook, ocolumn.attributes[:style_class]), - :width => ocolumn.attributes[:width], - :auto_fit_width => ocolumn.attributes[:autofit], - :hidden => ocolumn.attributes[:hidden] - }) - end - - def row(xworkbook, orow) - xworkbook.row({ - :style_id => style_id(xworkbook, orow.attributes[:style_class]), - :height => orow.attributes[:height], - :auto_fit_height => orow.attributes[:autofit], - :hidden => orow.attributes[:hidden] - }) do - orow.cells.each { |ocell| cell(xworkbook, ocell) } - end - end - - def cell(xworkbook, ocell) - xworkbook.cell({ - :style_id => style_id(xworkbook, ocell.attributes[:style_class], ocell.attributes[:format]), - :href => ocell.attributes[:href], - :index => ocell.attributes[:index], - :merge_across => cell_merge(ocell.attributes[:colspan]), - :merge_down => cell_merge(ocell.attributes[:rowspan]), - :formula => ocell.attributes[:formula] - }) do - data(xworkbook, ocell.attributes[:data]) - end - end - - def data(xworkbook, odata) - xworkbook.data(odata) - end - - private - - # convert osheet col/row span value to xmlss merge value - def cell_merge(span) - span.kind_of?(::Fixnum) && span > 1 ? span-1 : 0 - end - -end diff --git a/lib/osheet/xmlss_writer/style_cache.rb b/lib/osheet/xmlss_writer/style_cache.rb new file mode 100644 index 0000000..f6313e9 --- /dev/null +++ b/lib/osheet/xmlss_writer/style_cache.rb @@ -0,0 +1,64 @@ +require 'osheet/style' +require 'osheet/xmlss_writer' +require 'osheet/xmlss_writer/style_settings' + +class Osheet::XmlssWriter + class StyleCache + + attr_reader :styles + + def initialize(osheet_workbook, xmlss_workbook) + @osheet_workbook = osheet_workbook + @xmlss_workbook = xmlss_workbook + @styles = {} + end + + def [](key); @styles[key]; end + def empty?; @styles.empty?; end + def size; @styles.keys.size; end + + def keys + @styles.keys + end + + def get(style_class, format) + # generate the style key and get the get the cached style or + # build and cache and return a style for the key + return nil if (key = self.key(style_class, format.key)).empty? + @styles[key] || + build_and_cache(key, @osheet_workbook.styles.for(style_class), format) + end + + protected + + # build a unique key for styling based off the style and format keys + def key(class_value, format_key) + (class_value || '').strip.split(/\s+/).collect do |c| + ".#{c}" + end.join('') + (format_key.nil? || format_key.empty? ? '' : "..#{format_key}") + end + + # build and cache an xmlss style + def build_and_cache(key, styles, format) + settings = StyleSettings.new(styles) + @styles[key] = @xmlss_workbook.style(key) { + settings.setting(:align) { @xmlss_workbook.alignment(settings[:align]) } + settings.setting(:font) { @xmlss_workbook.font(settings[:font]) } + settings.setting(:bg) { @xmlss_workbook.interior(settings[:bg]) } + + border_set = ::Osheet::Style::BORDERS.inject([]) do |set, bp| + settings.setting(bp) { set << settings[bp] } + set + end + if !border_set.empty? + @xmlss_workbook.borders { + border_set.each { |setting| @xmlss_workbook.border(setting) } + } + end + + @xmlss_workbook.number_format(format.style) + } + end + + end +end diff --git a/lib/osheet/xmlss_writer/style_settings.rb b/lib/osheet/xmlss_writer/style_settings.rb new file mode 100644 index 0000000..b2dd488 --- /dev/null +++ b/lib/osheet/xmlss_writer/style_settings.rb @@ -0,0 +1,148 @@ +require 'osheet/style' +require 'osheet/xmlss_writer' + +class Osheet::XmlssWriter + class StyleSettings + + attr_reader :styles, :value + + def initialize(styles) + @styles = styles + @value = @styles.inject({}) do |value, style| + merged_settings(value, style_settings(style)) + end + end + + def [](setting); @value[setting]; end + + # call the given block passing it the setting if that setting + # exists and is not empty + def setting(s, &block) + block.call if self.value[s] && !self.value[s].empty? + end + + protected + + def merged_settings(current, add) + # concat values for keys in both sets + current.keys.each do |k| + current[k].merge!(add.delete(k) || {}) + end + # merge keys for anything not in the current + current.merge(add) + end + + def style_settings(osheet_style_obj) + settings = {} + Osheet::Style::SETTINGS.each do |setting| + if !(v = osheet_style_obj.send(setting)).empty? + settings[setting] = self.send("#{setting}_settings", v) + end + end + settings + end + + def align_settings(osheet_align_directives) + osheet_align_directives.inject({}) do |settings, directive| + case directive + when :left, :center, :right + settings[:horizontal] = directive + when :top, :bottom + settings[:vertical] = directive + when :middle + settings[:vertical] = :center + when :wrap + settings[:wrap_text] = true + when ::Fixnum + settings[:rotate] = directive + end + settings + end + end + + def font_settings(osheet_font_directives) + osheet_font_directives.inject({}) do |settings, directive| + case directive + when ::Fixnum + settings[:size] = directive + when ::String + if directive =~ /^#/ + settings[:color] = directive + else + settings[:name] = directive + end + when :bold, :italic, :shadow + settings[directive] = true + when :subscript, :superscript + settings[:alignment] = directive + when :strikethrough + settings[:strike_through] = true + when :underline + settings[:underline] = :single + when :double_underline + settings[:underline] = :double + when :accounting_underline + settings[:underline] = :single_accounting + when :double_accounting_underline + settings[:underline] = :double_accounting + end + settings + end + end + + def bg_settings(osheet_bg_directives) + osheet_bg_directives.inject({}) do |settings, directive| + case directive + when ::String + if directive =~ /^#/ + settings[:color] = directive + end + when ::Symbol + if ::Xmlss::Style::Interior.pattern_set.include?(directive) + settings[:pattern] = directive + end + when ::Hash + directive.each do |pattern, color| + if ::Xmlss::Style::Interior.pattern_set.include?(pattern) && color =~ /^#/ + settings[:pattern] = pattern + settings[:pattern_color] = color + end + end + end + + if !settings[:color].nil? && settings[:pattern].nil? + settings[:pattern] = :solid + end + + settings + end + end + + def border_settings(osheet_border_directives) + osheet_border_directives.inject({}) do |settings, directive| + case directive + when ::String + if directive =~ /^#/ + settings[:color] = directive + end + when ::Symbol + if ::Xmlss::Style::Border.position_set.include?(directive) + settings[:position] = directive + elsif ::Xmlss::Style::Border.weight_set.include?(directive) + settings[:weight] = directive + elsif ::Xmlss::Style::Border.line_style_set.include?(directive) + settings[:line_style] = directive + end + end + settings + end + end + + ::Osheet::Style::BORDER_POSITIONS.each do |p| + define_method("border_#{p}_settings") do |cmds| + border_settings(cmds+[p]) + end + end + + end +end diff --git a/lib/osheet/xmlss_writer/styles.rb b/lib/osheet/xmlss_writer/styles.rb deleted file mode 100644 index 4565903..0000000 --- a/lib/osheet/xmlss_writer/styles.rb +++ /dev/null @@ -1,216 +0,0 @@ -require 'osheet/format' -module Osheet::XmlssWriter::Styles - - protected - - def style_id(xworkbook, style_class, oformat=nil) - xstyle = style(xworkbook, style_class, oformat) - xstyle.nil? ? '' : xstyle.id - end - - def style(xworkbook, style_class, oformat=nil) - oformat ||= Osheet::Format.new(:general) - - # generate the style key for the given class/format - key = style_key(style_class, oformat.key) - - # see if we have already created/used an xmlss style for the given key - xstyle = @used_xstyles.find{ |xs| xs.id == key } - - # if not, create an xmlss style for the key and add it to the used cache - if !key.empty? && xstyle.nil? - xstyle = xmlss_style(xworkbook, key, oformat) - @used_xstyles << xstyle - end - - xstyle - end - - private - - def style_key(style_class, format_key) - (style_class || '').strip.split(/\s+/).collect do |c| - ".#{c}" - end.join('') + (format_key.nil? || format_key.empty? ? '' : "..#{format_key}") - end - - def xmlss_style(xworkbook, key, oformat) - # take all the matching osheet styles for the given key, - # and build up xmlss style settings for them - settings = style_settings(key) - - xworkbook.style(key) do - - if settings.has_key?(:align) && !settings[:align].empty? - xworkbook.alignment(settings[:align]) - end - - if settings.has_key?(:font) && !settings[:font].empty? - xworkbook.font(settings[:font]) - end - - if settings.has_key?(:bg) && !settings[:bg].empty? - xworkbook.interior(settings[:bg]) - end - - border_set = ::Osheet::Style::BORDERS.inject([]) do |set, bp| - if settings.has_key?(bp) && !settings[bp].empty? - set << settings[bp] - end - set - end - if !border_set.empty? - xworkbook.borders { - border_set.each { |border_setting| xworkbook.border(border_setting) } - } - end - - if oformat - xworkbook.number_format(oformat.style) - end - - end - end - - # TODO: would be nice to have a class handle all of the osheet-xmlss style translations... - - def style_settings(key) - @oworkbook.styles.for(key).inject({}) do |style_settings, ostyle| - merged_settings(style_settings, ostyle_settings(ostyle)) - end - end - - def merged_settings(current, add) - # concat values for keys in both sets - current.keys.each do |k| - current[k].merge!(add.delete(k) || {}) - end - # merge keys for anything not in the current - current.merge(add) - end - - def ostyle_settings(ostyle) - ostyle_settings = {} - ostyle.attributes.each do |k,v| - unless v.empty? - ostyle_settings[k] = send("#{k}_settings", v) - end - end - ostyle_settings - end - - def align_settings(align_cmds) - align_cmds.inject({}) do |align_settings, align_cmd| - if (setting = case align_cmd - when :left, :center, :right - [:horizontal, align_cmd] - when :top, :bottom - [:vertical, align_cmd] - when :middle - [:vertical, :center] - when :wrap - [:wrap_text, true] - when ::Fixnum - [:rotate, align_cmd] - end - ) - align_settings[setting.first] = setting.last - end - align_settings - end - end - - def font_settings(font_cmds) - font_cmds.inject({}) do |font_settings, font_cmd| - if (setting = case font_cmd - when ::Fixnum - [:size, font_cmd] - when ::String - if font_cmd =~ /^#/ - [:color, font_cmd] - else - [:name, font_cmd] - end - when :bold, :italic, :shadow - [font_cmd, true] - when :subscript, :superscript - [:alignment, font_cmd] - when :strikethrough - [:strike_through, true] - when :underline - [:underline, :single] - when :double_underline - [:underline, :double] - when :accounting_underline - [:underline, :single_accounting] - when :double_accounting_underline - [:underline, :double_accounting] - end - ) - font_settings[setting.first] = setting.last - end - font_settings - end - end - - def bg_settings(bg_cmds) - bg_cmds.inject({}) do |bg_settings, bg_cmd| - if (settings = case bg_cmd - when ::String - if bg_cmd =~ /^#/ - [[:color, bg_cmd]] - end - when ::Symbol - if ::Xmlss::Style::Interior.pattern_set.include?(bg_cmd) - [[:pattern, bg_cmd]] - end - when ::Hash - bg_cmd.inject([]) do |set, kv| - if ::Xmlss::Style::Interior.pattern_set.include?(kv.first) && kv.last =~ /^#/ - set << [:pattern, kv.first] - set << [:pattern_color, kv.last] - end - set - end - end - ) - settings.each do |setting| - bg_settings[setting.first] = setting.last - end - end - if !bg_settings[:color].nil? && bg_settings[:pattern].nil? - bg_settings[:pattern] = :solid - end - bg_settings - end - end - - def border_settings(border_cmds) - border_cmds.inject({}) do |border_settings, border_cmd| - if (setting = case border_cmd - when ::String - if border_cmd =~ /^#/ - [:color, border_cmd] - end - when ::Symbol - if ::Xmlss::Style::Border.position_set.include?(border_cmd) - [:position, border_cmd] - elsif ::Xmlss::Style::Border.weight_set.include?(border_cmd) - [:weight, border_cmd] - elsif ::Xmlss::Style::Border.line_style_set.include?(border_cmd) - [:line_style, border_cmd] - end - end - ) - border_settings[setting.first] = setting.last - end - border_settings - end - end - ::Osheet::Style::BORDER_POSITIONS.each do |p| - define_method("border_#{p}_settings") do |cmds| - border_settings(cmds+[p]) - end - end - -end diff --git a/osheet.gemspec b/osheet.gemspec index 948cde7..d172ce6 100644 --- a/osheet.gemspec +++ b/osheet.gemspec @@ -23,6 +23,6 @@ Gem::Specification.new do |s| s.add_development_dependency("ruby-prof") s.add_dependency("enumeration", ["~> 1.3"]) - s.add_dependency("xmlss", ["~> 0.4"]) + s.add_dependency("xmlss", ["~> 1.0.0.rc"]) end diff --git a/test/cell_test.rb b/test/cell_test.rb index 29fc935..4284eef 100644 --- a/test/cell_test.rb +++ b/test/cell_test.rb @@ -3,133 +3,68 @@ module Osheet - class CellTest < Assert::Context - desc "Osheet::Cell" + class CellTests < Assert::Context + desc "a Cell" before { @c = Cell.new } subject { @c } - should_be_a_styled_element(Cell) - should_be_a_worksheet_element(Cell) - should_be_a_workbook_element(Cell) + should be_a_styled_element + should be_a_meta_element - should have_instance_methods :data, :format, :colspan, :rowspan, :href, :index + should have_instance_methods :data, :format, :href, :index + should have_instance_methods :colspan, :rowspan should "set it's defaults" do - assert_equal nil, subject.send(:get_ivar, "data") - assert_kind_of Format::General, subject.send(:get_ivar, "format") - assert_equal 1, subject.send(:get_ivar, "colspan") - assert_equal 1, subject.send(:get_ivar, "rowspan") - assert_equal nil, subject.send(:get_ivar, "href") - assert_equal nil, subject.send(:get_ivar, "index") - assert_equal nil, subject.send(:get_ivar, "formula") + assert_equal nil, subject.data + assert_equal 1, subject.colspan + assert_equal 1, subject.rowspan + assert_equal nil, subject.href + assert_equal nil, subject.index + assert_equal nil, subject.formula + assert_kind_of Format::General, subject.format + end + + should "set its data from an init arg" do + assert_equal "something", Cell.new("something").data end should "type cast data strings/symbols" do ['a string', :symbol].each do |thing| - cell = Cell.new{data thing} - assert_kind_of ::String, cell.send(:get_ivar, "data") + subject.data thing + assert_kind_of ::String, subject.data + assert_kind_of ::String, Cell.new(thing).data end end should "type cast data dates" do - cell = Cell.new{data Date.today} - assert_kind_of ::Date, cell.send(:get_ivar, "data") + subject.data Date.today + assert_kind_of ::Date, subject.data + assert_kind_of ::Date, Cell.new(Date.today).data end should "type cast data numerics" do [1, 1.0].each do |thing| - cell = Cell.new{data thing} - assert_kind_of ::Numeric, cell.send(:get_ivar, "data") + subject.data thing + assert_kind_of ::Numeric, subject.data + assert_kind_of ::Numeric, Cell.new(thing).data end end should "type cast all other data to string" do [Osheet, [:a, 'Aye'], {:a => 'Aye'}].each do |thing| - cell = Cell.new{data thing} - assert_kind_of ::String, cell.send(:get_ivar, "data") - end - end - - end - - class CellAttributeTest < CellTest - desc "a celll that has attributes" - before do - @c = Cell.new do - style_class 'more poo' - data "Poo" - format :number - colspan 4 - rowspan 2 - index 3 - formula "=R1C1" - href "http://www.google.com" - end - end - - should "should set them correctly" do - assert_equal "Poo", subject.send(:get_ivar, "data") - assert_kind_of Format::Number, subject.send(:get_ivar, "format") - assert_equal 4, subject.send(:get_ivar, "colspan") - assert_equal 2, subject.send(:get_ivar, "rowspan") - assert_equal 3, subject.send(:get_ivar, "index") - assert_equal "=R1C1", subject.send(:get_ivar, "formula") - assert_equal "http://www.google.com", subject.send(:get_ivar, "href") - end - - should "know it's attribute(s)" do - [:style_class, :data, :format, :rowspan, :colspan, :index, :href].each do |a| - assert subject.attributes.has_key?(a) + subject.data thing + assert_kind_of ::String, subject.data + assert_kind_of ::String, Cell.new(thing).data end - - assert_equal "more poo", subject.attributes[:style_class] - assert_equal "Poo", subject.attributes[:data] - assert_kind_of Format::Number, subject.attributes[:format] - assert_equal 4, subject.attributes[:colspan] - assert_equal 2, subject.attributes[:rowspan] - assert_equal 3, subject.attributes[:index] - assert_equal "=R1C1", subject.attributes[:formula] - assert_equal "http://www.google.com", subject.attributes[:href] end - end - - class CellPartialTest < Assert::Context - desc "A workbook that defines column partials" - before do - @wkbk = Workbook.new { - partial(:cell_stuff) { - style_class 'more poo' - data "Poo" - } - - worksheet { row { cell { - add :cell_stuff - } } } - } - end - subject { @wkbk } + should "format data explicitly" do + subject.data Time.now + subject.format :datetime, 'mm/dd/yyyy' - should "add it's partials to it's markup" do - assert_equal 'more poo', subject.worksheets.first.rows.first.cells.first.attributes[:style_class] - assert_equal 'Poo', subject.worksheets.first.rows.first.cells.first.attributes[:data] + assert_kind_of Format::Datetime, subject.format end end - class CellBindingTest < Assert::Context - desc "a cell defined w/ a block" - - should "access instance vars from that block's binding" do - @test = 'test' - @cell = Cell.new { data @test} - - assert !@cell.send(:instance_variable_get, "@test").nil? - assert_equal @test, @cell.send(:instance_variable_get, "@test") - assert_equal @test.object_id, @cell.send(:instance_variable_get, "@test").object_id - assert_equal @test, @cell.attributes[:data] - assert_equal @test.object_id, @cell.attributes[:data].object_id - end - end - end diff --git a/test/column_test.rb b/test/column_test.rb index cff2437..21ec2ff 100644 --- a/test/column_test.rb +++ b/test/column_test.rb @@ -1,119 +1,51 @@ require "assert" + require "osheet/column" module Osheet - class ColumnTest < Assert::Context - desc "Osheet::Column" + class ColumnTests < Assert::Context + desc "a Column" before { @c = Column.new } subject { @c } - should_be_a_styled_element(Column) - should_be_a_worksheet_element(Column) - should_be_a_workbook_element(Column) + should be_a_styled_element + should be_a_meta_element - should have_instance_method :width + should have_reader :format + should have_instance_methods :width should have_instance_methods :autofit, :autofit? should have_instance_methods :hidden, :hidden? - should have_instance_method :meta should "set it's defaults" do - assert_equal nil, subject.send(:get_ivar, "width") - assert_equal false, subject.send(:get_ivar, "autofit") + assert_equal nil, subject.width + assert_equal false, subject.autofit assert !subject.autofit? - assert_equal false, subject.send(:get_ivar, "hidden") + assert_equal false, subject.hidden assert !subject.hidden? - - assert_equal nil, subject.meta + assert_kind_of Format::General, subject.format end should "set it's width" do subject.width(false) assert_equal false, subject.width + subject.width(180) assert_equal 180, subject.width + subject.width(nil) assert_equal 180, subject.width - end - should "cast autofit and hidden to bool" do - col = Column.new { autofit :true; hidden 'false'} - assert_kind_of ::TrueClass, col.send(:get_ivar, "autofit") - assert_kind_of ::TrueClass, col.send(:get_ivar, "hidden") + assert_equal 200, Column.new(200).width end - end - - class ColumnAttributesTest < ColumnTest - desc "that has attributes" - before do - @c = Column.new do - style_class "more poo" - width 100 - autofit true - hidden true - meta( - {} - ) - end - end - - should "should set them correctly" do - assert_equal 100, subject.send(:get_ivar, "width") - assert_equal true, subject.send(:get_ivar, "autofit") - assert subject.autofit? - assert_equal true, subject.send(:get_ivar, "hidden") - assert subject.hidden? - assert_equal({}, subject.meta) - end - - should "know it's attribute(s)" do - [:style_class, :width, :autofit, :hidden].each do |a| - assert subject.attributes.has_key?(a) - end - assert_equal 'more poo', subject.attributes[:style_class] - assert_equal 100, subject.attributes[:width] - assert_equal true, subject.attributes[:autofit] - assert_equal true, subject.attributes[:hidden] - end - - end - - class ColumnPartialTest < Assert::Context - desc "A workbook that defines column partials" - before do - @wkbk = Workbook.new { - partial(:column_stuff) { - width 200 - meta(:label => 'awesome') - } - - worksheet { column { - add :column_stuff - } } - } - end - subject { @wkbk } - - should "add it's partials to it's markup" do - assert_equal 200, subject.worksheets.first.columns.first.width - assert_equal({:label => 'awesome'}, subject.worksheets.first.columns.first.meta) - end - - end - - class ColumnBindingTest < Assert::Context - desc "a column defined w/ a block" - - should "access instance vars from that block's binding" do - @test = 50 - @col = Column.new { width @test } + should "cast autofit and hidden to bool" do + col = Column.new + col.autofit :true + col.hidden 'false' - assert !@col.send(:instance_variable_get, "@test").nil? - assert_equal @test, @col.send(:instance_variable_get, "@test") - assert_equal @test.object_id, @col.send(:instance_variable_get, "@test").object_id - assert_equal @test, @col.attributes[:width] - assert_equal @test.object_id, @col.attributes[:width].object_id + assert_equal true, col.autofit + assert_equal true, col.hidden end end diff --git a/test/mixins.rb b/test/fixtures/mixins.rb similarity index 75% rename from test/mixins.rb rename to test/fixtures/mixins.rb index 91d22b0..f69b62c 100644 --- a/test/mixins.rb +++ b/test/fixtures/mixins.rb @@ -9,7 +9,9 @@ class StyledMixin include Osheet::Mixin style('.test') - style('.test.awesome') + style('.test.awesome') { + align :left + } end @@ -39,10 +41,10 @@ class PartialedMixin row { } } - partial(:two_cells_in_a_row) { + partial(:two_cells_in_a_row) { |data_one, data_two| row { - cell { data "one" } - cell { data "two" } + cell { data data_one.to_s } + cell { data data_two.to_s } } } end diff --git a/test/fixtures/test_writer.rb b/test/fixtures/test_writer.rb new file mode 100644 index 0000000..1772f19 --- /dev/null +++ b/test/fixtures/test_writer.rb @@ -0,0 +1,68 @@ +class TestWriter + + attr_accessor :styles, :worksheets, :columns, :rows, :cells + + def initialize + @styles = [] + @worksheets = [] + @columns = [] + @rows = [] + @cells = [] + end + + def bind(workbook) + workbook + end + + def style(obj, &block) + @styles << obj + block.call if !block.nil? + end + + def worksheet(obj, &block) + @worksheets << obj + block.call if !block.nil? + end + + def column(obj, &block) + @columns << obj + block.call if !block.nil? + end + + def row(obj, &block) + @rows << obj + block.call if !block.nil? + end + + def cell(obj, &block) + @cells << obj + block.call if !block.nil? + end + + def style(*args) + return *args + end + + [ :title, # workbook_element + :name, # worksheet + :meta, # worksheet, column, row, cell + :width, # column + :height, # row + :autofit, # column, row + :autofit?, # column, row + :hidden, # column, row + :hidden?, # column, row + :data, # cell + :href, # cell + :formula, # cell + :index, # cell + :rowspan, # cell + :colspan, # cell + ].each do |meth| + define_method(meth) do |*args| + # cool story bro (don't do anything, just allow) + return *args + end + end + +end diff --git a/test/format_test.rb b/test/format_test.rb index be94987..9b58ee9 100644 --- a/test/format_test.rb +++ b/test/format_test.rb @@ -3,8 +3,8 @@ module Osheet - class FormatTest < Assert::Context - desc "Osheet::Format" + class FormatTests < Assert::Context + desc "a Format" before do @f = Format.new(:number, { :decimal_places => 4, diff --git a/test/helper.rb b/test/helper.rb index ae02e6c..9907136 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -3,104 +3,70 @@ # add root dir to the load path $LOAD_PATH.unshift(File.expand_path("../..", __FILE__)) -require 'osheet' - class Assert::Context - def xstyle_markup(xworkbook) - xworkbook.instance_variable_get("@__xmlss_undies_writer").flush.style_markup + def xmlss_style_markup(writer) + Xmlss::Workbook.writer(writer.xmlss_workbook).styles_markup.flush.to_s end - def xelement_markup(xworkbook) - xworkbook.instance_variable_get("@__xmlss_undies_writer").flush.element_markup + def xmlss_element_markup(writer) + Xmlss::Workbook.writer(writer.xmlss_workbook).worksheets_markup.flush.to_s end - class << self - + # Macros - def should_be_a_workbook_element(klass) - should have_instance_methods :workbook, :styles, :templates + def self.be_a_meta_element(*args) + called_from = caller.first + macro_name = "be a meta element" - should "be able to read the workbook and it's styles/templates" do - wkbk = Osheet::Workbook.new { - template(:column, :thing) {} - template(:row, :empty) {} - style('.title') { - font 14 - } - style('.title', '.header') { - font :bold - } - } - klass = klass.new(wkbk) + Assert::Macro.new(macro_name) do + should have_instance_method :meta, [called_from] - assert_equal wkbk, klass.workbook - assert_equal wkbk.styles, klass.styles - assert_equal wkbk.templates, klass.templates + should "default an empty meta value", called_from do + assert_equal nil, subject.class.new.meta end - end - - def should_be_a_worksheet_element(klass) - should have_instance_methods :worksheet, :columns - should "be able to read the worksheet and it's columns" do - wksht = Osheet::Worksheet.new { - column {} - column {} - column {} - } - klass = klass.new(nil, wksht) + should "set meta info", called_from do + meta_elem = subject.class.new - assert_equal wksht, klass.worksheet - assert_equal wksht.columns, klass.columns + meta_elem.meta({:key => "value"}) + assert_equal({:key => "value"}, meta_elem.meta) end end + end + + def self.be_a_styled_element(*args) + called_from = caller.first + macro_name = "be a styled element" - def should_be_a_styled_element(klass) - should have_instance_methods :style_class + Assert::Macro.new(macro_name) do + should have_instance_method :style_class, [called_from] - should "default an empty style class" do - default = klass.new - assert_equal nil, default.send(:get_ivar, "style_class") + should "default an empty style class", called_from do + assert_equal nil, subject.class.new.style_class end - should "set a style class" do - styled = klass.new{ style_class "awesome thing" } - assert_equal "awesome thing", styled.send(:get_ivar, "style_class") + should "set a style class", called_from do + styled = subject.class.new + styled.style_class("awesome thing") + assert_equal "awesome thing", styled.style_class end - should "verify the style class string" do + should "validate the style class string", called_from do ['.thing', 'thing.thing', 'thing .thing > thing', 'thin>g'].each do |s| assert_raises ArgumentError do - klass.new { style_class s } + subject.class.new.style_class(s) end end + ['thing', '#thing 123', 'thing-one thing_one'].each do |s| assert_nothing_raised do - klass.new { style_class s } + subject.class.new.style_class(s) end end end - end - - def should_hm(klass, collection, item_klass) - should have_reader collection - should have_instance_method collection.to_s.sub(/s$/, '') - - should "should initialize #{collection} and add them to it's collection" do - singular = collection.to_s.sub(/s$/, '') - thing = klass.new do - self.send(singular) {} - end - - items = thing.send(:get_ivar, collection) - assert_equal items, thing.send(collection) - assert !items.empty? - assert_equal 1, items.size - assert_kind_of item_klass, items.first - end end - end + end diff --git a/test/mixin_test.rb b/test/mixin_test.rb index 1ce0968..e8466e6 100644 --- a/test/mixin_test.rb +++ b/test/mixin_test.rb @@ -1,10 +1,11 @@ require "assert" -require "test/mixins" +require "test/fixtures/mixins" +require 'osheet/workbook' module Osheet - class MixinBaseTest < Assert::Context - desc "Osheet::Mixin thing" + class MixinTests < Assert::Context + desc "a mixin" subject { DefaultMixin } should have_readers :styles, :templates, :partials @@ -17,70 +18,63 @@ class MixinBaseTest < Assert::Context end end - class MixinStyleTest < Assert::Context + class MixinArgsTests < MixinTests + desc "args class" + before do + @build_block = Proc.new {} + @ma = Mixin::Args.new('some', 'args', 'here', &@build_block) + end + subject { @ma } + + should have_readers :args, :build + + should "collect arguments" do + assert_equal ['some', 'args', 'here'], subject.args + end + + should "store a build block" do + assert_same @build_block, subject.build + end + + should "default with empty args and an empty Proc build" do + default = Mixin::Args.new + + assert_empty default.args + assert_kind_of Proc, default.build + end + + end + + class MixinStyleTests < Assert::Context desc "that defines styles" subject { StyledMixin } should "have it's styles defined" do assert_equal 2, subject.styles.size - assert_equal 1, subject.styles.first.selectors.size - assert_equal '.test', subject.styles.first.selectors.first - assert_equal 1, subject.styles.last.selectors.size - assert_equal '.test.awesome', subject.styles.last.selectors.first + assert_kind_of Mixin::Args, subject.styles.first end end - class MixinTemplateTest < Assert::Context + class MixinTemplateTests < Assert::Context desc "that defines templates" subject { TemplatedMixin } should "have it's templates defined" do assert subject.templates assert_equal 3, subject.templates.size - assert_kind_of Template, subject.templates.first + assert_kind_of Mixin::Args, subject.templates.first end end - class MixinPartialTest < Assert::Context + class MixinPartialTests < Assert::Context desc "that defines partials" subject { PartialedMixin } should "have it's partials defined" do assert subject.partials assert_equal 2, subject.partials.size - assert_kind_of Partial, subject.partials.first - end - end - - class MixinUseTest < Assert::Context - desc "A workbook that uses mixins" - setup do - @workbook = Osheet::Workbook.new do - use PartialedMixin - use TemplatedMixin - use StyledMixin - end - end - subject { @workbook } - - should "have mixin partials" do - workbook_partials = @workbook.partials.values - PartialedMixin.partials.each do |partial| - assert workbook_partials.include?(partial) - end + assert_kind_of Mixin::Args, subject.partials.first end - - should "have mixin templates" do - workbook_templates = @workbook.templates.values.collect{ |t| t.values.first } - TemplatedMixin.templates.each do |template| - assert workbook_templates.include?(template) - end - end - - should "have mixin styles" do - assert_equal StyledMixin.styles, @workbook.styles - end - end end diff --git a/test/osheet_test.rb b/test/osheet_test.rb deleted file mode 100644 index 69cc657..0000000 --- a/test/osheet_test.rb +++ /dev/null @@ -1,13 +0,0 @@ -require "assert" - -class OsheetTest < Assert::Context - desc "Osheet" - subject {::Osheet} - - should have_instance_method :register - - should "use provide a default mime type" do - assert_equal "application/vnd.ms-excel", Osheet::MIME_TYPE - end - -end diff --git a/test/partial_set_test.rb b/test/partial_set_test.rb deleted file mode 100644 index 015d3dc..0000000 --- a/test/partial_set_test.rb +++ /dev/null @@ -1,64 +0,0 @@ -require "assert" -require "osheet/partial_set" - -module Osheet - - class PartialSetTest < Assert::Context - desc "Osheet::PartialSet" - before { @ps = PartialSet.new } - subject { @ps } - - should "be a Hash" do - assert_kind_of ::Hash, subject - end - - should have_instance_method :<< - should have_reader :get - - should "verify set objs are partials" do - assert_raises ArgumentError do - subject.send(:verify, {}) - end - assert_nothing_raised do - subject.send(:verify, Partial.new(:poo) {}) - end - end - - should "key using name values" do - assert_equal 'poo', subject.send(:key, :poo) - end - - should "key on partial objs" do - assert_equal 'poo', subject.send(:partial_key, Partial.new(:poo) {}) - end - - should "init the key in the set when verifying" do - key = subject.send(:verify, Partial.new(:thing) {}) - assert_equal 'thing', key - assert_equal({'thing' => nil}, subject) - end - - should "push partials onto the set" do - assert_nothing_raised do - subject << Partial.new(:poo) {} - subject << Partial.new(:not_poo) {} - subject << Partial.new(:awesome) {} - subject << Partial.new(:poo) {} - end - - assert_equal 3, subject.keys.size - assert subject["poo"] - assert_kind_of Partial, subject["poo"] - end - - should "lookup a partial by name" do - p = Partial.new(:poo) {} - subject << p - assert_equal p, subject.get(:poo) - assert_equal p, subject.get('poo') - assert_equal nil, subject.get(:ugh) - end - - end - -end diff --git a/test/partial_test.rb b/test/partial_test.rb index 5448d72..8f4176c 100644 --- a/test/partial_test.rb +++ b/test/partial_test.rb @@ -1,20 +1,19 @@ require "assert" + require "osheet/partial" module Osheet class PartialTest < Assert::Context - desc "Osheet::Partial" + desc "a Partial" before { @p = Partial.new(:thing) {} } subject { @p } - should have_accessor :name - should "be a Proc" do assert_kind_of ::Proc, subject end should "convert the name arg to a string and store off" do - assert_equal 'thing', subject.name + assert_equal 'thing', subject.instance_variable_get("@name") end end @@ -39,26 +38,4 @@ class PartialNameTest < Assert::Context end - class PartialBindingTest < Assert::Context - desc "a partial defined w/ a block" - - should "access instance vars from that block's binding" do - @test = 'test thing' - @workbook = Workbook.new { - partial(:stuff) { - worksheet(:thing) { name @test } - } - - add(:stuff) - } - - assert !@workbook.worksheets.first.send(:instance_variable_get, "@test").nil? - assert_equal @test, @workbook.worksheets.first.send(:instance_variable_get, "@test") - assert_equal @test.object_id, @workbook.worksheets.first.send(:instance_variable_get, "@test").object_id - assert_equal @test, @workbook.worksheets.first.attributes[:name] - assert_equal @test.object_id, @workbook.worksheets.first.attributes[:name].object_id - end - - end - end diff --git a/test/row_test.rb b/test/row_test.rb index 87b84ed..ebb0d64 100644 --- a/test/row_test.rb +++ b/test/row_test.rb @@ -1,124 +1,71 @@ require "assert" + +require 'osheet/cell' require 'osheet/row' module Osheet - class RowTest < Assert::Context - desc "Osheet::Row" + class RowTests < Assert::Context + desc "a Row" before { @rw = Row.new } subject { @rw } - should_be_a_styled_element(Row) - should_be_a_worksheet_element(Row) - should_be_a_workbook_element(Row) + should be_a_styled_element + should be_a_meta_element - should have_instance_method :height + should have_reader :format + should have_instance_methods :height should have_instance_methods :autofit, :autofit? should have_instance_methods :hidden, :hidden? - should have_instance_method :meta - - should_hm(Row, :cells, Cell) + should have_instance_methods :cells, :cell should "set it's defaults" do - assert_equal nil, subject.send(:get_ivar, "height") - assert_equal false, subject.send(:get_ivar, "autofit") + assert_equal nil, subject.height + assert_equal false, subject.autofit assert !subject.autofit? - assert_equal false, subject.send(:get_ivar, "hidden") + assert_equal false, subject.hidden assert !subject.hidden? - - assert_equal [], subject.cells - assert_equal nil, subject.meta + assert_kind_of Format::General, subject.format end - should "set it's height" do + should "set it's width" do subject.height(false) assert_equal false, subject.height + subject.height(180) assert_equal 180, subject.height + subject.height(nil) assert_equal 180, subject.height - end - - should "cast autofit and hidden to bool" do - rw = Row.new { autofit :true; hidden 'false'} - assert_kind_of ::TrueClass, rw.send(:get_ivar, "autofit") - assert_kind_of ::TrueClass, rw.send(:get_ivar, "hidden") - end - end - - class RowAttributeTest < RowTest - desc "a row that has attributes" - before do - @rw = Row.new do - style_class "poo" - height 100 - autofit true - hidden true - meta( - {} - ) - end + assert_equal 200, Row.new(200).height end - should "should set them correctly" do - assert_equal 100, subject.send(:get_ivar, "height") - assert_equal true, subject.send(:get_ivar, "autofit") - assert subject.autofit? - assert_equal true, subject.send(:get_ivar, "hidden") - assert subject.hidden? - assert_equal({}, subject.meta) - end + should "cast autofit and hidden to bool" do + row = Row.new + row.autofit :true + row.hidden 'false' - should "know it's attribute(s)" do - [:style_class, :height, :autofit, :hidden].each do |a| - assert subject.attributes.has_key?(a) - end - assert_equal 'poo', subject.attributes[:style_class] - assert_equal 100, subject.attributes[:height] - assert_equal true, subject.attributes[:autofit] - assert_equal true, subject.attributes[:hidden] + assert_equal true, row.autofit + assert_equal true, row.hidden end end - class RowPartialTest < Assert::Context - desc "A workbook that defines column partials" - before do - @wkbk = Workbook.new { - partial(:row_stuff) { - height 200 - meta 'awesome' - } - - worksheet { row { - add :row_stuff - } } - } - end - subject { @wkbk } + class RowCellTests < RowTests + desc "with cells" + before { + @cell = Cell.new + @rw.cell(@cell) + } - should "add it's partials to it's markup" do - assert_equal 200, subject.worksheets.first.rows.first.height - assert_equal 'awesome', subject.worksheets.first.rows.first.meta + should "know its cells" do + assert_equal 1, subject.cells.size + assert_same @cell, subject.cells.first end end - class RowBindingTest < Assert::Context - desc "a row defined w/ a block" - should "access instance vars from that block's binding" do - @test = 50 - @row = Row.new { height @test } - - assert !@row.send(:instance_variable_get, "@test").nil? - assert_equal @test, @row.send(:instance_variable_get, "@test") - assert_equal @test.object_id, @row.send(:instance_variable_get, "@test").object_id - assert_equal @test, @row.attributes[:height] - assert_equal @test.object_id, @row.attributes[:height].object_id - end - - end end diff --git a/test/style_set_test.rb b/test/style_set_test.rb deleted file mode 100644 index cf864f5..0000000 --- a/test/style_set_test.rb +++ /dev/null @@ -1,47 +0,0 @@ -require "assert" -require "osheet/style_set" - -module Osheet - class StyleSetTest < Assert::Context - desc "Osheet::StyleSet" - before { @set = StyleSet.new } - subject { @set } - - should "be an Array" do - assert_kind_of ::Array, subject - end - - should have_reader :for - - should "verify Template objs" do - assert_raises ArgumentError do - subject.send(:verify, {}) - end - assert_nothing_raised do - subject.send(:verify, Style.new('.awesome') {}) - end - end - - should "be able to lookup styles by class" do - subject << (a = Style.new('.awesome') {}) - subject << (at = Style.new('.awesome.thing') {}) - subject << (b = Style.new('.boring') {}) - subject << (bt = Style.new('.boring.thing') {}) - subject << (a_b = Style.new('.awesome', '.boring') {}) - subject << (t = Style.new('.thing') {}) - subject << (s = Style.new('.stupid') {}) - - { 'awesome' => [a, a_b], - 'boring' => [b, a_b], - 'thing' => [t], - 'awesome thing' => [a, at, a_b, t], - 'thing awesome' => [a, at, a_b, t], - 'other' => [] - }.each do |style_class, styles_set| - assert_equal styles_set, subject.for(style_class) - end - end - - end - -end diff --git a/test/style_test.rb b/test/style_test.rb index 56869c0..a1eb069 100644 --- a/test/style_test.rb +++ b/test/style_test.rb @@ -1,16 +1,18 @@ require "assert" + require "osheet/style" module Osheet class StyleTest < Assert::Context - desc "Osheet::Style" + desc "a Style" before { @st = Style.new('.test') } subject { @st } - should have_reader :selectors - should have_instance_methods :align, :font, :bg - should have_instance_methods :border, :border_left, :border_top, :border_right, :border_bottom + should have_reader :selectors, :build + should have_instance_methods :align, :font, :bg, :border + should have_instance_methods :border_left, :border_top + should have_instance_methods :border_right, :border_bottom should have_instance_method :match? should "set it's defaults" do @@ -19,15 +21,7 @@ class StyleTest < Assert::Context [ :align, :font, :bg, :border_left, :border_top, :border_right, :border_bottom ].each do |a| - assert_equal [], subject.send(:get_ivar, a) - end - end - - should "know it's attribute(s)" do - [ :align, :font, :bg, - :border_left, :border_top, :border_right, :border_bottom - ].each do |a| - assert subject.attributes.has_key?(a) + assert_equal [], subject.send(a) end end @@ -53,7 +47,7 @@ class StyleTest < Assert::Context should "collect styles for #{a}" do args = [1, "#FF0000", "Verdana", :love, 1.2] subject.send(a, *args) - assert_equal args, subject.send(:get_ivar, a) + assert_equal args, subject.send(a) end end @@ -61,7 +55,7 @@ class StyleTest < Assert::Context args = [:thick, '#FF00FF', :dot] subject.border *args [ :border_left, :border_top, :border_right, :border_bottom].each do |a| - assert_equal args, subject.send(:get_ivar, a) + assert_equal args, subject.send(a) end end @@ -88,17 +82,6 @@ class StyleTest < Assert::Context assert_equal false, a.match?('stupid') end - should "access instance vars from that block's binding" do - @test = 20 - @style = Style.new('.test') { font @test } - - assert !@style.send(:instance_variable_get, "@test").nil? - assert_equal @test, @style.send(:instance_variable_get, "@test") - assert_equal @test.object_id, @style.send(:instance_variable_get, "@test").object_id - assert_equal @test, @style.attributes[:font].first - assert_equal @test.object_id, @style.attributes[:font].first.object_id - end - end end diff --git a/test/template_set_test.rb b/test/template_set_test.rb deleted file mode 100644 index addb4a5..0000000 --- a/test/template_set_test.rb +++ /dev/null @@ -1,74 +0,0 @@ -require "assert" -require "osheet/template_set" - -module Osheet - class TemplateSetTest < Assert::Context - desc "Osheet::TemplateSet" - before { @set = TemplateSet.new } - subject { @set } - - should "be a PartialSet" do - assert_kind_of PartialSet, subject - end - - should "verify set objs are templates" do - assert_raises ArgumentError do - subject.send(:verify, {}) - end - assert_nothing_raised do - subject.send(:verify, Template.new(:row, :poo) {}) - end - end - - should "key templates using an array of their element and name" do - assert_equal ['row','poo'], subject.send(:key, :row, :poo) - end - - should "key on templates objs" do - assert_equal ['row', 'poo'], subject.send(:template_key, Template.new(:row, :poo) {}) - end - - should "init the key in the set when verifying" do - key = subject.send(:verify, Template.new(:row, :poo) {}) - assert_equal ['row', 'poo'], key - assert_equal({ - key.first => { key.last => nil } - }, subject) - end - - should "push templates onto the set" do - assert_nothing_raised do - subject << Template.new(:row, :poo) {} - subject << Template.new(:row, :not_poo) {} - subject << Template.new(:column, :awesome) {} - subject << Template.new(:column, :not_awesome) {} - end - - assert_equal 2, subject.keys.size - assert subject["row"] - assert_equal 2, subject["row"].keys.size - assert subject["row"]["poo"] - assert_kind_of Template, subject["row"]["poo"] - assert subject["row"]["not_poo"] - assert_kind_of Template, subject["row"]["not_poo"] - assert subject["column"] - assert_equal 2, subject["column"].keys.size - assert subject["column"]["awesome"] - assert_kind_of Template, subject["column"]["awesome"] - assert subject["column"]["not_awesome"] - assert_kind_of Template, subject["column"]["not_awesome"] - end - - should "lookup a template by element, name" do - t = Template.new(:row, :poo) {} - subject << t - assert_equal t, subject.get(:row, :poo) - assert_equal t, subject.get('row', 'poo') - - assert_equal nil, subject.get(:row, :ugh) - assert_equal nil, subject.get(:col, :ugh) - end - - end - -end diff --git a/test/template_test.rb b/test/template_test.rb index 2da8759..c496591 100644 --- a/test/template_test.rb +++ b/test/template_test.rb @@ -1,10 +1,11 @@ require "assert" + require "osheet/template" module Osheet - class TemplateTest < Assert::Context - desc "Osheet::Template" + class TemplateTests < Assert::Context + desc "a Template" before do @tmpl = Template.new('column', :thing) {} end @@ -14,19 +15,17 @@ class TemplateTest < Assert::Context assert_equal ['worksheet', 'column', 'row', 'cell'], Template::ELEMENTS end - should have_accessor :element - should "be a Partial" do assert_kind_of Partial, subject end should "convert the element ars to string and store off" do - assert_equal 'column', subject.element + assert_equal 'column', subject.instance_variable_get("@element") end end - class TemplateElementTest < TemplateTest + class TemplateElementTest < TemplateTests desc "a template" should "verify the element argument" do diff --git a/test/workbook_element_test.rb b/test/workbook_element_test.rb new file mode 100644 index 0000000..5e842ef --- /dev/null +++ b/test/workbook_element_test.rb @@ -0,0 +1,231 @@ +require "assert" + +require 'osheet/workbook_element' + +module Osheet + + + class WorkbookElementTests < Assert::Context + desc "a WorkbookElement object" + before do + @wd = WorkbookElement.new + end + subject { @wd } + + should have_reader :title + should have_readers :templates, :partials, :styles, :worksheets + should have_instance_methods :template, :partial, :style, :worksheet + + should "set it's defaults" do + assert_nil subject.title + assert_equal WorkbookElement::TemplateSet.new, subject.templates + assert_equal WorkbookElement::PartialSet.new, subject.partials + assert_equal WorkbookElement::StyleSet.new, subject.styles + assert_equal WorkbookElement::WorksheetSet.new, subject.worksheets + end + + end + + + + class PartialSetTests < Assert::Context + desc "a PartialSet" + before { @ps = WorkbookElement::PartialSet.new } + subject { @ps } + + should "be a Hash" do + assert_kind_of ::Hash, subject + end + + should have_instance_method :<< + should have_reader :get + + should "verify set objs are partials" do + assert_raises ArgumentError do + subject.send(:verify, {}) + end + assert_nothing_raised do + subject.send(:verify, Partial.new(:poo) {}) + end + end + + should "key using name values" do + assert_equal 'poo', subject.send(:key, :poo) + end + + should "key on partial objs" do + assert_equal 'poo', subject.send(:partial_key, Partial.new(:poo) {}) + end + + should "init the key in the set when verifying" do + key = subject.send(:verify, Partial.new(:thing) {}) + assert_equal 'thing', key + assert_equal({'thing' => nil}, subject) + end + + should "push partials onto the set" do + assert_nothing_raised do + subject << Partial.new(:poo) {} + subject << Partial.new(:not_poo) {} + subject << Partial.new(:awesome) {} + subject << Partial.new(:poo) {} + end + + assert_equal 3, subject.keys.size + assert subject["poo"] + assert_kind_of Partial, subject["poo"] + end + + should "lookup a partial by name" do + p = Partial.new(:poo) {} + subject << p + assert_equal p, subject.get(:poo) + assert_equal p, subject.get('poo') + assert_equal nil, subject.get(:ugh) + end + + end + + + + class TemplateSetTests < Assert::Context + desc "a TemplateSet" + before { @set = WorkbookElement::TemplateSet.new } + subject { @set } + + should "be a PartialSet" do + assert_kind_of WorkbookElement::PartialSet, subject + end + + should "verify set objs are templates" do + assert_raises ArgumentError do + subject.send(:verify, {}) + end + assert_nothing_raised do + subject.send(:verify, Template.new(:row, :poo) {}) + end + end + + should "key templates using an array of their element and name" do + assert_equal ['row','poo'], subject.send(:key, :row, :poo) + end + + should "key on templates objs" do + assert_equal ['row', 'poo'], subject.send(:template_key, Template.new(:row, :poo) {}) + end + + should "init the key in the set when verifying" do + key = subject.send(:verify, Template.new(:row, :poo) {}) + assert_equal ['row', 'poo'], key + assert_equal({ + key.first => { key.last => nil } + }, subject) + end + + should "push templates onto the set" do + assert_nothing_raised do + subject << Template.new(:row, :poo) {} + subject << Template.new(:row, :not_poo) {} + subject << Template.new(:column, :awesome) {} + subject << Template.new(:column, :not_awesome) {} + end + + assert_equal 2, subject.keys.size + assert subject["row"] + assert_equal 2, subject["row"].keys.size + assert subject["row"]["poo"] + assert_kind_of Template, subject["row"]["poo"] + assert subject["row"]["not_poo"] + assert_kind_of Template, subject["row"]["not_poo"] + assert subject["column"] + assert_equal 2, subject["column"].keys.size + assert subject["column"]["awesome"] + assert_kind_of Template, subject["column"]["awesome"] + assert subject["column"]["not_awesome"] + assert_kind_of Template, subject["column"]["not_awesome"] + end + + should "lookup a template by element, name" do + t = Template.new(:row, :poo) {} + subject << t + assert_equal t, subject.get(:row, :poo) + assert_equal t, subject.get('row', 'poo') + + assert_equal nil, subject.get(:row, :ugh) + assert_equal nil, subject.get(:col, :ugh) + end + + end + + + + class StyleSetTests < Assert::Context + desc "a StyleSet" + before { @set = WorkbookElement::StyleSet.new } + subject { @set } + + should "be an Array" do + assert_kind_of ::Array, subject + end + + should have_reader :for + + should "verify Style objs" do + assert_raises ArgumentError do + subject.send(:verify, {}) + end + assert_nothing_raised do + subject.send(:verify, Style.new('.awesome') {}) + end + end + + should "be able to lookup styles by class" do + subject << (a = Style.new('.awesome') {}) + subject << (at = Style.new('.awesome.thing') {}) + subject << (b = Style.new('.boring') {}) + subject << (bt = Style.new('.boring.thing') {}) + subject << (a_b = Style.new('.awesome', '.boring') {}) + subject << (t = Style.new('.thing') {}) + subject << (s = Style.new('.stupid') {}) + + { 'awesome' => [a, a_b], + 'boring' => [b, a_b], + 'thing' => [t], + 'awesome thing' => [a, at, a_b, t], + 'thing awesome' => [a, at, a_b, t], + 'other' => [] + }.each do |style_class, styles_set| + assert_equal styles_set, subject.for(style_class) + end + end + + should "return itself if calling for w/ no args" do + assert_equal subject, subject.for + end + + end + + + + class WorksheetSetTests < Assert::Context + desc "a WorksheetSet" + before { @set = WorkbookElement::WorksheetSet.new } + subject { @set } + + should "be an Array" do + assert_kind_of ::Array, subject + end + + should "verify Worksheet objs" do + assert_raises ArgumentError do + subject.send(:verify, {}) + end + assert_nothing_raised do + subject.send(:verify, Worksheet.new {}) + end + end + + end + + +end diff --git a/test/workbook_test.rb b/test/workbook_test.rb index 845215a..3004087 100644 --- a/test/workbook_test.rb +++ b/test/workbook_test.rb @@ -1,105 +1,143 @@ require "assert" +require 'test/fixtures/mixins' +require 'test/fixtures/test_writer' + require 'osheet/workbook' -require 'test/mixins' module Osheet - class WorkbookTest < Assert::Context - desc "Osheet::Workbook" - before { @wkbk = Workbook.new } + class WorkbookTests < Assert::Context + desc "a Workbook" + before do + @wkbk = Workbook.new + @test_writer = TestWriter.new + end subject { @wkbk } - should have_readers :styles, :templates, :partials - should have_instance_methods :title, :attributes, :use, :add - should have_instance_methods :style, :template, :partial + should have_instance_methods :writer, :element_stack + should have_instance_methods :use, :add + should have_instance_methods :to_s, :to_data, :to_file + + should have_instance_methods :workbook_element, :workbook + should have_instance_methods :templates, :template + should have_instance_methods :partials, :partial + should have_instance_methods :styles, :style + should have_instance_methods :worksheets, :worksheet + should have_instance_methods :columns, :column + should have_instance_methods :rows, :row + should have_instance_methods :cells, :cell - should_hm(Workbook, :worksheets, Worksheet) + should have_instance_methods :use, :add, :template, :partial + + should have_instance_methods :align, :font, :bg, :border + should have_instance_methods :border_left, :border_top + should have_instance_methods :border_right, :border_bottom + + should have_instance_methods :style_class, :format + should have_instance_methods :title, :meta, :name + should have_instance_methods :width, :height + should have_instance_methods :autofit, :autofit?, :hidden, :hidden? + should have_instance_methods :data, :href, :formula + should have_instance_methods :index, :rowspan, :colspan should "set it's defaults" do - assert_equal nil, subject.send(:get_ivar, "title") - assert_equal [], subject.worksheets - assert_equal StyleSet.new, subject.styles - assert_equal TemplateSet.new, subject.templates - assert_equal PartialSet.new, subject.partials + assert_equal nil, subject.workbook.title + + assert_equal WorkbookElement.new, subject.workbook_element + assert_equal subject.workbook_element, subject.workbook + + assert_kind_of Workbook::ElementStack, subject.element_stack + assert_equal 1, subject.element_stack.size + assert_equal subject.workbook_element, subject.element_stack.current end - should "know it's attribute(s)" do - subject.send(:title, "The Poo") - [:title].each do |a| - assert subject.attributes.has_key?(a) - end - assert_equal "The Poo", subject.attributes[:title] + should "set its title, casting it to a string" do + wb = Workbook.new { title :fun } + assert_equal "fun", wb.workbook.title end end - class WorkbookTitleTest < WorkbookTest - desc "A workbook with a title" - before { @wkbk = Workbook.new { title "The Poo" } } + class ElementStackTests < Assert::Context + desc "an ElementStack" + before { @s = Workbook::ElementStack.new } + subject { @s } - should "know it's title" do - assert_equal "The Poo", subject.title + should "be an Array" do + assert_kind_of ::Array, subject end - should "set it's title" do - subject.title(false) - assert_equal false, subject.title - subject.title('la') - assert_equal 'la', subject.title - subject.title(nil) - assert_equal 'la', subject.title + should have_instance_method :push, :pop, :current, :size, :using + + should "push objects onto the stack" do + assert_nothing_raised do + subject.push("something") + subject.push("something else") + end + + assert_equal 2, subject.size end - end + should "push objects onto the stack for the duration of a block" do + subject.push("something") - class WorkbookWorksheetsTest < WorkbookTest - desc "A workbook with worksheets" - before do - @wkbk = Workbook.new { - worksheet { - name "Poo!" - column - row { - cell { - format :number - data 1 - } - } - } - } + assert_equal "something", subject.current + subject.using("using") do + assert_equal "using", subject.current + end + assert_equal "something", subject.current end - should "set it's worksheets" do - assert_equal 1, subject.worksheets.size - assert_kind_of Worksheet, subject.worksheets.first + should "fetch the last item in the array with the current method" do + subject.push("something") + subject.push("something else") + + assert_equal "something else", subject.current end - should "not allow multiple worksheets with the same name" do - assert_raises ArgumentError do - Workbook.new { - worksheet { name "awesome" } - worksheet { name "awesome" } - } - end + should "remove the last item in the array with the pop method" do + subject.push("something") + subject.push("something else") + + assert_equal 2, subject.size + + item = subject.pop + assert_equal "something else", item + assert_equal 1, subject.size + end + + should "return nil if trying to pop on an empty stack" do + assert_equal 0, subject.size + assert_nothing_raised do - Workbook.new { - worksheet { name "awesome" } - worksheet { name "awesome1" } - } + subject.pop end + + assert_nil subject.pop end end - class WorkbookStyleTest < WorkbookTest - desc "A workbook that defines styles" + class WorkbookStyleTests < WorkbookTests + desc "that defines styles" before do - @wkbk = Workbook.new { + @wkbk = Workbook.new(@test_writer) { style('.test') style('.test.awesome') + + worksheet("styles") { + column { style_class "test awesome" } + } } end + should "return the style obj created" do + style = subject.style(".a.test.style") + + assert_kind_of Style, style + assert_equal '.a.test.style', style.selectors.first + end + should "add them to it's styles" do assert_equal 2, subject.styles.size assert_equal 1, subject.styles.first.selectors.size @@ -108,12 +146,104 @@ class WorkbookStyleTest < WorkbookTest assert_equal '.test.awesome', subject.styles.last.selectors.first end + should "write the style class on the element" do + assert_equal "test awesome", subject.worksheets.last.columns.last.style_class + end + end - class WorkbookPartialTest < WorkbookTest - desc "A workbook that defines partials" + class WorkbookWorksheetTests < WorkbookTests + desc "with worksheets" before do - @wkbk = Workbook.new { + @wkbk = Workbook.new(@test_writer) { + worksheet { + name "one" + } + worksheet { + name "two" + meta :some_meta + } + } + end + + should "return the worksheet obj created" do + assert_kind_of Worksheet, subject.worksheet("test") + end + + should "add them to it's worksheets reader" do + assert_equal 2, subject.worksheets.size + assert_equal "one", subject.worksheets.first.name + assert_equal "two", subject.worksheets.last.name + assert_equal :some_meta, subject.worksheets.last.meta + end + + should "return the last worksheet added if called with no args" do + assert_equal subject.worksheets.last, subject.worksheet + end + + should "call the writer with the created worksheet obj" do + assert_equal subject.worksheets.last, subject.writer.worksheets.last + end + + end + + class WorkbookRowCellMetaTests < WorkbookTests + desc "with columns, meta, rows, and cells" + before do + @wkbk = Workbook.new(@test_writer) { + worksheet { + column(100) { meta(:label => 'One') } + column { meta(:label => 'Two') } + + row { + columns.each do |column| + cell(column.meta[:label]) + end + } + row(120) { + cell { + data 12234 + format :currency + } + cell(Time.now) { + format :datetime, 'mm/dd/yyyy' + } + + } + } + } + end + + should "add the columns to it's columns reader" do + assert_equal 2, subject.columns.size + assert_equal 100, subject.columns.first.width + assert_equal nil, subject.columns.last.width + + assert_equal subject.columns.last, subject.writer.columns.last + end + + should "just keep the last row in its rows reader" do + assert_equal 1, subject.rows.size + assert_equal 120, subject.rows.first.height + + assert_equal 2, subject.writer.rows.size + assert_equal subject.rows.first, subject.writer.rows.last + end + + should "access the cells of its last row" do + assert_equal 2, subject.cells.size + assert_equal 12234, subject.cells.first.data + + assert_equal 4, subject.writer.cells.size + assert_equal subject.cells.last, subject.writer.rows.last.cells.last + end + + end + + class WorkbookPartialTests < WorkbookTests + desc "that defines partials" + before do + @wkbk = Workbook.new(@test_writer) { partial(:named_styles) { |name| style(".#{name}") style(".#{name}.awesome") @@ -123,10 +253,10 @@ class WorkbookPartialTest < WorkbookTest end should "add them to it's partials" do - assert_equal 2, subject.partials.keys.size - assert subject.partials.has_key?('named_styles') - assert subject.partials.has_key?('stuff') - assert_kind_of Partial, subject.partials.get('stuff') + assert_equal 2, subject.workbook.partials.keys.size + assert subject.workbook.partials.has_key?('named_styles') + assert subject.workbook.partials.has_key?('stuff') + assert_kind_of Partial, subject.workbook.partials.get('stuff') end should "add it's partials to it's markup" do @@ -138,10 +268,10 @@ class WorkbookPartialTest < WorkbookTest end - class WorkbookTemplateTest < WorkbookTest - desc "A workbook that defines templates" + class WorkbookTemplateTests < WorkbookTests + desc "that defines templates" before do - @wkbk = Workbook.new { + @wkbk = Workbook.new(@test_writer) { template(:column, :yo) { |color| width 200 meta(:color => color) @@ -158,7 +288,7 @@ class WorkbookTemplateTest < WorkbookTest should "add them to it's templates" do assert subject.templates - assert_kind_of TemplateSet, subject.templates + assert_kind_of WorkbookElement::TemplateSet, subject.templates assert_equal 3, subject.templates.keys.size assert_kind_of Template, subject.templates.get('column', 'yo') assert_kind_of Template, subject.templates.get('row', 'yo_yo') @@ -169,32 +299,18 @@ class WorkbookTemplateTest < WorkbookTest subject.worksheet(:go) assert_equal 1, subject.worksheets.size assert_equal 'blue', subject.worksheets.first.columns.first.meta[:color] - assert_equal 500, subject.worksheets.first.rows.first.attributes[:height] + assert_equal 500, subject.worksheets.first.rows.first.height end end - class WorkbookBindingTest < WorkbookTest - desc "a workbook defined w/ a block" - should "access instance vars from that block's binding" do - @test = 'test' - @workbook = Workbook.new { title @test } - - assert !@workbook.send(:instance_variable_get, "@test").nil? - assert_equal @test, @workbook.send(:instance_variable_get, "@test") - assert_equal @test.object_id, @workbook.send(:instance_variable_get, "@test").object_id - assert_equal @test, @workbook.attributes[:title] - assert_equal @test.object_id, @workbook.attributes[:title].object_id - end - - end - - class WorkbookMixins < WorkbookTest - desc "a workbook w/ mixins" + class WorkbookMixinTests < WorkbookTests + desc "with mixins" before do - @wkbk = Workbook.new { + @wkbk = Workbook.new(@test_writer) { use StyledMixin use TemplatedMixin + use PartialedMixin } end @@ -204,39 +320,32 @@ class WorkbookMixins < WorkbookTest assert_equal '.test', subject.styles.first.selectors.first assert_equal 1, subject.styles.last.selectors.size assert_equal '.test.awesome', subject.styles.last.selectors.first + assert_equal [:left], subject.styles.last.align end should "add the mixin templates to it's templates" do - assert subject.templates - assert_kind_of TemplateSet, subject.templates - assert_equal 3, subject.templates.keys.size - assert_kind_of Template, subject.templates.get('column', 'yo') - assert_kind_of Template, subject.templates.get('row', 'yo_yo') - assert_kind_of Template, subject.templates.get('worksheet', 'go') - subject.worksheet(:go) + assert_equal 1, subject.worksheets.size assert_equal 'blue', subject.worksheets.first.columns.first.meta[:color] - assert_equal 500, subject.worksheets.first.rows.first.attributes[:height] + assert_equal 500, subject.worksheets.first.rows.first.height end - end - - class WorkbookWriter < WorkbookTest - desc "a workbook" - before do - @wkbk = Workbook.new { - style('.test') - style('.test.awesome') + should "add the mixin partials to it's partials" do + subject.worksheet { + subject.add(:three_empty_rows) + subject.add(:two_cells_in_a_row, "one", "two") } - end - should have_instance_method :writer, :to_data, :to_file + assert_equal 4, subject.writer.rows.size + + assert_empty subject.writer.rows[0].cells + assert_empty subject.writer.rows[1].cells + assert_empty subject.writer.rows[2].cells - should "provide a writer for itself" do - writer = subject.writer - assert writer - assert_kind_of XmlssWriter::Base, writer + assert_equal 2, subject.writer.rows.last.cells.size + assert_equal "one", subject.writer.rows.last.cells.first.data + assert_equal "two", subject.writer.rows.last.cells.last.data end end diff --git a/test/worksheet_test.rb b/test/worksheet_test.rb index 82c55b0..9d2cdce 100644 --- a/test/worksheet_test.rb +++ b/test/worksheet_test.rb @@ -1,134 +1,87 @@ require "assert" + +require 'osheet/column' +require 'osheet/row' require 'osheet/worksheet' module Osheet - class WorksheetTest < Assert::Context - desc "Osheet::Worksheet" + class WorksheetTests < Assert::Context + desc "a Worksheet" before { @wksht = Worksheet.new } subject { @wksht } - should_be_a_workbook_element(Worksheet) + should be_a_meta_element - should have_instance_methods :name, :attributes, :meta + should have_instance_methods :name + should have_instance_methods :columns, :column + should have_instance_methods :rows, :row should "set it's defaults" do - assert_equal nil, subject.send(:get_ivar, "name") - assert_equal [], subject.columns - assert_equal [], subject.rows - - assert_equal nil, subject.meta - end - - should_hm(Worksheet, :columns, Column) - should_hm(Worksheet, :rows, Row) - - should "know it's attribute(s)" do - subject.send(:name, "Poo!") - [:name].each do |a| - assert subject.attributes.has_key?(a) - end - assert_equal "Poo!", subject.attributes[:name] + assert_equal nil, subject.name + assert_equal [], subject.columns + assert_equal [], subject.rows end end - class WorksheetNameMetaTest < WorksheetTest - desc "A named worksheet with meta" - before do - @wksht = Worksheet.new { - name "Poo!" - meta({}) - } - end - - should "know it's name and meta" do - assert_equal "Poo!", subject.send(:get_ivar, "name") - assert_equal({}, subject.meta) - end - - should "set it's name" do - subject.name(false) - assert_equal 'false', subject.name - subject.name('la') - assert_equal 'la', subject.name - subject.name(nil) - assert_equal 'la', subject.name - end + class WorksheetColumnTests < WorksheetTests + desc "with columns" + before { + @col = Column.new + @wksht.column(@col) + } - should "complain if name is longer than 31 chars" do - assert_raises ArgumentError do - subject.name('a'*32) - end - assert_nothing_raised do - subject.name('a'*31) - end + should "know its cols" do + assert_equal 1, subject.columns.size + assert_same @col, subject.columns.first end end - class WorksheetColumnRowTest < WorksheetTest - desc "A worksheet that has columns and rows" - before do - @wksht = Worksheet.new { - column - row { cell { - format :number - data 1 - } } - } - end + class WorksheetRowTests < WorksheetTests + desc "with rows" + before { + @row = Row.new + @wksht.row(@row) + } - should "set it's columns" do - columns = subject.columns - assert_equal 1, columns.size - assert_kind_of Column, columns.first - assert_equal subject.columns, columns.first.columns + should "know its rows" do + assert_equal 1, subject.rows.size + assert_same @row, subject.rows.first end - should "set it's rows" do - rows = subject.rows - assert_equal 1, rows.size - assert_kind_of Row, rows.first - assert_equal subject.columns, rows.first.columns - assert_equal subject.columns, rows.first.cells.first.columns + should "only keep the latest row" do + new_row = Row.new(120) + subject.row(new_row) + + assert_equal 1, subject.rows.size + assert_same new_row, subject.rows.last end end - class WorksheetWorkbookPartialTest < WorksheetTest - desc "A workbook that defines worksheet partials" + class WorksheetNameTests < WorksheetTests + desc "with a name" before do - @wksht = Workbook.new { - partial(:worksheet_stuff) { - row {} - row {} - } - - worksheet { - add :worksheet_stuff - } - } + @wksht = Worksheet.new("fun") end - should "add it's partials to it's markup" do - assert_equal 2, subject.worksheets.first.rows.size + should "know it's name" do + assert_equal "fun", subject.name end - end - - class WorksheetBindingTest < WorksheetTest - desc "a worksheet defined w/ a block" - - should "access instance vars from that block's binding" do - @test = 'test' - @worksheet = Worksheet.new { name @test } + should "set it's name" do + subject.name(false) + assert_equal 'false', subject.name + subject.name('la') + assert_equal 'la', subject.name + subject.name(nil) + assert_equal 'la', subject.name + end - assert !@worksheet.send(:instance_variable_get, "@test").nil? - assert_equal @test, @worksheet.send(:instance_variable_get, "@test") - assert_equal @test.object_id, @worksheet.send(:instance_variable_get, "@test").object_id - assert_equal @test, @worksheet.attributes[:name] - assert_equal @test.object_id, @worksheet.attributes[:name].object_id + should "set it's name with an init parameter" do + assert_equal "more fun", Worksheet.new("more fun").name end end diff --git a/test/xmlss_writer/api_test.rb b/test/xmlss_writer/api_test.rb new file mode 100644 index 0000000..8c0bb49 --- /dev/null +++ b/test/xmlss_writer/api_test.rb @@ -0,0 +1,139 @@ +require "assert" + +require 'osheet/xmlss_writer' + +module Osheet + + class XmlssWriter::ApiTest < Assert::Context + before do + @writer = XmlssWriter.new + @workbook = Workbook.new(@writer) + end + subject { @writer } + + end + + class XmlssWriter::CellTests < XmlssWriter::ApiTest + desc "when writing a cell" + before do + @cell = Osheet::Cell.new(100) + @cell.style_class "awesome thing" + @cell.format :number + @cell.href 'http://example.com' + @cell.rowspan 2 + @cell.colspan 5 + @cell.index 3 + @cell.formula "=R1C1" + @xmlss_cell = subject.cell(@cell) + end + + should "create an Xmlss::Cell with correct attributes" do + assert_kind_of ::Xmlss::Element::Cell, @xmlss_cell + assert_equal 100, @xmlss_cell.data + assert_equal 'http://example.com', @xmlss_cell.href + assert_equal 3, @xmlss_cell.index + assert_equal "=R1C1", @xmlss_cell.formula + assert_equal 1, @xmlss_cell.merge_down + assert_equal 4, @xmlss_cell.merge_across + end + + should "style the cell" do + assert_equal ".awesome.thing..number_none_0_nocomma_black", @xmlss_cell.style_id + assert_equal 1, subject.style_cache.size + end + + should "write cell element markup" do + assert_equal( + "100", + xmlss_element_markup(subject) + ) + end + + end + + class XmlssWriter::RowTests < XmlssWriter::ApiTest + desc "when writing a row" + before do + @row = Osheet::Row.new + @row.style_class "awesome thing" + @row.height 100 + @row.autofit true + @row.hidden true + @xmlss_row = subject.row(@row) + end + + should "create an Xmlss::Row with correct attributes" do + assert_kind_of ::Xmlss::Element::Row, @xmlss_row + assert_equal 100, @xmlss_row.height + assert_equal true, @xmlss_row.auto_fit_height + assert_equal true, @xmlss_row.hidden + end + + should "style the row" do + assert_equal ".awesome.thing", @xmlss_row.style_id + assert_equal 1, subject.style_cache.size + end + + should "write row element markup" do + assert_equal( + "", + xmlss_element_markup(subject) + ) + end + + end + + class XmlssWriter::ColumnTests < XmlssWriter::ApiTest + desc "when writing a column" + before do + @column = Osheet::Column.new + @column.style_class "awesome" + @column.width 100 + @column.autofit true + @column.hidden true + @xmlss_column = subject.column(@column) + end + + should "create an Xmlss::Column with correct attributes" do + assert_kind_of ::Xmlss::Element::Column, @xmlss_column + assert_equal 100, @xmlss_column.width + assert_equal true, @xmlss_column.auto_fit_width + assert_equal true, @xmlss_column.hidden + end + + should "style an Xmlss column" do + assert_equal ".awesome", @xmlss_column.style_id + assert_equal 1, subject.style_cache.size + end + + should "write column element markup" do + assert_equal( + "", + xmlss_element_markup(subject) + ) + end + + end + + class XmlssWriter::WorksheetTests < XmlssWriter::ApiTest + desc "when writing a worksheet" + before do + @worksheet = Worksheet.new("testsheet2") + @xmlss_worksheet = subject.worksheet(@worksheet) + end + + should "create an Xmlss::Worksheet with correct attributes" do + assert_kind_of ::Xmlss::Element::Worksheet, @xmlss_worksheet + assert_equal "testsheet2", @xmlss_worksheet.name + end + + should "write worksheet element markup" do + assert_equal( + "
", + xmlss_element_markup(subject) + ) + end + + end + +end diff --git a/test/xmlss_writer/base_test.rb b/test/xmlss_writer/base_test.rb deleted file mode 100644 index 19dce1f..0000000 --- a/test/xmlss_writer/base_test.rb +++ /dev/null @@ -1,103 +0,0 @@ -require "assert" -require 'osheet/xmlss_writer' - -module Osheet - - class XmlssWriter::BaseTest < Assert::Context - desc "XmlssWriter::Base" - before { @writer = XmlssWriter::Base.new } - subject { @writer } - - should have_readers :used_xstyles - should have_writer :oworkbook - should have_instance_methods :xworkbook - - end - - class XmlssWriter::WorkbookTests < XmlssWriter::BaseTest - before do - @workbook = Workbook.new { - title "xmlss" - worksheet { name "testsheet1" } - } - end - - should "only allow writing an Osheet::Workbook" do - assert_nothing_raised do - subject.oworkbook = @workbook - end - assert_raises ArgumentError do - subject.oworkbook = "poo" - end - end - - should "not allow writing a workbook that has multiple worksheets with the same name" do - assert_raises ArgumentError do - subject.workbook = Workbook.new { - title "invalid" - worksheet { name "testsheet1" } - worksheet { name "testsheet1" } - } - end - end - - should "create an Xmlss workbook" do - subject.oworkbook = @workbook - assert_kind_of ::Xmlss::Workbook, subject.xworkbook - - assert_equal( - "
", - subject.xworkbook.to_s - ) - end - - end - - class XmlssWriter::ToFileTests < Assert::Context - desc "XmlssWriter::Base" - before do - @writer = XmlssWriter::Base.new({ - :workbook => Workbook.new { - title "written" - worksheet { - name "Poo!" - column - row { - cell { - data 1 - format :number - } - } - } - } - }) - end - after do - # remove any test files this creates - end - subject { @writer } - - should have_instance_methods :to_data, :to_file - - should "return string xml data" do - xml_data = nil - assert_nothing_raised do - xml_data = subject.to_data - end - assert_kind_of ::String, xml_data - assert_match /^<\?xml/, xml_data - end - - should "write xml data to a file path" do - path = nil - assert_nothing_raised do - path = subject.to_file("./tmp/base_test.xls") - end - assert_kind_of ::String, path - assert_equal './tmp/base_test.xls', path - assert File.exists?(path) - end - - end - -end diff --git a/test/xmlss_writer/elements_test.rb b/test/xmlss_writer/elements_test.rb deleted file mode 100644 index d79f3eb..0000000 --- a/test/xmlss_writer/elements_test.rb +++ /dev/null @@ -1,172 +0,0 @@ -require "assert" - -require 'osheet/xmlss_writer' - -module Osheet - - class XmlssWriter::ElementsTest < Assert::Context - before do - @writer = XmlssWriter::Base.new - @xworkbook = ::Xmlss::Workbook.new - end - subject { @writer } - - end - - class XmlssWriter::CellTests < XmlssWriter::ElementsTest - desc "when writing a cell" - before do - @cell = Osheet::Cell.new do - style_class "awesome thing" - data 100 - format :number - href 'http://example.com' - rowspan 2 - colspan 5 - index 3 - formula "=R1C1" - end - @xmlss_cell = subject.send(:cell, @xworkbook, @cell) - end - - should "create an Xmlss cell with appropriate attributes" do - assert_kind_of ::Xmlss::Element::Cell, @xmlss_cell - assert_equal 'http://example.com', @xmlss_cell.href - assert_equal 3, @xmlss_cell.index - assert_equal "=R1C1", @xmlss_cell.formula - assert_equal 1, @xmlss_cell.merge_down - assert_equal 4, @xmlss_cell.merge_across - end - - should "style an Xmlss cell" do - assert_equal ".awesome.thing..number_none_0_nocomma_black", @xmlss_cell.style_id - assert_equal 1, subject.used_xstyles.size - assert_kind_of ::Xmlss::Style::Base, subject.used_xstyles.first - assert_equal @xmlss_cell.style_id, subject.used_xstyles.first.id - end - - should "write row element markup" do - assert_equal( - "100", - xelement_markup(@xworkbook) - ) - end - - end - - class XmlssWriter::RowTests < XmlssWriter::ElementsTest - desc "when writing a row" - before do - @row = Osheet::Row.new do - style_class "awesome thing" - height 100 - autofit true - hidden true - cell { - data 'one hundred' - } - end - @xmlss_row = subject.send(:row, @xworkbook, @row) - end - - should "create an Xmlss row" do - assert_kind_of ::Xmlss::Element::Row, @xmlss_row - assert_equal @row.attributes[:height], @xmlss_row.height - assert_equal @row.attributes[:autofit], @xmlss_row.auto_fit_height - assert_equal @row.attributes[:hidden], @xmlss_row.hidden - end - - should "style an Xmlss row" do - assert_equal ".awesome.thing", @xmlss_row.style_id - assert_equal 1, subject.used_xstyles.size - assert_kind_of ::Xmlss::Style::Base, subject.used_xstyles.first - assert_equal @xmlss_row.style_id, subject.used_xstyles.first.id - end - - should "write row element markup" do - assert_equal( - "one hundred", - xelement_markup(@xworkbook) - ) - end - - end - - class XmlssWriter::ColumnTests < XmlssWriter::ElementsTest - desc "when writing a column" - before do - @column = Osheet::Column.new { - style_class "awesome" - width 100 - autofit true - hidden true - meta({ - :color => 'blue' - }) - } - @xmlss_column = subject.send(:column, @xworkbook, @column) - end - - should "create an Xmlss column" do - assert_kind_of ::Xmlss::Element::Column, @xmlss_column - assert_equal @column.attributes[:width], @xmlss_column.width - assert_equal @column.attributes[:autofit], @xmlss_column.auto_fit_width - assert_equal @column.attributes[:hidden], @xmlss_column.hidden - end - - should "style an Xmlss column" do - assert_equal ".awesome", @xmlss_column.style_id - assert_equal 1, subject.used_xstyles.size - assert_kind_of ::Xmlss::Style::Base, subject.used_xstyles.first - assert_equal @xmlss_column.style_id, subject.used_xstyles.first.id - end - - should "write column element markup" do - assert_equal( - "", - xelement_markup(@xworkbook) - ) - end - - end - - class XmlssWriter::WorksheetTests < XmlssWriter::ElementsTest - desc "when writing a worksheet" - before do - @worksheet = Worksheet.new do - name "testsheet2" - column { width 100 } - row { height 50 } - end - end - - should "create an Xmlss worksheet" do - xmlss_worksheet = subject.send(:worksheet, @xworkbook, @worksheet) - assert_kind_of ::Xmlss::Element::Worksheet, xmlss_worksheet - end - - should "filter invalid worksheet names" do - { 'valid name' => 'valid name', - 'valid 2' => 'valid 2', - 'invalid :' => 'invalid ', - 'invalid ;' => 'invalid ', - 'invalid *' => 'invalid ', - 'invalid /' => 'invalid ', - 'invalid \\' => 'invalid ', - '[invalid]' => "invalid]" - }.each do |k,v| - assert_equal v, subject.send(:worksheet, @xworkbook, Worksheet.new { name k }).name - end - end - - should "write worksheet element markup" do - subject.send(:worksheet, @xworkbook, @worksheet) - assert_equal( - "
", - xelement_markup(@xworkbook) - ) - end - - end - -end diff --git a/test/xmlss_writer/style_cache_test.rb b/test/xmlss_writer/style_cache_test.rb new file mode 100644 index 0000000..faa0506 --- /dev/null +++ b/test/xmlss_writer/style_cache_test.rb @@ -0,0 +1,65 @@ +require "assert" + +require 'osheet/xmlss_writer/style_cache' + +module Osheet + + class XmlssWriterStyleCacheTests < Assert::Context + desc "the style cache" + before do + @workbook = Workbook.new + @workbook.style('.align.center') { @workbook.align :center } + @workbook.style('.font.size') { @workbook.font 14 } + @workbook.style('.font.weight') { @workbook.font :bold } + @workbook.style('.font.style') { @workbook.font :italic } + @workbook.style('.bg.color') { @workbook.bg '#FF0000' } + @workbook.style('.border.color') { @workbook.border '#FF0000', :thin } + @xmlss_workbook = Xmlss::Workbook.new(Xmlss::Writer.new) + + @cache = XmlssWriter::StyleCache.new(@workbook, @xmlss_workbook) + end + subject { @cache } + + should have_reader :styles + should have_instance_method :get, :keys, :empty?, :size, :[] + + should "have no cached styles by default" do + assert_empty subject + end + + should "key based off class value and format key" do + assert_equal '', subject.send(:key, '', nil) + assert_equal '.awesome', subject.send(:key, 'awesome', nil) + assert_equal '.awesome.thing', subject.send(:key, 'awesome thing', nil) + assert_equal '.awesome..something', subject.send(:key, 'awesome', 'something') + assert_equal '..something', subject.send(:key, '', 'something') + end + + should "return nil if trying to get the style for an empty class and general format" do + assert_not_nil subject.get('font', Osheet::Format.new(:general)) + assert_nil subject.get('', Osheet::Format.new(:general)) + end + + should "build and cache styles if not already cached" do + assert_equal 0, subject.size + subject.get('font', Osheet::Format.new(:currency)) + assert_equal 1, subject.size + end + + should "return cached style if requesting an already cached style" do + assert_equal 0, subject.size + subject.get('font', Osheet::Format.new(:currency)) + subject.get('font', Osheet::Format.new(:currency)) + assert_equal 1, subject.size + end + + should "build styles with ids matching the cache key" do + key = subject.send(:key, 'font', Osheet::Format.new(:currency).key) + assert_equal key, subject.get('font', Osheet::Format.new(:currency)).id + end + + end + + + +end diff --git a/test/xmlss_writer/style_settings_test.rb b/test/xmlss_writer/style_settings_test.rb new file mode 100644 index 0000000..fdeb504 --- /dev/null +++ b/test/xmlss_writer/style_settings_test.rb @@ -0,0 +1,263 @@ +require "assert" + +require 'osheet/xmlss_writer/style_settings' + +module Osheet + + class XmlssWriterStyleSettingsTests < Assert::Context + desc "the style writer when building" + before do + @settings = XmlssWriter::StyleSettings.new([]) + @workbook = Workbook.new + @workbook.style('.align.center') { @workbook.align :center } + @workbook.style('.font.size') { @workbook.font 14 } + @workbook.style('.font.weight') { @workbook.font :bold } + @workbook.style('.font.style') { @workbook.font :italic } + @workbook.style('.bg.color') { @workbook.bg '#FF0000' } + @workbook.style('.border.color') { @workbook.border '#FF0000', :thin } + end + subject { @settings } + + should have_reader :styles, :value + should have_instance_method :setting, :[] + + should "be empty if no styles given" do + assert_equal({}, subject.value) + end + + should "conditionally run a block if the setting exists and is not empty" do + settings = XmlssWriter::StyleSettings.new([@workbook.styles.first]) + @something = '' + + settings.setting(:font) { @something = 'i should be empty' } + assert_empty @something + + settings.setting(:align) { @something = 'i should not be empty' } + assert_not_empty @something + end + + end + + + + class XmlssWriterAlignmentSettingsTests < XmlssWriterStyleSettingsTests + desc "align settings" + + should "translate horizontal directives" do + assert_equal({:horizontal => :left}, subject.send(:align_settings, [:left])) + assert_equal({:horizontal => :center}, subject.send(:align_settings, [:center])) + assert_equal({:horizontal => :right}, subject.send(:align_settings, [:right])) + end + + should "translate vertical directives" do + assert_equal({:vertical => :top}, subject.send(:align_settings, [:top])) + assert_equal({:vertical => :center}, subject.send(:align_settings, [:middle])) + assert_equal({:vertical => :bottom}, subject.send(:align_settings, [:bottom])) + end + + should "translate wrap text and rotate directives" do + assert_equal({:wrap_text => true}, subject.send(:align_settings, [:wrap])) + assert_equal({:rotate => 90}, subject.send(:align_settings, [90])) + end + + end + + + + class XmlssWriterFontSettingsTests < XmlssWriterStyleSettingsTests + desc "font settings" + + should "translate size, color and name directives" do + assert_equal({:size => 12}, subject.send(:font_settings, [12])) + assert_equal({:color => '#FFFFFF'}, subject.send(:font_settings, ['#FFFFFF'])) + assert_equal({:name => 'Arial'}, subject.send(:font_settings, ['Arial'])) + end + + should "translate style directives" do + assert_equal({:bold => true}, subject.send(:font_settings, [:bold])) + assert_equal({:italic => true}, subject.send(:font_settings, [:italic])) + assert_equal({:shadow => true}, subject.send(:font_settings, [:shadow])) + assert_equal({:alignment => :subscript}, subject.send(:font_settings, [:subscript])) + assert_equal({:alignment => :superscript}, subject.send(:font_settings, [:superscript])) + assert_equal({:strike_through => true}, subject.send(:font_settings, [:strikethrough])) + end + + should "translate underline directives" do + assert_equal({:underline => :single}, subject.send(:font_settings, [:underline])) + assert_equal({:underline => :double}, subject.send(:font_settings, [:double_underline])) + assert_equal({:underline => :single_accounting}, subject.send(:font_settings, [:accounting_underline])) + assert_equal({:underline => :double_accounting}, subject.send(:font_settings, [:double_accounting_underline])) + end + + end + + + + + class XmlssWriterBgSettingsTests < XmlssWriterStyleSettingsTests + desc "bg settings" + + should "translate color directives" do + assert_equal({:color => '#FFFFFF', :pattern => :solid}, subject.send(:bg_settings, ['#FFFFFF'])) + end + + should "translate pattern directives" do + assert_equal({:pattern => :solid}, subject.send(:bg_settings, [:solid])) + assert_equal({ + :pattern => :horz_stripe, + :pattern_color => '#0000FF' + }, subject.send(:bg_settings, [{:horz_stripe => '#0000FF'}])) + end + + end + + + + + + class XmlssWriterBorderSettingsTests < XmlssWriterStyleSettingsTests + desc "border settings" + + should "translate color directives" do + assert_equal({:color => '#FFFFFF'}, subject.send(:border_settings, ['#FFFFFF'])) + end + + should "translate position directives" do + assert_equal({:position => :top}, subject.send(:border_settings, [:top])) + end + + should "translate weight directives" do + assert_equal({:weight => :thick}, subject.send(:border_settings, [:thick])) + end + + should "translate line_style directives" do + assert_equal({:line_style => :dot}, subject.send(:border_settings, [:dot])) + end + + should "translate position specific directives" do + assert_equal({ + :position => :left, + :color => '#FFFFFF', + :weight => :thin + }, subject.send(:border_left_settings, ['#FFFFFF', :thin])) + end + + end + + + + class XmlssWriterParseSettingsTests < XmlssWriterStyleSettingsTests + desc "settings" + + should "parse Osheet style objs for their settings" do + assert_equal({ + :align => { + :horizontal => :center + } + }, subject.send(:style_settings, @workbook.styles.first)) + + assert_equal({ + :border_top => { + :position=>:top, + :color=>"#FF0000", + :weight=>:thin + }, + :border_right => { + :position=>:right, + :color=>"#FF0000", + :weight=>:thin + }, + :border_bottom => { + :position=>:bottom, + :color=>"#FF0000", + :weight=>:thin + }, + :border_left => { + :position=>:left, + :color=>"#FF0000", + :weight=>:thin + } + }, subject.send(:style_settings, @workbook.styles.last)) + end + + should "merge style settings from multiple Osheet style objs" do + set1 = { + :font => { + :color => '#FF0000', + :name => "Arial" + } + } + set2 = { + :font => { + :color => '#0000FF', + :size => 12 + } + } + set3 = { + :align => { + :horizontal => :center + } + } + + # should concat keys in both sets + assert_equal({ + :font => { + :color => '#0000FF', + :name => "Arial", + :size => 12 + } + }, subject.send(:merged_settings, set1.dup, set2.dup)) + + # should add keys not in first set + assert_equal({ + :font => { + :color => '#0000FF', + :size => 12 + }, + :align => { + :horizontal => :center + } + }, subject.send(:merged_settings, set2.dup, set3.dup)) + + end + + should "set its value given its set of styles" do + assert_equal({ + :align => { + :horizontal => :center + }, + :font => { + :bold => true, + :italic => true, + :size => 14 + }, + :bg => { + :pattern => :solid, + :color => "#FF0000" + }, + :border_top => { + :position => :top, + :color => "#FF0000", + :weight => :thin + }, + :border_right => { + :position => :right, + :color => "#FF0000", + :weight => :thin + }, + :border_bottom => { + :position => :bottom, + :color => "#FF0000", + :weight => :thin + }, + :border_left => { + :position => :left, + :color => "#FF0000", + :weight => :thin + } + }, XmlssWriter::StyleSettings.new(@workbook.styles).value) + end + end + +end + diff --git a/test/xmlss_writer/styles_test.rb b/test/xmlss_writer/styles_test.rb index b9bdda9..54b434f 100644 --- a/test/xmlss_writer/styles_test.rb +++ b/test/xmlss_writer/styles_test.rb @@ -4,360 +4,328 @@ module Osheet - class XmlssWriter::StylesTest < Assert::Context + class XmlssWriterStylesTest < Assert::Context before do - @writer = XmlssWriter::Base.new - @xworkbook = ::Xmlss::Workbook.new + @writer = XmlssWriter.new + @workbook = Workbook.new(@writer) end subject { @writer } + should "not have a style cache until its bound to an osheet workbook" do + writer = XmlssWriter.new + assert_nil writer.style_cache + writer.bind(@workbook) + assert_not_nil writer.style_cache + end + end - class XmlssWriter::StyleTests < XmlssWriter::StylesTest + class XmlssWriterStyleTests < XmlssWriterStylesTest desc "Xmlss style writer" before do - subject.oworkbook = Workbook.new { - style('.font.size') { font 14 } - style('.font.weight') { font :bold } - style('.font.style') { font :italic } - style('.align.center') { align :center } - } - end - - should "key styles based off class str and format" do - assert_equal '', subject.send(:style_key, '', nil) - assert_equal '.awesome', subject.send(:style_key, 'awesome', nil) - assert_equal '.awesome.thing', subject.send(:style_key, 'awesome thing', nil) - assert_equal '.awesome..something', subject.send(:style_key, 'awesome', 'something') - assert_equal '..something', subject.send(:style_key, '', 'something') - end - - should "not build a style obj when writing styles with no class str or format" do - assert_equal nil, subject.send(:style, @xworkbook, '') + @workbook.style('.font.size') { @workbook.font 14 } + @workbook.style('.font.weight') { @workbook.font :bold } + @workbook.style('.font.style') { @workbook.font :italic } + @workbook.style('.align.center') { @workbook.align :center } end should "build a style obj and add it to the writers styles" do - xmlss_style = subject.send(:style, @xworkbook, 'awesome') + xmlss_style = subject.style('awesome') assert_kind_of ::Xmlss::Style::Base, xmlss_style assert_equal '.awesome', xmlss_style.id - assert_equal 1, subject.used_xstyles.size - assert_equal xmlss_style, subject.used_xstyles.first + assert_equal 1, subject.style_cache.size + assert_equal xmlss_style, subject.style_cache[xmlss_style.id] end should "write style markup from many matching osheet styles" do - xmlss_style = subject.send(:style, @xworkbook, 'font size weight style align center') + xmlss_style = subject.style('font size weight style align center') assert_equal '.font.size.weight.style.align.center', xmlss_style.id - - assert_equal( - "", - xstyle_markup(@xworkbook) - ) - end - - should "provide style ids" do - assert_equal '', subject.send(:style_id, @xworkbook, '') - assert_equal '.awesome', subject.send(:style_id, @xworkbook, 'awesome') - assert_equal '..number_none_0_nocomma_black', subject.send(:style_id, @xworkbook, '', Osheet::Format.new(:number)) - assert_equal 2, subject.used_xstyles.size end end - class XmlssWriter::AlignmentTests < XmlssWriter::StylesTest + class XmlssWriterAlignmentTests < XmlssWriterStylesTest desc "Alignment style writer" before do - subject.oworkbook = Workbook.new { - [ :left, :center, :right, - :top, :middle, :bottom, - :wrap - ].each do |s| - style(".align.#{s}") { align s } - end - style('.align.rotate') { align 90 } - } + [ :left, :center, :right, + :top, :middle, :bottom, + :wrap + ].each do |s| + @workbook.style(".align.#{s}") { @workbook.align s } + end + @workbook.style('.align.rotate') { @workbook.align 90 } end should "write style markup with no alignment settings if no alignment style match" do - subject.send(:style, @xworkbook, 'align') + subject.style('align') assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end should "write style markup for horizontal alignment settings" do - subject.send(:style, @xworkbook, 'align left') - subject.send(:style, @xworkbook, 'align center') - subject.send(:style, @xworkbook, 'align right') + subject.style('align left') + subject.style('align center') + subject.style('align right') assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end should "write style markup for vertical alignment settings" do - subject.send(:style, @xworkbook, 'align top') - subject.send(:style, @xworkbook, 'align middle') - subject.send(:style, @xworkbook, 'align bottom') + subject.style('align top') + subject.style('align middle') + subject.style('align bottom') assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end should "write style markup for text wrap settings" do - subject.send(:style, @xworkbook, 'align wrap') + subject.style('align wrap') assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end should "write style markup for text rotation settings" do - subject.send(:style, @xworkbook, 'align rotate') + subject.style('align rotate') assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end end - class XmlssWriter::FontTests < XmlssWriter::StylesTest + class XmlssWriterFontTests < XmlssWriterStylesTest desc "Font style writer" before do - subject.oworkbook = Workbook.new { - [ :underline, :double_underline, :accounting_underline, :double_accounting_underline, - :subscript, :superscript, :shadow, :strikethrough, :wrap, - :bold, :italic - ].each do |s| - style(".font.#{s}") { font s } - end - style('.font.size') { font 14 } - style('.font.color') { font '#FF0000' } - style('.font.name') { font 'Verdana' } - } + [ :underline, :double_underline, :accounting_underline, :double_accounting_underline, + :subscript, :superscript, :shadow, :strikethrough, :wrap, + :bold, :italic + ].each do |s| + @workbook.style(".font.#{s}") { @workbook.font s } + end + @workbook.style('.font.size') { @workbook.font 14 } + @workbook.style('.font.color') { @workbook.font '#FF0000' } + @workbook.style('.font.name') { @workbook.font 'Verdana' } end should "write style markup with empty font settings if no match" do - subject.send(:style, @xworkbook, 'font') + subject.style('font') assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end should "write style markup for font underline settings" do - subject.send(:style, @xworkbook, 'font underline') - subject.send(:style, @xworkbook, 'font double_underline') - subject.send(:style, @xworkbook, 'font accounting_underline') - subject.send(:style, @xworkbook, 'font double_accounting_underline') + subject.style('font underline') + subject.style('font double_underline') + subject.style('font accounting_underline') + subject.style('font double_accounting_underline') assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end should "write style markup for font alignment settings" do - subject.send(:style, @xworkbook, 'font subscript') - subject.send(:style, @xworkbook, 'font superscript') + subject.style('font subscript') + subject.style('font superscript') assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end should "write style markup for font style settings" do - subject.send(:style, @xworkbook, 'font bold') - subject.send(:style, @xworkbook, 'font italic') - subject.send(:style, @xworkbook, 'font strikethrough') - subject.send(:style, @xworkbook, 'font shadow') + subject.style('font bold') + subject.style('font italic') + subject.style('font strikethrough') + subject.style('font shadow') assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end should "write style markup for font size" do - subject.send(:style, @xworkbook, 'font size') + subject.style('font size') assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end should "write style markup for font color" do - subject.send(:style, @xworkbook, 'font color') + subject.style('font color') assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end should "write style markup for font name" do - subject.send(:style, @xworkbook, 'font name') + subject.style('font name') assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end end - class XmlssWriter::BgTests < XmlssWriter::StylesTest + class XmlssWriterBgTests < XmlssWriterStylesTest desc "Bg writer" before do - subject.oworkbook = Workbook.new { - style('.bg.color') { bg '#FF0000' } - style('.bg.pattern-only') { bg :solid } - style('.bg.pattern-color') { bg :horz_stripe => '#0000FF' } - style('.bg.color-first') { bg '#00FF00', {:horz_stripe => '#0000FF'} } - style('.bg.pattern-first') { bg({:horz_stripe => '#0000FF'}, '#00FF00') } - } + @workbook.style('.bg.color') { @workbook.bg '#FF0000' } + @workbook.style('.bg.pattern-only') { @workbook.bg :solid } + @workbook.style('.bg.pattern-color') { @workbook.bg :horz_stripe => '#0000FF' } + @workbook.style('.bg.color-first') { @workbook.bg '#00FF00', {:horz_stripe => '#0000FF'} } + @workbook.style('.bg.pattern-first') { @workbook.bg({:horz_stripe => '#0000FF'}, '#00FF00') } end should "write style markup with empty bg settings when no match" do - subject.send(:style, @xworkbook, 'bg') - # assert_equal nil, .interior + subject.style('bg') assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end should "write style markup for bg color and auto set the pattern to solid" do - subject.send(:style, @xworkbook, 'bg color') - # assert_equal '#FF0000', .interior.color + subject.style('bg color') assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end should "write style markup for bg pattern settings" do - subject.send(:style, @xworkbook, 'bg pattern-only') - subject.send(:style, @xworkbook, 'bg pattern-only') - subject.send(:style, @xworkbook, 'bg pattern-color') - subject.send(:style, @xworkbook, 'bg pattern-color') + subject.style('bg pattern-only') + subject.style('bg pattern-only') + subject.style('bg pattern-color') + subject.style('bg pattern-color') assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end should "write style markup setting pattern to solid when setting bg color" do - subject.send(:style, @xworkbook, 'bg color') + subject.style('bg color') assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end should "write style markup setting pattern to pattern setting when first setting bg color then pattern" do - subject.send(:style, @xworkbook, 'bg color-first') + subject.style('bg color-first') assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end should "write style markup setting pattern to pattern setting when first setting bg pattern then color" do - subject.send(:style, @xworkbook, 'bg pattern-first') + subject.style('bg pattern-first') assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end end - class XmlssWriter::BorderTests < XmlssWriter::StylesTest + class XmlssWriterBorderTests < XmlssWriterStylesTest desc "Font border writer" before do - subject.oworkbook = Workbook.new { - ::Osheet::Style::BORDER_POSITIONS.each do |p| - style(".border.#{p}") { send("border_#{p}", :thin) } - end - [:hairline, :thin, :medium, :thick].each do |w| - style(".border.#{w}") { border_top w } - end - [:none, :continuous, :dash, :dot, :dash_dot, :dash_dot_dot].each do |s| - style(".border.#{s}") { border_top s } - end - style('.border.color') { border_top '#FF0000' } - style('.border.all') { border :thick, :dash, '#FF0000' } - } + ::Osheet::Style::BORDER_POSITIONS.each do |p| + @workbook.style(".border.#{p}") { @workbook.send("border_#{p}", :thin) } + end + [:hairline, :thin, :medium, :thick].each do |w| + @workbook.style(".border.#{w}") { @workbook.border_top w } + end + [:none, :continuous, :dash, :dot, :dash_dot, :dash_dot_dot].each do |s| + @workbook.style(".border.#{s}") { @workbook.border_top s } + end + @workbook.style('.border.color') { @workbook.border_top '#FF0000' } + @workbook.style('.border.all') { @workbook.border :thick, :dash, '#FF0000' } end should "write style markup with empty border settings when no match" do - subject.send(:style, @xworkbook, 'border') + subject.style('border') assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end should "write style markup with identical settings for all positions when using 'border'" do - subject.send(:style, @xworkbook, 'border all') + subject.style('border all') assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end should "write style markup for border specific positions" do ::Osheet::Style::BORDER_POSITIONS.each do |p| - subject.send(:style, @xworkbook, "border #{p}") + subject.style("border #{p}") end assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end should "write style markup for border weight settings" do [:hairline, :thin, :medium, :thick].each do |w| - subject.send(:style, @xworkbook, "border #{w}") + subject.style("border #{w}") end assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end should "write style markup for border style settings" do [:none, :continuous, :dash, :dot, :dash_dot, :dash_dot_dot].each do |s| - subject.send(:style, @xworkbook, "border #{s}") + subject.style("border #{s}") end assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end should "write style markup for border color" do - subject.send(:style, @xworkbook, 'border color') + subject.style('border color') assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end end - class XmlssWriter::NumberFormatTests < XmlssWriter::StylesTest + class XmlssWriterNumberFormatTests < XmlssWriterStylesTest desc "Xmlss style number format writer" - before do - subject.oworkbook = Workbook.new {} - end should "write style markup with formatting" do - subject.send(:style, @xworkbook, '', Osheet::Format.new(:text)) - subject.send(:style, @xworkbook, '', Osheet::Format.new(:datetime, 'mm/dd/yy')) + subject.style('', Osheet::Format.new(:text)) + subject.style('', Osheet::Format.new(:datetime, 'mm/dd/yy')) assert_equal( "", - xstyle_markup(@xworkbook) + xmlss_style_markup(subject) ) end diff --git a/test/xmlss_writer_test.rb b/test/xmlss_writer_test.rb new file mode 100644 index 0000000..ad1a748 --- /dev/null +++ b/test/xmlss_writer_test.rb @@ -0,0 +1,91 @@ +require "assert" +require 'osheet/xmlss_writer' + +module Osheet + + class XmlssWriterTest < Assert::Context + desc "the xmlss writer" + before do + @writer = XmlssWriter.new + @workbook = Workbook.new(@writer) + end + subject { @writer } + + should have_reader :style_cache, :xmlss_workbook + should have_instance_methods :bind, :to_s, :to_data, :to_file + + should have_instance_methods :worksheet, :column, :row, :cell + should have_instance_methods :style + should have_instance_methods :name + should have_instance_methods :width, :height + should have_instance_methods :autofit, :autofit?, :hidden, :hidden? + should have_instance_methods :data, :format, :href, :formula + should have_instance_methods :index, :rowspan, :colspan + + end + + class XmlssWriterWorkbookTests < XmlssWriterTest + before do + @workbook.worksheet("testsheet1") + end + + should "not allow multiple worksheets with the same name" do + assert_raises ArgumentError do + subject.worksheet(Worksheet.new("testsheet1")) + end + assert_nothing_raised do + subject.worksheet(Worksheet.new("testsheet2")) + subject.worksheet(Worksheet.new) { + subject.name 'testsheet3' + } + subject.worksheet(Worksheet.new) { + subject.name 'testsheet4' + } + end + end + + end + + class XmlssWriterToFileTests < XmlssWriterTest + desc "used with a workbook" + before do + Workbook.new(@writer) { + title "written" + worksheet { + name "Test!" + column + row { + cell { + data 1 + format :number + } + } + } + } + end + + should "write workbook markup" do + assert_equal "\n1
", @writer.to_s + end + + should "return string xml data" do + xml_data = subject.to_s + assert_kind_of ::String, xml_data + assert_match /^<\?xml/, xml_data + assert_equal xml_data, subject.to_data + end + + should "write xml data to a file path" do + path = subject.to_file("./tmp/base_test.xls") + assert_kind_of ::String, path + assert_equal './tmp/base_test.xls', path + assert File.exists?(path) + + File.open(path) do |f| + assert_equal subject.to_data, f.read + end + end + + end + +end