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,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
- })