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,162 @@
1
+ #!/usr/bin/env ruby
2
+ require 'commander'
3
+ require 'mixml'
4
+
5
+ module Mixml
6
+ # Mixml application class
7
+ class Application
8
+ include Commander::Methods
9
+
10
+ # Command that selects nodes
11
+ class SelectCommand < Commander::Command
12
+ # Initialize a new command
13
+ #
14
+ # @param method [Symbol] Command method
15
+ # @param args [Array<String>] Command arguments
16
+ def initialize(method, args = ARGV)
17
+ super(method)
18
+
19
+ @method = method
20
+ @selectors = []
21
+
22
+ option '-x', '--xpath STRING', String, 'XPath expression to select nodes' do |value|
23
+ @selectors << ->(tool) {
24
+ xpath value
25
+ }
26
+ end
27
+
28
+ option '-c', '--css STRING', String, 'CSS rule to select nodes' do |value|
29
+ @selectors << ->(tool) {
30
+ css value
31
+ }
32
+ end
33
+
34
+ when_called self, :perform
35
+ end
36
+
37
+ # Run the command
38
+ #
39
+ # @param args [Array<String>] Arguments from the command line
40
+ # @param options [Commander::Command::Options] Options from the command line
41
+ def perform(args, options)
42
+ $tool.work(args) do
43
+ @selectors.each do |selector|
44
+ selection = instance_eval(&selector)
45
+ selection.send name
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ # Command that selects and modifies nodes
52
+ class ModifyCommand < SelectCommand
53
+ # Initialize a new command
54
+ #
55
+ # @param method [Symbol] Command method
56
+ # @param args [Array<String>] Command arguments
57
+ def initialize(method, args = ARGV)
58
+ super(method, args)
59
+
60
+ @template = nil
61
+
62
+ option '-s', '--string STRING', String, 'String value' do |value|
63
+ raise SystemExit, 'Value already specified. Please use --string or --template only once.' unless @template.nil?
64
+ @template = Mixml::Template::Text.new(value)
65
+ end
66
+
67
+ option '-t', '--template STRING', String, 'Template expression value' do |value|
68
+ raise SystemExit, 'Value already specified. Please use --string or --template only once.' unless @template.nil?
69
+ @template = Mixml::Template::Expression.new(value)
70
+ end
71
+ end
72
+
73
+ def perform(args, options)
74
+ raise SystemExit, 'Please specify a value with --string or --template.' if @template.nil?
75
+
76
+ $tool.work(args) do
77
+ @selectors.each do |selector|
78
+ selection = instance_eval(&selector)
79
+ selection.send name, @template
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ # Create a new selection command
86
+ #
87
+ # @param name [Symbol] command name
88
+ def select_command(name)
89
+ c = add_command SelectCommand.new(name)
90
+ yield c if block_given?
91
+ end
92
+
93
+ # Create a new modification command
94
+ #
95
+ # @param name [Symbol] command name
96
+ def modify_command(name)
97
+ c = add_command ModifyCommand.new(name)
98
+ yield c if block_given?
99
+ end
100
+
101
+ # Run the mixml command
102
+ def run
103
+ program :name, 'mixml'
104
+ program :version, Mixml::VERSION
105
+ program :description, 'XML helper tool'
106
+
107
+ $tool = Mixml::Tool.new
108
+
109
+ global_option('-p', '--pretty', 'Pretty print output') do |value|
110
+ $tool.pretty = value
111
+ end
112
+
113
+ global_option('-i', '--inplace', 'Replace the processed files with the new files') do |value|
114
+ $tool.save = value
115
+ $tool.print = !value
116
+ end
117
+
118
+ command :pretty do |c|
119
+ c.description = 'Pretty print XML files'
120
+ c.action do |args, options|
121
+ $tool.pretty = true
122
+ $tool.work(args)
123
+ end
124
+ end
125
+
126
+ select_command :remove do |c|
127
+ c.description = 'Remove nodes from the XML documents'
128
+ end
129
+
130
+ modify_command :replace do |c|
131
+ c.description = 'Replace nodes in the XML documents'
132
+ end
133
+
134
+ modify_command :append do |c|
135
+ c.description = 'Append child nodes in the XML documents'
136
+ end
137
+
138
+ modify_command :rename do |c|
139
+ c.description = 'Rename nodes in the XML documents'
140
+ end
141
+
142
+ modify_command :value do |c|
143
+ c.description = 'Set node values'
144
+ end
145
+
146
+ command :execute do |c|
147
+ c.description = 'Execute script on the XML documents'
148
+ c.option '-s', '--script STRING', String, 'Script file to execute'
149
+ c.option '-e', '--expression STRING', String, 'Command to execute'
150
+ c.action do |args, options|
151
+ script = options.expression || File.read(options.script)
152
+
153
+ $tool.work(args) do
154
+ execute(script)
155
+ end
156
+ end
157
+ end
158
+
159
+ run!
160
+ end
161
+ end
162
+ end
@@ -9,6 +9,8 @@ module Mixml
9
9
  # @return [String] XML document
