lib/hypershield.rb in hypershield-0.1.1 vs lib/hypershield.rb in hypershield-0.2.0

- old
+ new

@@ -8,28 +8,29 @@ # integrations require "hypershield/engine" if defined?(Rails) module Hypershield class << self - attr_accessor :schemas, :log_sql + attr_accessor :enabled, :log_sql, :schemas end + self.enabled = true + self.log_sql = false self.schemas = { hypershield: { hide: %w(encrypted password token secret), show: [] } } - self.log_sql = false class << self def drop_view(view) schemas.each do |schema, _| execute("DROP VIEW IF EXISTS #{quote_ident(schema)}.#{quote_ident(view)}") end end - def refresh + def refresh(dry_run: false) if adapter_name =~ /sqlite/i raise "Adapter not supported: #{adapter_name}" end quiet_logging do @@ -37,36 +38,45 @@ schemas.each do |schema, config| hide = config[:hide].to_a show = config[:show].to_a + hypershield_tables = tables(schema) + tables.sort_by { |k, _| k }.each do |table, columns| next if table == "pg_stat_statements" - statements << "DROP VIEW IF EXISTS #{quote_ident(schema)}.#{quote_ident(table)} CASCADE" - columns.reject! do |column| hide.any? { |m| "#{table}.#{column}".include?(m) } && !show.any? { |m| "#{table}.#{column}".include?(m) } end + # if the hypershield view has the same columns, assume it doesn't need updated + # this may not necessarily be true if someone manually updates the view + # we could check the view definition, but this is harder as the database normalizes it + next if hypershield_tables[table] == columns + + statements << "DROP VIEW IF EXISTS #{quote_ident(schema)}.#{quote_ident(table)} CASCADE" + if columns.any? statements << "CREATE VIEW #{quote_ident(schema)}.#{quote_ident(table)} AS SELECT #{columns.map { |c| quote_ident(c) }.join(", ")} FROM #{quote_ident(table)}" end end end - if statements.any? - connection.transaction do - if mysql? - statements.each do |statement| - execute(statement) - end - else - execute(statements.join(";\n")) - end + if dry_run + if statements.any? + puts statements.map { |v| "#{v};" }.join("\n") end + else + # originally this was performed in a transaction + # however, this appears to cause issues in certain situations - see #5 and #6 + # this shouldn't be a huge issue now that we only update specific views + # we already drop views outside of the transaction when migrations are run + statements.each do |statement| + execute(statement) + end end end end private @@ -95,18 +105,21 @@ def mysql? adapter_name =~ /mysql/i end - def tables - # TODO make schema configurable - schema = - if mysql? - "database()" - else - "'public'" - end + def tables(schema = nil) + if schema + schema = quote(schema) + else + schema = + if mysql? + "database()" + else + "'public'" + end + end query = <<-SQL SELECT table_name, column_name, @@ -129,9 +142,13 @@ connection.select_all(sql).to_a end def execute(sql) connection.execute(sql) + end + + def quote(literal) + connection.quote(literal) end def quote_ident(ident) connection.quote_table_name(ident.to_s) end