abroad 1.0.0
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.
- checksums.yaml +7 -0
- data/README.md +142 -0
- data/abroad.gemspec +25 -0
- data/lib/abroad/extractors/extractor.rb +46 -0
- data/lib/abroad/extractors/json/json_extractor.rb +17 -0
- data/lib/abroad/extractors/json/key_value_extractor.rb +38 -0
- data/lib/abroad/extractors/json.rb +8 -0
- data/lib/abroad/extractors/xml/android_extractor.rb +113 -0
- data/lib/abroad/extractors/xml/xml_extractor.rb +28 -0
- data/lib/abroad/extractors/xml.rb +8 -0
- data/lib/abroad/extractors/yaml/dotted_key_extractor.rb +40 -0
- data/lib/abroad/extractors/yaml/jruby_compat.rb +170 -0
- data/lib/abroad/extractors/yaml/rails_extractor.rb +15 -0
- data/lib/abroad/extractors/yaml/yaml_extractor.rb +28 -0
- data/lib/abroad/extractors/yaml.rb +9 -0
- data/lib/abroad/extractors.rb +28 -0
- data/lib/abroad/serializers/json/json_serializer.rb +27 -0
- data/lib/abroad/serializers/json/key_value_serializer.rb +20 -0
- data/lib/abroad/serializers/json.rb +8 -0
- data/lib/abroad/serializers/serializer.rb +52 -0
- data/lib/abroad/serializers/trie.rb +76 -0
- data/lib/abroad/serializers/xml/android_serializer.rb +143 -0
- data/lib/abroad/serializers/xml/xml_serializer.rb +23 -0
- data/lib/abroad/serializers/xml.rb +8 -0
- data/lib/abroad/serializers/yaml/rails_serializer.rb +110 -0
- data/lib/abroad/serializers/yaml/yaml_serializer.rb +19 -0
- data/lib/abroad/serializers/yaml.rb +8 -0
- data/lib/abroad/serializers.rb +29 -0
- data/lib/abroad/version.rb +3 -0
- data/lib/abroad.rb +37 -0
- data/lib/ext/htmlentities/android_xml_decoder.rb +15 -0
- data/lib/ext/htmlentities/android_xml_encoder.rb +23 -0
- data/spec/abroad_spec.rb +35 -0
- data/spec/extractors/json/fixtures/arrays.json +9 -0
- data/spec/extractors/json/fixtures/basic.json +5 -0
- data/spec/extractors/json/fixtures/objects.json +11 -0
- data/spec/extractors/json/fixtures.yml +22 -0
- data/spec/extractors/json/json_extractor_spec.rb +6 -0
- data/spec/extractors/xml/fixtures/basic_arrays.xml +9 -0
- data/spec/extractors/xml/fixtures/basic_plurals.xml +8 -0
- data/spec/extractors/xml/fixtures/basic_strings.xml +8 -0
- data/spec/extractors/xml/fixtures/entities.xml +4 -0
- data/spec/extractors/xml/fixtures/markup.xml +5 -0
- data/spec/extractors/xml/fixtures/newlines.xml +6 -0
- data/spec/extractors/xml/fixtures/quotes.xml +9 -0
- data/spec/extractors/xml/fixtures.yml +54 -0
- data/spec/extractors/xml/xml_extractor_spec.rb +6 -0
- data/spec/extractors/yaml/fixtures/arrays.yml +9 -0
- data/spec/extractors/yaml/fixtures/arrays_and_hashes.yml +29 -0
- data/spec/extractors/yaml/fixtures/invalid_single_quote_escape.yml +2 -0
- data/spec/extractors/yaml/fixtures/invalid_single_quote_escape_array.yml +3 -0
- data/spec/extractors/yaml/fixtures/nesting.yml +19 -0
- data/spec/extractors/yaml/fixtures/short.yml +2 -0
- data/spec/extractors/yaml/fixtures.yml +75 -0
- data/spec/extractors/yaml/jruby_compat_spec.rb +54 -0
- data/spec/extractors/yaml/yaml_extractor_spec.rb +6 -0
- data/spec/serializers/json/key_value_serializer_spec.rb +26 -0
- data/spec/serializers/xml/android_serializer_spec.rb +165 -0
- data/spec/serializers/yaml/rails_serializer_spec.rb +171 -0
- data/spec/spec_helper.rb +43 -0
- metadata +186 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Abroad
|
4
|
+
module Extractors
|
5
|
+
module Yaml
|
6
|
+
|
7
|
+
class YamlExtractor < Extractor
|
8
|
+
private
|
9
|
+
|
10
|
+
def parse
|
11
|
+
YAML.load(clean_yaml(stream.read))
|
12
|
+
rescue Psych::SyntaxError => e
|
13
|
+
raise Abroad::SyntaxError, "Syntax error in yaml: #{e.message}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def clean_yaml(yaml_content)
|
17
|
+
if Abroad.jruby?
|
18
|
+
require 'abroad/extractors/yaml/jruby_compat'
|
19
|
+
JRubyCompat.clean(yaml_content)
|
20
|
+
else
|
21
|
+
yaml_content
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Abroad
|
2
|
+
module Extractors
|
3
|
+
module Yaml
|
4
|
+
autoload :DottedKeyExtractor, 'abroad/extractors/yaml/dotted_key_extractor'
|
5
|
+
autoload :RailsExtractor, 'abroad/extractors/yaml/rails_extractor'
|
6
|
+
autoload :YamlExtractor, 'abroad/extractors/yaml/yaml_extractor'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Abroad
|
2
|
+
module Extractors
|
3
|
+
autoload :Extractor, 'abroad/extractors/extractor'
|
4
|
+
autoload :Json, 'abroad/extractors/json'
|
5
|
+
autoload :Xml, 'abroad/extractors/xml'
|
6
|
+
autoload :Yaml, 'abroad/extractors/yaml'
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def register(id, klass)
|
10
|
+
registered[id.to_s] = klass
|
11
|
+
end
|
12
|
+
|
13
|
+
def get(id)
|
14
|
+
registered[id.to_s]
|
15
|
+
end
|
16
|
+
|
17
|
+
def available
|
18
|
+
registered.keys
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def registered
|
24
|
+
@registered ||= {}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'json-write-stream'
|
2
|
+
|
3
|
+
module Abroad
|
4
|
+
module Serializers
|
5
|
+
module Json
|
6
|
+
|
7
|
+
class JsonSerializer < Serializer
|
8
|
+
attr_reader :writer
|
9
|
+
|
10
|
+
def initialize(stream, locale, encoding = Encoding::UTF_8)
|
11
|
+
super
|
12
|
+
@writer = JsonWriteStream.from_stream(stream, encoding)
|
13
|
+
end
|
14
|
+
|
15
|
+
def write_raw(text)
|
16
|
+
writer.stream.write(text)
|
17
|
+
end
|
18
|
+
|
19
|
+
def flush
|
20
|
+
writer.flush
|
21
|
+
stream.flush
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Abroad
|
2
|
+
module Serializers
|
3
|
+
module Json
|
4
|
+
|
5
|
+
class KeyValueSerializer < JsonSerializer
|
6
|
+
def initialize(stream, locale, encoding = Encoding::UTF_8)
|
7
|
+
super
|
8
|
+
writer.write_object
|
9
|
+
end
|
10
|
+
|
11
|
+
def write_key_value(key, value)
|
12
|
+
writer.write_key_value(
|
13
|
+
key.encode(encoding), value.encode(encoding)
|
14
|
+
)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Abroad
|
2
|
+
module Serializers
|
3
|
+
|
4
|
+
class Serializer
|
5
|
+
attr_reader :stream, :locale, :encoding
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def from_stream(stream, locale)
|
9
|
+
serializer = new(stream, locale)
|
10
|
+
|
11
|
+
if block_given?
|
12
|
+
yield(serializer).tap do
|
13
|
+
serializer.close
|
14
|
+
end
|
15
|
+
else
|
16
|
+
serializer
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def open(file, locale, &block)
|
21
|
+
from_stream(File.open(file), locale, &block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(stream, locale, encoding = Encoding::UTF_8)
|
26
|
+
@stream = stream
|
27
|
+
@locale = locale
|
28
|
+
@encoding = encoding
|
29
|
+
end
|
30
|
+
|
31
|
+
def write_key_value(key, value)
|
32
|
+
raise NotImplementedError,
|
33
|
+
'expected to be implemented in derived classes'
|
34
|
+
end
|
35
|
+
|
36
|
+
def write_raw(text)
|
37
|
+
raise NotImplementedError,
|
38
|
+
'expected to be implemented in derived classes'
|
39
|
+
end
|
40
|
+
|
41
|
+
def flush
|
42
|
+
stream.flush
|
43
|
+
end
|
44
|
+
|
45
|
+
def close
|
46
|
+
flush
|
47
|
+
stream.close
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Abroad
|
2
|
+
module Serializers
|
3
|
+
|
4
|
+
class Trie
|
5
|
+
attr_reader :root
|
6
|
+
|
7
|
+
def initialize(root = nil)
|
8
|
+
@root = root || TrieNode.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def add(key_enum, value)
|
12
|
+
node = root
|
13
|
+
|
14
|
+
key_enum.each do |key|
|
15
|
+
if node.has_child?(key)
|
16
|
+
node = node.child_at(key)
|
17
|
+
else
|
18
|
+
node = node.add_child(key, TrieNode.new)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
node.value = value
|
23
|
+
end
|
24
|
+
|
25
|
+
def find(key_enum)
|
26
|
+
node = root
|
27
|
+
key_enum.each do |key|
|
28
|
+
node = node.child_at(key)
|
29
|
+
return nil unless node
|
30
|
+
end
|
31
|
+
node.value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class TrieNode
|
36
|
+
NO_VALUE = :__novalue__
|
37
|
+
|
38
|
+
attr_reader :children
|
39
|
+
attr_accessor :value
|
40
|
+
|
41
|
+
def initialize(value = NO_VALUE)
|
42
|
+
@value = value
|
43
|
+
@children = {}
|
44
|
+
end
|
45
|
+
|
46
|
+
def each_child
|
47
|
+
if block_given?
|
48
|
+
children.each_pair { |key, child| yield key, child }
|
49
|
+
else
|
50
|
+
children.each
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def has_children?
|
55
|
+
!children.empty?
|
56
|
+
end
|
57
|
+
|
58
|
+
def has_child?(key)
|
59
|
+
children.include?(key)
|
60
|
+
end
|
61
|
+
|
62
|
+
def child_at(key)
|
63
|
+
children[key]
|
64
|
+
end
|
65
|
+
|
66
|
+
def add_child(key, node)
|
67
|
+
@children[key] = node
|
68
|
+
end
|
69
|
+
|
70
|
+
def has_value?
|
71
|
+
value != NO_VALUE
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'htmlentities'
|
2
|
+
require 'ext/htmlentities/android_xml_encoder'
|
3
|
+
|
4
|
+
module Abroad
|
5
|
+
module Serializers
|
6
|
+
module Xml
|
7
|
+
|
8
|
+
class AndroidSerializer < XmlSerializer
|
9
|
+
PLURAL_FORMS = %w(
|
10
|
+
zero one two few many other
|
11
|
+
)
|
12
|
+
|
13
|
+
attr_reader :plurals, :arrays
|
14
|
+
|
15
|
+
def after_initialize
|
16
|
+
writer.open_tag('resources')
|
17
|
+
@plurals = Hash.new { |hash, key| hash[key] = {} }
|
18
|
+
@arrays = Hash.new { |hash, key| hash[key] = {} }
|
19
|
+
end
|
20
|
+
|
21
|
+
def write_raw(text)
|
22
|
+
stream.write(text)
|
23
|
+
end
|
24
|
+
|
25
|
+
def write_key_value(key, value)
|
26
|
+
key_parts = split_key(key)
|
27
|
+
|
28
|
+
case detect_string_type(key_parts)
|
29
|
+
when :plural
|
30
|
+
record_plural(key_parts, value)
|
31
|
+
when :array_element
|
32
|
+
record_array_element(key_parts, value)
|
33
|
+
else
|
34
|
+
write_string(key, value)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def flush
|
39
|
+
write_recorded_plurals
|
40
|
+
write_recorded_arrays
|
41
|
+
writer.flush
|
42
|
+
stream.flush
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def split_key(key)
|
48
|
+
if idx = key.rindex('.')
|
49
|
+
[key[0..(idx - 1)], key[(idx + 1)..-1]]
|
50
|
+
else
|
51
|
+
# this case should never happen
|
52
|
+
[key, '']
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def record_plural(key_parts, value)
|
57
|
+
plurals[key_parts.first][key_parts.last] = value
|
58
|
+
end
|
59
|
+
|
60
|
+
def record_array_element(key_parts, value)
|
61
|
+
arrays[key_parts.first][key_parts.last.to_i] = value
|
62
|
+
end
|
63
|
+
|
64
|
+
def write_plural(name, plural_forms)
|
65
|
+
writer.open_tag(:plurals, name: name)
|
66
|
+
|
67
|
+
plural_forms.each_pair do |quantity, value|
|
68
|
+
writer.open_single_line_tag(:item, quantity: quantity)
|
69
|
+
write_text(value)
|
70
|
+
writer.close_tag
|
71
|
+
end
|
72
|
+
|
73
|
+
writer.close_tag
|
74
|
+
end
|
75
|
+
|
76
|
+
def write_array(name, array_elements)
|
77
|
+
writer.open_tag(:'string-array', name: name)
|
78
|
+
count = array_elements.keys.max
|
79
|
+
|
80
|
+
(0..count).each do |i|
|
81
|
+
writer.open_single_line_tag(:item)
|
82
|
+
write_text(array_elements[i] || '')
|
83
|
+
writer.close_tag
|
84
|
+
end
|
85
|
+
|
86
|
+
writer.close_tag
|
87
|
+
end
|
88
|
+
|
89
|
+
def write_recorded_plurals
|
90
|
+
plurals.each_pair do |name, plural_forms|
|
91
|
+
write_plural(name, plural_forms)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def write_recorded_arrays
|
96
|
+
arrays.each_pair do |name, array_elements|
|
97
|
+
write_array(name, array_elements)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def write_string(key, value)
|
102
|
+
writer.open_single_line_tag(:string, name: key)
|
103
|
+
write_text(value)
|
104
|
+
writer.close_tag
|
105
|
+
end
|
106
|
+
|
107
|
+
def write_text(text)
|
108
|
+
escaped_text = escape(text)
|
109
|
+
writer.write_text(
|
110
|
+
escaped_text, escape: false
|
111
|
+
)
|
112
|
+
end
|
113
|
+
|
114
|
+
def detect_string_type(key_parts)
|
115
|
+
last_part = key_parts.last
|
116
|
+
|
117
|
+
if PLURAL_FORMS.include?(last_part)
|
118
|
+
:plural
|
119
|
+
elsif last_part =~ /\A[\d]+\z/
|
120
|
+
:array_element
|
121
|
+
else
|
122
|
+
:string
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def escape(text)
|
127
|
+
text.gsub!("\n", "\\n") # escape literal newlines
|
128
|
+
text.gsub!("\r", "\\r") # escape literal carriage returns
|
129
|
+
text.gsub!("\t", "\\t") # escape literal tabs
|
130
|
+
text.gsub!(/([^\\]?)(')/) { "#{$1}\\'" } # escape single quotes
|
131
|
+
text.gsub!(/([^\\]?)(")/) { "#{$1}\\\"" } # escape double quotes
|
132
|
+
|
133
|
+
coder.encode(text)
|
134
|
+
end
|
135
|
+
|
136
|
+
def coder
|
137
|
+
@coder ||= HTMLEntities::AndroidXmlEncoder.new
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'xml-write-stream'
|
2
|
+
|
3
|
+
module Abroad
|
4
|
+
module Serializers
|
5
|
+
module Xml
|
6
|
+
|
7
|
+
class XmlSerializer < Serializer
|
8
|
+
attr_reader :writer
|
9
|
+
|
10
|
+
def initialize(stream, locale, encoding = Encoding::UTF_8)
|
11
|
+
super
|
12
|
+
@writer = XmlWriteStream.from_stream(stream)
|
13
|
+
writer.write_header(encoding: encoding.to_s)
|
14
|
+
after_initialize
|
15
|
+
end
|
16
|
+
|
17
|
+
def after_initialize
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module Abroad
|
2
|
+
module Serializers
|
3
|
+
module Yaml
|
4
|
+
|
5
|
+
class RailsSerializer < YamlSerializer
|
6
|
+
attr_reader :trie
|
7
|
+
|
8
|
+
def initialize(stream, locale, encoding = Encoding::UTF_8)
|
9
|
+
super
|
10
|
+
@trie = Abroad::Serializers::Trie.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def write_raw(text)
|
14
|
+
writer.stream.write(text)
|
15
|
+
end
|
16
|
+
|
17
|
+
def write_key_value(key, value)
|
18
|
+
key_parts = split_key(key)
|
19
|
+
encoded_value = value.encode(encoding)
|
20
|
+
trie.add(key_parts, encoded_value)
|
21
|
+
end
|
22
|
+
|
23
|
+
def flush
|
24
|
+
writer.write_map(locale)
|
25
|
+
write_node(trie.root, locale)
|
26
|
+
writer.flush
|
27
|
+
stream.flush
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def split_key(key)
|
33
|
+
# Doesn't allow dots to come before spaces or at the end of the key.
|
34
|
+
# Uses regex negative lookahead, that's what the (?!) sections are.
|
35
|
+
# Examples:
|
36
|
+
# 'timezones.Solomon Is.' => ['timezones', 'Solomon Is.']
|
37
|
+
# 'timezones.Solomon Is.foo' => ['timezones', 'Solomon Is', 'foo']
|
38
|
+
# 'timezones.Solomon Is..foo' => ['timezones', 'Solomon Is.', 'foo']
|
39
|
+
key.split(/\.(?!\s)(?!\z)(?!\.)/)
|
40
|
+
end
|
41
|
+
|
42
|
+
# depth-first
|
43
|
+
def write_node(node, parent_key)
|
44
|
+
if node
|
45
|
+
if node.has_children?
|
46
|
+
if children_are_sequence(node)
|
47
|
+
write_sequence(node, parent_key)
|
48
|
+
else
|
49
|
+
write_map(node, parent_key)
|
50
|
+
end
|
51
|
+
elsif node.has_value?
|
52
|
+
write_value(node, parent_key)
|
53
|
+
end
|
54
|
+
else
|
55
|
+
write_value(node, parent_key)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def write_value(node, parent_key)
|
60
|
+
value = node ? node.value : ''
|
61
|
+
if writer.in_map?
|
62
|
+
writer.write_key_value(parent_key, value)
|
63
|
+
else
|
64
|
+
writer.write_element(value)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def write_map(node, parent_key)
|
69
|
+
if writer.in_map?
|
70
|
+
writer.write_map(parent_key)
|
71
|
+
else
|
72
|
+
writer.write_map
|
73
|
+
end
|
74
|
+
|
75
|
+
node.each_child do |key, child|
|
76
|
+
write_node(child, key)
|
77
|
+
end
|
78
|
+
|
79
|
+
writer.close_map
|
80
|
+
end
|
81
|
+
|
82
|
+
def write_sequence(node, parent_key)
|
83
|
+
if writer.in_map?
|
84
|
+
writer.write_sequence(parent_key)
|
85
|
+
else
|
86
|
+
writer.write_sequence
|
87
|
+
end
|
88
|
+
|
89
|
+
generate_sequence(node).each do |element|
|
90
|
+
write_node(element, nil)
|
91
|
+
end
|
92
|
+
|
93
|
+
writer.close_sequence
|
94
|
+
end
|
95
|
+
|
96
|
+
def children_are_sequence(node)
|
97
|
+
node.children.all? { |key, _| key =~ /\A[\d]+\z/ }
|
98
|
+
end
|
99
|
+
|
100
|
+
def generate_sequence(node)
|
101
|
+
keys = node.children.keys.map(&:to_i)
|
102
|
+
keys.each_with_object(Array.new(keys.max)) do |idx, arr|
|
103
|
+
arr[idx] = node.children[idx.to_s]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'yaml-write-stream'
|
3
|
+
|
4
|
+
module Abroad
|
5
|
+
module Serializers
|
6
|
+
module Yaml
|
7
|
+
|
8
|
+
class YamlSerializer < Serializer
|
9
|
+
attr_reader :writer
|
10
|
+
|
11
|
+
def initialize(stream, locale, encoding = Encoding::UTF_8)
|
12
|
+
super
|
13
|
+
@writer = YamlWriteStream.from_stream(stream, encoding)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Abroad
|
2
|
+
module Serializers
|
3
|
+
autoload :Json, 'abroad/serializers/json'
|
4
|
+
autoload :Serializer, 'abroad/serializers/serializer'
|
5
|
+
autoload :Trie, 'abroad/serializers/trie'
|
6
|
+
autoload :Xml, 'abroad/serializers/xml'
|
7
|
+
autoload :Yaml, 'abroad/serializers/yaml'
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def register(id, klass)
|
11
|
+
registered[id] = klass
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(id)
|
15
|
+
registered[id]
|
16
|
+
end
|
17
|
+
|
18
|
+
def available
|
19
|
+
registered.keys
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def registered
|
25
|
+
@registered ||= {}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/abroad.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module Abroad
|
2
|
+
class SyntaxError < StandardError; end
|
3
|
+
|
4
|
+
autoload :Extractors, 'abroad/extractors'
|
5
|
+
autoload :Serializers, 'abroad/serializers'
|
6
|
+
|
7
|
+
Extractors.register('yaml/rails', Extractors::Yaml::RailsExtractor)
|
8
|
+
Extractors.register('yaml/dotted-key', Extractors::Yaml::DottedKeyExtractor)
|
9
|
+
Extractors.register('json/key-value', Extractors::Json::KeyValueExtractor)
|
10
|
+
Extractors.register('xml/android', Extractors::Xml::AndroidExtractor)
|
11
|
+
|
12
|
+
Serializers.register('yaml/rails', Serializers::Yaml::RailsSerializer)
|
13
|
+
Serializers.register('json/key-value', Serializers::Json::KeyValueSerializer)
|
14
|
+
Serializers.register('xml/android', Serializers::Xml::AndroidSerializer)
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def extractors
|
18
|
+
Extractors.available
|
19
|
+
end
|
20
|
+
|
21
|
+
def extractor(id)
|
22
|
+
Extractors.get(id)
|
23
|
+
end
|
24
|
+
|
25
|
+
def serializers
|
26
|
+
Serializers.available
|
27
|
+
end
|
28
|
+
|
29
|
+
def serializer(id)
|
30
|
+
Serializers.get(id)
|
31
|
+
end
|
32
|
+
|
33
|
+
def jruby?
|
34
|
+
RUBY_ENGINE == 'jruby'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'htmlentities'
|
2
|
+
|
3
|
+
class HTMLEntities
|
4
|
+
MAPPINGS['android_xml'] = MAPPINGS['xhtml1'].dup.tap do |mappings|
|
5
|
+
mappings.delete('apos')
|
6
|
+
end
|
7
|
+
|
8
|
+
FLAVORS << 'android_xml'
|
9
|
+
|
10
|
+
class AndroidXmlDecoder < Decoder
|
11
|
+
def initialize
|
12
|
+
super('android_xml')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'htmlentities'
|
2
|
+
|
3
|
+
class HTMLEntities
|
4
|
+
MAPPINGS['android_xml'] = MAPPINGS['xhtml1'].dup.tap do |mappings|
|
5
|
+
mappings.delete('apos')
|
6
|
+
end
|
7
|
+
|
8
|
+
FLAVORS << 'android_xml'
|
9
|
+
|
10
|
+
class AndroidXmlEncoder < Encoder
|
11
|
+
def initialize(instructions = [])
|
12
|
+
super('android_xml', instructions)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
# had to adjust this so that single and double quotes don't get turned into
|
18
|
+
# entities (they're escaped by hand with backslashes)
|
19
|
+
def basic_entity_regexp
|
20
|
+
@basic_entity_regexp ||= /[&]/
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|