taro 2.2.0 → 2.4.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: d73348290387aeef3de0558f6557af6572326043dff427ee295386a12e8bf6a6
4
- data.tar.gz: 89b0a6c8efae50665d43385b0b4182fba6b07e5f97e933d3add5d0e1ba19f421
3
+ metadata.gz: 9c7750f1403fa65519d22915908d5aeb6cf5bbeec5497d68369bf5a4e37fb0e8
4
+ data.tar.gz: 0b6d6b440c1f4fcdb965af8fb1aaf2d26f2ca3a9d2a2fb924bfe9456cb811090
5
5
  SHA512:
6
- metadata.gz: 67a0d0a5d2464c7ec6954143c95311b92b1a4a78041b298449815b1e57340de7091ac89235fd9e674ee1d3e99ed531049e99207ec62498fa94687b7c91956edd
7
- data.tar.gz: '0768d75380ee9b18de093bebf1952b4eeac1e7965fea8c3a0cb5fbec3ef84ec6a27d5e141a837bf6a0f9d698e5b2ceff9ba2e30a33143edd434ebd25812a933a'
6
+ metadata.gz: 1f37ffe80f9f14ef49bd3131f0a64f5254219c4851de19ec6523143d508440cb263741c2ca56858eeff917cf0a4d1002b642b4e6f87498e47b62b3ffc5fbc4c8
7
+ data.tar.gz: 59fbfef1126825880c2d5b64b1d2a3799c25502d094f9758fa9181d7e1e3fa44b2b2504ccbf24c1d18925a5e53ceef1e1ca309cd8980f8950d385d1f3197aadb
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [2.4.0] - 2025-03-11
4
+
5
+ ### Added
6
+
7
+ - `PageWithTotalCountType` for paginated responses with a total count
8
+
9
+ ## [2.3.0] - 2025-02-24
10
+
11
+ ### Added
12
+
13
+ - `Taro.config.raise_for_undeclared_params`
14
+
3
15
  ## [2.2.0] - 2025-02-22
4
16
 
5
17
  ### Added
data/README.md CHANGED
@@ -65,7 +65,7 @@ class BikesController < ApplicationController
65
65
 
66
66
  # Support for arrays and paginated lists is built-in.
67
67
  api 'List all bikes'
68
- returns code: :ok, array_of: 'BikeType', desc: 'list of bikes'
68
+ returns code: :ok, array_of: 'BikeType', desc: 'List of bikes'
69
69
  def index
70
70
  render json: BikeType.array.render(Bike.all)
71
71
  end
@@ -89,9 +89,6 @@ class BikeType < ObjectType
89
89
  # Fields can reference other types and arrays of values
90
90
  field :users, array_of: 'UserType', null: false
91
91
 
92
- # Pagination is built-in for big lists
93
- field :parts, page_of: 'PartType', null: false
94
-
95
92
  # Custom methods can be chosen to resolve fields
96
93
  field :has_brand, type: 'Boolean', null: false, method: :brand?
97
94
 
@@ -140,6 +137,14 @@ Requests are automatically validated to match the declared input schema, unless
140
137
  Taro.config.parse_params = false
141
138
  ```
142
139
 
140
+ There is also an option to raise an error if any undeclared params are submitted:
141
+
142
+ ```ruby
143
+ Taro.config.raise_for_undeclared_params = true
144
+ ```
145
+
146
+ This option is similar to `action_on_unpermitted_parameters = :raise` in Rails and is most useful in dev and test environments. The railtie enables it automatically in these environments.
147
+
143
148
  #### Response validation
144
149
 
145
150
  Responses are automatically validated to have used the correct type for rendering, which guarantees that they match the declaration. This means you have to use the types to render complex responses, and manually building a Hash that conforms to the schema will raise an error. Primitive/scalar types are an exception, e.g. you *can* do:
@@ -265,6 +270,32 @@ class BikeType < ObjectType
265
270
  end
266
271
  ```
267
272
 
