xml_convert 0.0.1 → 0.1.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.
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 Jeremy Redburn
1
+ Copyright (c) 2012 Social Ceramics Inc.
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # XmlConvert
2
2
 
3
- TODO: Write a gem description
3
+ XmlConvert is a Ruby implementation of the [.NET XmlConvert class](http://msdn.microsoft.com/en-us/library/e2104c2x.aspx). It encodes and decodes XML names, giving you an easy way to create NCName-compliant names from arbitrary strings.
4
4
 
5
5
  ## Installation
6
6
 
@@ -18,7 +18,23 @@ Or install it yourself as:
18
18
 
19
19
  ## Usage
20
20
 
21
- TODO: Write usage instructions here
21
+ ### Encoding
22
+
23
+ Encode a name:
24
+
25
+ ```ruby
26
+ encoded_name = XmlConvert.encode_name("My Name") # "My_x0020_Name"
27
+ ```
28
+
29
+ Encode a local name, escaping colons as well:
30
+
31
+ ```ruby
32
+ encoded_name = XmlConvert.encode_local_name("Foo: Bar") # "Foo_x003a__x0020_Bar"
33
+ ```
34
+
35
+ ### Decoding
36
+
37
+ Any of the above [Encoding examples](#encoding) can be reversed with `XmlConvert.decode_name(name)`.
22
38
 
23
39
  ## Contributing
24
40
 
@@ -1,3 +1,3 @@
1
1
  module XmlConvert
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/xml_convert.rb CHANGED
@@ -1,28 +1,42 @@
1
1
  require "xml_convert/version"
2
2
 
3
3
  module XmlConvert
4
-
4
+
5
+ # Converts an encoded XML name to its original form.
6
+ #
7
+ # @example XmlConvert.decode_name('Order_x0020_Details') #=> 'Order Details'
8
+ #
9
+ # @param [String] name the name to be decoded.
10
+ # @return [String] the decoded name.
11
+ def self.decode_name(name)
12
+ return name if name.nil? || name.length == 0
13
+
14
+ pos = name.index('_')
15
+ return name if pos.nil? || pos + 6 >= name.length
16
+
17
+ return name if name[pos+1] != 'x' || name[pos+6] != '_'
18
+
19
+ name.slice(0, pos) << try_decoding(name[pos+1..-1])
20
+ end
21
+
5
22
  # Converts the name to a valid XML name.
6
23
  #
7
24
  # @example XmlConvert.encode_name('Order Details') #=> 'Order_x0020_Details'
8
25
  #
9
26
  # @param [String] name the name to be encoded.
10
- # @return [String, nil] the encoded name or nil if the name cannot be
11
- # converted to a string.
27
+ # @return [String] the encoded name.
12
28
  def self.encode_name(name)
13
29
  return name if name.nil? || name.length == 0
14
30
 
15
- encoded_name = ""
16
- name.chars.each_with_index do |c, i|
17
- if is_invalid?(c, encoded_name == "")
18
- encoded_name << "_x#{'%04x' % c.ord}_"
31
+ name.chars.each_with_index.reduce("") do |memo, (c, i)|
32
+ if is_invalid?(c, memo == "")
33
+ memo << "_x#{'%04x' % c.ord}_"
19
34
  elsif c == '_' && i+6 < name.length && name[i+1] == 'x' && name[i+6] == '_'
20
- encoded_name << '_x005f_'
35
+ memo << '_x005f_'
21
36
  else
22
- encoded_name << c
37
+ memo << c
23
38
  end
24
39
  end
25
- encoded_name
26
40
  end
27
41
 
28
42
  # Converts the name to a valid XML local name.
@@ -38,6 +52,20 @@ module XmlConvert
38
52
 
39
53
  private
40
54
 
55
+ def self.try_decoding(string)
56
+ return string if string.nil? || string.length < 6
57
+
58
+ ord = string[1..4].hex
59
+
60
+ if ord == 0
61
+ string[0] << decode_name(string[1..-1])
62
+ elsif string.length == 6
63
+ ord.chr
64
+ else
65
+ ord.chr << decode_name(string[6..-1])
66
+ end
67
+ end
68
+
41
69
  def self.is_invalid?(char, is_first_letter)
42
70
  !self.is_valid?(char, is_first_letter)
43
71
  end
@@ -52,36 +80,44 @@ module XmlConvert
52
80
  # [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] |
53
81
  # [#x10000-#xEFFFF]
54
82
  def self.is_name_start_char?(char)
55
- ord = char.ord
56
-
57
- (ord == ':'.ord) ||
58
- (ord >= 'A'.ord && ord <= 'Z'.ord) ||
59
- (ord == '_'.ord) ||
60
- (ord >= 'a'.ord && ord <= 'z'.ord) ||
61
- (ord >= 'C0'.hex && ord <= 'D6'.hex) ||
62
- (ord >= 'D8'.hex && ord <= 'F6'.hex) ||
63
- (ord >= 'F8'.hex && ord <= '2FF'.hex) ||
64
- (ord >= '370'.hex && ord <= '37D'.hex) ||
65
- (ord >= '37F'.hex && ord <= '1FFF'.hex) ||
66
- (ord >= '200C'.hex && ord <= '200D'.hex) ||
67
- (ord >= '2070'.hex && ord <= '218F'.hex) ||
68
- (ord >= '2C00'.hex && ord <= '2FEF'.hex) ||
69
- (ord >= '3001'.hex && ord <= 'D7FF'.hex) ||
70
- (ord >= 'F900'.hex && ord <= 'FDCF'.hex) ||
71
- (ord >= 'FDFO'.hex && ord <= 'FFFD'.hex) ||
72
- (ord >= '10000'.hex && ord <= 'EFFFF'.hex)
83
+ case char.ord
84
+ when ':'.ord,
85
+ 'A'.ord .. 'Z'.ord,
86
+ '_'.ord,
87
+ 'a'.ord .. 'z'.ord,
88
+ 'C0'.hex .. 'D6'.hex,
89
+ 'D8'.hex .. 'F6'.hex,
90
+ 'F8'.hex .. '2FF'.hex,
91
+ '370'.hex .. '37D'.hex,
92
+ '37F'.hex .. '1FFF'.hex,
93
+ '200C'.hex .. '200D'.hex,
94
+ '2070'.hex .. '218F'.hex,
95
+ '2C00'.hex .. '2FEF'.hex,
96
+ '3001'.hex .. 'D7FF'.hex,
97
+ 'F900'.hex .. 'FDCF'.hex,
98
+ 'FDFO'.hex .. 'FFFD'.hex,
99
+ '10000'.hex .. 'EFFFF'.hex
100
+ true
101
+ else
102
+ false
103
+ end
73
104
  end
74
105
 
75
106
  # NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] |
76
107
  # [#x203F-#x2040]
77
108
  def self.is_name_char?(char)
78
- ord = char.ord
79
-
80
- is_name_start_char?(char) ||
81
- (ord == '-'.ord) || (ord == '.'.ord) ||
82
- (ord >= '0'.ord && ord <= '9'.ord) ||
83
- (ord == 'B7'.hex) ||
84
- (ord >= '300'.hex && ord <= '36F'.hex) ||
85
- (ord >= '203F'.hex && ord <= '2040'.hex)
109
+ return true if is_name_start_char?(char)
110
+
111
+ case char.ord
112
+ when '-'.ord,
113
+ '.'.ord,
114
+ '0'.ord .. '9'.ord,
115
+ 'B7'.hex,
116
+ '300'.hex .. '36F'.hex,
117
+ '203F'.hex .. '2040'.hex
118
+ true
119
+ else
120
+ false
121
+ end
86
122
  end
87
123
  end
@@ -2,12 +2,34 @@ require "spec_helper"
2
2
 
3
3
  describe "XmlConvert" do
4
4
 
5
- describe ".encode_name" do
5
+ describe ".decode_name" do
6
6
  it "returns nil when argument is nil" do
7
+ XmlConvert.decode_name(nil).should eq nil
8
+ end
9
+
10
+ it "returns the empty string when name is an empty string" do
11
+ XmlConvert.decode_name("").should eq ""
12
+ end
13
+
14
+ it "unescapes a single space" do
15
+ XmlConvert.decode_name("_x0020_").should eq " "
16
+ end
17
+
18
+ it "unescapes spaces" do
19
+ XmlConvert.decode_name("Order_x0020_Details").should eq "Order Details"
20
+ end
21
+
22
+ it "does not unescape leading underscores" do
23
+ XmlConvert.decode_name("_Test").should eq "_Test"
24
+ end
25
+ end
26
+
27
+ describe ".encode_name" do
28
+ it "returns nil when name is nil" do
7
29
  XmlConvert.encode_name(nil).should eq nil
8
30
  end
9
31
 
10
- it "returns the empty string when argument is an empty string" do
32
+ it "returns the empty string when name is an empty string" do
11
33
  XmlConvert.encode_name("").should eq ""
12
34
  end
13
35
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xml_convert
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -108,7 +108,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
108
108
  version: '0'
109
109
  segments:
110
110
  - 0
111
- hash: 957471371544989854
111
+ hash: 4323374162532717687
112
112
  required_rubygems_version: !ruby/object:Gem::Requirement
113
113
  none: false
114
114
  requirements:
@@ -117,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
117
117
  version: '0'
118
118
  segments:
119
119
  - 0
120
- hash: 957471371544989854
120
+ hash: 4323374162532717687
121
121
  requirements: []
122
122
  rubyforge_project:
123
123
  rubygems_version: 1.8.24