ddr-core 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +12 -0
  3. data/README.md +27 -0
  4. data/Rakefile +30 -0
  5. data/app/assets/config/ddr_core_manifest.js +0 -0
  6. data/app/controllers/users/omniauth_callbacks_controller.rb +11 -0
  7. data/app/controllers/users/sessions_controller.rb +15 -0
  8. data/app/models/concerns/ddr/captionable.rb +25 -0
  9. data/app/models/concerns/ddr/describable.rb +108 -0
  10. data/app/models/concerns/ddr/governable.rb +25 -0
  11. data/app/models/concerns/ddr/has_admin_metadata.rb +141 -0
  12. data/app/models/concerns/ddr/has_attachments.rb +10 -0
  13. data/app/models/concerns/ddr/has_children.rb +10 -0
  14. data/app/models/concerns/ddr/has_content.rb +132 -0
  15. data/app/models/concerns/ddr/has_extracted_text.rb +10 -0
  16. data/app/models/concerns/ddr/has_intermediate_file.rb +25 -0
  17. data/app/models/concerns/ddr/has_multires_image.rb +14 -0
  18. data/app/models/concerns/ddr/has_parent.rb +18 -0
  19. data/app/models/concerns/ddr/has_struct_metadata.rb +21 -0
  20. data/app/models/concerns/ddr/has_thumbnail.rb +33 -0
  21. data/app/models/concerns/ddr/solr_document_behavior.rb +429 -0
  22. data/app/models/concerns/ddr/streamable.rb +25 -0
  23. data/app/models/ddr/admin_set.rb +28 -0
  24. data/app/models/ddr/attachment.rb +14 -0
  25. data/app/models/ddr/collection.rb +28 -0
  26. data/app/models/ddr/component.rb +31 -0
  27. data/app/models/ddr/contact.rb +23 -0
  28. data/app/models/ddr/digest.rb +8 -0
  29. data/app/models/ddr/file.rb +40 -0
  30. data/app/models/ddr/item.rb +36 -0
  31. data/app/models/ddr/language.rb +31 -0
  32. data/app/models/ddr/media_type.rb +22 -0
  33. data/app/models/ddr/resource.rb +94 -0
  34. data/app/models/ddr/rights_statement.rb +25 -0
  35. data/app/models/ddr/target.rb +17 -0
  36. data/config/initializers/devise.rb +262 -0
  37. data/config/locales/ddr-core.en.yml +85 -0
  38. data/config/routes.rb +3 -0
  39. data/db/migrate/20141104181418_create_users.rb +34 -0
  40. data/db/migrate/20141107124012_add_columns_to_user.rb +46 -0
  41. data/lib/ddr-core.rb +1 -0
  42. data/lib/ddr/auth.rb +80 -0
  43. data/lib/ddr/auth/ability.rb +18 -0
  44. data/lib/ddr/auth/ability_definitions.rb +26 -0
  45. data/lib/ddr/auth/ability_definitions/admin_set_ability_definitions.rb +9 -0
  46. data/lib/ddr/auth/ability_definitions/alias_ability_definitions.rb +23 -0
  47. data/lib/ddr/auth/ability_definitions/attachment_ability_definitions.rb +13 -0
  48. data/lib/ddr/auth/ability_definitions/collection_ability_definitions.rb +28 -0
  49. data/lib/ddr/auth/ability_definitions/component_ability_definitions.rb +13 -0
  50. data/lib/ddr/auth/ability_definitions/item_ability_definitions.rb +13 -0
  51. data/lib/ddr/auth/ability_definitions/lock_ability_definitions.rb +13 -0
  52. data/lib/ddr/auth/ability_definitions/publication_ability_definitions.rb +16 -0
  53. data/lib/ddr/auth/ability_definitions/role_based_ability_definitions.rb +39 -0
  54. data/lib/ddr/auth/ability_definitions/superuser_ability_definitions.rb +9 -0
  55. data/lib/ddr/auth/ability_factory.rb +10 -0
  56. data/lib/ddr/auth/abstract_ability.rb +48 -0
  57. data/lib/ddr/auth/affiliation.rb +14 -0
  58. data/lib/ddr/auth/affiliation_groups.rb +20 -0
  59. data/lib/ddr/auth/anonymous_ability.rb +7 -0
  60. data/lib/ddr/auth/auth_context.rb +109 -0
  61. data/lib/ddr/auth/auth_context_factory.rb +13 -0
  62. data/lib/ddr/auth/detached_auth_context.rb +19 -0
  63. data/lib/ddr/auth/dynamic_groups.rb +13 -0
  64. data/lib/ddr/auth/effective_permissions.rb +12 -0
  65. data/lib/ddr/auth/effective_roles.rb +9 -0
  66. data/lib/ddr/auth/failure_app.rb +16 -0
  67. data/lib/ddr/auth/group.rb +40 -0
  68. data/lib/ddr/auth/grouper_gateway.rb +70 -0
  69. data/lib/ddr/auth/groups.rb +32 -0
  70. data/lib/ddr/auth/ldap_gateway.rb +74 -0
  71. data/lib/ddr/auth/permissions.rb +18 -0
  72. data/lib/ddr/auth/remote_groups.rb +14 -0
  73. data/lib/ddr/auth/role_based_access_controls_enforcement.rb +56 -0
  74. data/lib/ddr/auth/roles.rb +28 -0
  75. data/lib/ddr/auth/roles/role.rb +121 -0
  76. data/lib/ddr/auth/roles/role_type.rb +23 -0
  77. data/lib/ddr/auth/roles/role_types.rb +52 -0
  78. data/lib/ddr/auth/superuser_ability.rb +7 -0
  79. data/lib/ddr/auth/test_helpers.rb +22 -0
  80. data/lib/ddr/auth/user.rb +54 -0
  81. data/lib/ddr/auth/web_auth_context.rb +29 -0
  82. data/lib/ddr/core.rb +110 -0
  83. data/lib/ddr/core/engine.rb +8 -0
  84. data/lib/ddr/core/version.rb +5 -0
  85. data/lib/ddr/error.rb +16 -0
  86. data/lib/ddr/files.rb +13 -0
  87. data/lib/ddr/fits.rb +189 -0
  88. data/lib/ddr/index.rb +29 -0
  89. data/lib/ddr/index/abstract_query_result.rb +22 -0
  90. data/lib/ddr/index/connection.rb +38 -0
  91. data/lib/ddr/index/csv_query_result.rb +84 -0
  92. data/lib/ddr/index/document_builder.rb +9 -0
  93. data/lib/ddr/index/field.rb +35 -0
  94. data/lib/ddr/index/field_attribute.rb +22 -0
  95. data/lib/ddr/index/fields.rb +154 -0
  96. data/lib/ddr/index/filter.rb +139 -0
  97. data/lib/ddr/index/query.rb +82 -0
  98. data/lib/ddr/index/query_builder.rb +185 -0
  99. data/lib/ddr/index/query_clause.rb +112 -0
  100. data/lib/ddr/index/query_params.rb +40 -0
  101. data/lib/ddr/index/query_result.rb +102 -0
  102. data/lib/ddr/index/response.rb +30 -0
  103. data/lib/ddr/index/sort_order.rb +28 -0
  104. data/lib/ddr/index/unique_key_field.rb +12 -0
  105. data/lib/ddr/managers.rb +9 -0
  106. data/lib/ddr/managers/manager.rb +13 -0
  107. data/lib/ddr/managers/technical_metadata_manager.rb +141 -0
  108. data/lib/ddr/structure.rb +188 -0
  109. data/lib/ddr/structures/agent.rb +49 -0
  110. data/lib/ddr/structures/component_type_term.rb +29 -0
  111. data/lib/ddr/structures/div.rb +64 -0
  112. data/lib/ddr/structures/f_locat.rb +54 -0
  113. data/lib/ddr/structures/file.rb +52 -0
  114. data/lib/ddr/structures/file_grp.rb +35 -0
  115. data/lib/ddr/structures/file_sec.rb +22 -0
  116. data/lib/ddr/structures/fptr.rb +31 -0
  117. data/lib/ddr/structures/mets_hdr.rb +37 -0
  118. data/lib/ddr/structures/mptr.rb +49 -0
  119. data/lib/ddr/structures/struct_map.rb +40 -0
  120. data/lib/ddr/utils.rb +185 -0
  121. data/lib/ddr/vocab.rb +22 -0
  122. data/lib/ddr/vocab/asset.rb +51 -0
  123. data/lib/ddr/vocab/contact.rb +9 -0
  124. data/lib/ddr/vocab/display.rb +9 -0
  125. data/lib/ddr/vocab/duke_terms.rb +13 -0
  126. data/lib/ddr/vocab/rdf_vocabulary_parser.rb +43 -0
  127. data/lib/ddr/vocab/roles.rb +25 -0
  128. data/lib/ddr/vocab/sources/duketerms.rdf +870 -0
  129. data/lib/ddr/vocab/vocabulary.rb +37 -0
  130. data/lib/ddr/workflow.rb +8 -0
  131. data/lib/tasks/ddr/core_tasks.rake +4 -0
  132. metadata +428 -0
