Goal: Replace :string content in Choice/Fallback with proper DrawingML classes Duration: 30-45 minutes (compressed timeline) Status: Ready to begin Prerequisite: Session 2B Complete ✅ (9 DrawingML classes created, 258/258 baseline)
Session 2B successfully created 9 DrawingML classes:
- ✅ Drawing, Extent, DocProperties, NonVisualDrawingProps
- ✅ Inline, Anchor
- ✅ Graphic, GraphicData
- ✅ Baseline: 258/258 tests passing
Current Problem: Choice and Fallback still store nested content as :string.
The Solution: Integrate DrawingML classes into AlternateContent hierarchy.
# lib/uniword/wordprocessingml/alternate_content.rb
class AlternateContent < Lutaml::Model::Serializable
attribute :choice, Choice, collection: true
attribute :fallback, Fallback
end
# lib/uniword/wordprocessingml/choice.rb
class Choice < Lutaml::Model::Serializable
attribute :requires, :string
attribute :content, :string # ❌ WRONG - Should be Drawing
end
# lib/uniword/wordprocessingml/fallback.rb
class Fallback < Lutaml::Model::Serializable
attribute :content, :string # ❌ WRONG - Should be Pict or Drawing
endclass Choice < Lutaml::Model::Serializable
attribute :requires, :string
attribute :drawing, Drawing # ✅ Modern DrawingML (w:drawing)
end
class Fallback < Lutaml::Model::Serializable
attribute :pict, Pict # ✅ VML picture (w:pict)
attribute :drawing, Drawing # ✅ Or DrawingML fallback
endWe'll modify 2 classes in 3 tasks:
File: lib/uniword/wordprocessingml/choice.rb
Change:
# BEFORE
attribute :content, :string
# AFTER
attribute :drawing, DrawingXML Mapping:
map_element 'drawing', to: :drawing, render_nil: falseFile: lib/uniword/wordprocessingml/fallback.rb
Change:
# BEFORE
attribute :content, :string
# AFTER
attribute :pict, Pict # VML picture
attribute :drawing, Drawing # DrawingML fallbackXML Mapping:
map_element 'pict', to: :pict, render_nil: false
map_element 'drawing', to: :drawing, render_nil: falseTests:
- Unit test: AlternateContent serialization
- Baseline: 258/258 still passing
- Document Elements: Check improvement in glossary tests
<read_file>
lib/uniword/wordprocessingml/choice.rb</read_file>
<apply_diff> Replace:
attribute :content, :stringWith:
attribute :drawing, DrawingReplace in xml block:
map_element 'drawing', to: :contentWith:
map_element 'drawing', to: :drawing, render_nil: false</apply_diff>
<read_file>
lib/uniword/wordprocessingml/fallback.rb</read_file>
<apply_diff> Replace:
attribute :content, :stringWith:
attribute :pict, Pict
attribute :drawing, DrawingReplace in xml block:
map_element 'pict', to: :contentWith:
map_element 'pict', to: :pict, render_nil: false
map_element 'drawing', to: :drawing, render_nil: false</apply_diff>
Unit Test:
cd /Users/mulgogi/src/mn/uniword
bundle exec ruby -e "
require './lib/uniword'
ac = Uniword::Wordprocessingml::AlternateContent.new
choice = Uniword::Wordprocessingml::Choice.new
choice.drawing = Uniword::Wordprocessingml::Drawing.new
ac.choice = [choice]
puts 'AlternateContent integration: PASS' if ac.choice.first.drawing
"Baseline Test:
cd /Users/mulgogi/src/mn/uniword
bundle exec rspec spec/uniword/styleset_roundtrip_spec.rb \
spec/uniword/theme_roundtrip_spec.rb \
--format progressExpected: 258/258 passing ✅
Document Elements Test:
cd /Users/mulgogi/src/mn/uniword
bundle exec rspec spec/uniword/document_element_roundtrip_spec.rb \
--format progressExpected: Improvement in glossary tests (currently 0/8, expecting 4-8/8)
- Choice class uses
Drawinginstead of:string - Fallback class uses
PictandDrawinginstead of:string - All classes follow Pattern 0 (attributes BEFORE xml)
- All classes are model-driven (NO :string for XML content)
- Baseline tests: 258/258 still passing ✅
- Document Elements: Improvement in glossary round-trip
⚠️ ALWAYS usebundle execwith Ruby commands!- 🚨 Pattern 0: Attributes BEFORE xml (ALWAYS)
- 🏗️ Model-Driven: NO :string for XML content
- 🔧 Namespace: Each class defines its own
- 📦 Mixed Content: Use
mixed_contentfor elements with nested content - ✨ Render Nil: Use
render_nil: falsefor optional elements - ✅ Zero Regressions: Baseline must stay at 258/258
- Choice: stores Drawing as :string
- Fallback: stores Pict/Drawing as :string
- Round-trip: Limited (glossary 0/8)
- Choice: properly models Drawing
- Fallback: properly models Pict and Drawing
- Round-trip: Significant improvement (glossary 4-8/8)
- Architecture: 100% model-driven (no XML strings)
Session 2D: Final Verification (30 min)
- Run complete test suite (expecting 270-274/274)
- Document improvements
- Create Phase 5 Session 2 summary
- Update memory bank
- Celebrate near-100% achievement! 🎊
Low Risk:
- Simple attribute type changes
- No new classes needed
- Well-tested foundation (Session 2A/2B)
Mitigation:
- Verify baseline after each change
- Use
render_nil: falsefor optional elements - Follow exact pattern from Session 2A/2B
- Task 1 (Choice): 15 min
- Task 2 (Fallback): 15 min
- Task 3 (Verification): 15 min
- Total: 45 minutes
Created in Session 2B:
lib/uniword/wordprocessingml/drawing.rblib/uniword/wp_drawing/extent.rblib/uniword/wp_drawing/doc_properties.rblib/uniword/wp_drawing/non_visual_drawing_props.rblib/uniword/wp_drawing/inline.rblib/uniword/wp_drawing/anchor.rblib/uniword/drawingml/graphic.rblib/uniword/drawingml/graphic_data.rb
To Modify in Session 2C:
lib/uniword/wordprocessingml/choice.rblib/uniword/wordprocessingml/fallback.rb
Created in Session 2A:
lib/uniword/wordprocessingml/text_box_content.rblib/uniword/wordprocessingml/pict.rblib/uniword/vml/textbox.rblib/uniword/vml/shape.rb(enhanced)
This integration completes the AlternateContent architecture:
- AlternateContent - Container for modern/fallback content
- Choice - Modern DrawingML content (Office 2007+)
- Fallback - Legacy VML content (Office 2003+)
- Drawing - WordProcessing Drawing (wp: namespace)
- Pict - VML Picture (v: namespace)
All layers are now properly modeled with NO :string XML content! 🎯