mixml 0.9.1 → 0.9.3

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.
@@ -0,0 +1,107 @@
1
+ # mixml Invocation
2
+
3
+ This document demonstrates the different methods to use mixml.
4
+
5
+ ## Command Line
6
+
7
+ For simple tasks, you can specify the mixml command in the command line.
8
+
9
+ Let's use the following XML in file `test.xml`:
10
+
11
+ <list>
12
+ <philosopher name="Hobbes"/>
13
+ <philosopher name="Rawls"/>
14
+ </list>
15
+
16
+ Now execute the following command to select some nodes and replace them:
17
+
18
+ # mixml replace --xpath '//philosopher[@name="Hobbes"]' --string '<tiger name="Hobbes"/>' test.xml
19
+
20
+ This produces the following XML output:
21
+
22
+ <list>
23
+ <tiger name="Hobbes"/>
24
+ <philosopher name="Rawls"/>
25
+ </list>
26
+
27
+ ## Script files
28
+
29
+ In addition to specifying what to do in the command line, you can also run scripts written in a simple DSL.
30
+
31
+ Let's use the following XML in file `test.xml`:
32
+
33
+ <list>
34
+ <philosopher name="Hobbes"/>
35
+ <philosopher name="Rawls"/>
36
+ </list>
37
+
38
+ You can then use the following mixml script in `test.mixml` to select all philosopher nodes with name 'Hobbes' and replace
39
+ them, and to delete all remaining philosopher nodes:
40
+
41
+ xpath '//philosopher[@name="Hobbes"]' do
42
+ replace template '<tiger-and-{=node.name} name="Hobbes"/>'
43
+ end
44
+ css 'philosopher' do
45
+ remove
46
+ end
47
+
48
+ Now execute the following command to execute the script:
49
+
50
+ # mixml execute --script test.mixml test.xml
51
+
52
+ This produces the following XML output:
53
+
54
+ <list>
55
+ <tiger-and-philosopher name="Hobbes"/>
56
+ </list>
57
+
58
+ ## Script commands
59
+
60
+ You can also specify mixml script expressions directly on the command line without using a script file.
61
+
62
+ Let's use the following XML in file `test.xml`:
63
+
64
+ <list>
65
+ <philosopher name="Hobbes"/>
66
+ <philosopher name="Rawls"/>
67
+ </list>
68
+
69
+ Now execute the following command to select some nodes and replace them:
70
+
71
+ # mixml execute --expression "xpath('//philosopher[@name=\"Hobbes\"]') { replace '<tiger name=\"Hobbes\"/>' }" test.xml
72
+
73
+ This produces the following XML output:
74
+
75
+ <list>
76
+ <tiger name="Hobbes"/>
77
+ <philosopher name="Rawls"/>
78
+ </list>
79
+
80
+ ## Ruby Code
81
+
82
+ You can also use mixml directly from your Ruby code.
83
+
84
+ Let's use the following XML in file `test.xml`:
85
+
86
+ <list>
87
+ <philosopher name="Hobbes"/>
88
+ <philosopher name="Rawls"/>
89
+ </list>
90
+
91
+ Just create a tool object, and let it execute a mixml script.
92
+
93
+ tool = Mixml::Tool.new
94
+ tool.pretty = true
95
+
96
+ tool.work('test.xml') do
97
+ xpath '//philosopher[@name="Hobbes"]' do
98
+ rename 'tiger'
99
+ end
100
+ end
101
+
102
+ This produces the following XML output:
103
+
104
+ <list>
105
+ <tiger name="Hobbes"/>
106
+ <philosopher name="Rawls"/>
107
+ </list>
@@ -0,0 +1,107 @@
1
+ # mixml Output
2
+
3
+ This document demonstrates the different output options when using mixml.
4
+
5
+ ## Print Modified Documents
6
+
7
+ Mixml prints the modified documents to the console per default.
8
+
9
+ Let's use the following XML in file `test.xml`:
10
+
11
+ <list>
12
+ <philosopher name="Hobbes"/>
13
+ <philosopher name="Rawls"/>
14
+ </list>
15
+
16
+ Now execute the following command to replace some nodes:
17
+
18
+ # mixml rename --xpath '//philosopher[@name="Hobbes"]' --string 'tiger' test.xml
19
+
20
+ This produces the following XML output:
21
+
22
+ <list>
23
+ <tiger name="Hobbes"/>
24
+ <philosopher name="Rawls"/>
25
+ </list>
26
+
27
+ ## Print Headers When Processing Multiple Documents
28
+
29
+ Mixml also prints the file names of the documents if multiple documents are processed.
30
+
31
+ Let's use the following XML in file `test.xml`:
32
+
33
+ <list>
34
+ <philosopher name="Hobbes"/>
35
+ <philosopher name="Rawls"/>
36
+ </list>
37
+
38
+ Let's use the following XML in file `more.xml`:
39
+
40
+ <list>
41
+ <philosopher name="Kant"/>
42
+ <philosopher name="Platon"/>
43
+ </list>
44
+
45
+ Now execute the following command to remove some nodes:
46
+
47
+ # mixml remove --xpath '//philosopher[@name="Kant"]' test.xml more.xml
48
+
49
+ This produces the following text output:
50
+
51
+ --------
52
+ test.xml
53
+ --------
54
+ <?xml version="1.0"?>
55
+ <list>
56
+ <philosopher name="Hobbes"/>
57
+ <philosopher name="Rawls"/>
58
+ </list>
59
+ --------
60
+ more.xml
61
+ --------
62
+ <?xml version="1.0"?>
63
+ <list>
64
+ <philosopher name="Platon"/>
65
+ </list>
66
+
67
+ ## Save Modified Documents
68
+
69
+ We can also save the modified files after changing them.
70
+
71
+ Let's use the following XML in file `test.xml`:
72
+
73
+ <list>
74
+ <philosopher name="Hobbes"/>
75
+ <philosopher name="Rawls"/>
76
+ </list>
77
+
78
+ Now execute the following command to rename some nodes:
79
+
80
+ # mixml rename --inplace --xpath '//philosopher[@name="Hobbes"]' --string 'tiger' test.xml
81
+
82
+ This produces the following XML in file `test.xml`:
83
+
84
+ <list>
85
+ <tiger name="Hobbes"/>
86
+ <philosopher name="Rawls"/>
87
+ </list>
88
+
89
+ ## Pretty Print Output
90
+
91
+ You can also pretty print the output.
92
+
93
+ Let's use the following XML in file `test.xml`:
94
+
95
+ <list><philosopher name="Hobbes"/><philosopher name="Rawls"/></list>
96
+
97
+ Now execute the following command to rename some nodes:
98
+
99
+ # mixml rename --pretty --xpath '//philosopher[@name="Hobbes"]' --string 'tiger' test.xml
100
+
101
+ This produces the following text output:
102
+
103
+ <?xml version="1.0"?>
104
+ <list>
105
+ <tiger name="Hobbes"/>
106
+ <philosopher name="Rawls"/>
107
+ </list>
@@ -0,0 +1,47 @@
1
+ # mixml Selection
2
+
3
+ This document demonstrates the different methods to select XML nodes when using mixml.
4
+
5
+ ## Select nodes using XPath expressions
6
+
7
+ You can use standard XPath expressions to select the nodes to process.
8
+
9
+ Let's use the following XML in file `test.xml`:
10
+
11
+ <list>
12
+ <philosopher name="Hobbes"/>
13
+ <philosopher name="Rawls"/>
14
+ </list>
15
+
16
+ Now execute the following command to rename some nodes:
17
+
18
+ # mixml rename --xpath '//philosopher[@name="Hobbes"]' --string 'tiger' test.xml
19
+
20
+ This produces the following XML output:
21
+
22
+ <list>
23
+ <tiger name="Hobbes"/>
24
+ <philosopher name="Rawls"/>
25
+ </list>
26
+
27
+ ### Select nodes using CSS rules
28
+
29
+ You can also use CSS rules instead of XPath expressions to select the nodes to process.
30
+
31
+ Let's use the following XML in file `test.xml`:
32
+
33
+ <list>
34
+ <philosopher name="Hobbes"/>
35
+ <philosopher name="Rawls"/>
36
+ </list>
37
+
38
+ Now execute the following command to rename some nodes:
39
+
40
+ # mixml rename --css 'philosopher:first-child' --string 'tiger' test.xml
41
+
42
+ This produces the following XML output:
43
+
44
+ <list>
45
+ <tiger name="Hobbes"/>
46
+ <philosopher name="Rawls"/>
47
+ </list>
@@ -0,0 +1,167 @@
1
+ # mixml Values
2
+
3
+ This document demonstrates the different methods to specify values when using mixml.
4
+
5
+ ## Strings
6
+
7
+ You can specify values with a string, e.g. the new node when replacing content. Ruby
8
+ [string interpolation][string interpolation] is performed on string values, so you can also use Ruby expressions
9
+ in string values.
10
+
11
+ [string interpolation]: http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Literals#Interpolation
12
+
13
+ Let's use the following XML in file `test.xml`:
14
+
15
+ <list>
16
+ <philosopher name="Hobbes"/>
17
+ <philosopher name="Rawls"/>
18
+ </list>
19
+
20
+ You can then use the following mixml script in `test.mixml` to replace some nodes:
21
+
22
+ xpath '//philosopher[@name="Hobbes"]' do
23
+ replace '<tiger-and-#{node.name} name="Hobbes"/>'
24
+ end
25
+
26
+ Now execute the following command to run the script:
27
+
28
+ # mixml execute --script test.mixml test.xml
29
+
30
+ This produces the following XML output:
31
+
32
+ <list>
33
+ <tiger-and-philosopher name="Hobbes"/>
34
+ <philosopher name="Rawls"/>
35
+ </list>
36
+
37
+ ## Templates
38
+
39
+ You can also use a template to specify complex replacement. Mixml uses [Erubis][Erubis] as templating engine, and `{`
40
+ and `}` as delimiters.
41
+
42
+ [Erubis]: http://www.kuwata-lab.com/erubis
43
+
44
+ Let's use the following XML in file `test.xml`:
45
+
46
+ <list>
47
+ <philosopher name="Hobbes"/>
48
+ <philosopher name="Rawls"/>
49
+ </list>
50
+
51
+ You can then use the following mixml script in `test.mixml` to replace some nodes:
52
+
53
+ xpath '//philosopher' do
54
+ replace template %{
55
+ <{=node.name} name="{=node['name']}">
56
+ {if node['name'] == "Hobbes"}
57
+ <hobby name="Space Travel"/>
58
+ {else}
59
+ <hobby name="Philosophy"/>
60
+ {end}
61
+ </{=node.name}>
62
+ }
63
+ end
64
+
65
+ Now execute the following command to run the script:
66
+
67
+ # mixml execute --script test.mixml test.xml
68
+
69
+ This produces the following XML output:
70
+
71
+ <list>
72
+ <philosopher name="Hobbes">
73
+ <hobby name="Space Travel"/>
74
+ </philosopher>
75
+ <philosopher name="Rawls">
76
+ <hobby name="Philosophy"/>
77
+ </philosopher>
78
+ </list>
79
+
80
+ ### XML Builder
81
+
82
+ You can also use an XML builder to create values using the simple DSL provided by [Nokogiri][Nokogiri builder].
83
+
84
+ [Nokogiri builder]: http://nokogiri.org/Nokogiri/XML/Builder.html
85
+
86
+ Let's use the following XML in file `test.xml`:
87
+
88
+ <list>
89
+ <philosopher name="Hobbes"/>
90
+ <philosopher name="Rawls"/>
91
+ </list>
92
+
93
+ You can then use the following mixml script in `test.mixml` to replace some nodes:
94
+
95
+ xpath '//philosopher[@name="Hobbes"]' do
96
+ replace xml ->(node, xml) {
97
+ xml.tiger(:name => node['name']) {
98
+ xml.hobby(:name => 'Space Travel')
99
+ }
100
+ }
101
+ end
102
+
103
+ Now execute the following command to run the script:
104
+
105
+ # mixml execute --script test.mixml test.xml
106
+
107
+ This produces the following XML output:
108
+
109
+ <list>
110
+ <tiger name="Hobbes">
111
+ <hobby name="Space Travel"/>
112
+ </tiger>
113
+ <philosopher name="Rawls"/>
114
+ </list>
115
+
116
+ ### Plain Ruby
117
+
118
+ If you prefer, you can also use plain Ruby code to modify nodes.
119
+
120
+ Let's use the following XML in file `test.xml`:
121
+
122
+ <list>
123
+ <philosopher name="Hobbes"/>
124
+ <philosopher name="Rawls"/>
125
+ </list>
126
+
127
+ You can then use the following mixml script in `test.mixml` to rename some nodes:
128
+
129
+ with_nodes xpath '//philosopher[@name="Hobbes"]' do |node|
130
+ node.name = 'tiger'
131
+ end
132
+
133
+ Now execute the following command to run the script:
134
+
135
+ # mixml execute --script test.mixml test.xml
136
+
137
+ This produces the following XML output:
138
+
139
+ <list>
140
+ <tiger name="Hobbes"/>
141
+ <philosopher name="Rawls"/>
142
+ </list>
143
+
144
+ Instead of processing each node individually, you can also process the selected node sets.
145
+
146
+ Let's use the following XML in file `test.xml`:
147
+
148
+ <list>
149
+ <philosopher name="Hobbes"/>
150
+ <philosopher name="Rawls"/>
151
+ </list>
152
+
153
+ You can then use the following mixml script in `test.mixml` to replace some nodes:
154
+
155
+ with_nodesets xpath '//philosopher[@name="Hobbes"]' do |nodeset|
156
+ nodeset.remove
157
+ end
158
+
159
+ Now execute the following command to run the script:
160
+
161
+ # mixml execute --script test.mixml test.xml
162
+
163
+ This produces the following XML output:
164
+
165
+ <list>
166
+ <philosopher name="Rawls"/>
167
+ </list>
@@ -1,57 +1,12 @@
1
1
  require 'fileutils'
