marksman 0.0.0 → 0.1

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