marksman 0.0.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +23 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +7 -0
  6. data/Guardfile +7 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +80 -0
  9. data/Rakefile +14 -0
  10. data/bin/marksman +8 -0
  11. data/lib/marksman.rb +13 -0
  12. data/lib/marksman/cli.rb +29 -0
  13. data/lib/marksman/converter.rb +50 -0
  14. data/lib/marksman/equality.rb +15 -0
  15. data/lib/marksman/parser.rb +55 -0
  16. data/lib/marksman/presentation.rb +26 -0
  17. data/lib/marksman/slide.rb +12 -0
  18. data/lib/marksman/theme.rb +21 -0
  19. data/lib/marksman/version.rb +3 -0
  20. data/lib/marksman/watcher.rb +47 -0
  21. data/lib/marksman/writer.rb +18 -0
  22. data/marksman.gemspec +32 -0
  23. data/spec/data/example.md +11 -0
  24. data/spec/data/example_without_metadata.md +7 -0
  25. data/spec/data/output/index.html +44 -0
  26. data/spec/marksman/cli_spec.rb +26 -0
  27. data/spec/marksman/converter_spec.rb +40 -0
  28. data/spec/marksman/parser_spec.rb +64 -0
  29. data/spec/marksman/presentation_spec.rb +25 -0
  30. data/spec/marksman/slide_spec.rb +17 -0
  31. data/spec/marksman/theme_spec.rb +29 -0
  32. data/spec/marksman/watcher_spec.rb +45 -0
  33. data/spec/marksman/writer_spec.rb +15 -0
  34. data/spec/spec_helper.rb +12 -0
  35. data/themes/blank/css/blank.css +5 -0
  36. data/themes/blank/css/tomorrow-night.css +93 -0
  37. data/themes/blank/index.haml +21 -0
  38. data/themes/blank/js/highlight.min.js +1 -0
  39. data/themes/plain/css/default.css +42 -0
  40. data/themes/plain/css/github.css +125 -0
  41. data/themes/plain/css/tomorrow-night.css +93 -0
  42. data/themes/plain/css/tomorrow.css +90 -0
  43. data/themes/plain/index.haml +18 -0
  44. data/themes/plain/js/highlight.min.js +1 -0
  45. metadata +210 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f48ad2830561d8e10bec72c0d97ad5c9372a4973
4
- data.tar.gz: f96caf8be940cdd93133ef9974dfa0c58398877b
3
+ metadata.gz: 057ad0bdc1acdca0c3c4683862837088232521df
4
+ data.tar.gz: a105f581953f1c52d9416dda50c63fca60c0b82c
5
5
  SHA512:
6
- metadata.gz: e03abe1e627443dcb4374720a2cdf532cf3318dc7eab8af71989fc48295db35554b9833922c8e7abb0368b5d9cb79fb1e7e620ab0a63961925725a28c4f74b9f
7
- data.tar.gz: 850c31fc8806187d3eb88f703dbb1c0f4f444038ac61b4a92d1a5037f9f9e44e55eff1bf923b711c4f2fad061cc9975b26f4c38616bf509154b7b97f9efeb04e
6
+ metadata.gz: 7c355dbbc9670e5fa05583a04f4b3caecae926d318a8a6a2379f4d4d09a296a95ac2afa08e9b5ec90fb53f29263236607c0ac33d23087865246471772cafa11d
7
+ data.tar.gz: 90d00af80c4ba2a6873250d568150c53088678a3132898be165d3ca18e88e8e7d342fc83854d5fe36b5b36a9f3723d1537db45fb789e5dd9d0da3f3c77bbdea4
@@ -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
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ script: "bundle exec rake spec"
5
+ cache: bundler
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in marksman.gemspec
4
+ gemspec
5
+
6
+ gem 'rake'
7
+ gem 'coveralls', :require => false, group: :test
@@ -0,0 +1,7 @@
1
+
2
+ guard :rspec do
3
+ watch(%r{^spec/.+_spec\.rb$})
4
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
5
+ watch('spec/spec_helper.rb') { "spec" }
6
+ end
7
+
@@ -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.
@@ -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
@@ -0,0 +1,14 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+ require "pry"
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ task :default => :spec
7
+
8
+ task :console do
9
+ require 'pry'
10
+ require 'marksman'
11
+ ARGV.clear
12
+ Pry.start
13
+ end
14
+
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ require 'rubygems'
4
+ require 'bundler/setup'
5
+
6
+ require 'marksman'
7
+
8
+ Marksman::CLI.start(ARGV)
@@ -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
@@ -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,12 @@
1
+ module Marksman
2
+ class Slide
3
+ include Equality
4
+ attr_accessor :slide, :notes
5
+
6
+ def initialize(options)
7
+ @slide = options[:slide]
8
+ @notes = options[:notes]
9
+ end
10
+
11
+ end
12
+ 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,3 @@
1
+ module Marksman
2
+ VERSION = '0.1'
3
+ 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