2
- require 'rspec/expectations'
3
- require 'rspec/collection_matchers'
4
2
  require 'equivalent-xml'
5
3
  require 'nokogiri'
4
+ require 'ae'
5
+ require 'mixml'
6
+ require 'mixml/application'
6
7
 
7
- include RSpec::Matchers
8
-
9
- # File with test content
10
- class TestFile
11
- include RSpec::Matchers
12
-
13
- # @return [String] File name
14
- attr_reader :file_name
15
-
16
- # @param file_name [String] File name
17
- def initialize(file_name)
18
- @file_name = file_name
19
- end
20
-
21
- # Write text into file
22
- #
23
- # @param text [String] Text to write
24
- # @return [void]
25
- def <<(text)
26
- File.open(@file_name, 'w') do |f|
27
- f.write text
28
- end
29
- end
30
-
31
- # Expect file to match content
32
- #
33
- # @param object [String, Nokogiri::XML::Document] Text or XML to match
34
- # @return [void]
35
- def matches(object)
36
- content = File.read(@file_name)
37
- if object.is_a?(Nokogiri::XML::Document) then
38
- expect(content).to be_equivalent_to(object).respecting_element_order
39
- else
40
- expect(content).to be(object)
41
- end
42
- end
43
- end
44
-
45
- # Create a new test file
46
- def file(name)
47
- FileUtils.mkpath(File.dirname(name))
48
- TestFile.new(name)
49
- end
50
-
51
- # Create a new XML document
52
- def xml(text)
53
- Nokogiri::XML(text)
54
- end
8
+ # Add bin directory to path
9
+ ENV['PATH'] = File.join(QED::Utils.root, 'bin') + ":" + ENV['PATH']
55
10
 
