taro 1.1.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -0
  3. data/README.md +76 -9
  4. data/lib/taro/errors.rb +1 -1
  5. data/lib/taro/export/open_api_v3.rb +76 -24
  6. data/lib/taro/rails/declaration.rb +44 -20
  7. data/lib/taro/rails/generators/install_generator.rb +1 -1
  8. data/lib/taro/rails/generators/templates/errors_type.erb +15 -10
  9. data/lib/taro/rails/normalized_route.rb +8 -0
  10. data/lib/taro/rails/tasks/export.rake +5 -1
  11. data/lib/taro/types/base_type.rb +2 -0
  12. data/lib/taro/types/coercion.rb +24 -14
  13. data/lib/taro/types/field.rb +8 -16
  14. data/lib/taro/types/field_validation.rb +1 -1
  15. data/lib/taro/types/list_type.rb +4 -6
  16. data/lib/taro/types/object_types/free_form_type.rb +1 -0
  17. data/lib/taro/types/object_types/no_content_type.rb +1 -0
  18. data/lib/taro/types/object_types/page_info_type.rb +2 -0
  19. data/lib/taro/types/object_types/page_type.rb +15 -25
  20. data/lib/taro/types/scalar/iso8601_date_type.rb +3 -3
  21. data/lib/taro/types/scalar/iso8601_datetime_type.rb +3 -3
  22. data/lib/taro/types/scalar/string_type.rb +17 -6
  23. data/lib/taro/types/scalar/timestamp_type.rb +1 -0
  24. data/lib/taro/types/scalar/uuid_v4_type.rb +3 -20
  25. data/lib/taro/types/scalar_type.rb +1 -0
  26. data/lib/taro/types/shared/custom_field_resolvers.rb +2 -2
  27. data/lib/taro/types/shared/deprecation.rb +3 -0
  28. data/lib/taro/types/shared/derived_types.rb +27 -0
  29. data/lib/taro/types/shared/errors.rb +3 -1
  30. data/lib/taro/types/shared/fields.rb +6 -5
  31. data/lib/taro/types/shared/item_type.rb +1 -0
  32. data/lib/taro/types/shared/object_coercion.rb +13 -0
  33. data/lib/taro/types/shared/openapi_name.rb +8 -6
  34. data/lib/taro/types/shared/pattern.rb +69 -0
  35. data/lib/taro/types/shared/rendering.rb +4 -4
  36. data/lib/taro/version.rb +1 -1
  37. data/tasks/benchmark.rake +1 -1
  38. metadata +7 -5
  39. data/lib/taro/types/shared/derivable_types.rb +0 -9
@@ -1,11 +1,11 @@
1
1
  require_relative 'field_validation'
2
2
 
3
- Taro::Types::Field = Data.define(:name, :type, :null, :method, :default, :enum, :defined_at, :desc) do
3
+ Taro::Types::Field = Data.define(:name, :type, :null, :method, :default, :enum, :defined_at, :desc, :deprecated) do
4
4
  include Taro::Types::FieldValidation
5
5
 
6
- def initialize(name:, type:, null:, method: name, default: :none, enum: nil, defined_at: nil, desc: nil)
6
+ def initialize(name:, type:, null:, method: name, default: :none, enum: nil, defined_at: nil, desc: nil, deprecated: nil)
7
7
  enum = coerce_to_enum(enum)
8
- super(name:, type:, null:, method:, default:, enum:, defined_at:, desc:)
8
+ super(name:, type:, null:, method:, default:, enum:, defined_at:, desc:, deprecated:)
9
9
  end
10
10
 
11
11
  def value_for_input(object)
