ts_schema 0.1.10 → 0.1.13

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: a5093baca17f4486d16008b5e3eb15dde18d5064b902a45c0d05defe5948f0ee
4
- data.tar.gz: 14d0f1352c9c716af3040814447c8aac81a3cdb66b04870fe698c5f9fa1c98f9
3
+ metadata.gz: 8d6e690832a89b7cbde62be4a27e6dc9374be937df6627bff391db15c2f1e777
4
+ data.tar.gz: 9a6aec0dd27e0938cdd856ad723dc28898a582de67122b7ed9bc147ed6d335c6
5
5
  SHA512:
6
- metadata.gz: b8425b8aefc7433b1360b6fb703ae9d0664513d6f6396e347f2b4c1e7d2345daf2a5047779c576cd070187c34fe87edd0da8aba6947dee644951c01e89fe2fbf
7
- data.tar.gz: 146ce2d710732da6f36cad8d4c9a9ff4d9fb95f186247c937441719951539517d8705184b8b151f42446a413b20f0b19f2dde0e79476b29d96c69aa62271aadc
6
+ metadata.gz: 7df830e6e1a3543944f9550e57801f16c9ef2121462983c67944814e70f21c47f48b8fe6d5841286c92d1876cee50c8b32bf1533a49ae90f436234ed52723bcd
7
+ data.tar.gz: 1cc1c840bef030c08afee4480102d6a4cbda128d30c1de01969ad9b03312c9f71f3019c589ea382e23756ba267f0ab939bc839f62c79619381d7535e9be33de7
data/MIT-LICENSE CHANGED
File without changes
data/README.md CHANGED
@@ -16,9 +16,9 @@ rails generate ts_schema:install
16
16
 
17
17
  ## Usage
18
18
 
19
- All options with their defaults are in the config file.
19
+ All options with their defaults are in the generated initializer file.
20
20
 
21
- By default, every migration, rollback, reset or setup task will auto generate a new schema file, replacing the existing one. You can disable this behavior in the config file.
21
+ By default, every migration will auto generate a new schema file, replacing the existing one. You can disable this behavior in the config file.
22
22
 
23
23
  You can manually generate the schema file by running:
24
24
 
@@ -28,7 +28,7 @@ rails ts_schema:generate
28
28
 
29
29
  ## Gotchas
30
30
 
31
- Apparantly, sometimes ActiveRecord's inflections will alter the class name. For instance, with a class named `Five`; `"Fives".singularize` returns "Fife", which is not the classname. In the case where Rails alters the classname for an association, you must explicitly define it on the association in the model using `class_name`. Example: `has_many :fives, class_name: "Five"`.
31
+ Sometimes ActiveRecord's inflections will alter the class name. For instance, with a class named `Five`; `"Fives".singularize` returns "Fife", which is not the classname. In the case where Rails alters the classname for an association, you must explicitly define it on the association in the model using `class_name`. Example: `has_many :fives, class_name: "Five"`.
32
32
 
33
33
  ## License
34
34
 
data/Rakefile CHANGED
@@ -1,2 +1,3 @@
1
1
  require "bundler/setup"
2
2
  require "bundler/gem_tasks"
3
+ require "ts_schema"
File without changes
@@ -1,57 +1,102 @@
1
+ # Default type mappings:
2
+ #
3
+ # string: string
4
+ # text: string
5
+ # integer: number
6
+ # enum: number
7
+ # bigint: number
8
+ # float: number
9
+ # decimal: number
10
+ # json: Record<string, any>
11
+ # jsonb: Record<string, any>
12
+ # binary: string
13
+ # boolean: boolean
14
+ # date: string
15
+ # datetime: string
16
+ # timestamp: string
17
+ # datetime_with_timezone: string
18
+ # inet: string
19
+ # cidr: string
20
+ # macaddr: string
21
+
1
22
  TsSchema.setup do |config|
2
- # Options: camel|snake|pascal
23
+ # Case options: camel|snake|pascal
24
+ #
3
25
  # config.case = :camel
4
26
 
27
+
5
28
  # Customize output path and file name
29
+ #
6
30
  # config.output = Rails.root.join('app', 'assets', 'javascripts', 'schema.d.ts')
7
31
 
8
- # Whether to generate the schema file on migrations and rollbacks
32
+
33
+ # Whether to generate the schema file after running migrations
34
+ #
9
35
  # config.auto_generate = true
10
36
 
37
+
11
38
  # Add custom type mappings or overrides
12
- #
13
- # Default type mappings:
14
- #
15
- # string: string
16
- # text: string
17
- # integer: number
18
- # enum: number
19
- # bigint: number
20
- # float: number
21
- # decimal: number
22
- # json: Record<string, any>
23
- # jsonb: Record<string, any>
24
- # binary: string
25
- # boolean: boolean
26
- # date: string
27
- # datetime: string
28
- # timestamp: string
29
- # datetime_with_timezone: string
30
- # inet: string
31
- # cidr: string
32
- # macaddr: string
33
- #
39
+ #
34
40
  # config.custom_types = {
