jsapi 0.5.0 → 0.6.1

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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/lib/jsapi/controller/methods.rb +39 -13
  3. data/lib/jsapi/controller/parameters.rb +25 -23
  4. data/lib/jsapi/controller/response.rb +35 -22
  5. data/lib/jsapi/dsl/class_methods.rb +18 -10
  6. data/lib/jsapi/dsl/definitions.rb +11 -0
  7. data/lib/jsapi/dsl/openapi/callbacks.rb +34 -0
  8. data/lib/jsapi/dsl/openapi/examples.rb +32 -0
  9. data/lib/jsapi/dsl/openapi.rb +2 -0
  10. data/lib/jsapi/dsl/operation.rb +1 -1
  11. data/lib/jsapi/dsl/parameter.rb +1 -1
  12. data/lib/jsapi/dsl/request_body.rb +1 -1
  13. data/lib/jsapi/dsl/response.rb +1 -1
  14. data/lib/jsapi/dsl.rb +1 -3
  15. data/lib/jsapi/helpers/invalid_value_helper.rb +17 -0
  16. data/lib/jsapi/invalid_argument_error.rb +12 -0
  17. data/lib/jsapi/invalid_value_error.rb +12 -0
  18. data/lib/jsapi/json/object.rb +15 -14
  19. data/lib/jsapi/json.rb +1 -1
  20. data/lib/jsapi/meta/{attributes/class_methods.rb → base/attributes.rb} +2 -2
  21. data/lib/jsapi/meta/base/model.rb +44 -0
  22. data/lib/jsapi/meta/base/reference.rb +36 -0
  23. data/lib/jsapi/meta/{attributes → base}/type_caster.rb +2 -2
  24. data/lib/jsapi/meta/base.rb +4 -40
  25. data/lib/jsapi/meta/callable/symbol_evaluator.rb +30 -0
  26. data/lib/jsapi/meta/callable/symbol_sequence_evaluator.rb +29 -0
  27. data/lib/jsapi/meta/callable.rb +28 -0
  28. data/lib/jsapi/meta/defaults.rb +28 -0
  29. data/lib/jsapi/meta/definitions.rb +35 -29
  30. data/lib/jsapi/meta/openapi/callback/model.rb +1 -1
  31. data/lib/jsapi/meta/openapi/callback/reference.rb +1 -1
  32. data/lib/jsapi/meta/openapi/contact.rb +1 -1
  33. data/lib/jsapi/meta/openapi/example/model.rb +1 -1
  34. data/lib/jsapi/meta/openapi/example/reference.rb +1 -1
  35. data/lib/jsapi/meta/openapi/external_documentation.rb +1 -1
  36. data/lib/jsapi/meta/openapi/header/model.rb +17 -2
  37. data/lib/jsapi/meta/openapi/header/reference.rb +1 -1
  38. data/lib/jsapi/meta/openapi/info.rb +1 -1
  39. data/lib/jsapi/meta/openapi/license.rb +1 -1
  40. data/lib/jsapi/meta/openapi/link/model.rb +1 -1
  41. data/lib/jsapi/meta/openapi/link/reference.rb +1 -1
  42. data/lib/jsapi/meta/openapi/oauth_flow.rb +2 -2
  43. data/lib/jsapi/meta/openapi/root.rb +6 -20
  44. data/lib/jsapi/meta/openapi/security_requirement.rb +2 -2
  45. data/lib/jsapi/meta/openapi/security_scheme/base.rb +1 -1
  46. data/lib/jsapi/meta/openapi/security_scheme.rb +1 -1
  47. data/lib/jsapi/meta/openapi/server.rb +1 -1
  48. data/lib/jsapi/meta/openapi/server_variable.rb +1 -1
  49. data/lib/jsapi/meta/openapi/tag.rb +1 -1
  50. data/lib/jsapi/meta/operation.rb +21 -24
  51. data/lib/jsapi/meta/parameter/model.rb +4 -3
  52. data/lib/jsapi/meta/parameter/reference.rb +1 -1
  53. data/lib/jsapi/meta/property.rb +9 -3
  54. data/lib/jsapi/meta/request_body/model.rb +8 -3
  55. data/lib/jsapi/meta/request_body/reference.rb +1 -1
  56. data/lib/jsapi/meta/response/model.rb +9 -4
  57. data/lib/jsapi/meta/response/reference.rb +1 -1
  58. data/lib/jsapi/meta/schema/additional_properties.rb +5 -6
  59. data/lib/jsapi/meta/schema/base.rb +13 -7
  60. data/lib/jsapi/meta/schema/discriminator.rb +1 -9
  61. data/lib/jsapi/meta/schema/object.rb +27 -4
  62. data/lib/jsapi/meta/schema/reference.rb +1 -1
  63. data/lib/jsapi/meta/schema.rb +12 -5
  64. data/lib/jsapi/meta.rb +3 -5
  65. data/lib/jsapi/model/attributes.rb +10 -4
  66. data/lib/jsapi/model/base.rb +1 -1
  67. data/lib/jsapi/model/nestable.rb +25 -10
  68. data/lib/jsapi/version.rb +1 -1
  69. data/lib/jsapi.rb +4 -0
  70. metadata +15 -11
  71. data/lib/jsapi/dsl/callbacks.rb +0 -32
  72. data/lib/jsapi/dsl/examples.rb +0 -30
  73. data/lib/jsapi/meta/attributes.rb +0 -4
  74. data/lib/jsapi/meta/base_reference.rb +0 -34
  75. data/lib/jsapi/meta/invalid_argument_error.rb +0 -12
  76. data/lib/jsapi/meta/method_chain.rb +0 -32