@@ -40,14 +40,15 @@ Taro::Types::Field = Data.define(:name, :type, :null, :method, :default, :enum,
40
40
  end
41
41
 
42
42
  def retrieve_response_value(object, context, object_is_hash)
43
- if object_is_hash
44
- retrieve_hash_value(object)
45
- elsif context&.resolve?(method)
43
+ if context&.resolve?(method)
46
44
  context.public_send(method)
45
+ elsif object_is_hash
46
+ retrieve_hash_value(object)
47
47
  elsif object.respond_to?(method, true)
48
48
  object.public_send(method)
49
49
  else
50
- raise_response_coercion_error(object)
50
+ # Note that the ObjectCoercion module rescues this and adds context.
51
+ raise Taro::ResponseError, "No such method or resolver `:#{method}`."
51
52
  end
52
53
  end
53
54
 
@@ -65,14 +66,5 @@ Taro::Types::Field = Data.define(:name, :type, :null, :method, :default, :enum,
65
66
 
66
67
  type_obj = type.new(value)
67
68
  from_input ? type_obj.coerce_input : type_obj.coerce_response
68
- rescue Taro::Error => e
69
- raise e.class, "#{e.message}, after using method/key `:#{method}` to resolve field `#{name}`"
70
- end
71
-
72
- def raise_response_coercion_error(object)
73
- raise Taro::ResponseError, <<~MSG
74
- Failed to coerce value #{object.inspect} for field `#{name}` using method/key `:#{method}`.
75
- It is not a valid #{type} value.
76
- MSG
77
69
  end
78
70
  end
@@ -18,7 +18,7 @@ module Taro::Types::FieldValidation
18
18
  end
19
19
 
20
20
  def validate_enum_inclusion(value, for_input)
21
- return if enum.nil? || enum.include?(value)
21
+ return if enum.nil? || null && value.nil? || enum.include?(value)
22
22
 
23
23
  raise for_input ? Taro::InputError : Taro::ResponseError, <<~MSG
24
24
  Field #{name} has an invalid value #{value.inspect} (expected one of #{enum.inspect})
@@ -2,7 +2,6 @@
2
2
  # Unlike other types, this one should not be manually inherited from,
3
3
  # but is used indirectly via `array_of: SomeType`.
4
4
  class Taro::Types::ListType < Taro::Types::BaseType
5
- extend Taro::Types::Shared::DerivableType
6
5
  extend Taro::Types::Shared::ItemType
7
6
 
8
7
  self.openapi_type = :array
@@ -20,11 +19,10 @@ class Taro::Types::ListType < Taro::Types::BaseType
20
19
  item_type = self.class.item_type
21
20
  object.map { |el| item_type.new(el).coerce_response }
22
21
  end
23
- end
24
22
 
25
- # add shortcut to other types
26
- class Taro::Types::BaseType
27
- def self.array
28
- Taro::Types::ListType.for(self)
23
+ def self.default_openapi_name
24
+ "#{item_type.openapi_name}_List"
29
25
  end
26
+
27
+ define_derived_type :array, 'Taro::Types::ListType'
30
28
  end
@@ -1,6 +1,7 @@
1
1
  class Taro::Types::ObjectTypes::FreeFormType < Taro::Types::ObjectType
2
2
  self.desc = 'An arbitrary, unvalidated Hash or JSON object. Use with care.'
3
3
  self.additional_properties = true
4
+ self.openapi_name = 'FreeForm'
4
5
 
5
6
  def coerce_input
6
7
  object.is_a?(Hash) && object || input_error('must be a Hash')
@@ -1,5 +1,6 @@
1
1
  class Taro::Types::ObjectTypes::NoContentType < Taro::Types::ObjectType
2
2
  self.desc = 'An empty response'
3
+ self.openapi_name = 'NoContent'
3
4
 
4
5
  # render takes no arguments in this case
5
6
  def self.render
@@ -1,4 +1,6 @@
1
1
  class Taro::Types::ObjectTypes::PageInfoType < Taro::Types::ObjectType
2
+ self.openapi_name = 'PageInfo'
3
+
2
4
  field :has_previous_page, type: 'Boolean', null: false, desc: 'Whether there is a previous page of results'
3
5
  field :has_next_page, type: 'Boolean', null: false, desc: 'Whether there is another page of results'
4
6
  field :start_cursor, type: 'String', null: true, desc: 'The first cursor in the current page of results (null if zero results)'
@@ -4,42 +4,32 @@
4
4
  #
5
5
  # The gem rails_cursor_pagination must be installed to use this.
6
6
  #
7
- class Taro::Types::ObjectTypes::PageType < Taro::Types::BaseType
8
- extend Taro::Types::Shared::DerivableType
7
+ class Taro::Types::ObjectTypes::PageType < Taro::Types::ObjectType
9
8
  extend Taro::Types::Shared::ItemType
10
9
 
10
+ def self.derive_from(from_type)
11
+ super
12
+ field(:page, array_of: from_type.name, null: false)
13
+ field(:page_info, type: 'Taro::Types::ObjectTypes::PageInfoType', null: false)
14
+ end
15
+
11
16
  def coerce_input
12
17
  input_error 'PageTypes cannot be used as input types'
13
18
  end
14
19
 
15
- def coerce_response(after:, limit: 20, order_by: nil, order: nil)
16
- list = RailsCursorPagination::Paginator.new(
17
- object, limit:, order_by:, order:, after:
20
+ def self.render(relation, after:, limit: 20, order_by: nil, order: nil)
21
+ result = RailsCursorPagination::Paginator.new(
22
+ relation, limit:, order_by:, order:, after:
18
23
  ).fetch
19
- coerce_paginated_list(list)
20
- end
21
24
 
22
- def coerce_paginated_list(list)
23
- item_type = self.class.item_type
24
- items = list[:page].map do |item|
25
- item_type.new(item[:data]).coerce_response
26
- end
25
+ result[:page].map! { |el| el.fetch(:data) }
27
26
 
28
- {
29
- self.class.items_key => items,
30
- page_info: Taro::Types::ObjectTypes::PageInfoType.new(list[:page_info]).coerce_response,
31
- }
27
+ super(result)
32
28
  end
33
29
 
34
- # support overrides, e.g. based on item_type
35
- def self.items_key
36
- :page
30
+ def self.default_openapi_name
31
+ "#{item_type.openapi_name}_Page"
37
32
  end
38
- end
39
33
 
40
- # add shortcut to other types
41
- class Taro::Types::BaseType
42
- def self.page
43
- Taro::Types::ObjectTypes::PageType.for(self)
44
- end
34
+ define_derived_type :page, 'Taro::Types::ObjectTypes::PageType'
45
35
  end
@@ -1,11 +1,11 @@
1
1
  class Taro::Types::Scalar::ISO8601DateType < Taro::Types::ScalarType
2
2
  self.desc = 'Represents a time as Date in ISO8601 format.'
3
+ self.openapi_name = 'ISO8601Date'
3
4
  self.openapi_type = :string
4
-
5
- PATTERN = /\A\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])\z/
5
+ self.pattern = /\A\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])\z/
6
6
 
