cocina-models 0.84.4 → 0.85.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ec3a00a348ca9a851ee3455b7b0208628ca9590556ad76b0f536c4da3e1b6079
4
- data.tar.gz: 156105d8055745c353db2690e2c39b72a043243272fddc72fb80e5e9a42b81c4
3
+ metadata.gz: f3e8bdc15327c32a4d90a32780285048b947d799816e30ce9c47feb328f7deaf
4
+ data.tar.gz: d3a3b4e03410cad650555b2a085dd85e429029465d0ddf5e950a2c9231e27d73
5
5
  SHA512:
6
- metadata.gz: ff595ddde9a4729ed1e77239d78b7d1729798708bb44a407a6019d98943b50d8f14a3ad12fa06f17085294d499a32b82ece50f7ca6f23a30ce857bcd12a2de87
7
- data.tar.gz: 3bdb368b93e0ba0fc8f865cbb2888045589c2169af810f3380f18e2c8511fafea30fc088980a4503f6f0405a1b60daf575c1df07ac7463830058e9517827d97a
6
+ metadata.gz: 18af72ea7b0ff5ce24800694b2a29d519d975c05e99d6124660fce6a78f66f274513914ee7763ef05d60bb1beac194aa6d77db610879f829b827dfccf88ef0cd
7
+ data.tar.gz: cb1837cdb6f1b78640ef91c8efae91e0dbe9d872525bf87beb9a5b345e59fff3156f6c7d9fdb98e9e713964cfc98094ae373c897fffb2e4469faf6bde37e8a6b
data/.rubocop.yml CHANGED
@@ -6,8 +6,7 @@ require:
6
6
  - rubocop-rake
7
7
 
8
8
  AllCops:
9
- # dor-services-app is stuck at ruby 2 until we get off Fedora3
10
- TargetRubyVersion: 2.7
9
+ TargetRubyVersion: 3.0
11
10
 
12
11
  # ----- Layout ------
13
12
 
@@ -336,3 +335,10 @@ RSpec/Capybara/SpecificMatcher: # new in 2.12
336
335
  Enabled: false
337
336
  RSpec/Rails/HaveHttpStatus: # new in 2.12
338
337
  Enabled: false
338
+
339
+ RSpec/ClassCheck: # new in 2.13
340
+ Enabled: true
341
+ RSpec/NoExpectationExample: # new in 2.13
342
+ Enabled: true
343
+ RSpec/Capybara/SpecificFinders: # new in 2.13
344
+ Enabled: true
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cocina-models (0.84.4)
4
+ cocina-models (0.85.0)
5
5
  activesupport
6
6
  deprecation
7
7
  dry-struct (~> 1.0)
@@ -20,7 +20,7 @@ PATH
20
20
  GEM
21
21
  remote: https://rubygems.org/
22
22
  specs:
23
- activesupport (7.0.3.1)
23
+ activesupport (7.0.4)
24
24
  concurrent-ruby (~> 1.0, >= 1.0.2)
25
25
  i18n (>= 1.6, < 2)
26
26
  minitest (>= 5.1)
@@ -32,13 +32,13 @@ GEM
32
32
  json_schema (~> 0.14, >= 0.14.3)
33
33
  openapi_parser (>= 0.11.1, < 1.0)
34
34
  rack (>= 1.5)
35
- commonmarker (0.23.5)
35
+ commonmarker (0.23.6)
36
36
  concurrent-ruby (1.1.10)
37
37
  deprecation (1.1.0)
38
38
  activesupport
39
39
  diff-lcs (1.5.0)
40
40
  docile (1.4.0)
41
- dry-container (0.10.1)
41
+ dry-container (0.11.0)
42
42
  concurrent-ruby (~> 1.0)
43
43
  dry-core (0.8.1)
44
44
  concurrent-ruby (~> 1.0)
@@ -83,7 +83,7 @@ GEM
83
83
  patience_diff (1.2.0)
84
84
  optimist (~> 3.0)
