rhet-butler 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/bin/rhet-butler +5 -0
  2. data/default-configuration/assets/apple-touch-icon-precomposed.png +0 -0
  3. data/default-configuration/assets/apple-touch-icon.png +0 -0
  4. data/default-configuration/assets/favicon.ico +0 -0
  5. data/default-configuration/assets/fonts/opensans/v6/MTP_ySUJH_bn48VBG8sNSonF5uFdDttMLvmWuJdhhgs.ttf +0 -0
  6. data/default-configuration/assets/fonts/opensans/v6/PRmiXeptR36kaC0GEAetxi8cqLH4MEiSE0ROcU-qHOA.ttf +0 -0
  7. data/default-configuration/assets/fonts/opensans/v6/cJZKeOuBrn4kERxqtaUH3aCWcynf_cDxXwCLxiixG1c.ttf +0 -0
  8. data/default-configuration/assets/fonts/opensans/v6/xjAJXh38I15wypJXxuGMBp0EAVxt0G0biEntp43Qt6E.ttf +0 -0
  9. data/default-configuration/assets/fonts/ptsans/v5/0XxGQsSc1g4rdRdjJKZrNC3USBnSvpkopQaUR-2r7iU.ttf +0 -0
  10. data/default-configuration/assets/fonts/ptsans/v5/FUDHvzEKSJww3kCxuiAo2A.ttf +0 -0
  11. data/default-configuration/assets/fonts/ptsans/v5/PIPMHY90P7jtyjpXuZ2cLKCWcynf_cDxXwCLxiixG1c.ttf +0 -0
  12. data/default-configuration/assets/fonts/ptsans/v5/lILlYDvubYemzYzN7GbLkInF5uFdDttMLvmWuJdhhgs.ttf +0 -0
  13. data/default-configuration/assets/fonts/ptserif/v5/03aPdn7fFF3H6ngCgAlQzC3USBnSvpkopQaUR-2r7iU.ttf +0 -0
  14. data/default-configuration/assets/fonts/ptserif/v5/EgBlzoNBIHxNPCMwXaAhYPesZW2xOQ-xsNqO47m55DA.ttf +0 -0
  15. data/default-configuration/assets/fonts/ptserif/v5/Foydq9xJp--nfYIx2TBz9fEr6Hm6RMS0v1dtXsGir4g.ttf +0 -0
  16. data/default-configuration/assets/fonts/ptserif/v5/QABk9IxT-LFTJ_dQzv7xpJ0EAVxt0G0biEntp43Qt6E.ttf +0 -0
  17. data/default-configuration/assets/javascript/highlight.js/LICENSE +24 -0
  18. data/default-configuration/assets/javascript/highlight.js/README.md +143 -0
  19. data/default-configuration/assets/javascript/highlight.js/README.ru.md +149 -0
  20. data/default-configuration/assets/javascript/highlight.js/classref.txt +568 -0
  21. data/default-configuration/assets/javascript/highlight.pack.js +1 -0
  22. data/default-configuration/assets/javascript/impress.js +800 -0
  23. data/default-configuration/assets/javascript/sockjs-0.2.1.js +2014 -0
  24. data/default-configuration/assets/javascript/sockjs-0.3.js +2314 -0
  25. data/default-configuration/assets/rhet-butler.jpg +0 -0
  26. data/default-configuration/assets/stylesheets/google-open-sans.css +72 -0
  27. data/default-configuration/assets/stylesheets/highlight/solarized_dark.css +88 -0
  28. data/default-configuration/assets/stylesheets/presentation.css +483 -0
  29. data/default-configuration/assets/stylesheets/presenter/presentation.css +477 -0
  30. data/default-configuration/assets/stylesheets/setup.css +0 -0
  31. data/default-configuration/common/config.yaml +4 -0
  32. data/default-configuration/common/templates/header-javascript.html +3 -0
  33. data/default-configuration/common/templates/presentation.html.erb +44 -0
  34. data/default-configuration/common/templates/presenter-qr.html.erb +77 -0
  35. data/default-configuration/common/templates/stylesheets.html.erb +3 -0
  36. data/default-configuration/presenter/config.yaml +7 -0
  37. data/default-configuration/presenter/templates/live-javascript.html.erb +26 -0
  38. data/default-configuration/presenter/templates/slide-notes.html.erb +3 -0
  39. data/default-configuration/presenter/templates/stylesheets.html.erb +3 -0
  40. data/default-configuration/viewer/config.yaml +7 -0
  41. data/default-configuration/viewer/templates/live-javascript.html.erb +16 -0
  42. data/default-configuration/viewer/templates/slide-notes.html.erb +0 -0
  43. data/lib/rhet-butler/arrangement.rb +78 -0
  44. data/lib/rhet-butler/command-line.rb +49 -0
  45. data/lib/rhet-butler/configuration.rb +76 -0
  46. data/lib/rhet-butler/file-manager.rb +126 -0
  47. data/lib/rhet-butler/html-generator.rb +29 -0
  48. data/lib/rhet-butler/layout-rule.rb +57 -0
  49. data/lib/rhet-butler/messaging.rb +58 -0
  50. data/lib/rhet-butler/slide-group.rb +56 -0
  51. data/lib/rhet-butler/slide-includer.rb +34 -0
  52. data/lib/rhet-butler/slide-loader.rb +361 -0
  53. data/lib/rhet-butler/slide.rb +164 -0
  54. data/lib/rhet-butler/static-generator.rb +44 -0
  55. data/lib/rhet-butler/web/assets-app.rb +40 -0
  56. data/lib/rhet-butler/web/main-app.rb +139 -0
  57. data/lib/rhet-butler/web/presentation-app.rb +50 -0
  58. data/lib/rhet-butler/web/qr-display-app.rb +37 -0
  59. data/lib/rhet-butler/yaml-schema.rb +9 -0
  60. data/lib/rhet-butler/yaml-type.rb +23 -0
  61. data/lib/rhet-butler.rb +2 -0
  62. data/spec/arrangements.rb +76 -0
  63. data/spec/html-generation.rb +48 -0
  64. data/spec/presentation-view.rb +72 -0
  65. data/spec/rhet-butler.rb +59 -0
  66. data/spec/slide-loader.rb +51 -0
  67. data/spec/slide-processing.rb +73 -0
  68. data/spec_support/gem_test_suite.rb +17 -0
  69. metadata +324 -0