7
7
  def coerce_input
8
- if object.instance_of?(String) && object.match?(PATTERN)
8
+ if object.instance_of?(String) && object.match?(pattern)
9
9
  Date.parse(object)
10
10
  else
11
11
  input_error("must be a ISO8601 formatted string")
@@ -1,11 +1,11 @@
1
1
  class Taro::Types::Scalar::ISO8601DateTimeType < Taro::Types::ScalarType
2
2
  self.desc = 'Represents a time as DateTime in ISO8601 format.'
3
+ self.openapi_name = 'ISO8601DateTime'
3
4
  self.openapi_type = :string
4
-
5
- PATTERN = /\A\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])T([01]\d|2[0-3]):[0-5]\d:[0-5]\d(Z|[+-](0[0-9]|1[0-4]):[0-5]\d)\z/
5
+ self.pattern = /\A\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])T([01]\d|2[0-3]):[0-5]\d:[0-5]\d(Z|[+-](0[0-9]|1[0-4]):[0-5]\d)\z/
6
6
 
7
7
  def coerce_input
8
- if object.instance_of?(String) && object.match?(PATTERN)
8
+ if object.instance_of?(String) && object.match?(pattern)
9
9
  DateTime.iso8601(object)
10
10
  else
11
11
  input_error("must be a ISO8601 formatted string")
@@ -2,14 +2,25 @@ class Taro::Types::Scalar::StringType < Taro::Types::ScalarType
2
2
  self.openapi_type = :string
3
3
 
4
4
  def coerce_input
5
- object.instance_of?(String) ? object : input_error('must be a String')
5
+ object.instance_of?(String) || input_error('must be a String')
6
+
7
+ pattern.nil? || pattern.match?(object) ||
8
+ input_error("must match pattern #{pattern.inspect}")
9
+
10
+ object
6
11
  end
