lib/osheet/workbook.rb in osheet-0.10.0 vs lib/osheet/workbook.rb in osheet-1.0.0.rc.1

- old
+ new

@@ -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 + # 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 - 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) + 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 + metaclass = class << self; self; end + data.each {|key, value| metaclass.class_eval { define_method(key){value} }} + + # 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) + + # run any instance workbook build given + instance_eval(&build) if build end - def title(value=nil) - !value.nil? ? set_ivar(:title, value) : get_ivar(:title) + def writer + get_ivar(:writer) end - def style(*selectors, &block); push_ivar(:styles, Style.new(*selectors, &block)); end - def styles - get_ivar(:styles) + def element_stack + get_ivar(:element_stack) end - def template(element, name, &block); push_ivar(:templates, Template.new(element, name, &block)); end - def templates - get_ivar(:templates) - end - def partial(name, &block); push_ivar(:partials, Partial.new(name, &block)); end - def partials - get_ivar(:partials) - 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 - [:to_data, :to_file].each do |meth| + # overriding to make less noisy + def to_str(*args) + "#<Osheet::Workbook:#{self.object_id} @title=#{workbook.title.inspect}, " + + "worksheet_count=#{workbook.worksheets.size.inspect}, " + + "style_count=#{workbook.styles.size.inspect}>" + end + alias_method :inspect, :to_str + + # 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