@@ -0,0 +1,78 @@
1
+ module RhetButler
2
+ class Arrangement
3
+ include Enumerable
4
+
5
+ def initialize(*args)
6
+ @slides = []
7
+ @slide_width = 1000
8
+ @slide_height = 1000
9
+ end
10
+
11
+ attr_accessor :slides, :slide_width, :slide_height
12
+
13
+ def previous_slide
14
+ @slides.last
15
+ end
16
+
17
+ def each
18
+ if block_given?
19
+ @slides.each{|slide| yield slide}
20
+ else
21
+ @slides.each
22
+ end
23
+ end
24
+
25
+ class << self
26
+ def register(name)
27
+ Arrangement.registry[name] = self
28
+ end
29
+
30
+ def registry
31
+ @registry ||= {}
32
+ end
33
+
34
+ def [](name)
35
+ @registry[name]
36
+ end
37
+ end
38
+ end
39
+
40
+ class Linear < Arrangement
41
+ register "linear"
42
+
43
+ def initialize(plus_x, plus_y)
44
+ super
45
+ @plus_x = plus_x
46
+ @plus_y = plus_y
47
+ end
48
+
49
+ def arrange(slideset)
50
+ slide = slideset.current_slide
51
+ slide.position.x = slideset.previous_slide.position.x + @plus_x
52
+ slide.position.y = slideset.previous_slide.position.y + @plus_y
53
+ return slide
54
+ end
55
+ end
56
+
57
+ class LinearDigression < Linear
58
+ register "digress-linear"
59
+ register "linear-digress"
60
+
61
+ def previous_slide
62
+ @preceeding
63
+ end
64
+
65
+ def arrange(slideset)
66
+ @preceeding ||= slideset.previous_slide
67
+ super
68
+ end
69
+ end
70
+
71
+ class Horizontal < Linear
72
+ register "horizontal"
73
+
74
+ def initialize
75
+ super(1000, 0)
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,49 @@
1
+ require 'thor'
2
+ require 'valise'
3
+ require 'rhet-butler/configuration'
4
+ require 'rhet-butler/file-manager'
5
+
6
+ module RhetButler
7
+ class CommandLine < ::Thor
8
+ def self.shared_options
9
+ method_option :sources, :type => :array
10
+ method_option :root_slide, :type => :string
11
+ end
12
+
13
+ desc "static", "Builds a static version of the presentation"
14
+ method_option :target, :type => :string
15
+ shared_options
16
+ def static
17
+ require 'rhet-butler/static-generator'
18
+
19
+ file_manager = FileManager.new(options)
20
+ generator = StaticGenerator.new(file_manager)
21
+
22
+ generator.go!
23
+ end
24
+
25
+ desc "check", "Load slide set to check syntax"
26
+ shared_options
27
+ def check
28
+ require 'rhet-butler/web/main-app'
29
+
30
+ file_manager = FileManager.new(options)
31
+ app = Web::MainApp.new(file_manager)
32
+
33
+ say "Slides loaded and parsed"
34
+ say " #{app.viewer_app.slides.length} slides loaded"
35
+ say " Serving slides and assets found in: #{app.slides}"
36
+ end
37
+
38
+ desc "serve", "Run the presentation server"
39
+ shared_options
40
+ def serve
41
+ require 'rhet-butler/web/main-app'
42
+
43
+ file_manager = FileManager.new(options)
44
+ app = Web::MainApp.new(file_manager)
45
+ app.check
46
+ app.start
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,76 @@
1
+ module RhetButler
2
+ #This class is used to manage application config throughout. Basically it
3
+ #wraps a hash loaded from the base fileset. Since the file search path can be
4
+ #configured from there, it would be overly complex to allow config.yaml files
5
+ #in a configured source path.
6
+ #
7
+ #Also, n.b. all access to configuration is through methods on this class, so
8
+ #it's easy to see what values are allowed
9
+ class Configuration
10
+ def initialize(files, overrides=nil)
11
+ @base_hash =
12
+ begin
13
+ files.find("config.yaml").contents rescue {}
14
+ rescue Object
15
+ {}
16
+ end
17
+ @base_hash.merge!(overrides) unless overrides.nil?
18
+ end
19
+
20
+ def root_slide_template
21
+ @base_hash['root_slide_template'] || 'presentation.html'
22
+ end
23
+
24
+ def username
25
+ @base_hash['username'] || 'judson'
26
+ end
27
+
28
+ def password
29
+ @base_hash['password'] || 'judsonr00tzme'
30
+ end
31
+
32
+ def author
33
+ @base_hash['author_name'] || "Judson Lester"
34
+ end
35
+
36
+ def title
37
+ @base_hash['presentation_title'] || 'Presentation'
38
+ end
39
+
40
+ def description
41
+ @base_hash['presentation_description'] || "A nifty presentation made with Rhet Butler"
42
+ end
43
+
44
+ def search_paths
45
+ @base_hash["sources"] || []
46
+ end
47
+
48
+ def static_target
49
+ @base_hash["static_target"] || "static"
50
+ end
51
+
52
+ def impress_config
53
+ @base_hash['impress-config'] || {}
54
+ end
55
+
56
+ def root_arrangement
57
+ @base_hash["arrangement"] || "horizontal"
58
+ end
59
+
60
+ def arrangement_blueprint
61
+ @base_hash["blueprint"] || {}
62
+ end
63
+
64
+ def serve_port
65
+ @base_hash["serve_port"] || 8081
66
+ end
67
+
68
+ def root_slide
69
+ @base_hash["root_slide"] || "slides.yaml"
70
+ end
71
+
72
+ def default_slide_type
73
+ @base_hash["default_slide_type"] || "textile"
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,126 @@
1
+ require 'valise'
2
+ require 'rhet-butler/configuration'
3
+
4
+ module RhetButler
5
+ #All file handling is routed through this class.
6
+ #
7
+ #Basic configuration (including additional search paths for other configs and
8
+ #slides) can only come from the current directory or the "basic search path":
9
+ #* .rhet/
10
+ #* ~/.rhet/
11
+ #* /usr/share/rhet-butler/
12
+ #* /etc/rhet-butler/
13
+ #[the defaults provided by the gem]
14
+ #
15
+ #Other paths configured by --sources or sources: [] in base configs are added
16
+ #after the current directory and before that list.
17
+ #
18
+ #There are several subdirectories searched in these search paths, depending
19
+ #on context:
20
+ #
21
+ #* presenter/
22
+ #* viewer/
23
+ #* common/
24
+ #
25
+ #Presenter and viewer files are used for displaying the appropriate
26
+ #presentation, so that the presenter can have a different display (e.g. with
27
+ #notes and a timer) than the audience (e.g. nifty transitions etc.)
28
+ #
29
+ #Slide files are loaded without a sub-directory - they're always the same
30
+ #regardless of context.
31
+ #
32
+ #Presenter configs, templates, and assets are searched for in presenter, then
33
+ #common.
34
+ #
35
+ #Audience configs, templates and assets are searched for in viewer, then
36
+ #common.
37
+ #
38
+ class FileManager
39
+ def initialize(overrides = nil)
40
+ @overrides = overrides || {}
41
+ @cached_configs = {}
42
+ @cached_templates = {}
43
+ end
44
+
45
+ def base_config
46
+ @base_config ||= load_config(base_config_search_path)
47
+ end
48
+
49
+ def slide_files
50
+ all_files.sub_set("slides") + all_files
51
+ end
52
+
53
+ def base_assets
54
+ all_files.templates("assets")
55
+ end
56
+
57
+ def aspect_config(aspect_name)
58
+ @cached_configs[aspect_name] ||= load_config(aspect_search_path(aspect_name))
59
+ end
60
+
61
+ def aspect_templates(aspect)
62
+ @cached_templates[aspect] ||= aspect_search_path(aspect).templates
63
+ end
64
+
65
+
66
+ def load_config(files)
67
+ Configuration.new(files, @overrides)
68
+ end
69
+
70
+ def current_directory
71
+ Valise::Set.define do
72
+ rw "."
73
+ handle "config.yaml", :yaml, :hash_merge
74
+ end
75
+ end
76
+
77
+ def configured_search_path
78
+ base_config = self.base_config
79
+ Valise::Set.define do
80
+ base_config.search_paths.each do |path|
81
+ rw path
82
+ end
83
+ end
84
+ end
85
+
86
+ def base_config_set
87
+ @base_config_set ||=
88
+ Valise::Set.define do
89
+ rw [".rhet"]
90
+ rw ["~", ".rhet"]
91
+ rw ["", "usr", "share", "rhet-butler"]
92
+ rw ["", "etc", "rhet-butler"]
93
+ ro from_here(["..", "..", "..", "default-configuration"])
94
+
95
+ handle "config.yaml", :yaml, :hash_merge
96
+ end
97
+ end
98
+
99
+ def target_valise
100
+ @target_valise ||=
101
+ begin
102
+ target_directory = base_config.static_target
103
+ Valise::define do
104
+ rw target_directory
105
+ end
106
+ end
107
+ end
108
+
109
+ def all_files
110
+ current_directory + configured_search_path + base_config_set
111
+ end
112
+
113
+ def base_config_search_path
114
+ set = current_directory + base_config_set
115
+ set + set.sub_set("common")
116
+ end
117
+
118
+ def aspect_search_path(aspect)
119
+ aspect = aspect.to_s
120
+ set = all_files.sub_set(aspect)
121
+ set += all_files.sub_set("common")
122
+ set += all_files
123
+ return set
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,29 @@
1
+ require 'tilt'
2
+ require 'valise'
3
+
4
+ module RhetButler
5
+ class HTMLGenerator
6
+ class Presentation
7
+ def initialize(configuration)
8
+ @author_name = configuration.author
9
+ @title = configuration.title
10
+ @description = configuration.description
11
+ end
12
+
13
+ attr_accessor :author_name, :title, :description
14
+ end
15
+
16
+ def initialize(configuration, templates)
17
+ @impress_config = configuration.impress_config
18
+ @templates = templates
19
+ @slides = []
20
+ @presentation = Presentation.new(configuration)
21
+ end
22
+
23
+ attr_accessor :slides, :presentation, :impress_config
24
+
25
+ def render(path, scope=nil, locals=nil)
26
+ @templates.find(path).contents.render(scope || self, locals || {})
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,57 @@
1
+ require 'rhet-butler/yaml-type'
2
+
3
+ module RhetButler
4
+ class LayoutRule
5
+ include YamlType
6
+ def initialize
7
+ @match = {}
8
+ @layout_args = nil
9
+ end
10
+ attr_accessor :match, :layout_type, :layout_args
11
+
12
+ def self.optional_config
13
+ %w[match]
14
+ end
15
+
16
+ def self.required_config
17
+ %w[layout]
18
+ end
19
+
20
+ def init_with(coder)
21
+ @config_hash =
22
+ case coder.type
23
+ when :seq
24
+ { 'match' => coder.seq[0], 'layout' => coder.seq[1] }
25
+ when :map
26
+ coder.map
27
+ else
28
+ raise "Tried to configure a layout rule with non-sequence: #{coder.inspect}"
29
+ end
30
+
31
+ check_config_hash(@config_hash)
32
+
33
+ value_from_config("match") do |value|
34
+ if value == 'default'
35
+ @match = {}
36
+ else
37
+ @match = value
38
+ end
39
+ end
40
+
41
+ @layout_type, @layout_args = *@config_hash['layout']
42
+ end
43
+
44
+ def layout
45
+ Arrangement[@layout_type].new(*(@layout_args||[]))
46
+ end
47
+
48
+ def match?(group)
49
+ @match.all? do |key, value|
50
+ target = group.metadata.fetch(key)
51
+ value === target
52
+ end
53
+ rescue KeyError
54
+ false
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,58 @@
1
+ require 'thin'
2
+ require 'rack/sockjs'
3
+
4
+ module RhetButler
5
+ class FollowerSession < SockJS::Session
6
+ def initialize(connection)
7
+ super
8
+ @queue = connection.options[:queue]
9
+ end
10
+
11
+ def opened
12
+ @queue.subscribe(self)
13
+ end
14
+
15
+ def close(*args)
16
+ @queue.unsubscribe(self)
17
+ super
18
+ end
19
+ end
20
+
21
+ class LeaderSession < SockJS::Session
22
+ def initialize(connection)
23
+ super
24
+ @queue = connection.options[:queue]
25
+ end
26
+
27
+ def process_message(message)
28
+ @queue.current_slide = message
29
+ @queue.enqueue(message)
30
+ end
31
+ end
32
+
33
+ class SlideMessageQueue
34
+ attr_accessor :current_slide
35
+ def initialize
36
+ @listeners = {}
37
+ end
38
+
39
+ def inspect
40
+ "<<#{self.class.name} Listeners: #{@listeners.keys.length}>>"
41
+ end
42
+
43
+ def subscribe(session)
44
+ @listeners[session] = true
45
+ session.send(current_slide) unless current_slide.nil?
46
+ end
47
+
48
+ def unsubscribe(session)
49
+ @listeners.delete_key(session)
50
+ end
51
+
52
+ def enqueue(message)
53
+ @listeners.keys.each do |session|
54
+ session.send(message)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,56 @@
1
+ require 'rhet-butler/yaml-type'
2
+
3
+ module RhetButler
4
+ class SlideGroup
5
+ include YamlType
6
+ include Enumerable
7
+
8
+ class <<self
9
+ def optional_config
10
+ %w[type slide_type]
11
+ end
12
+
13
+ def required_config
14
+ %w[slides]
15
+ end
16
+ end
17
+
18
+
19
+ def setup_defaults
20
+ end
21
+
22
+ def initialize
23
+ @slides = []
24
+ @metadata = {}
25
+ end
26
+ attr_accessor :slides
27
+ attr_reader :metadata
28
+
29
+ def each
30
+ if block_given?
31
+ @slides.each{|slide| yield(slide)}
32
+ else
33
+ @slides.each
34
+ end
35
+ end
36
+
37
+ def init_with(coder)
38
+ setup_defaults
39
+
40
+ @config_hash =
41
+ case coder.type
42
+ when :map
43
+ coder.map
44
+ when :scalar
45
+ raise "A slide group needs to at least be a list of slides"
46
+ when :seq
47
+ { 'slides' => coder.seq}
48
+ end
49
+
50
+ check_config_hash(@config_hash)
51
+
52
+ @slides = @config_hash.delete('slides')
53
+ @metadata = @config_hash
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,34 @@
1
+ module RhetButler
2
+ class Includer
3
+ include Enumerable
4
+
5
+
6
+ def initialize
7
+ @slides = []
8
+ end
9
+
10
+ def init_with(coder)
11
+ unless coder.type == :scalar
12
+ raise "!include with non-scalar - use a path string"
13
+ end
14
+ @path = coder.scalar
15
+ end
16
+
17
+ attr_accessor :path
18
+ attr_reader :slides
19
+
20
+ def each
21
+ if block_given?
22
+ @slides.each{|slide| yield slide}
23
+ else
24
+ @slides.each
25
+ end
26
+ end
27
+
28
+ def load(loader)
29
+ loader = loader.dup
30
+ @slides = loader.load_file(path)
31
+ loader
32
+ end
33
+ end
34
+ end