nanoc-core 4.11.12 → 4.11.13

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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/lib/nanoc/core.rb +37 -0
  3. data/lib/nanoc/core/basic_item_rep_collection_view.rb +88 -0
  4. data/lib/nanoc/core/basic_item_rep_view.rb +83 -0
  5. data/lib/nanoc/core/basic_item_view.rb +54 -0
  6. data/lib/nanoc/core/checksummer.rb +2 -0
  7. data/lib/nanoc/core/compilation_item_rep_collection_view.rb +12 -0
  8. data/lib/nanoc/core/compilation_item_rep_view.rb +51 -0
  9. data/lib/nanoc/core/compilation_item_view.rb +47 -0
  10. data/lib/nanoc/core/compilation_phases/abstract.rb +48 -0
  11. data/lib/nanoc/core/compilation_phases/cache.rb +43 -0
  12. data/lib/nanoc/core/compilation_phases/mark_done.rb +23 -0
  13. data/lib/nanoc/core/compilation_phases/notify.rb +19 -0
  14. data/lib/nanoc/core/compilation_phases/recalculate.rb +49 -0
  15. data/lib/nanoc/core/compilation_phases/resume.rb +52 -0
  16. data/lib/nanoc/core/compilation_phases/write.rb +84 -0
  17. data/lib/nanoc/core/compilation_stages/build_reps.rb +36 -0
  18. data/lib/nanoc/core/compilation_stages/calculate_checksums.rb +42 -0
  19. data/lib/nanoc/core/compilation_stages/cleanup.rb +43 -0
  20. data/lib/nanoc/core/compilation_stages/compile_reps.rb +96 -0
  21. data/lib/nanoc/core/compilation_stages/determine_outdatedness.rb +49 -0
  22. data/lib/nanoc/core/compilation_stages/forget_outdated_dependencies.rb +20 -0
  23. data/lib/nanoc/core/compilation_stages/load_stores.rb +35 -0
  24. data/lib/nanoc/core/compilation_stages/postprocess.rb +21 -0
  25. data/lib/nanoc/core/compilation_stages/preprocess.rb +32 -0
  26. data/lib/nanoc/core/compilation_stages/prune.rb +30 -0
  27. data/lib/nanoc/core/compilation_stages/store_post_compilation_state.rb +20 -0
  28. data/lib/nanoc/core/compilation_stages/store_pre_compilation_state.rb +32 -0
  29. data/lib/nanoc/core/compiler.rb +214 -0
  30. data/lib/nanoc/core/compiler_loader.rb +48 -0
  31. data/lib/nanoc/core/config_loader.rb +95 -0
  32. data/lib/nanoc/core/config_view.rb +67 -0
  33. data/lib/nanoc/core/configuration.rb +2 -4
  34. data/lib/nanoc/core/document_view_mixin.rb +87 -0
  35. data/lib/nanoc/core/errors.rb +97 -0
  36. data/lib/nanoc/core/executor.rb +134 -0
  37. data/lib/nanoc/core/feature.rb +92 -0
  38. data/lib/nanoc/core/filter.rb +269 -0
  39. data/lib/nanoc/core/identifiable_collection_view.rb +111 -0
  40. data/lib/nanoc/core/item_collection_with_reps_view.rb +12 -0
  41. data/lib/nanoc/core/item_collection_without_reps_view.rb +12 -0
  42. data/lib/nanoc/core/item_rep_builder.rb +54 -0
  43. data/lib/nanoc/core/item_rep_selector.rb +67 -0
  44. data/lib/nanoc/core/item_rep_writer.rb +85 -0
  45. data/lib/nanoc/core/layout_collection_view.rb +12 -0
  46. data/lib/nanoc/core/layout_view.rb +9 -0
  47. data/lib/nanoc/core/mutable_config_view.rb +16 -0
  48. data/lib/nanoc/core/mutable_document_view_mixin.rb +60 -0
  49. data/lib/nanoc/core/mutable_identifiable_collection_view.rb +19 -0
  50. data/lib/nanoc/core/mutable_item_collection_view.rb +34 -0
  51. data/lib/nanoc/core/mutable_item_view.rb +9 -0
  52. data/lib/nanoc/core/mutable_layout_collection_view.rb +26 -0
  53. data/lib/nanoc/core/mutable_layout_view.rb +9 -0
  54. data/lib/nanoc/core/outdatedness_checker.rb +222 -0
  55. data/lib/nanoc/core/outdatedness_rules/attributes_modified.rb +41 -0
  56. data/lib/nanoc/core/outdatedness_rules/code_snippets_modified.rb +31 -0
  57. data/lib/nanoc/core/outdatedness_rules/content_modified.rb +21 -0
  58. data/lib/nanoc/core/outdatedness_rules/item_collection_extended.rb +20 -0
  59. data/lib/nanoc/core/outdatedness_rules/layout_collection_extended.rb +20 -0
  60. data/lib/nanoc/core/outdatedness_rules/not_written.rb +17 -0
  61. data/lib/nanoc/core/outdatedness_rules/rules_modified.rb +45 -0
  62. data/lib/nanoc/core/outdatedness_rules/uses_always_outdated_filter.rb +26 -0
  63. data/lib/nanoc/core/post_compile_item_collection_view.rb +12 -0
  64. data/lib/nanoc/core/post_compile_item_rep_collection_view.rb +12 -0
  65. data/lib/nanoc/core/post_compile_item_rep_view.rb +33 -0
  66. data/lib/nanoc/core/post_compile_item_view.rb +20 -0
  67. data/lib/nanoc/core/pruner.rb +119 -0
  68. data/lib/nanoc/core/site_loader.rb +102 -0
  69. data/lib/nanoc/core/trivial_error.rb +10 -0
  70. data/lib/nanoc/core/version.rb +1 -1
  71. data/lib/nanoc/core/view.rb +43 -0
  72. data/lib/nanoc/core/view_context_for_compilation.rb +6 -6
  73. metadata +95 -2
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ module OutdatednessRules
6
+ class AttributesModified < Nanoc::Core::OutdatednessRule
7
+ include Nanoc::Core::ContractsSupport
8
+
9
+ affects_props :attributes, :compiled_content
10
+
11
+ contract C::Or[Nanoc::Core::ItemRep, Nanoc::Core::Item, Nanoc::Core::Configuration, Nanoc::Core::Layout], C::Named['Nanoc::Core::OutdatednessChecker'] => C::Maybe[Nanoc::Core::OutdatednessReasons::Generic]
12
+ def apply(obj, outdatedness_checker)
13
+ case obj
14
+ when Nanoc::Core::ItemRep
15
+ apply(obj.item, outdatedness_checker)
16
+ when Nanoc::Core::Item, Nanoc::Core::Layout, Nanoc::Core::Configuration
17
+ if outdatedness_checker.checksum_store[obj] == outdatedness_checker.checksums.checksum_for(obj)
18
+ return nil
19
+ end
20
+
21
+ old_checksums = outdatedness_checker.checksum_store.attributes_checksum_for(obj)
22
+ unless old_checksums
23
+ return Nanoc::Core::OutdatednessReasons::AttributesModified.new(true)
24
+ end
25
+
26
+ new_checksums = outdatedness_checker.checksums.attributes_checksum_for(obj)
27
+
28
+ attributes = Set.new(old_checksums.keys) + Set.new(new_checksums.keys)
29
+ changed_attributes = attributes.reject { |a| old_checksums[a] == new_checksums[a] }
30
+
31
+ if changed_attributes.any?
32
+ Nanoc::Core::OutdatednessReasons::AttributesModified.new(changed_attributes)
33
+ end
34
+ else
35
+ raise ArgumentError
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ module OutdatednessRules
6
+ class CodeSnippetsModified < Nanoc::Core::OutdatednessRule
7
+ DDMemoize.activate(self)
8
+
9
+ include Nanoc::Core::ContractsSupport
10
+
11
+ affects_props :raw_content, :attributes, :compiled_content, :path
12
+
13
+ def apply(_obj, outdatedness_checker)
14
+ if any_snippets_modified?(outdatedness_checker)
15
+ Nanoc::Core::OutdatednessReasons::CodeSnippetsModified
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ memoized def any_snippets_modified?(outdatedness_checker)
22
+ outdatedness_checker.site.code_snippets.any? do |cs|
23
+ ch_old = outdatedness_checker.checksum_store[cs]
24
+ ch_new = outdatedness_checker.checksums.checksum_for(cs)
25
+ ch_old != ch_new
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ module OutdatednessRules
6
+ class ContentModified < Nanoc::Core::OutdatednessRule
7
+ affects_props :raw_content, :compiled_content
8
+
9
+ def apply(obj, outdatedness_checker)
10
+ obj = obj.item if obj.is_a?(Nanoc::Core::ItemRep)
11
+
12
+ ch_old = outdatedness_checker.checksum_store.content_checksum_for(obj)
13
+ ch_new = outdatedness_checker.checksums.content_checksum_for(obj)
14
+ if ch_old != ch_new
15
+ Nanoc::Core::OutdatednessReasons::ContentModified
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ module OutdatednessRules
6
+ class ItemCollectionExtended < Nanoc::Core::OutdatednessRule
7
+ affects_props :raw_content
8
+
9
+ contract Nanoc::Core::ItemCollection, C::Named['Nanoc::Core::OutdatednessChecker'] => C::Maybe[Nanoc::Core::OutdatednessReasons::Generic]
10
+ def apply(_obj, outdatedness_checker)
11
+ new_items = outdatedness_checker.dependency_store.new_items
12
+
13
+ if new_items.any?
14
+ Nanoc::Core::OutdatednessReasons::ItemCollectionExtended.new(new_items)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ module OutdatednessRules
6
+ class LayoutCollectionExtended < Nanoc::Core::OutdatednessRule
7
+ affects_props :raw_content
8
+
9
+ contract Nanoc::Core::LayoutCollection, C::Named['Nanoc::Core::OutdatednessChecker'] => C::Maybe[Nanoc::Core::OutdatednessReasons::Generic]
10
+ def apply(_obj, outdatedness_checker)
11
+ new_layouts = outdatedness_checker.dependency_store.new_layouts
12
+
13
+ if new_layouts.any?
14
+ Nanoc::Core::OutdatednessReasons::LayoutCollectionExtended.new(new_layouts)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ module OutdatednessRules
6
+ class NotWritten < Nanoc::Core::OutdatednessRule
7
+ affects_props :raw_content, :attributes, :compiled_content, :path
8
+
9
+ def apply(obj, _outdatedness_checker)
10
+ if obj.raw_paths.values.flatten.compact.any? { |fn| !File.file?(fn) }
11
+ Nanoc::Core::OutdatednessReasons::NotWritten
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ module OutdatednessRules
6
+ class RulesModified < Nanoc::Core::OutdatednessRule
7
+ affects_props :compiled_content, :path
8
+
9
+ def apply(obj, outdatedness_checker)
10
+ # Check rules of obj itself
11
+ if rules_modified?(obj, outdatedness_checker)
12
+ return Nanoc::Core::OutdatednessReasons::RulesModified
13
+ end
14
+
15
+ # Check rules of layouts used by obj
16
+ layouts = layouts_touched_by(obj, outdatedness_checker)
17
+ if layouts.any? { |layout| rules_modified?(layout, outdatedness_checker) }
18
+ return Nanoc::Core::OutdatednessReasons::RulesModified
19
+ end
20
+
21
+ nil
22
+ end
23
+
24
+ private
25
+
26
+ def rules_modified?(obj, outdatedness_checker)
27
+ seq_old = outdatedness_checker.action_sequence_store[obj]
28
+ seq_new = outdatedness_checker.action_sequence_for(obj).serialize
29
+
30
+ !seq_old.eql?(seq_new)
31
+ end
32
+
33
+ def layouts_touched_by(obj, outdatedness_checker)
34
+ actions = outdatedness_checker.action_sequence_store[obj]
35
+ layout_actions = actions.select { |a| a.first == :layout }
36
+
37
+ layout_actions.map do |layout_action|
38
+ layout_pattern = layout_action[1]
39
+ outdatedness_checker.site.layouts[layout_pattern]
40
+ end.compact
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ module OutdatednessRules
6
+ class UsesAlwaysOutdatedFilter < Nanoc::Core::OutdatednessRule
7
+ affects_props :raw_content, :attributes, :path
8
+
9
+ def apply(obj, outdatedness_checker)
10
+ seq = outdatedness_checker.action_sequence_for(obj)
11
+ if any_always_outdated?(seq)
12
+ Nanoc::Core::OutdatednessReasons::UsesAlwaysOutdatedFilter
13
+ end
14
+ end
15
+
16
+ def any_always_outdated?(seq)
17
+ seq
18
+ .select { |a| a.is_a?(Nanoc::Core::ProcessingActions::Filter) }
19
+ .map { |a| Nanoc::Core::Filter.named(a.filter_name) }
20
+ .compact
21
+ .any?(&:always_outdated?)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class PostCompileItemCollectionView < Nanoc::Core::IdentifiableCollectionView
6
+ # @api private
7
+ def view_class
8
+ Nanoc::Core::PostCompileItemView
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class PostCompileItemRepCollectionView < Nanoc::Core::BasicItemRepCollectionView
6
+ # @api private
7
+ def view_class
8
+ Nanoc::Core::PostCompileItemRepView
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class PostCompileItemRepView < ::Nanoc::Core::BasicItemRepView
6
+ def item_view_class
7
+ Nanoc::Core::PostCompileItemView
8
+ end
9
+
10
+ def compiled_content(snapshot: nil)
11
+ compilation_context = @context.compilation_context
12
+ snapshot_contents = compilation_context.compiled_content_cache[_unwrap] || {}
13
+
14
+ snapshot_name = snapshot || (snapshot_contents[:pre] ? :pre : :last)
15
+
16
+ unless snapshot_contents[snapshot_name]
17
+ raise Nanoc::Core::Errors::NoSuchSnapshot.new(_unwrap, snapshot_name)
18
+ end
19
+
20
+ content = snapshot_contents[snapshot_name]
21
+ if content.binary?
22
+ raise Nanoc::Core::Errors::CannotGetCompiledContentOfBinaryItem.new(_unwrap)
23
+ end
24
+
25
+ content.string
26
+ end
27
+
28
+ def raw_path(snapshot: :last)
29
+ @item_rep.raw_path(snapshot: snapshot)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class PostCompileItemView < Nanoc::Core::CompilationItemView
6
+ def reps
7
+ Nanoc::Core::PostCompileItemRepCollectionView.new(@context.reps[_unwrap], @context)
8
+ end
9
+
10
+ # @deprecated Use {#modified_reps} instead
11
+ def modified
12
+ modified_reps
13
+ end
14
+
15
+ def modified_reps
16
+ reps.select { |rep| rep._unwrap.modified? }
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ # Responsible for finding and deleting files in the site’s output directory
6
+ # that are not managed by Nanoc.
7
+ class Pruner
8
+ include Nanoc::Core::ContractsSupport
9
+
10
+ contract Nanoc::Core::Configuration, Nanoc::Core::ItemRepRepo, C::KeywordArgs[dry_run: C::Optional[C::Bool], exclude: C::Optional[C::IterOf[String]]] => C::Any
11
+ def initialize(config, reps, dry_run: false, exclude: [])
12
+ @config = config
13
+ @reps = reps
14
+ @dry_run = dry_run
15
+ @exclude = Set.new(exclude)
16
+ end
17
+
18
+ def run
19
+ return unless File.directory?(@config.output_dir)
20
+
21
+ compiled_files = @reps.flat_map { |r| r.raw_paths.values.flatten }.compact
22
+ present_files, present_dirs = files_and_dirs_in(@config.output_dir + '/')
23
+
24
+ remove_stray_files(present_files, compiled_files)
25
+ remove_empty_directories(present_dirs)
26
+ end
27
+
28
+ contract String => C::Bool
29
+ def filename_excluded?(filename)
30
+ pathname = Pathname.new(strip_output_dir(filename))
31
+ @exclude.any? { |e| pathname_components(pathname).include?(e) }
32
+ end
33
+
34
+ contract String => String
35
+ def strip_output_dir(filename)
36
+ if filename.start_with?(@config.output_dir)
37
+ filename[@config.output_dir.size..-1]
38
+ else
39
+ filename
40
+ end
41
+ end
42
+
43
+ contract Pathname => C::ArrayOf[String]
44
+ def pathname_components(pathname)
45
+ components = []
46
+ tmp = pathname
47
+ loop do
48
+ old = tmp
49
+ components << File.basename(tmp)
50
+ tmp = File.dirname(tmp)
51
+ break if old == tmp
52
+ end
53
+ components.reverse
54
+ end
55
+
56
+ contract C::ArrayOf[String], C::ArrayOf[String] => self
57
+ # @api private
58
+ def remove_stray_files(present_files, compiled_files)
59
+ (present_files - compiled_files).each do |f|
60
+ delete_file(f) unless filename_excluded?(f)
61
+ end
62
+ self
63
+ end
64
+
65
+ contract C::ArrayOf[String] => self
66
+ # @api private
67
+ def remove_empty_directories(present_dirs)
68
+ present_dirs.reverse_each do |dir|
69
+ next if Dir.foreach(dir) { |n| break true if n !~ /\A\.\.?\z/ }
70
+ next if filename_excluded?(dir)
71
+
72
+ delete_dir(dir)
73
+ end
74
+ self
75
+ end
76
+
77
+ contract String => C::ArrayOf[C::ArrayOf[String]]
78
+ # @api private
79
+ def files_and_dirs_in(dir)
80
+ present_files = []
81
+ present_dirs = []
82
+
83
+ expanded_dir = File.expand_path(dir)
84
+
85
+ Find.find(dir) do |f|
86
+ case File.ftype(f)
87
+ when 'file'
88
+ unless filename_excluded?(f)
89
+ present_files << f
90
+ end
91
+ when 'directory'
92
+ if filename_excluded?(f)
93
+ Find.prune
94
+ elsif expanded_dir != File.expand_path(f)
95
+ present_dirs << f
96
+ end
97
+ end
98
+ end
99
+
100
+ [present_files, present_dirs]
101
+ end
102
+
103
+ protected
104
+
105
+ def delete_file(file)
106
+ log_delete_and_run(file) { FileUtils.rm(file) }
107
+ end
108
+
109
+ def delete_dir(dir)
110
+ log_delete_and_run(dir) { Dir.rmdir(dir) }
111
+ end
112
+
113
+ def log_delete_and_run(thing)
114
+ Nanoc::Core::NotificationCenter.post(:file_pruned, thing)
115
+ yield unless @dry_run
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class SiteLoader
6
+ ENCODING_REGEX = /\A#\s+(-\*-\s+)?(en)?coding: (?<encoding>[^\s]+)(\s+-\*-\s*)?\n{0,2}/.freeze
7
+
8
+ def new_from_cwd
9
+ site_from_config(Nanoc::Core::ConfigLoader.new.new_from_cwd)
10
+ end
11
+
12
+ # @return [Boolean]
13
+ def self.cwd_is_nanoc_site?
14
+ Nanoc::Core::ConfigLoader.cwd_is_nanoc_site?
15
+ end
16
+
17
+ def gen_data_source_for_config(config)
18
+ data_sources_to_aggregate =
19
+ with_data_sources(config) do |data_sources|
20
+ data_sources.map do |ds|
21
+ Nanoc::Core::PrefixedDataSource.new(ds, ds.items_root, ds.layouts_root)
22
+ end
23
+ end
24
+
25
+ Nanoc::Core::AggregateDataSource.new(data_sources_to_aggregate, config)
26
+ end
27
+
28
+ private
29
+
30
+ def site_from_config(config)
31
+ code_snippets = code_snippets_from_config(config)
32
+ code_snippets.each(&:load)
33
+
34
+ data_source = gen_data_source_for_config(config)
35
+
36
+ Nanoc::Core::Site.new(
37
+ config: config,
38
+ code_snippets: code_snippets,
39
+ data_source: data_source,
40
+ )
41
+ end
42
+
43
+ def with_data_sources(config, &_block)
44
+ data_sources = create_data_sources(config)
45
+
46
+ begin
47
+ data_sources.each(&:use)
48
+ yield(data_sources)
49
+ ensure
50
+ data_sources.each(&:unuse)
51
+ end
52
+ end
53
+
54
+ def create_data_sources(config)
55
+ config[:data_sources].map do |data_source_hash|
56
+ # Get data source class
57
+ data_source_class = Nanoc::Core::DataSource.named(data_source_hash[:type].to_sym)
58
+ if data_source_class.nil?
59
+ raise Nanoc::Core::Errors::UnknownDataSource.new(data_source_hash[:type])
60
+ end
61
+
62
+ # Create data source
63
+ data_source_class.new(
64
+ config,
65
+ data_source_hash[:items_root],
66
+ data_source_hash[:layouts_root],
67
+ data_source_hash.merge(data_source_hash[:config] || {}),
68
+ )
69
+ end
70
+ end
71
+
72
+ def code_snippets_from_config(config)
73
+ config[:lib_dirs].flat_map do |lib|
74
+ Dir["#{lib}/**/*.rb"].sort.map do |filename|
75
+ Nanoc::Core::CodeSnippet.new(
76
+ read_code_snippet_contents(filename),
77
+ filename,
78
+ )
79
+ end
80
+ end
81
+ end
82
+
83
+ def encoding_from_magic_comment(raw)
84
+ match = ENCODING_REGEX.match(raw)
85
+ match ? match['encoding'] : nil
86
+ end
87
+
88
+ def read_code_snippet_contents(filename)
89
+ raw = File.read(filename, encoding: 'ASCII-8BIT')
90
+
91
+ enc = encoding_from_magic_comment(raw)
92
+ if enc
93
+ raw = raw.force_encoding(enc).encode('UTF-8').sub(ENCODING_REGEX, '')
94
+ else
95
+ raw.force_encoding('UTF-8')
96
+ end
97
+
98
+ raw
99
+ end
100
+ end
101
+ end
102
+ end