rdoc-generator-solarfish 0.0.2

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 (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rubocop.yml +26 -0
  4. data/.travis.yml +9 -0
  5. data/Gemfile +2 -0
  6. data/LICENSE +21 -0
  7. data/README.md +175 -0
  8. data/Rakefile +28 -0
  9. data/data/rdoc-generator-solarfish/templates/onepage.slim +110 -0
  10. data/data/rdoc-generator-solarfish/themes/common/fonts/LICENSE +7 -0
  11. data/data/rdoc-generator-solarfish/themes/common/fonts/LICENSE.Raleway +95 -0
  12. data/data/rdoc-generator-solarfish/themes/common/fonts/LICENSE.Roboto +201 -0
  13. data/data/rdoc-generator-solarfish/themes/common/fonts/Raleway-Regular.ttf +0 -0
  14. data/data/rdoc-generator-solarfish/themes/common/fonts/Raleway-SemiBold.ttf +0 -0
  15. data/data/rdoc-generator-solarfish/themes/common/fonts/Roboto-Regular.ttf +0 -0
  16. data/data/rdoc-generator-solarfish/themes/common/syntax/LICENSE +3 -0
  17. data/data/rdoc-generator-solarfish/themes/common/syntax/LICENSE.onelight +20 -0
  18. data/data/rdoc-generator-solarfish/themes/common/syntax/onelight.sass +47 -0
  19. data/data/rdoc-generator-solarfish/themes/light/main.sass +215 -0
  20. data/data/rdoc-generator-solarfish/themes/light.yml +16 -0
  21. data/docs/example.rb +142 -0
  22. data/docs/example_output/Raleway-Regular.ttf +0 -0
  23. data/docs/example_output/Raleway-SemiBold.ttf +0 -0
  24. data/docs/example_output/Roboto-Regular.ttf +0 -0
  25. data/docs/example_output/created.rid +2 -0
  26. data/docs/example_output/index.html +404 -0
  27. data/docs/example_output/index.json +428 -0
  28. data/docs/example_output/main.css +210 -0
  29. data/docs/example_output/onelight.css +22 -0
  30. data/lib/rdoc/discover.rb +1 -0
  31. data/lib/rdoc/generator/doc_loader.rb +315 -0
  32. data/lib/rdoc/generator/html_writer.rb +35 -0
  33. data/lib/rdoc/generator/json_writer.rb +16 -0
  34. data/lib/rdoc/generator/settings.rb +57 -0
  35. data/lib/rdoc/generator/solarfish.rb +135 -0
  36. data/lib/rdoc/generator/template_loader.rb +42 -0
  37. data/lib/rdoc/generator/theme_loader.rb +160 -0
  38. data/lib/solarfish.rb +2 -0
  39. data/rdoc-generator-solarfish.gemspec +31 -0
  40. data/test/test_generator.rb +95 -0
  41. data/test/test_theme_loader.rb +89 -0
  42. data/test/themes.rb +213 -0
  43. metadata +216 -0
@@ -0,0 +1,160 @@
1
+ require 'fileutils'
2
+ require 'yaml'
3
+ require 'sass'
4
+
5
+ require_relative 'settings'
6
+
7
+ # ThemeLoader reads theme files from `.yml' files and builds a hash that
8
+ # will be passed to HTML template or written to JSON file, and a list
9
+ # of theme files to be installed along with the HTML file.
10
+ class ThemeLoader
11
+ def self.themes_list
12
+ Settings.list_file_names 'themes', '.yml'
13
+ end
14
+
15
+ def self.theme_path(name)
16
+ Settings.find_file 'themes', '.yml', name
17
+ end
18
+
19
+ def initialize(options)
20
+ @options = options
21
+ end
22
+
23
+ def load
24
+ theme = {
25
+ head: {
26
+ styles: [],
27
+ fonts: [],
28
+ scripts: [],
29
+ html: []
30
+ },
31
+ body: {
32
+ header: [],
33
+ footer: []
34
+ }
35
+ }
36
+
37
+ theme_files = []
38
+
39
+ @options.sf_themes.each do |theme_path|
40
+ load_theme(theme, theme_files, theme_path)
41
+ end
42
+
43
+ build_files(theme_files)
44
+
45
+ [theme, theme_files]
46
+ end
47
+
48
+ private
49
+
50
+ def load_theme(theme, theme_files, theme_path)
51
+ config = YAML.load_file theme_path
52
+
53
+ config.each do |section, content|
54
+ check_one_of(
55
+ message: 'Unexpected section in theme config',
56
+ expected: %w[head body],
57
+ actual: section
58
+ )
59
+
60
+ case section
61
+ when 'head'
62
+ content.each do |key, files|
63
+ check_one_of(
64
+ message: "Unexpected key in 'head'",
65
+ expected: %w[styles fonts scripts html],
66
+ actual: key
67
+ )
68
+ section = key.to_sym
69
+ files.each do |file_info|
70
+ path = theme_file(theme_path, file_info['file'])
71
+ case section
72
+ when :styles, :scripts, :fonts
73
+ name = File.basename(path)
74
+ fh = {
75
+ url: theme_url(name)
76
+ }
77
+ fh[:family] = file_info['family'] if section == :fonts
78
+ fp = {
79
+ src_path: path,
80
+ dst_name: name,
81
+ dst_info: fh
82
+ }
83
+ when :html
84
+ fh = {
85
+ data: File.read(path)
86
+ }
87
+ end
88
+ theme[:head][section] << fh
89
+ theme_files << fp if fp
90
+ end
91
+ end
92
+
93
+ when 'body'
94
+ content.each do |key, files|
95
+ check_one_of(
96
+ message: "Unexpected key in 'body'",
97
+ expected: %w[header footer],
98
+ actual: key
99
+ )
100
+ files.each do |file_info|
101
+ path = theme_file(theme_path, file_info['file'])
102
+ fh = {
103
+ data: File.read(path)
104
+ }
105
+ theme[:body][key.to_sym] << fh
106
+ end
107
+ end
108
+ end
109
+ end
110
+ rescue => error
111
+ raise "Can't load theme - #{theme_path}\n#{error}"
112
+ end
113
+
114
+ def build_files(theme_files)
115
+ theme_files.each do |file|
116
+ ext = File.extname file[:src_path]
117
+ build_css_file file, ext if %w[.sass .scss].include? ext
118
+ end
119
+ end
120
+
121
+ def build_css_file(file, ext)
122
+ options = {
123
+ cache: false,
124
+ style: :default
125
+ }
126
+
127
+ options[:syntax] = if ext == '.sass'
128
+ :sass
129
+ else
130
+ :scss
131
+ end
132
+
133
+ input_data = File.read(file[:src_path])
134
+ renderer = Sass::Engine.new(input_data, options)
135
+ output_data = renderer.render
136
+
137
+ file.delete(:src_path)
138
+ file[:dst_name] = File.basename(file[:dst_name], '.*') + '.css'
139
+ file[:dst_info][:url] = theme_url(file[:dst_name])
140
+ file[:data] = output_data
141
+ end
142
+
143
+ def theme_file(theme_path, file_path)
144
+ File.join(File.dirname(theme_path), file_path)
145
+ end
146
+
147
+ def theme_url(name)
148
+ url = @options.sf_prefix || ''
149
+ url += '/' if !url.empty? && !url.end_with?('/')
150
+ url + name
151
+ end
152
+
153
+ def check_one_of(message: '', expected: [], actual: '')
154
+ unless expected.include?(actual)
155
+ raise %(#{message}: ) +
156
+ %(got '#{actual}', ) +
157
+ %(expected one of: #{expected.map { |e| "'#{e}'" }.join(', ')})
158
+ end
159
+ end
160
+ end
data/lib/solarfish.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'rdoc/rdoc'
2
+ require 'rdoc/generator/solarfish'
@@ -0,0 +1,31 @@
1
+ lib = File.expand_path('lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = 'rdoc-generator-solarfish'
6
+ spec.version = '0.0.2'
7
+ spec.authors = ['Victor Gaydov', 'Dmitriy Shilin', 'Valeria Khomyakova']
8
+ spec.email = ['victor@enise.org']
9
+ spec.description = 'Single page HTML5 generator for Ruby RDoc'
10
+ spec.summary = 'Exposes a new HTML formatter for RDoc'
11
+ spec.homepage = 'https://github.com/rbdoc/rdoc-generator-solarfish'
12
+ spec.license = 'MIT'
13
+
14
+ spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
15
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.require_paths = ['lib']
18
+
19
+ spec.required_ruby_version = '>= 2.3'
20
+
21
+ spec.add_runtime_dependency 'rdoc', '~> 5.1'
22
+ spec.add_runtime_dependency 'sass', '~> 3.4'
23
+ spec.add_runtime_dependency 'slim', '~> 3.0'
24
+ spec.add_runtime_dependency 'recursive-open-struct', '~> 1.0'
25
+
26
+ spec.add_development_dependency 'bundler'
27
+ spec.add_development_dependency 'rake', '~> 12.0'
28
+ spec.add_development_dependency 'rubocop', '~> 0.49'
29
+ spec.add_development_dependency 'minitest', '~> 5.10'
30
+ spec.add_development_dependency 'html5_validator', '~> 1.0'
31
+ end
@@ -0,0 +1,95 @@
1
+ require 'html5_validator/validator'
2
+ require 'pp'
3
+ require 'json'
4
+
5
+ require 'minitest'
6
+ require 'minitest/autorun'
7
+
8
+ require 'rdoc/rdoc'
9
+ require 'rdoc/generator/solarfish'
10
+
11
+ require_relative 'themes'
12
+
13
+ class TestGenerator < MiniTest::Test
14
+ def source_file
15
+ File.join(File.dirname(__FILE__), '../docs/example.rb')
16
+ end
17
+
18
+ def run_generator(file, title)
19
+ dir = File.join(Dir.mktmpdir, 'out')
20
+
21
+ options = RDoc::Options.new
22
+ options.setup_generator 'solarfish'
23
+
24
+ options.verbosity = 0
25
+ options.files = [file]
26
+ options.op_dir = dir
27
+ options.title = title
28
+
29
+ options.sf_htmlfile = 'test.html'
30
+ options.sf_jsonfile = 'test.json'
31
+
32
+ yield options if block_given?
33
+
34
+ rdoc = RDoc::RDoc.new
35
+ rdoc.document options
36
+
37
+ html = File.read(File.join(dir, 'test.html'))
38
+ json = File.read(File.join(dir, 'test.json'))
39
+
40
+ [html, json]
41
+ end
42
+
43
+ def check_generator(&block)
44
+ html, json = run_generator(source_file, 'test title', &block)
45
+
46
+ validator = Html5Validator::Validator.new
47
+ validator.validate_text(html)
48
+
49
+ flunk validator.errors.pretty_inspect unless validator.valid?
50
+
51
+ JSON.parse json
52
+ end
53
+
54
+ def test_defaults
55
+ check_generator
56
+ end
57
+
58
+ def test_prefix
59
+ check_generator do |options|
60
+ options.sf_prefix = '/test_prefix'
61
+ end
62
+ end
63
+
64
+ def test_template
65
+ check_generator do |options|
66
+ options.sf_template = TemplateLoader.template_path(Settings::DEFAULT_TEMPLATE)
67
+ end
68
+ end
69
+
70
+ def test_themes
71
+ check_generator do |options|
72
+ options.sf_themes = [ThemeLoader.theme_path(Settings::DEFAULT_THEME)]
73
+ end
74
+ check_generator do |options|
75
+ options.sf_themes = [Theme1.new.theme_path]
76
+ end
77
+ check_generator do |options|
78
+ options.sf_themes = [Theme2.new.theme_path]
79
+ end
80
+ check_generator do |options|
81
+ options.sf_themes = [Theme1.new.theme_path, Theme2.new.theme_path]
82
+ end
83
+ end
84
+
85
+ def test_filters
86
+ check_generator do |options|
87
+ options.sf_filter_classes = '.*'
88
+ options.sf_filter_members = '.*'
89
+ end
90
+ check_generator do |options|
91
+ options.sf_filter_classes = 'nevermatch'
92
+ options.sf_filter_members = 'nevermatch'
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,89 @@
1
+ require 'minitest'
2
+ require 'minitest/autorun'
3
+
4
+ require 'rdoc/generator/theme_loader'
5
+
6
+ require_relative 'themes'
7
+
8
+ class TestThemeLoader < MiniTest::Test
9
+ class Options
10
+ attr_accessor :sf_themes
11
+ attr_accessor :sf_prefix
12
+ end
13
+
14
+ def subset?(outer, inner)
15
+ if outer.is_a?(Hash)
16
+ inner.each do |k, v|
17
+ return false unless subset? outer[k], v
18
+ end
19
+ elsif outer.is_a?(Array)
20
+ inner.each do |i|
21
+ return false unless outer.any? do |o|
22
+ subset? o, i
23
+ end
24
+ end
25
+ else
26
+ return false unless outer == inner
27
+ end
28
+ return true
29
+ end
30
+
31
+ def load_themes(themes, prefix)
32
+ options = Options.new
33
+ options.sf_themes = []
34
+ options.sf_prefix = prefix
35
+
36
+ themes.each do |t|
37
+ options.sf_themes << t.theme_path
38
+ end
39
+
40
+ theme_loader = ThemeLoader.new(options)
41
+ theme_loader.load
42
+ end
43
+
44
+ def check_themes_with_prefix(themes: [], in_prefix: '', out_prefix: '')
45
+ theme, files = load_themes themes, in_prefix
46
+
47
+ themes.each do |t|
48
+ assert subset? theme, t.expected_hash(out_prefix)
49
+ assert subset? files, t.expected_files
50
+ end
51
+ end
52
+
53
+ def check_themes(themes)
54
+ check_themes_with_prefix(
55
+ themes: themes,
56
+ in_prefix: '',
57
+ out_prefix: '',
58
+ )
59
+ check_themes_with_prefix(
60
+ themes: themes,
61
+ in_prefix: '/test_prefix',
62
+ out_prefix: '/test_prefix/',
63
+ )
64
+ check_themes_with_prefix(
65
+ themes: themes,
66
+ in_prefix: '/test_prefix/',
67
+ out_prefix: '/test_prefix/',
68
+ )
69
+ end
70
+
71
+ def test_theme1
72
+ check_themes([
73
+ Theme1.new,
74
+ ])
75
+ end
76
+
77
+ def test_theme2
78
+ check_themes([
79
+ Theme2.new,
80
+ ])
81
+ end
82
+
83
+ def test_theme1_theme2
84
+ check_themes([
85
+ Theme1.new,
86
+ Theme2.new,
87
+ ])
88
+ end
89
+ end
data/test/themes.rb ADDED
@@ -0,0 +1,213 @@
1
+ require 'fileutils'
2
+
3
+ class BasicTheme
4
+ def initialize(name)
5
+ @dir = Dir.mktmpdir
6
+ @path = File.join(@dir, name)
7
+ end
8
+
9
+ def theme_path
10
+ @path
11
+ end
12
+
13
+ def file_path(name)
14
+ File.join(@dir, name)
15
+ end
16
+
17
+ def write_file(name, data)
18
+ path = file_path(name)
19
+ FileUtils.mkpath(File.dirname(path))
20
+ File.write(path, data)
21
+ end
22
+ end
23
+
24
+ class Theme1 < BasicTheme
25
+ def initialize
26
+ super 'theme1.yml'
27
+
28
+ write_file 'theme1.yml', theme_yaml
29
+
30
+ write_file 'dir1/style1a.css', ''
31
+ write_file 'dir2/style1b.sass', ''
32
+ write_file 'dir2/style1c.scss', ''
33
+
34
+ write_file 'dir1/script1a.js', ''
35
+ write_file 'dir2/script1b.js', ''
36
+
37
+ write_file 'dir1/font1a.ttf', ''
38
+ write_file 'dir2/font1b.ttf', ''
39
+
40
+ write_file 'dir1/head1a.html', '<style>h1a</style>'
41
+ write_file 'dir2/head1b.html', '<style>h1b</style>'
42
+
43
+ write_file 'dir1/body1a.html', '<p>b1a</p>'
44
+ write_file 'dir2/body1b.html', '<p>b1b</p>'
45
+ write_file 'dir1/body1c.html', '<p>b1c</p>'
46
+ write_file 'dir2/body1d.html', '<p>b1d</p>'
47
+ end
48
+
49
+ def theme_yaml
50
+ <<END
51
+ head:
52
+ styles:
53
+ -
54
+ file: dir1/style1a.css
55
+ -
56
+ file: dir2/style1b.sass
57
+ -
58
+ file: dir2/style1c.scss
59
+ scripts:
60
+ -
61
+ file: dir1/script1a.js
62
+ -
63
+ file: dir2/script1b.js
64
+ fonts:
65
+ -
66
+ file: dir1/font1a.ttf
67
+ family: Family1a
68
+ -
69
+ file: dir2/font1b.ttf
70
+ family: Family1b
71
+ html:
72
+ -
73
+ file: dir1/head1a.html
74
+ -
75
+ file: dir2/head1b.html
76
+ body:
77
+ header:
78
+ -
79
+ file: dir1/body1a.html
80
+ -
81
+ file: dir2/body1b.html
82
+ footer:
83
+ -
84
+ file: dir1/body1c.html
85
+ -
86
+ file: dir2/body1d.html
87
+ END
88
+ end
89
+
90
+ def expected_files
91
+ [
92
+ { dst_name: 'style1a.css', src_path: file_path('dir1/style1a.css') },
93
+ { dst_name: 'style1b.css' },
94
+ { dst_name: 'style1c.css' },
95
+
96
+ { dst_name: 'script1a.js', src_path: file_path('dir1/script1a.js') },
97
+ { dst_name: 'script1b.js', src_path: file_path('dir2/script1b.js') },
98
+
99
+ { dst_name: 'font1a.ttf', src_path: file_path('dir1/font1a.ttf') },
100
+ { dst_name: 'font1b.ttf', src_path: file_path('dir2/font1b.ttf') },
101
+ ]
102
+ end
103
+
104
+ def expected_hash(prefix)
105
+ {
106
+ :head => {
107
+ :styles => [
108
+ { :url => "#{prefix}style1a.css" },
109
+ { :url => "#{prefix}style1b.css" },
110
+ { :url => "#{prefix}style1c.css" },
111
+ ],
112
+ :scripts => [
113
+ { :url => "#{prefix}script1a.js" },
114
+ { :url => "#{prefix}script1b.js" },
115
+ ],
116
+ :fonts => [
117
+ { :url => "#{prefix}font1a.ttf", :family => 'Family1a' },
118
+ { :url => "#{prefix}font1b.ttf", :family => 'Family1b' },
119
+ ],
120
+ :html => [
121
+ { :data => '<style>h1a</style>' },
122
+ { :data => '<style>h1b</style>' },
123
+ ],
124
+ },
125
+ :body => {
126
+ :header => [
127
+ { :data => '<p>b1a</p>' },
128
+ { :data => '<p>b1b</p>' },
129
+ ],
130
+ :footer => [
131
+ { :data => '<p>b1c</p>' },
132
+ { :data => '<p>b1d</p>' },
133
+ ],
134
+ }
135
+ }
136
+ end
137
+ end
138
+
139
+ class Theme2 < BasicTheme
140
+ def initialize
141
+ super 'theme2.yml'
142
+
143
+ write_file 'theme2.yml', theme_yaml
144
+
145
+ write_file 'style2a.css', ''
146
+ write_file 'script2a.js', ''
147
+ write_file 'font2a.ttf', ''
148
+ write_file 'head2a.html', '<style>h2a</style>'
149
+ write_file 'body2a.html', '<p>b2a</p>'
150
+ write_file 'body2b.html', '<p>b2b</p>'
151
+ end
152
+
153
+ def theme_yaml
154
+ <<END
155
+ head:
156
+ styles:
157
+ -
158
+ file: style2a.css
159
+ scripts:
160
+ -
161
+ file: script2a.js
162
+ fonts:
163
+ -
164
+ file: font2a.ttf
165
+ family: Family2a
166
+ html:
167
+ -
168
+ file: head2a.html
169
+ body:
170
+ header:
171
+ -
172
+ file: body2a.html
173
+ footer:
174
+ -
175
+ file: body2b.html
176
+ END
177
+ end
178
+
179
+ def expected_files
180
+ [
181
+ { dst_name: 'style2a.css', src_path: file_path('style2a.css') },
182
+ { dst_name: 'script2a.js', src_path: file_path('script2a.js') },
183
+ { dst_name: 'font2a.ttf', src_path: file_path('font2a.ttf') },
184
+ ]
185
+ end
186
+
187
+ def expected_hash(prefix)
188
+ {
189
+ :head => {
190
+ :styles => [
191
+ { :url => "#{prefix}style2a.css" },
192
+ ],
193
+ :scripts => [
194
+ { :url => "#{prefix}script2a.js" },
195
+ ],
196
+ :fonts => [
197
+ { :url => "#{prefix}font2a.ttf", :family => 'Family2a' },
198
+ ],
199
+ :html => [
200
+ { :data => '<style>h2a</style>' },
201
+ ],
202
+ },
203
+ :body => {
204
+ :header => [
205
+ { :data => '<p>b2a</p>' },
206
+ ],
207
+ :footer => [
208
+ { :data => '<p>b2b</p>' },
209
+ ],
210
+ }
211
+ }
212
+ end
213
+ end