273
+ ### Pagination
274
+
275
+ Use `page_of:` to declare a paginated response. Call `page.render` on a type to render a page of records.
276
+
277
+ ```ruby
278
+ api 'List all bikes'
279
+ param :cursor, type: 'String', null: true, desc: 'Show bikes after this cursor'
280
+ returns code: :ok, page_of: 'BikeType', desc: 'A page of bikes'
281
+ def index
282
+ render json: BikeType.page.render(Bike.all, after: params[:cursor])
283
+ end
284
+ ```
285
+
286
+ By default, the response does not include a total count. To include it, use `page_with_total_count`:
287
+
288
+ ```ruby
289
+ api 'List all bikes'
290
+ param :cursor, type: 'String', null: true, desc: 'Show bikes after this cursor'
291
+ returns code: :ok, page_with_total_count_of: 'BikeType', desc: 'A page of bikes'
292
+ def index
293
+ render json: BikeType.page_with_total_count.render(Bike.all, after: params[:cursor])
294
+ end
295
+ ```
296
+
297
+ See also: [Derived types](#derived-types).
298
+
268
299
  ## FAQ
269
300
 
270
301
  ### How do I render API docs?
@@ -335,6 +366,7 @@ Why e.g. `field :id, type: 'UUID'` instead of `field :id, type: UUID`?
335
366
 
336
367
  The purpose of this is to reduce unnecessary autoloading of the whole type dependency tree in dev and test environments.
337
368
 
369
+ <a name="derived-types"></a>
338
370
  ### Can I define my own derived types like `page_of` or `array_of`?
339
371
 
340
372
  Yes.
@@ -367,7 +399,6 @@ end
367
399
 
368
400
  ## Possible future features
369
401
 
370
- - warning/raising for undeclared input params (currently they are ignored)
371
402
  - usage without rails is possible but not convenient yet
372
403
  - rspec matchers for testing
373
404
  - sum types
data/lib/taro/config.rb CHANGED
@@ -5,6 +5,7 @@ module Taro::Config
5
5
  :export_format,
6
6
  :export_path,
7
7
  :parse_params,
8
+ :raise_for_undeclared_params,
8
9
  :validate_response,
9
10
  )
10
11
 
@@ -14,6 +15,7 @@ module Taro::Config
14
15
  self.export_format = :yaml
15
16
  self.export_path = 'api.yml'
16
17
  self.parse_params = true
18
+ self.raise_for_undeclared_params = false # may be overridden by railtie
17
19
  self.validate_response = true
18
20
  end
19
21
 
@@ -5,7 +5,7 @@ class Taro::Declaration
5
5
  attr_reader :desc, :summary, :params, :return_defs, :return_descriptions, :tags
6
6
 
7
7
  def initialize(for_klass = nil)
8
- @params = Class.new(Taro::Types::InputType)
8
+ @params = Class.new(Taro::Types::RailsParamsType)
9
9
  @return_defs = {}
10
10
  @return_descriptions = {}
11
11
 
@@ -1,5 +1,7 @@
1
1
  class Taro::Rails::Railtie < ::Rails::Railtie
2
2
  initializer("taro") do |app|
3
+ Taro.config.raise_for_undeclared_params = %w[development test].include?(Rails.env)
4
+
3
5
  # The `:action_controller` hook fires for both ActionController::API
4
6
  # and ActionController::Base, executing the block in their context.
5
7
  ActiveSupport.on_load(:action_controller) do
@@ -13,14 +13,16 @@ class Taro::Types::ObjectTypes::PageType < Taro::Types::ResponseType
13
13
  field(:page_info, type: 'Taro::Types::ObjectTypes::PageInfoType', null: false)
14
14
  end
15
15
 
16
- def self.render(relation, after:, limit: 20, order_by: nil, order: nil)
16
+ def self.render(relation, **)
17
+ super(paginate(relation, **))
18
+ end
19
+
20
+ def self.paginate(relation, after:, limit: 20, order_by: nil, order: nil)
17
21
  result = RailsCursorPagination::Paginator.new(
18
22
  relation, limit:, order_by:, order:, after:
19
23
  ).fetch
20
-
21
24
  result[:page].map! { |el| el.fetch(:data) }
22
-
23
- super(result)
25
+ result
24
26
  end
