marksman 0.0.0 → 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +23 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +7 -0
- data/Guardfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +80 -0
- data/Rakefile +14 -0
- data/bin/marksman +8 -0
- data/lib/marksman.rb +13 -0
- data/lib/marksman/cli.rb +29 -0
- data/lib/marksman/converter.rb +50 -0
- data/lib/marksman/equality.rb +15 -0
- data/lib/marksman/parser.rb +55 -0
- data/lib/marksman/presentation.rb +26 -0
- data/lib/marksman/slide.rb +12 -0
- data/lib/marksman/theme.rb +21 -0
- data/lib/marksman/version.rb +3 -0
- data/lib/marksman/watcher.rb +47 -0
- data/lib/marksman/writer.rb +18 -0
- data/marksman.gemspec +32 -0
- data/spec/data/example.md +11 -0
- data/spec/data/example_without_metadata.md +7 -0
- data/spec/data/output/index.html +44 -0
- data/spec/marksman/cli_spec.rb +26 -0
- data/spec/marksman/converter_spec.rb +40 -0
- data/spec/marksman/parser_spec.rb +64 -0
- data/spec/marksman/presentation_spec.rb +25 -0
- data/spec/marksman/slide_spec.rb +17 -0
- data/spec/marksman/theme_spec.rb +29 -0
- data/spec/marksman/watcher_spec.rb +45 -0
- data/spec/marksman/writer_spec.rb +15 -0
- data/spec/spec_helper.rb +12 -0
- data/themes/blank/css/blank.css +5 -0
- data/themes/blank/css/tomorrow-night.css +93 -0
- data/themes/blank/index.haml +21 -0
- data/themes/blank/js/highlight.min.js +1 -0
- data/themes/plain/css/default.css +42 -0
- data/themes/plain/css/github.css +125 -0
- data/themes/plain/css/tomorrow-night.css +93 -0
- data/themes/plain/css/tomorrow.css +90 -0
- data/themes/plain/index.haml +18 -0
- data/themes/plain/js/highlight.min.js +1 -0
- metadata +210 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 057ad0bdc1acdca0c3c4683862837088232521df
|
4
|
+
data.tar.gz: a105f581953f1c52d9416dda50c63fca60c0b82c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7c355dbbc9670e5fa05583a04f4b3caecae926d318a8a6a2379f4d4d09a296a95ac2afa08e9b5ec90fb53f29263236607c0ac33d23087865246471772cafa11d
|
7
|
+
data.tar.gz: 90d00af80c4ba2a6873250d568150c53088678a3132898be165d3ca18e88e8e7d342fc83854d5fe36b5b36a9f3723d1537db45fb789e5dd9d0da3f3c77bbdea4
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
public
|
19
|
+
*.bundle
|
20
|
+
*.so
|
21
|
+
*.o
|
22
|
+
*.a
|
23
|
+
mkmf.log
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Bodo Tasche
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# Marksman
|
2
|
+
|
3
|
+
[![Code Climate](https://codeclimate.com/github/bitboxer/marksman.png)](https://codeclimate.com/github/bitboxer/marksman)
|
4
|
+
[![Coverage Status](https://coveralls.io/repos/bitboxer/marksman/badge.png?branch=master)](https://coveralls.io/r/bitboxer/marksman?branch=master)
|
5
|
+
[![Gem Version](http://img.shields.io/gem/v/marksman.svg)](http://rubygems.org/gems/marksman)
|
6
|
+
|
7
|
+
This is an opinionated approach to presentations. I don't want to
|
8
|
+
think a lot about the design or how to place my text on a slide.
|
9
|
+
|
10
|
+
I want to focus on the text. On what I want to say. On the important
|
11
|
+
part of the presentation.
|
12
|
+
|
13
|
+
If you think the same and love markdown, this might be for you.
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
gem install marksman
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
This generates a public folder with all things needed from the
|
22
|
+
markdown file `presentation.md`:
|
23
|
+
|
24
|
+
marksman generate presentation.md
|
25
|
+
|
26
|
+
You can also run it as a watcher that regenerates the presentation each
|
27
|
+
time the markdown file was changed
|
28
|
+
|
29
|
+
marksman watch presentation.md
|
30
|
+
|
31
|
+
## File format
|
32
|
+
|
33
|
+
The system uses plain markdown files with two additions:
|
34
|
+
|
35
|
+
* The header can have a jekyll like yaml block
|
36
|
+
* Slides are separated with `----------`
|
37
|
+
* Presenter Notes within a slide are separated with `**********` from the
|
38
|
+
text in the slide
|
39
|
+
|
40
|
+
Here is an example for that format:
|
41
|
+
|
42
|
+
---
|
43
|
+
title: "How to run a company"
|
44
|
+
theme: "plain"
|
45
|
+
---
|
46
|
+
Bodo Tasche
|
47
|
+
@bitboxer
|
48
|
+
********
|
49
|
+
Here I write what I want to say about me. Aka presenter notes.
|
50
|
+
--------
|
51
|
+
This is code:
|
52
|
+
```
|
53
|
+
def hello(name)
|
54
|
+
puts "Hello #{name}"
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
58
|
+
## Themes
|
59
|
+
|
60
|
+
Marksman supports themes. You can either set the used theme in the header of the
|
61
|
+
presentation or override that choice by adding `-t name` as a parameter.
|
62
|
+
|
63
|
+
Currently there are only two themes available:
|
64
|
+
|
65
|
+
* `blank` - this renders the presentation as a simple HTML that can be used
|
66
|
+
to print the presentation
|
67
|
+
* `plain` - this is a basic presentation theme
|
68
|
+
|
69
|
+
If you want to create a theme, just copy one of the themes to your project folder and
|
70
|
+
reference the directory name in the header.
|
71
|
+
|
72
|
+
If you have a theme you would love to share, please send a pull request.
|
73
|
+
|
74
|
+
## Contributing
|
75
|
+
|
76
|
+
1. Fork it ( https://github.com/[my-github-username]/marksman/fork )
|
77
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
78
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
79
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
80
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/marksman
ADDED
data/lib/marksman.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'marksman/equality'
|
2
|
+
require 'marksman/cli'
|
3
|
+
require 'marksman/converter'
|
4
|
+
require 'marksman/writer'
|
5
|
+
require 'marksman/parser'
|
6
|
+
require 'marksman/presentation'
|
7
|
+
require 'marksman/slide'
|
8
|
+
require 'marksman/theme'
|
9
|
+
require 'marksman/version'
|
10
|
+
require 'marksman/watcher'
|
11
|
+
|
12
|
+
module Marksman
|
13
|
+
end
|
data/lib/marksman/cli.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'marksman'
|
3
|
+
|
4
|
+
module Marksman
|
5
|
+
class CLI < Thor
|
6
|
+
|
7
|
+
desc 'generate FILE [OUTPUT DIRECTORY]', 'generates a presentation from a markdown file'
|
8
|
+
long_desc <<-LONGDESC
|
9
|
+
`marsksman generate file.md` will parse the markdown file and
|
10
|
+
create a presentation in the folder 'public'
|
11
|
+
|
12
|
+
You can optionally specify the folder where the presentation is stored
|
13
|
+
as a second parameter.
|
14
|
+
|
15
|
+
> $ marksman FILE.md output
|
16
|
+
LONGDESC
|
17
|
+
method_option :theme, desc: 'override the theme in the presentation', aliases: ['-t']
|
18
|
+
def generate(file, target = 'public')
|
19
|
+
Marksman::Writer.new(file, target, options[:theme]).generate
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'watch FILE [OUTPUT DIRECTORY]', 'same as generate, but watches the file and regenerates it on each change'
|
23
|
+
method_option :theme, desc: 'override the theme in the presentation', aliases: ['-t']
|
24
|
+
def watch(file, target = 'public')
|
25
|
+
Marksman::Watcher.new(file, target, options[:theme]).watch
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'haml'
|
3
|
+
|
4
|
+
module Marksman
|
5
|
+
class Converter
|
6
|
+
|
7
|
+
attr_accessor :presentation, :theme
|
8
|
+
|
9
|
+
def initialize(presentation)
|
10
|
+
@presentation = presentation
|
11
|
+
end
|
12
|
+
|
13
|
+
def write(directory)
|
14
|
+
puts 'Generating HTML...'
|
15
|
+
FileUtils.mkdir_p(directory)
|
16
|
+
Dir.glob(@presentation.theme.path.join('**/*')).each do |file|
|
17
|
+
if File.file? file
|
18
|
+
copy_file(Pathname.new(file), directory)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def copy_file(source, target_dir)
|
26
|
+
target = target_name(source, target_dir)
|
27
|
+
FileUtils.mkdir(target.dirname) unless File.exist? target.dirname
|
28
|
+
if (source.extname == '.haml')
|
29
|
+
convert_haml(source, target.to_s.gsub(/\.haml$/, '.html'))
|
30
|
+
else
|
31
|
+
FileUtils.copy(source, target)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def target_name(file, target_dir)
|
36
|
+
base_file = file.to_s[(@presentation.theme.path.to_s.length + 1)..-1]
|
37
|
+
target_file = Pathname.new(target_dir).join(base_file)
|
38
|
+
end
|
39
|
+
|
40
|
+
def convert_haml(file, output)
|
41
|
+
template = File.read(file)
|
42
|
+
haml_engine = Haml::Engine.new(template)
|
43
|
+
html = haml_engine.render(Object.new, {
|
44
|
+
presentation: @presentation
|
45
|
+
})
|
46
|
+
File.write(output, html)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Marksman
|
2
|
+
module Equality
|
3
|
+
|
4
|
+
def == (other)
|
5
|
+
self.class == other.class && self.attributes == other.attributes
|
6
|
+
end
|
7
|
+
|
8
|
+
def attributes
|
9
|
+
Hash[instance_variables.map do |attribute|
|
10
|
+
[attribute, instance_variable_get(attribute)]
|
11
|
+
end]
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'redcarpet'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Marksman
|
6
|
+
class Parser
|
7
|
+
|
8
|
+
def parse(file)
|
9
|
+
metadata, markdown = load_file(file)
|
10
|
+
slides = split_slides(markdown).map do |slide|
|
11
|
+
sections = separate_presenter_notes(slide)
|
12
|
+
Slide.new(
|
13
|
+
slide: generate_html(sections.slide).strip,
|
14
|
+
notes: generate_html(sections.notes).strip
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
Presentation.new(filename: File.basename(file), metadata: metadata, slides: slides)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def load_file(file)
|
24
|
+
content = File.read(file)
|
25
|
+
if content.match /^(---\s*\n.*?\n?)^(?:---\s*$\n?)(.*)/m
|
26
|
+
[parse_yaml($1), $2]
|
27
|
+
else
|
28
|
+
[{}, content]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def parse_yaml(data)
|
33
|
+
yaml = YAML.load(data)
|
34
|
+
yaml.keys.each do |key|
|
35
|
+
yaml[key.to_sym] = yaml.delete key
|
36
|
+
end
|
37
|
+
yaml
|
38
|
+
end
|
39
|
+
|
40
|
+
def separate_presenter_notes(slide)
|
41
|
+
sections = slide.split(/^(?:\*{1}\s?){3,}\s*$/).each(&:strip)
|
42
|
+
Slide.new(slide: sections[0], notes: sections[1])
|
43
|
+
end
|
44
|
+
|
45
|
+
def split_slides(markdown)
|
46
|
+
slides = markdown.split(/^(?:[-]{1}\s?){3,}\s*$/).each(&:strip)
|
47
|
+
end
|
48
|
+
|
49
|
+
def generate_html(slide)
|
50
|
+
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML,{fenced_code_blocks: true})
|
51
|
+
markdown.render(slide || '')
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Marksman
|
2
|
+
class Presentation
|
3
|
+
include Equality
|
4
|
+
attr_accessor :filename, :metadata, :slides, :theme
|
5
|
+
|
6
|
+
def initialize(options)
|
7
|
+
@filename = options[:filename]
|
8
|
+
@slides = options[:slides] || []
|
9
|
+
@metadata = defaults(filename).merge(options[:metadata] || {})
|
10
|
+
@theme = Theme.new @metadata[:theme]
|
11
|
+
end
|
12
|
+
|
13
|
+
def title
|
14
|
+
@metadata[:title]
|
15
|
+
end
|
16
|
+
|
17
|
+
def defaults(filename)
|
18
|
+
{
|
19
|
+
filename: filename,
|
20
|
+
title: filename,
|
21
|
+
theme: 'plain'
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Marksman
|
2
|
+
class Theme
|
3
|
+
include Equality
|
4
|
+
|
5
|
+
def initialize(name)
|
6
|
+
@name = name
|
7
|
+
end
|
8
|
+
|
9
|
+
def path
|
10
|
+
@path ||= if File.directory?(@name)
|
11
|
+
Pathname.new @name
|
12
|
+
else
|
13
|
+
Pathname.new(File.dirname(__FILE__))
|
14
|
+
.join('../../')
|
15
|
+
.join('themes')
|
16
|
+
.join(@name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'listen'
|
2
|
+
|
3
|
+
module Marksman
|
4
|
+
class Watcher
|
5
|
+
|
6
|
+
def initialize(file, output_directory, theme_name = nil)
|
7
|
+
@file = Pathname.new file
|
8
|
+
@output_directory = output_directory
|
9
|
+
@theme_name = theme_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def watch
|
13
|
+
update(true)
|
14
|
+
listen
|
15
|
+
end
|
16
|
+
|
17
|
+
def update(start_listener = false)
|
18
|
+
puts 'Updating files...'
|
19
|
+
presentation = Marksman::Writer.new(@file, @output_directory, @theme_name).generate
|
20
|
+
start_theme_listener(presentation.theme) unless @theme_listener
|
21
|
+
end
|
22
|
+
|
23
|
+
def listen
|
24
|
+
puts 'Listening to changes...'
|
25
|
+
@markdown_listener = Listen.to(Pathname.new(@file).dirname) do |modified, added, removed|
|
26
|
+
if modified.include? @file.realpath.to_s
|
27
|
+
update
|
28
|
+
end
|
29
|
+
end
|
30
|
+
@markdown_listener.start
|
31
|
+
sleep
|
32
|
+
end
|
33
|
+
|
34
|
+
def start_theme_listener(theme)
|
35
|
+
@theme_listener = Listen.to(theme.path) do |modified, added, removed|
|
36
|
+
update
|
37
|
+
end
|
38
|
+
@theme_listener.start
|
39
|
+
end
|
40
|
+
|
41
|
+
def stop
|
42
|
+
@markdown_listener.stop
|
43
|
+
@theme_listener.stop if @theme_listener
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|