10
10
  attr_reader :xml
11
11
 
12
+ # Initialize a new document
13
+ #
12
14
  # @param name [String] Document name
13
15
  # @param xml [Nokigiri::XML::Document] XML document
14
16
  def initialize(name, xml)
@@ -7,6 +7,8 @@ module Mixml
7
7
  # @return [Nokogiri::XML::NodeSet] Selected nodes
8
8
  attr_reader :nodesets
9
9
 
10
+ # Initialize a new selection
11
+ #
10
12
  # @param nodesets [Array<Nokogiri::XML::NodeSet>] Selected nodes
11
13
  def initialize(nodesets)
12
14
  @nodesets = nodesets
@@ -11,6 +11,8 @@ module Mixml
11
11
  #
12
12
  # @param text [String] Template text
13
13
  def initialize(text)
14
+ raise(ArgumentError, 'Text must not be nil.') if text.nil?
15
+
14
16
  @text = '"' << text.gsub('"', '\"') << '"'
15
17
  end
16
18
 
@@ -2,6 +2,7 @@ require 'mixml/selection'
2
2
  require 'mixml/document'
3
3
  require 'mixml/template/expression'
4
4
  require 'mixml/template/xml'
5
+ require 'mixml/template/text'
5
6
  require 'docile'
6
7
  require 'nokogiri'
7
8
 
@@ -27,16 +28,12 @@ module Mixml
27
28
  attr_accessor :indent
28
29
 
29
30
  # Intialize a new mixml tool
30
- def initialize(&block)
31
+ def initialize
31
32
  @indent = 4
32
33
  @pretty = false
33
34
  @save = false
34
35
  @print = true
35
36
  @documents = []
36
-
37
- if block_given? then
38
- yield self
39
- end
40
37
  end
41
38
 
42
39
  # Load XML files
@@ -56,12 +53,11 @@ module Mixml
56
53
  end
57
54
  end
58
55
 
59
- # Save all loaded XML files
60
- #
61
- # Pretty prints the XML if {#pretty} is enabled.
56
+ # Output all loaded XML files
62
57
  #
63
- # @return [void]
64
- def save_all
58
+ # @yield Block to write each document
59
+ # @yieldparam document [Document] Document to write
60
+ def output_all
65
61
  options = {}
66
62
 
67
63
  if @pretty then
@@ -69,6 +65,17 @@ module Mixml
69
65
  end
70
66
 
71
67
  @documents.each do |document|
68
+ yield document, options
69
+ end
70
+ end
71
+
72
+ # Save all loaded XML files
73
+ #
74
+ # Pretty prints the XML if {#pretty} is enabled.
75
+ #
76
+ # @return [void]
77
+ def save_all
78
+ output_all do |document, options|
72
79
  File.open(document.name, 'w') do |file|
73
80
  document.xml.write_xml_to(file, options)
74
81
  end
@@ -82,13 +89,7 @@ module Mixml
82
89
  #
83
90
  # @return [void]
84
91
  def print_all
85
- options = {}
86
-
87
- if @pretty then
88
- options[:indent] = @indent
89
- end
90
-
91
- @documents.each do |document|
92
+ output_all do |document, options|
92
93
  if @documents.size > 1 then
