cocina-models 0.29.0 → 0.30.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 +4 -4
- data/.rubocop.yml +10 -3
- data/README.md +11 -0
- data/cocina-models.gemspec +5 -0
- data/exe/generator +9 -0
- data/lib/cocina/generator.rb +18 -0
- data/lib/cocina/generator/generator.rb +80 -0
- data/lib/cocina/generator/schema.rb +84 -0
- data/lib/cocina/generator/schema_array.rb +20 -0
- data/lib/cocina/generator/schema_base.rb +49 -0
- data/lib/cocina/generator/schema_ref.rb +16 -0
- data/lib/cocina/generator/schema_value.rb +60 -0
- data/lib/cocina/generator/vocab.rb +63 -0
- data/lib/cocina/models.rb +51 -25
- data/lib/cocina/models/access.rb +10 -0
- data/lib/cocina/models/admin_policy.rb +13 -56
- data/lib/cocina/models/admin_policy_administrative.rb +11 -0
- data/lib/cocina/models/administrative.rb +14 -0
- data/lib/cocina/models/catalog_link.rb +4 -1
- data/lib/cocina/models/collection.rb +21 -31
- data/lib/cocina/models/collection_identification.rb +9 -0
- data/lib/cocina/models/description.rb +1 -8
- data/lib/cocina/models/dro.rb +34 -70
- data/lib/cocina/models/dro_access.rb +16 -0
- data/lib/cocina/models/dro_structural.rb +14 -0
- data/lib/cocina/models/embargo.rb +16 -0
- data/lib/cocina/models/file.rb +20 -36
- data/lib/cocina/models/file_administrative.rb +10 -0
- data/lib/cocina/models/file_set.rb +8 -15
- data/lib/cocina/models/file_set_structural.rb +9 -0
- data/lib/cocina/models/geographic.rb +10 -0
- data/lib/cocina/models/identification.rb +11 -0
- data/lib/cocina/models/message_digest.rb +17 -0
- data/lib/cocina/models/presentation.rb +12 -0
- data/lib/cocina/models/release_tag.rb +12 -7
- data/lib/cocina/models/request_admin_policy.rb +15 -3
- data/lib/cocina/models/request_collection.rb +21 -4
- data/lib/cocina/models/request_dro.rb +32 -11
- data/lib/cocina/models/request_dro_structural.rb +13 -0
- data/lib/cocina/models/request_file.rb +15 -6
- data/lib/cocina/models/request_file_set.rb +7 -9
- data/lib/cocina/models/request_file_set_structural.rb +9 -0
- data/lib/cocina/models/sequence.rb +2 -5
- data/lib/cocina/models/title.rb +12 -0
- data/lib/cocina/models/validator.rb +22 -0
- data/lib/cocina/models/version.rb +1 -1
- data/lib/cocina/models/vocab.rb +45 -60
- data/openapi.yml +695 -0
- metadata +101 -9
- data/lib/cocina/models/admin_policy_attributes.rb +0 -21
- data/lib/cocina/models/collection_attributes.rb +0 -22
- data/lib/cocina/models/dro_attributes.rb +0 -22
- data/lib/cocina/models/file_attributes.rb +0 -25
- data/lib/cocina/models/file_set_attributes.rb +0 -16
- data/lib/cocina/models/types.rb +0 -10
data/lib/cocina/models.rb
CHANGED
@@ -5,23 +5,34 @@ require 'zeitwerk'
|
|
5
5
|
require 'dry-struct'
|
6
6
|
require 'dry-types'
|
7
7
|
require 'json'
|
8
|
+
require 'openapi_parser'
|
9
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
10
|
+
require 'yaml'
|
8
11
|
|
9
12
|
# Help Zeitwerk find some of our classes
|
10
13
|
class CocinaModelsInflector < Zeitwerk::Inflector
|
14
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
15
|
+
# rubocop:disable Metrics/MethodLength
|
11
16
|
def camelize(basename, _abspath)
|
12
17
|
case basename
|
13
18
|
when 'dro'
|
14
19
|
'DRO'
|
15
|
-
when 'dro_builder'
|
16
|
-
'DROBuilder'
|
17
20
|
when 'request_dro'
|
18
21
|
'RequestDRO'
|
22
|
+
when 'dro_access'
|
23
|
+
'DROAccess'
|
24
|
+
when 'dro_structural'
|
25
|
+
'DROStructural'
|
26
|
+
when 'request_dro_structural'
|
27
|
+
'RequestDROStructural'
|
19
28
|
when 'version'
|
20
29
|
'VERSION'
|
21
30
|
else
|
22
31
|
super
|
23
32
|
end
|
24
33
|
end
|
34
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
35
|
+
# rubocop:enable Metrics/MethodLength
|
25
36
|
end
|
26
37
|
|
27
38
|
loader = Zeitwerk::Loader.new
|
@@ -36,43 +47,58 @@ module Cocina
|
|
36
47
|
# Raised when the type attribute is not valid.
|
37
48
|
class UnknownTypeError < Error; end
|
38
49
|
|
50
|
+
# Raised when an error occurs validating against openapi.
|
51
|
+
class ValidationError < Error; end
|
52
|
+
|
39
53
|
# Base class for Cocina Structs
|
40
54
|
class Struct < Dry::Struct
|
41
55
|
transform_keys(&:to_sym)
|
56
|
+
schema schema.strict
|
57
|
+
end
|
58
|
+
|
59
|
+
# DRY Types
|
60
|
+
module Types
|
61
|
+
include Dry.Types()
|
42
62
|
end
|
43
63
|
|
44
64
|
# @param [Hash] dyn a ruby hash representation of the JSON serialization of a collection or DRO
|
45
|
-
# @
|
65
|
+
# @param [boolean] validate
|
66
|
+
# @return [DRO,Collection,AdminPolicy]
|
46
67
|
# @raises [UnknownTypeError] if a valid type is not found in the data
|
47
68
|
# @raises [KeyError] if a type field cannot be found in the data
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
69
|
+
# @raises [ValidationError] if hash representation fails openapi validation
|
70
|
+
def self.build(dyn, validate: true)
|
71
|
+
clazz = case dyn.fetch('type')
|
72
|
+
when *DRO::TYPES
|
73
|
+
DRO
|
74
|
+
when *Collection::TYPES
|
75
|
+
Collection
|
76
|
+
when *AdminPolicy::TYPES
|
77
|
+
AdminPolicy
|
78
|
+
else
|
79
|
+
raise UnknownTypeError, "Unknown type: '#{dyn.fetch('type')}'"
|
80
|
+
end
|
81
|
+
clazz.new(dyn, validate: validate)
|
59
82
|
end
|
60
83
|
|
61
84
|
# @param [Hash] dyn a ruby hash representation of the JSON serialization of a request for a Collection or DRO
|
85
|
+
# @param [boolean] validate
|
62
86
|
# @return [RequestDRO,RequestCollection,RequestAdminPolicy]
|
63
87
|
# @raises [UnknownTypeError] if a valid type is not found in the data
|
64
88
|
# @raises [KeyError] if a type field cannot be found in the data
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
89
|
+
# @raises [ValidationError] if hash representation fails openapi validation
|
90
|
+
def self.build_request(dyn, validate: true)
|
91
|
+
clazz = case dyn.fetch('type')
|
92
|
+
when *DRO::TYPES
|
93
|
+
RequestDRO
|
94
|
+
when *Collection::TYPES
|
95
|
+
RequestCollection
|
96
|
+
when *AdminPolicy::TYPES
|
97
|
+
RequestAdminPolicy
|
98
|
+
else
|
99
|
+
raise UnknownTypeError, "Unknown type: '#{dyn.fetch('type')}'"
|
100
|
+
end
|
101
|
+
clazz.new(dyn, validate: validate)
|
76
102
|
end
|
77
103
|
end
|
78
104
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cocina
|
4
|
+
module Models
|
5
|
+
class Access < Struct
|
6
|
+
# Access level
|
7
|
+
attribute :access, Types::Strict::String.default('dark').enum('world', 'stanford', 'location-based', 'citation-only', 'dark').meta(omittable: true)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -2,67 +2,24 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
-
# An admin policy object.
|
6
5
|
class AdminPolicy < Struct
|
7
6
|
include Checkable
|
8
7
|
|
9
|
-
TYPES = [
|
10
|
-
Vocab.admin_policy
|
11
|
-
].freeze
|
8
|
+
TYPES = ['http://cocina.sul.stanford.edu/models/admin_policy.jsonld'].freeze
|
12
9
|
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
# Subschema for administrative concerns
|
18
|
-
class Administrative < Struct
|
19
|
-
# This was copied from the ActiveFedora defaults: Dor::AdminPolicyObject.new.defaultObjectRights.content
|
20
|
-
DEFAULT_OBJECT_RIGHTS = <<~XML
|
21
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
22
|
-
|
23
|
-
<rightsMetadata>
|
24
|
-
<access type="discover">
|
25
|
-
<machine>
|
26
|
-
<world/>
|
27
|
-
</machine>
|
28
|
-
</access>
|
29
|
-
<access type="read">
|
30
|
-
<machine>
|
31
|
-
<world/>
|
32
|
-
</machine>
|
33
|
-
</access>
|
34
|
-
<use>
|
35
|
-
<human type="useAndReproduction"/>
|
36
|
-
<human type="creativeCommons"/>
|
37
|
-
<machine type="creativeCommons" uri=""/>
|
38
|
-
<human type="openDataCommons"/>
|
39
|
-
<machine type="openDataCommons" uri=""/>
|
40
|
-
</use>
|
41
|
-
<copyright>
|
42
|
-
<human/>
|
43
|
-
</copyright>
|
44
|
-
</rightsMetadata>
|
45
|
-
XML
|
46
|
-
|
47
|
-
# An XML blob that is to be used temporarily until we model rights
|
48
|
-
attribute :default_object_rights, Types::Strict::String.optional.default(DEFAULT_OBJECT_RIGHTS)
|
49
|
-
|
50
|
-
# which workflow to start when registering (used by Web Archive apos to start wasCrawlPreassemblyWF)
|
51
|
-
attribute :registration_workflow, Types::String.optional.default(nil)
|
52
|
-
|
53
|
-
# TODO: Allowing hasAdminPolicy to be omittable for now (until rolled out to consumers),
|
54
|
-
# but I think it's actually required for every Admin Policy
|
55
|
-
attribute :hasAdminPolicy, Types::Strict::String.optional.default(nil)
|
56
|
-
end
|
57
|
-
|
58
|
-
class Identification < Struct
|
59
|
-
end
|
60
|
-
|
61
|
-
class Structural < Struct
|
62
|
-
end
|
63
|
-
|
64
|
-
include AdminPolicyAttributes
|
10
|
+
# example: item
|
11
|
+
attribute :type, Types::Strict::String.enum(*AdminPolicy::TYPES)
|
12
|
+
# example: druid:bc123df4567
|
65
13
|
attribute :externalIdentifier, Types::Strict::String
|
14
|
+
attribute :label, Types::Strict::String
|
15
|
+
attribute :version, Types::Strict::Integer
|
16
|
+
attribute(:administrative, AdminPolicyAdministrative.default { AdminPolicyAdministrative.new })
|
17
|
+
attribute :description, Description.optional.meta(omittable: true)
|
18
|
+
|
19
|
+
def self.new(attributes = default_attributes, safe = false, validate = true, &block)
|
20
|
+
Validator.validate(self, attributes.with_indifferent_access) if validate
|
21
|
+
super(attributes, safe, &block)
|
22
|
+
end
|
66
23
|
end
|
67
24
|
end
|
68
25
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cocina
|
4
|
+
module Models
|
5
|
+
class AdminPolicyAdministrative < Struct
|
6
|
+
attribute :defaultObjectRights, Types::Strict::String.default('<?xml version="1.0" encoding="UTF-8"?><rightsMetadata><access type="discover"><machine><world/></machine></access><access type="read"><machine><world/></machine></access><use><human type="useAndReproduction"/><human type="creativeCommons"/><machine type="creativeCommons" uri=""/><human type="openDataCommons"/><machine type="openDataCommons" uri=""/></use><copyright><human/></copyright></rightsMetadata>').meta(omittable: true)
|
7
|
+
attribute :registrationWorkflow, Types::Strict::String.meta(omittable: true)
|
8
|
+
attribute :hasAdminPolicy, Types::Strict::String.meta(omittable: true)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cocina
|
4
|
+
module Models
|
5
|
+
class Administrative < Struct
|
6
|
+
# example: druid:bc123df4567
|
7
|
+
attribute :hasAdminPolicy, Types::Strict::String.meta(omittable: true)
|
8
|
+
attribute :releaseTags, Types::Strict::Array.of(ReleaseTag).meta(omittable: true)
|
9
|
+
# Administrative or Internal project this resource is a part of
|
10
|
+
# example: Google Books
|
11
|
+
attribute :partOfProject, Types::Strict::String.meta(omittable: true)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -2,9 +2,12 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
-
# Metadata for a catalog link
|
6
5
|
class CatalogLink < Struct
|
6
|
+
# Catalog that is the source of the linked record.
|
7
|
+
# example: symphony
|
7
8
|
attribute :catalog, Types::Strict::String
|
9
|
+
# Record identifier that is unique within the context of the linked record's catalog.
|
10
|
+
# example: 11403803
|
8
11
|
attribute :catalogRecordId, Types::Strict::String
|
9
12
|
end
|
10
13
|
end
|
@@ -2,43 +2,33 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
-
# A digital repository collection.
|
6
|
-
# See http://sul-dlss.github.io/cocina-models/maps/Collection.json
|
7
5
|
class Collection < Struct
|
8
6
|
include Checkable
|
9
7
|
|
10
|
-
TYPES = [
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
Vocab.user_collection
|
16
|
-
].freeze
|
8
|
+
TYPES = ['http://cocina.sul.stanford.edu/models/collection.jsonld',
|
9
|
+
'http://cocina.sul.stanford.edu/models/curated-collection.jsonld',
|
10
|
+
'http://cocina.sul.stanford.edu/models/user-collection.jsonld',
|
11
|
+
'http://cocina.sul.stanford.edu/models/exhibit.jsonld',
|
12
|
+
'http://cocina.sul.stanford.edu/models/series.jsonld'].freeze
|
17
13
|
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
# Identification sub-schema for the Collection
|
33
|
-
class Identification < Struct
|
34
|
-
attribute :catalogLinks, Types::Strict::Array.of(CatalogLink).meta(omittable: true)
|
35
|
-
end
|
14
|
+
# The content type of the Collection. Selected from an established set of values.
|
15
|
+
# example: item
|
16
|
+
attribute :type, Types::Strict::String.enum(*Collection::TYPES)
|
17
|
+
# example: druid:bc123df4567
|
18
|
+
attribute :externalIdentifier, Types::Strict::String
|
19
|
+
# Primary processing label (can be same as title) for a Collection.
|
20
|
+
attribute :label, Types::Strict::String
|
21
|
+
# Version for the Collection within SDR.
|
22
|
+
attribute :version, Types::Strict::Integer
|
23
|
+
attribute(:access, Access.default { Access.new })
|
24
|
+
attribute :administrative, Administrative.optional.meta(omittable: true)
|
25
|
+
attribute :description, Description.optional.meta(omittable: true)
|
26
|
+
attribute :identification, CollectionIdentification.optional.meta(omittable: true)
|
36
27
|
|
37
|
-
|
28
|
+
def self.new(attributes = default_attributes, safe = false, validate = true, &block)
|
29
|
+
Validator.validate(self, attributes.with_indifferent_access) if validate
|
30
|
+
super(attributes, safe, &block)
|
38
31
|
end
|
39
|
-
|
40
|
-
include CollectionAttributes
|
41
|
-
attribute :externalIdentifier, Types::Strict::String
|
42
32
|
end
|
43
33
|
end
|
44
34
|
end
|
@@ -2,15 +2,8 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
-
# Descriptive metadata. See http://sul-dlss.github.io/cocina-models/maps/Description.json
|
6
5
|
class Description < Struct
|
7
|
-
|
8
|
-
class Title < Struct
|
9
|
-
attribute :primary, Types::Strict::Bool
|
10
|
-
attribute :titleFull, Types::Strict::String
|
11
|
-
end
|
12
|
-
|
13
|
-
attribute :title, Types::Strict::Array.of(Title)
|
6
|
+
attribute :title, Types::Strict::Array.of(Title).default([].freeze)
|
14
7
|
end
|
15
8
|
end
|
16
9
|
end
|
data/lib/cocina/models/dro.rb
CHANGED
@@ -2,80 +2,44 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
-
# A digital repository object.
|
6
|
-
# See http://sul-dlss.github.io/cocina-models/maps/DRO.json
|
7
5
|
class DRO < Struct
|
8
6
|
include Checkable
|
9
7
|
|
10
|
-
TYPES = [
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
# Subschema for embargo concerns
|
31
|
-
class Embargo < Struct
|
32
|
-
attribute :releaseDate, Types::Params::DateTime
|
33
|
-
attribute :access, Types::String.default('dark')
|
34
|
-
.enum('world', 'stanford', 'location-based', 'citation-only', 'dark')
|
35
|
-
attribute :useAndReproductionStatement, Types::Strict::String.meta(omittable: true)
|
36
|
-
end
|
37
|
-
|
38
|
-
attribute :access, Types::String.default('dark')
|
39
|
-
.enum('world', 'stanford', 'location-based', 'citation-only', 'dark')
|
40
|
-
attribute :copyright, Types::Strict::String.meta(omittable: true)
|
41
|
-
attribute :embargo, Embargo.optional.meta(omittable: true)
|
42
|
-
attribute :useAndReproductionStatement, Types::Strict::String.meta(omittable: true)
|
43
|
-
end
|
44
|
-
|
45
|
-
# Subschema for administrative concerns
|
46
|
-
class Administrative < Struct
|
47
|
-
# TODO: Allowing hasAdminPolicy to be omittable for now (until rolled out to consumers),
|
48
|
-
# but I think it's actually required for every DRO
|
49
|
-
attribute :hasAdminPolicy, Types::Strict::String.optional.default(nil)
|
50
|
-
attribute :releaseTags, Types::Strict::Array.of(ReleaseTag).meta(omittable: true).default([].freeze)
|
51
|
-
attribute :partOfProject, Types::Strict::String.meta(omittable: true)
|
52
|
-
end
|
53
|
-
|
54
|
-
# Identification sub-schema for the DRO
|
55
|
-
class Identification < Struct
|
56
|
-
attribute :sourceId, Types::Strict::String.meta(omittable: true)
|
57
|
-
attribute :catalogLinks, Types::Strict::Array.of(CatalogLink).meta(omittable: true)
|
58
|
-
end
|
59
|
-
|
60
|
-
# Geographic sub-schema for the DRO
|
61
|
-
class Geographic < Struct
|
62
|
-
attribute :iso19139, Types::Strict::String
|
63
|
-
end
|
64
|
-
|
65
|
-
# Structural sub-schema for the DRO (uses FileSet, unlike RequestDRO which uses RequestFileSet)
|
66
|
-
class Structural < Struct
|
67
|
-
attribute :contains, Types::Strict::Array.of(FileSet).meta(omittable: true)
|
68
|
-
attribute :hasAgreement, Types::Strict::String.meta(omittable: true)
|
69
|
-
attribute :isMemberOf, Types::Strict::String.meta(omittable: true)
|
70
|
-
attribute :hasMemberOrders, Types::Strict::Array.of(Sequence).meta(omittable: true)
|
71
|
-
end
|
72
|
-
|
73
|
-
include DroAttributes
|
8
|
+
TYPES = ['http://cocina.sul.stanford.edu/models/object.jsonld',
|
9
|
+
'http://cocina.sul.stanford.edu/models/3d.jsonld',
|
10
|
+
'http://cocina.sul.stanford.edu/models/agreement.jsonld',
|
11
|
+
'http://cocina.sul.stanford.edu/models/book.jsonld',
|
12
|
+
'http://cocina.sul.stanford.edu/models/document.jsonld',
|
13
|
+
'http://cocina.sul.stanford.edu/models/geo.jsonld',
|
14
|
+
'http://cocina.sul.stanford.edu/models/image.jsonld',
|
15
|
+
'http://cocina.sul.stanford.edu/models/page.jsonld',
|
16
|
+
'http://cocina.sul.stanford.edu/models/photograph.jsonld',
|
17
|
+
'http://cocina.sul.stanford.edu/models/manuscript.jsonld',
|
18
|
+
'http://cocina.sul.stanford.edu/models/map.jsonld',
|
19
|
+
'http://cocina.sul.stanford.edu/models/media.jsonld',
|
20
|
+
'http://cocina.sul.stanford.edu/models/track.jsonld',
|
21
|
+
'http://cocina.sul.stanford.edu/models/webarchive-binary.jsonld',
|
22
|
+
'http://cocina.sul.stanford.edu/models/webarchive-seed.jsonld'].freeze
|
23
|
+
|
24
|
+
# The content type of the DRO. Selected from an established set of values.
|
25
|
+
# example: item
|
26
|
+
attribute :type, Types::Strict::String.enum(*DRO::TYPES)
|
27
|
+
# example: druid:bc123df4567
|
74
28
|
attribute :externalIdentifier, Types::Strict::String
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
29
|
+
# Primary processing label (can be same as title) for a DRO.
|
30
|
+
attribute :label, Types::Strict::String
|
31
|
+
# Version for the DRO within SDR.
|
32
|
+
attribute :version, Types::Strict::Integer
|
33
|
+
attribute(:access, DROAccess.default { DROAccess.new })
|
34
|
+
attribute :administrative, Administrative.optional.meta(omittable: true)
|
35
|
+
attribute :description, Description.optional.meta(omittable: true)
|
36
|
+
attribute :identification, Identification.optional.meta(omittable: true)
|
37
|
+
attribute :structural, DROStructural.optional.meta(omittable: true)
|
38
|
+
attribute :geographic, Geographic.optional.meta(omittable: true)
|
39
|
+
|
40
|
+
def self.new(attributes = default_attributes, safe = false, validate = true, &block)
|
41
|
+
Validator.validate(self, attributes.with_indifferent_access) if validate
|
42
|
+
super(attributes, safe, &block)
|
79
43
|
end
|
80
44
|
end
|
81
45
|
end
|