@@ -4,7 +4,7 @@ module Jsapi
4
4
  module Meta
5
5
  module OpenAPI
6
6
  # Represents a server variable object.
7
- class ServerVariable < Base
7
+ class ServerVariable < Meta::Base::Model
8
8
  include Extensions
9
9
 
10
10
  ##
@@ -4,7 +4,7 @@ module Jsapi
4
4
  module Meta
5
5
  module OpenAPI
6
6
  # Represents a tag object.
7
- class Tag < Base
7
+ class Tag < Meta::Base::Model
8
8
  include Extensions
9
9
 
10
10
  ##
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Jsapi
4
4
  module Meta
5
- class Operation < Base
5
+ class Operation < Base::Model
6
6
  include OpenAPI::Extensions
7
7
 
8
8
  ##
@@ -10,15 +10,6 @@ module Jsapi
10
10
  # The optional callbacks. Applies to \OpenAPI 3.0 and higher.
11
11
  attribute :callbacks, { String => OpenAPI::Callback }
12
12
 
13
- ##
14
- # :attr: consumed_mime_types
15
- # The MIME types consumed by the operation. Applies to \OpenAPI 2.0 only.
16
- attribute :consumed_mime_types, [String]
17
-
18
- alias :consumes :consumed_mime_types
19
- alias :consumes= :consumed_mime_types=
20
- alias :add_consumes :add_consumed_mime_type
21
-
22
13
  ##
23
14
  # :attr: deprecated
24
15
  # Specifies whether or not the operation is deprecated.
@@ -51,8 +42,7 @@ module Jsapi
51
42
 
52
43
  ##
53
44
  # :attr: model
54
- # The model class to access top-level parameters by. The default model class
55
- # is Model::Base.
45
+ # The model class to access top-level parameters by. Model::Base by default.
56
46
  attribute :model, Class, default: Model::Base
57
47
 
58
48
  ##
@@ -70,15 +60,6 @@ module Jsapi
70
60
  # The relative path of the operation.
71
61
  attribute :path, String
72
62
 
73
- ##
74
- # :attr: consumed_mime_types
75
- # The MIME types produced by the operation. Applies to \OpenAPI 2.0 only.
76
- attribute :produced_mime_types, [String]
77
-
78
- alias :produces :produced_mime_types
79
- alias :produces= :produced_mime_types=
80
- alias :add_produces :add_produced_mime_type
81
-
82
63
  ##
83
64
  # :attr: request_body
84
65
  # The request body of the operation.
@@ -120,7 +101,7 @@ module Jsapi
120
101
 