93
94
  puts '-' * document.name.length
94
95
  puts document.name
@@ -235,7 +236,7 @@ module Mixml
235
236
  # @param selection [Selection] Selected nodes
236
237
  # @yield Block to execute for each node
237
238
  # @yieldparam node [Nokogiri::XML::Node] Current node
238
- def node(selection)
239
+ def with_nodes(selection)
239
240
  selection.nodesets.each do |nodeset|
240
241
  nodeset.each do |node|
241
242
  yield node
@@ -248,7 +249,7 @@ module Mixml
248
249
  # @param selection [Selection] Selected nodes
249
250
  # @yield Block to execute for each node set
250
251
  # @yieldparam node [Nokogiri::XML::NodeSet] Current node set
251
- def nodes(selection, &block)
252
+ def with_nodesets(selection, &block)
252
253
  selection.nodesets.each do |nodeset|
253
254
  yield nodeset
254
255
  end
@@ -1,4 +1,4 @@
1
1
  module Mixml
2
2
  # Current version
3
- VERSION = '0.9.1'
3
+ VERSION = '0.9.3'
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mixml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.9.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jochen Seeber
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-20 00:00:00.000000000 Z
11
+ date: 2014-08-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: commander
@@ -109,33 +109,19 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: '2.9'
111
111
  - !ruby/object:Gem::Dependency
112
- name: rspec-expectations
112
+ name: ae
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - ~>
116
116
  - !ruby/object:Gem::Version
117
- version: '3.0'
117
+ version: '1.8'
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - ~>
123
123
  - !ruby/object:Gem::Version
124
- version: '3.0'
125
- - !ruby/object:Gem::Dependency
126
- name: rspec-collection_matchers
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - ~>
130
- - !ruby/object:Gem::Version
131
- version: '1.0'
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - ~>
137
- - !ruby/object:Gem::Version
138
- version: '1.0'
124
+ version: '1.8'
139
125
  - !ruby/object:Gem::Dependency
140
126
  name: equivalent-xml
141
127
  requirement: !ruby/object:Gem::Requirement
@@ -190,9 +176,15 @@ files:
190
176
  - LICENSE.txt
191
177
  - README.md
192
178
  - bin/mixml
179
+ - demo/application_checks.md
180
+ - demo/application_commands.md
181
+ - demo/application_invocation.md
182
+ - demo/application_output.md
183
+ - demo/application_selection.md
184
+ - demo/application_values.md
193
185
  - demo/applique/test.rb
194
- - demo/tool.md
195
186
  - lib/mixml.rb
187
+ - lib/mixml/application.rb
196
188
  - lib/mixml/document.rb
197
189
  - lib/mixml/selection.rb
198
190
  - lib/mixml/template.rb
