hs-cli 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.hs.yml +2 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +8 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +5 -0
  8. data/Gemfile +8 -0
  9. data/Gemfile.lock +86 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +54 -0
  12. data/Rakefile +12 -0
  13. data/bin/console +14 -0
  14. data/bin/setup +8 -0
  15. data/hs.gemspec +41 -0
  16. data/lib/hs/chapter.rb +31 -0
  17. data/lib/hs/config.rb +40 -0
  18. data/lib/hs/course.rb +35 -0
  19. data/lib/hs/elements/br.rb +9 -0
  20. data/lib/hs/elements/code.rb +22 -0
  21. data/lib/hs/elements/dd.rb +9 -0
  22. data/lib/hs/elements/dl.rb +9 -0
  23. data/lib/hs/elements/dt.rb +9 -0
  24. data/lib/hs/elements/element.rb +46 -0
  25. data/lib/hs/elements/emphasize.rb +9 -0
  26. data/lib/hs/elements/header.rb +16 -0
  27. data/lib/hs/elements/image.rb +22 -0
  28. data/lib/hs/elements/intro.rb +9 -0
  29. data/lib/hs/elements/li.rb +9 -0
  30. data/lib/hs/elements/link.rb +21 -0
  31. data/lib/hs/elements/note.rb +9 -0
  32. data/lib/hs/elements/ol.rb +9 -0
  33. data/lib/hs/elements/original.rb +9 -0
  34. data/lib/hs/elements/paragraph.rb +9 -0
  35. data/lib/hs/elements/strong.rb +9 -0
  36. data/lib/hs/elements/table.rb +39 -0
  37. data/lib/hs/elements/text.rb +21 -0
  38. data/lib/hs/elements/ul.rb +9 -0
  39. data/lib/hs/loaders/chapter_loader.rb +38 -0
  40. data/lib/hs/loaders/course_loader.rb +36 -0
  41. data/lib/hs/loaders/element_loader.rb +127 -0
  42. data/lib/hs/loaders/xml_parser.rb +62 -0
  43. data/lib/hs/preview/chapter_preview.rb +49 -0
  44. data/lib/hs/preview/course_preview.rb +43 -0
  45. data/lib/hs/preview/element_preview.rb +45 -0
  46. data/lib/hs/preview/html_preview.rb +27 -0
  47. data/lib/hs/version.rb +5 -0
  48. data/lib/hs.rb +28 -0
  49. metadata +231 -0
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HS
4
+ class Table < HS::Element
5
+ def tag_name
6
+ 'table'
7
+ end
8
+ end
9
+
10
+ class Thead < HS::Element
11
+ def tag_name
12
+ 'thead'
13
+ end
14
+ end
15
+
16
+ class Tbody < HS::Element
17
+ def tag_name
18
+ 'tbody'
19
+ end
20
+ end
21
+
22
+ class Tr < HS::Element
23
+ def tag_name
24
+ 'tr'
25
+ end
26
+ end
27
+
28
+ class Th < HS::Element
29
+ def tag_name
30
+ 'th'
31
+ end
32
+ end
33
+
34
+ class Td < HS::Element
35
+ def tag_name
36
+ 'td'
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'hs/preview/element_preview'
4
+
5
+ module HS
6
+ class Text
7
+ include ElementPreview
8
+
9
+ attr_accessor :text
10
+
11
+ def initialize(args)
12
+ args.each do |key, val|
13
+ instance_variable_set("@#{key}", val)
14
+ end
15
+ end
16
+
17
+ def preview
18
+ text_preview(text)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HS
4
+ class Ul < HS::Element
5
+ def tag_name
6
+ 'ul'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'hs/loaders/xml_parser'
4
+ require 'hs/loaders/element_loader'
5
+
6
+ module HS
7
+ module ChapterLoader
8
+ include HS::XMLParser
9
+ include HS::ElementLoader
10
+
11
+ def load_chapter(course, xml)
12
+ create_chapter(course, parse_xml(xml))
13
+ end
14
+
15
+ private
16
+
17
+ def create_chapter(course, data) # rubocop:disable Metrics/MethodLength
18
+ chapter = HS::Chapter.new(course, slug: data[:slug])
19
+
20
+ data[:_children].each do |child|
21
+ case child[:_name]
22
+ when :title
23
+ chapter.title = get_text(child)
24
+ when :intro
25
+ chapter.intro = load_element(child)
26
+ when :modules
27
+ # TODO: extract :modules
28
+ nil
29
+ else
30
+ element = load_element(child)
31
+ chapter.content << element if element
32
+ end
33
+ end
34
+
35
+ chapter
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'hs/loaders/xml_parser'
4
+
5
+ module HS
6
+ module CourseLoader
7
+ include HS::XMLParser
8
+
9
+ def load_course(xml, &block)
10
+ create_course(parse_xml(xml), &block)
11
+ end
12
+
13
+ private
14
+
15
+ def create_course(data) # rubocop:disable Metrics/MethodLength
16
+ course = HS::Course.new(slug: data[:slug])
17
+
18
+ data[:_children].each do |child|
19
+ case child[:_name]
20
+ when :title
21
+ course.title = get_text(child)
22
+ when :chapters
23
+ if block_given?
24
+ files = child[:_children].map { |c| c[:file] }.compact
25
+
26
+ files.each do |file|
27
+ yield course, file
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ course
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HS
4
+ module ElementLoader
5
+ def load_element(data)
6
+ name = data[:_name]
7
+ element = initialize_element(name, data)
8
+ children = load_elements(data[:_children])
9
+ children.each { |child| element.children << child }
10
+
11
+ element
12
+ end
13
+
14
+ def load_elements(data)
15
+ return [] unless data
16
+ data.map { |child_data| load_element(child_data) }
17
+ end
18
+
19
+ private
20
+
21
+ def get_params(data, names = %i[class_name style])
22
+ names.map do |name|
23
+ data[name] ? [name, data[name]] : nil
24
+ end.compact.to_h
25
+ end
26
+
27
+ def initialize_header(data)
28
+ HS::Header.new(get_params(data, %i[level class_name style]))
29
+ end
30
+
31
+ def initialize_element(name, data)
32
+ send("initialize_#{name}", data)
33
+ end
34
+
35
+ def initialize_text(data)
36
+ HS::Text.new(text: data[:text])
37
+ end
38
+
39
+ def initialize_p(data)
40
+ HS::Paragraph.new(get_params(data))
41
+ end
42
+
43
+ def initialize_note(data)
44
+ HS::Note.new(get_params(data))
45
+ end
46
+
47
+ def initialize_intro(data)
48
+ HS::Intro.new(get_params(data))
49
+ end
50
+
51
+ def initialize_em(data)
52
+ HS::Emphasize.new(get_params(data))
53
+ end
54
+
55
+ def initialize_link(data)
56
+ HS::Link.new(get_params(data, %i[to class_name style]))
57
+ end
58
+
59
+ def initialize_original(data)
60
+ HS::Original.new(get_params(data))
61
+ end
62
+
63
+ def initialize_strong(data)
64
+ HS::Strong.new(get_params(data))
65
+ end
66
+
67
+ def initialize_ul(data)
68
+ HS::Ul.new(get_params(data))
69
+ end
70
+
71
+ def initialize_ol(data)
72
+ HS::Ol.new(get_params(data))
73
+ end
74
+
75
+ def initialize_li(data)
76
+ HS::Li.new(get_params(data))
77
+ end
78
+
79
+ def initialize_image(data)
80
+ HS::Image.new(get_params(data, %i[src class_name style]))
81
+ end
82
+
83
+ def initialize_code(data)
84
+ HS::Code.new(get_params(data, %i[lang class_name style]))
85
+ end
86
+
87
+ def initialize_dd(data)
88
+ HS::Dd.new(get_params(data))
89
+ end
90
+
91
+ def initialize_dl(data)
92
+ HS::Dl.new(get_params(data))
93
+ end
94
+
95
+ def initialize_dt(data)
96
+ HS::Dt.new(get_params(data))
97
+ end
98
+
99
+ def initialize_table(data)
100
+ HS::Table.new(get_params(data))
101
+ end
102
+
103
+ def initialize_thead(data)
104
+ HS::Thead.new(get_params(data))
105
+ end
106
+
107
+ def initialize_tbody(data)
108
+ HS::Tbody.new(get_params(data))
109
+ end
110
+
111
+ def initialize_tr(data)
112
+ HS::Tr.new(get_params(data))
113
+ end
114
+
115
+ def initialize_th(data)
116
+ HS::Th.new(get_params(data))
117
+ end
118
+
119
+ def initialize_td(data)
120
+ HS::Td.new(get_params(data))
121
+ end
122
+
123
+ def initialize_br(_data)
124
+ HS::Br.new # no params!
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/inflector'
4
+ require 'nokogiri'
5
+
6
+ module HS
7
+ module XMLParser
8
+ def parse_xml(xml)
9
+ doc = Nokogiri::XML(xml) do |config|
10
+ config.options = Nokogiri::XML::ParseOptions::STRICT
11
+ end
12
+
13
+ to_object_notation(doc.root)
14
+ end
15
+
16
+ # Text content of the node.
17
+ # Note, that we don't go deep into element tree.
18
+ def get_text(object)
19
+ object[:_children].map { |obj| obj[:text] }.compact.join(' ')
20
+ end
21
+
22
+ private
23
+
24
+ def attributes_hash(xml)
25
+ xml.attributes.map do |key, attr|
26
+ [key.underscore.to_sym, attr.value]
27
+ end.to_h
28
+ end
29
+
30
+ def to_object_notation(xml)
31
+ class_name = xml.class.name.split('::').last.downcase
32
+ send("#{class_name}_to_object_notation", xml)
33
+ end
34
+
35
+ def element_to_object_notation(xml)
36
+ data = attributes_hash(xml)
37
+ data[:_name] = xml.name.underscore.to_sym
38
+ data[:_children] = to_object_notation(xml.children)
39
+
40
+ data
41
+ end
42
+
43
+ def cdata_to_object_notation(xml)
44
+ text_to_object_notation(xml)
45
+ end
46
+
47
+ def text_to_object_notation(xml)
48
+ {
49
+ _name: :text,
50
+ text: xml.text
51
+ }
52
+ end
53
+
54
+ def nodeset_to_object_notation(xml)
55
+ xml.map { |node| to_object_notation(node) }.compact
56
+ end
57
+
58
+ def comment_to_object_notation(_xml)
59
+ nil
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'hs/preview/html_preview'
4
+
5
+ module HS
6
+ module ChapterPreview
7
+ include HS::HTMLPreview
8
+
9
+ def preview
10
+ html_preview do
11
+ body_element
12
+ end
13
+ end
14
+
15
+ def toc_preview
16
+ "<li><a href=\"/#{slug}\">#{title.strip}</a></li>"
17
+ end
18
+
19
+ def save_preview
20
+ file = "#{HS::Config.preview_dir}/#{slug}.html"
21
+ File.open(file, 'w') { |f| f.write(preview) }
22
+ end
23
+
24
+ private
25
+
26
+ def body_element
27
+ [
28
+ '<body>',
29
+ title_preview,
30
+ intro_preview,
31
+ content_preview,
32
+ '</body>'
33
+ ]
34
+ end
35
+
36
+ def intro_preview
37
+ return unless intro
38
+ intro.preview
39
+ end
40
+
41
+ def title_preview
42
+ "<h1 class=\"title\">#{title.strip}</h1>"
43
+ end
44
+
45
+ def content_preview
46
+ content.map(&:preview).compact.join
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'hs/preview/html_preview'
4
+
5
+ module HS
6
+ module CoursePreview
7
+ include HS::HTMLPreview
8
+
9
+ def preview
10
+ html_preview do
11
+ body_element
12
+ end
13
+ end
14
+
15
+ def save_preview
16
+ file = "#{HS::Config.preview_dir}/index.html"
17
+ File.open(file, 'w') { |f| f.write(preview) }
18
+ end
19
+
20
+ private
21
+
22
+ def body_element
23
+ [
24
+ '<body>',
25
+ title_preview,
26
+ toc_preview,
27
+ '</body>'
28
+ ].join("\n")
29
+ end
30
+
31
+ def title_preview
32
+ " <h1 class=\"title\">#{title.strip}</h1>"
33
+ end
34
+
35
+ def toc_preview
36
+ [
37
+ ' <ul>',
38
+ chapters.map(&:toc_preview).join("\n"),
39
+ ' </ul>'
40
+ ].join("\n")
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cgi'
4
+
5
+ module HS
6
+ module ElementPreview
7
+ def text_preview(text)
8
+ return unless text
9
+ CGI.escapeHTML(text)
10
+ end
11
+
12
+ def element_preview(tag_name, params, children)
13
+ [
14
+ element_start(tag_name, params, children),
15
+ element_content(children),
16
+ element_end(tag_name, children)
17
+ ].join
18
+ end
19
+
20
+ private
21
+
22
+ def element_start(tag, params, children)
23
+ start = "<#{tag}"
24
+
25
+ unless params.empty?
26
+ params_str = params.map { |k, v| "#{k}=\"#{v}\"" }.join(' ')
27
+ start = "#{start} #{params_str}"
28
+ end
29
+
30
+ return "#{start} />" if children.empty?
31
+ "#{start}>"
32
+ end
33
+
34
+ def element_content(children)
35
+ return if children.empty?
36
+ children.map(&:preview).compact.join
37
+ end
38
+
39
+ def element_end(tag, children)
40
+ return if children.empty?
41
+
42
+ "</#{tag}>"
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HS
4
+ module HTMLPreview
5
+ def html_preview
6
+ body = yield
7
+ [
8
+ '<html>',
9
+ head_element,
10
+ body,
11
+ '</html>'
12
+ ].compact.join("\n")
13
+ end
14
+
15
+ private
16
+
17
+ def head_element
18
+ [
19
+ '<head>',
20
+ '<meta charset="utf-8"/> ',
21
+ '<meta name="viewport" content="width=device-width, initial-scale=1"/>',
22
+ '<link rel="stylesheet" href="/default.css"/>',
23
+ '</head>'
24
+ ].join("\n")
25
+ end
26
+ end
27
+ end
data/lib/hs/version.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HS
4
+ VERSION = '0.1.2'
5
+ end
data/lib/hs.rb ADDED
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'hs/version'
4
+ require 'hs/config'
5
+
6
+ require 'hs/course'
7
+ require 'hs/chapter'
8
+
9
+ require 'hs/elements/element'
10
+ require 'hs/elements/br'
11
+ require 'hs/elements/code'
12
+ require 'hs/elements/dd'
13
+ require 'hs/elements/dl'
14
+ require 'hs/elements/dt'
15
+ require 'hs/elements/emphasize'
16
+ require 'hs/elements/header'
17
+ require 'hs/elements/image'
18
+ require 'hs/elements/intro'
19
+ require 'hs/elements/li'
20
+ require 'hs/elements/link'
21
+ require 'hs/elements/note'
22
+ require 'hs/elements/ol'
23
+ require 'hs/elements/original'
24
+ require 'hs/elements/paragraph'
25
+ require 'hs/elements/strong'
26
+ require 'hs/elements/table'
27
+ require 'hs/elements/text'
28
+ require 'hs/elements/ul'