diff --git a/lib/active_admin_import/dsl.rb b/lib/active_admin_import/dsl.rb index 405caeb..eb6349a 100644 --- a/lib/active_admin_import/dsl.rb +++ b/lib/active_admin_import/dsl.rb @@ -25,6 +25,22 @@ module ActiveAdminImport # +plural_resource_label+:: pluralized resource label value (default config.plural_resource_label) # module DSL + CONTEXT_METHOD = :active_admin_import_context + + def self.prepare_import_model(template_object, controller, params: nil) + model = template_object.is_a?(Proc) ? template_object.call : template_object + if params + params_key = ActiveModel::Naming.param_key(model.class) + model.assign_attributes(params[params_key].try(:deep_symbolize_keys) || {}) + end + return model unless controller.respond_to?(CONTEXT_METHOD, true) + context = controller.send(CONTEXT_METHOD) + return model unless context.is_a?(Hash) + context = context.merge(file: model.file) if model.respond_to?(:file) && !context.key?(:file) + model.assign_attributes(context) + model + end + DEFAULT_RESULT_PROC = lambda do |result, options| model_name = options[:resource_label].downcase plural_model_name = options[:plural_resource_label].downcase @@ -57,11 +73,7 @@ def active_admin_import(options = {}, &block) collection_action :import, method: :get do authorize!(ActiveAdminImport::Auth::IMPORT, active_admin_config.resource_class) - @active_admin_import_model = if options[:template_object].is_a?(Proc) - options[:template_object].call - else - options[:template_object] - end + @active_admin_import_model = ActiveAdminImport::DSL.prepare_import_model(options[:template_object], self) render template: options[:template] end @@ -78,13 +90,9 @@ def active_admin_import(options = {}, &block) authorize!(ActiveAdminImport::Auth::IMPORT, active_admin_config.resource_class) _params = params.respond_to?(:to_unsafe_h) ? params.to_unsafe_h : params params = ActiveSupport::HashWithIndifferentAccess.new _params - @active_admin_import_model = if options[:template_object].is_a?(Proc) - options[:template_object].call - else - options[:template_object] - end - params_key = ActiveModel::Naming.param_key(@active_admin_import_model.class) - @active_admin_import_model.assign_attributes(params[params_key].try(:deep_symbolize_keys) || {}) + @active_admin_import_model = ActiveAdminImport::DSL.prepare_import_model( + options[:template_object], self, params: params + ) # go back to form return render template: options[:template] unless @active_admin_import_model.valid? @importer = Importer.new( diff --git a/spec/fixtures/files/post_comments.csv b/spec/fixtures/files/post_comments.csv new file mode 100644 index 0000000..08582a5 --- /dev/null +++ b/spec/fixtures/files/post_comments.csv @@ -0,0 +1,3 @@ +"Body" +"First comment" +"Second comment" diff --git a/spec/import_spec.rb b/spec/import_spec.rb index 39be155..e859716 100644 --- a/spec/import_spec.rb +++ b/spec/import_spec.rb @@ -626,4 +626,67 @@ def upload_file!(name, ext = 'csv') end.not_to change { Author.count } end end + + context 'with active_admin_import_context defined on the controller' do + before { Author.create!(name: 'John', last_name: 'Doe') } + + let(:author) { Author.take } + + context 'when context returns request-derived attributes' do + before do + author_id = author.id + add_post_resource( + template_object: ActiveAdminImport::Model.new(author_id: author_id), + before_batch_import: lambda do |importer| + ip = importer.model.request_ip + a_id = importer.model.author_id + importer.csv_lines.map! { |row| row << ip << a_id } + importer.headers.merge!(:'Request Ip' => :request_ip, :'Author Id' => :author_id) + end, + controller_block: proc do + def active_admin_import_context + { request_ip: request.remote_ip } + end + end + ) + visit '/admin/posts/import' + upload_file!(:posts_for_author) + end + + it 'merges the context into the import model so callbacks see it' do + expect(page).to have_content 'Successfully imported 2 posts' + expect(Post.count).to eq(2) + Post.all.each do |post| + expect(post.request_ip).to eq('127.0.0.1') + expect(post.author_id).to eq(author.id) + end + end + end + + context 'when context returns parent id for a nested belongs_to resource' do + let(:post) { Post.create!(title: 'A post', body: 'body', author: author) } + + before do + add_nested_post_comment_resource( + before_batch_import: lambda do |importer| + importer.csv_lines.map! { |row| row << importer.model.post_id } + importer.headers.merge!(:'Post Id' => :post_id) + end, + controller_block: proc do + def active_admin_import_context + { post_id: parent.id } + end + end + ) + visit "/admin/posts/#{post.id}/post_comments/import" + upload_file!(:post_comments) + end + + it 'automatically assigns the parent post_id to every imported comment' do + expect(page).to have_content 'Successfully imported 2 post comments' + expect(PostComment.count).to eq(2) + expect(PostComment.where(post_id: post.id).count).to eq(2) + end + end + end end diff --git a/spec/support/admin.rb b/spec/support/admin.rb index 03fb0bd..e168535 100644 --- a/spec/support/admin.rb +++ b/spec/support/admin.rb @@ -8,8 +8,24 @@ def add_author_resource(options = {}, &block) end def add_post_resource(options = {}, &block) + cb = options.delete(:controller_block) ActiveAdmin.register Post do config.filters = false + controller(&cb) if cb + active_admin_import(options, &block) + end + Rails.application.reload_routes! +end + +def add_nested_post_comment_resource(options = {}, &block) + cb = options.delete(:controller_block) + ActiveAdmin.register Post do + config.filters = false + end + ActiveAdmin.register PostComment do + config.filters = false + belongs_to :post + controller(&cb) if cb active_admin_import(options, &block) end Rails.application.reload_routes! diff --git a/spec/support/rails_template.rb b/spec/support/rails_template.rb index 682c1c7..f821edf 100644 --- a/spec/support/rails_template.rb +++ b/spec/support/rails_template.rb @@ -1,10 +1,11 @@ create_file "app/assets/config/manifest.js", skip: true generate :model, 'author name:string{10}:uniq last_name:string birthday:date --force' -generate :model, 'post title:string:uniq body:text author:references --force' +generate :model, 'post title:string:uniq body:text request_ip:string author:references --force' +generate :model, 'post_comment body:text post:references --force' inject_into_file 'app/models/author.rb', " validates_presence_of :name\n validates_uniqueness_of :last_name\n", before: 'end' -inject_into_file 'app/models/post.rb', " validates_presence_of :author\n", before: 'end' +inject_into_file 'app/models/post.rb', " validates_presence_of :author\n has_many :post_comments\n", before: 'end' # Add our local Active Admin to the load path (Rails 7.1+) gsub_file "config/environment.rb",