7
12
 
8
13
  def coerce_response
9
- case object
10
- when String then object
11
- when Symbol then object.to_s
12
- else response_error('must be a String or Symbol')
13
- end
14
+ str =
15
+ case object
16
+ when String then object
17
+ when Symbol then object.to_s
18
+ else response_error('must be a String or Symbol')
19
+ end
20
+
21
+ pattern.nil? || pattern.match?(str) ||
22
+ response_error("must match pattern #{pattern.inspect}")
23
+
24
+ str
14
25
  end
15
26
  end
@@ -1,5 +1,6 @@
1
1
  class Taro::Types::Scalar::TimestampType < Taro::Types::ScalarType
2
2
  self.desc = 'Represents a time as Time on the server side and UNIX timestamp (integer) on the client side.'
3
+ self.openapi_name = 'Timestamp'
3
4
  self.openapi_type = :integer
4
5
 
5
6
  def coerce_input
@@ -1,22 +1,5 @@
1
- class Taro::Types::Scalar::UUIDv4Type < Taro::Types::ScalarType
1
+ class Taro::Types::Scalar::UUIDv4Type < Taro::Types::Scalar::StringType
2
2
  self.desc = "A UUID v4 string"
3
- self.openapi_type = :string
4
-
5
- PATTERN = /\A\h{8}-?(?:\h{4}-?){3}\h{12}\z/
6
-
7
- def coerce_input
8
- if object.is_a?(String) && object.match?(PATTERN)
9
- object
10
- else
11
- input_error("must be a UUID v4 string")
12
- end
13
- end
14
-
15
- def coerce_response
16
- if object.is_a?(String) && object.match?(PATTERN)
17
- object
18
- else
19
- response_error("must be a UUID v4 string")
20
- end
21
- end
3
+ self.openapi_name = 'UUIDv4'
4
+ self.pattern = /\A\h{8}-?(?:\h{4}-?){3}\h{12}\z/
22
5
  end
@@ -1,5 +1,6 @@
1
1
  # Abstract base class for scalar types, i.e. types without fields.
2
2
  class Taro::Types::ScalarType < Taro::Types::BaseType
3
+ include Taro::Types::Shared::Pattern
3
4
  end
4
5
 
5
6
  module Taro::Types::Scalar
@@ -15,8 +15,8 @@ module Taro::Types::Shared::CustomFieldResolvers
15
15
  end
16
16
 
17
17
  def method_added(name)
18
- if name == :object
19
- raise(Taro::ArgumentError, '#object is a reserved, internally used method name')
18
+ if [:object, :pattern].include?(name)
19
+ raise(Taro::ArgumentError, "##{name} is a reserved, internally used method name")
20
20
  elsif ![:coerce_input, :coerce_response].include?(name) &&
21
21
  !self.name.to_s.start_with?('Taro::Types::')
22
22
  custom_resolvers[name] = true
@@ -0,0 +1,3 @@
1
+ module Taro::Types::Shared::Deprecation
2
+ attr_accessor :deprecated
3
+ end
@@ -0,0 +1,27 @@
1
+ module Taro::Types::Shared::DerivedTypes
2
+ # Adds `name` as a method to all type classes and adds
3
+ # `name`_of as a supported key to the Coercion module.
4
+ # When `name` is called on a type class T, it returns a new subclass
5
+ # S inheriting from `type` and passes T to S::derive_from.
6
+ def define_derived_type(name, type)
7
+ root = Taro::Types::BaseType
8
+ raise ArgumentError, "#{name} is already in use" if root.respond_to?(name)
9
+
10
+ ckey = :"#{name}#{Taro::Types::Coercion.derived_suffix}"
11
+ ckeys = Taro::Types::Coercion.keys
12
+ raise ArgumentError, "#{ckey} is already in use" if ckeys.include?(ckey)
13
+
14
+ root.define_singleton_method(name) do
15
+ derived_types[type] ||= begin
16
+ type_class = Taro::Types::Coercion.call(type:)
17
+ Class.new(type_class).tap { |t| t.derive_from(self) }
18
+ end
19
+ end
20
+
21
+ ckeys << ckey
22
+ end
23
+
24
+ def derived_types
25
+ @derived_types ||= {}
26
+ end
27
+ end
@@ -8,6 +8,8 @@ module Taro::Types::Shared::Errors
8
8
  end