@@ -0,0 +1,30 @@
1
+ require "delegate"
2
+
3
+ module Ddr::Index
4
+ #
5
+ # Wraps an index query response
6
+ #
7
+ class Response < SimpleDelegator
8
+
9
+ def docs
10
+ response["docs"]
11
+ end
12
+
13
+ def num_found
14
+ response["numFound"].to_i
15
+ end
16
+
17
+ def facet_counts
18
+ self["facet_counts"] || {}
19
+ end
20
+
21
+ def facet_fields
22
+ facet_counts["facet_fields"] || {}
23
+ end
24
+
25
+ def response
26
+ self["response"]
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,28 @@
1
+ require "virtus"
2
+
3
+ module Ddr::Index
4
+ class SortOrder
5
+ include Virtus.value_object
6
+
7
+ ASC = "asc"
8
+ DESC = "desc"
9
+
10
+ values do
11
+ attribute :field, FieldAttribute
12
+ attribute :order, String
13
+ end
14
+
15
+ def to_s
16
+ [ field, order ].join(" ")
17
+ end
18
+
19
+ def self.asc(field)
20
+ new(field: field, order: ASC)
21
+ end
22
+
23
+ def self.desc(field)
24
+ new(field: field, order: DESC)
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,12 @@
1
+ require 'singleton'
2
+
3
+ module Ddr::Index
4
+ class UniqueKeyField < Field
5
+ include Singleton
6
+
7
+ def initialize
8
+ super Ddr::SOLR_DOCUMENT_ID
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ module Ddr
2
+ module Managers
3
+ extend ActiveSupport::Autoload
4
+
5
+ autoload :Manager
6
+ autoload :TechnicalMetadataManager
7
+
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module Ddr
2
+ module Managers
3
+ # @abstract
4
+ class Manager
5
+ attr_reader :object
6
+
7
+ def initialize(object)
8
+ @object = object
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,141 @@
1
+ module Ddr
2
+ module Managers
3
+ class TechnicalMetadataManager < Manager
4
+
5
+ FITS_TIMESTAMP_FORMAT = "%D %l:%M %p" # Ex. 7/3/15 8:29 PM
6
+
7
+ delegate :content, to: :object
8
+
9
+ delegate :color_space,
10
+ :created,
11
+ :creating_application,
12
+ :extent,
13
+ :format_label,
14
+ :format_version,
15
+ :icc_profile_name,
16
+ :icc_profile_version,
17
+ :image_height,
18
+ :image_width,
19
+ :media_type,
20
+ :modified,
21
+ :pronom_identifier,
22
+ :valid,
23
+ :well_formed,
24
+ to: :fits
25
+
26
+ alias_method :last_modified, :modified
27
+
28
+ def fits
29
+ if object.fits_file.present?
30
+ Ddr::Fits.new(Nokogiri::XML(object.fits_file.file))
31
+ else
32
+ Ddr::Fits.new(Ddr::Fits.xml_template)
33
+ end
34
+ end
35
+
36
+ def fits?
37
+ !fits_version.nil?
38
+ end
39
+
40
+ def fits_version
41
+ fits.version.first
42
+ end
43
+
44
+ def fits_datetime
45
+ if fits_timestamp = fits.timestamp.first
46
+ # no_zone = DateTime.strptime(fits_timestamp, FITS_TIMESTAMP_FORMAT)
47
+ # no_zone.change(offset: no_zone.to_time.zone)
48
+ Time.zone.strptime(fits_timestamp, FITS_TIMESTAMP_FORMAT)
49
+ end
50
+ end
51
+
52
+ def file_size
53
+ extent.map(&:to_i)
54
+ end
55
+
56
+ def file_human_size
57
+ file_size.map do |fs|
58
+ "%s (%s bytes)" % [ ActiveSupport::NumberHelper.number_to_human_size(fs),
59
+ ActiveSupport::NumberHelper.number_to_delimited(fs) ]
60
+ end
61
+ end
62
+
63
+ def md5
64
+ fits.md5.first
65
+ end
66
+
67
+ # Ddr::File currently implements the 'digest' attribute as a set (array) of
68
+ # Ddr::Digest objects. This method (and the 'checksum_value' method that
69
+ # follows) assumes there is only one checksum associated with a file (as was
70
+ # the case in ddr-models). The current implementation simply picks the first
71
+ # checksum, unless/until we decide on a different approach.
72
+ def checksum_digest
73
+ content&.digest&.first&.type
74
+ end
75
+
76
+ def checksum_value
77
+ content&.digest&.first&.value
78
+ end
79
+
80
+ def invalid?
81
+ valid.any? { |v| v == "false" }
82
+ end
83
+
84
+ def valid?
85
+ !invalid?
86
+ end
87
+
88
+ def ill_formed?
89
+ well_formed.any? { |v| v == "false" }
90
+ end
91
+
92
+ def well_formed?
93
+ !ill_formed?
94
+ end
95
+
96
+ def creation_time
97
+ created.map { |datestr| coerce_to_time(datestr) }.compact
98
+ end
99
+
100
+ def modification_time
101
+ modified.map { |datestr| coerce_to_time(datestr) }.compact
102
+ end
103
+
104
+ ### DDRevo #######################################################
105
+ # Holding off on this until we address indexing more generally,
106
+ # perhaps in the context of the ddr-admin staff app.
107
+ ### DDRevo #######################################################
108
+ # def index_fields
109
+ # { Ddr::Index::Fields::TECHMD_FITS_VERSION => fits_version,
110
+ # Ddr::Index::Fields::TECHMD_FITS_DATETIME => Ddr::Utils.solr_date(fits_datetime),
111
+ # Ddr::Index::Fields::TECHMD_CREATION_TIME => Ddr::Utils.solr_dates(creation_time),
112
+ # Ddr::Index::Fields::TECHMD_MODIFICATION_TIME => Ddr::Utils.solr_dates(modification_time),
113
+ # Ddr::Index::Fields::TECHMD_COLOR_SPACE => color_space,
114
+ # Ddr::Index::Fields::TECHMD_CREATING_APPLICATION => creating_application,
115
+ # Ddr::Index::Fields::TECHMD_FILE_SIZE => file_size,
116
+ # Ddr::Index::Fields::TECHMD_FORMAT_LABEL => format_label,
117
+ # Ddr::Index::Fields::TECHMD_FORMAT_VERSION => format_version,
118
+ # Ddr::Index::Fields::TECHMD_ICC_PROFILE_NAME => icc_profile_name,
119
+ # Ddr::Index::Fields::TECHMD_ICC_PROFILE_VERSION => icc_profile_version,
120
+ # Ddr::Index::Fields::TECHMD_IMAGE_HEIGHT => image_height,
121
+ # Ddr::Index::Fields::TECHMD_IMAGE_WIDTH => image_width,
122
+ # Ddr::Index::Fields::TECHMD_MD5 => md5,
123
+ # Ddr::Index::Fields::TECHMD_MEDIA_TYPE => media_type,
124
+ # Ddr::Index::Fields::TECHMD_PRONOM_IDENTIFIER => pronom_identifier,
125
+ # Ddr::Index::Fields::TECHMD_VALID => valid,
126
+ # Ddr::Index::Fields::TECHMD_WELL_FORMED => well_formed,
127
+ # }
128
+ # end
129
+
130
+ private
131
+
132
+ def coerce_to_time(datestr)
133
+ datestr.sub! /\A(\d+):(\d+):(\d+)/, '\1-\2-\3'
134
+ DateTime.parse(datestr).to_time
135
+ rescue ArgumentError
136
+ nil
137
+ end
138
+
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,188 @@
1
+ module Ddr
2
+ #
3
+ # Wraps a Nokogiri (XML) 'mets' Document
4
+ #
5
+ class Structure < SimpleDelegator
6
+
7
+ # Indicates whether the structure is externally provided or maintained by the repository itself (i.e., is the
8
+ # default structure for the object).
9
+ EXTERNALLY_PROVIDED = 'provided'.freeze
10
+ REPOSITORY_MAINTAINED = 'repository'.freeze
11
+
12
+ TYPE_DEFAULT = 'default'.freeze
13
+
14
+ # Based on the PCDM Extension 'Use' ontology -- https://github.com/duraspace/pcdm/blob/master/pcdm-ext/use.rdf
15
+ USE_EXTRACTED_TEXT = 'ExtractedText'.freeze
16
+ USE_INTERMEDIATE_FILE = 'IntermediateFile'.freeze
17
+ USE_ORIGINAL_FILE = 'OriginalFile'.freeze
18
+ USE_PRESERVATION_MASTER_FILE = 'PreservationMasterFile'.freeze
19
+ USE_SERVICE_FILE = 'ServiceFile'.freeze
20
+ USE_THUMBNAIL_IMAGE = 'ThumbnailImage'.freeze
21
+ USE_TRANSCRIPT = 'Transcript'.freeze
22
+
23
+ def filesec
24
+ @filesec ||= Ddr::Structures::FileSec.new(fileSec_node)
25
+ end
26
+
27
+ def files
28
+ @files ||= collect_files
29
+ end
30
+
31
+ def uses
32
+ @uses ||= collect_uses
33
+ end
34
+
35
+ def structmap(type=nil)
36
+ sm = type ? structMap_node(type) : structMap_nodes.first
37
+ @structmap ||= Ddr::Structures::StructMap.new(sm)
38
+ end
39
+
40
+ def structmaps
41
+ @structmaps ||= structMap_nodes.map { |sm| Ddr::Structures::StructMap.new(sm) }
42
+ end
43
+
44
+ def metshdr
45
+ @metshdr ||= Ddr::Structures::MetsHdr.new(metsHdr_node)
46
+ end
47
+
48
+ def creator
49
+ @creator ||= metshdr.empty? ? nil
50
+ : Ddr::Structures::MetsHdr.new(metsHdr_node).agents.first.name
51
+ end
52
+
53
+ def repository_maintained?
54
+ creator == Ddr::Structures::Agent::NAME_REPOSITORY_DEFAULT
55
+ end
56
+
57
+ def dereferenced_structure
58
+ deref_struct = {}
59
+ structmaps.each do |sm|
60
+ deref_struct[sm.type] = sm.dereferenced_hash
61
+ end
62
+ deref_struct
63
+ end
64
+
65
+ def as_xml_document
66
+ __getobj__
67
+ end
68
+
69
+ def add_metshdr(id:nil, createdate:nil, lastmoddate:nil, recordstatus:nil)
70
+ metshdr = Ddr::Structures::MetsHdr.build(id: id, createdate: createdate, lastmoddate: lastmoddate,
71
+ recordstatus: recordstatus, document: as_xml_document)
72
+ root.add_child(metshdr)
73
+ metshdr
74
+ end
75
+
76
+ def add_agent(parent:, id:nil, role:, otherrole:nil, type:nil, othertype:nil, name:nil)
77
+ agent = Ddr::Structures::Agent.build(id: id, role: role, otherrole: otherrole, type: type,
78
+ othertype: othertype, name: name, document: as_xml_document)
79
+ parent.add_child(agent)
80
+ agent
81
+ end
82
+
83
+ def add_filesec(id:nil)
84
+ filesec = Ddr::Structures::FileSec.build(id: id, document: as_xml_document)
85
+ root.add_child(filesec)
86
+ filesec
87
+ end
88
+
89
+ def add_filegrp(parent:, id:nil, use:nil)
90
+ filegrp = Ddr::Structures::FileGrp.build(id: id, use: use, document: as_xml_document)
91
+ parent.add_child(filegrp)
92
+ filegrp
93
+ end
94
+
95
+ def add_file(parent:, id:SecureRandom.uuid, use:nil)
96
+ file = Ddr::Structures::File.build(id: id, use: use, document: as_xml_document)
97
+ parent.add_child(file)
98
+ file
99
+ end
100
+
101
+ def add_flocat(parent:, id:nil, loctype:'ARK', otherloctype: nil, use:nil, href:)
102
+ flocat = Ddr::Structures::FLocat.build(id: id, loctype: loctype, otherloctype: otherloctype, use: use,
103
+ href: href, document: as_xml_document)
104
+ parent.add_child(flocat)
105
+ flocat
106
+ end
107
+
108
+ def add_structmap(id:nil, label:nil, type:)
109
+ structmap = Ddr::Structures::StructMap.build(id: id, label: label, type: type, document: as_xml_document)
110
+ root.add_child(structmap)
111
+ structmap
112
+ end
113
+
114
+ def add_div(parent:, id:nil, label:nil, order:nil, orderlabel: nil, type:nil)
115
+ div = Ddr::Structures::Div.build(id: id, label: label, order:order, orderlabel: orderlabel, type: type,
116
+ document: as_xml_document)
117
+ parent.add_child(div)
118
+ div
119
+ end
120
+
121
+ def add_fptr(parent:, id: nil, fileid:)
122
+ fptr = Ddr::Structures::Fptr.build(id: id, fileid: fileid, document: as_xml_document)
123
+ parent.add_child(fptr)
124
+ fptr
125
+ end
126
+
127
+ def add_mptr(parent:, id: nil, loctype:'ARK', otherloctype: nil, href:)
128
+ mptr = Ddr::Structures::Mptr.build(id: id, loctype: loctype, otherloctype: otherloctype, href: href,
129
+ document: as_xml_document)
130
+ parent.add_child(mptr)
131
+ mptr
132
+ end
133
+
134
+ private
135
+
136
+ def fileSec_node
137
+ xpath("//xmlns:fileSec").first
138
+ end
139
+
140
+ def structMap_nodes
141
+ xpath("//xmlns:structMap")
142
+ end
143
+
144
+ def structMap_node(type)
145
+ xpath("//xmlns:structMap[@TYPE='#{type}']").first
146
+ end
147
+
148
+ def metsHdr_node
149
+ xpath("//xmlns:metsHdr")
150
+ end
151
+
152
+ def file_nodes
153
+ xpath("//xmlns:file")
154
+ end
155
+
156
+ def flocat_nodes
157
+ xpath("//xmlns:FLocat")
158
+ end
159
+
160
+ def collect_files
161
+ files = {}
162
+ file_nodes.each do |file_node|
163
+ file = Ddr::Structures::File.new(file_node)
164
+ files[file.id] = file
165
+ end
166
+ files
167
+ end
168
+
169
+ def collect_uses
170
+ uses = {}
171
+ flocat_nodes.each do |flocat_node|
172
+ flocat = Ddr::Structures::FLocat.new(flocat_node)
173
+ uses[flocat.effective_use] ||= []
174
+ uses[flocat.effective_use] << flocat
175
+ end
176
+ uses
177
+ end
178
+
179
+ def self.xml_template
180
+ Nokogiri::XML(
181
+ '<mets xmlns="http://www.loc.gov/METS/" xmlns:xlink="http://www.w3.org/1999/xlink" />'
182
+ ) do |config|
183
+ config.noblanks
184
+ end
185
+ end
186
+
187
+ end
188
+ end
@@ -0,0 +1,49 @@
1
+ module Ddr::Structures
2
+ #
3
+ # Wraps a Nokogiri (XML) 'agent' node
4
+ #
5
+ class Agent < SimpleDelegator
6
+
7
+ ROLE_CREATOR = 'CREATOR'.freeze
8
+
9
+ NAME_REPOSITORY_DEFAULT = 'REPOSITORY DEFAULT'.freeze
10
+
11
+ def id
12
+ self['ID']
13
+ end
14
+
15
+ def role
16
+ self['ROLE']
17
+ end
18
+
19
+ def otherrole
20
+ self['OTHERROLE']
21
+ end
22
+
23
+ def type
24
+ self['TYPE']
25
+ end
26
+
27
+ def othertype
28
+ self['OTHERTYPE']
29
+ end
30
+
31
+ def name
32
+ xpath('xmlns:name').first.content
33
+ end
34
+
35
+ def self.build(args)
36
+ node = Nokogiri::XML::Node.new('agent', args[:document])
37
+ node['ID'] = args[:id] if args[:id]
38
+ node['ROLE'] = args[:role] if args[:role]
39
+ node['OTHERROLE'] = args[:otherrole] if args[:otherrole]
40
+ node['TYPE'] = args[:type] if args[:type]
41
+ node['OTHERTYPE'] = args[:othertype] if args[:othertype]
42
+ name_node = Nokogiri::XML::Node.new('name', args[:document])
43
+ name_node.content = args[:name]
44
+ node.add_child(name_node)
45
+ node
46
+ end
47
+
48
+ end
49
+ end