25
27
 
26
28
  def self.default_openapi_name
@@ -0,0 +1,26 @@
1
+ # This is an expanded `PageType` that adds a `total_count` field
2
+ # to show the total number of records in the paginated relation.
3
+ # It is not recommended for very large relations where counting might be slow.
4
+ #
5
+ # Usage:
6
+ # - `returns code: :ok, page_with_total_count_of: 'UserType'`
7
+ # - `UserType.page_with_total_count.render(User.all, after: params[:cursor])`
8
+ #
9
+ # The gem rails_cursor_pagination must be installed to use this.
10
+ #
11
+ class Taro::Types::ObjectTypes::PageWithTotalCountType < Taro::Types::ObjectTypes::PageType
12
+ def self.derive_from(from_type)
13
+ super
14
+ field(:total_count, type: 'Integer', null: false)
15
+ end
16
+
17
+ def self.paginate(relation, **)
18
+ super.merge(total_count: relation.count)
19
+ end
20
+
21
+ def self.default_openapi_name
22
+ "#{item_type.openapi_name}_PageWithTotalCount"
23
+ end
24
+
25
+ define_derived_type :page_with_total_count, 'Taro::Types::ObjectTypes::PageWithTotalCountType'
26
+ end
@@ -0,0 +1,10 @@
1
+ require_relative 'object_type'
2
+
3
+ # Abstract base class for rails declaration params. Internal use only.
4
+ class Taro::Types::RailsParamsType < Taro::Types::InputType
5
+ # Skip validation of base params because they contain rails "additions"
6
+ # like controller, action, routing-related stuff, de-nested values, etc.
7
+ def validate_no_undeclared_params?
8
+ false
9
+ end
10
+ end
@@ -1,6 +1,7 @@
1
1
  # Provides input and response handling for types with fields.
2
2
  module Taro::Types::Shared::ObjectCoercion
3
3
  def coerce_input
4
+ validate_no_undeclared_params
4
5
  self.class.fields.transform_values do |field|
5
6
  field.value_for_input(object)
6
7
  end
@@ -13,4 +14,17 @@ module Taro::Types::Shared::ObjectCoercion
13
14
  field.value_for_response(object, context: self, object_is_hash:)
14
15
  end
15
16
  end
17
+
18
+ private
19
+
20
+ def validate_no_undeclared_params
21
+ return unless validate_no_undeclared_params?
22
+
23
+ undeclared = object.to_h.keys.map(&:to_sym) - self.class.send(:field_defs).keys
24
+ undeclared.any? && input_error("Undeclared params: #{undeclared.join(', ')}")
25
+ end
26
+
27
+ def validate_no_undeclared_params?
28
+ Taro.config.raise_for_undeclared_params
29
+ end
16
30
  end
@@ -1,10 +1,8 @@
1
1
  module Taro::Types::Shared::Rendering
2
2
  # The `::render` method is intended for use in controllers.
3
3
  # Overrides of this method must call super.
4
- def render(object, cache_attrs = {})
5
- result = Taro::Cache.call(object, **cache_attrs) do
6
- new(object).cached_coerce_response
7
- end
4
+ def render(object)
5
+ result = new(object).cached_coerce_response
8
6
  self.last_render = [type_class, result.__id__]
9
7
  result
10
8
  end
data/lib/taro/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # :nocov:
2
2
  module Taro
3
- VERSION = "2.2.0"
3
+ VERSION = "2.4.0"
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: taro
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janosch Müller
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2025-02-22 00:00:00.000000000 Z
12
+ date: 2025-03-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
@@ -89,6 +89,8 @@ files:
89
89
  - lib/taro/types/object_types/no_content_type.rb
90
90
  - lib/taro/types/object_types/page_info_type.rb
91
91
  - lib/taro/types/object_types/page_type.rb
92
+ - lib/taro/types/object_types/page_with_total_count_type.rb
93
+ - lib/taro/types/rails_params_type.rb
92
94
  - lib/taro/types/response_type.rb
93
95
  - lib/taro/types/scalar/boolean_type.rb
94
96
  - lib/taro/types/scalar/float_type.rb