oscal 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.docker/Dockerfile +19 -0
  3. data/.docker/Makefile +43 -0
  4. data/.docker/docker-compose.yml +14 -0
  5. data/.docker/readme.md +61 -0
  6. data/.gitignore +2 -0
  7. data/.rspec +0 -1
  8. data/.rubocop.yml +1 -1
  9. data/.ruby-version +1 -0
  10. data/Gemfile +2 -0
  11. data/LICENSE +25 -0
  12. data/Makefile +1 -0
  13. data/README.adoc +3 -0
  14. data/Rakefile +13 -6
  15. data/bin/console +2 -2
  16. data/bin/rspec +27 -0
  17. data/docker-compose.yml +1 -0
  18. data/lib/oscal/add.rb +5 -4
  19. data/lib/oscal/address.rb +3 -2
  20. data/lib/oscal/address_line.rb +1 -0
  21. data/lib/oscal/alter.rb +3 -2
  22. data/lib/oscal/assembly.rb +119 -0
  23. data/lib/oscal/assessment_plan.rb +28 -0
  24. data/lib/oscal/assessment_result.rb +230 -0
  25. data/lib/oscal/attribute_type_hash.rb +80 -0
  26. data/lib/oscal/back_matter.rb +2 -1
  27. data/lib/oscal/base64_object.rb +1 -0
  28. data/lib/oscal/base_class.rb +5 -4
  29. data/lib/oscal/catalog.rb +8 -7
  30. data/lib/oscal/choice.rb +1 -0
  31. data/lib/oscal/citation.rb +3 -2
  32. data/lib/oscal/combine.rb +1 -0
  33. data/lib/oscal/common_utils.rb +1 -1
  34. data/lib/oscal/constraint.rb +2 -1
  35. data/lib/oscal/control.rb +6 -5
  36. data/lib/oscal/custom.rb +3 -2
  37. data/lib/oscal/datatypes.rb +50 -0
  38. data/lib/oscal/document_id.rb +1 -0
  39. data/lib/oscal/email_address.rb +1 -0
  40. data/lib/oscal/exclude_control.rb +3 -2
  41. data/lib/oscal/external_id.rb +1 -0
  42. data/lib/oscal/group.rb +9 -8
  43. data/lib/oscal/guideline.rb +1 -0
  44. data/lib/oscal/hash_object.rb +1 -0
  45. data/lib/oscal/import_object.rb +3 -2
  46. data/lib/oscal/include_control.rb +3 -2
  47. data/lib/oscal/insert_control.rb +3 -2
  48. data/lib/oscal/link.rb +1 -0
  49. data/lib/oscal/list.rb +160 -0
  50. data/lib/oscal/location.rb +8 -7
  51. data/lib/oscal/location_uuid.rb +1 -0
  52. data/lib/oscal/logger.rb +8 -0
  53. data/lib/oscal/matching.rb +1 -0
  54. data/lib/oscal/member_of_organization.rb +1 -0
  55. data/lib/oscal/merge.rb +2 -1
  56. data/lib/oscal/metadata_block.rb +11 -10
  57. data/lib/oscal/modify.rb +3 -2
  58. data/lib/oscal/parameter.rb +8 -7
  59. data/lib/oscal/parsing_functions.rb +19 -0
  60. data/lib/oscal/part.rb +4 -3
  61. data/lib/oscal/party.rb +11 -10
  62. data/lib/oscal/party_uuid.rb +1 -0
  63. data/lib/oscal/profile.rb +7 -6
  64. data/lib/oscal/property.rb +1 -0
  65. data/lib/oscal/remove.rb +1 -0
  66. data/lib/oscal/resource.rb +7 -6
  67. data/lib/oscal/responsible_party.rb +11 -10
  68. data/lib/oscal/revision.rb +4 -3
  69. data/lib/oscal/rlink.rb +2 -1
  70. data/lib/oscal/role.rb +3 -2
  71. data/lib/oscal/select.rb +2 -1
  72. data/lib/oscal/set_parameter.rb +8 -7
  73. data/lib/oscal/telephone_number.rb +1 -0
  74. data/lib/oscal/test.rb +1 -0
  75. data/lib/oscal/url.rb +1 -0
  76. data/lib/oscal/value.rb +5 -4
  77. data/lib/oscal/version.rb +1 -1
  78. data/lib/oscal/with_id.rb +2 -1
  79. data/lib/oscal.rb +1 -1
  80. data/spec/oscal/catalog_spec.rb +5 -4
  81. data/spec/sample_inputs/import-ap.json +4 -0
  82. metadata +21 -3
