Skip to content

Commit 18fba76

Browse files
committed
resolves asciidoctor#2535 replace ostruct with internal implementation
1 parent b5f75ab commit 18fba76

File tree

5 files changed

+93
-33
lines changed

5 files changed

+93
-33
lines changed

CHANGELOG.adoc

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Improvements::
3939
* drop support for the unmaintained payment font (`pf`) for use in font-based icons
4040
* refactor formatted text transform to simplify how inner space is collapsed; verify only inner hard breaks are preserved
4141
* allow relative font size for sub and sup to be set independently; support combined setting for backwards compatibility
42+
* replace OpenStruct with internal ThemeData class for storing theme data (#2535)
4243

4344
Bug Fixes::
4445

Gemfile

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ gem 'asciidoctor-diagram', ENV['ASCIIDOCTOR_DIAGRAM_VERSION'], require: false if
1010
gem 'asciidoctor-kroki', ENV['ASCIIDOCTOR_KROKI_VERSION'], require: false if ENV.key? 'ASCIIDOCTOR_KROKI_VERSION'
1111
gem 'coderay', '~> 1.1.0', require: false
1212
gem 'ffi-icu', ENV['FFI_ICU_VERSION'], require: false if ENV.key? 'FFI_ICU_VERSION'
13+
gem 'logger', require: false if (Gem::Version.new RUBY_VERSION) > (Gem::Version.new '3.3.4')
1314
gem 'open-uri-cached', '~> 1.0.0', require: false
1415
gem 'prawn-gmagick', ENV['PRAWN_GMAGICK_VERSION'], require: false if (ENV.key? 'PRAWN_GMAGICK_VERSION') && RUBY_ENGINE == 'ruby'
1516
gem 'pygments.rb', ENV['PYGMENTS_VERSION'], require: false if ENV.key? 'PYGMENTS_VERSION'
@@ -36,5 +37,6 @@ end
3637

3738
group :coverage do
3839
gem 'deep-cover-core', '~> 1.1.0', require: false
40+
gem 'json', '~> 2.7', require: false if (Gem::Version.new RUBY_VERSION) > (Gem::Version.new '3.3.4')
3941
gem 'simplecov', '~> 0.22.0', require: false
4042
end

lib/asciidoctor/pdf/theme_data.rb

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# frozen_string_literal: true
2+
3+
module Asciidoctor
4+
module PDF
5+
class ThemeData
6+
attr_reader :table
7+
8+
def initialize data = nil
9+
@table = (data || {}).transform_keys(&:to_sym)
10+
end
11+
12+
def [] name
13+
@table[name.to_sym]
14+
end
15+
16+
def []= name, value
17+
@table[name.to_sym] = value
18+
end
19+
20+
def each_pair &block
21+
@table.each_pair(&block)
22+
end
23+
24+
def eql? other
25+
@table.to_h.eql? other.to_h
26+
end
27+
28+
def delete_field name
29+
@table.delete name
30+
end
31+
32+
def dup
33+
ThemeData.new @table
34+
end
35+
36+
def method_missing name, *args
37+
if (name_str = name.to_s).end_with? '='
38+
@table[name_str.chop.to_sym] = args[0]
39+
else
40+
@table[name]
41+
end
42+
end
43+
44+
def respond_to? name, _include_all = false
45+
@table.key? name.to_sym
46+
end
47+
48+
def respond_to_missing? name, _include_all = false
49+
@table.key? name.to_sym
50+
end
51+
52+
def to_h
53+
@table
54+
end
55+
end
56+
end
57+
end

lib/asciidoctor/pdf/theme_loader.rb

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# frozen_string_literal: true
22

3-
require 'ostruct'
3+
require_relative 'theme_data'
44
require_relative 'measurements'
55

66
module Asciidoctor
@@ -70,7 +70,7 @@ def self.resolve_theme_asset asset_path, theme_dir = nil
7070
# NOTE: base theme is loaded "as is" (no post-processing)
7171
def self.load_base_theme
7272
::File.open BaseThemePath, mode: 'r:UTF-8' do |io|
73-
(::OpenStruct.new ::YAML.safe_load io, filename: BaseThemePath).tap {|theme| theme.__dir__ = ThemesDir }
73+
(ThemeData.new ::YAML.safe_load io, filename: BaseThemePath).tap {|theme| theme.__dir__ = ThemesDir }
7474
end
7575
end
7676

@@ -79,7 +79,7 @@ def self.load_theme theme_name = nil, theme_dir = nil
7979
if theme_path == BaseThemePath
8080
load_base_theme
8181
else
82-
theme_data = load_file theme_path, (::OpenStruct.new base_font_size: 12), theme_dir
82+
theme_data = load_file theme_path, (ThemeData.new base_font_size: 12), theme_dir
8383
unless (::File.dirname theme_path) == ThemesDir
8484
theme_data.base_text_align ||= 'left'
8585
theme_data.base_line_height ||= 1
@@ -103,12 +103,12 @@ def self.load_file filename, theme_data = nil, theme_dir = nil
103103
line.sub(HexColorEntryRx) { %(#{(m = $~)[:k]}: #{m[:h] || (m[:k].end_with? 'color') ? "'#{m[:v]}'" : m[:v]}) }
104104
end.join unless (::File.dirname filename) == ThemesDir
105105
yaml_data = ::YAML.safe_load data, aliases: true, filename: filename
106-
(loaded = (theme_data ||= ::OpenStruct.new).__loaded__ ||= ::Set.new).add filename
106+
(loaded = (theme_data ||= ThemeData.new).__loaded__ ||= ::Set.new).add filename
107107
if ::Hash === yaml_data && (extends = yaml_data.delete 'extends')
108108
(Array extends).each do |extend_path|
109109
extend_path = extend_path.slice 0, extend_path.length - 11 if (force = extend_path.end_with? ' !important')
110110
if extend_path == 'base'
111-
theme_data = ::OpenStruct.new theme_data.to_h.merge load_base_theme.to_h if (loaded.add? 'base') || force
111+
theme_data = ThemeData.new theme_data.to_h.merge load_base_theme.to_h if (loaded.add? 'base') || force
112112
next
113113
elsif BundledThemeNames.include? extend_path
114114
extend_path, extend_theme_dir = resolve_theme_file extend_path, ThemesDir
@@ -124,7 +124,7 @@ def self.load_file filename, theme_data = nil, theme_dir = nil
124124
end
125125

126126
def load hash, theme_data = nil
127-
::Hash === hash ? hash.reduce(theme_data || ::OpenStruct.new) {|data, (key, val)| process_entry key, val, data, true } : (theme_data || ::OpenStruct.new)
127+
::Hash === hash ? hash.reduce(theme_data || ThemeData.new) {|data, (key, val)| process_entry key, val, data, true } : (theme_data || ThemeData.new)
128128
end
129129

130130
private

spec/theme_loader_spec.rb

+27-27
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,26 @@
99
it 'should not fail if theme data is empty' do
1010
theme = subject.new.load ''
1111
(expect theme).not_to be_nil
12-
(expect theme).to be_an OpenStruct
12+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
1313
(expect theme.to_h).to be_empty
1414
end
1515

1616
it 'should not fail if theme data is falsy' do
1717
theme = subject.new.load false
1818
(expect theme).not_to be_nil
19-
(expect theme).to be_an OpenStruct
19+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
2020
(expect theme.to_h).to be_empty
2121
end
2222

2323
# NOTE: this API is not used by the converter
2424
it 'should use specified theme data if raw theme data is nil' do
25-
theme_data = OpenStruct.new
25+
theme_data = Asciidoctor::PDF::ThemeData.new
2626
theme_data.base_font_color = '222222'
2727
theme = subject.new.load nil, theme_data
2828
(expect theme).to be theme_data
2929
end
3030

31-
it 'should store flattened keys in OpenStruct' do
31+
it 'should store flattened keys in Asciidoctor::PDF::ThemeData' do
3232
theme_data = YAML.safe_load <<~'END'
3333
page:
3434
size: A4
@@ -41,7 +41,7 @@
4141
font_style: bold
4242
END
4343
theme = subject.new.load theme_data
44-
(expect theme).to be_an OpenStruct
44+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
4545
(expect theme).to respond_to :page_size
4646
(expect theme).to respond_to :base_font_family
4747
(expect theme).to respond_to :base_border_width
@@ -62,7 +62,7 @@
6262
size: 24
6363
END
6464
theme = subject.new.load theme_data
65-
(expect theme).to be_an OpenStruct
65+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
6666
(expect theme.admonition_icon_tip).to be_a Hash
6767
(expect theme.admonition_icon_tip).to eql name: 'far-lightbulb', stroke_color: 'FFFF00', size: 24
6868
(expect theme.admonition_icon_note).to be_a Hash
@@ -76,7 +76,7 @@
7676
advice: ~
7777
END
7878
theme = subject.new.load theme_data
79-
(expect theme).to be_an OpenStruct
79+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
8080
(expect theme.admonition_icon_advice).to be_nil
8181
end
8282

@@ -93,7 +93,7 @@
9393
stroke-color: FFFF00
9494
END
9595
theme = subject.new.load theme_data
96-
(expect theme).to be_an OpenStruct
96+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
9797
(expect theme).to respond_to :page_size
9898
(expect theme).to respond_to :base_font_family
9999
(expect theme).to respond_to :abstract_title_font_size
@@ -112,7 +112,7 @@
112112
color: 0000ff
113113
END
114114
theme = subject.new.load theme_data
115-
(expect theme).to be_an OpenStruct
115+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
116116
(expect theme).to respond_to 'role_flaming-red_font_color'
117117
(expect theme['role_flaming-red_font_color']).to eql 'FF0000'
118118
(expect theme).to respond_to 'role_so-very-blue_font_color'
@@ -126,7 +126,7 @@
126126
font-style: bold
127127
END
128128
theme = subject.new.load theme_data
129-
(expect theme).to be_an OpenStruct
129+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
130130
(expect theme).to respond_to 'role_BOLD_font_style'
131131
(expect theme['role_BOLD_font_style']).to eql 'bold'
132132
end
@@ -148,7 +148,7 @@
148148
content: 2 * 2
149149
END
150150
theme = subject.new.load theme_data
151-
(expect theme).to be_an OpenStruct
151+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
152152
(expect theme.menu_caret_content).to eql '[">"]'
153153
(expect theme.ulist_marker_disc_content).to eql '0'
154154
(expect theme.footer_recto_left_content).to eql 'true'
@@ -172,7 +172,7 @@
172172
text-align: $heading-align
173173
END
174174
theme = subject.new.load theme_data
175-
(expect theme).to be_an OpenStruct
175+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
176176
(expect theme.base_align).to be_nil
177177
(expect theme.base_text_align).to eql 'center'
178178
(expect theme.heading_align).to be_nil
@@ -197,7 +197,7 @@
197197
end: $table-caption-side
198198
END
199199
theme = subject.new.load theme_data
200-
(expect theme).to be_an OpenStruct
200+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
201201
(expect theme.table_caption_side).to be_nil
202202
(expect theme.table_caption_end).to eql 'bottom'
203203
(expect theme.image_caption_end).to eql 'bottom'
@@ -211,7 +211,7 @@
211211
separator: '-'
212212
END
213213
theme = subject.new.load theme_data
214-
(expect theme).to be_an OpenStruct
214+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
215215
(expect theme.kbd_separator).to be_nil
216216
(expect theme.kbd_separator_content).to eql '-'
217217
end).to log_message severity: :WARN, message: 'the kbd-separator theme key is deprecated; use the kbd-separator-content key instead'
@@ -227,7 +227,7 @@
227227
item-spacing: $outline_list_item_spacing / 2
228228
END
229229
theme = subject.new.load theme_data
230-
(expect theme).to be_an OpenStruct
230+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
231231
(expect theme.outline_list_item_spacing).to be_nil
232232
(expect theme.list_item_spacing).to eql 6
233233
(expect theme.footnotes_margin_top).to eql theme.list_item_spacing
@@ -245,7 +245,7 @@
245245
font-color: $blockquote-font-color
246246
END
247247
theme = subject.new.load theme_data
248-
(expect theme).to be_an OpenStruct
248+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
249249
(expect theme.blockquote_font_color).to be_nil
250250
(expect theme.quote_font_color).to eql '4A4A4A'
251251
(expect theme.quote_border_color).to eql theme.quote_font_color
@@ -262,7 +262,7 @@
262262
font-color: $key-border-color
263263
END
264264
theme = subject.new.load theme_data
265-
(expect theme).to be_an OpenStruct
265+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
266266
(expect theme.key_border_color).to be_nil
267267
(expect theme.kbd_border_color).to eql 'CCCCCC'
268268
(expect theme.kbd_font_color).to eql theme.kbd_border_color
@@ -278,7 +278,7 @@
278278
font-family: $literal-font-family
279279
END
280280
theme = subject.new.load theme_data
281-
(expect theme).to be_an OpenStruct
281+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
282282
(expect theme.literal_font_family).to be_nil
283283
(expect theme.codespan_font_family).to eql 'M+ 1mn'
284284
(expect theme.verse_font_family).to eql 'M+ 1mn'
@@ -297,7 +297,7 @@
297297
padding: [6, 12, -6, 14]
298298
END
299299
theme = subject.new.load theme_data
300-
(expect theme).to be_an OpenStruct
300+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
301301
(expect theme.example_padding).to eql [12, 12, 12, 12]
302302
(expect theme.quote_padding).to eql [0, 12, 0, 14]
303303
(expect theme.sidebar_padding).to eql [12, 12, 12, 12]
@@ -310,7 +310,7 @@
310310
padding: [-3, 12, -3, 14]
311311
END
312312
theme = subject.new.load theme_data
313-
(expect theme).to be_an OpenStruct
313+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
314314
(expect theme.quote_padding).to eql [-3, 12, -3, 14]
315315
end
316316

@@ -328,7 +328,7 @@
328328
content: $page_size
329329
END
330330
theme = subject.new.load theme_data
331-
(expect theme).to be_an OpenStruct
331+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
332332
(expect theme.footer_verso_left_content).to eql '2 * 12'
333333
(expect theme.footer_verso_right_content).to eql 'A4'
334334
end
@@ -490,16 +490,16 @@
490490
describe '.load_file' do
491491
it 'should not fail if theme file is empty' do
492492
theme = subject.load_file fixture_file 'empty-theme.yml'
493-
(expect theme).to be_an OpenStruct
493+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
494494
theme.delete_field :__loaded__
495-
(expect theme).to eql OpenStruct.new
495+
(expect theme).to eql Asciidoctor::PDF::ThemeData.new
496496
end
497497

498498
it 'should not fail if theme file resolves to nil' do
499499
theme = subject.load_file fixture_file 'nil-theme.yml'
500-
(expect theme).to be_an OpenStruct
500+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
501501
theme.delete_field :__loaded__
502-
(expect theme).to eql OpenStruct.new
502+
(expect theme).to eql Asciidoctor::PDF::ThemeData.new
503503
end
504504

505505
it 'should throw error that includes filename and reason if theme is indented using tabs' do
@@ -739,7 +739,7 @@
739739
it 'should load base theme if theme name is base' do
740740
theme = subject.load_theme 'base'
741741
(expect theme).not_to be_nil
742-
(expect theme).to be_an OpenStruct
742+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
743743
(expect theme.base_font_family).to eql 'Helvetica'
744744
(expect theme.codespan_font_family).to eql 'Courier'
745745
(expect theme).to eql subject.load_base_theme
@@ -748,7 +748,7 @@
748748
it 'should load default theme if no arguments are given' do
749749
theme = subject.load_theme
750750
(expect theme).not_to be_nil
751-
(expect theme).to be_an OpenStruct
751+
(expect theme).to be_an Asciidoctor::PDF::ThemeData
752752
(expect theme.base_font_family).to eql 'Noto Serif'
753753
(expect theme.link_font_color).to eql '428BCA'
754754
end

0 commit comments

Comments
 (0)