mixml 0.9.1 → 0.9.3

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