model-to-schema 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +20 -0
  4. data/.ruby-version +1 -0
  5. data/CHANGELOG.md +13 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +111 -0
  8. data/Rakefile +12 -0
  9. data/esquema.gemspec +38 -0
  10. data/lib/esquema/builder.rb +155 -0
  11. data/lib/esquema/configuration.rb +34 -0
  12. data/lib/esquema/keyword_validator.rb +98 -0
  13. data/lib/esquema/model.rb +31 -0
  14. data/lib/esquema/property.rb +238 -0
  15. data/lib/esquema/schema_enhancer.rb +90 -0
  16. data/lib/esquema/type_caster.rb +53 -0
  17. data/lib/esquema/version.rb +5 -0
  18. data/lib/esquema/virtual_column.rb +46 -0
  19. data/lib/esquema.rb +14 -0
  20. data/lib/generators/esquema/install/install_generator.rb +16 -0
  21. data/lib/generators/esquema/install/templates/esquema_initializer.rb +22 -0
  22. data/sorbet/config +4 -0
  23. data/sorbet/rbi/annotations/.gitattributes +1 -0
  24. data/sorbet/rbi/annotations/activemodel.rbi +89 -0
  25. data/sorbet/rbi/annotations/activerecord.rbi +92 -0
  26. data/sorbet/rbi/annotations/activesupport.rbi +421 -0
  27. data/sorbet/rbi/annotations/rainbow.rbi +269 -0
  28. data/sorbet/rbi/gems/.gitattributes +1 -0
  29. data/sorbet/rbi/gems/activemodel@7.1.3.rbi +8 -0
  30. data/sorbet/rbi/gems/activerecord@7.1.3.rbi +8 -0
  31. data/sorbet/rbi/gems/activesupport@7.1.3.rbi +192 -0
  32. data/sorbet/rbi/gems/ast@2.4.2.rbi +584 -0
  33. data/sorbet/rbi/gems/base64@0.2.0.rbi +8 -0
  34. data/sorbet/rbi/gems/bigdecimal@3.1.6.rbi +8 -0
  35. data/sorbet/rbi/gems/byebug@11.1.3.rbi +3606 -0
  36. data/sorbet/rbi/gems/coderay@1.1.3.rbi +3426 -0
  37. data/sorbet/rbi/gems/concurrent-ruby@1.2.3.rbi +8 -0
  38. data/sorbet/rbi/gems/connection_pool@2.4.1.rbi +8 -0
  39. data/sorbet/rbi/gems/diff-lcs@1.5.1.rbi +1130 -0
  40. data/sorbet/rbi/gems/drb@2.2.0.rbi +1272 -0
  41. data/sorbet/rbi/gems/erubi@1.12.0.rbi +145 -0
  42. data/sorbet/rbi/gems/i18n@1.14.1.rbi +8 -0
  43. data/sorbet/rbi/gems/json@2.7.1.rbi +1553 -0
  44. data/sorbet/rbi/gems/language_server-protocol@3.17.0.3.rbi +14237 -0
  45. data/sorbet/rbi/gems/method_source@1.0.0.rbi +272 -0
  46. data/sorbet/rbi/gems/minitest@5.22.2.rbi +8 -0
  47. data/sorbet/rbi/gems/mutex_m@0.2.0.rbi +8 -0
  48. data/sorbet/rbi/gems/netrc@0.11.0.rbi +158 -0
  49. data/sorbet/rbi/gems/parallel@1.24.0.rbi +280 -0
  50. data/sorbet/rbi/gems/parser@3.3.0.5.rbi +5472 -0
  51. data/sorbet/rbi/gems/prettier_print@1.2.1.rbi +951 -0
  52. data/sorbet/rbi/gems/prism@0.24.0.rbi +31040 -0
  53. data/sorbet/rbi/gems/pry-byebug@3.10.1.rbi +1150 -0
  54. data/sorbet/rbi/gems/pry@0.14.2.rbi +10075 -0
  55. data/sorbet/rbi/gems/racc@1.7.3.rbi +157 -0
  56. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +402 -0
  57. data/sorbet/rbi/gems/rake@13.1.0.rbi +3027 -0
  58. data/sorbet/rbi/gems/rbi@0.1.9.rbi +3006 -0
  59. data/sorbet/rbi/gems/regexp_parser@2.9.0.rbi +3771 -0
  60. data/sorbet/rbi/gems/rexml@3.2.6.rbi +4781 -0
  61. data/sorbet/rbi/gems/rspec-core@3.13.0.rbi +10978 -0
  62. data/sorbet/rbi/gems/rspec-expectations@3.13.0.rbi +8153 -0
  63. data/sorbet/rbi/gems/rspec-mocks@3.13.0.rbi +5340 -0
  64. data/sorbet/rbi/gems/rspec-support@3.13.0.rbi +1629 -0
  65. data/sorbet/rbi/gems/rspec@3.13.0.rbi +82 -0
  66. data/sorbet/rbi/gems/rubocop-ast@1.30.0.rbi +7006 -0
  67. data/sorbet/rbi/gems/rubocop@1.60.2.rbi +57383 -0
  68. data/sorbet/rbi/gems/ruby-progressbar@1.13.0.rbi +1317 -0
  69. data/sorbet/rbi/gems/ruby2_keywords@0.0.5.rbi +8 -0
  70. data/sorbet/rbi/gems/spoom@1.2.4.rbi +3777 -0
  71. data/sorbet/rbi/gems/sqlite3@1.7.2.rbi +1691 -0
  72. data/sorbet/rbi/gems/syntax_tree@6.2.0.rbi +23133 -0
  73. data/sorbet/rbi/gems/tapioca@0.12.0.rbi +3510 -0
  74. data/sorbet/rbi/gems/thor@1.3.0.rbi +4345 -0
  75. data/sorbet/rbi/gems/timeout@0.4.1.rbi +142 -0
  76. data/sorbet/rbi/gems/tzinfo@2.0.6.rbi +8 -0
  77. data/sorbet/rbi/gems/unicode-display_width@2.5.0.rbi +65 -0
  78. data/sorbet/rbi/gems/yard-sorbet@0.8.1.rbi +428 -0
  79. data/sorbet/rbi/gems/yard@0.9.34.rbi +18219 -0
  80. data/sorbet/rbi/todo.rbi +20 -0
  81. data/sorbet/tapioca/config.yml +13 -0
  82. data/sorbet/tapioca/require.rb +4 -0
  83. metadata +176 -0
