Created
February 22, 2023 20:26
-
-
Save RichardOrnelas/7de2915e51872d373e49bad68591830e to your computer and use it in GitHub Desktop.
Generators for Rails models and GQL types
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
namespace :generate do | |
desc "create blueprint for each loan entity" | |
task graphql_attributes: :environment do | |
# include the GeneratorHelpers | |
include GeneratorHelpers | |
# Types to be generated after looping through the models | |
new_union_types = { | |
# "user_type": [ CustomerType, CatType ] | |
} | |
# go through each model and build their associated GQL Type | |
model_names_and_filepaths.filter do |(model_name, _fp)| | |
model_name != "application_record" | |
end.each do |(model_name, _model_file_path)| | |
lines = [ | |
"module Types::Mutation\n", | |
"\tclass #{model_name.camelize}Attributes < Types::BaseInputObject\n" | |
] | |
model = model_name.camelize.constantize | |
# 1 - get all the column names | |
lines << %(\t\t# Database fields\n) | |
model.columns.each do |c| | |
gql_type = "" | |
gql_type = case c.type.to_s.camelize | |
when "Date", "Datetime" | |
"GraphQL::Types::ISO8601DateTime" | |
when "Integer" | |
# check for enum | |
enum_names = model.defined_enums.keys | |
gql_type = enum_names.include?(c.name) ? "String" : "Integer" | |
when "Decimal", "Float" | |
"Float" | |
when "Uuid", "Inet", "Text" | |
"String" | |
else | |
c.type.to_s.camelize | |
end | |
# @todo how to controll for password_digest and ssn_ciphertext, ssn_bidx situations? | |
lines << %(\t\targument :#{c.name}, #{gql_type}, required: false\n) | |
end | |
lines << %(\n) | |
lines << %(\t\t# Relationships\n) | |
# 2 - Put all of the relationships into the file body | |
model.reflections.keys.filter do |key| | |
["attachments_attachments", "attachments_blobs", "versions"].exclude? key | |
end.each do |association_key| | |
reflection = model.reflections[association_key] | |
# i - Insert a new union type if this relationship has a "type" attribute (i.e. polymorphic) | |
if model.reflections[association_key].type | |
r_file_name = reflection.type.gsub("type", "attributes") | |
new_union_types[r_file_name] = [] if new_union_types[r_file_name].nil? | |
new_union_types[r_file_name] << %(#{model_name.camelize}Attributes) | |
end | |
lines << %(\t\targument :#{association_key}, [Types::Mutation::#{reflection.class_name}Attributes], required: false\n) | |
end | |
lines << %(\n) | |
# Finish the boilerplate | |
lines << %(\tend\n) | |
lines << %(end) | |
# 4 - write it to a new type file | |
write_file_by_lines("app/graphql/types/mutation/#{model_name}_attributes.rb", lines) | |
end | |
# Build all the union types that were identified while going through each of the models | |
new_union_types.each do |key, arr_of_types| | |
# build some boilerplate for the new union GQL Type | |
union_file_lines = [ | |
"module Types::Mutation\n", | |
"\tclass #{key.camelize} < Types::BaseInputObject\n", | |
# "\t\tpossible_types #{arr_of_types.join(', ')}\n", | |
"\n", | |
"\t\tdef self.resolve_type(obj, ctx)\n", | |
"\t\t\t\"\#{obj.class}Attributes\".constantize\n", | |
"\t\tend\n", | |
"\tend\n", | |
"end\n" | |
] | |
write_file_by_lines("app/graphql/types/mutation/#{key.underscore}.rb", union_file_lines) | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
namespace :generate do | |
desc "create blueprint for each loan entity" | |
task graphql_types: :environment do | |
# include the GeneratorHelpers | |
include GeneratorHelpers | |
# Types to be generated after looping through the models | |
new_union_types = { | |
# "user_type": [ CustomerType, CatType ] | |
} | |
# go through each model and build their associated GQL Type | |
model_names_and_filepaths.filter do |(model_name, _fp)| | |
model_name != "application_record" | |
end.each do |(model_name, _model_file_path)| | |
lines = [ | |
"module Types::Query\n", | |
"\tclass #{model_name.camelize}Type < Types::BaseObject\n" | |
] | |
model = model_name.camelize.constantize | |
# 1 - get all the column names | |
lines << %(\t\t# Database fields\n) | |
model.columns.each do |c| | |
gql_type = "" | |
gql_type = case c.type.to_s.camelize | |
when "Date", "Datetime" | |
"GraphQL::Types::ISO8601DateTime" | |
when "Integer" | |
# check for enum | |
enum_names = model.defined_enums.keys | |
gql_type = enum_names.include?(c.name) ? "String" : "Integer" | |
when "Decimal", "Float" | |
"Float" | |
when "Uuid", "Inet", "Text" | |
"String" | |
else | |
c.type.to_s.camelize | |
end | |
lines << %(\t\tfield :#{c.name}, #{gql_type}, null: #{c.null}\n) | |
end | |
lines << %(\n) | |
lines << %(\t\t# Relationships\n) | |
# 2 - Put all of the relationships into the file body | |
model.reflections.keys.filter do |key| | |
["attachments_attachments", "attachments_blobs", "versions"].exclude? key | |
end.each do |association_key| | |
reflection = model.reflections[association_key] | |
# i - Insert a new union type if this relationship has a "type" attribute (i.e. polymorphic) | |
if model.reflections[association_key].type | |
new_union_types[reflection.type] = [] if new_union_types[reflection.type].nil? | |
new_union_types[reflection.type] << %(#{model_name.camelize}Type) | |
end | |
lines << %(\t\tfield :#{association_key}, [Types::Query::#{reflection.class_name}Type], null: true\n) | |
end | |
lines << %(\n) | |
# 2 - get the derived methods | |
lines << %(\t\t# Derived fields\n) | |
get_model_derived_methods(model_name).each do |method_name| | |
lines << %(\t\tdef #{method_name}\n) | |
lines << %(\t\t\tobject.#{method_name}\n) | |
lines << %(\t\tend\n\n) | |
end | |
# Finish the boilerplate | |
lines << %(\tend\n) | |
lines << %(end) | |
# 4 - write it to a new type file | |
write_file_by_lines("app/graphql/types/query/#{model_name}_type.rb", lines) | |
end | |
# Build all the union types that were identified while going through each of the models | |
new_union_types.each do |key, arr_of_types| | |
# build some boilerplate for the new union GQL Type | |
union_file_lines = [ | |
"module Types::Query\n", | |
"\tclass #{key.camelize} < Types::BaseUnion\n", | |
"\t\tpossible_types #{arr_of_types.uniq.join(', ')}\n", | |
"\n", | |
"\t\tdef self.resolve_type(obj, ctx)\n", | |
"\t\t\t\"\#{obj.class}Type\".constantize\n", | |
"\t\tend\n", | |
"\tend\n", | |
"end\n" | |
] | |
write_file_by_lines("app/graphql/types/query/#{key.underscore}.rb", union_file_lines) | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
namespace :generate do | |
desc "create custom models" | |
task models: :environment do | |
# Include helper data | |
require_relative "../../generation_data/gov_1003_mapping" | |
require_relative "./generator_helpers" | |
include GeneratorHelpers | |
include Gov1003Mapping | |
def gather_all_fields | |
all_fields = [] | |
gov_mapping_hash.each do |_section, section_content| | |
fields = section_content[:fields] | |
fields.each do |field| | |
all_fields << field unless all_fields.include?(field) | |
next if field[:dependent_fields].nil? | |
field[:dependent_fields].each do |_dfield| | |
all_fields << field unless all_fields.include?(field) | |
end | |
end | |
end | |
return all_fields | |
end | |
# pull derived fields from gov 1003 mapping for custom methods | |
def add_derived_methods(fields, entity) | |
output = [] | |
derived_fields = [] | |
fields.each do |field| | |
next unless field[:derived] && field[:entity] == entity && !derived_fields.include?(field[:db_field_name]) | |
derived_fields << field[:db_field_name] | |
output << "\tdef #{field[:db_field_name].downcase.gsub(/[^\w]/, '_')}\n" | |
output << "\t\t# @note #{field[:notes]}\n" | |
output << "\t\treturn nil\n" | |
output << "\tend\n\n" | |
end | |
return output | |
end | |
# pull derived fields from gov 1003 mapping for custom methods | |
def define_enums(fields, entity) | |
output = [] | |
defined_enums_fields = [] | |
fields.each do |field| | |
unless field[:entity] == entity && field[:enum].present? && !defined_enums_fields.include?(field[:db_field_name]) | |
next | |
end | |
defined_enums_fields << field[:db_field_name] | |
output << "\tenum #{field[:db_field_name]}: [\n" | |
field[:enum].each { |e| output << "\t\t :#{e.downcase.gsub(/[^\w]/, '_')},\n" } | |
output << "\t]\n" | |
end | |
return output | |
end | |
models_to_ignore = ["application_record", "event"] | |
model_names_and_filepaths.filter do |(model, _file_path)| | |
models_to_ignore.exclude? model | |
end.each do |(model, model_file_path)| | |
puts "#{model}, path: #{model_file_path}" | |
lines = [] | |
lines << "class #{model.capitalize} < ApplicationRecord\n" | |
lines << "\t# Relationships #\n" | |
# use relationship hash to insert relatiionships | |
lines << "\thas_paper_trail\n\n" | |
# binding.pry if model == 'document' | |
if entity_data_for_models[model.to_sym] | |
entity_data_for_models[model.to_sym][:relationships]&.each do |r| | |
lines << "\t#{r}\n" | |
end | |
end | |
# Universal validations | |
lines << "\n\n\t# Validations #\n" | |
lines << "\t# @todo add validations\n" | |
fields = gather_all_fields | |
# Define Enums | |
lines << "\n\n\t# Enum Definitions #\n" | |
lines.concat(define_enums(fields, model)) | |
# Derived Methods | |
lines << "\n\n\t# Custom Derived Methods #\n\n" | |
lines.concat(add_derived_methods(fields, model)) | |
lines << "end" | |
write_file_by_lines(model_file_path, lines) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment