deckrb 0.1.0 → 0.1.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 (81) hide show
  1. data/README.md +55 -20
  2. data/bin/deck +5 -10
  3. data/lib/deck.rb +1 -1
  4. data/lib/deck/noko.rb +15 -0
  5. data/lib/deck/rack_app.rb +61 -0
  6. data/lib/deck/slide.rb +50 -28
  7. data/lib/deck/{deck.rb → slide_deck.rb} +42 -41
  8. data/lib/deck/version.rb +1 -1
  9. data/spec/rack_app_spec.rb +177 -0
  10. data/spec/slide_deck_spec.rb +46 -0
  11. data/spec/slide_spec.rb +24 -21
  12. data/spec/spec_helper.rb +5 -0
  13. metadata +51 -81
  14. data/deck/GPL-license.txt +0 -278
  15. data/deck/MIT-license.txt +0 -21
  16. data/deck/README.md +0 -57
  17. data/deck/core/deck.core.css +0 -404
  18. data/deck/core/deck.core.html +0 -39
  19. data/deck/core/deck.core.js +0 -498
  20. data/deck/core/deck.core.scss +0 -447
  21. data/deck/extensions/goto/deck.goto.css +0 -41
  22. data/deck/extensions/goto/deck.goto.html +0 -7
  23. data/deck/extensions/goto/deck.goto.js +0 -134
  24. data/deck/extensions/goto/deck.goto.scss +0 -46
  25. data/deck/extensions/hash/deck.hash.css +0 -13
  26. data/deck/extensions/hash/deck.hash.html +0 -2
  27. data/deck/extensions/hash/deck.hash.js +0 -129
  28. data/deck/extensions/hash/deck.hash.scss +0 -15
  29. data/deck/extensions/menu/deck.menu.css +0 -47
  30. data/deck/extensions/menu/deck.menu.js +0 -187
  31. data/deck/extensions/menu/deck.menu.scss +0 -58
  32. data/deck/extensions/navigation/deck.navigation.css +0 -43
  33. data/deck/extensions/navigation/deck.navigation.html +0 -3
  34. data/deck/extensions/navigation/deck.navigation.js +0 -91
  35. data/deck/extensions/navigation/deck.navigation.scss +0 -56
  36. data/deck/extensions/scale/deck.scale.css +0 -16
  37. data/deck/extensions/scale/deck.scale.js +0 -155
  38. data/deck/extensions/scale/deck.scale.scss +0 -17
  39. data/deck/extensions/status/deck.status.css +0 -18
  40. data/deck/extensions/status/deck.status.html +0 -6
  41. data/deck/extensions/status/deck.status.js +0 -95
  42. data/deck/extensions/status/deck.status.scss +0 -22
  43. data/deck/extensions/theme-picker/deck.theme-picker.css +0 -55
  44. data/deck/extensions/theme-picker/deck.theme-picker.js +0 -13
  45. data/deck/introduction/index.html +0 -221
  46. data/deck/introduction/index.rb +0 -101
  47. data/deck/jquery-1.7.min.js +0 -4
  48. data/deck/modernizr.custom.js +0 -4
  49. data/deck/test/fixtures/complex.html +0 -24
  50. data/deck/test/fixtures/empty.html +0 -19
  51. data/deck/test/fixtures/iframe_simple.html +0 -10
  52. data/deck/test/fixtures/iframes.html +0 -32
  53. data/deck/test/fixtures/nesteds.html +0 -36
  54. data/deck/test/fixtures/standard.html +0 -42
  55. data/deck/test/index.html +0 -39
  56. data/deck/test/lib/jasmine-html.js +0 -190
  57. data/deck/test/lib/jasmine-jquery.js +0 -288
  58. data/deck/test/lib/jasmine.css +0 -166
  59. data/deck/test/lib/jasmine.js +0 -2477
  60. data/deck/test/settings.js +0 -3
  61. data/deck/test/spec.core.js +0 -434
  62. data/deck/test/spec.goto.js +0 -119
  63. data/deck/test/spec.hash.js +0 -81
  64. data/deck/test/spec.menu.js +0 -66
  65. data/deck/test/spec.navigation.js +0 -51
  66. data/deck/test/spec.scale.js +0 -57
  67. data/deck/test/spec.status.js +0 -58
  68. data/deck/themes/style/neon.css +0 -114
  69. data/deck/themes/style/neon.scss +0 -139
  70. data/deck/themes/style/swiss.css +0 -75
  71. data/deck/themes/style/swiss.scss +0 -91
  72. data/deck/themes/style/web-2.0.css +0 -205
  73. data/deck/themes/style/web-2.0.scss +0 -236
  74. data/deck/themes/transition/fade.css +0 -44
  75. data/deck/themes/transition/fade.scss +0 -70
  76. data/deck/themes/transition/horizontal-slide.css +0 -79
  77. data/deck/themes/transition/horizontal-slide.scss +0 -94
  78. data/deck/themes/transition/vertical-slide.css +0 -97
  79. data/deck/themes/transition/vertical-slide.scss +0 -116
  80. data/lib/deck/app.rb +0 -16
  81. data/spec/deck_spec.rb +0 -98
data/README.md CHANGED
@@ -11,6 +11,13 @@
11
11
  * Erector
12
12
  * Markdown
13
13
  * presentations can comprise several source files
14
+ * source files look good as source, built HTML, preview HTML, or as a deck doc
15
+ * slide directives look like comments when rendered into HTML (e.g. as <!SLIDE>)
16
+ * links to auxiliary files (e.g. `img src`) are resolved relative to the source file
17
+ * generated HTML is pretty-printed
18
+ * uses RedCarpet markdown extensions, including
19
+ * tables <http://michelf.com/projects/php-markdown/extra/#table>
20
+ * fenced code blocks <http://michelf.com/projects/php-markdown/extra/#fenced-code-blocks>
14
21
 
15
22
  ## Command-Line API (proposed)
16
23
 
@@ -18,7 +25,7 @@
18
25
 
19
26
  * start a local Rack server (probably Sinatra) on port 4333
20
27
  * http://localhost:4333/ serves the presentation in foo.md
21
- * can also specify multiple source files in a row
28
+ * can specify multiple source files in a row
22
29
 
23
30
  `deck build foo.md`
24
31
 
@@ -29,35 +36,63 @@
29
36
 
30
37
  `deck deploy` ???
31
38
 
32
- * some way to build/rebuild a project that is deployable to heroky
33
-
39
+ * some way to build/rebuild a project that is deployable to heroku
34
40
 
35
41
  ### Options
36
42
 
37
- * --output dir
38
- * --config deck.json
39
- * --port portnum
40
- * --theme themename
43
+ * none yet :-)
41
44
 
42
45
  ## Credits
43
46
 
44
- * deck.js by Caleb at http://imakewebthings.com
45
- * deck.rb by Alex Chaffee http://alexchaffee.com, with help from
46
- * Steven! Ragnarök @nuclearsandwich
47
+ * deck.js by Caleb at <http://imakewebthings.com>
48
+ * deck.rb by Alex Chaffee <http://alexchaffee.com>, with help from
49
+ * Steven! Ragnarök <http://nuclearsandwich.com>
50
+
51
+ ### See Also
52
+
47
53
  * showoff by Scott Chacon
54
+ * keydown by Davis Frank
55
+
56
+ ## Bugs and Limitations
57
+
58
+ * auxiliary files are interleaved in URL path space, so overlapping file names might not resolve to the right file
59
+ * H1s are converted to H2s for compatibility with deck.js's CSS
60
+ * unless they're the only item on the slide, in which case they remain H1s
48
61
 
49
62
  ## TODO
50
63
 
51
- * markdown
52
- * multiple files
53
- * rack app
64
+ * config file
54
65
  * config:
55
- * show theme selector
56
- * show page number/nav
66
+ * show/hide theme selector
67
+ * show/hide page number/nav
57
68
  * choose deck extensions
58
- * slim
59
- * haml
69
+ * command-line tool can take a directory
70
+ * first pass: globs all *.md files in it
71
+ * command-line options:
72
+ * --output dir
73
+ * --config deck.json
74
+ * --port portnum
75
+ * --theme themename
76
+ * more slide file types
77
+ * html
78
+ * slim http://slim-lang.com/index.html
79
+ * haml
80
+ * tilt
60
81
  * specify Redcarpet Markdown extensions
61
- * syntax highlighting (using sh (js) or coderay (rack)?)
62
- * option to render all JS and CSS inline, for a self-contained HTML doc (and maybe images too)
63
-
82
+ * option to render all JS and CSS inline, for a self-contained HTML doc
83
+ * and maybe images too, base64-encoded
84
+ * image scaling
85
+ * build and push into a gh-pages branch
86
+ * build and push into a heroku app
87
+ * find any lines that start with a <p>.(something) and turn them into <p class="something">
88
+ * see showoff.rb:189
89
+ * fix title tag (base it off of presentation name or something)
90
+
91
+ ## TODO (community)
92
+
93
+ * submit theme-picker extension to deck.js
94
+ * add to deck.js wiki https://github.com/imakewebthings/deck.js/wiki
95
+ * announce on https://groups.google.com/forum/#!forum/webslideshow
96
+ * mix with keydown https://github.com/infews/keydown
97
+ * gh-pages documentation site
98
+ * integrate with slideshow https://github.com/geraldb/slideshow-deck.js
data/bin/deck CHANGED
@@ -1,12 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  here = File.expand_path(File.dirname(__FILE__))
4
- root = File.join(here, '..')
5
4
  $: << File.join(here, '..', 'lib')
6
5
 
7
6
  require "rack"
8
7
  require "deck"
9
- require "deck/app"
8
+ require "deck/rack_app"
10
9
 
11
10
  command = ARGV.shift
12
11
 
@@ -26,7 +25,7 @@ when 'build'
26
25
  end
27
26
 
28
27
  File.open(output_path, "w") do |file|
29
- deck = Deck.new :slides => slides
28
+ deck = SlideDeck.new :slides => slides
30
29
  # deck.to_pretty(:output => file) # todo: figure out why this doesn't work
31
30
  file.write deck.to_pretty
32
31
  end
@@ -36,14 +35,10 @@ when 'run'
36
35
  # Fix Rack bug https://github.com/rack/rack/issues/301
37
36
  require 'deck/rack_static_patch'
38
37
 
38
+
39
39
  port = 4333 # todo: option
40
- app = Rack::Builder.app do
41
- use Rack::ShowExceptions
42
- use Rack::ShowStatus
43
- use Rack::Static, :urls => ["/deck"], :root => root
44
- run Deck::App.new(Dir.pwd, ARGV)
45
- end
46
- Rack::Handler.default.run app, :Port => port
40
+ slide_files = ARGV
41
+ Rack::Handler.default.run Deck::RackApp.build(slide_files), :Port => port
47
42
 
48
43
  else
49
44
  puts "Unknown command '#{command}'"
@@ -3,5 +3,5 @@ here = File.expand_path(File.dirname(__FILE__))
3
3
  module Deck
4
4
  end
5
5
 
6
- require "deck/deck"
6
+ require "deck/slide_deck"
7
7
  require "deck/slide"