9
9
 
10
10
  def coerce_error_message(msg)
11
- "#{object.inspect} (#{object.class}) is not valid as #{self.class}: #{msg}"
11
+ type_desc = (self.class.name || self.class.superclass.name)
12
+ .sub(/^Taro::Types::.*?([^:]+)Type$/, '\1')
13
+ "#{object.class} is not valid as #{type_desc}: #{msg}"
12
14
  end
13
15
  end
@@ -2,7 +2,7 @@
2
2
  module Taro::Types::Shared::Fields
3
3
  # Field types are set using class name Strings. The respective type classes
4
4
  # are evaluated lazily to allow for circular or recursive type references,
5
- # and to avoid unnecessary eager loading of all types in dev/test envs.
5
+ # and to avoid unnecessary autoloading of all types in dev/test envs.
6
6
  def field(name, **kwargs)
7
7
  defined_at = kwargs[:defined_at] || caller_locations(1..1)[0]
8
8
  validate_name(name, defined_at:)
@@ -27,11 +27,12 @@ module Taro::Types::Shared::Fields
27
27
  [true, false].include?(kwargs[:null]) ||
28
28
  raise(Taro::ArgumentError, "null has to be specified as true or false for field #{name} at #{defined_at}")
29
29
 
30
- (type_keys = (kwargs.keys & Taro::Types::Coercion::KEYS)).size == 1 ||
31
- raise(Taro::ArgumentError, "exactly one of type, array_of, or page_of must be given for field #{name} at #{defined_at}")
30
+ c_keys = Taro::Types::Coercion.keys
31
+ (type_keys = (kwargs.keys & c_keys)).size == 1 ||
32
+ raise(Taro::ArgumentError, "exactly one of #{c_keys.join(', ')} must be given for field #{name} at #{defined_at}")
32
33
 
33
34
  kwargs[type_keys.first].class == String ||
34
- raise(Taro::ArgumentError, "#{type_key} must be a String for field #{name} at #{defined_at}")
35
+ raise(Taro::ArgumentError, "#{type_keys.first} must be a String for field #{name} at #{defined_at}")
35
36
  end
36
37
 
37
38
  def validate_no_override(name, defined_at:)
@@ -46,7 +47,7 @@ module Taro::Types::Shared::Fields
46
47
  def evaluate_field_defs
47
48
  field_defs.transform_values do |field_def|
48
49
  type = Taro::Types::Coercion.call(field_def)
49
- Taro::Types::Field.new(**field_def.except(*Taro::Types::Coercion::KEYS), type:)
50
+ Taro::Types::Field.new(**field_def.except(*Taro::Types::Coercion.keys), type:)
50
51
  end
51
52
  end
52
53
 
@@ -6,6 +6,7 @@ module Taro::Types::Shared::ItemType
6
6
  item_type.nil? || new_type == item_type || raise_mixed_types(new_type)
7
7
  @item_type = new_type
8
8
  end
9
+ alias_method :derive_from, :item_type=
9
10
 
10
11
  def raise_mixed_types(new_type)
11
12
  raise Taro::ArgumentError, <<~MSG
@@ -3,6 +3,8 @@ module Taro::Types::Shared::ObjectCoercion
3
3
  def coerce_input
4
4
  self.class.fields.transform_values do |field|
5
5
  field.value_for_input(object)
6
+ rescue Taro::Error => e
7
+ raise_enriched_coercion_error(e, field)
6
8
  end
7
9
  end
8
10
 
@@ -11,6 +13,17 @@ module Taro::Types::Shared::ObjectCoercion
11
13
  object_is_hash = object.is_a?(Hash)
12
14
  self.class.fields.transform_values do |field|
13
15
  field.value_for_response(object, context: self, object_is_hash:)
16
+ rescue Taro::Error => e
17
+ raise_enriched_coercion_error(e, field)
14
18
  end
15
19
  end
