Class: Mxrb::Runtime::SchemaMigrator
- Inherits:
-
Object
- Object
- Mxrb::Runtime::SchemaMigrator
- Defined in:
- lib/mxrb/runtime/schema_migrator.rb
Overview
Derives a deterministic SQLite schema from a parsed Mendix project and evolves an existing database without relying on the Java Runtime. rubocop:disable Metrics/AbcSize, Metrics/ClassLength, Metrics/CyclomaticComplexity rubocop:disable Metrics/MethodLength, Metrics/PerceivedComplexity
Constant Summary collapse
- SYSTEM_COLUMNS =
{ owner: %w[__owner_id TEXT], created_date: %w[__created_at TEXT], changed_date: %w[__changed_at TEXT], changed_by: %w[__changed_by_id TEXT] }.freeze
- TYPE_MAP =
{ integer: 'INTEGER', long: 'INTEGER', autonumber: 'INTEGER', boolean: 'INTEGER', float: 'REAL', decimal: 'REAL', binary: 'BLOB', datetime: 'TEXT', enum: 'TEXT', hashstring: 'TEXT', string: 'TEXT' }.freeze
Instance Attribute Summary collapse
-
#migration_plan ⇒ Object
readonly
Returns the value of attribute migration_plan.
-
#schema ⇒ Object
readonly
Returns the value of attribute schema.
Class Method Summary collapse
- .derive(project) ⇒ Object
- .entity_schema(mod, entity) ⇒ Object
- .physical_name(kind, key) ⇒ Object
- .qualified_entity_name(mod, entity) ⇒ Object
- .resolve_target(project, pointer, local) ⇒ Object
Instance Method Summary collapse
-
#initialize(project, database:, allow_destructive: false) ⇒ SchemaMigrator
constructor
A new instance of SchemaMigrator.
- #migrate! ⇒ Object
Constructor Details
#initialize(project, database:, allow_destructive: false) ⇒ SchemaMigrator
Returns a new instance of SchemaMigrator.
78 79 80 81 82 83 84 |
# File 'lib/mxrb/runtime/schema_migrator.rb', line 78 def initialize(project, database:, allow_destructive: false) @project = project @allow_destructive = allow_destructive == true @owns_database = !database.is_a?(SQLite3::Database) @database = @owns_database ? SQLite3::Database.new(database.to_s) : database @schema = self.class.derive(project) end |
Instance Attribute Details
#migration_plan ⇒ Object (readonly)
Returns the value of attribute migration_plan.
76 77 78 |
# File 'lib/mxrb/runtime/schema_migrator.rb', line 76 def migration_plan @migration_plan end |
#schema ⇒ Object (readonly)
Returns the value of attribute schema.
76 77 78 |
# File 'lib/mxrb/runtime/schema_migrator.rb', line 76 def schema @schema end |
Class Method Details
.derive(project) ⇒ Object
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/mxrb/runtime/schema_migrator.rb', line 86 def self.derive(project) entities = [] associations = [] project.modules.each do |mod| by_id = mod.entities.to_h { [_1.id.to_s, qualified_entity_name(mod, _1)] } by_id.merge!(mod.entities.to_h { [qualified_entity_name(mod, _1), qualified_entity_name(mod, _1)] }) mod.entities.select { _1.persistable != false && !_1.oql_view? }.each do |entity| entities << entity_schema(mod, entity) end mod.associations.each do |association| from = by_id[association.from_entity_id.to_s] || association.from_entity_id.to_s to = resolve_target(project, association.to_entity_id, by_id) next if from.empty? || to.empty? qualified = "#{mod.name}.#{association.name}" key = association.id.to_s.empty? ? qualified : association.id.to_s associations << AssociationSchema.new( association.name.to_s, qualified, key, physical_name('association', key), from, to, association.association_type.to_sym ) end end RuntimeSchema.new(entities.freeze, associations.freeze) end |
.entity_schema(mod, entity) ⇒ Object
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/mxrb/runtime/schema_migrator.rb', line 111 def self.entity_schema(mod, entity) qualified = qualified_entity_name(mod, entity) key = entity.data_storage_guid.to_s key = entity.id.to_s if key.empty? key = qualified if key.empty? columns = entity.attributes.map do |attribute| attribute_key = attribute.data_storage_guid.to_s attribute_key = attribute.id.to_s if attribute_key.empty? attribute_key = "#{key}:#{attribute.name}" if attribute_key.empty? type = attribute.type.to_sym SchemaColumn.new( attribute.name.to_s, attribute_key, physical_name('attribute', attribute_key), TYPE_MAP.fetch(type, 'TEXT'), type, attribute.default_value, attribute.required == true, attribute.unique == true || type == :autonumber ) end flags = (entity.system_members || {}).to_h.each_with_object({}) do |(name, enabled), selected| selected[name.to_sym] = SYSTEM_COLUMNS.fetch(name.to_sym) if enabled && SYSTEM_COLUMNS.key?(name.to_sym) end EntitySchema.new(qualified, key, physical_name('entity', key), columns.freeze, flags.freeze) end |
.physical_name(kind, key) ⇒ Object
150 151 152 |
# File 'lib/mxrb/runtime/schema_migrator.rb', line 150 def self.physical_name(kind, key) "mxrb_#{kind}_#{Digest::SHA256.hexdigest(key.to_s)[0, 20]}" end |
.qualified_entity_name(mod, entity) ⇒ Object
133 134 135 136 |
# File 'lib/mxrb/runtime/schema_migrator.rb', line 133 def self.qualified_entity_name(mod, entity) value = entity.qualified_name.to_s value.empty? ? "#{mod.name}.#{entity.name}" : value end |
.resolve_target(project, pointer, local) ⇒ Object
138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/mxrb/runtime/schema_migrator.rb', line 138 def self.resolve_target(project, pointer, local) value = pointer.to_s return local[value] if local.key?(value) return value if value.include?('.') project.modules.each do |mod| entity = mod.entities.find { _1.id.to_s == value } return qualified_entity_name(mod, entity) if entity end '' end |
Instance Method Details
#migrate! ⇒ Object
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/mxrb/runtime/schema_migrator.rb', line 154 def migrate! created = [] added = [] rebuilt = [] transaction do @migration_plan = build_migration_plan reject_destructive_migration! if migration_plan.destructive? && !@allow_destructive apply_destructive_migration(rebuilt) if migration_plan.destructive? schema.entities.each { migrate_entity(_1, created, added, rebuilt) } schema.associations.each { migrate_association(_1, created, rebuilt) } end MigrationResult.new(created.freeze, added.freeze, rebuilt.freeze) ensure @database.close if @owns_database end |