ballmer 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +1 -0
- data/Gemfile +4 -0
- data/Guardfile +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +54 -0
- data/Rakefile +8 -0
- data/ballmer.gemspec +29 -0
- data/bin/ballmer +10 -0
- data/bin/ballmer-console +9 -0
- data/bin/ballmer-duplicator +30 -0
- data/bin/ballmer-pack +19 -0
- data/bin/ballmer-unpack +14 -0
- data/lib/ballmer/document/archive.rb +89 -0
- data/lib/ballmer/document/content_types.rb +43 -0
- data/lib/ballmer/document/part.rb +26 -0
- data/lib/ballmer/document/rels.rb +79 -0
- data/lib/ballmer/document/xml_part.rb +29 -0
- data/lib/ballmer/document.rb +52 -0
- data/lib/ballmer/presentation/notes.rb +32 -0
- data/lib/ballmer/presentation/notes_parser.rb +57 -0
- data/lib/ballmer/presentation/slide.rb +19 -0
- data/lib/ballmer/presentation/slides.rb +172 -0
- data/lib/ballmer/presentation/tags.rb +50 -0
- data/lib/ballmer/presentation/tags.xml +3 -0
- data/lib/ballmer/presentation.rb +20 -0
- data/lib/ballmer/version.rb +3 -0
- data/lib/ballmer.rb +7 -0
- data/spec/fixtures/notes.pptx +0 -0
- data/spec/fixtures/presentation1.pptx +0 -0
- data/spec/fixtures/presentation2.pptx +0 -0
- data/spec/fixtures/presentation3.pptx +0 -0
- data/spec/lib/ballmer/document/content_types_spec.rb +35 -0
- data/spec/lib/ballmer/document/rels_spec.rb +43 -0
- data/spec/lib/ballmer/document_spec.rb +12 -0
- data/spec/lib/ballmer/presentation/notes_parser_spec.rb +19 -0
- data/spec/lib/ballmer/presentation/tags_spec.rb +22 -0
- data/spec/lib/ballmer/presentation_spec.rb +118 -0
- data/spec/spec_helper.rb +38 -0
- metadata +198 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
module Ballmer
|
2
|
+
class Presentation
|
3
|
+
# Load a slide up in thar.
|
4
|
+
class Slide < Document::XMLPart
|
5
|
+
# Key used to look up slides from [Content-Types].xml.
|
6
|
+
CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.slide+xml".freeze
|
7
|
+
|
8
|
+
# Key used to look up slides from .xml.rel documents
|
9
|
+
REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide".freeze
|
10
|
+
|
11
|
+
def notes
|
12
|
+
# TODO - Move a type caster into rels based on content type like
|
13
|
+
# rels[Notes::REL_TYPE].first
|
14
|
+
notes_path = rels.targets(Notes::REL_TYPE).first.expand_path(@path.dirname)
|
15
|
+
Notes.new(@doc, notes_path)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
module Ballmer
|
2
|
+
class Presentation
|
3
|
+
# Manages concerns around keeping slide and notesSlides files in
|
4
|
+
# sync with an array of slides. These basically needs to trasnact
|
5
|
+
# the slide\d+ and slideNote\d+ numbers to be in sync with an array.
|
6
|
+
# Its a big, ugly ass complicated beast. Send your thank you cards to Bill Gates.
|
7
|
+
class Slides
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
def initialize(doc)
|
11
|
+
@doc = doc
|
12
|
+
end
|
13
|
+
|
14
|
+
def each(&block)
|
15
|
+
# TODO - Do NOT read content-types, but read Rels instead (and move this type casting in there.)
|
16
|
+
@doc.content_types[Slide::CONTENT_TYPE].each { |path| block.call slide path }
|
17
|
+
end
|
18
|
+
|
19
|
+
# This method is crazy because it has to manipulate a ton of files within the PPTX. Most of
|
20
|
+
# what happens in here I figured out by diff-ing PPTX files that had copies of identical slides, but
|
21
|
+
# a different number of slides.
|
22
|
+
def push(slide)
|
23
|
+
n = to_a.size + 1
|
24
|
+
# Paths within the zip file of new files we have to write.
|
25
|
+
slide_path = Pathname.new("/ppt/slides/slide#{n}.xml")
|
26
|
+
slide_rels_path = Pathname.new("/ppt/slides/_rels/slide#{n}.xml.rels")
|
27
|
+
slide_notes_path = Pathname.new("/ppt/notesSlides/notesSlide#{n}.xml")
|
28
|
+
slide_notes_rels_path = Pathname.new("/ppt/notesSlides/_rels/notesSlide#{n}.xml.rels")
|
29
|
+
|
30
|
+
# Update ./ppt
|
31
|
+
# !!! CREATE !!!
|
32
|
+
# ./slides
|
33
|
+
# Create new files
|
34
|
+
# ./slide(\d+).xml file
|
35
|
+
@doc.copy slide_path, slide.path
|
36
|
+
# ./_rels/slide(\d+).xml.rels
|
37
|
+
@doc.copy slide_rels_path, slide.rels.path
|
38
|
+
# ./notesSlides
|
39
|
+
# Create new files
|
40
|
+
# ./notesSlide(\d+).xml file
|
41
|
+
@doc.copy slide_notes_path, slide.notes.path
|
42
|
+
# ./_rels/notesSlide(\d+).xml.rels
|
43
|
+
@doc.copy slide_notes_rels_path, slide.notes.rels.path
|
44
|
+
|
45
|
+
# !!! UPDATES !!!
|
46
|
+
# Update the notes in the new slide to point at the new notes
|
47
|
+
@doc.edit_xml slide_rels_path do |xml|
|
48
|
+
# TODO - Move this rel logic into the parts so that we don't have to repeat ourselves when calculating this stuff out.
|
49
|
+
xml.at_xpath("//xmlns:Relationship[@Type='#{Notes::REL_TYPE}']")['Target'] = slide_notes_path.relative_path_from(slide_path.dirname)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Update teh slideNotes reference to point at the new slide
|
53
|
+
@doc.edit_xml slide_notes_rels_path do |xml|
|
54
|
+
xml.at_xpath("//xmlns:Relationship[@Type='#{Slide::REL_TYPE}']")['Target'] = slide_path.relative_path_from(slide_notes_path.dirname)
|
55
|
+
end
|
56
|
+
|
57
|
+
# ./_rels/presentation.xml.rels
|
58
|
+
# Update Relationship ids
|
59
|
+
# Insert a new one slideRef
|
60
|
+
@doc.edit_xml @doc.presentation.rels.path do |xml|
|
61
|
+
# Calucate the next id
|
62
|
+
next_id = xml.xpath('//xmlns:Relationship[@Id]').map{ |n| n['Id'] }.sort.last.succ
|
63
|
+
# TODO - Figure out how to make this more MS idiomatic up 9->10 instead of incrementing
|
64
|
+
# the character....
|
65
|
+
# Insert that into the slide and crakc open the presentation.xml file
|
66
|
+
types = xml.at_xpath('/xmlns:Relationships')
|
67
|
+
types << Nokogiri::XML::Node.new("Relationship", xml).tap do |n|
|
68
|
+
n['Id'] = next_id
|
69
|
+
n['Type'] = Slide::REL_TYPE
|
70
|
+
n['Target'] = slide_path.relative_path_from(@doc.presentation.path.dirname)
|
71
|
+
end
|
72
|
+
# ./presentation.xml
|
73
|
+
# Update attr
|
74
|
+
# p:notesMasterId
|
75
|
+
# Insert attr
|
76
|
+
# p:sldId, increment, etc.
|
77
|
+
@doc.edit_xml '/ppt/presentation.xml' do |xml|
|
78
|
+
slides = xml.at_xpath('/p:presentation/p:sldIdLst')
|
79
|
+
next_slide_id = slides.xpath('//p:sldId[@id]').map{ |n| n['id'] }.sort.last.succ
|
80
|
+
slides << Nokogiri::XML::Node.new("p:sldId", xml).tap do |n|
|
81
|
+
# TODO - Fix the ID that's jacked up.
|
82
|
+
n['id'] = next_slide_id
|
83
|
+
n['r:id'] = next_id
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Update ./[Content-Types].xml with new slide link and slideNotes link
|
89
|
+
@doc.edit_xml @doc.content_types.path do |xml|
|
90
|
+
types = xml.at_xpath('/xmlns:Types')
|
91
|
+
types << Nokogiri::XML::Node.new("Override", xml).tap do |n|
|
92
|
+
n['PartName'] = slide_path
|
93
|
+
n['ContentType'] = Slide::CONTENT_TYPE
|
94
|
+
end
|
95
|
+
types << Nokogiri::XML::Node.new("Override", xml).tap do |n|
|
96
|
+
n['PartName'] = slide_notes_path
|
97
|
+
n['ContentType'] = Notes::CONTENT_TYPE
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Great, that's all done, so lets return the slide eh?
|
102
|
+
slide slide_path
|
103
|
+
end
|
104
|
+
|
105
|
+
# Removes a slide from the slides collection
|
106
|
+
def delete(slide)
|
107
|
+
# ./_rels/presentation.xml.rels
|
108
|
+
# Update Relationship ids
|
109
|
+
# Insert a new one slideRef
|
110
|
+
@doc.edit_xml @doc.presentation.rels.path do |xml|
|
111
|
+
# Calucate the next id
|
112
|
+
# next_id = xml.xpath('//xmlns:Relationship[@Id]').map{ |n| n['Id'] }.sort.last.succ
|
113
|
+
# TODO - Figure out how to make this more MS idiomatic up 9->10 instead of incrementing
|
114
|
+
# the character....
|
115
|
+
# Insert that into the slide and crakc open the presentation.xml file
|
116
|
+
|
117
|
+
target = slide.path.relative_path_from(@doc.presentation.path.dirname)
|
118
|
+
relationship = xml.at_xpath("/xmlns:Relationships/xmlns:Relationship[@Type='#{Slide::REL_TYPE}' and @Target='#{target}']")
|
119
|
+
# ./presentation.xml
|
120
|
+
# Update attr
|
121
|
+
# p:notesMasterId
|
122
|
+
# Insert attr
|
123
|
+
# p:sldId, increment, etc.
|
124
|
+
@doc.edit_xml '/ppt/presentation.xml' do |xml|
|
125
|
+
xml.at_xpath("/p:presentation/p:sldIdLst/p:sldId[@r:id='#{relationship['Id']}']").remove
|
126
|
+
end
|
127
|
+
relationship.remove
|
128
|
+
end
|
129
|
+
|
130
|
+
# Delete slide link and slideNotes link from ./[Content-Types].xml
|
131
|
+
@doc.edit_xml @doc.content_types.path do |xml|
|
132
|
+
xml.at_xpath("/xmlns:Types/xmlns:Override[@ContentType='#{Slide::CONTENT_TYPE}' and @PartName='#{slide.path}']").remove
|
133
|
+
xml.at_xpath("/xmlns:Types/xmlns:Override[@ContentType='#{Notes::CONTENT_TYPE}' and @PartName='#{slide.notes.path}']").remove
|
134
|
+
end
|
135
|
+
|
136
|
+
# Update ./ppt
|
137
|
+
# !!! DESTROY !!!
|
138
|
+
# ./slides
|
139
|
+
# Delete files
|
140
|
+
# ./_rels/notesSlide(\d+).xml.rels
|
141
|
+
@doc.delete slide.notes.rels.path
|
142
|
+
# ./notesSlide(\d+).xml file
|
143
|
+
@doc.delete slide.notes.path
|
144
|
+
# ./_rels/slide(\d+).xml.rels
|
145
|
+
@doc.delete slide.rels.path
|
146
|
+
# ./slide(\d+).xml file
|
147
|
+
@doc.delete slide.path
|
148
|
+
# ./notesSlides
|
149
|
+
# Delete files
|
150
|
+
|
151
|
+
# Hooray! We're done! Ummm, what should we return though? can't be the slide since
|
152
|
+
# its destroyed and there's no practical way to keep it around in memory.
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
def slide(path)
|
157
|
+
Slide.new(@doc, path)
|
158
|
+
end
|
159
|
+
# Reads from the [Content_Types].xml file the paths for the slide
|
160
|
+
# <Override PartName="/ppt/slides/slide1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml"/>
|
161
|
+
def parts
|
162
|
+
@doc.content_types.parts Slide::CONTENT_TYPE
|
163
|
+
end
|
164
|
+
|
165
|
+
# Microsoft decided it would be cool to start at 1 instead of 0
|
166
|
+
# for the part indices, so this deals with that seperatly
|
167
|
+
def next_number
|
168
|
+
self.to_a.size + 1
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Ballmer
|
2
|
+
class Presentation
|
3
|
+
class Tags < Document::XMLPart
|
4
|
+
# TODO, there are three types of notes. We need to figure out
|
5
|
+
# how to resolve the slide number, notes, and whatever the hell else
|
6
|
+
# the first note type is.
|
7
|
+
|
8
|
+
# Key used to look up notes from [Content-Types].xml.
|
9
|
+
CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.tags+xml".freeze
|
10
|
+
|
11
|
+
# Key used to look up notes from .xml.rel documents
|
12
|
+
REL_TYPE = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/tags'.freeze
|
13
|
+
|
14
|
+
# Read tag
|
15
|
+
def [](key)
|
16
|
+
if tag = tag(key)
|
17
|
+
tag['val']
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# If the tag exists, create it -- otherwise update.
|
22
|
+
# <p:tag name="" val=""/>
|
23
|
+
def []=(key, value)
|
24
|
+
unless tag key # Don't add the tag if it already exists.
|
25
|
+
tag_list << Nokogiri::XML::Node.new("p:tag", @xml) do |tag|
|
26
|
+
tag['name'] = key
|
27
|
+
tag['val'] = value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def tag(key)
|
35
|
+
tag_list.at_xpath("./p:tag[@name='#{key}']")
|
36
|
+
end
|
37
|
+
|
38
|
+
def tag_list
|
39
|
+
xml.at_xpath('p:tagLst')
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.build(doc, path)
|
43
|
+
# Write the template
|
44
|
+
doc.write path, File.read(File.join(File.dirname(__FILE__), 'tags.xml'))
|
45
|
+
# K, now lets read it and decorate with the part.
|
46
|
+
new doc, path
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,3 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
2
|
+
<p:tagLst xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
|
3
|
+
</p:tagLst>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Ballmer
|
2
|
+
# Represents a presentation that has many slides.
|
3
|
+
class Presentation < Document
|
4
|
+
autoload :Slides, 'ballmer/presentation/slides'
|
5
|
+
autoload :Slide, 'ballmer/presentation/slide'
|
6
|
+
autoload :Notes, 'ballmer/presentation/notes'
|
7
|
+
autoload :NotesParser, 'ballmer/presentation/notes_parser'
|
8
|
+
autoload :Tags, 'ballmer/presentation/tags'
|
9
|
+
|
10
|
+
# Return an array of slides.
|
11
|
+
def slides
|
12
|
+
@slides ||= Slides.new(self)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Presentation XML file.
|
16
|
+
def presentation
|
17
|
+
XMLPart.new(self, '/ppt/presentation.xml')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/ballmer.rb
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ballmer::Document::ContentTypes do
|
4
|
+
subject { read_presentation('presentation3.pptx') }
|
5
|
+
let(:tags) { Ballmer::Presentation::Tags.new(subject, 'tags/tag1.xml') }
|
6
|
+
|
7
|
+
describe "#exists?" do
|
8
|
+
it "should detect if part is in XML" do
|
9
|
+
subject.content_types.should exist(subject.slides.first)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should detect if part is not in XML" do
|
13
|
+
subject.content_types.should_not exist(tags)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#append" do
|
18
|
+
before(:each) do
|
19
|
+
subject.content_types.append tags
|
20
|
+
end
|
21
|
+
|
22
|
+
let(:nodes) do
|
23
|
+
subject.content_types.xml.xpath("//xmlns:Override[@ContentType='#{tags.class::CONTENT_TYPE}' and @PartName='#{tags.path}']")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should write part to XML" do
|
27
|
+
nodes.should have(1).item
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should not write duplicate parts to XML" do
|
31
|
+
subject.content_types.append tags
|
32
|
+
nodes.should have(1).item
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ballmer::Document::Rels do
|
4
|
+
subject { read_presentation('presentation3.pptx') }
|
5
|
+
let(:tags) { Ballmer::Presentation::Tags.new(subject, '/ppt/tags/tag1.xml') }
|
6
|
+
let(:slide) { subject.slides.first }
|
7
|
+
|
8
|
+
describe "#exists?" do
|
9
|
+
it "should detect if part is in XML" do
|
10
|
+
subject.presentation.rels.should exist(subject.slides.first)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should detect if part is not in XML" do
|
14
|
+
subject.presentation.rels.should_not exist(tags)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#append" do
|
19
|
+
before(:each) do
|
20
|
+
slide.rels.append tags
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:nodes) do
|
24
|
+
slide.rels.xml.xpath("//xmlns:Relationship[@Type='#{tags.class::REL_TYPE}' and @Target='#{slide.relative_path_from(tags)}']")
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should write part to XML" do
|
28
|
+
nodes.should have(1).item
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should not write duplicate parts to XML" do
|
32
|
+
slide.rels.append tags
|
33
|
+
nodes.should have(1).item
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#id" do
|
38
|
+
it "should get id" do
|
39
|
+
# I just pulled this value out of the template.
|
40
|
+
subject.presentation.rels.id(subject.slides.first).should == 'rId2'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Ballmer::Presentation::NotesParser do
|
6
|
+
# Fixture contains a complex note with several different formats.
|
7
|
+
subject { read_presentation('notes.pptx').slides.first.notes }
|
8
|
+
|
9
|
+
it "should have notes" do
|
10
|
+
subject.body.should == %[Some crazy notes are here. Some words are in bold, others are italic.
|
11
|
+
|
12
|
+
Funny thing about notes. When there’s a mispullin’ it gets underlined and tokenized.]
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should edit notes" do
|
16
|
+
subject.body = "Hi\bpig"
|
17
|
+
subject.body.should == "Hi\bpig"
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ballmer::Presentation::Tags do
|
4
|
+
let(:presentation) { read_presentation('presentation1.pptx') }
|
5
|
+
subject { Ballmer::Presentation::Tags.build(presentation, 'ppt/tags/tags1.xml') }
|
6
|
+
|
7
|
+
it "should set key" do
|
8
|
+
subject['foo'] = 'bar'
|
9
|
+
subject.xml.at_xpath("//p:tag[@name='foo']")['val'].should == 'bar'
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should read key" do
|
13
|
+
subject['foo'] = 'bar'
|
14
|
+
subject['foo'].should == 'bar'
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should not write duplicate keys" do
|
18
|
+
subject['foo'] = 'bar'
|
19
|
+
subject['foo'] = 'bar'
|
20
|
+
subject.xml.xpath("//p:tag[@name='foo']").should have(1).item
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ballmer::Presentation do
|
4
|
+
subject { read_presentation('presentation3.pptx') }
|
5
|
+
|
6
|
+
context "presentation" do
|
7
|
+
it "should have slides" do
|
8
|
+
subject.slides.should have(3).items
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context "slides" do
|
13
|
+
let(:original) { subject.slides.to_a.first }
|
14
|
+
let(:copy) { subject.slides.push original }
|
15
|
+
|
16
|
+
# Explicitly call copy before each test in case
|
17
|
+
# we don't call "copy" from a spec below since
|
18
|
+
# we don't make assertions on "copy" per test.
|
19
|
+
before(:each){ copy }
|
20
|
+
|
21
|
+
context "#push" do
|
22
|
+
it "should copy slide" do
|
23
|
+
original.notes.body.should == copy.notes.body
|
24
|
+
end
|
25
|
+
|
26
|
+
context "slide.xml" do
|
27
|
+
let(:slide) { copy }
|
28
|
+
|
29
|
+
it "should append" do
|
30
|
+
slide.path.to_s.should == '/ppt/slides/slide4.xml'
|
31
|
+
end
|
32
|
+
|
33
|
+
context "rels" do
|
34
|
+
it "should append ./_rels/slide.xml" do
|
35
|
+
slide.rels.path.to_s.should == '/ppt/slides/_rels/slide4.xml.rels'
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should reference notesSlides.xml" do
|
39
|
+
rels = slide.rels.targets(Ballmer::Presentation::Notes::REL_TYPE)
|
40
|
+
rels.should have(1).item
|
41
|
+
rels.first.to_s.should == '../notesSlides/notesSlide4.xml'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "notesSlides.xml" do
|
47
|
+
let(:notes) { copy.notes }
|
48
|
+
|
49
|
+
it "should append" do
|
50
|
+
notes.path.to_s.should == '/ppt/notesSlides/notesSlide4.xml'
|
51
|
+
end
|
52
|
+
|
53
|
+
context "rels" do
|
54
|
+
it "should append ./_rels/notesSlides.xml" do
|
55
|
+
notes.rels.path.to_s.should == '/ppt/notesSlides/_rels/notesSlide4.xml.rels'
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should reference slides.xml" do
|
59
|
+
rels = notes.rels.targets(Ballmer::Presentation::Slide::REL_TYPE)
|
60
|
+
rels.should have(1).item
|
61
|
+
rels.first.to_s.should == '../slides/slide4.xml'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "[Content-Type].xml" do
|
67
|
+
it "should add slide Override" do
|
68
|
+
subject.content_types[Ballmer::Presentation::Slide::CONTENT_TYPE].should have(4).items
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "presentation.xml" do
|
73
|
+
it "should add slide"
|
74
|
+
it "should add slideId"
|
75
|
+
|
76
|
+
context "_rels" do
|
77
|
+
it "should add slide"
|
78
|
+
it "should add "
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context "#delete" do
|
84
|
+
before(:each) do
|
85
|
+
subject.slides.delete subject.slides.first
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should remove slide" do
|
89
|
+
subject.should have(3).slides
|
90
|
+
end
|
91
|
+
|
92
|
+
context "[Content-Type].xml" do
|
93
|
+
it "should remove slide"
|
94
|
+
end
|
95
|
+
|
96
|
+
context "presentation.xml" do
|
97
|
+
it "should remove slide"
|
98
|
+
context "_rels" do
|
99
|
+
it "should remove slide"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "slide.xml" do
|
104
|
+
it "should delete"
|
105
|
+
context "rels" do
|
106
|
+
it "should delete"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context "noteSlide.xml" do
|
111
|
+
it "should delete"
|
112
|
+
context "rels" do
|
113
|
+
it "should delete"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require "ballmer"
|
2
|
+
|
3
|
+
# Helpers
|
4
|
+
module Ballmer
|
5
|
+
module Helpers
|
6
|
+
# Open a presentation, k?
|
7
|
+
def read_presentation(file)
|
8
|
+
Presentation.read(read(file))
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
# Read the bytes of a document.
|
13
|
+
def read(file)
|
14
|
+
File.read(File.join(File.dirname(__FILE__), 'fixtures', file))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
20
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
21
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
22
|
+
# loaded once.
|
23
|
+
#
|
24
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
25
|
+
RSpec.configure do |config|
|
26
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
27
|
+
config.run_all_when_everything_filtered = true
|
28
|
+
config.filter_run :focus
|
29
|
+
|
30
|
+
# Run specs in random order to surface order dependencies. If you find an
|
31
|
+
# order dependency and want to debug it, you can fix the order by providing
|
32
|
+
# the seed, which is printed after each run.
|
33
|
+
# --seed 1234
|
34
|
+
config.order = 'random'
|
35
|
+
|
36
|
+
# Include the helpers module
|
37
|
+
config.include Ballmer::Helpers
|
38
|
+
end
|