module ForestLiana class SchemaAdapter def initialize(model) @model = model end def perform add_columns add_associations # ActsAsTaggable attribute if @model.respond_to?(:acts_as_taggable) && @model.acts_as_taggable.try(:to_a) @model.acts_as_taggable.to_a.each do |key, value| field = collection.fields.find {|x| x[:field] == key.to_s} if field field[:type] = 'String' field[:reference] = nil field[:inverse_of] = nil collection.fields.delete_if do |f| ['taggings', 'base_tags', 'tag_taggings'].include?(f[:field]) end end end end collection end private def collection @collection ||= begin collection = ForestLiana.apimap.find do |x| x.name.to_s == @model.table_name end if collection.blank? collection = ForestLiana::Model::Collection.new({ name: @model.table_name, fields: [] }) ForestLiana.apimap << collection end collection end end def add_columns @model.columns.each do |column| collection.fields << get_schema_for_column(column) end # Intercom if ForestLiana.integrations.try(:[], :intercom) .try(:[], :mapping).try(:include?, @model.name) model_name = @model.table_name collection.fields << { field: :intercom_conversations, type: ['String'], reference: "#{model_name}_intercom_conversations.id", column: nil, is_searchable: false, integration: 'intercom' } @collection.fields << { field: :intercom_attributes, type: 'String', reference: "#{model_name}_intercom_attributes.id", column: nil, is_searchable: false, integration: 'intercom' } end # Stripe stripe_mapping = ForestLiana.integrations.try(:[], :stripe) .try(:[], :mapping) if stripe_mapping if stripe_mapping .select { |mapping| mapping.split('.')[0] == @model.name } .size > 0 model_name = @model.table_name collection.fields << { field: :stripe_payments, type: ['String'], reference: "#{model_name}_stripe_payments.id", column: nil, is_searchable: false, integration: 'stripe' } collection.fields << { field: :stripe_invoices, type: ['String'], reference: "#{model_name}_stripe_invoices.id", column: nil, is_searchable: false, integration: 'stripe' } collection.fields << { field: :stripe_cards, type: ['String'], reference: "#{model_name}_stripe_cards.id", column: nil, is_searchable: false, integration: 'stripe' } collection.fields << { field: :stripe_subscriptions, type: ['String'], reference: "#{model_name}_stripe_subscriptions.id", column: nil, is_searchable: false, integration: 'stripe' } collection.fields << { field: :stripe_bank_accounts, type: ['String'], reference: "#{model_name}_stripe_bank_accounts.id", column: nil, is_searchable: false, integration: 'stripe' } end end # Paperclip url attribute if @model.respond_to?(:attachment_definitions) @model.attachment_definitions.each do |key, value| collection.fields << { field: key, type: 'File' } collection.fields.delete_if do |f| ["#{key}_file_name", "#{key}_file_size", "#{key}_content_type", "#{key}_updated_at"].include?(f[:field]) end end end # CarrierWave attribute if @model.respond_to?(:uploaders) @model.uploaders.each do |key, value| field = collection.fields.find {|x| x[:field] == key.to_s} field[:type] = 'File' if field end end end def add_associations SchemaUtils.associations(@model).each do |association| begin if schema = column_association(collection, association) schema[:reference] = get_ref_for(association) schema[:field] = deforeign_key(schema[:field]) schema[:inverseOf] = inverse_of(association) else collection.fields << get_schema_for_association(association) end rescue => error puts error.inspect end end end def inverse_of(association) association.inverse_of.try(:name).try(:to_s) || automatic_inverse_of(association) end def automatic_inverse_of(association) name = association.active_record.name.demodulize.underscore inverse_association = association.klass.reflections.keys.find do |k| k.to_s == name || k.to_s == name.pluralize end inverse_association.try(:to_s) end def get_schema_for_column(column) schema = { field: column.name, type: get_type_for(column) } add_enum_values_if_is_enum(schema, column) end def get_schema_for_association(association) { field: association.name.to_s, type: get_type_for_association(association), reference: "#{association.klass.table_name}.id", inverseOf: inverse_of(association) } end def get_type_for(column) return 'Enum' if @model.defined_enums.has_key?(column.name) case column.type when :integer 'Number' when :float 'Number' when :decimal 'Number' when :datetime 'Date' when :date 'Date' when :string 'String' when :text 'String' when :citext 'String' when :boolean 'Boolean' end end def add_enum_values_if_is_enum(column_schema, column) if column_schema[:type] == 'Enum' column_schema[:enums] = [] @model.defined_enums[column.name].each do |name, value| column_schema[:enums] << name end end column_schema end def get_ref_for(association) if association.options[:polymorphic] == true '*.id' else "#{association.klass.table_name.underscore}.id" end end def column_association(collection, field) collection.fields.find {|x| x[:field] == field.foreign_key } end def get_type_for_association(association) if association.macro == :has_many || association.macro == :has_and_belongs_to_many ['Number'] else 'Number' end end def deforeign_key(column_name) if column_name[-3..-1] == '_id' column_name[0..-4] else column_name end end end end