121
102
  ##
122
103
  # :attr: tags
123
- # The tags. Tags are used to group operations in an \OpenAPI document.
104
+ # The tags used to group operations in an \OpenAPI document.
124
105
  attribute :tags, [String]
125
106
 
126
107
  def initialize(name = nil, keywords = {})
@@ -132,6 +113,18 @@ module Jsapi
132
113
  (@parameters ||= {})[name.to_s] = Parameter.new(name, keywords)
133
114
  end
134
115
 
116
+ # Returns the MIME type consumed by the operation.
117
+ def consumes(definitions)
118
+ request_body&.resolve(definitions)&.content_type
119
+ end
120
+
121
+ # Returns an array containing the MIME types produced by the operation.
122
+ def produces(definitions)
123
+ responses.values.filter_map do |response|
124
+ response.resolve(definitions).content_type
125
+ end.uniq.sort
126
+ end
127
+
135
128
  # Returns a hash representing the \OpenAPI operation object.
136
129
  def to_openapi(version, definitions)
137
130
  version = OpenAPI::Version.from(version)
@@ -146,8 +139,12 @@ module Jsapi
146
139
  security: security_requirements&.map(&:to_openapi)
147
140
  ).tap do |hash|
148
141
  if version.major == 2
149
- hash[:consumes] = consumed_mime_types if consumed_mime_types
150
- hash[:produces] = produced_mime_types if produced_mime_types
142
+ if (consumes = consumes(definitions)).present?
143
+ hash[:consumes] = [consumes]
144
+ end
145
+ if (produces = produces(definitions)).present?
146
+ hash[:produces] = produces
147
+ end
151
148
  hash[:schemes] = schemes if schemes
152
149
  elsif servers
153
150
  hash[:servers] = servers.map(&:to_openapi)
@@ -3,7 +3,7 @@
3
3
  module Jsapi
4
4
  module Meta
5
5
  module Parameter
6
- class Model < Base
6
+ class Model < Base::Model
7
7
  include OpenAPI::Extensions
8
8
 
9
9
  delegate_missing_to :schema
@@ -27,11 +27,12 @@ module Jsapi
27
27
  # :attr: in
28
28
  # The location of the parameter. Possible values are:
29
29
  #
30
+ # - <code>"header"</code>
30
31
  # - <code>"path"</code>
31
32
  # - <code>"query"</code>
32
33
  #
33
34
  # The default location is <code>"query"</code>.
34
- attribute :in, String, values: %w[path query], default: 'query'
35
+ attribute :in, String, values: %w[header path query], default: 'query'
35
36
 
36
37
  ##
37
38
  # :attr_reader: name
@@ -124,7 +125,7 @@ module Jsapi
124
125
  private
125
126
 
126
127
  def explode_object_parameter(name, schema, version, definitions, **options)
127
- schema.resolve_properties(:read, definitions).values.flat_map do |property|
128
+ schema.resolve_properties(definitions, context: :request).values.flat_map do |property|
128
129
  property_schema = property.schema.resolve(definitions)
129
130
  parameter_name = "#{name}[#{property.name}]"
130
131
 
@@ -3,7 +3,7 @@
3
3
  module Jsapi
4
4
  module Meta
5
5
  module Parameter
6
- class Reference < BaseReference
6
+ class Reference < Base::Reference
7
7
  # Returns an array of hashes. Each element represents an \OpenAPI parameter object
8
8
  # if the type of the referred parameter is <code>"object"</code>. Otherwise the
9
9
  # array contains a single hash representing the \OpenAPI reference object.
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Jsapi
4
4
  module Meta
5
- class Property < Base
5
+ class Property < Base::Model
6
6
  delegate_missing_to :schema
7
7
 
8
8
  ##
@@ -21,8 +21,8 @@ module Jsapi
21
21
 
22
22
  ##
23
23
  # :attr: source
24
- # The MethodChain to be called when reading a property value.
25
- attribute :source, MethodChain
24
+ # The alternative Callable used to read property values.
25
+ attribute :source, Callable
26
26
 
27
27
  ##
28
28
  # :attr: write_only
