lib/rspreadsheet/cell.rb in rspreadsheet-0.2.15 vs lib/rspreadsheet/cell.rb in rspreadsheet-0.3
- old
+ new
@@ -1,66 +1,73 @@
# @markup markdown
# @author Jakub Tesinsky
# @title rspreadsheet Cell
require 'andand'
-require 'rspreadsheet/xml_tied'
+require 'rspreadsheet/xml_tied_item'
require 'date'
+require 'time'
require 'bigdecimal'
require 'bigdecimal/util' # for to_d method
+require 'helpers/class_extensions'
module Rspreadsheet
+using ClassExtensions if RUBY_VERSION > '2.1'
###
# Represents a cell in spreadsheet which has coordinates, contains value, formula and can be formated.
# You can get this object like this (suppose that @worksheet contains {Rspreadsheet::Worksheet} object)
#
# @worksheet.cells(5,2)
#
# Note that when using syntax like `@worksheet[5,2]` or `@worksheet.B5` you won't get this object, but rather the value of the cell.
+# More precisely it is equvalient to @worksheet.cells(5,2).value.
+#
class Cell < XMLTiedItem
- attr_accessor :worksheet, :coli, :rowi
- # `xml_options[:xml_items_node_name]` gives the name of the tag representing cell
- # `xml_options[:number-columns-repeated]` gives the name of the previous tag which sais how many times the item is repeated
- def xml_options; {:xml_items_node_name => 'table-cell', :xml_repeated_attribute => 'number-columns-repeated'} end
+ attr_accessor :worksheet
+ attr_reader :rowi
+ InternalDateFormat = '%Y-%m-%d'
+ InternalTimeFormat = 'PT%HH%MM%SS'
+# InternalTimeFormat = 'PT%HH%MM%SS,%LS'
- ## defining abstract methods from XMLTiedItem
- # returns parent XMLTiedArray object of myself (XMLTiedItem)
+ # @!group XMLTiedItem related methods and extensions
+ def xml_options; {:xml_items_node_name => 'table-cell', :xml_repeated_attribute => 'number-columns-repeated'} end
def parent; row end
- def index; @coli end
- def set_index(value); @coli=value end
+ def coli; index end
+
def set_rowi(arowi); @rowi = arowi end # this should ONLY be used by parent row
def initialize(aworksheet,arowi,acoli)
raise "First parameter should be Worksheet object not #{aworksheet.class}" unless aworksheet.kind_of?(Rspreadsheet::Worksheet)
@worksheet = aworksheet
@rowi = arowi
- @coli = acoli
+ initialize_xml_tied_item(row,acoli)
end
def row; @worksheet.rows(rowi) end
def coordinates; [rowi,coli] end
def to_s; value.to_s end
def valuexml; self.valuexmlnode.andand.inner_xml end
- def valuexmlnode; self.xmlnode.children.first end
+ def valuexmlnode; self.xmlnode.elements.first end
# use this to find node in cell xml. ex. xmlfind('.//text:a') finds all link nodes
def valuexmlfindall(path)
valuexmlnode.nil? ? [] : valuexmlnode.find(path)
end
def valuexmlfindfirst(path)
valuexmlfindall(path).first
end
def inspect
- "#<Rspreadsheet::Cell:Cell\n row:#{rowi}, col:#{coli} address:#{address}\n type: #{guess_cell_type.to_s}, value:#{value}\n mode: #{mode}\n>"
+ "#<Rspreadsheet::Cell\n row:#{rowi}, col:#{coli} address:#{address}\n type: #{guess_cell_type.to_s}, value:#{value}\n mode: #{mode}, format: #{format.inspect}\n>"
end
def value
gt = guess_cell_type
if (self.mode == :regular) or (self.mode == :repeated)
case
when gt == nil then nil
when gt == Float then xmlnode.attributes['value'].to_f
when gt == String then xmlnode.elements.first.andand.content.to_s
- when gt == Date then Date.strptime(xmlnode.attributes['date-value'].to_s, '%Y-%m-%d')
+ when gt == Date then Date.strptime(xmlnode.attributes['date-value'].to_s, InternalDateFormat)
+ when gt == Time then Time.strptime(xmlnode.attributes['time-value'].to_s, InternalTimeFormat)
when gt == :percentage then xmlnode.attributes['value'].to_f
when gt == :currency then xmlnode.attributes['value'].to_d
end
elsif self.mode == :outbound
nil
@@ -84,12 +91,18 @@
set_type_attribute('string')
xmlnode << Tools.prepare_ns_node('text','p', avalue.to_s)
when gt == Date then
remove_all_value_attributes_and_content(xmlnode)
set_type_attribute('date')
- Tools.set_ns_attribute(xmlnode,'office','date-value', avalue.strftime('%Y-%m-%d'))
- xmlnode << Tools.prepare_ns_node('text','p', avalue.strftime('%Y-%m-%d'))
+ avalue = avalue.strftime(InternalDateFormat)
+ Tools.set_ns_attribute(xmlnode,'office','date-value', avalue)
+ xmlnode << Tools.prepare_ns_node('text','p', avalue)
+ when gt == Time then
+ remove_all_value_attributes_and_content(xmlnode)
+ set_type_attribute('time')
+ Tools.set_ns_attribute(xmlnode,'office','time-value', avalue.strftime(InternalTimeFormat))
+ xmlnode << Tools.prepare_ns_node('text','p', avalue.strftime('%H:%M'))
when gt == :percentage then
remove_all_value_attributes_and_content(xmlnode)
set_type_attribute('percentage')
Tools.set_ns_attribute(xmlnode,'office','value', '%0.2d%' % avalue.to_f)
xmlnode << Tools.prepare_ns_node('text','p', (avalue.to_f*100).round.to_s+'%')
@@ -103,10 +116,11 @@
Tools.set_ns_attribute(xmlnode,'calcext','value-type',typestring)
end
def remove_all_value_attributes_and_content(node=xmlnode)
if att = Tools.get_ns_attribute(node, 'office','value') then att.remove! end
if att = Tools.get_ns_attribute(node, 'office','date-value') then att.remove! end
+ if att = Tools.get_ns_attribute(node, 'office','time-value') then att.remove! end
if att = Tools.get_ns_attribute(node, 'table','formula') then att.remove! end
node.content=''
end
def remove_all_type_attributes
set_type_attribute(nil)
@@ -118,10 +132,11 @@
gct = guess_cell_type
case
when gct == Float then :float
when gct == String then :string
when gct == Date then :date
+ when gct == Time then :time
when gct == :percentage then :percentage
when gct == :unassigned then :unassigned
when gct == :currency then :currency
when gct == NilClass then :empty
when gct == nil then :unknown
@@ -130,32 +145,33 @@
end
def guess_cell_type(avalue=nil)
# try guessing by value
valueguess = case avalue
when Numeric then Float
+ when Time then Time
when Date then Date
when String,nil then nil
else nil
end
result = valueguess
- if valueguess.nil? # valueguess is most important
- # if not succesfull then try guessing by type from node xml
+ if valueguess.nil? # valueguess is most important if not succesfull then try guessing by type from node xml
typ = xmlnode.nil? ? 'N/A' : xmlnode.attributes['value-type']
typeguess = case typ
when nil then nil
when 'float' then Float
when 'string' then String
+ when 'time' then Time
when 'date' then Date
when 'percentage' then :percentage
when 'N/A' then :unassigned
when 'currency' then :currency
else
- if xmlnode.children.size == 0
+ if xmlnode.elements.size == 0
nil
else
- raise "Unknown type at #{coordinates.to_s} from #{xmlnode.to_s} / children size=#{xmlnode.children.size.to_s} / type=#{xmlnode.attributes['value-type'].to_s}"
+ raise "Unknown type at #{coordinates.to_s} from #{xmlnode.to_s} / elements size=#{xmlnode.elements.size.to_s} / type=#{xmlnode.attributes['value-type'].to_s}"
end
end
result =
if !typeguess.nil? # if not certain by value, but have a typeguess
@@ -207,10 +223,15 @@
remove_all_value_attributes_and_content(xmlnode)
remove_all_type_attributes
Tools.set_ns_attribute(xmlnode,'table','formula','of:'+formulastring.to_s)
end
def blank?; self.type==:empty or self.type==:unassigned end
+
+ def border_top; format.border_top end
+ def border_right; format.border_right end
+ def border_bottom; format.border_bottom end
+ def border_left; format.border_left end
end
# proxy object to allow cell.format syntax. Also handles all logic for formats.
# @private
@@ -247,10 +268,11 @@
end
Tools.set_ns_attribute(text_style_node,'fo',attribute_name,value)
end
def background_color=(value); set_cell_style_node_attribute('background-color', value) end
def set_cell_style_node_attribute(attribute_name,value)
+ @cell.detach if @cell.mode != :regular
if cell_style_node.nil?
self.create_cell_style_node
raise 'Style node was not correctly initialized' if cell_style_node.nil?
end
Tools.set_ns_attribute(cell_style_node,'fo',attribute_name,value)
@@ -298,12 +320,81 @@
cellnode.doc.root.find("./office:automatic-styles#{xpath}").first
end
def currency
Tools.get_ns_attribute_value(cellnode,'office','currency',nil)
end
+ #returns object representing top border of the cell
+ def top; @top ||= Border.new(self,:top) end
+ def bottom; @bottom ||= Border.new(self,:bottom) end
+ def left; @left ||= Border.new(self,:left) end
+ def right; @right ||= Border.new(self,:right) end
+ alias :border_top :top
+ alias :border_right :right
+ alias :border_bottom :bottom
+ alias :border_left :left
+
+ def inspect
+ "#<Rspreadsheet::CellFormat bold:#{bold?.inspect}, borders:#{top.get_value_string.inspect} #{right.get_value_string.inspect} #{bottom.get_value_string.inspect} #{left.get_value_string.inspect}>"
+ end
+
end
+# represents one of the borders of a cell
+class Border
+ def initialize(cellformat,side)
+ @cellformat = cellformat
+ @side = side.to_s
+ raise "Wrong side of border object, can be top, bottom, left or right" unless ['left','right','top','bottom'].include? @side
+ end
+ def cellnode; @cell.xmlnode end
+ def attribute_name; "border-#{@side}" end
+
+ def width=(value); set_border_string_part(1, value) end
+ def style=(value); set_border_string_part(2, value.to_s) end
+ def color=(value); set_border_string_part(3, value) end
+ def width; get_border_string_part(1).to_f end
+ def style; get_border_string_part(2) end
+ def color; get_border_string_part(3) end
+ def delete
+ @cellformat.set_cell_style_node_attribute(attribute_name, 'none')
+ end
+
+
+ ## internals
+
+ # set parth-th part of string which represents the border. String looks like "0.06pt solid #00ee00"
+ # part is 1 for width, 2 for style or 3 for color
+ def set_border_string_part(part,value)
+ current_value = @cellformat.get_cell_style_node_attribute(attribute_name)
+
+ if current_value.nil? or (current_value=='none')
+ value_array = ['0.75pt', 'solid', '#000000'] # set default values
+ else
+ value_array = current_value.split(' ')
+ end
+ raise 'Strange border attribute value. Does not have 3 parts' unless value_array.length == 3
+ value_array[part-1]=value
+ @cellformat.set_cell_style_node_attribute(attribute_name, value_array.join(' '))
+ end
+
+ def get_border_string_part(part)
+ current_value = @cellformat.get_cell_style_node_attribute(attribute_name) || @cellformat.get_cell_style_node_attribute('border')
+ if current_value.nil? or (current_value=='none')
+ return nil
+ else
+ value_array = current_value.split(' ')
+ raise 'Strange border attribute value. Does not have 3 parts' unless value_array.length == 3
+ return value_array[part-1]
+ end
+ end
+
+ def get_value_string
+ @cellformat.get_cell_style_node_attribute(attribute_name)
+ end
+
end
+
+end # module