@@ -0,0 +1,15 @@
1
+ require 'nokogiri'
2
+
3
+ module Deck
4
+ # Wrappers around Nokogiri's occasionally inscrutable API
5
+ module Noko
6
+ def noko_html nokogiri_node
7
+ nokogiri_node.serialize(:save_with => 0).chomp
8
+ end
9
+
10
+ def noko_doc html_snippet
11
+ html_doc = html_snippet =~ /<html/ ? html_snippet : "<html>#{html_snippet}</html>"
12
+ Nokogiri.parse(html_doc)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,61 @@
1
+ here = File.expand_path File.dirname(__FILE__)
2
+
3
+ require 'coderay'
4
+ require 'rack/codehighlighter'
5
+
6
+ module Deck
7
+ class RackApp
8
+ def self.app_root
9
+ here = File.dirname(__FILE__)
10
+ app_root = File.expand_path "#{here}/../.."
11
+ end
12
+
13
+ def self.build slide_files
14
+
15
+ if const_defined?(:Thin)
16
+ if require "thin/logging"
17
+ Thin::Logging.debug = true
18
+ end
19
+ end
20
+
21
+ Rack::Builder.app do
22
+ use Rack::ShowExceptions
23
+ use Rack::ShowStatus
24
+ use Rack::Codehighlighter, :coderay,
25
+ :element => "pre>code",
26
+ :markdown => true,
27
+ :pattern => /\A[:@]{3}\s?(\w+)\s*(\n|&#x000A;)/i
28
+ run ::Deck::RackApp.new(slide_files)
29
+ end
30
+ end
31
+
32
+ def initialize slide_files
33
+ @slide_files = [slide_files].flatten
34
+
35
+ @file_servers =
36
+ [Rack::File.new("#{::Deck::RackApp.app_root}/public")] +
37
+ @slide_files.map do |slide_file|
38
+ Rack::File.new(File.dirname slide_file)
39
+ end
40
+ end
41
+
42
+ def call env
43
+ request = Rack::Request.new(env)
44
+ if request.path == "/"
45
+ slides = []
46
+ @slide_files.each do |file|
47
+ slides += Slide.from_file file
48
+ end
49
+ deck = SlideDeck.new :slides => slides
50
+ [200, {'Content-Type' => 'text/html'}, [deck.to_pretty]]
51
+ else
52
+ result = [404, {}, []]
53
+ @file_servers.each do |file_server|
54
+ result = file_server.call(env)
55
+ return result if result.first < 400
56
+ end
57
+ result
58
+ end
59
+ end
60
+ end
61
+ end
@@ -1,10 +1,12 @@
1
1
  # based on work by Alex and others in Showoff
2
2
  require 'redcarpet'
3
-
3
+ require 'deck/noko'
4
4
 
5
5
  module Deck
6
6
  class Slide < Erector::Widget
7
7
 
8
+ include Deck::Noko
9
+
8
10
  # todo: test this method
9
11
  def self.from_file markdown_file
10
12
  split File.read(markdown_file)
@@ -12,8 +14,10 @@ module Deck
12
14
 
13
15
  # given a chunk of Markdown text, splits it into an array of Slide objects
14
16
  def self.split content
15
- unless content =~ /^\<?!SLIDE/m
16
- content = content.gsub(/^# /m, "<!SLIDE>\n# ")
17
+ unless content =~ /^\<?!SLIDE/m # this only applies to files with no !SLIDEs at all, which is odd
18
+ content = content.
19
+ gsub(/^# /m, "<!SLIDE>\n# ").
20
+ gsub(/^(.*)\n(===+)/, "<!SLIDE>\n\\1\n\\2")
17
21
  end
18
22
 
19
23
  lines = content.split("\n")
@@ -24,7 +28,7 @@ module Deck
24
28
  if line =~ /^<?!SLIDE(.*)>?/
25
29
  slides << (slide = Slide.new(:classes => $1))
26
30
 
27
- elsif line =~ /^# / and !slide.empty?
31
+ elsif (line =~ /^# / or lines.first =~ /^(===+)/) and !slide.empty?
28
32
  # every H1 defines a new slide, unless there's a !SLIDE before it
29
33
  slides << (slide = Slide.new)
30
34
  slide << line
@@ -44,15 +48,15 @@ module Deck
44
48
  attr_reader :classes, :markdown_text
45
49
 
46
50
  needs :classes => nil, :markdown_text => nil, :slide_id => nil
47
-
48
-
51
+
52
+
49
53
  def initialize options = {}
50
54
  super options
51
-
55
+
52
56
  @classes = process_classes
53
57
  @markdown_text = ""
54
58
  end
55
-
59
+
56
60
  def process_classes
57
61
  ["slide"] + case @classes
58
62
  when NilClass
@@ -64,7 +68,7 @@ module Deck
64
68
  else
65
69
  raise "can't deal with :classes => #{@classes.inspect}"
66
70
  end
67
- end
71
+ end
68
72
 
69
73
  def markdown
70
74
  @@markdown ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML,
@@ -79,42 +83,60 @@ module Deck
79
83
  :superscript => false
80
84
  )
81
85
  end
82
-
86
+
83
87
  def <<(s)
84
88
  @markdown_text << s
85
89
  @markdown_text << "\n"
86
90
  end
87
-
91
+
88
92
  def empty?
89
93
  @markdown_text.strip == ""
90
94
  end
91
-
95
+
92
96
  def slide_id
93
97
  @slide_id ||= begin
94
98
  lines = @markdown_text.split("\n")
95
99
  raise "an empty slide has no id" if lines.empty?
96
- lines.first.gsub(/^#*/, '').strip.downcase.gsub(/[^\w\s]/, '').gsub(/\s/, '_')
97
- end
98
- end
99
-
100
- def header_only?
101
- markdown_text.strip =~ /^# / and markdown_text.strip.split("\n").size == 1
102
- end
103
-
104
- def massaged_markdown_text
105
- unless header_only?
106
- "##{markdown_text.strip}"
107
- else
108
- markdown_text
100
+ lines.first.downcase.gsub(/[^\w\s]/, '').strip.gsub(/\s/, '_')
109
101
  end
110
102
  end
111
-
103
+
112
104
  def content
113
105
  section :class => @classes, :id => slide_id do
114
106
  text "\n" # markdown HTML should be left-aligned, in case of PRE blocks and other quirks
115
- html = markdown.render(massaged_markdown_text)
107
+ html = markdown.render(markdown_text)
108
+ html = munge(html)
116
109
  rawtext html
117
- end
110
+ end
118
111
  end
112
+
113
+ private
114
+
115
+ # if there is an H1, change it to an H2, unless it's the only thing there
116
+ # TODO: or unless the slide class is whatever
117
+ def mutate_h1? doc
118
+ h1s = doc.css('h1') || []
119
+ if h1s.size == 0
120
+ false
121
+ else
122
+ stuff = doc.css('body>*')
123
+ if stuff.size == 1
124
+ false
125
+ else
126
+ true
127
+ end
128
+ end
129
+ end
130
+
131
+ def munge html
132
+ doc = noko_doc(html)
133
+ if mutate_h1? doc
134
+ doc.css('h1').each {|node| node.node_name = "h2"}
135
+ doc.css('body').inner_html + "\n"
136
+ else
137
+ html
138
+ end
139
+ end
140
+
119
141
  end
120
142
  end
@@ -4,23 +4,23 @@ require 'redcarpet'
4
4
  require "deck/slide"
5
5
 
6
6
  module Deck
7
- class Deck < Erector::Widgets::Page
7
+ class SlideDeck < Erector::Widgets::Page
8
8
  needs :title => "deck.rb presentation",
9
9
  :description => nil,
10
10
  :author => nil
11
-
11
+
12
12
  needs :slides => nil
13
-
13
+
14
14
  def page_title
15
15
  @title
16
16
  end
17
-
17
+
18
18
  # todo: promote into Text
19
19
  # todo: support numbers a la '&#1234;'
20
20
  def entity entity_id
21
21
  raw("&#{entity_id};")
22
22
  end
23
-
23
+
24
24
  # left over from deck.js' introduction/index.html
25
25
 
26
26
  # <!DOCTYPE html>
@@ -34,54 +34,55 @@ module Deck
34
34
  link({:rel => "stylesheet", :href => src}.merge(attributes))
35
35
  end
36
36
 
37
+ def extensions
38
+ @extensions || [
39
+ 'goto',
40
+ 'menu',
41
+ 'navigation',
42
+ 'status',
43
+ 'hash',
44
+ 'scale',
45
+ # 'theme-picker',
46
+ ]
47
+ end
48
+
37
49
  def head_content
38
50
  super
39
51
  meta 'charset' => 'utf-8'
40
52
  meta 'http-equiv'=>"X-UA-Compatible", 'content'=>"IE=edge,chrome=1"
41
- meta :name=>"viewport", :content=>"width=1024, user-scalable=no"
53
+ meta :name => "viewport", :content=> "width=1024, user-scalable=no"
42
54
  meta :name => "description", :content=> @description if @description
43
55
  meta :name => "author", :content=> @author if @author
44
56
 
57
+ stylesheet "coderay.css"
58
+
45
59
  # <!-- Core and extension CSS files -->
46
- stylesheet "deck/core/deck.core.css"
47
-
48
- stylesheet "deck/extensions/goto/deck.goto.css"
49
- stylesheet "deck/extensions/menu/deck.menu.css"
50
- stylesheet "deck/extensions/navigation/deck.navigation.css"
51
- stylesheet "deck/extensions/status/deck.status.css"
52
- stylesheet "deck/extensions/hash/deck.hash.css"
53
- stylesheet "deck/extensions/scale/deck.scale.css"
54
-
55
- stylesheet "deck/extensions/theme-picker/deck.theme-picker.css"
56
-
60
+ stylesheet "deck.js/core/deck.core.css"
61
+ extensions.each do |extension|
62
+ stylesheet "deck.js/extensions/#{extension}/deck.#{extension}.css"
63
+ end
64
+
57
65
  # <!-- Theme CSS files (menu swaps these out) -->
58
- stylesheet "deck/themes/style/web-2.0.css", :id=>"style-theme-link"
59
- stylesheet "deck/themes/transition/horizontal-slide.css", :id => "transition-theme-link"
60
-
61
- script :src=>"deck/modernizr.custom.js"
66
+ stylesheet "deck.js/themes/style/swiss.css", :id=>"style-theme-link"
62
67
  end
63
68
 
64
69
  def scripts
70
+ script :src => "deck.js/modernizr.custom.js"
71
+
65
72
  # comment 'Grab CDN jQuery, with a protocol relative URL; fall back to local if offline'
66
73
  # script :src => '//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.min.js'
67
- script :src => './deck/jquery-1.7.min.js'
68
-
69
- comment 'Deck Core and extensions'
70
- script :type => "text/javascript", :src => 'deck/core/deck.core.js'
74
+ script :src => 'deck.js/jquery-1.7.min.js'
71
75
 
72
- script :type => "text/javascript", :src => 'deck/extensions/hash/deck.hash.js'
73
- script :type => "text/javascript", :src => 'deck/extensions/menu/deck.menu.js'
74
- script :type => "text/javascript", :src => 'deck/extensions/goto/deck.goto.js'
75
- script :type => "text/javascript", :src => 'deck/extensions/status/deck.status.js'
76
- script :type => "text/javascript", :src => 'deck/extensions/navigation/deck.navigation.js'
77
- script :type => "text/javascript", :src => 'deck/extensions/scale/deck.scale.js'
76
+ comment 'Deck Core and extensions'
77
+ script :type => "text/javascript", :src => 'deck.js/core/deck.core.js'
78
78
 
79
+ extensions.each do |extension|
80
+ script :type => "text/javascript", :src => "deck.js/extensions/#{extension}/deck.#{extension}.js"
81
+ end
79
82
 
80
83
  # fire up deck.js
81
84
  script "$(function(){$.deck('.slide');});"
82
-
83
- script :type => "text/javascript", :src => 'deck/extensions/theme-picker/deck.theme-picker.js'
84
-
85
+
85
86
  end
86
87
 
87
88
  def body_attributes
@@ -96,7 +97,7 @@ module Deck
96
97
  permalink
97
98
  scripts
98
99
  end
99
-
100
+
100
101
  def slide slide_id
101
102
  # todo: use Slide object, but without markdown
102
103
  # slide = Slide.new(:slide_id => slide_id)
@@ -104,7 +105,7 @@ module Deck
104
105
  yield
105
106
  end
106
107
  end
107
-
108
+
108
109
  def slides
109
110
  if @slides
110
111
  @slides.each do |slide|
@@ -114,14 +115,14 @@ module Deck
114
115
  default_slide
115
116
  end
116
117
  end
117
-
118
+
118
119
  def default_slide
119
120
  slide 'readme' do
120
121
  h2 "deck.rb"
121
122
  ul {
122
123
  li "based on deck.js"
123
124
  li "create a subclass of Deck (see introduction.rb)"
124
- li "run erector to build it"
125
+ li "run erector to build it"
125
126
  }
126
127
  pre "erector --to-html ./deck.rb # generates deck.html"
127
128
  end
@@ -145,7 +146,7 @@ module Deck
145
146
  end
146
147
  end
147
148
  end
148
-
149
+
149
150
  def goto_slide
150
151
  form :action => '.', :method => 'get', :class => 'goto-form' do
151
152
  label :for => 'goto-slide' do
@@ -161,6 +162,6 @@ module Deck
161
162
  def permalink
162
163
  a "#", :href => '.', :title => 'Permalink to this slide', :class => 'deck-permalink'
163
164
  end
164
-
165
+
165
166
  end
166
- end
167
+ end