@@ -5,13 +5,14 @@ module Oscal
5
5
  KEY = %i(with_child_controls with_ids matching)
6
6
 
7
7
  attr_accessor *KEY
8
+
8
9
  attr_serializable *KEY
9
10
 
10
11
  def set_value(key_name, val)
11
12
  case key_name
12
- when 'with_ids'
13
+ when "with_ids"
13
14
  WithId.wrap(val)
14
- when 'matching'
15
+ when "matching"
15
16
  Matching.wrap(val)
16
17
  else
17
18
  val
@@ -5,13 +5,14 @@ module Oscal
5
5
  KEY = %i(order include_all include_controls exclude_controls)
6
6
 
7
7
  attr_accessor *KEY
8
+
8
9
  attr_serializable *KEY
9
10
 
10
11
  def set_value(key_name, val)
11
12
  case key_name
12
- when 'include_controls'
13
+ when "include_controls"
13
14
  IncludeControl.wrap(val)
14
- when 'exclude_controls'
15
+ when "exclude_controls"
15
16
  ExcludeControls.wrap(val)
16
17
  else
17
18
  val
data/lib/oscal/link.rb CHANGED
@@ -5,6 +5,7 @@ module Oscal
5
5
  KEY = %i(href rel media_type text)
6
6
 
7
7
  attr_accessor *KEY
8
+
8
9
  attr_serializable *KEY
9
10
  end
10
11
  end