35
41
  #
36
42
  # }
37
43
 
44
+
38
45
  # Default type for unrecognized types
46
+ #
39
47
  # config.default_type = :string
40
48
 
41
- # Whether to generate types for associations
49
+
50
+ # Whether to generate types for associated models
51
+ #
42
52
  # config.include_associated = true
43
53
 
44
- # Additional models to map which don't have a model file
45
- # config.additional_models = [
46
- #
47
- # ]
48
54
 
49
- # Namespace
55
+ # Parent classes of models to generate (as strings or symbols)
56
+ # Only classes inheriting from those in this list will have types generated
57
+ #
58
+ # config.parent_classes = [
59
+ # "ApplicationRecord",
60
+ # ]
61
+
62
+
63
+ # Additional models to generate schema from, such as those added by other gems
64
+ # which don't have a model file. (as strings or symbols)
65
+ #
66
+ # config.additional_models = [
67
+ #
68
+ # ]
69
+
70
+
71
+ # Ignore certain fields, omitting them from the generated schema: :optional|(string)|false
72
+ # Key is the name of the field to override options for.
73
+ # [:optional] will make this an optional field by adding '?' to the defintion (example: password?: string)
74
+ # [(string)] enter a field name override, for instance rename encrypted_password to password
75
+ # In that scenario, also specifying passwoord as an optional field will append a ? in the output
76
+ # [:omit] will omit the field from being output in the schema entirely
77
+ #
78
+ # config.field_overrides: {
79
+ # encrypted_password: :password,
80
+ # password: :optional,
81
+ # }
82
+
83
+
84
+ # Namespace for generated types
85
+ #
50
86
  # config.namespace = :schema
51
87
 
52
- # Options: tab|space
88
+
89
+ # Output schema as types or interfaces: type|interface
90
+ #
91
+ # config.schema_type = :interface
92
+
93
+
94
+ # Indentation options: tab|space
95
+ #
53
96
  # config.indent = :tab
54
97
 
98
+
55
99
  # If indent is spaces, specify how many
100
+ #
56
101
  # config.spaces = 2
57
102
  end
@@ -1,8 +1,9 @@
1
1
  require "rake"
2
+ require "ts_schema"
2
3
 
3
4
  namespace :ts_schema do
4
5
  desc "Generates a schema file in the default javascripts location, or the location specified in the ts_config initializer options"
5
- task :generate do
6
+ task generate: :environment do
6
7
  TsSchema.output_file
7
8
  end
8
9
  end
@@ -12,19 +13,11 @@ namespace :db do
12
13
  TsSchema.output_file if TsSchema.configuration.auto_generate
13
14
  end
14
15
 
15
- task migrate: :environment do
16
- auto_generate_and_save_file
16
+ task :migrate do
17
+ at_exit { auto_generate_and_save_file }
17
18
  end
18
19
 
19
- task rollback: :environment do
20
- auto_generate_and_save_file
21
- end
22
-
23
- task reset: :environment do
24
- auto_generate_and_save_file
25
- end
26
-
27
- task setup: :environment do
28
- auto_generate_and_save_file
20
+ task :setup do
21
+ at_exit { auto_generate_and_save_file }
29
22
  end
30
23
  end
@@ -10,10 +10,16 @@ module TsSchema
10
10
  custom_types: {},
11
11
  default_type: :string,
12
12
  include_associated: true,
13
+ parent_classes: ["ApplicationRecord"],
14
+ additional_models: [],
15
+ field_overrides: {
16
+ "encrypted_password" => :password,
17
+ "password" => :optional,
18
+ },
13
19
  namespace: :schema,
20
+ schema_type: :interface,
14
21
  indent: :tab,
15
22
  spaces: 2,
16
- additional_models: []
17
23
  }
18
24
 
19
25
  attr_accessor(*DEFAULTS.keys)
@@ -37,6 +43,5 @@ module TsSchema
37
43
  end
38
44
  self
39
45
  end
40
-
41
46
  end
42
47
  end
@@ -2,7 +2,6 @@ string: string
2
2
  text: string
3
3
  integer: number
4
4
  enum: number
5
- bigint: number
6
5
  float: number
7
6
  decimal: number
8
7
  json: Record<string, any>
@@ -12,7 +11,6 @@ boolean: boolean
12
11
  date: string
13
12
  datetime: string
14
13
  timestamp: string
15
- datetime_with_timezone: string
16
14
  inet: string
17
15
  cidr: string
18
16
  macaddr: string
