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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +1 -0
  5. data/Gemfile +4 -0
  6. data/Guardfile +11 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +54 -0
  9. data/Rakefile +8 -0
  10. data/ballmer.gemspec +29 -0
  11. data/bin/ballmer +10 -0
  12. data/bin/ballmer-console +9 -0
  13. data/bin/ballmer-duplicator +30 -0
  14. data/bin/ballmer-pack +19 -0
  15. data/bin/ballmer-unpack +14 -0
  16. data/lib/ballmer/document/archive.rb +89 -0
  17. data/lib/ballmer/document/content_types.rb +43 -0
  18. data/lib/ballmer/document/part.rb +26 -0
  19. data/lib/ballmer/document/rels.rb +79 -0
  20. data/lib/ballmer/document/xml_part.rb +29 -0
  21. data/lib/ballmer/document.rb +52 -0
  22. data/lib/ballmer/presentation/notes.rb +32 -0
  23. data/lib/ballmer/presentation/notes_parser.rb +57 -0
  24. data/lib/ballmer/presentation/slide.rb +19 -0
  25. data/lib/ballmer/presentation/slides.rb +172 -0
  26. data/lib/ballmer/presentation/tags.rb +50 -0
  27. data/lib/ballmer/presentation/tags.xml +3 -0
  28. data/lib/ballmer/presentation.rb +20 -0
  29. data/lib/ballmer/version.rb +3 -0
  30. data/lib/ballmer.rb +7 -0
  31. data/spec/fixtures/notes.pptx +0 -0
  32. data/spec/fixtures/presentation1.pptx +0 -0
  33. data/spec/fixtures/presentation2.pptx +0 -0
  34. data/spec/fixtures/presentation3.pptx +0 -0
  35. data/spec/lib/ballmer/document/content_types_spec.rb +35 -0
  36. data/spec/lib/ballmer/document/rels_spec.rb +43 -0
  37. data/spec/lib/ballmer/document_spec.rb +12 -0
  38. data/spec/lib/ballmer/presentation/notes_parser_spec.rb +19 -0
  39. data/spec/lib/ballmer/presentation/tags_spec.rb +22 -0
  40. data/spec/lib/ballmer/presentation_spec.rb +118 -0
  41. data/spec/spec_helper.rb +38 -0
  42. 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
@@ -0,0 +1,3 @@
1
+ module Ballmer
2
+ VERSION = "0.0.1"
3
+ end
data/lib/ballmer.rb ADDED
@@ -0,0 +1,7 @@
1
+ require "ballmer/version"
2
+ require "nokogiri"
3
+
4
+ module Ballmer
5
+ autoload :Presentation, 'ballmer/presentation'
6
+ autoload :Document, 'ballmer/document'
7
+ end
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,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe Ballmer::Document do
4
+ subject { read_presentation('presentation3.pptx') }
5
+
6
+ context "rels" do
7
+ it "should resolve"
8
+ end
9
+
10
+ context "content_types"
11
+ context "zip"
12
+ 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
@@ -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