data/lib/oscal/list.rb ADDED
@@ -0,0 +1,160 @@
1
+ require_relative("link")
2
+ require_relative("property")
3
+ require_relative("logger")
4
+
5
+ module Oscal
6
+ class OscalArray < Array
7
+ include ParsingLogger
8
+
9
+ def validate_member_type(input)
10
+ @logger.debug("Validating array members are #{self.class::MEMBER_TYPE}")
11
+ input.map do |item|
12
+ self.class::MEMBER_TYPE.new(item)
13
+ end
14
+ end
15
+
16
+ def initialize(input)
17
+ super
18
+ @logger = get_logger
19
+ @logger.debug("#{self.class}.new called with #{input.to_s[0, 25]}")
20
+ validate_member_type(input)
21
+ end
22
+ end
23
+
24
+ # These classes define generic arrays of basic DataTypes,
25
+ # They appear in several different places in the Oscal specs
26
+ # and can be subclassed with a different name
27
+ class StringDataTypeArray < Oscal::OscalArray
28
+ MEMBER_TYPE = StringDataType
29
+ end
30
+
31
+ class TokenDataTypeArray < Oscal::OscalArray
32
+ MEMBER_TYPE = TokenDataType
33
+ end
34
+
35
+ class UuidArray < Oscal::OscalArray
36
+ MEMBER_TYPE = Uuid
37
+ end
38
+
39
+ module AssessmentResult
40
+ class ActivityArray < Oscal::OscalArray
41
+ MEMBER_TYPE = Activity
42
+ end
43
+
44
+ class AssessmentPlatformArray < Oscal::OscalArray
45
+ MEMBER_TYPE = AssessmentPlatform
46
+ end
47
+
48
+ class AssessmentTaskArray < Oscal::OscalArray
49
+ MEMBER_TYPE = AssessmentTask
50
+ end
51
+
52
+ class AssociatedActivityArray < Oscal::OscalArray
53
+ MEMBER_TYPE = AssociatedActivity
54
+ end
55
+
56
+ class AttestationArray < Oscal::OscalArray
57
+ MEMBER_TYPE = Attestation
58
+ end
59
+
60
+ class ComponentArray < Oscal::OscalArray
61
+ MEMBER_TYPE = Component
62
+ end
63
+
64
+ class ControlObjectiveSelectionArray < Oscal::OscalArray
65
+ MEMBER_TYPE = ControlObjectiveSelection
66
+ end
67
+
68
+ class ControlSelectionArray < Oscal::OscalArray
69
+ MEMBER_TYPE = ControlSelection
70
+ end
71
+
72
+ class EntryArray < Oscal::OscalArray
73
+ MEMBER_TYPE = Entry
74
+ end
75
+
76
+ class ExcludeControlArray < Oscal::OscalArray
77
+ MEMBER_TYPE = ExcludeControl
78
+ end
79
+
80
+ class ExcludeObjectiveArray < Oscal::OscalArray
81
+ MEMBER_TYPE = ExcludeObjective
82
+ end
83
+
84
+ class FindingArray < Oscal::OscalArray
85
+ MEMBER_TYPE = Finding
86
+ end
87
+
88
+ class IncludeControlArray < Oscal::OscalArray
89
+ MEMBER_TYPE = IncludeControl
90
+ end
91
+
92
+ class InventoryItemArray < Oscal::OscalArray
93
+ MEMBER_TYPE = InventoryItem
94
+ end
95
+
96
+ class LinkArray < Oscal::OscalArray
97
+ MEMBER_TYPE = Oscal::Link
98
+ end
99
+
100
+ class MethodArray < StringDataTypeArray
101
+ end
102
+
103
+ class ObjectivesAndMethodsArray < Oscal::OscalArray
104
+ MEMBER_TYPE = ObjectivesAndMethods
105
+ end
106
+
107
+ class ObservationArray < Oscal::OscalArray
108
+ MEMBER_TYPE = Observation
109
+ end
110
+
111
+ class PartArray < Oscal::OscalArray
112
+ MEMBER_TYPE = Link
113
+ end
114
+
115
+ class PartyUuidArray < UuidArray
116
+ end
117
+
118
+ class PropArray < Oscal::OscalArray
119
+ MEMBER_TYPE = Property
120
+ end
121
+
122
+ class RelatedObservationArray < Oscal::OscalArray
123
+ MEMBER_TYPE = RelatedObservation
124
+ end
125
+
126
+ class RelatedRiskArray < Oscal::OscalArray
127
+ MEMBER_TYPE = AssociatedRisk
128
+ end
129
+
130
+ class ResponsibleRoleArray < Oscal::OscalArray
131
+ MEMBER_TYPE = ResponsibleRole
132
+ end
133
+
134
+ class ResultArray < Oscal::OscalArray
135
+ MEMBER_TYPE = Result
136
+ end
137
+
138
+ class RiskArray < Oscal::OscalArray
139
+ MEMBER_TYPE = Risk
140
+ end
141
+
142
+ class StatementIdArray < TokenDataTypeArray
143
+ end
144
+
145
+ class StepArray < Oscal::OscalArray
146
+ MEMBER_TYPE = Step
147
+ end
148
+
149
+ class SubjectArray < Oscal::OscalArray
150
+ MEMBER_TYPE = Subject
151
+ end
152
+
153
+ class TypeArray < TokenDataTypeArray
154
+ end
155
+
156
+ class UserArray < Oscal::OscalArray
157
+ MEMBER_TYPE = User
158
+ end
159
+ end
160
+ end
@@ -3,24 +3,25 @@ require_relative "base_class"
3
3
  module Oscal
4
4
  class Location < Oscal::BaseClass
5
5
  KEY = %i(uuid title address email_addresses telephone_numbers urls
6
- props links remakrs)
6
+ props links remakrs)
7
7
 
8
8
  attr_accessor *KEY
9
+
9
10
  attr_serializable *KEY
10
11
 
11
12
  def set_value(key_name, val)
12
13
  case key_name
13
- when 'address'
14
+ when "address"
14
15
  Address.wrap(val)
15
- when 'email_addresses'
16
+ when "email_addresses"
16
17
  EmailAddress.wrap(val)
17
- when 'telephone_numbers'
18
+ when "telephone_numbers"
18
19
  TelephoneNumber.wrap(val)
19
- when 'urls'
20
+ when "urls"
20
21
  Url.wrap(val)
21
- when 'props'
22
+ when "props"
22
23
  Property.wrap(val)
23
- when 'links'
24
+ when "links"
24
25
  Link.wrap(val)
25
26
  else
26
27
  val
@@ -5,6 +5,7 @@ module Oscal
5
5
  KEY = %i(val)
6
6
 
7
7
  attr_accessor *KEY
8
+
8
9
  attr_serializable *KEY
9
10
  end
10
11
  end