@@ -1,4 +1,7 @@
1
+ require "rails"
2
+
1
3
  module TsSchema
2
4
  class Railtie < ::Rails::Railtie
5
+ load "tasks/ts_schema_tasks.rake"
3
6
  end
4
7
  end
@@ -2,65 +2,99 @@ require 'fileutils'
2
2
 
3
3
  module TsSchema
4
4
  class SchemaGenerator
5
- def initialize
5
+ attr_accessor :config
6
+ attr_accessor :models
7
+
8
+ def initialize(config = nil)
9
+ @config = config || TsSchema::Configuration.new
10
+ @config.field_overrides = @config.field_overrides.stringify_keys
11
+ @models = []
12
+
6
13
  Rails.application.eager_load!
7
- @models = ApplicationRecord.send(:subclasses)
8
- unless TsSchema.configuration.additional_models.empty?
9
- @models.concat(TsSchema.configuration.additional_models.map {|m| m.to_s.constantize })
14
+ @config.parent_classes.each do |parent|
15
+ @models.concat(get_subclasses(parent.to_s.constantize))
10
16
  end
11
- @types = TsSchema.configuration.types.merge(TsSchema.configuration.custom_types || {})
12
- end
13
-
14
- def generate
15
- generate_typescript
17
+ unless @config.additional_models.empty?
18
+ @models.concat(@config.additional_models.map do |m|
19
+ m.to_s.constantize
20
+ end)
21
+ end
22
+ @types = @config.types.stringify_keys.merge(@config.custom_types.stringify_keys || {})
16
23
  end
17
24
 
18
- def output_file
19
- path = TsSchema.configuration.output
20
- FileUtils.mkdir_p(File.dirname(path))
21
-
22
- content = generate
23
- return if File.exist?(path) && File.read(path) == content
24
-
25
- File.open(path, 'w') do |f|
26
- f.write content
25
+ def get_subclasses(model)
26
+ models = model.send(:subclasses)
27
+ unless nested_empty?(models)
28
+ models.concat(models.flat_map do |m|
29
+ get_subclasses(m)
30
+ end)
27
31
  end
32
+ models
28
33
  end
29
34
 
30
- private
31
-
32
- def generate_typescript
35
+ def generate
33
36
  type_template = ""
37
+
34
38
  @models.each do |model|
35
39
  columns = map_column_types(model)
36
- columns.concat(map_associations(model)) if TsSchema.configuration.include_associated
40
+ columns.concat(map_associations(model)) if @config.include_associated
37
41
 
38
42
  type_template += <<~TYPESCRIPT
39
- interface #{model.model_name.param_key.camelize} {
43
+ #{@config.schema_type} #{model.model_name.param_key.camelize} #{@config.schema_type.to_sym == :type ? "= " : ""}{
40
44
  #{columns.map { |column| "#{indent_as_str}#{column_name_cased(column[:name])}: #{column[:ts_type]};" }.join("\n")}
41
45
  }\n
42
46
  TYPESCRIPT
43
47
  end
48
+
44
49
  type_template = <<~TPL
45
- declare namespace #{TsSchema.configuration.namespace} {
50
+ declare namespace #{@config.namespace} {
46
51
  #{indent_wrapper(type_template)}
47
52
  }
48
53
  TPL
49
54
  end
50
55
 
56
+ def output_file
57
+ path = @config.output
58
+ FileUtils.mkdir_p(File.dirname(path))
59
+
60
+ content = generate
61
+ return if File.exist?(path) && File.read(path) == content
62
+
63
+ File.open(path, 'w') do |f|
64
+ f.write content
65
+ end
66
+ end
67
+
51
68
  def map_column_types(model)
52
69
  model.columns.map { |i|
53
- type = @types[i.type.to_s] || TsSchema.configuration.default_type
70
+ next if @config.field_overrides[i.name.to_s] == :omit
54
71
 
55
- if(enum = model.defined_enums[i.name])
56
- type = enum.keys.map { |k| "'#{k}'" }.join(" | ")
72
+ type = @types[i.type.to_s] || @config.default_type
73
+ name = map_name(i.name)
74
+ null = i.null
75
+ null = true if @config.field_overrides[name]&.to_s == "optional"
76
+
77
+ if(enum = model.defined_enums[name])
78
+ type = enum.keys.map { |k| "'#{k}'" }.join("|")
57
79
  end
58
-
80
+
59
81
  {
60
- name: "#{i.name}#{"?" if i.null }",
61
- ts_type: "#{type}#{" | null" if i.null}"
82
+ name: "#{name}#{"?" if null }",
83
+ ts_type: "#{type}#{" | null" if null}"
62
84
  }
63
- }
85
+ }.compact
86
+ end
87
+
88
+ def map_name(name)
89
+ final_name = name.to_s
90
+ return final_name unless @config.field_overrides[final_name]
91
+
92
+ if @config.field_overrides[final_name]&.to_s != "optional"
93
+ final_name = @config.field_overrides[final_name]&.to_s
94
+
95
+ final_name = map_name(final_name) if @config.field_overrides[final_name]
96
+ end
97
+ final_name
64
98
  end
