class ActiveFacts::Generators::Rails::SchemaRb
Generate a Rails-friendly schema for the vocabulary Invoke as
afgen --rails/schema[=options] <file>.cql
Public Class Methods
new(vocabulary, *options)
click to toggle source
# File lib/activefacts/generators/rails/schema.rb, line 22 def initialize(vocabulary, *options) @vocabulary = vocabulary @vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary help if options.include? "help" @exclude_fks = options.include? "exclude_fks" @include_comments = options.include? "include_comments" @closed_world = options.include? "closed_world" end
Public Instance Methods
generate_column(table, pk, column)
click to toggle source
# File lib/activefacts/generators/rails/schema.rb, line 68 def generate_column table, pk, column name = column.rails_name type, params, constraints = *column.type length = params[:length] length &&= length.to_i scale = params[:scale] scale &&= scale.to_i rails_type, length = *column.rails_type length_name = rails_type == 'decimal' ? 'precision' : 'limit' length_option = length ? ", #{length_name}: #{length}" : '' scale_option = scale ? ", scale: #{scale}" : '' comment = column.comment null_option = ", null: #{!column.is_mandatory}" if pk.size == 1 && pk[0] == column case rails_type when 'serial' rails_type = "primary_key" when 'uuid' rails_type = "uuid, default: 'gen_random_uuid()', primary_key: true" end else case rails_type when 'serial' rails_type = 'integer' # An integer foreign key end end (@include_comments ? [" \# #{comment}"] : []) + [ %Q{ t.column "#{name}", :#{rails_type}#{length_option}#{scale_option}#{null_option}} ] end
generate_columns(table, pk, fk_columns)
click to toggle source
# File lib/activefacts/generators/rails/schema.rb, line 103 def generate_columns table, pk, fk_columns sc = sorted_columns(table, pk, fk_columns) lines = sc.map do |column| generate_column table, pk, column end lines.flatten end
generate_table(table, foreign_keys)
click to toggle source
# File lib/activefacts/generators/rails/schema.rb, line 111 def generate_table table, foreign_keys ar_table_name = table.rails_name pk = table.identifier_columns if pk[0].is_auto_assigned identity_column = pk[0] warn "Warning: redundant column(s) after #{identity_column.name} in primary key of #{ar_table_name}" if pk.size > 1 end # Get the list of references that give rise to foreign keys: fk_refs = table.references_from.select{|ref| ref.is_simple_reference } # Get the list of columns that embody the foreign keys: fk_columns = table.columns.select do |column| column.references[0].is_simple_reference end # Detect if this table is a join table. # Join tables have multi-part primary keys that are made up only of foreign keys is_join_table = pk.length > 1 and !pk.detect do |pk_column| !fk_columns.include?(pk_column) end warn "Warning: #{table.name} has a multi-part primary key" if pk.length > 1 and !is_join_table puts %Q{ create_table "#{ar_table_name}", id: false, force: true do |t|} columns = generate_columns table, pk, fk_columns unless @exclude_fks table.foreign_keys.each do |fk| from_columns = fk.from_columns.map{|column| column.rails_name} to_columns = fk.to_columns.map{|column| column.rails_name} foreign_keys.concat( if (from_columns.length == 1) index_name = RMap.rails_name_trunc('index_'+fk.from.rails_name+'_on_'+from_columns[0]) [ " add_foreign_key :#{fk.from.rails_name}, :#{fk.to.rails_name}, column: :#{from_columns[0]}, primary_key: :#{to_columns[0]}, on_delete: :cascade" ]+ Array( # Index it non-uniquely only if it's not unique already: fk.jump_reference.to_role.is_unique ? nil : " add_index :#{fk.from.rails_name}, [:#{from_columns[0]}], unique: false, name: :#{index_name}" ) else # This probably isn't going to work without Dr Nic's CPK gem: [ " add_foreign_key :#{fk.to.rails_name}, :#{fk.from.rails_name}, column: [:#{from_columns.join(':, ')}], primary_key: [:#{to_columns.join(':, ')}], on_delete: :cascade" ] end ) end end indices = table.indices index_text = [] indices.each do |index| next if index.is_primary && index.columns.size == 1 # We've handled this already index_name = index.rails_name unique = !index.columns.detect{|column| !column.is_mandatory} and !@closed_world index_text << %Q{ add_index "#{ar_table_name}", ["#{index.columns.map{|c| c.rails_name}*'", "'}"], name: :#{index_name}#{ unique ? ", unique: true" : '' }} end puts columns.join("\n") puts " end\n\n" puts index_text.join("\n") puts "\n" unless index_text.empty? end
sorted_columns(table, pk, fk_columns)
click to toggle source
We sort the columns here, not in the rmap layer, because it affects the ordering of columns in an index :-(.
# File lib/activefacts/generators/rails/schema.rb, line 52 def sorted_columns table, pk, fk_columns table.columns.sort_by do |column| [ # Emit columns alphabetically, but PK first, then FKs, then others case when i = pk.index(column) i when fk_columns.include?(column) pk.size+1 else pk.size+2 end, column.rails_name ] end end
Private Instance Methods
help()
click to toggle source
# File lib/activefacts/generators/rails/schema.rb, line 31 def help @helping = true warn %Q{Options for --rails/schema: exclude_fks Don't generate foreign key definitions for use with Rails 4 or the foreigner gem include_comments Generate a comment for each column showing the absorption path closed_world Set this if your DBMS only allows one null in a unique index (MS SQL) } end
puts(s)
click to toggle source
# File lib/activefacts/generators/rails/schema.rb, line 44 def puts s @out.puts s end
warn(*a)
click to toggle source
# File lib/activefacts/generators/rails/schema.rb, line 40 def warn *a $stderr.puts *a end