om 3.0.3 → 3.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/projecthydra/om.png?branch=master)](https://travis-ci.org/projecthydra/om)
|
2
|
+
[![Gem Version](https://badge.fury.io/rb/om.png)](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
|