ballmer 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|