85
85
  racc (1.6.0)
86
- rack (2.2.4)
86
+ rack (3.0.0)
87
87
  rainbow (3.1.1)
88
88
  rake (13.0.6)
89
89
  regexp_parser (2.5.0)
@@ -94,18 +94,18 @@ GEM
94
94
  rspec-mocks (~> 3.11.0)
95
95
  rspec-core (3.11.0)
96
96
  rspec-support (~> 3.11.0)
97
- rspec-expectations (3.11.0)
97
+ rspec-expectations (3.11.1)
98
98
  diff-lcs (>= 1.2.0, < 2.0)
99
99
  rspec-support (~> 3.11.0)
100
100
  rspec-mocks (3.11.1)
101
101
  diff-lcs (>= 1.2.0, < 2.0)
102
102
  rspec-support (~> 3.11.0)
103
- rspec-support (3.11.0)
103
+ rspec-support (3.11.1)
104
104
  rspec_junit_formatter (0.5.1)
105
105
  rspec-core (>= 2, < 4, != 2.12.0)
106
106
  rss (0.2.9)
107
107
  rexml
108
- rubocop (1.35.1)
108
+ rubocop (1.36.0)
109
109
  json (~> 2.3)
110
110
  parallel (~> 1.10)
111
111
  parser (>= 3.1.2.1)
@@ -119,8 +119,8 @@ GEM
119
119
  parser (>= 3.1.1.0)
120
120
  rubocop-rake (0.6.0)
121
121
  rubocop (~> 1.0)
122
- rubocop-rspec (2.12.1)
123
- rubocop (~> 1.31)
122
+ rubocop-rspec (2.13.2)
123
+ rubocop (~> 1.33)
124
124
  ruby-progressbar (1.11.0)
125
125
  simplecov (0.21.2)
126
126
  docile (~> 1.1)
@@ -135,7 +135,7 @@ GEM
135
135
  thor (1.2.1)
136
136
  tzinfo (2.0.5)
137
137
  concurrent-ruby (~> 1.0)
138
- unicode-display_width (2.2.0)
138
+ unicode-display_width (2.3.0)
139
139
  zeitwerk (2.6.0)
140
140
 
141
141
  PLATFORMS
data/README.md CHANGED
@@ -55,7 +55,8 @@ If there is a possibility that a model or validation change will conflict with s
55
55
 
56
56
  1. Create a cocina-models branch containing the proposed change and push to GitHub.
57
57
  2. On sdr-infra, check out `main`, update the `Gemfile` so that cocina-models references the branch, and `bundle install`.
58
- 3. Select the appropriate database.
58
+ 3. Select the appropriate environment vars below - they are set to values in puppet. (first 2 lines are the same; last two lines use different variables)
59
+
59
60
  For QA:
60
61
  ```
61
62
  export DATABASE_NAME="dor_services"
@@ -135,24 +136,34 @@ which pushes the gem to rubygems.org. Next write up the release notes: https://
135
136
 
136
137
  ### Step 2: Update client gems coupled to the models
137
138
 
138
- **NOTE**: You can skip this step if the new release is a patch-level bump only.
139
-
140
- Next, you should release versions of [sdr-client](https://github.com/sul-dlss/sdr-client) and [dor-services-client](https://github.com/sul-dlss/dor-services-client/) pinned to this version because applications such as [Argo](https://github.com/sul-dlss/argo) depend on both of these gems using the same models.
139
+ **NOTE**: You may skip this step if the new release is a patch-level bump only, as the client gems are pinned to a minor release of cocina-models. However, a PR to update Gemfile.lock with the new cocina-models version is welcome ... and not a blocker.
141
140
 
142
- ### Step 3: Update service API specifications and gems
141
+ If this is a minor or major cocina-models version change, release new versions of [sdr-client](https://github.com/sul-dlss/sdr-client) and [dor-services-client](https://github.com/sul-dlss/dor-services-client/) pinned to use the new cocina-models version because applications such as [Argo](https://github.com/sul-dlss/argo) depend on both of these gems using the same models.
143
142
 
144
- **NOTE**: You can skip this first half of the step if there have not been any changes to the `cocina-models` OpenAPI spec since the prior release.
143
+ ### Step 3: Update services directly coupled to the models
145
144
 
146
- The cocina-models gem is used in applications that have an API specification that accepts Cocina models. Next, make sure that the `openapi.yml` for these applications include the `openapi.yml` schema changes made in cocina-models. This list of services is known to include:
145
+ This list of services is known to include:
147
146
 
148
147
  * [sul-dlss/sdr-api](https://github.com/sul-dlss/sdr-api)
149
148
  * [sul-dlss/dor-services-app](https://github.com/sul-dlss/dor-services-app/)
150
149
 
151
- This can be accomplished by copying and pasting these schemas. By convention, these schemas are listed first in the `openapi.yml` of the associated projects, followed by the application-specific schemas.
150
+ **NOTE**: You can skip step 3A if there have not been any changes to the `cocina-models` OpenAPI spec since the prior release.
151
+
152
+ #### Step 3A: Update API specifications
153
+
154
+ The cocina-models gem is used in applications that have an API specification that accepts Cocina models. Make sure that the `openapi.yml` for these applications include the `openapi.yml` schema changes made in cocina-models.
155
+
156
+ This can be accomplished by copying and pasting the cocina-models schemas to the openapi.yml of the associated project. By convention, these schemas are listed first in the `openapi.yml` of the associated projects, followed by the application-specific schemas.
157
+
158
+ #### Step 3B: Bump gems and create the PRs
159
+
160
+ If step 3A was needed, use the same PRs to also bump the versions of cocina-models, sdr-client, and dor-services-client in these applications/services. Why? When [dor-services-app](https://github.com/sul-dlss/dor-services-app), for example, is updated to use the new models (via the auto-update script), these clients should be updated at the same time or there is risk of models produced by dor-services-app not being acceptable to the clients.
161
+
162
+ With or without step 3A, perform `bundle update` for cocina-models, sdr-client, and dor-services-client gems in the listed services and then make PRs for those repos.
152
163
 
153
- #### Step 3b: Bump gems
164
+ #### Step 3C: Merge 'em
154
165
 
155
- At the same, we have found it convenient to use these PRs to also bump the versions of cocina-models, sdr-client, and dor-services-client in these applications/services. Why? When [dor-services-app](https://github.com/sul-dlss/dor-services-app), for example, is updated to use the new models (via the auto-update script), these clients should be updated at the same time or there is risk of models produced by dor-services-app not being acceptable to the clients.
166
+ Get the directly coupled services PRs merged before the deploy in step 5.
156
167
 
157
168
  ### Step 4: Update other dependent applications
158
169
 
@@ -160,19 +171,19 @@ Once the above listed steps have been completed, all applications that use cocin
160
171
 
161
172
  There are scripts to help with updating other dependent applications:
162
173
 
163
- #### Step 4A: Create the PRs
174
+ #### Step 4A: Create the Cocina Level 2 PRs
164
175
 
165
176
  There is a Jenkins CI job that you can run manually to create all the PRs you need. Head to https://sul-ci-prod.stanford.edu/job/SUL-DLSS/job/access-update-scripts/job/cocina-level2-updates/ and then click `Build Now`. Click the new build that is created and then `Console Output` to watch the build. Once it has completed, you can proceed with the next step.
166
177
 
167
178
  If for some reason the above method does not work, the sul-dlss/access-update-scripts repo has a script for this: `cocina_level2_prs.rb`. You will need a github access token with scopes of "read:org" and "repo" (see https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) to run it, as noted in the comments at the top of that script.
168
179
 
169
- #### Step 4B: Merge the PRs
180
+ #### Step 4B: Merge the Cocina Level 2 PRs
170
181
 
171
182
  [sul-dlss/access-update-scripts](https://github.com/sul-dlss/access-update-scripts) has a switch in the `merge-all.rb` script for this, as noted in the comments at the top of that script. (`REPOS_PATH=infrastructure GH_ACCESS_TOKEN=abc123 COCINA_LEVEL2= ./merge-all.rb`)
172
183
 
173
184
  ### Step 5: Deploy all affected applications together
174
185
 
175
- [sul-dlss/sdr-deploy](https://github.com/sul-dlss/sdr-deploy) has a flag in the deploy script to limit deploys to cocina dependent applications. Refer to instructions in the [sdr-deploy/README](https://github.com/sul-dlss/sdr-deploy/blob/main/README.md#only-deploy-repos-related-to-cocina-models-update).
186
+ [sul-dlss/sdr-deploy](https://github.com/sul-dlss/sdr-deploy) has a flag (-c) in the deploy script to limit deploys to cocina dependent applications. Refer to instructions in the [sdr-deploy/README](https://github.com/sul-dlss/sdr-deploy/blob/main/README.md#only-deploy-repos-related-to-cocina-models-update).
176
187
 
177
188
  Note that running the integration tests is currently the best way we have to check for unintended effects and/or bugs when rolling out cocina-models changes.
178
189
 
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.bindir = 'exe'
23
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
24
  spec.require_paths = ['lib']
25
- spec.required_ruby_version = '>= 2.7'
25
+ spec.required_ruby_version = '>= 3.0'
26
26
 
27
27
  spec.add_dependency 'activesupport'
28
28
  spec.add_dependency 'deprecation'
@@ -14,7 +14,6 @@ module Cocina
14
14
  attribute :note, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
15
15
  # URL or other pointer to the location of the contributor information.
16
16
  attribute? :valueAt, Types::Strict::String
17
- attribute :parallelContributor, Types::Strict::Array.of(DescriptiveParallelContributor).default([].freeze)
18
17
  end
19
18
  end
20
19
  end
@@ -20,7 +20,7 @@ module Cocina
20
20
  attribute? :adminMetadata, DescriptiveAdminMetadata.optional
21
21
  # URL or other pointer to the location of the resource description.
22
22
  attribute? :valueAt, Types::Strict::String
23
- # Stanford persistent URL associated with the related resource. Note this is http, not https.
23
+ # Stanford persistent URL associated with the related resource.
24
24
  attribute :purl, Types::Strict::String
25
25
  end
26
26
  end
@@ -34,7 +34,7 @@ module Cocina
34
34
  values.concat(extent_values.flatten)
35
35
  values.concat(part_note_value_for(part_element, 'text'))
36
36
  values.concat(part_note_value_for(part_element, 'date'))
37
- values.reject!(&:blank?)
37
+ values.compact_blank!
38
38
 
39
39
  return if values.empty?
40
40
 
@@ -50,7 +50,7 @@ module Cocina
50
50
  values.concat(extent_values)
51
51
  values.concat(part_note_value_for(part_element, 'text'))
52
52
  values.concat(part_note_value_for(part_element, 'date'))
53
- values.reject!(&:blank?)
53
+ values.compact_blank!
54
54
 
55
55
  return if values.empty?
56
56
 
@@ -83,7 +83,7 @@ module Cocina
83
83
  detail_values.concat(part_note_value_for(detail_node, 'number'))
84
84
  detail_values.concat(part_note_value_for(detail_node, 'caption'))
85
85
  detail_values.concat(part_note_value_for(detail_node, 'title'))
86
- detail_values.reject!(&:blank?)
86
+ detail_values.compact_blank!
87
87
  if detail_values.present?
88
88
  detail_values.concat(part_note_value_for(detail_node, 'detail type',
89
89
  xpath: '@type'))
@@ -101,7 +101,7 @@ module Cocina
101
101
  extent_values = []
102
102
  extent_values.concat(part_note_value_for(extent_node, 'list'))
103
103
  extent_values << pages_for(extent_node)
104
- extent_values.reject!(&:blank?)
104
+ extent_values.compact_blank!
105
105
  if extent_values.present?
106
106
  extent_values.concat(part_note_value_for(extent_node, 'extent unit',
107
107
  xpath: '@unit'))
@@ -6,9 +6,9 @@ module Cocina
6
6
  module ToMods
7
7
  # Maps geo extension from cocina to MODS
8
8
  class Geographic # rubocop:disable Metrics/ClassLength
9
- TYPE_REGEX = /^type$/.freeze
10
- MEDIA_REGEX = /^media type$/.freeze
11
- DATA_FORMAT_REGEX = /^data format$/.freeze
9
+ TYPE_REGEX = /^type$/
10
+ MEDIA_REGEX = /^media type$/
11
+ DATA_FORMAT_REGEX = /^data format$/
12
12
 
13
13
  ABOUT_URI_PREFIX = 'http://purl.stanford.edu/'
14
14
 
@@ -12,7 +12,7 @@ module Cocina
12
12
  'genre' => :genre,
13
13
  'occupation' => :occupation
14
14
  }.freeze
15
- DEORDINAL_REGEX = /(?<=[0-9])(?:st|nd|rd|th)/.freeze
15
+ DEORDINAL_REGEX = /(?<=[0-9])(?:st|nd|rd|th)/
16
16
 
17
17
  # @params [Nokogiri::XML::Builder] xml
18
18
  # @params [Array<Cocina::Models::DescriptiveValue>] subjects
@@ -19,7 +19,7 @@ module Cocina
19
19
  attribute :identifier, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
20
20
  attribute? :standard, Standard.optional
21
21
  attribute :subject, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
22
- # Stanford persistent URL associated with the related resource. Note this is http, not https.
22
+ # Stanford persistent URL associated with the related resource.
23
23
  attribute? :purl, Types::Strict::String
24
24
  attribute? :access, DescriptiveAccessMetadata.optional
25
25
  attribute :relatedResource, Types::Strict::Array.of(RelatedResource).default([].freeze)
@@ -76,8 +76,7 @@ module Cocina
76
76
  #
77
77
  # So we catch the false positives from the upstream gem and allow
78
78
  # this pattern to validate
79
- /\AY-?\d{5,}\Z/.match?(value) ||
80
- /\A-?\d{1,3}\Z/.match?(value) # temporarily allow format violations
79
+ /\AY-?\d{5,}\Z/.match?(value)
81
80
  end
82
81
 
83
82
  def valid_iso8601?(value)
@@ -71,7 +71,7 @@ module Cocina
71
71
  # Some part of the path are ignored for the purpose of matching.
72
72
  def clean_path(path)
73
73
  new_path = path.reject do |part|
74
- part.is_a?(Integer) || %i[parallelValue parallelContributor parallelEvent].include?(part)
74
+ part.is_a?(Integer) || %i[parallelValue parallelEvent].include?(part)
75
75
  end
76
76
  # This needs to happen after parallelValue is removed
77
77
  # to handle structuredValue > parallelValue > structuredValue
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Cocina
4
4
  module Models
5
- VERSION = '0.84.4'
5
+ VERSION = '0.85.0'
6
6
  end
7
7
  end
data/openapi.yml CHANGED
@@ -549,11 +549,6 @@ components:
549
549
  valueAt:
550
550
  description: URL or other pointer to the location of the contributor information.
551
551
  type: string
552
- parallelContributor:
553
- description: For multiple representations of information about the same contributor (e.g. in different languages).
554
- type: array
555
- items:
556
- $ref: "#/components/schemas/DescriptiveParallelContributor"
557
552
  ControlledDigitalLendingAccess:
558
553
  type: object
559
554
  properties:
@@ -777,44 +772,6 @@ components:
777
772
  type: array
778
773
  items:
779
774
  $ref: "#/components/schemas/DescriptiveValue"
780
- DescriptiveParallelContributor:
781
- description: Value model for multiple representations of information about the same contributor (e.g. in different languages).
782
- deprecated: true
783
- type: object
784
- additionalProperties: false
785
- properties:
786
- name:
787
- description: Names associated with a contributor.
788
- type: array
789
- items:
790
- $ref: "#/components/schemas/DescriptiveValue"
791
- type:
792
- description: Entity type of the contributor (person, organization, etc.). See https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md for valid types.
793
- type: string
794
- status:
795
- description: Status of the contributor relative to other parallel contributors (e.g. the primary author among a group of contributors).
796
- type: string
797
- role:
798
- description: Relationships of the contributor to the resource or to an event in its history.
799
- type: array
800
- items:
801
- $ref: "#/components/schemas/DescriptiveValue"
802
- identifier:
803
- description: Identifiers and URIs associated with the contributor entity.
804
- type: array
805
- items:
806
- $ref: "#/components/schemas/DescriptiveValue"
807
- note:
808
- description: Other information associated with the contributor.
809
- type: array
810
- items:
811
- $ref: "#/components/schemas/DescriptiveValue"
812
- valueAt:
813
- description: URL or other pointer to the location of the contributor information.
814
- type: string
815
- valueLanguage:
816
- # description: Language of the descriptive element value
817
- $ref: "#/components/schemas/DescriptiveValueLanguage"
818
775
  DescriptiveParallelEvent:
819
776
  description: Value model for multiple representations of information about the same event (e.g. in different languages).
820
777
  type: object
@@ -1459,7 +1416,7 @@ components:
1459
1416
  description: Width in pixels
1460
1417
  type: integer
1461
1418
  Purl:
1462
- description: Stanford persistent URL associated with the related resource. Note this is http, not https.
1419
+ description: Stanford persistent URL associated with the related resource.
1463
1420
  type: string
1464
1421
  format: uri
1465
1422
  # Canonical URI is https
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cocina-models
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.84.4
4
+ version: 0.85.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Coyne
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-09-01 00:00:00.000000000 Z
11
+ date: 2022-09-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -392,7 +392,6 @@ files:
392
392
  - lib/cocina/models/descriptive_basic_value.rb
393
393
  - lib/cocina/models/descriptive_geographic_metadata.rb
394
394
  - lib/cocina/models/descriptive_grouped_value.rb
395
- - lib/cocina/models/descriptive_parallel_contributor.rb
396
395
  - lib/cocina/models/descriptive_parallel_event.rb
397
396
  - lib/cocina/models/descriptive_parallel_value.rb
398
397
  - lib/cocina/models/descriptive_structured_value.rb
@@ -531,14 +530,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
531
530
  requirements:
532
531
  - - ">="
533
532
  - !ruby/object:Gem::Version
534
- version: '2.7'
533
+ version: '3.0'
535
534
  required_rubygems_version: !ruby/object:Gem::Requirement
536
535
  requirements:
537
536
  - - ">="
538
537
  - !ruby/object:Gem::Version
539
538
  version: '0'
540
539
  requirements: []
541
- rubygems_version: 3.3.7
540
+ rubygems_version: 3.2.32
542
541
  signing_key:
543
542
  specification_version: 4
544
543
  summary: Data models for the SDR
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Cocina
4
- module Models
5
- # DEPRECATED
6
- # Value model for multiple representations of information about the same contributor (e.g. in different languages).
7
- class DescriptiveParallelContributor < Struct
8
- attribute :name, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
9
- # Entity type of the contributor (person, organization, etc.). See https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md for valid types.
10
- attribute? :type, Types::Strict::String
11
- # Status of the contributor relative to other parallel contributors (e.g. the primary author among a group of contributors).
12
- attribute? :status, Types::Strict::String
13
- attribute :role, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
14
- attribute :identifier, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
15
- attribute :note, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
16
- # URL or other pointer to the location of the contributor information.
17
- attribute? :valueAt, Types::Strict::String
18
- attribute? :valueLanguage, DescriptiveValueLanguage.optional
19
- end
20
- end
21
- end