@@ -0,0 +1,8 @@
1
+ module Oscal
2
+ module ParsingLogger
3
+ require "logger"
4
+ def get_logger
5
+ Logger.new($stdout)
6
+ end
7
+ end
8
+ end
@@ -5,6 +5,7 @@ module Oscal
5
5
  KEY = %i(pattern)
6
6
 
7
7
  attr_accessor *KEY
8
+
8
9
  attr_serializable *KEY
9
10
  end
10
11
  end
@@ -5,6 +5,7 @@ module Oscal
5
5
  KEY = %i(val)
6
6
 
7
7
  attr_accessor *KEY
8
+
8
9
  attr_serializable *KEY
9
10
  end
10
11
  end
data/lib/oscal/merge.rb CHANGED
@@ -5,11 +5,12 @@ module Oscal
5
5
  KEY = %i(combine flat as_is custom)
6
6
 
7
7
  attr_accessor *KEY
8
+
8
9
  attr_serializable *KEY
9
10
 
10
11
  def set_value(key_name, val)
11
12
  case key_name
12
- when 'custom'
13
+ when "custom"
13
14
  Custom.wrap(val)
14
15
  else
15
16
  val
@@ -3,29 +3,30 @@ require_relative "base_class"
3
3
  module Oscal
4
4
  class MetadataBlock < Oscal::BaseClass
5
5
  KEY = %i(title published last_modified version
6
- oscal_version revisions document_ids props links roles
7
- locations parties responsible_parties remarks)
6
+ oscal_version revisions document_ids props links roles
7
+ locations parties responsible_parties remarks)
8
8
 
9
9
  attr_accessor *KEY
10
+
10
11
  attr_serializable *KEY
11
12
 
12
13
  def set_value(key_name, val)
13
14
  case key_name
14
- when 'revisions'
15
+ when "revisions"
15
16
  Revision.wrap(val)
16
- when 'docuement_ids'
17
+ when "docuement_ids"
17
18
  DocumentId.wrap(val)
18
- when 'props'
19
+ when "props"
19
20
  Property.wrap(val)
20
- when 'links'
21
+ when "links"
21
22
  Link.wrap(val)
22
- when 'roles'
23
+ when "roles"
23
24
  Role.wrap(val)
24
- when 'locations'
25
+ when "locations"
25
26
  Location.wrap(val)
26
- when 'parties'
27
+ when "parties"
27
28
  Party.wrap(val)
28
- when 'responsible_parties'
29
+ when "responsible_parties"
29
30
  ResponsibleParty.wrap(val)
30
31
  else
31
32
  val
data/lib/oscal/modify.rb CHANGED
@@ -5,13 +5,14 @@ module Oscal
5
5
  KEY = %i(set_parameters alters)
6
6
 
7
7
  attr_accessor *KEY
8
+
8
9
  attr_serializable *KEY
9
10
 
10
11
  def set_value(key_name, val)
11
12
  case key_name
12
- when 'set_parameters'
13
+ when "set_parameters"
13
14
  SetParameter.wrap(val)
14
- when 'alters'
15
+ when "alters"
15
16
  Alter.wrap(val)
16
17
  else
17
18
  val
@@ -3,24 +3,25 @@ require_relative "base_class"
3
3
  module Oscal
4
4
  class Parameter < Oscal::BaseClass
5
5
  KEY = %i(id klass depneds_on props links label usage
6
- constraints guidelines values select remarks)
6
+ constraints guidelines values select remarks)
7
7
 
8
8
  attr_accessor *KEY
9
+
9
10
  attr_serializable *KEY
10
11
 
11
12
  def set_value(key_name, val)
12
13
  case key_name
13
- when 'props'
14
+ when "props"
14
15
  Property.wrap(val)
15
- when 'links'
16
+ when "links"
16
17
  Link.wrap(val)
17
- when 'constraints'
18
+ when "constraints"
18
19
  Constraint.wrap(val)
19
- when 'guidelines'
20
+ when "guidelines"
20
21
  Guideline.wrap(val)
21
- when 'values'
22
+ when "values"
22
23
  Value.wrap(val)
23
- when 'select'
24
+ when "select"
24
25
  Select.wrap(val)
25
26
  else
26
27
  val