56
11
  # Redirect stdout and return output
57
12
  #
@@ -60,25 +15,104 @@ end
60
15
  def redirect
61
16
  begin
62
17
  old_stdout = $stdout
18
+ old_stderr = $stderr
63
19
  $stdout = StringIO.new('', 'w')
20
+ $stderr = $stdout
64
21
  yield
65
22
  $stdout.string
66
23
  ensure
67
24
  $stdout = old_stdout
25
+ $stderr = old_stderr
68
26
  end
69
27
  end
70
28
 
71
- # Define a new matcher to compare text files ignoring empty lines as well as leading and trailing spaces.
72
- RSpec::Matchers.define :match_text do |expected|
73
- # Remove empty lines and leading and trailing spaces from text
74
- #
75
- # @param text [String] Text to clean
76
- # @return [String] cleaned text
77
- def clean(text)
78
- text.gsub(/\n+/, "\n").gsub(/^\s+|\s+$/, '')
29
+ # Remove empty lines and leading and trailing spaces from text
30
+ #
31
+ # @param text [String] Text to clean
32
+ # @return [String] cleaned text
33
+ def clean(text)
34
+ raise(ArgumentError, 'Text must not be nil.') if text.nil?
35
+
36
+ text.gsub(/\n+/, "\n").gsub(/^\s+|\s+$/, '')
37
+ end
38
+
39
+ When /\buse\b[^.]+\bXML in (?:file )?`([a-z]+\.xml)`/i do |file_name, xml|
40
+ raise(ArgumentError, 'Please supply a block with the XML document') if xml.nil?
41
+
42
+ doc = Nokogiri::XML(xml) do |config|
43
+ config.default_xml.noblanks
44
+ end
45
+
46
+ File.open(file_name, 'w') do |f|
47
+ doc.write_xml_to(f, :indent => 4)
79
48
  end
