diff --git a/CodeGenerator/CodeGenerator.cs b/CodeGenerator/CodeGenerator.cs index 3b5e541..61c12b4 100644 --- a/CodeGenerator/CodeGenerator.cs +++ b/CodeGenerator/CodeGenerator.cs @@ -46,7 +46,7 @@ public Task Generate(GenerateRequest generateRequest) DbDriver = InstantiateDriver(); var fileQueries = GetFileQueries(); var files = fileQueries - .Select(fq => GenerateFile(fq.Value, fq.Key)) + .SelectMany(fq => GenerateFiles(fq.Value, fq.Key)) .AppendIfNotNull(GenerateGemfile()); return Task.FromResult(new GenerateResponse { Files = { files } }); @@ -67,20 +67,38 @@ string QueryFilenameToClassName(string filenameWithExtension) } } - private File GenerateFile(IList queries, string className) + private IEnumerable GenerateFiles(IList queries, string className) { var (requiredGems, moduleDeclaration) = GenerateModule(queries, className); - var contents = $""" - {AutoGeneratedComment} - {requiredGems.Select(r => r.Build()).JoinByNewLine()} - - {moduleDeclaration.Build()} - """; - return new File + IEnumerable files = new List { - Name = $"{className.SnakeCase()}.rb", - Contents = ByteString.CopyFromUtf8(contents) + new() + { + Name = $"{className.SnakeCase()}.rb", + Contents = ByteString.CopyFromUtf8( + $""" + {AutoGeneratedComment} + {requiredGems.Select(r => r.Build()).JoinByNewLine()} + + {moduleDeclaration.BuildCode()} + """ + ) + } }; + if (!Options.GenerateTypes) + return files; + + files = files.Append(new File + { + Name = $"{className.SnakeCase()}.rbs", + Contents = ByteString.CopyFromUtf8( + $""" + {AutoGeneratedComment} + {moduleDeclaration.BuildType()} + """ + ) + }); + return files; } private File? GenerateGemfile() @@ -91,15 +109,18 @@ private File GenerateFile(IList queries, string className) return new File { Name = "Gemfile", - Contents = ByteString.CopyFromUtf8($""" - source 'https://rubygems.org' + Contents = ByteString.CopyFromUtf8( + $""" + source 'https://rubygems.org' - {requireGems} - """) + {requireGems} + """ + ) }; } - private (IEnumerable, ModuleDeclaration) GenerateModule(IList queries, string className) + private (IEnumerable, ModuleDeclaration) GenerateModule(IList queries, + string className) { var requiredGems = DbDriver.GetRequiredGems(); var initMethod = DbDriver.GetInitMethod(); @@ -130,37 +151,39 @@ ClassDeclaration GetClassDeclaration() } } - private static SimpleStatement GenerateDataclass(string name, ClassMember classMember, IEnumerable columns, + private IComposableRbsType GenerateDataclass(string funcName, ClassMember classMember, IList columns, Options options) { - var dataclassName = $"{name.FirstCharToUpper()}{classMember.Name()}"; - var dataColumns = columns.Select(c => $":{c.Name.ToLower()}").ToList(); - var dataColumnsStr = dataColumns.JoinByCommaAndFormat(); - return new SimpleStatement(dataclassName, - new SimpleExpression(options.RubyVersion.ImmutableDataSupported() - ? $"Data.define({dataColumnsStr})" - : $"Struct.new({dataColumnsStr})")); + var dataclassName = $"{funcName.FirstCharToUpper()}{classMember.Name()}"; + var nameToType = columns.ToDictionary( + kv => kv.Name, + kv => DbDriver.GetColumnType(kv) + ); + return options.RubyVersion.ImmutableDataSupported() + ? new DataDefine(dataclassName, nameToType) + : new NewStruct(dataclassName, nameToType); } - private SimpleStatement? GetQueryColumnsDataclass(Query query) + private IComposableRbsType? GetQueryColumnsDataclass(Query query) { return query.Columns.Count <= 0 ? null : GenerateDataclass(query.Name, ClassMember.Row, query.Columns, Options); } - private SimpleStatement? GetQueryParamsDataclass(Query query) + + private IComposableRbsType? GetQueryParamsDataclass(Query query) { if (query.Params.Count <= 0) return null; - var columns = query.Params.Select(p => p.Column); + var columns = query.Params.Select(p => p.Column).ToList(); return GenerateDataclass(query.Name, ClassMember.Args, columns, Options); } private MethodDeclaration GetMethodDeclaration(Query query) { var queryTextConstant = GetInterfaceName(ClassMember.Sql); - var argInterface = GetInterfaceName(ClassMember.Args).SnakeCase(); + var argInterface = GetInterfaceName(ClassMember.Args); var returnInterface = GetInterfaceName(ClassMember.Row); var funcName = query.Name.SnakeCase(); diff --git a/Drivers/DbDriver.cs b/Drivers/DbDriver.cs index c3ad65e..d6d7990 100644 --- a/Drivers/DbDriver.cs +++ b/Drivers/DbDriver.cs @@ -1,5 +1,6 @@ using Plugin; using RubyCodegen; +using System; using System.Collections.Generic; namespace SqlcGenRuby.Drivers; @@ -15,7 +16,20 @@ protected static IEnumerable GetCommonGems() public abstract MethodDeclaration GetInitMethod(); - public abstract SimpleStatement QueryTextConstantDeclare(Query query); + protected abstract List<(string, HashSet)> GetColumnMapping(); + + public string GetColumnType(Column column) + { + var columnType = column.Type.Name.ToLower(); + foreach (var (csharpType, dbTypes) in GetColumnMapping()) + { + if (dbTypes.Contains(columnType)) + return csharpType; + } + throw new NotSupportedException($"Unsupported column type: {column.Type.Name}"); + } + + public abstract PropertyDeclaration QueryTextConstantDeclare(Query query); public abstract IComposable PrepareStmt(string funcName, string queryTextConstant); diff --git a/Drivers/MethodGen.cs b/Drivers/MethodGen.cs index d69fdc0..9d43582 100644 --- a/Drivers/MethodGen.cs +++ b/Drivers/MethodGen.cs @@ -26,7 +26,11 @@ public MethodDeclaration OneDeclare(string funcName, string queryTextConstant, s ] ).ToList(); - return new MethodDeclaration(funcName, GetMethodArgs(argInterface, parameters), + return new MethodDeclaration( + funcName, + argInterface, + GetMethodArgs(argInterface, parameters), + returnInterface, new List { new WithResource(Variable.Pool.AsProperty(), Variable.Client.AsVar(), withResourceBody.ToList()) @@ -58,7 +62,11 @@ public MethodDeclaration ManyDeclare(string funcName, string queryTextConstant, ] ); - return new MethodDeclaration(funcName, GetMethodArgs(argInterface, parameters), + return new MethodDeclaration( + funcName, + argInterface, + GetMethodArgs(argInterface, parameters), + returnInterface, new List { new WithResource(Variable.Pool.AsProperty(), Variable.Client.AsVar(), withResourceBody.ToList()) @@ -76,7 +84,10 @@ public MethodDeclaration ExecDeclare(string funcName, string queryTextConstant, .Append(dbDriver.ExecuteStmt(funcName, queryParams)) .ToList(); - return new MethodDeclaration(funcName, GetMethodArgs(argInterface, parameters), + return new MethodDeclaration(funcName, + argInterface, + GetMethodArgs(argInterface, parameters), + null, new List { new WithResource(Variable.Pool.AsProperty(), Variable.Client.AsVar(), withResourceBody.ToList() @@ -100,7 +111,10 @@ public MethodDeclaration ExecLastIdDeclare(string funcName, string queryTextCons ); return new MethodDeclaration( - funcName, GetMethodArgs(argInterface, parameters), + funcName, + argInterface, + GetMethodArgs(argInterface, parameters), + "Integer", new List { new WithResource(Variable.Pool.AsProperty(), Variable.Client.AsVar(), @@ -111,7 +125,7 @@ public MethodDeclaration ExecLastIdDeclare(string funcName, string queryTextCons private static SimpleStatement? GetQueryParams(string argInterface, IList parameters) { - var queryParams = parameters.Select(p => $"{argInterface}.{p.Column.Name}").ToList(); + var queryParams = parameters.Select(p => $"{argInterface.SnakeCase()}.{p.Column.Name}").ToList(); return queryParams.Count == 0 ? null : new SimpleStatement(Variable.QueryParams.AsVar(), diff --git a/Drivers/Mysql2Driver.cs b/Drivers/Mysql2Driver.cs index 8ad319e..9fbd22e 100644 --- a/Drivers/Mysql2Driver.cs +++ b/Drivers/Mysql2Driver.cs @@ -21,17 +21,65 @@ public override IEnumerable GetRequiredGems() public override MethodDeclaration GetInitMethod() { - return new MethodDeclaration("initialize", "connection_pool_params, mysql2_params", + var connectionPoolInit = new NewObject("ConnectionPool", + new[] { new SimpleExpression("**connection_pool_params") }, + new List { new SimpleExpression("Mysql2::Client.new(**mysql2_params)") }); + return new MethodDeclaration( + "initialize", + "Hash[String, String], Hash[String, String]", + "connection_pool_params, mysql2_params", + null, [ - new SimpleStatement(Variable.Pool.AsProperty(), new SimpleExpression( - "ConnectionPool::new(**connection_pool_params) { Mysql2::Client.new(**mysql2_params) }")) + new PropertyDeclaration(Variable.Pool.AsProperty(), "untyped", connectionPoolInit) ] ); } - public override SimpleStatement QueryTextConstantDeclare(Query query) + protected override List<(string, HashSet)> GetColumnMapping() { - return new SimpleStatement($"{query.Name}{ClassMember.Sql}", new SimpleExpression($"%q({query.Text})")); + return + [ + ("Array[Integer]", [ + "binary", + "bit", + "blob", + "longblob", + "mediumblob", + "tinyblob", + "varbinary" + ]), + ("String", [ + "char", + "date", + "datetime", + "decimal", + "longtext", + "mediumtext", + "text", + "time", + "timestamp", + "tinytext", + "varchar", + "json" + ]), + ("Integer", [ + "bigint", + "int", + "mediumint", + "smallint", + "tinyint", + "year" + ]), + ("Float", ["double", "float"]), + ]; + } + + public override PropertyDeclaration QueryTextConstantDeclare(Query query) + { + return new PropertyDeclaration( + $"{query.Name}{ClassMember.Sql}", + "String", + new SimpleExpression($"%q({query.Text})")); } public override IComposable PrepareStmt(string _, string queryTextConstant) diff --git a/Drivers/PgDriver.cs b/Drivers/PgDriver.cs index e1174c0..1a3fcad 100644 --- a/Drivers/PgDriver.cs +++ b/Drivers/PgDriver.cs @@ -24,14 +24,17 @@ public override IEnumerable GetRequiredGems() public override MethodDeclaration GetInitMethod() { - return new MethodDeclaration("initialize", "connection_pool_params, pg_params", + var connectionPoolInit = new NewObject("ConnectionPool", + new[] { new SimpleExpression("**connection_pool_params") }, + PgClientCreate()); + return new MethodDeclaration( + "initialize", + "Hash[String, String], Hash[String, String]", + "connection_pool_params, pg_params", + null, [ - new SimpleStatement( - Variable.Pool.AsProperty(), - new NewObject("ConnectionPool", - new[] { new SimpleExpression("**connection_pool_params") }, - PgClientCreate())), - new SimpleStatement(Variable.PreparedStatements.AsProperty(), new SimpleExpression("Set[]")) + new PropertyDeclaration(Variable.Pool.AsProperty(), "untyped", connectionPoolInit), + new PropertyDeclaration(Variable.PreparedStatements.AsProperty(), "Set[String]", new SimpleExpression("Set[]")) ] ); @@ -50,11 +53,61 @@ IList PgClientCreate() } } - public override SimpleStatement QueryTextConstantDeclare(Query query) + protected override List<(string, HashSet)> GetColumnMapping() + { + return + [ + ("bool", [ + "bool", + "boolean" + ]), + ("Array[Integer]", [ + "binary", + "bit", + "bytea", + "blob", + "longblob", + "mediumblob", + "tinyblob", + "varbinary" + ]), + ("String", [ + "char", + "date", + "datetime", + "longtext", + "mediumtext", + "text", + "bpchar", + "time", + "timestamp", + "tinytext", + "varchar", + "json" + ]), + ("Integer", [ + "int2", + "int4", + "int8", + "serial", + "bigserial" + ]), + ("Float", [ + "numeric", + "float4", + "float8", + "decimal" + ]) + ]; + } + + public override PropertyDeclaration QueryTextConstantDeclare(Query query) { var counter = 1; - var transformedQueryText = BindRegexToReplace().Replace(query.Text, m => $"${counter++}"); - return new SimpleStatement($"{query.Name}{ClassMember.Sql}", + var transformedQueryText = BindRegexToReplace().Replace(query.Text, _ => $"${counter++}"); + return new PropertyDeclaration( + $"{query.Name}{ClassMember.Sql}", + "String", new SimpleExpression($"%q({transformedQueryText})")); } diff --git a/Extensions/ListExtensions.cs b/Extensions/ListExtensions.cs index 39dcc0a..56bcd15 100644 --- a/Extensions/ListExtensions.cs +++ b/Extensions/ListExtensions.cs @@ -17,7 +17,7 @@ public static string JoinByNewLine(this IEnumerable me, int cnt = 1) public static string JoinByCommaAndFormat(this IList me) { return me.Count < MaxElementsPerLine - ? string.Join(", ", me).Indent() + ? string.Join(", ", me) : $"\n{string.Join(",\n", me).Indent()}\n"; } } \ No newline at end of file diff --git a/PluginOptions/Options.cs b/PluginOptions/Options.cs index be1b5d2..82ce59a 100644 --- a/PluginOptions/Options.cs +++ b/PluginOptions/Options.cs @@ -22,6 +22,7 @@ public Options(GenerateRequest generateRequest) Enum.TryParse(rawOptions.DriverName, true, out DriverName outDriverName); DriverName = outDriverName; GenerateGemfile = rawOptions.GenerateGemfile; + GenerateTypes = rawOptions.GenerateTypes; RubyVersion = RubyVersionExtensions.ParseName(rawOptions.RubyVersion); } @@ -30,4 +31,6 @@ public Options(GenerateRequest generateRequest) public RubyVersion RubyVersion { get; } public bool GenerateGemfile { get; } + + public bool GenerateTypes { get; } } \ No newline at end of file diff --git a/PluginOptions/RawOptions.cs b/PluginOptions/RawOptions.cs index a78b255..d3e6507 100644 --- a/PluginOptions/RawOptions.cs +++ b/PluginOptions/RawOptions.cs @@ -10,6 +10,9 @@ internal class RawOptions [JsonPropertyName("generateGemfile")] public bool GenerateGemfile { get; init; } // not generating Gemfile files by default + [JsonPropertyName("generateTypes")] + public bool GenerateTypes { get; init; } = true; + [JsonPropertyName("rubyVersion")] public string RubyVersion { get; init; } = "3.3"; } \ No newline at end of file diff --git a/RubySyntax/Basic.cs b/RubySyntax/Basic.cs index 61ce34f..0730003 100644 --- a/RubySyntax/Basic.cs +++ b/RubySyntax/Basic.cs @@ -15,55 +15,153 @@ public string Name() } } -public class ModuleDeclaration(string name, IEnumerable members) : IComposable +public class ModuleDeclaration(string name, IEnumerable members) : IComposable, IRbsType { - public string Build() + public string BuildCode() { var moduleBody = members - .Select(m => m.Build()) + .Select(m => m.BuildCode()) + .JoinByNewLine(2) + .Indent(); + return $"module {name}\n{moduleBody}\nend"; + } + + public string BuildType() + { + var moduleBody = members + .Where(m => m is IRbsType) + .Select(m => ((IRbsType)m).BuildType()) .JoinByNewLine(2) .Indent(); return $"module {name}\n{moduleBody}\nend"; } } -public class ClassDeclaration(string name, IEnumerable members) : IComposable +public class ClassDeclaration(string name, IEnumerable members) : IComposable, IRbsType { - public string Build() + public string BuildCode() { var classBody = members - .Select(m => m.Build()) + .Select(m => m.BuildCode()) .JoinByNewLine(2) .Indent(); return $"class {name}\n{classBody}\nend"; } + + public string BuildType() + { + var moduleBody = members + .Where(m => m is IRbsType) + .Select(m => ((IRbsType)m).BuildType()) + .JoinByNewLine() + .Indent(); + return $"class {name}\n{moduleBody}\nend"; + } } -public class MethodDeclaration(string name, string? args, IEnumerable statements) : IComposable +public class MethodDeclaration(string name, string? argInterface, string? args, string? returnInterface, + IEnumerable statements) : IComposable, IRbsType { - public string Build() + public string BuildCode() { var methodParams = args is null ? string.Empty : $"({args})"; var methodBody = statements - .Select(c => c.Build()) + .Select(c => c.BuildCode()) .JoinByNewLine() .Indent(); return $"def {name}{methodParams}\n{methodBody}\nend"; } + + public string BuildType() + { + var methodParams = args is null ? string.Empty : $"({argInterface}) "; + var returnType = returnInterface ?? "void"; + var propertiesDef = statements + .Where(s => s is PropertyDeclaration) + .Select(s => ((IRbsType)s).BuildType()) + .JoinByNewLine(); + return $"{propertiesDef}\ndef {name}: {methodParams}-> {returnType}"; + } +} + +public class PropertyDeclaration(string name, string constType, IComposable value) : IComposableRbsType +{ + public string BuildCode() + { + return $"{name.FirstCharToUpper()} = {value.BuildCode()}".TrimTrailingWhitespacesPerLine(); + } + + public string BuildType() + { + return $"{name}: {constType}"; + } } public class SimpleStatement(string assignment, IComposable expression) : IComposable { - public string Build() + public string BuildCode() { - return $"{assignment} = {expression.Build()}".TrimTrailingWhitespacesPerLine(); + return $"{assignment} = {expression.BuildCode()}".TrimTrailingWhitespacesPerLine(); } } public class SimpleExpression(string expression) : IComposable { - public string Build() + public string BuildCode() { return expression; } +} + +public class DataDefine(string name, Dictionary nameToType) : IComposableRbsType +{ + public string BuildCode() + { + var attributes = nameToType + .Select(kv => $":{kv.Key}") + .ToList() + .JoinByCommaAndFormat(); + return $"class {name} < Data.define({attributes})\nend"; + } + + public string BuildType() + { + var attributes = nameToType + .Select(column => $"attr_reader {column.Key}: {column.Value}") + .ToList() + .JoinByNewLine() + .Indent(); + var initializeArgs = nameToType + .Select(kv => $"{kv.Value} {kv.Key}") + .ToList() + .JoinByCommaAndFormat() + .Indent(); + var initializeDef = $"def initialize: ({initializeArgs}) -> void".Indent(); + return $"class {name}\n{attributes}\n{initializeDef}\nend"; + } +} + +public class NewStruct(string name, Dictionary nameToType) : IComposableRbsType +{ + public string BuildCode() + { + var initializeDef = nameToType.Select(kv => kv.Key).ToList().JoinByCommaAndFormat(); + return $"class {name} < Struct.new({initializeDef})\nend"; + } + + public string BuildType() + { + var attributes = nameToType + .Select(kv => $"attr_accessor {kv.Key}: {kv.Value}") + .ToList() + .JoinByNewLine() + .Indent(); + var initializeArgs = nameToType + .Select(kv => $"{kv.Value} {kv.Key}") + .ToList() + .JoinByCommaAndFormat() + .Indent(); + var initializeDef = $"def self.new: ({initializeArgs}) -> {name}".Indent(); + return $"class {name} < ::Struct\n{attributes}\n{initializeDef}\nend"; + } } \ No newline at end of file diff --git a/RubySyntax/Flows.cs b/RubySyntax/Flows.cs index da29bf0..cd92e27 100644 --- a/RubySyntax/Flows.cs +++ b/RubySyntax/Flows.cs @@ -4,10 +4,10 @@ namespace RubyCodegen; public class WithResource(string resourceFrom, string resourceName, IList statements) : IComposable { - public string Build() + public string BuildCode() { var withResourceBody = statements - .Select(s => s.Build()) + .Select(s => s.BuildCode()) .JoinByNewLine() .TrimTrailingWhitespacesPerLine() .Indent(); @@ -19,17 +19,17 @@ public string Build() public class IfCondition(string condition, IList thenStatements, IList? elseStatements = null) : IComposable { - public string Build() + public string BuildCode() { var thenBody = thenStatements - .Select(s => s.Build()) + .Select(s => s.BuildCode()) .JoinByNewLine() .TrimTrailingWhitespacesPerLine() .Indent(); if (elseStatements is null || elseStatements.Count == 0) return $"if {condition}\n{thenBody}\nend"; var elseBody = elseStatements - .Select(s => s.Build()) + .Select(s => s.BuildCode()) .JoinByNewLine() .TrimTrailingWhitespacesPerLine() .Indent(); @@ -39,10 +39,10 @@ public string Build() public class UnlessCondition(string condition, IList thenStatements) : IComposable { - public string Build() + public string BuildCode() { var thenBody = thenStatements - .Select(s => s.Build()) + .Select(s => s.BuildCode()) .JoinByNewLine() .TrimTrailingWhitespacesPerLine() .Indent(); @@ -53,10 +53,10 @@ public string Build() public class NewObject(string objectType, IList initExpressions, IList? bodyStatements = null) : IComposable { - public string Build() + public string BuildCode() { var initParams = initExpressions - .Select(e => e.Build()) + .Select(e => e.BuildCode()) .ToList() .JoinByCommaAndFormat(); var baseCommand = $"{objectType}.new({initParams})"; @@ -64,7 +64,7 @@ public string Build() return baseCommand; var body = "{\n" + bodyStatements - .Select(s => s.Build()) + .Select(s => s.BuildCode()) .JoinByNewLine() .TrimTrailingWhitespacesPerLine() .Indent() + "\n}"; @@ -74,10 +74,10 @@ public string Build() public class ForeachLoop(string collectionVar, string controlVar, IList statements) : IComposable { - public string Build() + public string BuildCode() { var foreachBody = statements - .Select(s => s.Build()) + .Select(s => s.BuildCode()) .JoinByNewLine(); var foreachLoop = $"{collectionVar}.each do |{controlVar}|\n{foreachBody}\nend"; return foreachLoop; @@ -86,8 +86,8 @@ public string Build() public class ListAppend(string listVar, NewObject newObject) : IComposable { - public string Build() + public string BuildCode() { - return $"{listVar} << {newObject.Build()}"; + return $"{listVar} << {newObject.BuildCode()}"; } } \ No newline at end of file diff --git a/RubySyntax/IComposable.cs b/RubySyntax/IComposable.cs deleted file mode 100644 index d5eddbf..0000000 --- a/RubySyntax/IComposable.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace RubyCodegen; - -public interface IComposable -{ - string Build(); -} \ No newline at end of file diff --git a/RubySyntax/Interfaces.cs b/RubySyntax/Interfaces.cs new file mode 100644 index 0000000..618fbbf --- /dev/null +++ b/RubySyntax/Interfaces.cs @@ -0,0 +1,13 @@ +namespace RubyCodegen; + +public interface IComposable +{ + string BuildCode(); +} + +public interface IRbsType +{ + string BuildType(); +} + +public interface IComposableRbsType : IComposable, IRbsType; \ No newline at end of file diff --git a/examples/mysql2/query_sql.rb b/examples/mysql2/query_sql.rb index 15b03d1..a49f973 100644 --- a/examples/mysql2/query_sql.rb +++ b/examples/mysql2/query_sql.rb @@ -6,14 +6,17 @@ module Mysql2Codegen GetAuthorSql = %q(SELECT id, name, bio FROM authors WHERE id = ? LIMIT 1) - GetAuthorRow = Data.define( :id, :name, :bio) + class GetAuthorRow < Data.define(:id, :name, :bio) + end - GetAuthorArgs = Data.define( :id) + class GetAuthorArgs < Data.define(:id) + end ListAuthorsSql = %q(SELECT id, name, bio FROM authors ORDER BY name) - ListAuthorsRow = Data.define( :id, :name, :bio) + class ListAuthorsRow < Data.define(:id, :name, :bio) + end CreateAuthorSql = %q(INSERT INTO authors ( name, bio @@ -21,13 +24,15 @@ module Mysql2Codegen ?, ? )) - CreateAuthorArgs = Data.define( :name, :bio) + class CreateAuthorArgs < Data.define(:name, :bio) + end UpdateAuthorSql = %q(UPDATE authors SET bio = ? WHERE id = ?) - UpdateAuthorArgs = Data.define( :bio, :id) + class UpdateAuthorArgs < Data.define(:bio, :id) + end CreateAuthorReturnIdSql = %q(INSERT INTO authors ( name, bio @@ -35,17 +40,19 @@ module Mysql2Codegen ?, ? )) - CreateAuthorReturnIdArgs = Data.define( :name, :bio) + class CreateAuthorReturnIdArgs < Data.define(:name, :bio) + end DeleteAuthorSql = %q(DELETE FROM authors WHERE id = ?) - DeleteAuthorArgs = Data.define( :id) + class DeleteAuthorArgs < Data.define(:id) + end TestSql = %q(SELECT c_bit, c_tinyint, c_bool, c_boolean, c_smallint, c_mediumint, c_int, c_integer, c_bigint, c_serial, c_decimal, c_dec, c_numeric, c_fixed, c_float, c_double, c_double_precision, c_date, c_time, c_datetime, c_timestamp, c_year, c_char, c_nchar, c_national_char, c_varchar, c_binary, c_varbinary, c_tinyblob, c_tinytext, c_blob, c_text, c_mediumblob, c_mediumtext, c_longblob, c_longtext, c_json FROM node_mysql_types LIMIT 1) - TestRow = Data.define( + class TestRow < Data.define( :c_bit, :c_tinyint, :c_bool, @@ -84,20 +91,23 @@ module Mysql2Codegen :c_longtext, :c_json ) + end class QuerySql def initialize(connection_pool_params, mysql2_params) - @pool = ConnectionPool::new(**connection_pool_params) { Mysql2::Client.new(**mysql2_params) } + @pool = ConnectionPool.new(**connection_pool_params) { + Mysql2::Client.new(**mysql2_params) + } end def get_author(get_author_args) @pool.with do |client| - query_params = [ get_author_args.id] + query_params = [get_author_args.id] stmt = client.prepare(GetAuthorSql) result = stmt.execute(*query_params) row = result.first return nil if row.nil? - entity = GetAuthorRow.new( row['id'], row['name'], row['bio']) + entity = GetAuthorRow.new(row['id'], row['name'], row['bio']) return entity end end @@ -108,7 +118,7 @@ def list_authors result = stmt.execute entities = [] result.each do |row| - entities << ListAuthorsRow.new( row['id'], row['name'], row['bio']) + entities << ListAuthorsRow.new(row['id'], row['name'], row['bio']) end return entities end @@ -116,7 +126,7 @@ def list_authors def create_author(create_author_args) @pool.with do |client| - query_params = [ create_author_args.name, create_author_args.bio] + query_params = [create_author_args.name, create_author_args.bio] stmt = client.prepare(CreateAuthorSql) stmt.execute(*query_params) end @@ -124,7 +134,7 @@ def create_author(create_author_args) def update_author(update_author_args) @pool.with do |client| - query_params = [ update_author_args.bio, update_author_args.id] + query_params = [update_author_args.bio, update_author_args.id] stmt = client.prepare(UpdateAuthorSql) stmt.execute(*query_params) end @@ -132,7 +142,7 @@ def update_author(update_author_args) def create_author_return_id(create_author_return_id_args) @pool.with do |client| - query_params = [ create_author_return_id_args.name, create_author_return_id_args.bio] + query_params = [create_author_return_id_args.name, create_author_return_id_args.bio] stmt = client.prepare(CreateAuthorReturnIdSql) stmt.execute(*query_params) return client.last_id @@ -141,7 +151,7 @@ def create_author_return_id(create_author_return_id_args) def delete_author(delete_author_args) @pool.with do |client| - query_params = [ delete_author_args.id] + query_params = [delete_author_args.id] stmt = client.prepare(DeleteAuthorSql) stmt.execute(*query_params) end diff --git a/examples/mysql2/query_sql.rbs b/examples/mysql2/query_sql.rbs new file mode 100644 index 0000000..5ee7afe --- /dev/null +++ b/examples/mysql2/query_sql.rbs @@ -0,0 +1,156 @@ +# auto-generated by sqlc - do not edit +module Mysql2Codegen + GetAuthorSql: String + + class GetAuthorRow + attr_reader id: Integer + attr_reader name: String + attr_reader bio: String + def initialize: ( Integer id, String name, String bio) -> void + end + + class GetAuthorArgs + attr_reader id: Integer + def initialize: ( Integer id) -> void + end + + ListAuthorsSql: String + + class ListAuthorsRow + attr_reader id: Integer + attr_reader name: String + attr_reader bio: String + def initialize: ( Integer id, String name, String bio) -> void + end + + CreateAuthorSql: String + + class CreateAuthorArgs + attr_reader name: String + attr_reader bio: String + def initialize: ( String name, String bio) -> void + end + + UpdateAuthorSql: String + + class UpdateAuthorArgs + attr_reader bio: String + attr_reader id: Integer + def initialize: ( String bio, Integer id) -> void + end + + CreateAuthorReturnIdSql: String + + class CreateAuthorReturnIdArgs + attr_reader name: String + attr_reader bio: String + def initialize: ( String name, String bio) -> void + end + + DeleteAuthorSql: String + + class DeleteAuthorArgs + attr_reader id: Integer + def initialize: ( Integer id) -> void + end + + TestSql: String + + class TestRow + attr_reader c_bit: Array[Integer] + attr_reader c_tinyint: Integer + attr_reader c_bool: Integer + attr_reader c_boolean: Integer + attr_reader c_smallint: Integer + attr_reader c_mediumint: Integer + attr_reader c_int: Integer + attr_reader c_integer: Integer + attr_reader c_bigint: Integer + attr_reader c_serial: Integer + attr_reader c_decimal: String + attr_reader c_dec: String + attr_reader c_numeric: String + attr_reader c_fixed: String + attr_reader c_float: Float + attr_reader c_double: Float + attr_reader c_double_precision: Float + attr_reader c_date: String + attr_reader c_time: String + attr_reader c_datetime: String + attr_reader c_timestamp: String + attr_reader c_year: Integer + attr_reader c_char: String + attr_reader c_nchar: String + attr_reader c_national_char: String + attr_reader c_varchar: String + attr_reader c_binary: Array[Integer] + attr_reader c_varbinary: Array[Integer] + attr_reader c_tinyblob: Array[Integer] + attr_reader c_tinytext: String + attr_reader c_blob: Array[Integer] + attr_reader c_text: String + attr_reader c_mediumblob: Array[Integer] + attr_reader c_mediumtext: String + attr_reader c_longblob: Array[Integer] + attr_reader c_longtext: String + attr_reader c_json: String + def initialize: ( + Array[Integer] c_bit, + Integer c_tinyint, + Integer c_bool, + Integer c_boolean, + Integer c_smallint, + Integer c_mediumint, + Integer c_int, + Integer c_integer, + Integer c_bigint, + Integer c_serial, + String c_decimal, + String c_dec, + String c_numeric, + String c_fixed, + Float c_float, + Float c_double, + Float c_double_precision, + String c_date, + String c_time, + String c_datetime, + String c_timestamp, + Integer c_year, + String c_char, + String c_nchar, + String c_national_char, + String c_varchar, + Array[Integer] c_binary, + Array[Integer] c_varbinary, + Array[Integer] c_tinyblob, + String c_tinytext, + Array[Integer] c_blob, + String c_text, + Array[Integer] c_mediumblob, + String c_mediumtext, + Array[Integer] c_longblob, + String c_longtext, + String c_json + ) -> void + end + + class QuerySql + @pool: untyped + def initialize: (Hash[String, String], Hash[String, String]) -> void + + def get_author: (GetAuthorArgs) -> GetAuthorRow + + def list_authors: -> ListAuthorsRow + + def create_author: (CreateAuthorArgs) -> void + + def update_author: (UpdateAuthorArgs) -> void + + def create_author_return_id: (CreateAuthorReturnIdArgs) -> Integer + + def delete_author: (DeleteAuthorArgs) -> void + + def test: -> TestRow + end +end \ No newline at end of file diff --git a/examples/pg/query_sql.rb b/examples/pg/query_sql.rb index 0d95168..9b08310 100644 --- a/examples/pg/query_sql.rb +++ b/examples/pg/query_sql.rb @@ -7,14 +7,17 @@ module PgCodegen GetAuthorSql = %q(SELECT id, name, bio FROM authors WHERE id = $1 LIMIT 1) - GetAuthorRow = Data.define( :id, :name, :bio) + class GetAuthorRow < Data.define(:id, :name, :bio) + end - GetAuthorArgs = Data.define( :id) + class GetAuthorArgs < Data.define(:id) + end ListAuthorsSql = %q(SELECT id, name, bio FROM authors ORDER BY name) - ListAuthorsRow = Data.define( :id, :name, :bio) + class ListAuthorsRow < Data.define(:id, :name, :bio) + end CreateAuthorSql = %q(INSERT INTO authors ( name, bio @@ -23,19 +26,22 @@ module PgCodegen ) RETURNING id, name, bio) - CreateAuthorRow = Data.define( :id, :name, :bio) + class CreateAuthorRow < Data.define(:id, :name, :bio) + end - CreateAuthorArgs = Data.define( :name, :bio) + class CreateAuthorArgs < Data.define(:name, :bio) + end DeleteAuthorSql = %q(DELETE FROM authors WHERE id = $1) - DeleteAuthorArgs = Data.define( :id) + class DeleteAuthorArgs < Data.define(:id) + end TestSql = %q(SELECT c_bit, c_smallint, c_boolean, c_integer, c_bigint, c_serial, c_decimal, c_numeric, c_real, c_double_precision, c_date, c_time, c_timestamp, c_char, c_varchar, c_bytea, c_text, c_json FROM node_postgres_types LIMIT 1) - TestRow = Data.define( + class TestRow < Data.define( :c_bit, :c_smallint, :c_boolean, @@ -55,10 +61,11 @@ module PgCodegen :c_text, :c_json ) + end class QuerySql def initialize(connection_pool_params, pg_params) - @pool = ConnectionPool.new( **connection_pool_params) { + @pool = ConnectionPool.new(**connection_pool_params) { client = PG.connect(**pg_params) client.type_map_for_results = PG::BasicTypeMapForResults.new client client @@ -68,7 +75,7 @@ def initialize(connection_pool_params, pg_params) def get_author(get_author_args) @pool.with do |client| - query_params = [ get_author_args.id] + query_params = [get_author_args.id] unless @prepared_statements.include?('get_author') client.prepare('get_author', GetAuthorSql) @prepared_statements.add('get_author') @@ -76,7 +83,7 @@ def get_author(get_author_args) result = client.exec_prepared('get_author', query_params) row = result.first return nil if row.nil? - entity = GetAuthorRow.new( row['id'], row['name'], row['bio']) + entity = GetAuthorRow.new(row['id'], row['name'], row['bio']) return entity end end @@ -90,7 +97,7 @@ def list_authors result = client.exec_prepared('list_authors') entities = [] result.each do |row| - entities << ListAuthorsRow.new( row['id'], row['name'], row['bio']) + entities << ListAuthorsRow.new(row['id'], row['name'], row['bio']) end return entities end @@ -98,7 +105,7 @@ def list_authors def create_author(create_author_args) @pool.with do |client| - query_params = [ create_author_args.name, create_author_args.bio] + query_params = [create_author_args.name, create_author_args.bio] unless @prepared_statements.include?('create_author') client.prepare('create_author', CreateAuthorSql) @prepared_statements.add('create_author') @@ -106,14 +113,14 @@ def create_author(create_author_args) result = client.exec_prepared('create_author', query_params) row = result.first return nil if row.nil? - entity = CreateAuthorRow.new( row['id'], row['name'], row['bio']) + entity = CreateAuthorRow.new(row['id'], row['name'], row['bio']) return entity end end def delete_author(delete_author_args) @pool.with do |client| - query_params = [ delete_author_args.id] + query_params = [delete_author_args.id] unless @prepared_statements.include?('delete_author') client.prepare('delete_author', DeleteAuthorSql) @prepared_statements.add('delete_author') diff --git a/examples/pg/query_sql.rbs b/examples/pg/query_sql.rbs new file mode 100644 index 0000000..64f92a5 --- /dev/null +++ b/examples/pg/query_sql.rbs @@ -0,0 +1,106 @@ +# auto-generated by sqlc - do not edit +module PgCodegen + GetAuthorSql: String + + class GetAuthorRow + attr_reader id: Integer + attr_reader name: String + attr_reader bio: String + def initialize: ( Integer id, String name, String bio) -> void + end + + class GetAuthorArgs + attr_reader id: Integer + def initialize: ( Integer id) -> void + end + + ListAuthorsSql: String + + class ListAuthorsRow + attr_reader id: Integer + attr_reader name: String + attr_reader bio: String + def initialize: ( Integer id, String name, String bio) -> void + end + + CreateAuthorSql: String + + class CreateAuthorRow + attr_reader id: Integer + attr_reader name: String + attr_reader bio: String + def initialize: ( Integer id, String name, String bio) -> void + end + + class CreateAuthorArgs + attr_reader name: String + attr_reader bio: String + def initialize: ( String name, String bio) -> void + end + + DeleteAuthorSql: String + + class DeleteAuthorArgs + attr_reader id: Integer + def initialize: ( Integer id) -> void + end + + TestSql: String + + class TestRow + attr_reader c_bit: Array[Integer] + attr_reader c_smallint: Integer + attr_reader c_boolean: bool + attr_reader c_integer: Integer + attr_reader c_bigint: Integer + attr_reader c_serial: Integer + attr_reader c_decimal: Float + attr_reader c_numeric: Float + attr_reader c_real: Float + attr_reader c_double_precision: Float + attr_reader c_date: String + attr_reader c_time: String + attr_reader c_timestamp: String + attr_reader c_char: String + attr_reader c_varchar: String + attr_reader c_bytea: Array[Integer] + attr_reader c_text: String + attr_reader c_json: String + def initialize: ( + Array[Integer] c_bit, + Integer c_smallint, + bool c_boolean, + Integer c_integer, + Integer c_bigint, + Integer c_serial, + Float c_decimal, + Float c_numeric, + Float c_real, + Float c_double_precision, + String c_date, + String c_time, + String c_timestamp, + String c_char, + String c_varchar, + Array[Integer] c_bytea, + String c_text, + String c_json + ) -> void + end + + class QuerySql + @pool: untyped + @prepared_statements: Set[String] + def initialize: (Hash[String, String], Hash[String, String]) -> void + + def get_author: (GetAuthorArgs) -> GetAuthorRow + + def list_authors: -> ListAuthorsRow + + def create_author: (CreateAuthorArgs) -> CreateAuthorRow + + def delete_author: (DeleteAuthorArgs) -> void + + def test: -> TestRow + end +end \ No newline at end of file