xml_convert 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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