49
+ end
50
+
51
+ When /\buse\b[^.]+\bscript in (?:file )?`([a-z]+\.mixml)`/i do |file_name, text|
52
+ raise(ArgumentError, 'Please supply a block with the mixml script') if text.nil?
80
53
 
81
- match do |actual|
82
- clean(actual) == clean(expected)
54
+ File.open(file_name, 'w') do |f|
55
+ f.write(text)
56
+ end
57
+ end
58
+
59
+ When /\bproduces\b[^.]+\bXML in (?:file )?`([a-z]+\.xml)`/i do |file_name, xml|
60
+ raise(ArgumentError, 'Please supply a block with the XML document') if xml.nil?
61
+
62
+ actual = Nokogiri::XML(File.read('test.xml')) do |config|
63
+ config.default_xml.noblanks
64
+ end
65
+
66
+ expected = Nokogiri::XML(xml) do |config|
67
+ config.default_xml.noblanks
68
+ end
69
+
70
+ EquivalentXml.assert.equivalent?(actual, expected, :element_order => true)
71
+ end
72
+
73
+ When /\bproduces\b[^.]+\bXML output\b/i do |xml|
74
+ raise(ArgumentError, 'Please supply a block with the XML document') if xml.nil?
75
+
76
+ actual = Nokogiri::XML(@output) do |config|
77
+ config.default_xml.noblanks
78
+ end
79
+
80
+ expected = Nokogiri::XML(xml) do |config|
81
+ config.default_xml.noblanks
82
+ end
83
+
84
+ options = {:element_order => true}
85
+ EquivalentXml.assert.equivalent?(actual, expected, options)
86
+ end
87
+
88
+ When /\bproduces\b[^.]+\btext output\b/i do |text|
89
+ raise(ArgumentError, 'Please supply a block with the text') if text.nil?
90
+ clean(@output).assert == clean(text)
91
+ end
92
+
93
+ When /\bexecute\b[^.]+\bcommand\b/i do |command|
94
+ raise(ArgumentError, 'Please supply a block with the command to run') if command.nil?
95
+ command.gsub!(/^#\s*/, '')
96
+ arguments = Shellwords.shellsplit(command)
97
+ Commander::Runner.instance_variable_set :"@singleton", Commander::Runner.new(arguments[1..-1])
98
+ app = Mixml::Application.new
99
+
100
+ @output = redirect do
101
+ app.run
102
+ end
103
+ end
104
+
105
+ When /\bexecuting\b[^.]+\bfails\b/i do |command|
106
+ raise(ArgumentError, 'Please supply a block with the command to run') if command.nil?
107
+ command.gsub!(/^#\s*/, '')
108
+ arguments = Shellwords.shellsplit(command)
109
+ Commander::Runner.instance_variable_set :"@singleton", Commander::Runner.new(arguments[1..-1])
110
+ app = Mixml::Application.new
111
+ app.run
112
+
113
+ SystemExit.expect do
114
+ @output = redirect do
115
+ app.run
116
+ end
83
117
  end
84
118
  end