types_from_serializers 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ab46cb83091bc01087646a3e9c1eee590d4148ea16f86fcca2c83ad91b24d2a5
4
+ data.tar.gz: 95e62afbb4b49ab897434fe33926322964439af03726c570bec6296eb4c51ca1
5
+ SHA512:
6
+ metadata.gz: 8280b0616181a1b13b4192bc6cd9ceb6b43498d31bd83b871d3da0bba924c63b52d67598df83df8d8ccac535999f81a99e42b530cbc0ab6afeddbfa9366928e2
7
+ data.tar.gz: ee8d6f445603ef26932c631a2064f337186c6f27451771b2f31577df37c587f318b1abe22e9277e7db48f2d65d47ca7c62f1177a67cbc35df615e5277931bce1
data/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # 0.1.0 (2022-07-12)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * handle non-ActiveRecord models and extract types from unions ([ea9b2a7](https://github.com/ElMassimo/types_from_serializers/commit/ea9b2a71cb85503ff691e5ef115ab73f89b005af))
7
+
8
+
9
+ ### Features
10
+
11
+ * make the DSL is available without manually requiring ([04a4f3a](https://github.com/ElMassimo/types_from_serializers/commit/04a4f3ad3c96658639cbe75de07f19cc7f79f4c6))
12
+ * support specifying base serializers and additional dirs to scan ([164cfe1](https://github.com/ElMassimo/types_from_serializers/commit/164cfe17bb0527c59cf95441381aef7bf797a568))
13
+
14
+
15
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2021 Máximo Mussini
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,33 @@
1
+ <h1 align="center">
2
+ Types From Serializers
3
+ <p align="center">
4
+ <a href="https://travis-ci.org/ElMassimo/types_from_serializers"><img alt="Build Status" src="https://travis-ci.org/ElMassimo/types_from_serializers.svg"/></a>
5
+ <a href="http://inch-ci.org/github/ElMassimo/types_from_serializers"><img alt="Inline docs" src="http://inch-ci.org/github/ElMassimo/types_from_serializers.svg"/></a>
6
+ <a href="https://codeclimate.com/github/ElMassimo/types_from_serializers"><img alt="Maintainability" src="https://codeclimate.com/github/ElMassimo/types_from_serializers/badges/gpa.svg"/></a>
7
+ <a href="https://codeclimate.com/github/ElMassimo/types_from_serializers"><img alt="Test Coverage" src="https://codeclimate.com/github/ElMassimo/types_from_serializers/badges/coverage.svg"/></a>
8
+ <a href="https://rubygems.org/gems/types_from_serializers"><img alt="Gem Version" src="https://img.shields.io/gem/v/types_from_serializers.svg?colorB=e9573f"/></a>
9
+ <a href="https://github.com/ElMassimo/types_from_serializers/blob/main/LICENSE.txt"><img alt="License" src="https://img.shields.io/badge/license-MIT-428F7E.svg"/></a>
10
+ </p>
11
+ </h1>
12
+
13
+ [aliases]: https://vite-ruby.netlify.app/guide/development.html#import-aliases-%F0%9F%91%89
14
+ [config options]: https://github.com/ElMassimo/types_from_serializers/blob/main/lib/types_from_serializers/generator.rb#L82-L85
15
+ [readme]: https://github.com/ElMassimo/types_from_serializers
16
+
17
+ For more information, check the main [README].
18
+
19
+ ### Installation 💿
20
+
21
+ Add this line to your application's Gemfile:
22
+
23
+ ```ruby
24
+ gem 'types_from_serializers'
25
+ ```
26
+
27
+ And then execute:
28
+
29
+ $ bundle
30
+
31
+ Or install it yourself as:
32
+
33
+ $ gem install types_from_serializers
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ # Internal: A DSL to specify types for serializer attributes.
6
+ module TypesFromSerializer
7
+ module DSL
8
+ extend ActiveSupport::Concern
9
+
10
+ module ClassMethods
11
+ # Override: Capture the name of the model related to the serializer.
12
+ #
13
+ # name - An alias for the internal object in the serializer.
14
+ # model - The name of an ActiveRecord model to infer types from the schema.
15
+ # types_from - The name of a TypeScript interface to infer types from.
16
+ def object_as(name, model: nil, types_from: nil)
17
+ # NOTE: Avoid taking memory for type information that won't be used.
18
+ if Rails.env.development?
19
+ model ||= name.is_a?(Symbol) ? name : try(:_serializer_model_name)
20
+ @_serializer_model_name = model || name
21
+ @_serializer_types_from = types_from if types_from
22
+ end
23
+
24
+ super(name)
25
+ end
26
+
27
+ # Public: Like `attributes`, but providing type information for each field.
28
+ def typed_attributes(attrs)
29
+ attributes(*attrs.keys)
30
+
31
+ # NOTE: Avoid taking memory for type information that won't be used.
32
+ if Rails.env.development?
33
+ _typed_attributes.update(attrs.map { |key, type|
34
+ [key.to_s, type.is_a?(Hash) ? type : {type: type}]
35
+ }.to_h)
36
+ end
37
+ end
38
+
39
+ # Public: Allows to specify the type for a serializer method that will
40
+ # be defined immediately after calling this method.
41
+ def type(type = :unknown, optional: false)
42
+ @_current_attribute_type = {type: type, optional: optional}
43
+ end
44
+
45
+ # Internal: Intercept a method definition, tying a type that was
46
+ # previously specified to the name of the attribute.
47
+ def method_added(name)
48
+ super(name)
49
+ if @_current_attribute_type
50
+ serializer_attributes name
51
+
52
+ # NOTE: Avoid taking memory for type information that won't be used.
53
+ if Rails.env.development?
54
+ _typed_attributes[name.to_s] = @_current_attribute_type
55
+ end
56
+
57
+ @_current_attribute_type = nil
58
+ end
59
+ end
60
+
61
+ # NOTE: Avoid taking memory for type information that won't be used.
62
+ if Rails.env.development?
63
+ # Internal: Contains type information for serializer attributes.
64
+ def _typed_attributes
65
+ unless defined?(@_typed_attributes)
66
+ @_typed_attributes = superclass.try(:_typed_attributes)&.dup || {}
67
+ end
68
+ @_typed_attributes
69
+ end
70
+
71
+ # Internal: The name of the model that will be serialized by this
72
+ # serializer, used to infer field types from the SQL columns.
73
+ def _serializer_model_name
74
+ unless defined?(@_serializer_model_name)
75
+ @_serializer_model_name = superclass.try(:_serializer_model_name)
76
+ end
77
+ @_serializer_model_name
78
+ end
79
+
80
+ # Internal: The TypeScript interface that will be used by default to
81
+ # infer the serializer field types when not explicitly provided.
82
+ def _serializer_types_from
83
+ unless defined?(@_serializer_types_from)
84
+ @_serializer_types_from = superclass.try(:_serializer_types_from)
85
+ end
86
+ @_serializer_types_from
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,343 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest"
4
+ require "fileutils"
5
+ require "pathname"
6
+
7
+ # Public: Automatically generates TypeScript interfaces for Ruby serializers.
8
+ module TypesFromSerializers
9
+ # Internal: The configuration for TypeScript generation.
10
+ Config = Struct.new(
11
+ :base_serializers,
12
+ :serializers_dirs,
13
+ :output_dir,
14
+ :name_from_serializer,
15
+ :native_types,
16
+ :sql_to_typescript_type_mapping,
17
+ keyword_init: true,
18
+ )
19
+
20
+ # Internal: The type metadata for a serializer.
21
+ SerializerMetadata = Struct.new(
22
+ :attributes,
23
+ :associations,
24
+ :model_name,
25
+ :types_from,
26
+ keyword_init: true,
27
+ )
28
+
29
+ # Internal: The type metadata for a serializer field.
30
+ FieldMetadata = Struct.new(:name, :type, :optional, :many, keyword_init: true) do
31
+ def typescript_name
32
+ name.to_s.camelize(:lower)
33
+ end
34
+ end
35
+
36
+ # Internal: Extensions that simplify the implementation of the generator.
37
+ module SerializerRefinements
38
+ refine String do
39
+ # Internal: Converts a name such as :user to the User constant.
40
+ def to_model
41
+ classify.safe_constantize
42
+ end
43
+ end
44
+
45
+ refine Symbol do
46
+ def safe_constantize
47
+ to_s.classify.safe_constantize
48
+ end
49
+
50
+ delegate :to_model, to: :to_s
51
+ end
52
+ # rubocop:enable Rails/Delegate
53
+
54
+ refine Class do
55
+ # Internal: Name of the TypeScript interface.
56
+ def typescript_interface_name
57
+ TypesFromSerializers.config.name_from_serializer.call(name).tr_s(":", "")
58
+ end
59
+
60
+ # Internal: The base name of the TypeScript file to be written.
61
+ def typescript_interface_basename
62
+ TypesFromSerializers.config.name_from_serializer.call(name).gsub("::", "/")
63
+ end
64
+
65
+ # Internal: A first pass of gathering types for the serializer fields.
66
+ def typescript_metadata
67
+ SerializerMetadata.new(
68
+ model_name: _serializer_model_name,
69
+ types_from: _serializer_types_from,
70
+ attributes: _attributes.map { |key, options|
71
+ typed_attrs = _typed_attributes.fetch(key, {})
72
+ FieldMetadata.new(
73
+ **typed_attrs,
74
+ name: key,
75
+ optional: typed_attrs[:optional] || options.key?(:if),
76
+ )
77
+ },
78
+ associations: _associations.map { |key, options|
79
+ FieldMetadata.new(
80
+ name: options.fetch(:root, key),
81
+ type: options.fetch(:serializer),
82
+ optional: options.key?(:if),
83
+ many: options.fetch(:write_method) == :write_many,
84
+ )
85
+ },
86
+ )
87
+ end
88
+
89
+ # Internal: Infers field types by checking the SQL columns for the model
90
+ # serialized, or from a TypeScript interface if provided.
91
+ def typescript_infer_types(metadata)
92
+ model = metadata.model_name&.to_model
93
+ interface = metadata.types_from
94
+
95
+ metadata.attributes.reject(&:type).each do |meta|
96
+ if model&.respond_to?(:columns_hash) && (column = model.columns_hash[meta.name.to_s])
97
+ meta[:type] = TypesFromSerializers.config.sql_to_typescript_type_mapping[column.type]
98
+ meta[:optional] ||= column.null
99
+ elsif interface
100
+ meta[:type] = "#{interface}['#{meta.typescript_name}']"
101
+ end
102
+ end
103
+ end
104
+
105
+ def typescript_imports(metadata)
106
+ assoc_imports = metadata.associations.map { |meta|
107
+ [meta.type.typescript_interface_name, "~/types/serializers/#{meta.type.typescript_interface_basename}"]
108
+ }
109
+
110
+ attr_imports = metadata.attributes
111
+ .flat_map { |meta| extract_typescript_types(meta.type.to_s) }
112
+ .uniq
113
+ .reject { |type| typescript_native_type?(type) }
114
+ .map { |type|
115
+ [type, "~/types/#{type}"]
116
+ }
117
+
118
+ (assoc_imports + attr_imports).uniq.map { |interface, filename|
119
+ "import type #{interface} from '#{filename}'\n"
120
+ }.uniq
121
+ end
122
+
123
+ # Internal: Extracts any types inside generics or array types.
124
+ def extract_typescript_types(type)
125
+ type.split(/[<>\[\],\s|]+/)
126
+ end
127
+
128
+ # NOTE: Treat uppercase names as custom types.
129
+ # Lowercase names would be native types, such as :string and :boolean.
130
+ def typescript_native_type?(type)
131
+ type[0] == type[0].downcase || TypesFromSerializers.config.native_types.include?(type)
132
+ end
133
+
134
+ def typescript_fields(metadata)
135
+ (metadata.attributes + metadata.associations).map { |meta|
136
+ type = meta.type.is_a?(Class) ? meta.type.typescript_interface_name : meta.type || :unknown
137
+ type = meta.many ? "#{type}[]" : type
138
+ " #{meta.typescript_name}#{"?" if meta.optional}: #{type}"
139
+ }
140
+ end
141
+ end
142
+ end
143
+
144
+ # Internal: Structure to keep track of changed files.
145
+ class Changes
146
+ def initialize(dirs)
147
+ @added = Set.new
148
+ @removed = Set.new
149
+ @modified = Set.new
150
+ track_changes(dirs)
151
+ end
152
+
153
+ def updated?
154
+ @modified.any? || @added.any? || @removed.any?
155
+ end
156
+
157
+ def any_removed?
158
+ @removed.any?
159
+ end
160
+
161
+ def modified_files
162
+ @modified
163
+ end
164
+
165
+ def only_modified?
166
+ @added.empty? && @removed.empty?
167
+ end
168
+
169
+ def clear
170
+ @added.clear
171
+ @removed.clear
172
+ @modified.clear
173
+ end
174
+
175
+ private
176
+
177
+ def track_changes(dirs)
178
+ Listen.to(*dirs, only: %r{.rb$}) do |modified, added, removed|
179
+ modified.each { |file| @modified.add(file) }
180
+ added.each { |file| @added.add(file) }
181
+ removed.each { |file| @removed.add(file) }
182
+ end.start
183
+ end
184
+ end
185
+
186
+ class << self
187
+ using SerializerRefinements
188
+
189
+ attr_reader :force_generation
190
+
191
+ # Public: Configuration of the code generator.
192
+ def config
193
+ (@config ||= default_config(root)).tap do |config|
194
+ yield(config) if block_given?
195
+ end
196
+ end
197
+
198
+ # Public: Generates code for all serializers in the app.
199
+ def generate(force: ENV["SERIALIZER_TYPES_FORCE"])
200
+ @force_generation = force
201
+ generate_index_file
202
+
203
+ loaded_serializers.each do |serializer|
204
+ generate_interface_for(serializer)
205
+ end
206
+ end
207
+
208
+ def generate_changed
209
+ if changes.updated?
210
+ config.output_dir.rmtree if changes.any_removed?
211
+ load_serializers(changes.modified_files)
212
+ generate
213
+ changes.clear
214
+ end
215
+ end
216
+
217
+ # Internal: Defines a TypeScript interface for the serializer.
218
+ def generate_interface_for(serializer)
219
+ metadata = serializer.typescript_metadata
220
+ filename = serializer.typescript_interface_basename
221
+
222
+ write_if_changed(filename: filename, cache_key: metadata.inspect) {
223
+ serializer.typescript_infer_types(metadata)
224
+ <<~TS
225
+ //
226
+ // DO NOT MODIFY: This file was automatically generated by TypesFromSerializers.
227
+ #{serializer.typescript_imports(metadata).join}
228
+ export default interface #{serializer.typescript_interface_name} {
229
+ #{serializer.typescript_fields(metadata).join("\n")}
230
+ }
231
+ TS
232
+ }
233
+ end
234
+
235
+ # Internal: Allows to import all serializer types from a single file.
236
+ def generate_index_file
237
+ write_if_changed(filename: "index", cache_key: all_serializer_files.join) {
238
+ load_serializers(all_serializer_files)
239
+ <<~TS
240
+ //
241
+ // DO NOT MODIFY: This file was automatically generated by TypesFromSerializers.
242
+ #{loaded_serializers.map { |s| "export type { default as #{s.typescript_interface_name} } from './#{s.typescript_interface_basename}'" }.join("\n")}
243
+ TS
244
+ }
245
+ end
246
+
247
+ # Internal: Checks if it should avoid generating an interface.
248
+ def skip_serializer?(name)
249
+ name.include?("BaseSerializer") || name.in?(config.base_serializers)
250
+ end
251
+
252
+ # Internal: Returns an object compatible with FileUpdateChecker.
253
+ def track_changes
254
+ changes
255
+ end
256
+
257
+ private
258
+
259
+ def root
260
+ defined?(Rails) ? Rails.root : Pathname.new(Dir.pwd)
261
+ end
262
+
263
+ def changes
264
+ @changes ||= Changes.new(config.serializers_dirs)
265
+ end
266
+
267
+ def all_serializer_files
268
+ config.serializers_dirs.flat_map { |dir| Dir["#{dir}/**/*.rb"] }
269
+ end
270
+
271
+ def load_serializers(files)
272
+ files.each { |file| require file }
273
+ end
274
+
275
+ def loaded_serializers
276
+ config.base_serializers.map(&:constantize)
277
+ .flat_map(&:descendants)
278
+ .uniq
279
+ .sort_by(&:name)
280
+ .reject { |s| skip_serializer?(s.name) }
281
+ rescue NameError
282
+ raise ArgumentError, "Please ensure all your serializers extend BaseSerializer, or configure `config.base_serializers`."
283
+ end
284
+
285
+ def default_config(root)
286
+ Config.new(
287
+ # The base serializers that all other serializers extend.
288
+ base_serializers: ["BaseSerializer"],
289
+
290
+ # The dirs where the serializer files are located.
291
+ serializers_dirs: [root.join("app/serializers").to_s],
292
+
293
+ # The dir where interface files are placed.
294
+ output_dir: root.join(defined?(ViteRuby) ? ViteRuby.config.source_code_dir : "app/frontend").join("types/serializers"),
295
+
296
+ # Remove the serializer suffix from the class name.
297
+ name_from_serializer: ->(name) { name.delete_suffix("Serializer") },
298
+
299
+ # Types that don't need to be imported in TypeScript.
300
+ native_types: [
301
+ "Array",
302
+ "Record",
303
+ "Date",
304
+ ].to_set,
305
+
306
+ # Maps SQL column types to TypeScript native and custom types.
307
+ sql_to_typescript_type_mapping: {
308
+ boolean: :boolean,
309
+ date: "string | Date",
310
+ datetime: "string | Date",
311
+ decimal: :number,
312
+ integer: :number,
313
+ string: :string,
314
+ text: :string,
315
+ }.tap do |types|
316
+ types.default = :unknown
317
+ end,
318
+ )
319
+ end
320
+
321
+ # Internal: Writes if the file does not exist or the cache key has changed.
322
+ # The cache strategy consists of a comment on the first line of the file.
323
+ #
324
+ # Yields to receive the rendered file content when it needs to.
325
+ def write_if_changed(filename:, cache_key:)
326
+ filename = config.output_dir.join("#{filename}.ts")
327
+ FileUtils.mkdir_p(filename.dirname)
328
+ cache_key_comment = "// TypesFromSerializers CacheKey #{Digest::MD5.hexdigest(cache_key)}\n"
329
+ File.open(filename, "a+") { |file|
330
+ if stale?(file, cache_key_comment)
331
+ file.truncate(0)
332
+ file.write(cache_key_comment)
333
+ file.write(yield)
334
+ end
335
+ }
336
+ end
337
+
338
+ # Internal: Returns true if the cache key has changed since the last codegen.
339
+ def stale?(file, cache_key_comment)
340
+ @force_generation || file.gets != cache_key_comment
341
+ end
342
+ end
343
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/railtie"
4
+
5
+ class TypesFromSerializers::Railtie < Rails::Railtie
6
+ railtie_name :types_from_serializers
7
+
8
+ # Automatically generates code whenever a serializer is loaded.
9
+ if defined?(Rails.env) && Rails.env.development?
10
+ require_relative "generator"
11
+
12
+ initializer "types_from_serializers.reloader" do |app|
13
+ if Gem.loaded_specs["listen"]
14
+ require "listen"
15
+
16
+ app.config.after_initialize do
17
+ app.reloaders << TypesFromSerializers.track_changes
18
+ end
19
+
20
+ app.config.to_prepare do
21
+ TypesFromSerializers.generate_changed
22
+ end
23
+ else
24
+ app.config.to_prepare do
25
+ TypesFromSerializers.generate
26
+ end
27
+
28
+ Rails.logger.warn "Add 'listen' to your Gemfile to automatically generate code on serializer changes."
29
+ end
30
+ end
31
+ end
32
+
33
+ # Suitable when triggering code generation manually.
34
+ rake_tasks do |app|
35
+ namespace :types_from_serializers do
36
+ desc "Generates TypeScript interfaces for each serializer in the app."
37
+ task generate: :environment do
38
+ require_relative "generator"
39
+ serializers = TypesFromSerializers.generate(force: true)
40
+ puts "Generated TypeScript interfaces for #{serializers.size} serializers:"
41
+ puts serializers.map { |s| "\t#{s.name}" }.join("\n")
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TypesFromSerializers
4
+ # Public: This library adheres to semantic versioning.
5
+ VERSION = "0.1.0"
6
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "types_from_serializers/version"
4
+ require_relative "types_from_serializers/dsl"
5
+ require_relative "types_from_serializers/railtie"
metadata ADDED
@@ -0,0 +1,228 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: types_from_serializers
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Máximo Mussini
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-07-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: railties
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '5.1'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '8'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '5.1'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '8'
33
+ - !ruby/object:Gem::Dependency
34
+ name: oj_serializers
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.0'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: listen
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.2'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.2'
61
+ - !ruby/object:Gem::Dependency
62
+ name: bundler
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '2'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '2'
75
+ - !ruby/object:Gem::Dependency
76
+ name: pry-byebug
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '3.9'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '3.9'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rake
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '13'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '13'
103
+ - !ruby/object:Gem::Dependency
104
+ name: rspec-given
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '3.8'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '3.8'
117
+ - !ruby/object:Gem::Dependency
118
+ name: simplecov
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "<"
122
+ - !ruby/object:Gem::Version
123
+ version: '0.18'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "<"
129
+ - !ruby/object:Gem::Version
130
+ version: '0.18'
131
+ - !ruby/object:Gem::Dependency
132
+ name: standard
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '1.0'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '1.0'
145
+ - !ruby/object:Gem::Dependency
146
+ name: rubocop-rails
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ - !ruby/object:Gem::Dependency
160
+ name: rubocop-rspec
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ - !ruby/object:Gem::Dependency
174
+ name: rubocop-performance
175
+ requirement: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ type: :development
181
+ prerelease: false
182
+ version_requirements: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - ">="
185
+ - !ruby/object:Gem::Version
186
+ version: '0'
187
+ description: types_from_serializers helps you by automatically generating TypeScript
188
+ interfaces for your JSON serializers, allowing you typecheck your frontend code
189
+ to ship fast and with confidence.
190
+ email:
191
+ - maximomussini@gmail.com
192
+ executables: []
193
+ extensions: []
194
+ extra_rdoc_files:
195
+ - README.md
196
+ files:
197
+ - CHANGELOG.md
198
+ - LICENSE.txt
199
+ - README.md
200
+ - lib/types_from_serializers.rb
201
+ - lib/types_from_serializers/dsl.rb
202
+ - lib/types_from_serializers/generator.rb
203
+ - lib/types_from_serializers/railtie.rb
204
+ - lib/types_from_serializers/version.rb
205
+ homepage: https://github.com/ElMassimo/types_from_serializers
206
+ licenses:
207
+ - MIT
208
+ metadata: {}
209
+ post_install_message:
210
+ rdoc_options: []
211
+ require_paths:
212
+ - lib
213
+ required_ruby_version: !ruby/object:Gem::Requirement
214
+ requirements:
215
+ - - ">="
216
+ - !ruby/object:Gem::Version
217
+ version: '0'
218
+ required_rubygems_version: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
223
+ requirements: []
224
+ rubygems_version: 3.3.7
225
+ signing_key:
226
+ specification_version: 4
227
+ summary: Generate TypeScript interfaces from your JSON serializers.
228
+ test_files: []