@@ -1,334 +0,0 @@
1
- # Usage examples
2
-
3
- ## Setup
4
-
5
- First we need to create a new Tool object and load an XML file. We create a Mixml tool object and use a
6
- [helper](applique/test.rb) to load the following XML for each example.
7
-
8
- require 'mixml'
9
-
10
- Before do
11
- @tool = Mixml::Tool.new do |t|
12
- # Pretty print output
13
- t.pretty = true
14
-
15
- # Save output after processing
16
- t.save = true
17
-
18
- # Don't print documents after processing
19
- t.print = false
20
- end
21
-
22
- # Save test.xml
23
- file('test.xml') << %{
24
- <list>
25
- <philosopher name="Hobbes"/>
26
- <philosopher name="Rawls"/>
27
- </list>
28
- }
29
-
30
- @tool.load('test.xml')
31
- end
32
-
33
- ## Remove nodes
34
-
35
- Select some nodes with an XPath expression and then remove them
36
-
37
- @tool.execute do
38
- xpath '//philosopher' do
39
- remove
40
- end
41
- end
42
- @tool.flush
43
-
44
- file('test.xml').matches xml %{
45
- <list/>
46
- }
47
-
48
- ## Replace nodes
49
-
50
- Select some elements with an XPath expression and then change the element name.
51
-
52
- @tool.execute do
53
- xpath '//*[@name = "Hobbes"]' do
54
- replace '<tiger name="Hobbes"/>'
55
- end
56
- end
57
- @tool.flush
58
-
59
- file('test.xml').matches xml %{
60
- <list>
61
- <tiger name="Hobbes"/>
62
- <philosopher name="Rawls"/>
63
- </list>
64
- }
65
-
66
- ## Replace nodes with string interpolation
67
-
68
- Ruby [string interpolation](http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Literals#Interpolation) is performed on
69
- string parameters, so you can also select some elements with an XPath expression and then change each element using a
70
- Ruby expression.
71
-
72
- @tool.execute do
73
- xpath '//*[@name = "Hobbes"]' do
74
- replace '<tiger-and-#{node.name} name="Hobbes"/>'
75
- end
76
- end
77
- @tool.flush
78
-
79
- file('test.xml').matches xml %{
80
- <list>
81
- <tiger-and-philosopher name="Hobbes"/>
82
- <philosopher name="Rawls"/>
83
- </list>
84
- }
85
-
86
- This works for all commands that take a string parameter.
87
-
88
- ## Replace nodes with a template
89
-
90
- If you prefer, you can also use template expressions instead of string parameters. Mixml uses
91
- [Erubis](http://www.kuwata-lab.com/erubis) as templating engine, and `{` and `}` as delimiters. With this, you can
92
- select some elements with an XPath expression and then replace each element.
93
-
94
- @tool.execute do
95
- xpath '//*[@name = "Hobbes"]' do
96
- replace template '<tiger-and-{=node.name} name="{=node["name"]}"/>'
97
- end
98
- end
99
- @tool.flush
100
-
101
- file('test.xml').matches xml %{
102
- <list>
103
- <tiger-and-philosopher name="Hobbes"/>
104
- <philosopher name="Rawls"/>
105
- </list>
106
- }
107
-
108
- This works for all commands that take a string parameter.
109
-
110
- ## Replace nodes with XML
111
-
112
- If you prefer, you can also use an XML builder to create values using the simple DSL provided by
113
- [Nokogiri](http://nokogiri.org/Nokogiri/XML/Builder.html). Using this, you can select some elements with an XPath
114
- expression and then replace each element.
115
-
116
- @tool.execute do
117
- xpath '//*[@name = "Hobbes"]' do
118
- replace xml ->(node, xml) {
119
- xml.send(:"tiger-and-philosopher", :name => node['name'])
120
- }
121
- end
122
- end
123
- @tool.flush
124
-
125
- file('test.xml').matches xml %{
126
- <list>
127
- <tiger-and-philosopher name="Hobbes"/>
128
- <philosopher name="Rawls"/>
129
- </list>
130
- }
131
-
132
- This works for all commands that take XML text as parameter (e.g. replace and append).
133
-
134
- ## Replace nodes with Ruby
135
-
136
- If you prefer, you can also use plain Ruby code to create values. with this, you can select some elements with an XPath
137
- expression and then replace each element.
138
-
139
- @tool.execute do
140
- node xpath '//*[@name = "Hobbes"]' do |node|
141
- node.name = "tiger-and-#{node.name}"
142
- end
143
- end
144
- @tool.flush
145
-
146
- file('test.xml').matches xml %{
147
- <list>
148
- <tiger-and-philosopher name="Hobbes"/>
149
- <philosopher name="Rawls"/>
150
- </list>
151
- }
152
-
153
- Instead of processing each node individually, you can also process the selected node sets.
154
-
155
- @tool.execute do
156
- nodes xpath('//*[@name = "Hobbes"]') do |nodeset|
157
- nodeset.each do |node|
158
- node.name = "tiger-and-#{node.name}"
159
- end
160
- end
161
- end
162
- @tool.flush
163
-
164
- file('test.xml').matches xml %{
165
- <list>
166
- <tiger-and-philosopher name="Hobbes"/>
167
- <philosopher name="Rawls"/>
168
- </list>
169
- }
170
-
171
- This works for all commands that take XML text as parameter (e.g. replace and append).
172
-
173
- ## Append nodes
174
-
175
- Select some elements with an XPath expression and then append children to them.
176
-
177
- @tool.execute do
178
- xpath '/list' do
179
- append '<tiger name="Hobbes"/>'
180
- end
181
- end
182
- @tool.flush
183
-
184
- file('test.xml').matches xml %{
185
- <list>
186
- <philosopher name="Hobbes"/>
187
- <philosopher name="Rawls"/>
188
- <tiger name="Hobbes"/>
189
- </list>
190
- }
191
-
192
- ## Replace attribute values
193
-
194
- Select some attributes with an XPath expression and change their value.
195
-
196
- @tool.execute do
197
- xpath '//philosopher[1]/@name' do
198
- value 'Thomas Hobbes'
199
- end
200
- end
201
- @tool.flush
202
-
203
- file('test.xml').matches xml %{
204
- <list>
205
- <philosopher name="Thomas Hobbes"/>
206
- <philosopher name="Rawls"/>
207
- </list>
208
- }
209
-
210
- ## Rename nodes
211
-
212
- Select some nodes with an XPath expression and change their name.
213
-
214
- @tool.execute do
215
- xpath '//philosopher[@name = "Hobbes"]' do
216
- rename 'tiger-and-#{node.name}'
217
- end
218
- end
219
- @tool.flush
220
-
221
- file('test.xml').matches xml %{
222
- <list>
223
- <tiger-and-philosopher name="Hobbes"/>
224
- <philosopher name="Rawls"/>
225
- </list>
226
- }
227
-
228
- ## Evaluate a command string
229
-
230
- Evaluate a command string with mixml commands
231
-
232
- @tool.execute("xpath('//philosopher') { remove }")
233
- @tool.flush
234
-
235
- file('test.xml').matches xml %{
236
- <list/>
237
- }
238
-
239
- ## Select nodes using CSS rules
240
-
241
- You can also use CSS rules to select the nodes to process
242
-
243
- @tool.execute("css('philosopher:first-child') { remove }")
244
- @tool.flush
245
-
246
- file('test.xml').matches xml %{
247
- <list>
248
- <philosopher name="Rawls"/>
249
- </list>
250
- }
251
-
252
- ## Do everything in one step
253
-
254
- Load files, modify them and optionally save them again in one step using the `work` method.
255
-
256
- tool = Mixml::Tool.new do |t|
257
- t.pretty = true
258
- end
259
-
260
- @tool.work('test.xml') do
261
- xpath '//philosopher' do
262
- remove
263
- end
264
- end
265
-
266
- expect(@tool.documents).to have(0).items
267
-
268
- file('test.xml').matches xml %{
269
- <list/>
270
- }
271
-
272
- ## Print modified documents without saving
273
-
274
- Print files whithout saving them.
275
-
276
- @tool.save = false
277
- @tool.print = true
278
-
279
- text = redirect do
280
- @tool.execute do
281
- xpath '//philosopher' do
282
- remove
283
- end
284
- end
285
-
286
- @tool.flush
287
- end
288
-
289
- # Check output
290
- expect(text).to match_text(%{
291
- <?xml version="1.0"?>
292
- <list/>
293
- })
294
-
295
- # Check if file still is unmodified
296
- file('test.xml').matches xml %{
297
- <list>
298
- <philosopher name="Hobbes"/>
299
- <philosopher name="Rawls"/>
300
- </list>
301
- }
302
-
303
- ## Print headers when processing multiple documents
304
-
305
- text = redirect do
306
- file('more.xml') << %{
307
- <list>
308
- <philosopher name="Kant"/>
309
- <philosopher name="Platon"/>
310
- </list>
311
- }
312
-
313
- @tool.load('more.xml')
314
- @tool.print_all
315
- end
316
-
317
- expect(text).to match_text(%{
318
- --------
319
- test.xml
320
- --------
321
- <?xml version="1.0"?>
322
- <list>
323
- <philosopher name="Hobbes"/>
324
- <philosopher name="Rawls"/>
325
- </list>
326
- --------
327
- more.xml
328
- --------
329
- <?xml version="1.0"?>
330
- <list>
331
- <philosopher name="Kant"/>
332
- <philosopher name="Platon"/>
333
- </list>
334
- })