65
99
 
66
100
  def map_associations(model)
@@ -80,8 +114,14 @@ module TsSchema
80
114
  end
81
115
  end
82
116
 
117
+ private
118
+
119
+ def nested_empty?(arr)
120
+ arr.is_a?(Array) && arr.flatten.empty?
121
+ end
122
+
83
123
  def column_name_cased(name)
84
- case TsSchema.configuration.case.to_sym
124
+ case @config.case.to_sym
85
125
  when :camel
86
126
  name.camelize(:lower)
87
127
  when :pascal
@@ -92,18 +132,18 @@ module TsSchema
92
132
  end
93
133
 
94
134
  def indent_as_str
95
- case TsSchema.configuration.indent.to_sym
135
+ case @config.indent.to_sym
96
136
  when :space || :spaces
97
- "".rjust(TsSchema.configuration.spaces, " ")
137
+ "".rjust(@config.spaces, " ")
98
138
  else
99
139
  "\t"
100
140
  end
101
141
  end
102
142
 
103
143
  def indent_wrapper(str)
104
- case TsSchema.configuration.indent.to_sym
144
+ case @config.indent.to_sym
105
145
  when :space || :spaces
106
- str.indent(TsSchema.configuration.spaces)
146
+ str.indent(@config.spaces)
107
147
  else
108
148
  str.indent(1, "\t")
109
149
  end
@@ -1,3 +1,3 @@
1
1
  module TsSchema
2
- VERSION = "0.1.10"
2
+ VERSION = "0.1.13"
3
3
  end
data/lib/ts_schema.rb CHANGED
@@ -4,6 +4,7 @@ require "ts_schema/railtie"
4
4
  require "ts_schema/configuration"
5
5
  require "ts_schema/schema_generator"
6
6
 
7
+
7
8
  module TsSchema
8
9
  class << self
9
10
  def setup(&block)
@@ -15,14 +16,21 @@ module TsSchema
15
16
  end
16
17
 
17
18
  def generate
18
- SchemaGenerator.new.generate
19
+ if ActiveRecord::Base.connection.migration_context.needs_migration?
20
+ puts "Aborting: There are pending migrations"
21
+ else
22
+ SchemaGenerator.new(@configuration).generate
23
+ end
19
24
  end
20
25
 
21
26
  def output_file
22
- SchemaGenerator.new.output_file
27
+ if ActiveRecord::Base.connection.migration_context.needs_migration?
28
+ puts "Aborting: There are pending migrations"
29
+ else
30
+ SchemaGenerator.new(@configuration).output_file
31
+ end
23
32
  end
24
33
  end
25
34
  end
26
35
 
27
36
  require "generators/install_generator"
28
- require "generators/generate_generator"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ts_schema
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.10
4
+ version: 0.1.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Avram Walden
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-04 00:00:00.000000000 Z
11
+ date: 2022-04-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 6.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: railties
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '4'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '4'
27
41
  description: ''
28
42
  email:
29
43
  - aviemet@gmail.com
@@ -34,7 +48,6 @@ files:
34
48
  - MIT-LICENSE
35
49
  - README.md
36
50
  - Rakefile
37
- - lib/generators/generate_generator.rb
38
51
  - lib/generators/install_generator.rb
39
52
  - lib/generators/templates/ts_schema.rb
40
53
  - lib/tasks/ts_schema_tasks.rake
@@ -50,7 +63,7 @@ licenses:
50
63
  metadata:
51
64
  homepage_uri: https://github.com/aviemet
52
65
  source_code_uri: https://github.com/aviemet/ts_schema
53
- post_install_message:
66
+ post_install_message:
54
67
  rdoc_options: []
55
68
  require_paths:
56
69
  - lib
@@ -66,7 +79,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
66
79
  version: '0'
67
80
  requirements: []
68
81
  rubygems_version: 3.2.32
69
- signing_key:
82
+ signing_key:
70
83
  specification_version: 4
71
84
  summary: Generates typescript definitions from ActiveRecord
72
85
  test_files: []
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'rails/generators'
4
- require 'rails/generators/base'
5
-
6
- module TsSchema
7
- module Generators
8
- class GenerateGenerator < Rails::Generators::Base
9
-
10
- desc "Generates a schema.d.ts file"
11
-
12
- def generate_schema
13
- create_file TsSchema.configuration.output, TsSchema.generate
14
- end
15
- end
16
- end
17
- end