@@ -43,6 +43,12 @@ module Jsapi
43
43
  @schema = Schema.new(keywords)
44
44
  end
45
45
 
46
+ # Returns the Callable used to read a property value. By default, a property value is
47
+ # read by calling the method whose name matches the property name.
48
+ def reader
49
+ source || (@reader ||= Callable.from(name.underscore.to_sym))
50
+ end
51
+
46
52
  # Returns true if the level of existence is greater than or equal to +ALLOW_NIL+,
47
53
  # false otherwise.
48
54
  def required?
@@ -3,11 +3,16 @@
3
3
  module Jsapi
4
4
  module Meta
5
5
  module RequestBody
6
- class Model < Base
6
+ class Model < Base::Model
7
7
  include OpenAPI::Extensions
8
8
 
9
9
  delegate_missing_to :schema
10
10
 
11
+ ##
12
+ # :attr: content_type
13
+ # The content type. <code>"application/json"</code> by default.
14
+ attribute :content_type, String, default: 'application/json'
15
+
11
16
  ##
12
17
  # :attr: description
13
18
  # The optional description of the request body.
@@ -25,7 +30,7 @@ module Jsapi
25
30
 
26
31
  def initialize(keywords = {})
27
32
  keywords = keywords.dup
28
- super(keywords.extract!(:description, :examples))
33
+ super(keywords.extract!(:content_type, :description, :examples))
29
34
 
30
35
  add_example(value: keywords.delete(:example)) if keywords.key?(:example)
31
36
  keywords[:ref] = keywords.delete(:schema) if keywords.key?(:schema)
