lib/evil_seed/relation_dumper.rb in evil-seed-0.5.0 vs lib/evil_seed/relation_dumper.rb in evil-seed-0.6.0

- old
+ new

@@ -23,17 +23,19 @@ class RelationDumper MAX_IDENTIFIERS_IN_IN_STMT = 1_000 attr_reader :relation, :root_dumper, :model_class, :association_path, :search_key, :identifiers, :nullify_columns, :belongs_to_reflections, :has_many_reflections, :foreign_keys, :loaded_ids, :to_load_map, - :record_dumper, :inverse_reflection, :table_names, :options + :record_dumper, :inverse_reflection, :table_names, :options, + :current_deep, :verbose - delegate :root, :configuration, :total_limit, :loaded_map, to: :root_dumper + delegate :root, :configuration, :dont_nullify, :total_limit, :deep_limit, :loaded_map, to: :root_dumper def initialize(relation, root_dumper, association_path, **options) @relation = relation @root_dumper = root_dumper + @verbose = configuration.verbose @identifiers = options[:identifiers] @to_load_map = Hash.new { |h, k| h[k] = [] } @foreign_keys = Hash.new { |h, k| h[k] = [] } @loaded_ids = [] @model_class = relation.klass @@ -44,50 +46,66 @@ @nullify_columns = [] @table_names = {} @belongs_to_reflections = setup_belongs_to_reflections @has_many_reflections = setup_has_many_reflections @options = options + @current_deep = association_path.split('.').size + @dont_nullify = dont_nullify + + puts("- #{association_path}") if verbose end # Generate dump and write it into +io+ # @return [Array<IO>] List of dump IOs for separate tables in order of dependencies (belongs_to are first) def call dump! - belongs_to_dumps = dump_belongs_to_associations! - has_many_dumps = dump_has_many_associations! - [belongs_to_dumps, record_dumper.result, has_many_dumps].flatten.compact + if deep_limit and current_deep > deep_limit + [record_dumper.result].flatten.compact + else + belongs_to_dumps = dump_belongs_to_associations! + has_many_dumps = dump_has_many_associations! + [belongs_to_dumps, record_dumper.result, has_many_dumps].flatten.compact + end end private def dump! original_ignored_columns = model_class.ignored_columns model_class.ignored_columns += Array(configuration.ignored_columns_for(model_class.sti_name)) model_class.send(:reload_schema_from_cache) if ActiveRecord.version < Gem::Version.new("6.1.0.rc1") # See https://github.com/rails/rails/pull/37581 if identifiers.present? + puts(" # #{search_key} => #{identifiers}") if verbose # Don't use AR::Base#find_each as we will get error on Oracle if we will have more than 1000 ids in IN statement identifiers.in_groups_of(MAX_IDENTIFIERS_IN_IN_STMT).each do |ids| - fetch_attributes(relation.where(search_key => ids.compact)).each do |attributes| + attrs = fetch_attributes(relation.where(search_key => ids.compact)) + puts(" -- dumped #{attrs.size}") if verbose + attrs.each do |attributes| next unless check_limits! dump_record!(attributes) end end else + puts(" # #{relation.count}") if verbose relation.in_batches do |relation| - fetch_attributes(relation).each do |attributes| + attrs = fetch_attributes(relation) + puts(" -- dumped #{attrs.size}") if verbose + attrs.each do |attributes| next unless check_limits! dump_record!(attributes) end end end ensure model_class.ignored_columns = original_ignored_columns end def dump_record!(attributes) - nullify_columns.each do |nullify_column| - attributes[nullify_column] = nil + unless dont_nullify + nullify_columns.each do |nullify_column| + attributes[nullify_column] = nil + end end return unless record_dumper.call(attributes) foreign_keys.each do |reflection_name, fk_column| foreign_key = attributes[fk_column] next if foreign_key.nil? || loaded_map[table_names[reflection_name]].include?(foreign_key) @@ -138,35 +156,48 @@ return true unless options[:limitable] root_dumper.check_limits!(association_path) end def build_relation(reflection) - relation = reflection.klass.all + if configuration.unscoped + relation = reflection.klass.unscoped + else + relation = reflection.klass.all + end relation = relation.instance_eval(&reflection.scope) if reflection.scope relation = relation.where(reflection.type => model_class.to_s) if reflection.options[:as] # polymorphic relation end def setup_belongs_to_reflections model_class.reflect_on_all_associations(:belongs_to).reject do |reflection| next false if reflection.options[:polymorphic] # TODO: Add support for polymorphic belongs_to + included = root.included?("#{association_path}.#{reflection.name}") excluded = root.excluded?("#{association_path}.#{reflection.name}") || reflection.name == inverse_reflection - if excluded - nullify_columns << reflection.foreign_key if model_class.column_names.include?(reflection.foreign_key) + if excluded and not included + if model_class.column_names.include?(reflection.foreign_key) + puts(" -- excluded #{reflection.foreign_key}") if verbose + nullify_columns << reflection.foreign_key + end else foreign_keys[reflection.name] = reflection.foreign_key table_names[reflection.name] = reflection.table_name end - excluded + excluded and not included end end # This method returns only direct has_one and has_many reflections. For HABTM it returns intermediate has_many def setup_has_many_reflections + puts(" -- reflections #{model_class._reflections.keys}") if verbose model_class._reflections.select do |_reflection_name, reflection| next false if model_class.primary_key.nil? + next false if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection) - %i[has_one has_many].include?(reflection.macro) && !root.excluded?("#{association_path}.#{reflection.name}") + + included = root.included?("#{association_path}.#{reflection.name}") + excluded = root.excluded?("#{association_path}.#{reflection.name}") || reflection.name == inverse_reflection + %i[has_one has_many].include?(reflection.macro) && !(excluded and not included) end.map(&:second) end end end