action_spec 1.0.0 → 1.2.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: 6fccbffa8f14fb73bec7eade75add8f70d0371741999475ef292ed980d985ddc
4
- data.tar.gz: 20f1b1d3b86826b1cb9d2ed3d72f0259ccd28c397790bbf8bc8fb8766cab0329
3
+ metadata.gz: a01ddc76d37b180c1564c96963ccce0bbe32cc9efd4c29deede6e1c3e7a8f7c7
4
+ data.tar.gz: a97df2927f2f5df0fce593b2ff55fa123d983ab6023690326bce9634ead51b31
5
5
  SHA512:
6
- metadata.gz: 552c83ccebe73754cb2b6e160cbfb8774ea818e22e665a1ba6df322c5051e5ff7902b533d82c2c2a93f94fd0f631264e2e344521b67a3582576a27b5f4ce2144
7
- data.tar.gz: 9585aa85edeb6654d00e06b7a0e2478aed741675d3d90e0b60c46ac99d267e68d13bbe30060f6b163b1869ac508b92c4509b8b0b0f69f3ae7d0c386da875afce
6
+ metadata.gz: e5ba8be1ae92c054686603867e11cda5b1af8482113ae2fcd4b36303e9631f1bdfd3c915b5e724a84dcd3c20c29f2a3f46bb92f74654716c3037625125d00820
7
+ data.tar.gz: 0d8327b6f04162da2ceedf697ac0a0e3dde789584870ebde8c2769ac43e6bb00836a81386b61d519b2472cd4acd8ffaf19472bbd0490c7911f9295c5216d4ed4
data/README.md CHANGED
@@ -6,7 +6,7 @@ Concise and Powerful API Documentation Solution for Rails.
6
6
 
7
7
  - OpenAPI version: `v3.2.0`
8
8
  - Requires: Ruby 3.1+ and Rails 7.0+
9
- - Note: this project was implemented with Codex in about 2 hours, has not yet been manually reviewed, and has not been validated in production. It does, however, come with fairly detailed RSpec tests generated with Codex.
9
+ - Note: this project was implemented with Codex in about 3 hours, has not yet been manually reviewed, and has not been validated in production. It does, however, come with fairly detailed RSpec tests generated with Codex.
10
10
 
11
11
  ## Table Of Contents
12
12
 