@@ -0,0 +1,238 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "type_caster"
4
+ module Esquema
5
+ # Represents a property in the Esquema schema.
6
+ class Property # rubocop:disable Metrics/ClassLength
7
+ # Mapping of database types to JSON types.
8
+ DB_TO_JSON_TYPE_MAPPINGS = {
9
+ date: "date",
10
+ datetime: "date-time",
11
+ time: "time",
12
+ string: "string",
13
+ text: "string",
14
+ integer: "integer",
15
+ float: "number",
16
+ number: "number",
17
+ decimal: "number",
18
+ boolean: "boolean",
19
+ array: "array",
20
+ object: "object"
21
+ }.freeze
22
+
23
+ NUMERIC_CONSTRAINT_KEYWORDS = %i[minimum maximum exclusiveMinimum exclusiveMaximum multipleOf].freeze
24
+ STRING_CONSTRAINT_KEYWORDS = %i[maxLength minLength pattern format].freeze
25
+ ARRAY_CONSTRAINT_KEYWORDS = %i[maxItems minItems uniqueItems items].freeze
26
+ OBJECT_CONSTRAINT_KEYWORDS = %i[maxProperties minProperties properties additionalProperties dependencies].freeze
27
+ LOGICAL_KEYWORDS = %i[allOf anyOf oneOf not].freeze
28
+ GENERIC_KEYWORDS = %i[type default title description enum const].freeze
29
+
30
+ KEYWORDS = (
31
+ NUMERIC_CONSTRAINT_KEYWORDS +
32
+ STRING_CONSTRAINT_KEYWORDS +
33
+ ARRAY_CONSTRAINT_KEYWORDS +
34
+ OBJECT_CONSTRAINT_KEYWORDS +
35
+ LOGICAL_KEYWORDS +
36
+ GENERIC_KEYWORDS
37
+ ).freeze
38
+
39
+ FORMAT_OPTIONS = %i[date-time date time email hostname ipv4 ipv6 uri uuid uri-reference uri-template].freeze
40
+
41
+ attr_reader :object, :options
42
+
43
+ # Initializes a new Property instance.
44
+ #
45
+ # @param object [Object] The object to build the property for.
46
+ # An object can be any of the following instance types:
47
+ # An ActiveRecord column. Example: ActiveRecord::ConnectionAdapters::SQLite3::Column
48
+ # An ActiveRecord association reflection. Example: ActiveRecord::Reflection::BelongsToReflection
49
+ # An Esquema virtual column. Example: Esquema::VirtualColumn
50
+ # @param options [Hash] Additional options for the property.
51
+ # @raise [ArgumentError] If the property does not have a name.
52
+ def initialize(object, options = {})
53
+ raise ArgumentError, "property must have a name" unless object.respond_to?(:name)
54
+
55
+ @object = object
56
+ @options = options
57
+ end
58
+
59
+ # Converts the Property instance to a JSON representation.
60
+ #
61
+ # @return [Hash] The JSON representation of the Property.
62
+ def as_json
63
+ KEYWORDS.each_with_object({}) do |property, hash|
64
+ value = send("build_#{property.downcase}")
65
+ next if value.nil? || (value.is_a?(String) && value.empty?)
66
+
67
+ hash[property] = value
68
+ end.compact
69
+ end
70
+
71
+ # Builds the title attribute for the Property.
72
+ #
73
+ # @return [String] The title attribute.
74
+ def build_title
75
+ options[:title].presence || object.name.to_s.humanize
76
+ end
77
+
78
+ # Builds the default attribute for the Property.
79
+ #
80
+ # @return [Object, nil] The default attribute.
81
+ def build_default
82
+ return unless object.respond_to?(:default)
83
+
84
+ default_value = object.default || options[:default].presence
85
+
86
+ @default = TypeCaster.cast(object.type, default_value) unless default_value.nil?
87
+ end
88
+
89
+ # Builds the type attribute for the Property.
90
+ #
91
+ # @return [String, nil] The type attribute.
92
+ def build_type
93
+ return DB_TO_JSON_TYPE_MAPPINGS[:array] if object.try(:collection?)
94
+
95
+ return unless object.respond_to?(:type)
96
+
97
+ @type = DB_TO_JSON_TYPE_MAPPINGS[object.type]
98
+ end
99
+
100
+ # Builds the description attribute for the Property.
101
+ #
102
+ # @return [String, nil] The description attribute.
103
+ def build_description
104
+ options[:description]
105
+ end
106
+
107
+ # Builds the items attribute for the Property.
108
+ #
109
+ # @return [Hash, nil] The items attribute.
110
+ def build_items
111
+ return unless object.try(:collection?)
112
+
113
+ case object.type
114
+ when :array
115
+ { type: DB_TO_JSON_TYPE_MAPPINGS[object.item_type] }
116
+ else
117
+ Builder.new(object.klass).build_schema
118
+ end
119
+ end
120
+
121
+ # Builds the enum attribute for the Property.
122
+ #
123
+ # @return [Array, nil] The enum attribute.
124
+ def build_enum
125
+ options[:enum]
126
+ end
127
+
128
+ def build_minimum
129
+ options[:minimum]
130
+ end
131
+
132
+ def build_maximum
133
+ options[:maximum]
134
+ end
135
+
136
+ def build_exclusiveminimum
137
+ options[:exclusiveMinimum]
138
+ end
139
+
140
+ def build_exclusivemaximum
141
+ options[:exclusiveMaximum]
142
+ end
143
+
144
+ def build_multipleof
145
+ options[:multipleof]
146
+ end
147
+
148
+ def build_maxlength
149
+ options[:maxLength]
150
+ end
151
+
152
+ def build_minlength
153
+ options[:minLength]
154
+ end
155
+
156
+ def build_pattern
157
+ options[:pattern]
158
+ end
159
+
160
+ def build_format
161
+ options[:format]
162
+ end
163
+
164
+ def build_maxitems # rubocop:disable Metrics/AbcSize
165
+ raise ArgumentError, "maxItems must be an integer" if options[:maxItems] && !options[:maxItems].is_a?(Integer)
166
+
167
+ if options[:maxItems]&.negative?
168
+ raise ArgumentError,
169
+ "maxItems must be a non-negative integer"
170
+ end
171
+
172
+ if options[:maxItems] && options[:type] != :array
173
+ raise ArgumentError, "maxItems must be use for array type properties only."
174
+ end
175
+
176
+ options[:maxItems]
177
+ end
178
+
179
+ def build_minitems # rubocop:disable Metrics/AbcSize
180
+ raise ArgumentError, "minItems must be an integer" if options[:minItems] && !options[:minItems].is_a?(Integer)
181
+
182
+ if options[:minItems]&.negative?
183
+ raise ArgumentError,
184
+ "minItems must be a non-negative integer"
185
+ end
186
+
187
+ if options[:minItems] && options[:type] != :array
188
+ raise ArgumentError, "minItems must be use for array type properties only."
189
+ end
190
+
191
+ options[:minItems]
192
+ end
193
+
194
+ def build_uniqueitems
195
+ options[:uniqueItems]
196
+ end
197
+
198
+ def build_properties
199
+ options[:properties]
200
+ end
201
+
202
+ def build_maxproperties
203
+ options[:maxProperties]
204
+ end
205
+
206
+ def build_minproperties
207
+ options[:minProperties]
208
+ end
209
+
210
+ def build_additionalproperties
211
+ options[:additionalProperties]
212
+ end
213
+
214
+ def build_dependencies
215
+ options[:dependencies]
216
+ end
217
+
218
+ def build_allof
219
+ options[:allOf]
220
+ end
221
+
222
+ def build_anyof
223
+ options[:anyOf]
224
+ end
225
+
226
+ def build_oneof
227
+ options[:oneOf]
228
+ end
229
+
230
+ def build_not
231
+ options[:not]
232
+ end
233
+
234
+ def build_const
235
+ options[:const]
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+ require "bigdecimal"
5
+ require_relative "keyword_validator"
6
+
7
+ module Esquema
8
+ # The SchemaEnhancer class is responsible for enhancing the schema of a model.
9
+ class SchemaEnhancer
10
+ attr_reader :model
11
+
12
+ def initialize(model, schema_enhancements)
13
+ @schema_enhancements = schema_enhancements
14
+ @model = model
15
+ end
16
+
17
+ # Sets the description for the model.
18
+ #
19
+ # @param description [String] The description of the model.
20
+ def model_description(description)
21
+ @schema_enhancements[:model_description] = description
22
+ end
23
+
24
+ # Sets the title for the model.
25
+ #
26
+ # @param title [String] The title of the model.
27
+ def model_title(title)
28
+ @schema_enhancements[:model_title] = title
29
+ end
30
+
31
+ # Adds a property to the schema.
32
+ #
33
+ # @param name [Symbol] The name of the property.
34
+ # @param options [Hash] Additional options for the property.
35
+ def property(name, options = {})
36
+ validate_property_as_attribute_for(name, options)
37
+
38
+ type = resolve_type(name, options)
39
+
40
+ KeywordValidator.validate!(name, type, options)
41
+
42
+ @schema_enhancements[:properties] ||= {}
43
+ @schema_enhancements[:properties][name] = options
44
+ end
45
+
46
+ # Adds a virtual property to the schema.
47
+ #
48
+ # @param name [Symbol] The name of the virtual property.
49
+ # @param options [Hash] Additional options for the virtual property.
50
+ def virtual_property(name, options = {})
51
+ options[:virtual] = true
52
+ property(name, options)
53
+ end
54
+
55
+ private
56
+
57
+ # Resolves the type of a property.
58
+ #
59
+ # @param name [Symbol] The name of the property.
60
+ # @param options [Hash] Additional options for the property.
61
+ # @return [Symbol] The resolved type of the property.
62
+ def resolve_type(name, options = {})
63
+ if options[:virtual] == true
64
+ options[:type]
65
+ else
66
+ model.type_for_attribute(name).type
67
+ end
68
+ end
69
+
70
+ # Retrieves the valid properties for the model.
71
+ #
72
+ # @return [Array<Symbol>] The valid properties for the model.
73
+ def valid_properties
74
+ @valid_properties ||= begin
75
+ properties = model.column_names + model.reflect_on_all_associations.map(&:name)
76
+ properties.map(&:to_sym)
77
+ end
78
+ end
79
+
80
+ # Validates that a property is a valid attribute for the model.
81
+ #
82
+ # @param prop_name [Symbol] The name of the property.
83
+ # @param options [Hash] Additional options for the property.
84
+ # @raise [ArgumentError] If the property is not a valid attribute for the model.
85
+ def validate_property_as_attribute_for(prop_name, options = {})
86
+ return if options[:virtual] == true
87
+ raise ArgumentError, "`#{prop_name}` is not a model attribute." unless valid_properties.include?(prop_name.to_sym)
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Esquema
4
+ module TypeCaster # rubocop:disable Style/Documentation
5
+ def self.cast(type, value) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
6
+ case type
7
+ when :string, :text
8
+ value.to_s
9
+ when :integer
10
+ begin
11
+ Integer(value)
12
+ rescue StandardError
13
+ nil
14
+ end
15
+ when :float
16
+ begin
17
+ Float(value)
18
+ rescue StandardError
19
+ nil
20
+ end
21
+ when :number
22
+ if value.to_s.include?(".")
23
+ begin
24
+ Float(value)
25
+ rescue StandardError
26
+ nil
27
+ end
28
+ else
29
+ begin
30
+ Integer(value)
31
+ rescue StandardError
32
+ nil
33
+ end
34
+ end
35
+ when :boolean
36
+ case value
37
+ when true, "true", "1", 1
38
+ true
39
+ when false, "false", "0", 0
40
+ false
41
+ end
42
+ when :array
43
+ Array(value)
44
+ when :object
45
+ value.is_a?(Hash) ? value : nil # or convert as desired
46
+ when :null
47
+ nil if value.nil?
48
+ else
49
+ raise ArgumentError, "Unsupported type: #{type}"
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Esquema
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Esquema
4
+ class VirtualColumn # rubocop:disable Style/Documentation
5
+ def initialize(property_name, options = {})
6
+ @property_name = property_name
7
+ @options = options
8
+ end
9
+
10
+ def name
11
+ @property_name.to_s
12
+ end
13
+
14
+ def class_name
15
+ @property_name.to_s.classify
16
+ end
17
+
18
+ def type
19
+ @options[:type]
20
+ end
21
+
22
+ def item_type
23
+ @options.dig(:items, :type)
24
+ end
25
+
26
+ def default
27
+ @options[:default]
28
+ end
29
+
30
+ def title
31
+ @options[:title]
32
+ end
33
+
34
+ def description
35
+ @options[:description]
36
+ end
37
+
38
+ def columns
39
+ []
40
+ end
41
+
42
+ def collection?
43
+ @options[:type] == :array
44
+ end
45
+ end
46
+ end
data/lib/esquema.rb ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "esquema/version"
4
+ require_relative "esquema/configuration"
5
+ require_relative "esquema/model"
6
+ require_relative "esquema/schema_enhancer"
7
+ require_relative "esquema/keyword_validator"
8
+ require_relative "esquema/builder"
9
+ require_relative "esquema/property"
10
+ require_relative "esquema/type_caster"
11
+ require_relative "esquema/virtual_column"
12
+
13
+ module Esquema # rubocop:disable Style/Documentation
14
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module Esquema
6
+ module Generators
7
+ # This generator is responsible for installing the Esquema gem.
8
+ class InstallGenerator < Rails::Generators::Base
9
+ source_root File.expand_path("templates", __dir__)
10
+
11
+ def copy_initializer_file
12
+ template "esquema_initializer.rb", "config/initializers/esquema.rb"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "esquema"
4
+
5
+ Esquema.configure do |config|
6
+ # Exclude Associations:
7
+ # Exclude associated models from the json-schema output.
8
+ # By default, all associated models are included.
9
+ config.exclude_associations = false
10
+
11
+ # Exclude Foreign Keys:
12
+ # Specify whether or not to exclude foreign keys from the json-schema output.
13
+ # By default, foreign keys are excluded.
14
+ # foreign keys are loosely defined as columns that end with "_id".
15
+ config.exclude_foreign_keys = true
16
+
17
+ # Excluded Columns:
18
+ # Specify the columns that you want to exclude from the json-schema output.
19
+ # These are columns common to all models, such as id, created_at, updated_at, etc.
20
+ # It's okay if not all models have these columns, they will be ignored.
21
+ config.excluded_columns = %i[id created_at updated_at deleted_at]
22
+ end
data/sorbet/config ADDED
@@ -0,0 +1,4 @@
1
+ --dir
2
+ .
3
+ --ignore=tmp/
4
+ --ignore=vendor/
@@ -0,0 +1 @@
1
+ **/*.rbi linguist-vendored=true
@@ -0,0 +1,89 @@
1
+ # typed: true
2
+
3
+ # DO NOT EDIT MANUALLY
4
+ # This file was pulled from a central RBI files repository.
5
+ # Please run `bin/tapioca annotations` to update it.
6
+
7
+ class ActiveModel::Errors
8
+ Elem = type_member { { fixed: ActiveModel::Error } }
9
+
10
+ sig { params(attribute: T.any(Symbol, String)).returns(T::Array[String]) }
11
+ def [](attribute); end
12
+
13
+ sig { params(attribute: T.any(Symbol, String), type: T.untyped, options: T.untyped).returns(ActiveModel::Error) }
14
+ def add(attribute, type = :invalid, **options); end
15
+
16
+ sig { params(attribute: T.any(Symbol, String), type: T.untyped, options: T.untyped).returns(T::Boolean) }
17
+ def added?(attribute, type = :invalid, options = {}); end
18
+
19
+ sig { params(options: T.untyped).returns(T::Hash[T.untyped, T.untyped]) }
20
+ def as_json(options = nil); end
21
+
22
+ sig { returns(T::Array[Symbol]) }
23
+ def attribute_names; end
24
+
25
+ sig { params(attribute: T.any(Symbol, String), type: T.untyped, options: T.untyped).returns(T.nilable(T::Array[String])) }
26
+ def delete(attribute, type = nil, **options); end
27
+
28
+ sig { returns(T::Hash[Symbol, T::Array[T::Hash[Symbol, T.untyped]]]) }
29
+ def details; end
30
+
31
+ sig { returns(T::Array[Elem]) }
32
+ def errors; end
33
+
34
+ sig { params(attribute: T.any(Symbol, String), message: String).returns(String) }
35
+ def full_message(attribute, message); end
36
+
37
+ sig { returns(T::Array[String]) }
38
+ def full_messages; end
39
+
40
+ sig { params(attribute: T.any(Symbol, String)).returns(T::Array[String]) }
41
+ def full_messages_for(attribute); end
42
+
43
+ sig { params(attribute: T.any(Symbol, String), type: T.untyped, options: T.untyped).returns(String) }
44
+ def generate_message(attribute, type = :invalid, options = {}); end
45
+
46
+ sig { returns(T::Hash[Symbol, T::Array[ActiveModel::Error]]) }
47
+ def group_by_attribute; end
48
+
49
+ sig { params(attribute: T.any(Symbol, String)).returns(T::Boolean) }
50
+ def has_key?(attribute); end
51
+
52
+ sig { params(error: ActiveModel::Error, override_options: T.untyped).returns(T::Array[ActiveModel::Error]) }
53
+ def import(error, override_options = {}); end
54
+
55
+ sig { params(attribute: T.any(Symbol, String)).returns(T::Boolean) }
56
+ def include?(attribute); end
57
+
58
+ sig { params(attribute: T.any(Symbol, String)).returns(T::Boolean) }
59
+ def key?(attribute); end
60
+
61
+ sig { params(other: T.untyped).returns(T::Array[ActiveModel::Error]) }
62
+ def merge!(other); end
63
+
64
+ sig { returns(T::Hash[Symbol, T::Array[String]]) }
65
+ def messages; end
66
+
67
+ sig { params(attribute: T.any(Symbol, String)).returns(T::Array[String]) }
68
+ def messages_for(attribute); end
69
+
70
+ sig { returns(T::Array[Elem]) }
71
+ def objects; end
72
+
73
+ sig { params(attribute: T.any(Symbol, String), type: T.untyped).returns(T::Boolean) }
74
+ def of_kind?(attribute, type = :invalid); end
75
+
76
+ sig { returns(T::Array[String]) }
77
+ def to_a; end
78
+
79
+ sig { params(full_messages: T.untyped).returns(T::Hash[Symbol, T::Array[String]]) }
80
+ def to_hash(full_messages = false); end
81
+
82
+ sig { params(attribute: T.any(Symbol, String), type: T.untyped, options: T.untyped).returns(T::Array[ActiveModel::Error]) }
83
+ def where(attribute, type = nil, **options); end
84
+ end
85
+
86
+ module ActiveModel::Validations
87
+ sig { returns(ActiveModel::Errors) }
88
+ def errors; end
89
+ end
@@ -0,0 +1,92 @@
1
+ # typed: true
2
+
3
+ # DO NOT EDIT MANUALLY
4
+ # This file was pulled from a central RBI files repository.
5
+ # Please run `bin/tapioca annotations` to update it.
6
+
7
+ class ActiveRecord::Schema
8
+ sig { params(info: T::Hash[T.untyped, T.untyped], blk: T.proc.bind(ActiveRecord::Schema).void).void }
9
+ def self.define(info = nil, &blk); end
10
+ end
11
+
12
+ class ActiveRecord::Migration
13
+ # @shim: Methods on migration are delegated to `SchemaStatements` using `method_missing`
14
+ include ActiveRecord::ConnectionAdapters::SchemaStatements
15
+
16
+ # @shim: Methods on migration are delegated to `DatabaseStatements` using `method_missing`
17
+ include ActiveRecord::ConnectionAdapters::DatabaseStatements
18
+ end
19
+
20
+ class ActiveRecord::Base
21
+ sig { returns(FalseClass) }
22
+ def blank?; end
23
+
24
+ # @shim: since `present?` is always true, `presence` always returns `self`
25
+ sig { returns(T.self_type) }
26
+ def presence; end
27
+
28
+ sig { returns(TrueClass) }
29
+ def present?; end
30
+
31
+ sig { params(args: T.untyped, options: T.untyped, block: T.nilable(T.proc.bind(T.attached_class).params(record: T.attached_class).void)).void }
32
+ def self.after_initialize(*args, **options, &block); end
33
+
34
+ sig { params(args: T.untyped, options: T.untyped, block: T.nilable(T.proc.bind(T.attached_class).params(record: T.attached_class).void)).void }
35
+ def self.after_find(*args, **options, &block); end
36
+
37
+ sig { params(args: T.untyped, options: T.untyped, block: T.nilable(T.proc.bind(T.attached_class).params(record: T.attached_class).void)).void }
38
+ def self.after_touch(*args, **options, &block); end
39
+
40
+ sig { params(args: T.untyped, options: T.untyped, block: T.nilable(T.proc.bind(T.attached_class).params(record: T.attached_class).void)).void }
41
+ def self.before_validation(*args, **options, &block); end
42
+
43
+ sig { params(args: T.untyped, options: T.untyped, block: T.nilable(T.proc.bind(T.attached_class).params(record: T.attached_class).void)).void }
44
+ def self.after_validation(*args, **options, &block); end
45
+
46
+ sig { params(args: T.untyped, options: T.untyped, block: T.nilable(T.proc.bind(T.attached_class).params(record: T.attached_class).void)).void }
47
+ def self.before_save(*args, **options, &block); end
48
+
49
+ sig { params(args: T.untyped, options: T.untyped, block: T.nilable(T.proc.bind(T.attached_class).params(record: T.attached_class).void)).void }
50
+ def self.around_save(*args, **options, &block); end
51
+
52
+ sig { params(args: T.untyped, options: T.untyped, block: T.nilable(T.proc.bind(T.attached_class).params(record: T.attached_class).void)).void }
53
+ def self.after_save(*args, **options, &block); end
54
+
55
+ sig { params(args: T.untyped, options: T.untyped, block: T.nilable(T.proc.bind(T.attached_class).params(record: T.attached_class).void)).void }
56
+ def self.before_create(*args, **options, &block); end
57
+
58
+ sig { params(args: T.untyped, options: T.untyped, block: T.nilable(T.proc.bind(T.attached_class).params(record: T.attached_class).void)).void }
59
+ def self.around_create(*args, **options, &block); end
60
+
61
+ sig { params(args: T.untyped, options: T.untyped, block: T.nilable(T.proc.bind(T.attached_class).params(record: T.attached_class).void)).void }
62
+ def self.after_create(*args, **options, &block); end
63
+
64
+ sig { params(args: T.untyped, options: T.untyped, block: T.nilable(T.proc.bind(T.attached_class).params(record: T.attached_class).void)).void }
65
+ def self.before_update(*args, **options, &block); end
66
+
67
+ sig { params(args: T.untyped, options: T.untyped, block: T.nilable(T.proc.bind(T.attached_class).params(record: T.attached_class).void)).void }
68
+ def self.around_update(*args, **options, &block); end
69
+
70
+ sig { params(args: T.untyped, options: T.untyped, block: T.nilable(T.proc.bind(T.attached_class).params(record: T.attached_class).void)).void }
71
+ def self.after_update(*args, **options, &block); end
72
+
73
+ sig { params(args: T.untyped, options: T.untyped, block: T.nilable(T.proc.bind(T.attached_class).params(record: T.attached_class).void)).void }
74
+ def self.before_destroy(*args, **options, &block); end
75
+
76
+ sig { params(args: T.untyped, options: T.untyped, block: T.nilable(T.proc.bind(T.attached_class).params(record: T.attached_class).void)).void }
77
+ def self.around_destroy(*args, **options, &block); end
78
+
79
+ sig { params(args: T.untyped, options: T.untyped, block: T.nilable(T.proc.bind(T.attached_class).params(record: T.attached_class).void)).void }
80
+ def self.after_destroy(*args, **options, &block); end
81
+
82
+ sig { params(args: T.untyped, options: T.untyped, block: T.nilable(T.proc.bind(T.attached_class).params(record: T.attached_class).void)).void }
83
+ def self.after_commit(*args, **options, &block); end
84
+
85
+ sig { params(args: T.untyped, options: T.untyped, block: T.nilable(T.proc.bind(T.attached_class).params(record: T.attached_class).void)).void }
86
+ def self.after_rollback(*args, **options, &block); end
87
+ end
88
+
89
+ class ActiveRecord::Relation
90
+ sig { returns(T::Boolean) }
91
+ def blank?; end
92
+ end