om 3.0.3 → 3.0.5
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.
- checksums.yaml +7 -0
- data/CONTRIBUTING.md +113 -0
- data/History.textile +0 -6
- data/README.md +41 -0
- data/gemfiles/gemfile.rails4 +1 -1
- data/lib/om.rb +2 -0
- data/lib/om/version.rb +1 -1
- data/lib/om/xml/document.rb +6 -1
- data/lib/om/xml/dynamic_node.rb +12 -2
- data/lib/om/xml/term.rb +242 -239
- data/lib/om/xml/term_builder.rb +39 -8
- data/lib/om/xml/terminology.rb +4 -1
- data/lib/om/xml/terminology_based_solrizer.rb +1 -1
- data/om.gemspec +1 -2
- data/spec/integration/serialization_spec.rb +37 -19
- data/spec/integration/subclass_terminology_spec.rb +14 -2
- data/spec/unit/container_spec.rb +2 -2
- data/spec/unit/dynamic_node_spec.rb +23 -0
- data/spec/unit/term_builder_spec.rb +41 -19
- data/spec/unit/term_spec.rb +3 -3
- data/spec/unit/term_xpath_generator_spec.rb +1 -1
- data/spec/unit/xml_terminology_based_solrizer_spec.rb +1 -0
- metadata +40 -68
- data/README.textile +0 -36
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1de731732be29cdf0c460beebcb5b603deccb18c
|
4
|
+
data.tar.gz: 6fa913f9d7bcdd57e7d29555a266d265dd426ad0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e947f9c90119f8ac601105505d947a2ad78d4307ada79f3f7e609fd64ecb71c0330bbe668438e1d4ee71aac4dbcf112a9f5d2a0fe4a841d9a22125b81cd78e9d
|
7
|
+
data.tar.gz: 4db6a2f387c9630f6e9909966bc81e9939110ac92b6588745fb82766ec23d3249041ddbe70b5ddba6322b094622e51cdd09da26365bec6c6af7fcb12cc2a9f5a
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
# How to Contribute
|
2
|
+
|
3
|
+
We want your help to make Project Hydra great.
|
4
|
+
There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things.
|
5
|
+
|
6
|
+
## Hydra Project Intellectual Property Licensing and Ownership
|
7
|
+
|
8
|
+
All code contributors must have an Individual Contributor License Agreement (iCLA) on file with the Hydra Project Steering Group.
|
9
|
+
If the contributor works for an institution, the institution must have a Corporate Contributor License Agreement (cCLA) on file.
|
10
|
+
|
11
|
+
https://wiki.duraspace.org/display/hydra/Hydra+Project+Intellectual+Property+Licensing+and+Ownership
|
12
|
+
|
13
|
+
You should also add yourself to the `CONTRIBUTORS.md` file in the root of the project.
|
14
|
+
|
15
|
+
## Contribution Tasks
|
16
|
+
|
17
|
+
* Reporting Issues
|
18
|
+
* Making Changes
|
19
|
+
* Submitting Changes
|
20
|
+
* Merging Changes
|
21
|
+
|
22
|
+
### Reporting Issues
|
23
|
+
|
24
|
+
* Make sure you have a [GitHub account](https://github.com/signup/free)
|
25
|
+
* Submit a [Github issue](./issues) by:
|
26
|
+
* Clearly describing the issue
|
27
|
+
* Provide a descriptive summary
|
28
|
+
* Explain the expected behavior
|
29
|
+
* Explain the actual behavior
|
30
|
+
* Provide steps to reproduce the actual behavior
|
31
|
+
|
32
|
+
### Making Changes
|
33
|
+
|
34
|
+
* Fork the repository on GitHub
|
35
|
+
* Create a topic branch from where you want to base your work.
|
36
|
+
* This is usually the master branch.
|
37
|
+
* To quickly create a topic branch based on master; `git branch fix/master/my_contribution master`
|
38
|
+
* Then checkout the new branch with `git checkout fix/master/my_contribution`.
|
39
|
+
* Please avoid working directly on the `master` branch.
|
40
|
+
* You may find the [hub suite of commands](https://github.com/defunkt/hub) helpful
|
41
|
+
* Make commits of logical units.
|
42
|
+
* Your commit should include a high level description of your work in HISTORY.textile
|
43
|
+
* Check for unnecessary whitespace with `git diff --check` before committing.
|
44
|
+
* Make sure your commit messages are [well formed](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
|
45
|
+
* If you created an issue, you can close it by including "Closes #issue" in your commit message. See [Github's blog post for more details](https://github.com/blog/1386-closing-issues-via-commit-messages)
|
46
|
+
|
47
|
+
```
|
48
|
+
Present tense short summary (50 characters or less)
|
49
|
+
|
50
|
+
More detailed description, if necessary. It should be wrapped to 72
|
51
|
+
characters. Try to be as descriptive as you can, even if you think that
|
52
|
+
the commit content is obvious, it may not be obvious to others. You
|
53
|
+
should add such description also if it's already present in bug tracker,
|
54
|
+
it should not be necessary to visit a webpage to check the history.
|
55
|
+
|
56
|
+
Include Closes #<issue-number> when relavent.
|
57
|
+
|
58
|
+
Description can have multiple paragraphs and you can use code examples
|
59
|
+
inside, just indent it with 4 spaces:
|
60
|
+
|
61
|
+
class PostsController
|
62
|
+
def index
|
63
|
+
respond_with Post.limit(10)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
You can also add bullet points:
|
68
|
+
|
69
|
+
- you can use dashes or asterisks
|
70
|
+
|
71
|
+
- also, try to indent next line of a point for readability, if it's too
|
72
|
+
long to fit in 72 characters
|
73
|
+
```
|
74
|
+
|
75
|
+
* Make sure you have added the necessary tests for your changes.
|
76
|
+
* Run _all_ the tests to assure nothing else was accidentally broken.
|
77
|
+
* When you are ready to submit a pull request
|
78
|
+
|
79
|
+
### Submitting Changes
|
80
|
+
|
81
|
+
[Detailed Walkthrough of One Pull Request per Commit](http://ndlib.github.io/practices/one-commit-per-pull-request/)
|
82
|
+
|
83
|
+
* Read the article ["Using Pull Requests"](https://help.github.com/articles/using-pull-requests) on GitHub.
|
84
|
+
* Make sure your branch is up to date with its parent branch (i.e. master)
|
85
|
+
* `git checkout master`
|
86
|
+
* `git pull --rebase`
|
87
|
+
* `git checkout <your-branch>`
|
88
|
+
* `git rebase master`
|
89
|
+
* It is likely a good idea to run your tests again.
|
90
|
+
* Squash the commits for your branch into one commit
|
91
|
+
* `git rebase --interactive HEAD~<number-of-commits>` ([See Github help](https://help.github.com/articles/interactive-rebase))
|
92
|
+
* To determine the number of commits on your branch: `git log master..<your-branch> --oneline | wc -l`
|
93
|
+
* Squashing your branch's changes into one commit is "good form" and helps the person merging your request to see everything that is going on.
|
94
|
+
* Push your changes to a topic branch in your fork of the repository.
|
95
|
+
* Submit a pull request from your fork to the project.
|
96
|
+
|
97
|
+
### Merging Changes
|
98
|
+
|
99
|
+
* It is considered "poor from" to merge your own request.
|
100
|
+
* Please take the time to review the changes and get a sense of what is being changed. Things to consider:
|
101
|
+
* Does the commit message explain what is going on?
|
102
|
+
* Does the code changes have tests? _Not all changes need new tests, some changes are refactorings_
|
103
|
+
* Does the commit contain more than it should? Are two separate concerns being addressed in one commit?
|
104
|
+
* Did the Travis tests complete successfully?
|
105
|
+
* If you are uncertain, bring other contributors into the conversation by creating a comment that includes their @username.
|
106
|
+
* If you like the pull request, but want others to chime in, create a +1 comment and tag a user.
|
107
|
+
|
108
|
+
# Additional Resources
|
109
|
+
|
110
|
+
* [General GitHub documentation](http://help.github.com/)
|
111
|
+
* [GitHub pull request documentation](http://help.github.com/send-pull-requests/)
|
112
|
+
* [Pro Git](http://git-scm.com/book) is both a free and excellent book about Git.
|
113
|
+
* [A Git Config for Contributing](http://ndlib.github.io/practices/my-typical-per-project-git-config/)
|
data/History.textile
CHANGED
@@ -1,9 +1,3 @@
|
|
1
|
-
h3. 3.0.3 (19 Jul 2013)
|
2
|
-
Fix overly-aggressive template inheritance bug introduced in 3.0.2
|
3
|
-
|
4
|
-
h3. 3.0.2 (15 Jul 2013)
|
5
|
-
Add terminology and template inheritance
|
6
|
-
|
7
1
|
h3. 3.0.1 (25 Jun 2013)
|
8
2
|
Fix bug where values that were the same as the existing values were
|
9
3
|
removed from the update list
|
data/README.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
[](https://travis-ci.org/projecthydra/om)
|
2
|
+
[](http://badge.fury.io/rb/om)
|
3
|
+
|
4
|
+
# om (Opinionated Metadata)
|
5
|
+
|
6
|
+
A library to help you tame sprawling XML schemas like MODS.
|
7
|
+
|
8
|
+
OM allows you to define a "terminology" to ease translation between XML and ruby objects – you can query the xml for Nodes or node values without ever writing a line of XPath.
|
9
|
+
|
10
|
+
OM "terms" are ruby symbols you define (in the terminology) that map specific XML content into ruby object attributes.
|
11
|
+
|
12
|
+
## Tutorials & Reference
|
13
|
+
|
14
|
+
* [Tame Your XML with OM](https://github.com/projecthydra/om/wiki/Tame-your-XML-with-OM)
|
15
|
+
* [Common OM Patterns](https://github.com/projecthydra/om/blob/master/COMMON_OM_PATTERNS.textile)
|
16
|
+
|
17
|
+
### Solrizing Documents
|
18
|
+
|
19
|
+
The solrizer gem provides support for indexing XML documents into Solr based on OM Terminologies.
|
20
|
+
That process is documented in the [solrizer README](https://github.com/projecthydra/solrizer)
|
21
|
+
|
22
|
+
## OM in the Wild
|
23
|
+
|
24
|
+
We have a page on the Hydra wiki with a list of OM Terminologies in active use:
|
25
|
+
[OM Terminologies in the Wild](https://wiki.duraspace.org/display/hydra/OM+Terminologies+in+the+Wild)
|
26
|
+
|
27
|
+
## Acknowledgments
|
28
|
+
|
29
|
+
### Creator
|
30
|
+
|
31
|
+
Matt Zumwalt ([MediaShelf](http://yourmediashelf.com)
|
32
|
+
|
33
|
+
### Thanks To
|
34
|
+
|
35
|
+
* Bess Sadler, who enabled us to take knowledge gleaned from developing Blacklight and apply it to OM metadata indexing
|
36
|
+
* Ross Singer
|
37
|
+
* Those who participated in the Opinionated MODS breakout session at Code4Lib 2010
|
38
|
+
|
39
|
+
## Copyright
|
40
|
+
|
41
|
+
Copyright (c) 2010 Matt Zumwalt. See LICENSE for details.
|
data/gemfiles/gemfile.rails4
CHANGED
data/lib/om.rb
CHANGED
data/lib/om/version.rb
CHANGED
data/lib/om/xml/document.rb
CHANGED
@@ -24,7 +24,12 @@ module OM::XML::Document
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def terminology_builder
|
27
|
-
@terminology_builder
|
27
|
+
return @terminology_builder if @terminology_builder
|
28
|
+
@terminology_builder = if superclass.respond_to? :terminology_builder
|
29
|
+
superclass.terminology_builder.dup
|
30
|
+
else
|
31
|
+
OM::XML::Terminology::Builder.new
|
32
|
+
end
|
28
33
|
end
|
29
34
|
|
30
35
|
def template_registry
|
data/lib/om/xml/dynamic_node.rb
CHANGED
@@ -44,7 +44,7 @@ module OM
|
|
44
44
|
elsif args.length > 1
|
45
45
|
new_update_node_with_index(name, args)
|
46
46
|
else
|
47
|
-
child =
|
47
|
+
child = term_child_by_name(term.nil? ? parent.term : term, name)
|
48
48
|
if child
|
49
49
|
OM::XML::DynamicNode.new(name, args.first, @document, child, self)
|
50
50
|
else
|
@@ -53,6 +53,10 @@ module OM
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
+
def respond_to?(method)
|
57
|
+
super || val.respond_to?(method)
|
58
|
+
end
|
59
|
+
|
56
60
|
def new_update_node(name, args)
|
57
61
|
modified_name = name.to_s.chop.to_sym
|
58
62
|
child = term.retrieve_term(modified_name)
|
@@ -97,6 +101,8 @@ module OM
|
|
97
101
|
end
|
98
102
|
end
|
99
103
|
|
104
|
+
# This resolves the target of this dynamic node into a reified Array
|
105
|
+
# @return [Array]
|
100
106
|
def val
|
101
107
|
query = xpath
|
102
108
|
trim_text = !query.index("text()").nil?
|
@@ -119,7 +125,11 @@ module OM
|
|
119
125
|
end
|
120
126
|
|
121
127
|
def ==(other)
|
122
|
-
|
128
|
+
other == val
|
129
|
+
end
|
130
|
+
|
131
|
+
def !=(other)
|
132
|
+
val != other
|
123
133
|
end
|
124
134
|
|
125
135
|
def eql?(other)
|
data/lib/om/xml/term.rb
CHANGED
@@ -4,288 +4,291 @@ require 'om/xml/term_builder'
|
|
4
4
|
# type, index_as, attributes,
|
5
5
|
# is_root_term, required
|
6
6
|
#
|
7
|
-
|
7
|
+
module OM
|
8
|
+
class XML::Term
|
8
9
|
|
9
|
-
|
10
|
-
|
10
|
+
include TreeNode
|
11
|
+
include XML::TermBuilder
|
11
12
|
|
12
|
-
|
13
|
-
|
13
|
+
attr_accessor :name, :xpath, :xpath_constrained, :xpath_relative, :path, :index_as, :required, :type, :variant_of, :path, :default_content_path, :is_root_term
|
14
|
+
attr_accessor :children, :internal_xml, :terminology
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
# Any XML attributes that qualify the Term.
|
17
|
+
#
|
18
|
+
# @example Declare a Term that has a given attribute (ie. //title[@xml:lang='eng'])
|
19
|
+
# t.english_title(:path=>"title", :attributes=>{"xml:lang"=>"eng"}
|
20
|
+
# @example Use nil to point to nodes that do not have a given attribute (ie. //title[not(@xml:lang)])
|
21
|
+
# t.title_without_lang_attribute(:path=>"title", :attributes=>{"xml:lang"=>nil})
|
22
|
+
attr_accessor :attributes
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
24
|
+
# Namespace Prefix (xmlns) for the Term.
|
25
|
+
#
|
26
|
+
# By default, OM assumes that all terms in a Terminology have the namespace set in the root of the document. If you want to set a different namespace for a Term, pass :namespace_prefix into its initializer (or call .namespace_prefix= on its builder)
|
27
|
+
# If a node has _no_ namespace, you must explicitly set namespace_prefix to nil. Currently you have to do this on _each_ term, you can't set namespace_prefix to nil for an entire Terminology.
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
# # For xml like this
|
31
|
+
# <foo xmlns="http://foo.com/schemas/fooschema" xmlns:bar="http://bar.com/schemas/barschema">
|
32
|
+
# <address>1400 Pennsylvania Avenue</address>
|
33
|
+
# <bar:latitude>56</bar:latitude>
|
34
|
+
# </foo>
|
35
|
+
#
|
36
|
+
# # The Terminology would look like this
|
37
|
+
# OM::XML::Terminology::Builder.new do |t|
|
38
|
+
# t.root(:name=>:foo, :path=>"foo", :xmlns=>"http://foo.com/schemas/fooschema", "xmlns:bar"=>"http://bar.com/schemas/barschema")
|
39
|
+
# t.address
|
40
|
+
# t.latitude(:namespace_prefix=>"bar")
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
attr_accessor :namespace_prefix
|
43
44
|
|
44
45
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
46
|
+
# h2. Namespaces
|
47
|
+
# By default, OM assumes you have no namespace defined unless it is explicitly defined at the root of your document.
|
48
|
+
#
|
49
|
+
# @param [Symbol] name the name to refer to this term by
|
50
|
+
# @param [Hash] opts
|
51
|
+
# @option opts [Array] :index_as a list of indexing hints provided to to_solr
|
52
|
+
# @option opts [String] :path partial xpath that points to the node.
|
53
|
+
# @option opts [Hash] :attributes xml attributes to match in the selector
|
54
|
+
# @option opts [String] :namespace_prefix xml namespace for this node. If not provided, the default namespace set in the terminology will be used.
|
55
|
+
# @option opts [Symbol] :type one of :string, :date, :time :integer. Defaults to :string
|
56
|
+
def initialize(name, opts={}, terminology=nil)
|
57
|
+
opts = {:ancestors=>[], :children=>{}}.merge(opts)
|
58
|
+
[:children, :ancestors,:path, :index_as, :required, :variant_of, :path, :attributes, :default_content_path, :namespace_prefix].each do |accessor_name|
|
59
|
+
instance_variable_set("@#{accessor_name}", opts.fetch(accessor_name, nil) )
|
60
|
+
end
|
60
61
|
|
61
|
-
|
62
|
+
self.type = opts[:type] || :string
|
62
63
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
64
|
+
unless terminology.nil?
|
65
|
+
if opts[:namespace_prefix].nil?
|
66
|
+
unless terminology.namespaces["xmlns"].nil?
|
67
|
+
@namespace_prefix = "oxns"
|
68
|
+
end
|
67
69
|
end
|
68
70
|
end
|
71
|
+
@name = name
|
72
|
+
if @path.nil? || @path.empty?
|
73
|
+
@path = name.to_s
|
74
|
+
end
|
69
75
|
end
|
70
|
-
@name = name
|
71
|
-
if @path.nil? || @path.empty?
|
72
|
-
@path = name.to_s
|
73
|
-
end
|
74
|
-
end
|
75
76
|
|
76
77
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
78
|
+
def sanitize_new_values(new_values)
|
79
|
+
# Sanitize new_values to always be a hash with indexes
|
80
|
+
case new_values
|
81
|
+
when Hash
|
82
|
+
sanitize_new_values(new_values.values)
|
83
|
+
when Array
|
84
|
+
new_values.map {|v| serialize(v)}
|
85
|
+
else
|
86
|
+
[serialize(new_values)]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# @param val [String,Date,Integer]
|
91
|
+
def serialize (val)
|
92
|
+
return if val.nil?
|
93
|
+
case type
|
94
|
+
when :date, :integer
|
95
|
+
val.to_s
|
96
|
+
when :time
|
97
|
+
time = val.to_time
|
98
|
+
raise TypeMismatch, "Can't convert `#{val}` to time" if time.nil?
|
99
|
+
time.utc.iso8601
|
100
|
+
when :boolean
|
101
|
+
val.to_s
|
84
102
|
else
|
85
|
-
|
103
|
+
val
|
86
104
|
end
|
87
|
-
end
|
88
|
-
|
89
|
-
# @param val [String,Date,Integer]
|
90
|
-
def serialize (val)
|
91
|
-
return if val.nil?
|
92
|
-
case type
|
93
|
-
when :date, :integer
|
94
|
-
val.to_s
|
95
|
-
when :time
|
96
|
-
val.to_time.utc.iso8601
|
97
|
-
when :boolean
|
98
|
-
val.to_s
|
99
|
-
else
|
100
|
-
val
|
101
105
|
end
|
102
|
-
end
|
103
106
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
107
|
+
# @param [String] val the value (from xml) to deserialize into the correct object type.
|
108
|
+
# @return [String,Date,Integer]
|
109
|
+
def deserialize(val)
|
110
|
+
case type
|
111
|
+
when :date
|
112
|
+
#TODO use present?
|
113
|
+
val.map { |v| !v.empty? ? Date.parse(v) : nil}
|
114
|
+
when :time
|
115
|
+
#TODO use present?
|
116
|
+
val.map { |v| !v.empty? ? DateTime.parse(v) : nil}
|
117
|
+
when :integer
|
118
|
+
#TODO use blank?
|
119
|
+
val.map { |v| v.empty? ? nil : v.to_i}
|
120
|
+
when :boolean
|
121
|
+
val.map { |v| v == 'true' }
|
122
|
+
else
|
123
|
+
val
|
124
|
+
end
|
121
125
|
end
|
122
|
-
end
|
123
126
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
end
|
130
|
-
new_mapper = self.new(name, :attributes=>attributes)
|
131
|
-
[:index_as, :required, :type, :variant_of, :path, :default_content_path, :namespace_prefix].each do |accessor_name|
|
132
|
-
attribute = mapper_xml.attribute(accessor_name.to_s)
|
133
|
-
unless attribute.nil?
|
134
|
-
new_mapper.instance_variable_set("@#{accessor_name}", attribute.text )
|
127
|
+
def self.from_node(mapper_xml)
|
128
|
+
name = mapper_xml.attribute("name").text.to_sym
|
129
|
+
attributes = {}
|
130
|
+
mapper_xml.xpath("./attribute").each do |a|
|
131
|
+
attributes[a.attribute("name").text.to_sym] = a.attribute("value").text
|
135
132
|
end
|
136
|
-
|
137
|
-
|
133
|
+
new_mapper = self.new(name, :attributes=>attributes)
|
134
|
+
[:index_as, :required, :type, :variant_of, :path, :default_content_path, :namespace_prefix].each do |accessor_name|
|
135
|
+
attribute = mapper_xml.attribute(accessor_name.to_s)
|
136
|
+
unless attribute.nil?
|
137
|
+
new_mapper.instance_variable_set("@#{accessor_name}", attribute.text )
|
138
|
+
end
|
139
|
+
end
|
140
|
+
new_mapper.internal_xml = mapper_xml
|
138
141
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
142
|
+
mapper_xml.xpath("./mapper").each do |child_node|
|
143
|
+
child = self.from_node(child_node)
|
144
|
+
new_mapper.add_child(child)
|
145
|
+
end
|
143
146
|
|
144
|
-
|
145
|
-
|
147
|
+
return new_mapper
|
148
|
+
end
|
146
149
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
150
|
+
##
|
151
|
+
# Always co-erce :index_as attributes into an Array
|
152
|
+
def index_as
|
153
|
+
Array(@index_as)
|
154
|
+
end
|
152
155
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
156
|
+
# crawl down into mapper's children hash to find the desired mapper
|
157
|
+
# ie. @test_mapper.retrieve_mapper(:conference, :role, :text)
|
158
|
+
def retrieve_term(*pointers)
|
159
|
+
children_hash = self.children
|
160
|
+
pointers.each do |p|
|
161
|
+
if children_hash.has_key?(p)
|
162
|
+
target = children_hash[p]
|
163
|
+
if pointers.index(p) == pointers.length-1
|
164
|
+
return target
|
165
|
+
else
|
166
|
+
children_hash = target.children
|
167
|
+
end
|
162
168
|
else
|
163
|
-
|
169
|
+
return nil
|
164
170
|
end
|
165
|
-
else
|
166
|
-
return nil
|
167
171
|
end
|
172
|
+
return target
|
168
173
|
end
|
169
|
-
return target
|
170
|
-
end
|
171
174
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
+
def is_root_term?
|
176
|
+
@is_root_term == true
|
177
|
+
end
|
175
178
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
+
def xpath_absolute
|
180
|
+
@xpath
|
181
|
+
end
|
179
182
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
183
|
+
# +term_pointers+ reference to the property you want to generate a builder template for
|
184
|
+
# @param [Hash] extra_opts
|
185
|
+
# @option extra_opts [Hash] :attributes
|
186
|
+
def xml_builder_template(extra_opts = {})
|
187
|
+
extra_attributes = extra_opts.fetch(:attributes, {})
|
185
188
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
189
|
+
node_options = []
|
190
|
+
node_child_template = ""
|
191
|
+
if !self.default_content_path.nil?
|
192
|
+
node_child_options = ["\':::builder_new_value:::\'"]
|
193
|
+
node_child_template = " { xml.#{self.default_content_path}( #{OM::XML.delimited_list(node_child_options)} ) }"
|
194
|
+
else
|
195
|
+
node_options = ["\':::builder_new_value:::\'"]
|
196
|
+
end
|
197
|
+
if !self.attributes.nil?
|
198
|
+
self.attributes.merge(extra_attributes).each_pair do |k,v|
|
199
|
+
node_options << "\'#{k}\'=>\'#{v}\'" unless v == :none
|
200
|
+
end
|
197
201
|
end
|
198
|
-
end
|
199
202
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
203
|
+
builder_ref = if self.path.include?(":")
|
204
|
+
"xml['#{self.path[0..path.index(":")-1]}']"
|
205
|
+
elsif !self.namespace_prefix.nil? and self.namespace_prefix != 'oxns'
|
206
|
+
"xml['#{self.namespace_prefix}']"
|
207
|
+
else
|
208
|
+
"xml"
|
209
|
+
end
|
207
210
|
|
208
|
-
|
211
|
+
attribute = OM::XML.delimited_list(node_options)
|
209
212
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
213
|
+
builder_method = if self.path.include?(":")
|
214
|
+
"#{self.path[path.index(":")+1..-1]}( #{attribute} )"
|
215
|
+
elsif self.path.include?(".")
|
216
|
+
"send(:\\\"#{self.path}\\\", #{attribute} )"
|
217
|
+
elsif self.path.kind_of?(Hash) && self.path[:attribute]
|
218
|
+
"@#{self.path[:attribute]}( #{OM::XML.delimited_list(node_options)} )"
|
219
|
+
elsif Nokogiri::XML::Builder.method_defined? self.path.to_sym
|
220
|
+
"#{self.path}_( #{OM::XML.delimited_list(node_options)} )"
|
221
|
+
else
|
222
|
+
"#{self.path}( #{OM::XML.delimited_list(node_options)} )"
|
223
|
+
end
|
224
|
+
template = "#{builder_ref}.#{builder_method}#{node_child_template}"
|
225
|
+
return template.gsub( /:::(.*?):::/ ) { '#{'+$1+'}' }
|
220
226
|
end
|
221
|
-
template = "#{builder_ref}.#{builder_method}#{node_child_template}"
|
222
|
-
return template.gsub( /:::(.*?):::/ ) { '#{'+$1+'}' }
|
223
|
-
end
|
224
227
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
228
|
+
# Generates absolute, relative, and constrained xpaths for the term, setting xpath, xpath_relative, and xpath_constrained accordingly.
|
229
|
+
# Also triggers update_xpath_values! on all child nodes, as their absolute paths rely on those of their parent nodes.
|
230
|
+
def generate_xpath_queries!
|
231
|
+
self.xpath = OM::XML::TermXpathGenerator.generate_absolute_xpath(self)
|
232
|
+
self.xpath_constrained = OM::XML::TermXpathGenerator.generate_constrained_xpath(self)
|
233
|
+
self.xpath_relative = OM::XML::TermXpathGenerator.generate_relative_xpath(self)
|
234
|
+
self.children.each_value {|child| child.generate_xpath_queries! }
|
235
|
+
return self
|
236
|
+
end
|
234
237
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
238
|
+
# Return an XML representation of the Term
|
239
|
+
# @param [Hash] options the term will be added to it. If :children=>false, skips rendering child Terms
|
240
|
+
# @param [Nokogiri::XML::Document] document (optional) document to insert the term xml into
|
241
|
+
# @return [Nokogiri::XML::Document]
|
242
|
+
# @example If :children=>false, skips rendering child Terms
|
243
|
+
# term.to_xml(:children=>false)
|
244
|
+
# @example You can provide your own Nokogiri document to insert the xml into
|
245
|
+
# doc = Nokogiri::XML::Document.new
|
246
|
+
# term.to_xml({}, document=doc)
|
247
|
+
def to_xml(options={}, document=Nokogiri::XML::Document.new)
|
248
|
+
builder = Nokogiri::XML::Builder.with(document) do |xml|
|
249
|
+
xml.term(:name=>name) {
|
250
|
+
if is_root_term?
|
251
|
+
xml.is_root_term("true")
|
252
|
+
end
|
253
|
+
xml.path path
|
254
|
+
xml.namespace_prefix namespace_prefix
|
255
|
+
unless attributes.nil? || attributes.empty?
|
256
|
+
xml.attributes {
|
257
|
+
attributes.each_pair do |attribute_name, attribute_value|
|
258
|
+
xml.send("#{attribute_name}_".to_sym, attribute_value)
|
259
|
+
end
|
260
|
+
}
|
261
|
+
end
|
262
|
+
xml.index_as {
|
263
|
+
unless index_as.nil?
|
264
|
+
index_as.each { |index_type| xml.index_type }
|
256
265
|
end
|
257
266
|
}
|
258
|
-
|
259
|
-
|
260
|
-
unless
|
261
|
-
|
267
|
+
xml.required required
|
268
|
+
xml.data_type type
|
269
|
+
unless variant_of.nil?
|
270
|
+
xml.variant_of variant_of
|
271
|
+
end
|
272
|
+
unless default_content_path.nil?
|
273
|
+
xml.default_content_path default_content_path
|
274
|
+
end
|
275
|
+
xml.xpath {
|
276
|
+
xml.relative xpath_relative
|
277
|
+
xml.absolute xpath
|
278
|
+
xml.constrained xpath_constrained
|
279
|
+
}
|
280
|
+
if options.fetch(:children, true)
|
281
|
+
xml.children
|
262
282
|
end
|
263
283
|
}
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
xml.default_content_path default_content_path
|
271
|
-
end
|
272
|
-
xml.xpath {
|
273
|
-
xml.relative xpath_relative
|
274
|
-
xml.absolute xpath
|
275
|
-
xml.constrained xpath_constrained
|
276
|
-
}
|
277
|
-
if options.fetch(:children, true)
|
278
|
-
xml.children
|
279
|
-
end
|
280
|
-
}
|
281
|
-
end
|
282
|
-
doc = builder.doc
|
283
|
-
if options.fetch(:children, true)
|
284
|
-
children.values.each {|child| child.to_xml(options, doc.xpath("//term[@name=\"#{name}\"]/children").first)}
|
284
|
+
end
|
285
|
+
doc = builder.doc
|
286
|
+
if options.fetch(:children, true)
|
287
|
+
children.values.each {|child| child.to_xml(options, doc.xpath("//term[@name=\"#{name}\"]/children").first)}
|
288
|
+
end
|
289
|
+
return doc
|
285
290
|
end
|
286
|
-
return doc
|
287
|
-
end
|
288
|
-
|
289
|
-
# private :update_xpath_values
|
290
291
|
|
292
|
+
# private :update_xpath_values
|
293
|
+
end
|
291
294
|
end
|