@@ -23,9 +23,8 @@ Concise and Powerful API Documentation Solution for Rails.
23
23
  - [Type And Boundary Matrix](#type-and-boundary-matrix)
24
24
  - [Parameter Validation And Type Coercion](#parameter-validation-and-type-coercion)
25
25
  - [Validation Flow](#validation-flow)
26
- - [Reading Validated Values With `px`](#reading-validated-values-with-px)
26
+ - [Reading Processed Values With `px`](#reading-processed-values-with-px)
27
27
  - [Errors](#errors)
28
- - [Default Rescue Behavior](#default-rescue-behavior)
29
28
  - [Configuration And I18n](#configuration-and-i18n)
30
29
  - [Configuration](#configuration)
31
30
  - [I18n](#i18n)
@@ -106,6 +105,7 @@ bin/rails action_spec:gen \
106
105
  Notes:
107
106
 
108
107
  - only routed controller actions with a matching `doc` declaration are included
108
+ - endpoints with `openapi false` are skipped even when routed
109
109
  - Rails paths such as `/users/:id(.:format)` are rendered as `/users/{id}`
110
110
  - parameters, request bodies, and response descriptions are generated from the current DSL support
111
111
  - if config and environment variables do not provide `TITLE` or `VERSION`, ActionSpec falls back to application-derived defaults
@@ -150,19 +150,27 @@ end
150
150
 
151
151
  ```ruby
152
152
  class ApplicationController < ActionController::API
153
- doc_dry %i[show update destroy] do
153
+ doc_dry(%i[show update destroy]) {
154
154
  path! :id, Integer
155
- end
155
+ }
156
156
 
157
- doc_dry :index do
157
+ doc_dry(:index) {
158
158
  query :page, Integer, default: 1
159
159
  query :per, Integer, default: 20
160
- end
160
+ }
161
161
  end
162
162
  ```
163
163
 
164
164
  All matching dry blocks are applied before the action-specific `doc`.
165
165
 
166
+ You can also opt an action out of OpenAPI generation from either `doc` or `doc_dry`:
167
+
168
+ ```ruby
169
+ doc {
170
+ openapi false
171
+ }
172
+ ```
173
+
166
174
  ### DSL Reference
167
175
 
168
176
  #### Parameter
@@ -238,6 +246,33 @@ data :file, File
238
246
 
239
247
  For `body/body!`, `json/json!`, and `form/form!`, the bang form is currently kept for DSL compatibility. At runtime they all contribute to the same body contract, and root-body requiredness is not yet enforced as a separate rule.
240
248
 
249
+ #### OpenAPI
250
+
251
+ ```ruby
252
+ openapi false
253
+ ```
254
+
255
+ Use this when an action should stay out of the generated OpenAPI document. It also works inside `doc_dry`.
256
+
257
+ #### Scope
258
+
259
+ Use `scope` when you want a grouped view that spans multiple request locations:
260
+
261
+ ```ruby
262
+ doc {
263
+ scope(:user) {
264
+ query :user_id, Integer
265
+ form data: { name: String }
266
+ }
267
+ }
268
+ ```
269
+
270
+ Then read it from `px.scope`:
271
+
272
+ ```ruby
273
+ px.scope[:user] # => { user_id: 1, name: "Tom" }
274
+ ```
275
+
241
276
  #### Response
242
277
 
243
278
  ```ruby
@@ -339,7 +374,7 @@ end
339
374
 
340
375
  `User.schemas` returns a hash that can be passed directly into `form data:`, `json data:`, or `body`.
341
376
 
342
- By default it includes all model fields:
377
+ By default, it includes all model fields:
343
378
 
344
379
  ```ruby
345
380
  User.schemas
@@ -409,6 +444,8 @@ Example:
409
444
  - DSL says `query :page, Integer`
410
445
  - result: `px[:page] == "2"`
411
446
 
447
+ You can safely put this hook on a base controller. If the current action has no matching `doc`, ActionSpec skips validation and returns an empty `px`.
448
+
412
449
  #### `validate_and_coerce_params!`
413
450
 
414
451
  Validates and coerces values before exposing them on `px`.
@@ -423,36 +460,41 @@ Example:
423
460
  - DSL says `query :page, Integer`
424
461
  - result: `px[:page] == 2`
425
462
 
426
- ### Reading Validated Values With `px`
463
+ This hook also skips actions without a matching `doc`, so it is safe to declare on a shared base controller.
427
464
 
428
- `px` is a hash.
465
+ ### Reading Processed Values With `px`
466
+
467
+ `px` stores the processed values produced by ActionSpec. With `validate_params!` they stay raw; with `validate_and_coerce_params!` they are coerced values.
429
468
 
430
469
  ```ruby
431
470
  px[:id]
432
471
  px[:page]
433
472
  px[:profile][:nickname]
434
473
  px.to_h
474
+ px.scope[:user]
435
475
  ```
436
476
 
437
- It also includes grouped buckets:
477
+ Grouped views live under `px.scope`:
438
478
 
439
479
  ```ruby
440
- px[:path]
441
- px[:query]
442
- px[:body]
443
- px[:headers]
444
- px[:cookies]
480
+ px.scope[:path]
481
+ px.scope[:query]
482
+ px.scope[:body]
483
+ px.scope[:headers]
484
+ px.scope[:cookies]
445
485
  ```
446
486
 
447
487
  Notes:
448
488
 
449
- - root values from path/query/body are also flattened into `px[:name]`
489
+ - every declared field from path/query/body is also flattened into the top-level `px[:field]`
490
+ - custom `scope(:name)` buckets are also exposed through `px.scope[:name]`
491
+ - headers and cookies stay inside their own grouped buckets; for example, `px[:Authorization]` is not a top-level shortcut
450
492
  - header keys are stored in lowercase dashed form, but reading remains compatible with original forms such as `Authorization` and `HTTP_AUTHORIZATION`, for example:
451
493
 
452
494
  ```ruby
453
- px[:headers][:authorization]
454
- px[:headers]["Authorization"]
455
- px[:headers]["HTTP_AUTHORIZATION"]
495
+ px.scope[:headers][:authorization]
496
+ px.scope[:headers]["Authorization"]
497
+ px.scope[:headers]["HTTP_AUTHORIZATION"]
456
498
  ```
457
499
 
458
500
  - original `params` are not mutated
@@ -461,60 +503,43 @@ px[:headers]["HTTP_AUTHORIZATION"]
461
503
 
462
504
  Validation errors are stored in `ActiveModel::Errors`.
463
505
 
464
- If invalid parameters are not rescued, ActionSpec raises `ActionSpec::InvalidParameters`:
506
+ When validation fails, ActionSpec raises `ActionSpec::InvalidParameters`:
465
507
 
466
508
  ```ruby
467
509
  begin
468
510
  validate_and_coerce_params!
469
511
  rescue ActionSpec::InvalidParameters => error
512
+ error.message
470
513
  error.errors.full_messages
471
514
  end
472
515
  ```
473
516
 
474
517
  The exception also keeps the full validation result on `error.result` and `error.parameters`.
518
+ ActionSpec does not render a default error response for you, so each application can decide its own rescue and JSON format.
475
519
 
476
- ### Default Rescue Behavior
477
-
478
- By default, when a controller raises `ActionSpec::InvalidParameters`, ActionSpec catches it automatically and returns a JSON error response:
479
-
480
- ```ruby
481
- rescue_from ActionSpec::InvalidParameters
482
- ```
520
+ `error.message` is built from `error.errors.full_messages.to_sentence`, so it follows normal `ActiveModel::Errors` wording:
483
521
 
484
- The default JSON response is:
522
+ - single error: `"Page is required"`
523
+ - multiple errors: `"Page is required and Birthday must be a valid date"`
524
+ - fallback when no detailed errors are present: `"Invalid parameters"`
485
525
 
486
- ```json
487
- {
488
- "errors": {
489
- "page": ["Page is required"]
490
- }
491
- }
492
- ```
526
+ Use `error.errors` when you need structured details, and `error.message` when you only need a single summary string.
493
527
 
494
528
  ## Configuration And I18n
495
529
 
496
530
  ### Configuration
497
531
 
498
532
  ```ruby
499
- ActionSpec.configure do |config|
500
- config.rescue_invalid_parameters = true
501
- config.invalid_parameters_status = :bad_request
533
+ ActionSpec.configure { |config|
502
534
  config.open_api_output = "docs/openapi.yml"
503
535
  config.open_api_title = "My API"
504
536
  config.open_api_version = "2026.03"
505
537
  config.open_api_server_url = "https://api.example.com"
506
538
 
507
- config.error_messages[:invalid_type] = ->(_attribute, options) do
539
+ config.error_messages[:invalid_type] = ->(_attribute, options) {
508
540
  "should be coercible to #{options.fetch(:expected)}"
509
- end
510
-
511
- config.invalid_parameters_renderer = ->(controller, error) do
512
- controller.render json: {
513
- code: "invalid_parameters",
514
- errors: error.errors.to_hash(full_messages: true)
515
- }, status: :unprocessable_entity
516
- end
517
- end
541
+ }
542
+ }
518
543
  ```
519
544
 
520
545
  Available config keys:
@@ -523,18 +548,6 @@ Available config keys:
523
548
  Default: `ActionSpec::InvalidParameters`.
524
549
  Controls which exception class is raised when validation fails.
525
550
 
526
- - `invalid_parameters_status`
527
- Default: `:bad_request`.
528
- Controls the HTTP status used by the built-in `rescue_from` renderer.
529
-
530
- - `rescue_invalid_parameters`
531
- Default: `true`.
532
- When this option is enabled, controllers use the default `rescue_from ActionSpec::InvalidParameters`.
533
-
534
- - `invalid_parameters_renderer`
535
- Default: `nil`.
536
- Lets you replace the built-in JSON error response. It can be a proc receiving `(controller, error)`, or a block executed in controller context.
537
-
538
551
  - `error_messages`
539
552
  Default: `{}`.
540
553
  Lets you override error messages by error type, or by attribute plus error type.
@@ -557,7 +570,7 @@ Available config keys:
557
570
 
558
571
  ### I18n
559
572
 
560
- ActionSpec loads its own locale files and uses `ActiveModel::Errors`, so you can override both messages and attribute names:
573
+ ActionSpec uses `ActiveModel::Errors`, so you can override both messages and attribute names:
561
574
 
562
575
  ```yml
563
576
  en:
@@ -574,13 +587,13 @@ en:
574
587
  You can also override messages per error type or per attribute in Ruby:
575
588
 
576
589
  ```ruby
577
- ActionSpec.configure do |config|
590
+ ActionSpec.configure { |config|
578
591
  config.error_messages[:required] = "must be present"
579
592
  config.error_messages[:invalid_type] = ->(_attribute, options) { "must be a valid #{options.fetch(:expected)}" }
580
593
  config.error_messages[:page] = {
581
594
  required: "page is mandatory"
582
595
  }
583
- end
596
+ }
584
597
  ```
585
598
 
586
599
  ## What Is Not Implemented Yet
@@ -605,7 +618,8 @@ end
605
618
  - richer schema keywords beyond the current subset, including nullable/blank semantics, object-level constraints, and composition keywords such as `oneOf`, `anyOf`, `allOf`, and `not`
606
619
 
607
620
  ## Contributing
608
- .
621
+
622
+ Contributions / Issues are welcome.
609
623
 
610
624
  ## License
611
625
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -2,16 +2,12 @@
2
2
 
3
3
  module ActionSpec
4
4
  class Configuration
5
- attr_accessor :invalid_parameters_exception_class, :invalid_parameters_status, :rescue_invalid_parameters,
6
- :invalid_parameters_renderer, :open_api_output, :open_api_title, :open_api_version,
5
+ attr_accessor :invalid_parameters_exception_class, :open_api_output, :open_api_title, :open_api_version,
7
6
  :open_api_server_url
8
7
  attr_reader :error_messages
9
8
 
10
9
  def initialize
11
10
  @invalid_parameters_exception_class = ActionSpec::InvalidParameters
12
- @invalid_parameters_status = :bad_request
13
- @rescue_invalid_parameters = true
14
- @invalid_parameters_renderer = nil
15
11
  @open_api_output = "docs/openapi.yml"
16
12
  @open_api_title = nil
17
13
  @open_api_version = nil
@@ -33,9 +29,6 @@ module ActionSpec
33
29
  def dup
34
30
  self.class.new.tap do |copy|
35
31
  copy.invalid_parameters_exception_class = invalid_parameters_exception_class
36
- copy.invalid_parameters_status = invalid_parameters_status
37
- copy.rescue_invalid_parameters = rescue_invalid_parameters
38
- copy.invalid_parameters_renderer = invalid_parameters_renderer
39
32
  copy.open_api_output = open_api_output
40
33
  copy.open_api_title = open_api_title
41
34
  copy.open_api_version = open_api_version
@@ -7,6 +7,7 @@ module ActionSpec
7
7
 
8
8
  def initialize(endpoint)
9
9
  @endpoint = endpoint
10
+ @scopes = []
10
11
  end
11
12
 
12
13
  PARAM_LOCATIONS.each do |location_name|
@@ -55,6 +56,17 @@ module ActionSpec
55
56
  add_body(:form, { name => options.merge(type:) })
56
57
  end
57
58
 
59
+ def scope(name, &block)
60
+ scopes.push(name.to_sym)
61
+ instance_exec(&block)
62
+ ensure
63
+ scopes.pop
64
+ end
65
+
66
+ def openapi(enabled)
67
+ endpoint.options[:openapi] = enabled
68
+ end
69
+
58
70
  def response(code, description = nil, media_type = nil, desc: nil, **options)
59
71
  endpoint.add_response(
60
72
  code,
@@ -73,10 +85,11 @@ module ActionSpec
73
85
  private
74
86
 
75
87
  attr_reader :endpoint
88
+ attr_reader :scopes
76
89
 
77
90
  def add_param(location_name, name, type, required:, **options)
78
91
  schema = ActionSpec::Schema.build(type, **options)
79
- endpoint.request.add_param(location_name, ActionSpec::Schema::Field.new(name:, required:, schema:))
92
+ endpoint.request.add_param(location_name, ActionSpec::Schema::Field.new(name:, required:, schema:, scopes: scopes.dup))
80
93
  end
81
94
 
82
95
  def add_many(location_name, params, required:)
@@ -86,7 +99,12 @@ module ActionSpec
86
99
  if (schema_options.keys - ActionSpec::Schema::OPTION_KEYS).present?
87
100
  endpoint.request.add_param(
88
101
  location_name,
89
- ActionSpec::Schema::Field.new(name:, required:, schema: ActionSpec::Schema.from_definition(definition))
102
+ ActionSpec::Schema::Field.new(
103
+ name:,
104
+ required:,
105
+ schema: ActionSpec::Schema.from_definition(definition),
106
+ scopes: scopes.dup
107
+ )
90
108
  )
91
109
  else
92
110
  add_param(location_name, name, String, required:, **definition)
@@ -100,7 +118,7 @@ module ActionSpec
100
118
  end
101
119
 
102
120
  def add_body(media_type, definition)
103
- ActionSpec::Schema.build_fields(definition).each_value do |field|
121
+ ActionSpec::Schema.build_fields(definition, scopes: scopes.dup).each_value do |field|
104
122
  endpoint.request.add_body(media_type, field)
105
123
  end
106
124
  end
@@ -48,6 +48,7 @@ module ActionSpec
48
48
  next unless (controller = controller_for(route))
49
49
  next unless controller.respond_to?(:action_spec_for)
50
50
  next unless (endpoint = controller.action_spec_for(route_action(route)))
51
+ next if endpoint.options[:openapi] == false
51
52
 
52
53
  path = normalized_path(route)
53
54
  next if path.blank?
@@ -1,9 +1,5 @@
1
1
  module ActionSpec
2
2
  class Railtie < ::Rails::Railtie
3
- initializer "action_spec.i18n" do |app|
4
- app.config.i18n.load_path += Dir[root.join("config/locales/*.yml")]
5
- end
6
-
7
3
  initializer "action_spec.controller" do
8
4
  ActiveSupport.on_load(:action_controller_base) do
9
5
  include ActionSpec::Doc
@@ -3,12 +3,13 @@
3
3
  module ActionSpec
4
4
  module Schema
5
5
  class Field
6
- attr_reader :name, :schema
6
+ attr_reader :name, :schema, :scopes
7
7
 
8
- def initialize(name:, required:, schema:)
8
+ def initialize(name:, required:, schema:, scopes: [])
9
9
  @name = name.to_sym
10
10
  @required = required
11
11
  @schema = schema
12
+ @scopes = Array(scopes).map(&:to_sym).freeze
12
13
  end
13
14
 
14
15
  def required?
@@ -20,7 +21,7 @@ module ActionSpec
20
21
  end
21
22
 
22
23
  def copy
23
- self.class.new(name:, required: required?, schema: schema.copy)
24
+ self.class.new(name:, required: required?, schema: schema.copy, scopes:)
24
25
  end
25
26
  end
26
27
  end
@@ -40,13 +40,14 @@ module ActionSpec
40
40
  ObjectOf.new(build_fields(definition))
41
41
  end
42
42
 
43
- def build_fields(definition_hash)
43
+ def build_fields(definition_hash, scopes: [])
44
44
  definition_hash.each_with_object(ActiveSupport::OrderedHash.new) do |(name, definition), fields|
45
45
  schema = build_field_schema(definition)
46
46
  fields[field_name(name)] = Field.new(
47
47
  name: field_name(name),
48
48
  required: required_key?(name),
49
- schema:
49
+ schema:,
50
+ scopes:
50
51
  )
51
52
  end
52
53
  end
@@ -5,26 +5,29 @@ module ActionSpec
5
5
  extend ActiveModel::Naming
6
6
  extend ActiveModel::Translation
7
7
 
8
+ BUILT_IN_SCOPES = %i[path query body headers cookies].freeze
9
+
10
+ class << self
11
+ def empty_px
12
+ new.px
13
+ end
14
+ end
15
+
8
16
  attr_reader :errors, :px
9
17
 
10
18
  def initialize
11
19
  @errors = ActiveModel::Errors.new(self)
12
- @px = ActiveSupport::HashWithIndifferentAccess.new(
13
- path: ActiveSupport::HashWithIndifferentAccess.new,
14
- query: ActiveSupport::HashWithIndifferentAccess.new,
15
- body: ActiveSupport::HashWithIndifferentAccess.new,
16
- headers: HeaderHash.new,
17
- cookies: ActiveSupport::HashWithIndifferentAccess.new
18
- )
20
+ @px = build_px
19
21
  end
20
22
 
21
23
  def invalid?
22
24
  errors.any?
23
25
  end
24
26
 
25
- def assign(location, key, value)
27
+ def assign(location, key, value, scopes: [])
26
28
  bucket(location)[key] = value
27
29
  px[key] = value if root_bucket?(location)
30
+ Array(scopes).each { |scope_name| scope_bucket(scope_name)[key] = value }
28
31
  end
29
32
 
30
33
  def add_error(attribute, type, **options)
@@ -60,8 +63,28 @@ module ActionSpec
60
63
 
61
64
  private
62
65
 
66
+ def build_px
67
+ values = ActiveSupport::HashWithIndifferentAccess.new
68
+ scope = ActiveSupport::HashWithIndifferentAccess.new
69
+
70
+ BUILT_IN_SCOPES.each do |scope_name|
71
+ bucket = scope_name == :headers ? HeaderHash.new : ActiveSupport::HashWithIndifferentAccess.new
72
+ values[scope_name] = bucket
73
+ scope[scope_name] = bucket
74
+ end
75
+
76
+ # Keep px hash-like while exposing grouped views through px.scope.
77
+ values.instance_variable_set(:@scope, scope)
78
+ values.define_singleton_method(:scope) { @scope }
79
+ values
80
+ end
81
+
63
82
  def bucket(location)
64
- px.fetch(location)
83
+ px.scope.fetch(location)
84
+ end
85
+
86
+ def scope_bucket(name)
87
+ px.scope[name] ||= ActiveSupport::HashWithIndifferentAccess.new
65
88
  end
66
89
 
67
90
  def root_bucket?(location)
@@ -28,7 +28,7 @@ module ActionSpec
28
28
  value = resolve_field(field, result:, source:, location:)
29
29
  next if value.equal?(ActionSpec::Schema::Missing)
30
30
 
31
- result.assign(location, storage_key(field, location), value)
31
+ result.assign(location, storage_key(field, location), value, scopes: field.scopes)
32
32
  end
33
33
  end
34
34
 
@@ -6,12 +6,8 @@ module ActionSpec
6
6
  module Validator
7
7
  extend ActiveSupport::Concern
8
8
 
9
- included do
10
- rescue_from ActionSpec::InvalidParameters, with: :render_invalid_parameters if ActionSpec.config.rescue_invalid_parameters
11
- end
12
-
13
9
  def px
14
- @px ||= ActiveSupport::HashWithIndifferentAccess.new
10
+ @px ||= ValidationResult.empty_px
15
11
  end
16
12
 
17
13
  def validate_params!
@@ -26,21 +22,12 @@ module ActionSpec
26
22
 
27
23
  def validate_with(coerce:)
28
24
  endpoint = self.class.respond_to?(:action_spec_for) ? self.class.action_spec_for(action_name) : nil
29
- return ActiveSupport::HashWithIndifferentAccess.new unless endpoint
25
+ return ValidationResult.empty_px unless endpoint
30
26
 
31
27
  result = Runner.new(endpoint:, controller: self, coerce:).call
32
28
  raise ActionSpec.config.invalid_parameters_exception_class.new(result) if result.invalid?
33
29
 
34
30
  result.px
35
31
  end
36
-
37
- def render_invalid_parameters(error)
38
- if (renderer = ActionSpec.config.invalid_parameters_renderer)
39
- return renderer.arity == 2 ? renderer.call(self, error) : instance_exec(error, &renderer)
40
- end
41
-
42
- render json: { errors: error.errors.to_hash(full_messages: true) },
43
- status: ActionSpec.config.invalid_parameters_status
44
- end
45
32
  end
46
33
  end
@@ -1,3 +1,3 @@
1
1
  module ActionSpec
2
- VERSION = "1.0.0"
2
+ VERSION = "1.2.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: action_spec
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - zhandao
@@ -43,7 +43,7 @@ dependencies:
43
43
  - - "<"
44
44
  - !ruby/object:Gem::Version
45
45
  version: '9.0'
46
- description: Concise and Powerful API Documentation Solution for Rails.
46
+ description: A concise Rails DSL for declaring API request and response schemas.
47
47
  email:
48
48
  - a@skipping.cat
49
49
  executables: []
@@ -53,8 +53,6 @@ files:
53
53
  - MIT-LICENSE
54
54
  - README.md
55
55
  - Rakefile
56
- - config/locales/en.yml
57
- - config/locales/zh.yml
58
56
  - lib/action_spec.rb
59
57
  - lib/action_spec/configuration.rb
60
58
  - lib/action_spec/doc.rb
@@ -87,8 +85,8 @@ licenses:
87
85
  - MIT
88
86
  metadata:
89
87
  homepage_uri: https://github.com/action-spec/action_spec
90
- source_code_uri: https://github.com/action-spec/action_spec
91
- changelog_uri: https://github.com/action-spec/action_spec/CHANils.
88
+ source_code_uri: https://github.com/action-spec/action_spec/tree/main
89
+ changelog_uri: https://github.com/action-spec/action_spec/releases
92
90
  rdoc_options: []
93
91
  require_paths:
94
92
  - lib
@@ -1,6 +0,0 @@
1
- en:
2
- activemodel:
3
- errors:
4
- messages:
5
- required: "is required"
6
- invalid_type: "must be a valid %{expected}"
@@ -1,6 +0,0 @@
1
- zh:
2
- activemodel:
3
- errors:
4
- messages:
5
- required: "不能为空"
6
- invalid_type: "必须是有效的%{expected}"