20
+
21
+ def raise_enriched_coercion_error(error, field)
22
+ # The indentation is on purpose. These errors can be recursively rescued
23
+ # and re-raised by a tree of object types, which should be made apparent.
24
+ raise error.class, <<~MSG
25
+ Failed to read #{self.class.name} field `#{field.name}` from #{object.class}:
26
+ #{error.message.lines.map { |line| " #{line}" }.join}
27
+ MSG
28
+ end
16
29
  end
@@ -5,13 +5,19 @@ module Taro::Types::Shared::OpenAPIName
5
5
  @openapi_name ||= default_openapi_name
6
6
  end
7
7
 
8
+ def openapi_name?
9
+ !!openapi_name
10
+ rescue Taro::Error
11
+ false
12
+ end
13
+
8
14
  def openapi_name=(arg)
9
15
  arg.nil? || arg.is_a?(String) ||
10
16
  raise(Taro::ArgumentError, 'openapi_name must be a String')
11
17
  @openapi_name = arg
12
18
  end
13
19
 
14
- def default_openapi_name # rubocop:disable Metrics
20
+ def default_openapi_name
15
21
  if self < Taro::Types::EnumType ||
16
22
  self < Taro::Types::InputType ||
17
23
  self < Taro::Types::ObjectType
@@ -19,12 +25,8 @@ module Taro::Types::Shared::OpenAPIName
19
25
  raise(Taro::Error, 'openapi_name must be set for anonymous type classes')
20
26
  elsif self < Taro::Types::ScalarType
21
27
  openapi_type
22
- elsif self < Taro::Types::ListType
23
- "#{item_type.openapi_name}_List"
24
- elsif self < Taro::Types::ObjectTypes::PageType
25
- "#{item_type.openapi_name}_Page"
26
28
  else
27
- raise NotImplementedError, 'no default_openapi_name for this type'
29
+ raise Taro::Error, 'no default_openapi_name implemented for this type'
28
30
  end
29
31
  end
30
32
  end
