xml-focus 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ee4a51865d564fd5835d939f3c10f55a4ca52eb1
4
+ data.tar.gz: 8f5d501b269037edb7504d7067af80e16b8f1cbb
5
+ SHA512:
6
+ metadata.gz: c3cb37e0d9e6c25651c21a1f342071d855ce728b4af17dfef3bf718e5750b423a74b7e1b6a04c337993ef5a3b502c5bdaf02806984b497c8b5f95328df7cc385
7
+ data.tar.gz: 2e30d4e8bad7a2699cc98a7aaf38af9dc91a76e8f5d5a503e9766ab3d3175359e64f194b94f3f0f9bbf41685127c6937d59f21ce54d45d9ad685bf2a0acb882c
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/COPYING ADDED
@@ -0,0 +1,121 @@
1
+ Creative Commons Legal Code
2
+
3
+ CC0 1.0 Universal
4
+
5
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12
+ HEREUNDER.
13
+
14
+ Statement of Purpose
15
+
16
+ The laws of most jurisdictions throughout the world automatically confer
17
+ exclusive Copyright and Related Rights (defined below) upon the creator
18
+ and subsequent owner(s) (each and all, an "owner") of an original work of
19
+ authorship and/or a database (each, a "Work").
20
+
21
+ Certain owners wish to permanently relinquish those rights to a Work for
22
+ the purpose of contributing to a commons of creative, cultural and
23
+ scientific works ("Commons") that the public can reliably and without fear
24
+ of later claims of infringement build upon, modify, incorporate in other
25
+ works, reuse and redistribute as freely as possible in any form whatsoever
26
+ and for any purposes, including without limitation commercial purposes.
27
+ These owners may contribute to the Commons to promote the ideal of a free
28
+ culture and the further production of creative, cultural and scientific
29
+ works, or to gain reputation or greater distribution for their Work in
30
+ part through the use and efforts of others.
31
+
32
+ For these and/or other purposes and motivations, and without any
33
+ expectation of additional consideration or compensation, the person
34
+ associating CC0 with a Work (the "Affirmer"), to the extent that he or she
35
+ is an owner of Copyright and Related Rights in the Work, voluntarily
36
+ elects to apply CC0 to the Work and publicly distribute the Work under its
37
+ terms, with knowledge of his or her Copyright and Related Rights in the
38
+ Work and the meaning and intended legal effect of CC0 on those rights.
39
+
40
+ 1. Copyright and Related Rights. A Work made available under CC0 may be
41
+ protected by copyright and related or neighboring rights ("Copyright and
42
+ Related Rights"). Copyright and Related Rights include, but are not
43
+ limited to, the following:
44
+
45
+ i. the right to reproduce, adapt, distribute, perform, display,
46
+ communicate, and translate a Work;
47
+ ii. moral rights retained by the original author(s) and/or performer(s);
48
+ iii. publicity and privacy rights pertaining to a person's image or
49
+ likeness depicted in a Work;
50
+ iv. rights protecting against unfair competition in regards to a Work,
51
+ subject to the limitations in paragraph 4(a), below;
52
+ v. rights protecting the extraction, dissemination, use and reuse of data
53
+ in a Work;
54
+ vi. database rights (such as those arising under Directive 96/9/EC of the
55
+ European Parliament and of the Council of 11 March 1996 on the legal
56
+ protection of databases, and under any national implementation
57
+ thereof, including any amended or successor version of such
58
+ directive); and
59
+ vii. other similar, equivalent or corresponding rights throughout the
60
+ world based on applicable law or treaty, and any national
61
+ implementations thereof.
62
+
63
+ 2. Waiver. To the greatest extent permitted by, but not in contravention
64
+ of, applicable law, Affirmer hereby overtly, fully, permanently,
65
+ irrevocably and unconditionally waives, abandons, and surrenders all of
66
+ Affirmer's Copyright and Related Rights and associated claims and causes
67
+ of action, whether now known or unknown (including existing as well as
68
+ future claims and causes of action), in the Work (i) in all territories
69
+ worldwide, (ii) for the maximum duration provided by applicable law or
70
+ treaty (including future time extensions), (iii) in any current or future
71
+ medium and for any number of copies, and (iv) for any purpose whatsoever,
72
+ including without limitation commercial, advertising or promotional
73
+ purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
74
+ member of the public at large and to the detriment of Affirmer's heirs and
75
+ successors, fully intending that such Waiver shall not be subject to
76
+ revocation, rescission, cancellation, termination, or any other legal or
77
+ equitable action to disrupt the quiet enjoyment of the Work by the public
78
+ as contemplated by Affirmer's express Statement of Purpose.
79
+
80
+ 3. Public License Fallback. Should any part of the Waiver for any reason
81
+ be judged legally invalid or ineffective under applicable law, then the
82
+ Waiver shall be preserved to the maximum extent permitted taking into
83
+ account Affirmer's express Statement of Purpose. In addition, to the
84
+ extent the Waiver is so judged Affirmer hereby grants to each affected
85
+ person a royalty-free, non transferable, non sublicensable, non exclusive,
86
+ irrevocable and unconditional license to exercise Affirmer's Copyright and
87
+ Related Rights in the Work (i) in all territories worldwide, (ii) for the
88
+ maximum duration provided by applicable law or treaty (including future
89
+ time extensions), (iii) in any current or future medium and for any number
90
+ of copies, and (iv) for any purpose whatsoever, including without
91
+ limitation commercial, advertising or promotional purposes (the
92
+ "License"). The License shall be deemed effective as of the date CC0 was
93
+ applied by Affirmer to the Work. Should any part of the License for any
94
+ reason be judged legally invalid or ineffective under applicable law, such
95
+ partial invalidity or ineffectiveness shall not invalidate the remainder
96
+ of the License, and in such case Affirmer hereby affirms that he or she
97
+ will not (i) exercise any of his or her remaining Copyright and Related
98
+ Rights in the Work or (ii) assert any associated claims and causes of
99
+ action with respect to the Work, in either case contrary to Affirmer's
100
+ express Statement of Purpose.
101
+
102
+ 4. Limitations and Disclaimers.
103
+
104
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
105
+ surrendered, licensed or otherwise affected by this document.
106
+ b. Affirmer offers the Work as-is and makes no representations or
107
+ warranties of any kind concerning the Work, express, implied,
108
+ statutory or otherwise, including without limitation warranties of
109
+ title, merchantability, fitness for a particular purpose, non
110
+ infringement, or the absence of latent or other defects, accuracy, or
111
+ the present or absence of errors, whether or not discoverable, all to
112
+ the greatest extent permissible under applicable law.
113
+ c. Affirmer disclaims responsibility for clearing rights of other persons
114
+ that may apply to the Work or any use thereof, including without
115
+ limitation any person's Copyright and Related Rights in the Work.
116
+ Further, Affirmer disclaims responsibility for obtaining any necessary
117
+ consents, permissions or other rights required for any use of the
118
+ Work.
119
+ d. Affirmer understands and acknowledges that Creative Commons is not a
120
+ party to this document and has no duty or obligation with respect to
121
+ this CC0 or use of the Work.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in xml-focus.gemspec
4
+ gemspec
@@ -0,0 +1,116 @@
1
+ xml-focus: extract XML subtrees and preserve structure
2
+ ======================================================
3
+
4
+ Extract subtrees from XML trees, making sure that the overall structure is
5
+ preserved.
6
+
7
+ Examples
8
+ --------
9
+
10
+ Extract a portion of TEI document, from the element `pb@n='1'` to the
11
+ element `pb@n='2'`:
12
+
13
+ doc = Nokogiri::XML(%q{
14
+ <TEI>
15
+ <text>
16
+ <body>
17
+ <sp>
18
+ <speaker>First</speaker>
19
+ <l n='1'>a</l>
20
+ <l n='2'>a</l>
21
+ <l n='3'>a</l>
22
+
23
+ <pb n='1'/>
24
+
25
+ <l n='4'>a</l>
26
+ </sp>
27
+
28
+ <sp>
29
+ <speaker>Second</speaker>
30
+ <l n='5'>a</l>
31
+ <l n='6'>a</l>
32
+ <l n='7'>a</l>
33
+ <l n='8'>a</l>
34
+
35
+ <pb n='2'/>
36
+ </sp>
37
+
38
+ <sp>
39
+ <speaker>Third</speaker>
40
+ <l n='9'>a</l>
41
+ <l n='10'>a</l>
42
+ <l n='11'>a</l>
43
+
44
+ <pb n='3'>
45
+ </sp>
46
+ </body>
47
+ </text>
48
+ </TEI>
49
+ })
50
+
51
+ # Select all t
52
+ portion = XML::Focus(doc, 'name()="pb" and @n="1"', 'name()="pb" and @n="2"')
53
+
54
+ puts portion.to_xml # =>
55
+
56
+ # <TEI>
57
+ # <text>
58
+ # <body>
59
+ # <sp>
60
+ # <pb n='1'/>
61
+ #
62
+ # <l n='4'>a</l>
63
+ # </sp>
64
+ #
65
+ # <sp>
66
+ # <speaker>Second</speaker>
67
+ # <l n='5'>a</l>
68
+ # <l n='6'>a</l>
69
+ # <l n='7'>a</l>
70
+ # <l n='8'>a</l>
71
+ #
72
+ # <pb n='2'/>
73
+ # </sp>
74
+ # </body>
75
+ # </text>
76
+ # </TEI>
77
+
78
+
79
+ Requirements
80
+ ------------
81
+
82
+ xml-focus is based on Nokogiri.
83
+
84
+
85
+ Install
86
+ -------
87
+
88
+ gem install xml-focus
89
+
90
+
91
+ Author
92
+ ------
93
+
94
+ * Gioele Barabucci <http://svario.it/gioele> (initial author)
95
+
96
+
97
+ Development
98
+ -----------
99
+
100
+ Code
101
+ : <https://github.com/gioele/xml-focus>
102
+
103
+ Report issues
104
+ : <https://github.com/gioele/xml-focus/issues>
105
+
106
+ Documentation
107
+ : <http://rubydoc.info/gems/xml-focus>
108
+
109
+
110
+ License
111
+ -------
112
+
113
+ This is free software released into the public domain (CC0 license).
114
+
115
+ See the `COPYING` file or <http://creativecommons.org/publicdomain/zero/1.0/>
116
+ for more details.
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,20 @@
1
+ require 'xml/focus/version'
2
+
3
+ require 'erb'
4
+
5
+ module XML
6
+ Focus::XSLT_TEMPLATE_PATH = File.join(File.dirname(__FILE__), '/focus/focus.xsl.erb')
7
+ Focus::XSLT_TEMPLATE = ERB.new(File.read(Focus::XSLT_TEMPLATE_PATH)).freeze
8
+
9
+ class << self
10
+ def Focus(xml, first_elem_path, last_elem_path)
11
+ # FIXME: escape xpath
12
+ xslt_code = Focus::XSLT_TEMPLATE.result(binding)
13
+ xslt = Nokogiri::XSLT(xslt_code)
14
+
15
+ focused = xslt.transform(xml)
16
+
17
+ return focused
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
3
+ <xsl:template match="*">
4
+ <xsl:variable name="has-begin" select='boolean(.//*[<%= first_elem_path %>])'/>
5
+
6
+ <xsl:variable name="has-end" select='boolean(.//*[<%= last_elem_path %>])'/>
7
+
8
+ <xsl:variable name="is-begin" select='<%= first_elem_path %>'/>
9
+
10
+ <xsl:variable name="is-end" select='<%= last_elem_path %>'/>
11
+
12
+ <xsl:variable name="between" select='following::*[<%= last_elem_path %>] and preceding::*[<%= first_elem_path %>]'/>
13
+
14
+ <xsl:if test="$has-begin or $has-end or $is-begin or $is-end or $between">
15
+ <xsl:copy>
16
+ <xsl:apply-templates select="*|@*|text()"/>
17
+ </xsl:copy>
18
+ </xsl:if>
19
+ </xsl:template>
20
+
21
+ <xsl:template match="@*|text()">
22
+ <xsl:copy/>
23
+ </xsl:template>
24
+ </xsl:stylesheet>
@@ -0,0 +1,5 @@
1
+ module XML
2
+ module Focus
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,34 @@
1
+ <root xml:id='e-root'>
2
+ <level1 xml:id='e-1'>
3
+ <level2 xml:id='e-1.1'>e-1.1</level2>
4
+ <level2 xml:id='e-1.2'>e-1.2</level2>
5
+ </level1>
6
+
7
+ <level1 xml:id='e-2'>
8
+ <level2 xml:id='e-2.1'>e-2.1</level2>
9
+ <level2 xml:id='e-2.2'>e-2.2</level2>
10
+ <level2 xml:id='e-2.3'>e-2.3</level2>
11
+ <level2 xml:id='e-2.4'>e-2.4</level2>
12
+ <level2 xml:id='e-2.5'>e-2.5</level2>
13
+ <level2 xml:id='e-2.6'>e-2.6</level2>
14
+ </level1>
15
+
16
+ <level1 xml:id='e-3'>
17
+ <level2 xml:id='e-3.1'>
18
+ <level3 xml:id='e-3.1.1'>e-3.1.1</level3>
19
+ <level3 xml:id='e-3.1.2'>e-3.1.2</level3>
20
+ <level3 xml:id='e-3.1.3'>e-3.1.3</level3>
21
+ </level2>
22
+ <level2 xml:id='e-3.2'>e-3.2</level2>
23
+ <level2 xml:id='e-3.3'>
24
+ <level3 xml:id='e-3.3.1'>e-3.3.1</level3>
25
+ <level3 xml:id='e-3.3.2'>
26
+ <level4 xml:id='e-3.3.2.1'>e-3.3.2.1</level4>
27
+ <level4 xml:id='e-3.3.2.2'>e-3.3.2.2</level4>
28
+ <level4 xml:id='e-3.3.2.3'>e-3.3.2.3</level4>
29
+ </level3>
30
+ <level3 xml:id='e-3.3.3'>e-3.3.3</level3>
31
+ </level2>
32
+ <level2 xml:id='e-3.4'>e-3.4</level2>
33
+ </level1>
34
+ </root>
@@ -0,0 +1,20 @@
1
+ <root xml:id='e-root'>
2
+ <level1 xml:id='e-1'>
3
+ <level2 xml:id='e-1.1'>e-1.1</level2>
4
+ <level2 xml:id='e-1.2'>e-1.2</level2>
5
+ </level1>
6
+
7
+ <level1 xml:id='e-2'>
8
+ <level2 xml:id='e-2.1'>e-2.1</level2>
9
+ <level2 xml:id='e-2.2'>e-2.2</level2>
10
+ <level2 xml:id='e-2.3'>e-2.3</level2>
11
+ <level2 xml:id='e-2.4'>e-2.4</level2>
12
+ <level2 xml:id='e-2.5'>e-2.5</level2>
13
+ <level2 xml:id='e-2.6'>e-2.6</level2>
14
+ </level1>
15
+
16
+ <level1 xml:id='e-3'>
17
+ <level2 xml:id='e-3.1'>e-3.1</level2>
18
+ <level2 xml:id='e-3.2'>e-3.2</level2>
19
+ </level1>
20
+ </root>
@@ -0,0 +1,47 @@
1
+ RSpec::Matchers.define :have_element do |xpath|
2
+ match do |model|
3
+ result = model.xpath(xpath)
4
+
5
+ result.count == 1
6
+ end
7
+
8
+ failure_message_for_should do |model|
9
+ result = model.xpath(xpath)
10
+
11
+ "expected one element matching '#{xpath}' but #{result.count} found"
12
+ end
13
+ end
14
+
15
+ RSpec::Matchers.define :have_no_element do |xpath|
16
+ match do |model|
17
+ result = model.xpath(xpath)
18
+
19
+ result.count == 0
20
+ end
21
+
22
+ failure_message_for_should do |model|
23
+ result = model.xpath(xpath)
24
+
25
+ "expected no elements matching `#{xpath}` but #{result.count} found"
26
+ end
27
+ end
28
+
29
+ RSpec::Matchers.define :have_element_with_id do |id|
30
+ match do |model|
31
+ expect(model).to have_element("//*[@xml:id = '#{id}']")
32
+ end
33
+
34
+ failure_message_for_should do |model|
35
+ "expected only one element with ID `#{id}` but ??? found"
36
+ end
37
+ end
38
+
39
+ RSpec::Matchers.define :have_no_element_with_id do |id|
40
+ match do |model|
41
+ expect(model).to have_no_element("//*[@xml:id = '#{id}']")
42
+ end
43
+
44
+ failure_message_for_should do |model|
45
+ "expected no elements with ID `#{id}` but ??? found"
46
+ end
47
+ end
@@ -0,0 +1,186 @@
1
+ require 'xml/focus'
2
+
3
+ require 'nokogiri'
4
+
5
+ require 'spec_helpers'
6
+
7
+ describe XML::Focus do
8
+ context "with shallow documents" do
9
+ context "when focuses on simple sequences" do
10
+ before(:all) do
11
+ xml = Nokogiri::XML(open('spec/fixtures/shallow.xml'))
12
+ first = '@xml:id="e-2.2"'
13
+ last = '@xml:id="e-2.5"'
14
+
15
+ @portion = XML::Focus(xml, first, last)
16
+ end
17
+
18
+ it "returns all the required elements" do
19
+ expect(@portion).to have_element('//level2[@xml:id = "e-2.2"]')
20
+ expect(@portion).to have_element('//level2[@xml:id = "e-2.3"]')
21
+ expect(@portion).to have_element('//level2[@xml:id = "e-2.4"]')
22
+ expect(@portion).to have_element('//level2[@xml:id = "e-2.5"]')
23
+
24
+ expect(@portion.xpath('//level2')).to have(4).elements
25
+ end
26
+
27
+ it "preserves the structure" do
28
+ expect(@portion).to have_element_with_id('e-root')
29
+ expect(@portion).to have_element_with_id('e-2')
30
+ end
31
+
32
+ it "does not include extra parents" do
33
+ expect(@portion).to have_no_element('//level1[@xml:id = "e-1"]')
34
+ expect(@portion).to have_no_element('//level1[@xml:id = "e-3"]')
35
+ end
36
+
37
+ it "does not include extra siblings" do
38
+ expect(@portion).to have_no_element_with_id('e-1.1')
39
+ expect(@portion).to have_no_element_with_id('e-1.2')
40
+
41
+ expect(@portion).to have_no_element_with_id('e-2.1')
42
+ expect(@portion).to have_no_element_with_id('e-2.6')
43
+
44
+ expect(@portion).to have_no_element_with_id('e-3.1')
45
+ expect(@portion).to have_no_element_with_id('e-3.2')
46
+ end
47
+ end
48
+
49
+ context "when focuses on overlapping sequences" do
50
+ before(:all) do
51
+ xml = Nokogiri::XML(open('spec/fixtures/shallow.xml'))
52
+ first = '@xml:id="e-2.2"'
53
+ last = '@xml:id="e-3.1"'
54
+
55
+ @portion = XML::Focus(xml, first, last)
56
+ end
57
+
58
+ it "returns all the required elements" do
59
+ expect(@portion).to have_element('//level2[@xml:id = "e-2.2"]')
60
+ expect(@portion).to have_element('//level2[@xml:id = "e-2.3"]')
61
+ expect(@portion).to have_element('//level2[@xml:id = "e-2.4"]')
62
+ expect(@portion).to have_element('//level2[@xml:id = "e-2.5"]')
63
+ expect(@portion).to have_element('//level2[@xml:id = "e-2.6"]')
64
+ expect(@portion).to have_element('//level2[@xml:id = "e-3.1"]')
65
+
66
+ expect(@portion.xpath('//level2')).to have(6).elements
67
+ end
68
+
69
+ it "preserves the structure" do
70
+ expect(@portion).to have_element_with_id('e-root')
71
+ expect(@portion).to have_element_with_id('e-2')
72
+ expect(@portion).to have_element_with_id('e-3')
73
+ end
74
+
75
+ it "does not include extra parents" do
76
+ expect(@portion).to have_no_element('//level1[@xml:id = "e-1"]')
77
+ end
78
+
79
+ it "does not include extra siblings" do
80
+ expect(@portion).to have_no_element_with_id('e-1.1')
81
+ expect(@portion).to have_no_element_with_id('e-1.2')
82
+
83
+ expect(@portion).to have_no_element_with_id('e-2.1')
84
+
85
+ expect(@portion).to have_no_element_with_id('e-3.2')
86
+ end
87
+ end
88
+ end
89
+
90
+ context "with deep documents" do
91
+ before(:all) do
92
+ xml = Nokogiri::XML(open('spec/fixtures/deep.xml'))
93
+ first = '@xml:id="e-2.2"'
94
+ last = '@xml:id="e-3.3.2.2"'
95
+
96
+ @portion = XML::Focus(xml, first, last)
97
+ end
98
+
99
+ it "returns all the required elements" do
100
+ expect(@portion).to have_element('//level2[@xml:id = "e-2.2"]')
101
+ expect(@portion).to have_element('//level2[@xml:id = "e-2.3"]')
102
+ expect(@portion).to have_element('//level2[@xml:id = "e-2.4"]')
103
+ expect(@portion).to have_element('//level2[@xml:id = "e-2.5"]')
104
+ expect(@portion).to have_element('//level2[@xml:id = "e-2.6"]')
105
+
106
+ expect(@portion).to have_element('//level2[@xml:id = "e-3.1"]')
107
+
108
+ expect(@portion).to have_element('//level3[@xml:id = "e-3.1.1"]')
109
+ expect(@portion).to have_element('//level3[@xml:id = "e-3.1.2"]')
110
+ expect(@portion).to have_element('//level3[@xml:id = "e-3.1.3"]')
111
+
112
+ expect(@portion).to have_element('//level2[@xml:id = "e-3.2"]')
113
+ expect(@portion).to have_element('//level2[@xml:id = "e-3.3"]')
114
+
115
+ expect(@portion).to have_element('//level3[@xml:id = "e-3.3.1"]')
116
+ expect(@portion).to have_element('//level3[@xml:id = "e-3.3.2"]')
117
+
118
+ expect(@portion).to have_element('//level4[@xml:id = "e-3.3.2.1"]')
119
+ expect(@portion).to have_element('//level4[@xml:id = "e-3.3.2.2"]')
120
+
121
+ expect(@portion.xpath('//level2')).to have(8).elements
122
+ expect(@portion.xpath('//level3')).to have(5).elements
123
+ expect(@portion.xpath('//level4')).to have(2).elements
124
+ end
125
+
126
+ it "preserves the structure" do
127
+ expect(@portion).to have_element_with_id('e-root')
128
+
129
+ expect(@portion).to have_element_with_id('e-2')
130
+ expect(@portion).to have_element_with_id('e-3')
131
+
132
+ expect(@portion).to have_element_with_id('e-3.1')
133
+
134
+ expect(@portion).to have_element_with_id('e-3.2')
135
+ expect(@portion).to have_element_with_id('e-3.3')
136
+ end
137
+
138
+ it "does not include extra parents" do
139
+ expect(@portion).to have_no_element('//level1[@xml:id = "e-1"]')
140
+ expect(@portion).to have_no_element('//level1[@xml:id = "e-3.4"]')
141
+ end
142
+
143
+ it "does not include extra siblings" do
144
+ expect(@portion).to have_no_element_with_id('e-1.1')
145
+ expect(@portion).to have_no_element_with_id('e-1.2')
146
+
147
+ expect(@portion).to have_no_element_with_id('e-2.1')
148
+
149
+ expect(@portion).to have_no_element_with_id('e-3.3.2.3')
150
+ expect(@portion).to have_no_element_with_id('e-3.3.3')
151
+ expect(@portion).to have_no_element_with_id('e-3.4')
152
+ end
153
+ end
154
+
155
+ describe "quote support" do
156
+ it "works with double quotes in condition" do
157
+ xml = Nokogiri::XML(open('spec/fixtures/shallow.xml'))
158
+ first = %q{@xml:id="e-2.2"}
159
+ last = %q{@xml:id="e-2.5"}
160
+
161
+ expect { XML::Focus(xml, first, last) }.not_to raise_exception
162
+ end
163
+
164
+ it "works with single quotes in condition" do
165
+ pending("Not yet implemented")
166
+
167
+ xml = Nokogiri::XML(open('spec/fixtures/shallow.xml'))
168
+ first = %q{@xml:id='e-2.2'}
169
+ last = %q{@xml:id='e-2.5'}
170
+
171
+ expect { XML::Focus(xml, first, last) }.not_to raise_exception
172
+ end
173
+
174
+ it "works with mixed quotes in condition" do
175
+ pending("Not yet implemented")
176
+
177
+ xml = Nokogiri::XML(open('spec/fixtures/shallow.xml'))
178
+ first = %q{@xml:id="e-2.2" and name()='level2'}
179
+ last = %q{@xml:id="e-2.5"}
180
+
181
+ expect { XML::Focus(xml, first, last) }.not_to raise_exception
182
+ end
183
+
184
+ it "works with escaped quotes in condition"
185
+ end
186
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ require 'xml/focus/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "xml-focus"
9
+ spec.version = XML::Focus::VERSION
10
+ spec.authors = ['Gioele Barabucci']
11
+ spec.email = ['gioele@svario.it']
12
+ spec.summary = "Extract XML subtrees"
13
+ spec.description = "Extract subtrees from XML trees, making sure " +
14
+ "that the overall structure is preserved."
15
+ spec.homepage = 'http://github.com/gioele/xml-focus'
16
+ spec.license = 'CC0'
17
+
18
+ spec.files = `git ls-files`.split($/)
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_dependency 'nokogiri'
24
+
25
+ spec.add_development_dependency 'bundler', '~> 1.3'
26
+ spec.add_development_dependency 'rake'
27
+ spec.add_development_dependency 'rspec'
28
+ end
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xml-focus
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Gioele Barabucci
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-08-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Extract subtrees from XML trees, making sure that the overall structure
70
+ is preserved.
71
+ email:
72
+ - gioele@svario.it
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - .gitignore
78
+ - COPYING
79
+ - Gemfile
80
+ - README.md
81
+ - Rakefile
82
+ - lib/xml/focus.rb
83
+ - lib/xml/focus/focus.xsl.erb
84
+ - lib/xml/focus/version.rb
85
+ - spec/fixtures/deep.xml
86
+ - spec/fixtures/shallow.xml
87
+ - spec/spec_helpers.rb
88
+ - spec/xml/extract_spec.rb
89
+ - xml-focus.gemspec
90
+ homepage: http://github.com/gioele/xml-focus
91
+ licenses:
92
+ - CC0
93
+ metadata: {}
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - '>='
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubyforge_project:
110
+ rubygems_version: 2.0.4
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: Extract XML subtrees
114
+ test_files:
115
+ - spec/fixtures/deep.xml
116
+ - spec/fixtures/shallow.xml
117
+ - spec/spec_helpers.rb
118
+ - spec/xml/extract_spec.rb