@@ -54,7 +59,7 @@ module Jsapi
54
59
  with_openapi_extensions(
55
60
  description: description,
56
61
  content: {
57
- 'application/json' => {
62
+ content_type => {
58
63
  schema: schema.to_openapi(version),
59
64
  examples: examples&.transform_values(&:to_openapi)
60
65
  }.compact
@@ -3,7 +3,7 @@
3
3
  module Jsapi
4
4
  module Meta
5
5
  module RequestBody
6
- class Reference < BaseReference
6
+ class Reference < Base::Reference
7
7
  # Returns a hash representing the \OpenAPI reference object.
8
8
  def to_openapi(*)
9
9
  { '$ref': "#/components/requestBodies/#{ref}" }
@@ -3,11 +3,16 @@
3
3
  module Jsapi
4
4
  module Meta
5
5
  module Response
6
- class Model < Base
6
+ class Model < Base::Model
7
7
  include OpenAPI::Extensions
8
8
 
9
9
  delegate_missing_to :schema
10
10
 
11
+ ##
12
+ # :attr: content_type
13
+ # The content type. <code>"application/json"</code> by default.
14
+ attribute :content_type, String, default: 'application/json'
15
+
11
16
  ##
12
17
  # :attr: description
13
18
  # The optional description of the response.
@@ -40,7 +45,7 @@ module Jsapi
40
45
 
41
46
  def initialize(keywords = {})
42
47
  keywords = keywords.dup
43
- super(keywords.extract!(:description, :examples, :locale))
48
+ super(keywords.extract!(:content_type, :description, :examples, :locale))
44
49
 
45
50
  add_example(value: keywords.delete(:example)) if keywords.key?(:example)
46
51
  keywords[:ref] = keywords.delete(:schema) if keywords.key?(:schema)
@@ -62,7 +67,7 @@ module Jsapi
62
67
  end&.compact,
63
68
  examples: (
64
69
  if (example = examples&.values&.first).present?
65
- { 'application/json' => example.resolve(definitions).value }
70
+ { content_type => example.resolve(definitions).value }
66
71
  end
67
72
  )
68
73
  }
@@ -70,7 +75,7 @@ module Jsapi
70
75
  {
71
76
  description: description,
72
77
  content: {
73
- 'application/json' => {
78
+ content_type => {
74
79
  schema: schema.to_openapi(version),
75
80
  examples: examples&.transform_values(&:to_openapi)
76
81
  }.compact
@@ -3,7 +3,7 @@
3
3
  module Jsapi
4
4
  module Meta
5
5
  module Response
6
- class Reference < BaseReference
6
+ class Reference < Base::Reference
7
7
  # Returns a hash representing the \OpenAPI reference object.
8
8
  def to_openapi(version, *)
9
9
  version = OpenAPI::Version.from(version)
@@ -3,9 +3,7 @@
3
3
  module Jsapi
4
4
  module Meta
5
5
  module Schema
6
- class AdditionalProperties < Meta::Base
7
- DEFAULT_METHOD_CHAIN = MethodChain.new(:additional_properties) # :nodoc:
8
-
6
+ class AdditionalProperties < Meta::Base::Model
9
7
  delegate_missing_to :schema
10
8
 
11
9
  ##
@@ -15,9 +13,10 @@ module Jsapi
15
13
 
16
14
  ##
17
15
  # :attr: source
18
- # The MethodChain to be called when reading additional properties.
19
- # The default method is +additional_properties+.
20
- attribute :source, MethodChain, default: DEFAULT_METHOD_CHAIN
16
+ # The Callable used to read additional properties. By default, additional properties
17
+ # are read by calling the +additional_properties+ method or retrieving the value
18
+ # assigned to the +:additional_properties+ key.
19
+ attribute :source, Callable, default: Callable.from(:additional_properties)
21
20
 
22
21
  def initialize(keywords = {})
23
22
  keywords = keywords.dup
@@ -3,15 +3,9 @@
3
3
  module Jsapi
4
4
  module Meta
5
5
  module Schema
6
- class Base < Meta::Base
6
+ class Base < Meta::Base::Model
7
7
  include OpenAPI::Extensions
8
8
 
9
- TYPES = %w[array boolean integer number object string].freeze # :nodoc:
10
-
11
- TYPES.each do |type|
12
- define_method("#{type}?") { self.type == type }
13
- end
14
-
15
9
  ##
16
10
  # :attr: default
17
11
  # The default value.
@@ -69,6 +63,13 @@ module Jsapi
69
63
  super(keywords)
70
64
  end
71
65
 
66
+ # Returns the default value within +context+.
67
+ def default_value(definitions = nil, context: nil)
68
+ return default unless default.nil?
69
+
70
+ definitions&.default_value(type, context: context)
71
+ end
72
+
72
73
  def enum=(value) # :nodoc:
73
74
  add_validation('enum', Validation::Enum.new(value))
74
75
  @enum = value
@@ -79,6 +80,11 @@ module Jsapi
79
80
  existence <= Existence::ALLOW_NIL
80
81
  end
81
82
 
83
+ # Returns true if and only if values can be omitted.
84
+ def omittable?
85
+ existence <= Existence::ALLOW_OMITTED
86
+ end
87
+
82
88
  # Returns a hash representing the \JSON \Schema object.
83
89
  def to_json_schema
84
90
  {
@@ -3,7 +3,7 @@
3
3
  module Jsapi
4
4
  module Meta
5
5
  module Schema
6
- class Discriminator < Meta::Base
6
+ class Discriminator < Meta::Base::Model
7
7
  ##
8
8
  # :attr: mappings
9
9
  attribute :mappings, { Object => String }
@@ -12,14 +12,6 @@ module Jsapi
12
12
  # :attr: property_name
13
13
  attribute :property_name, String
14
14
 
15
- # Looks up the inherriting schema for +value+ in +definitions+.
16
- def resolve(value, definitions)
17
- schema = definitions.schema(mapping(value) || value)
18
- raise "inherriting schema not found: #{value.inspect}" unless schema
19
-
20
- schema.resolve(definitions)
21
- end
22
-
23
15
  # Returns a hash representing the \OpenAPI discriminator object.
24
16
  def to_openapi(version)
25
17
  version = OpenAPI::Version.from(version)
@@ -36,19 +36,42 @@ module Jsapi
36
36
  (@properties ||= {})[name.to_s] = Property.new(name, **keywords)
37
37
  end
38
38
 
39
- def resolve_properties(access, definitions)
39
+ def resolve_properties(definitions, context: nil)
40
40
  properties = merge_properties(definitions, [])
41
41
 
42
- case access
43
- when :read
42
+ case context
43
+ when :response
44
44
  properties.reject { |_k, v| v.write_only? }
45
- when :write
45
+ when :request
46
46
  properties.reject { |_k, v| v.read_only? }
47
47
  else
48
48
  properties
49
49
  end
50
50
  end
51
51
 
52
+ # Resolves the schema within +context+.
53
+ #
54
+ # Raises a +RuntimeError+ when the schema couldn't be resolved.
55
+ def resolve_schema(object, definitions, context: nil)
56
+ return self if discriminator.nil?
57
+
58
+ properties = resolve_properties(definitions, context: context)
59
+
60
+ property = properties[discriminator.property_name]
61
+ raise InvalidValueError.new('discriminator property',
62
+ discriminator.property_name,
63
+ valid_values: properties.keys) if property.nil?
64
+
65
+ value = property.reader.call(object)
66
+ value = property.default_value(definitions, context: context) if value.nil?
67
+ raise "#{discriminator.property_name} can't be blank" if value.blank?
68
+
69
+ schema = definitions.schema(discriminator.mapping(value) || value)
70
+ raise "inheriting schema couldn't be found: #{value.inspect}" if schema.nil?
71
+
72
+ schema.resolve(definitions).resolve_schema(object, definitions, context: context)
73
+ end
74
+
52
75
  def to_json_schema # :nodoc:
53
76
  super.merge(
54
77
  allOf: all_of_references.map(&:to_json_schema).presence,
@@ -3,7 +3,7 @@
3
3
  module Jsapi
4
4
  module Meta
5
5
  module Schema
6
- class Reference < BaseReference
6
+ class Reference < Base::Reference
7
7
  ##
8
8
  # :attr: existence
9
9
  # The level of Existence. The default level of existence is +ALLOW_OMITTED+.
@@ -1,24 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'schema/additional_properties'
3
4
  require_relative 'schema/conversion'
4
5
  require_relative 'schema/boundary'
5
6
  require_relative 'schema/delegator'
7
+ require_relative 'schema/discriminator'
6
8
  require_relative 'schema/reference'
9
+ require_relative 'schema/validation'
10
+
7
11
  require_relative 'schema/base'
8
- require_relative 'schema/boolean'
9
12
  require_relative 'schema/array'
13
+ require_relative 'schema/boolean'
10
14
  require_relative 'schema/numeric'
11
15
  require_relative 'schema/integer'
12
16
  require_relative 'schema/number'
13
- require_relative 'schema/additional_properties'
14
- require_relative 'schema/discriminator'
15
17
  require_relative 'schema/object'
16
18
  require_relative 'schema/string'
17
- require_relative 'schema/validation'
18
19
 
19
20
  module Jsapi
20
21
  module Meta
21
22
  module Schema
23
+ TYPES = %w[array boolean integer number object string].freeze # :nodoc:
24
+
25
+ TYPES.each do |type|
26
+ Base.define_method("#{type}?") { self.type == type }
27
+ end
28
+
22
29
  class << self
23
30
  # Creates a new schema model or reference. The +:type+ keyword determines
24
31
  # the type of the schema to be created. Possible types are:
@@ -51,7 +58,7 @@ module Jsapi
51
58
  when 'string'
52
59
  String
53
60
  else
54
- raise InvalidArgumentError.new('type', type, Base::TYPES)
61
+ raise InvalidArgumentError.new('type', type, valid_values: TYPES)
55
62
  end.new(keywords.except(:type))
56
63
  end
57
64
  end
data/lib/jsapi/meta.rb CHANGED
@@ -1,13 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'meta/invalid_argument_error'
4
3
  require_relative 'meta/reference_error'
5
- require_relative 'meta/attributes'
4
+ require_relative 'meta/callable'
5
+ require_relative 'meta/existence'
6
6
  require_relative 'meta/base'
7
- require_relative 'meta/base_reference'
7
+ require_relative 'meta/defaults'
8
8
  require_relative 'meta/openapi'
9
- require_relative 'meta/existence'
10
- require_relative 'meta/method_chain'
11
9
  require_relative 'meta/property'
12
10
  require_relative 'meta/schema'
13
11
  require_relative 'meta/request_body'
@@ -6,16 +6,22 @@ module Jsapi
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  included do
9
- delegate :[], :attribute?, :attributes, to: :nested
9
+ delegate :[], :additional_attributes, :attribute?, :attributes, to: :nested
10
10
  end
11
11
 
12
12
  def method_missing(*args) # :nodoc:
13
- name = args.first
14
- attribute?(name) ? self[name] : super
13
+ name = args.first.to_s
14
+ _attr_readers.key?(name) ? _attr_readers[name] : super
15
15
  end
16
16
 
17
17
  def respond_to_missing?(param1, _param2) # :nodoc:
18
- attribute?(param1) ? true : super
18
+ _attr_readers.key?(param1.to_s) ? true : super
19
+ end
20
+
21
+ private
22
+
23
+ def _attr_readers
24
+ @_attr_readers ||= attributes.transform_keys(&:underscore)
19
25
  end
20
26
  end
21
27
  end
@@ -22,7 +22,7 @@ module Jsapi
22
22
  end
23
23
 
24
24
  def inspect # :nodoc:
25
- "#<#{self.class.name} " \
25
+ "#<#{self.class.name}#{' ' if attributes.any?}" \
26
26
  "#{attributes.map { |k, v| "#{k}: #{v.inspect}" }.join(', ')}>"
27
27
  end
28
28
 
@@ -3,33 +3,48 @@
3
3
  module Jsapi
4
4
  module Model
5
5
  module Nestable
6
-
7
6
  # Returns the value assigned to +name+.
8
7
  def [](name)
9
8
  raw_attributes[name&.to_s]&.value
10
9
  end
11
10
 
11
+ # Returns a hash containing the additional attributes.
12
+ def additional_attributes
13
+ raw_additional_attributes.transform_values(&:value)
14
+ end
15
+
12
16
  # Returns +true+ if +name+ is present, false +otherwise+.
13
17
  def attribute?(name)
14
18
  raw_attributes.key?(name&.to_s)
15
19
  end
16
20
 
17
- # Returns a +Hash+ containing all attributes.
21
+ # Returns a hash containing all attributes.
18
22
  def attributes
19
23
  raw_attributes.transform_values(&:value)
20
24
  end
21
25
 
26
+ def inspect # :nodoc:
27
+ "#<#{self.class.name} #{
28
+ raw_attributes
29
+ .merge('additional_attributes' => raw_additional_attributes)
30
+ .map { |k, v| "#{k}: #{v.inspect}" }
31
+ .join(', ')
32
+ }>"
33
+ end
34
+
22
35
  private
23
36
 
24
37
  def validate_attributes(errors)
25
- raw_attributes.map do |key, value|
26
- errors.nested(key) do
27
- next value.validate(errors) unless value.respond_to?(:model)
28
- next true if (model = value.model).valid?
29
-
30
- errors.merge!(model)
31
- false
32
- end
38
+ [raw_attributes, raw_additional_attributes].compact.map do |attributes|
39
+ attributes.map do |name, value|
40
+ errors.nested(name) do
41
+ next value.validate(errors) unless value.respond_to?(:model)
42
+ next true if (model = value.model).valid?
43
+
44
+ errors.merge!(model)
45
+ false
46
+ end
47
+ end.all?
33
48
  end.all?
34
49
  end
35
50
  end
data/lib/jsapi/version.rb CHANGED
@@ -5,6 +5,6 @@ module Jsapi
5
5
  # NOTE: See https://bundler.io/guides/creating_gem.html
6
6
 
7
7
  # The current GEM version.
8
- VERSION = '0.5.0'
8
+ VERSION = '0.6.1'
9
9
  end
10
10
  end
data/lib/jsapi.rb CHANGED
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'jsapi/helpers/invalid_value_helper'
4
+ require 'jsapi/invalid_value_error'
5
+ require 'jsapi/invalid_argument_error'
6
+
3
7
  require 'jsapi/model'
4
8
  require 'jsapi/meta'
5
9
  require 'jsapi/dsl'