@@ -0,0 +1,69 @@
1
+ module Taro::Types::Shared::Pattern
2
+ def pattern
3
+ self.class.pattern
4
+ end
5
+
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ attr_reader :pattern, :openapi_pattern
12
+
13
+ def pattern=(regexp)
14
+ openapi_type == :string ||
15
+ raise(Taro::RuntimeError, 'pattern requires openapi_type :string')
16
+
17
+ @pattern = regexp
18
+ @openapi_pattern = Taro::Types::Shared::Pattern.to_es262(regexp)
19
+ end
20
+ end
21
+
22
+ def self.to_es262(regexp)
23
+ validate(regexp).source.gsub(
24
+ /#{NOT_ESCAPED}\\[Ahz]/,
25
+ { '\\A' => '^', '\\h' => '[0-9a-fA-F]', '\\z' => '$' }
26
+ )
27
+ end
28
+
29
+ def self.validate(regexp)
30
+ validate_no_flags(regexp)
31
+ validate_not_empty(regexp)
32
+ validate_no_advanced_syntax(regexp)
33
+ regexp
34
+ end
35
+
36
+ def self.validate_no_flags(regexp)
37
+ (flags = regexp.inspect[%r{/\w+\z}]) &&
38
+ raise(Taro::ArgumentError, "pattern flags (#{flags}) are not supported")
39
+ end
40
+
41
+ def self.validate_not_empty(regexp)
42
+ regexp.source.empty? &&
43
+ raise(Taro::ArgumentError, 'pattern cannot be empty')
44
+ end
45
+
46
+ def self.validate_no_advanced_syntax(regexp)
47
+ return unless (match = regexp.source.match(ADVANCED_RUBY_REGEXP_SYNTAX_REGEXP))
48
+
49
+ feature = match.named_captures.find { |k, v| break k if v }
50
+ raise Taro::ArgumentError, <<~MSG
51
+ pattern uses non-JS syntax #{match} (#{feature}) at index #{match.begin(0)}
52
+ MSG
53
+ end
54
+
55
+ NOT_ESCAPED = /(?<!\\)(?:\\\\)*\K/
56
+
57
+ # This is not 100% accurate, e.g. /[?+]/ is a false positive, but it should be
58
+ # good enough so we don't need regexp_parser or js_regex as a dependency.
59
+ ADVANCED_RUBY_REGEXP_SYNTAX_REGEXP = /
60
+ #{NOT_ESCAPED}
61
+ (?:
62
+ (?<a special group or lookaround> \(\?[^:] )
63
+ | (?<a Ruby-specific escape> \\[a-zA-Z&&[^bBdDsSwWAzfhnrv]] )
64
+ | (?<an advanced quantifier> [?*+}][?+] )
65
+ | (?<a nested set> \[[^\]]*(?<!\\)\[ )
66
+ | (?<a set intersection> && )
67
+ )
68
+ /x
69
+ end
@@ -1,8 +1,8 @@
1
- # The `::render` method is intended for use in controllers.
2
- # Special types (e.g. PageType) may accept kwargs for `#coerce_response`.
3
1
  module Taro::Types::Shared::Rendering
4
- def render(object, opts = {})
5
- result = new(object).coerce_response(**opts)
2
+ # The `::render` method is intended for use in controllers.
3
+ # Overrides of this method must call super.
4
+ def render(object)
5
+ result = new(object).coerce_response
6
6
  self.last_render = [self, result.__id__]
7
7
  result
8
8
  end
data/lib/taro/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # :nocov:
2
2
  module Taro
3
- VERSION = "1.1.0"
3
+ VERSION = "1.3.0"
4
4
  end
data/tasks/benchmark.rake CHANGED
@@ -13,7 +13,7 @@ task :benchmark do
13
13
  field :version, type: 'Float', null: false
14
14
  end
15
15
 
16
- type = Taro::Types::ListType.for(item_type)
16
+ type = item_type.array
17
17
 
18
18
  # 143.889k (± 2.7%) i/s - 723.816k in 5.034247s
19
19
  Benchmark.ips do |x|
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: taro
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janosch Müller
8
+ - Johannes Opper
8
9
  autorequire:
9
10
  bindir: exe
10
11
  cert_chain: []
11
- date: 2024-11-16 00:00:00.000000000 Z
12
+ date: 2024-11-25 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: rack
@@ -27,7 +28,6 @@ dependencies:
27
28
  description: This library provides an object-based type system for RESTful Ruby APIs,
28
29
  with built-in parameter parsing, response rendering, and OpenAPI schema export.
29
30
  email:
30
- - janosch84@gmail.com
31
31
  executables: []
32
32
  extensions: []
33
33
  extra_rdoc_files: []
@@ -90,7 +90,8 @@ files:
90
90
  - lib/taro/types/shared.rb
91
91
  - lib/taro/types/shared/additional_properties.rb
92
92
  - lib/taro/types/shared/custom_field_resolvers.rb
93
- - lib/taro/types/shared/derivable_types.rb
93
+ - lib/taro/types/shared/deprecation.rb
94
+ - lib/taro/types/shared/derived_types.rb
94
95
  - lib/taro/types/shared/description.rb
95
96
  - lib/taro/types/shared/errors.rb
96
97
  - lib/taro/types/shared/fields.rb
@@ -98,6 +99,7 @@ files:
98
99
  - lib/taro/types/shared/object_coercion.rb
99
100
  - lib/taro/types/shared/openapi_name.rb
100
101
  - lib/taro/types/shared/openapi_type.rb
102
+ - lib/taro/types/shared/pattern.rb
101
103
  - lib/taro/types/shared/rendering.rb
102
104
  - lib/taro/version.rb
103
105
  - tasks/benchmark.rake
@@ -125,7 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
127
  - !ruby/object:Gem::Version
126
128
  version: '0'
127
129
  requirements: []
128
- rubygems_version: 3.5.16
130
+ rubygems_version: 3.5.22
129
131
  signing_key:
130
132
  specification_version: 4
131
133
  summary: Typed Api using Ruby Objects.
@@ -1,9 +0,0 @@
1
- module Taro::Types::Shared::DerivableType
2
- def for(type)
3
- derived_types[type] ||= Class.new(self).tap { |t| t.item_type = type }
4
- end
5
-
6
- def derived_types
7
- @derived_types ||= {}
8
- end
9
- end