@@ -0,0 +1,19 @@
1
+ module Oscal
2
+ module ParsingFunctions
3
+ def sym2str(key)
4
+ if key.is_a?(Symbol)
5
+ key.to_s.gsub("-", "_").gsub("class", "klass")
6
+ elsif key.is_a?(String)
7
+ key
8
+ end
9
+ end
10
+
11
+ def str2sym(var)
12
+ if var.is_a?(String)
13
+ var.gsub("-", "_").gsub("class", "klass").to_sym
14
+ elsif var.is_a?(Symbol)
15
+ var
16
+ end
17
+ end
18
+ end
19
+ end
data/lib/oscal/part.rb CHANGED
@@ -5,15 +5,16 @@ module Oscal
5
5
  KEY = %i(id name ns klass title props prose parts links)
6
6
 
7
7
  attr_accessor *KEY
8
+
8
9
  attr_serializable *KEY
9
10
 
10
11
  def set_value(key_name, val)
11
12
  case key_name
12
- when 'props'
13
+ when "props"
13
14
  Property.wrap(val)
14
- when 'parts'
15
+ when "parts"
15
16
  Part.wrap(val)
16
- when 'links'
17
+ when "links"
17
18
  Link.wrap(val)
18
19
  else
19
20
  val
data/lib/oscal/party.rb CHANGED
@@ -3,29 +3,30 @@ require_relative "base_class"
3
3
  module Oscal
4
4
  class Party < Oscal::BaseClass
5
5
  KEY = %i(uuid type name short_name external_ids props links
6
- email_addresses telephone_numbers addresses location_uuids
7
- member_of_organizations remakrs)
6
+ email_addresses telephone_numbers addresses location_uuids
7
+ member_of_organizations remakrs)
8
8
 
9
9
  attr_accessor *KEY
10
+
10
11
  attr_serializable *KEY
11
12
 
12
13
  def set_value(key_name, val)
13
14
  case key_name
14
- when 'external_ids'
15
+ when "external_ids"
15
16
  ExternalId.wrap(val)
16
- when 'props'
17
+ when "props"
17
18
  Property.wrap(val)
18
- when 'links'
19
+ when "links"
19
20
  Link.wrap(val)
20
- when 'email_addresses'
21
+ when "email_addresses"
21
22
  EmailAddress.wrap(val)
22
- when 'telephone_numbers'
23
+ when "telephone_numbers"
23
24
  TelephoneNumber.wrap(val)
24
- when 'addresses'
25
+ when "addresses"
25
26
  Address.wrap(val)
26
- when 'location_uuids'
27
+ when "location_uuids"
27
28
  LocationUuid.wrap(val)
28
- when 'member_of_organizations'
29
+ when "member_of_organizations"
29
30
  MemberOfOrganization.wrap(val)
30
31
  else
31
32
  val
@@ -5,6 +5,7 @@ module Oscal
5
5
  KEY = %i(val)
6
6
 
7
7
  attr_accessor *KEY
8
+
8
9
  attr_serializable *KEY
9
10
  end
10
11
  end
data/lib/oscal/profile.rb CHANGED
@@ -8,6 +8,7 @@ module Oscal
8
8
 
9
9
  KEY = %i(uuid metadata imports merge modify back_matter)
10
10
  attr_accessor *KEY
11
+
11
12
  attr_serializable *KEY
12
13
 
13
14
  def initialize(uuid, metadata, imports, merge, modify, back_matter)
@@ -23,12 +24,12 @@ module Oscal
23
24
  yaml_data = safe_load_yaml(path)
24
25
  yaml_profile = yaml_data["profile"]
25
26
 
26
- uuid = yaml_profile['uuid']
27
- metadata = yaml_profile['metadata']
28
- imports = yaml_profile['imports']
29
- merge = yaml_profile['merge']
30
- modify = yaml_profile['modify']
31
- back_matter = yaml_profile['back-matter']
27
+ uuid = yaml_profile["uuid"]
28
+ metadata = yaml_profile["metadata"]
29
+ imports = yaml_profile["imports"]
30
+ merge = yaml_profile["merge"]
31
+ modify = yaml_profile["modify"]
32
+ back_matter = yaml_profile["back-matter"]
32
33
 
33
34
  Profile.new(uuid, metadata, imports, merge, modify, back_matter)
34
35
  end
@@ -5,6 +5,7 @@ module Oscal
5
5
  KEY = %i(name uuid ns value klass remarks)
6
6
 
7
7
  attr_accessor *KEY
8
+
8
9
  attr_serializable *KEY
9
10
  end
10
11
  end
data/lib/oscal/remove.rb CHANGED
@@ -5,6 +5,7 @@ module Oscal
5
5
  KEY = %i(by_name by_class by_id by_item_name by_ns)
6
6
 
7
7
  attr_accessor *KEY
8
+
8
9
  attr_serializable *KEY
9
10
  end
10
11
  end
@@ -3,22 +3,23 @@ require_relative "base_class"
3
3
  module Oscal
4
4
  class Resource < Oscal::BaseClass
5
5
  KEY = %i(uuid title description props docuement_ids citation rlinks
6
- base64 remarks)
6
+ base64 remarks)
7
7
 
8
8
  attr_accessor *KEY
9
+
9
10
  attr_serializable *KEY
10
11
 
11
12
  def set_value(key_name, val)
12
13
  case key_name
13
- when 'props'
14
+ when "props"
14
15
  Property.wrap(val)
15
- when 'document_ids'
16
+ when "document_ids"
16
17
  DocumentId.wrap(val)
17
- when 'citation'
18
+ when "citation"
18
19
  Citation.wrap(val)
19
- when 'rlinks'
20
+ when "rlinks"
20
21
  Rlink.wrap(val)
21
- when 'base64'
22
+ when "base64"
22
23
  Base64Object.wrap(val)
23
24
  else
24
25
  val
@@ -5,19 +5,20 @@ module Oscal
5
5
  KEY = %i(role_id party_uuids props links remakrs)
6
6
 
7
7
  attr_accessor *KEY
8
+
8
9
  attr_serializable *KEY
9
10
 
10
11
  def set_value(key_name, val)
11
- case key_name
12
- when 'party_uuids'
13
- PartyUuid.wrap(val)
14
- when 'props'
15
- Property.wrap(val)
16
- when 'links'
17
- Link.wrap(val)
18
- else
19
- val
20
- end
12
+ case key_name
13
+ when "party_uuids"
14
+ PartyUuid.wrap(val)
15
+ when "props"
16
+ Property.wrap(val)
17
+ when "links"
18
+ Link.wrap(val)
19
+ else
20
+ val
21
+ end
21
22
  end
22
23
  end
23
24
  end
@@ -3,16 +3,17 @@ require_relative "base_class"
3
3
  module Oscal
4
4
  class Revision < Oscal::BaseClass
5
5
  KEY = %i(title published last_modified version oscal_version
6
- props links remarks)
6
+ props links remarks)
7
7
 
8
8
  attr_accessor *KEY
9
+
9
10
  attr_serializable *KEY
10
11
 
11
12
  def set_value(key_name, val)
12
13
  case key_name
13
- when 'props'
14
+ when "props"
14
15
  Property.wrap(val)
15
- when 'links'
16
+ when "links"
16
17
  Link.wrap(val)
17
18
  else
18
19
  val
data/lib/oscal/rlink.rb CHANGED
@@ -5,11 +5,12 @@ module Oscal
5
5
  KEY = %i(href media_type hashes)
6
6
 
7
7
  attr_accessor *KEY
8
+
8
9
  attr_serializable *KEY
9
10
 
10
11
  def set_value(key_name, val)
11
12
  case key_name
12
- when 'hashes'
13
+ when "hashes"
13
14
  HashObject.wrap(val)
14
15
  else
15
16
  val
data/lib/oscal/role.rb CHANGED
@@ -5,13 +5,14 @@ module Oscal
5
5
  KEY = %i(id title short_name description props links remakrs)
6
6
 
7
7
  attr_accessor *KEY
8
+
8
9
  attr_serializable *KEY
9
10
 
10
11
  def set_value(key_name, val)
11
12
  case key_name
12
- when 'props'
13
+ when "props"
13
14
  Property.wrap(val)
14
- when 'links'
15
+ when "links"
15
16
  Link.wrap(val)
16
17
  else
17
18
  val
data/lib/oscal/select.rb CHANGED
@@ -5,11 +5,12 @@ module Oscal
5
5
  KEY = %i(how_many choice)
6
6
 
7
7
  attr_accessor *KEY
8
+
8
9
  attr_serializable *KEY
9
10
 
10
11
  def set_value(key_name, val)
11
12
  case key_name
12
- when 'choice'
13
+ when "choice"
13
14
  Choice.wrap(val)
14
15
  else
15
16
  val