active_admin-exportable 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +45 -0
- data/Rakefile +3 -0
- data/active_admin-exportable.gemspec +30 -0
- data/app/admin/exportable/import.rb +43 -0
- data/lib/active_admin/exportable.rb +74 -0
- data/lib/active_admin/exportable/engine.rb +13 -0
- data/lib/active_admin/exportable/exporter.rb +132 -0
- data/lib/active_admin/exportable/importer.rb +109 -0
- data/lib/active_admin/exportable/version.rb +7 -0
- data/spec/exportable_spec.rb +29 -0
- data/spec/spec_helper.rb +43 -0
- metadata +145 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 87e5645f5817a953c4cbc2ab61cc5f6de965981c695f7a196b19b2d2b37c8e96
|
4
|
+
data.tar.gz: 40010fe028c4815e8b74e5839df7497ac81e4c56e4b85e2ceb9d42de57a965ea
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4b95c94889333ce39ce407d46f4b00e7eadab3b03482126864a27f17f2902eda2a32c965137e343d7ca5a21347a21147b0a4ae02582c62d4daa6b471fe21cfb3
|
7
|
+
data.tar.gz: 60bbcaedf2a77a2dcb5929766e9f98e7299c26c974580769cc4d65e9baeefc96dff2d3fa72f12f58112991eb1106d74309736b530dd8255eb28ef2a223ba1cca
|
data/.gitignore
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) Wagner Caixeta
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# ActiveAdmin::Exportable
|
2
|
+
|
3
|
+
Allow user to export/import of ActiveRecord records and associated records in ActiveAdmin.
|
4
|
+
|
5
|
+
# Usage
|
6
|
+
|
7
|
+
Add "exportable" to
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
ActiveAdmin.register Blog do
|
11
|
+
exportable includes: [:posts], format: :json, remove_ids: true, filename_method: :my_custom_filename_method
|
12
|
+
end
|
13
|
+
```
|
14
|
+
# Options
|
15
|
+
|
16
|
+
## includes
|
17
|
+
Default: []
|
18
|
+
|
19
|
+
Use the same syntax of ActiveRecord includes.
|
20
|
+
|
21
|
+
## format
|
22
|
+
Default: 'json'
|
23
|
+
|
24
|
+
Export will accept anything "to_format", like json and yaml, but import will only accept 'json' and 'yaml'.
|
25
|
+
|
26
|
+
## remove_ids
|
27
|
+
Default: true
|
28
|
+
|
29
|
+
The export will remove ids and association ids to avoid conflict on import in another system.
|
30
|
+
The import process can restore associations creating new ids.
|
31
|
+
|
32
|
+
## filename_method
|
33
|
+
If you dont define this the name os files will be "#{the_resource_class_name}_#{id}"
|
34
|
+
|
35
|
+
# Installation
|
36
|
+
|
37
|
+
Add this line to your application's Gemfile:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
gem 'active_admin-exportable', '~> 0.1.0'
|
41
|
+
```
|
42
|
+
|
43
|
+
And then execute:
|
44
|
+
|
45
|
+
$ bundle
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'active_admin/exportable/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'active_admin-exportable'
|
9
|
+
spec.version = ActiveAdmin::Exportable::VERSION
|
10
|
+
spec.authors = ['Wagner Caixeta']
|
11
|
+
spec.email = ['wagner.caixeta@gmail.com.com']
|
12
|
+
spec.summary = 'A export/import tool for ActiveAdmin.'
|
13
|
+
spec.description = 'Allow user to export/import of ActiveRecord records and associated records in ActiveAdmin.'
|
14
|
+
spec.homepage = 'https://github.com/zorab47/active_admin-exportable'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0")
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
|
22
|
+
spec.add_dependency 'activeadmin'
|
23
|
+
|
24
|
+
spec.add_development_dependency 'activerecord'
|
25
|
+
spec.add_development_dependency 'bundler'
|
26
|
+
spec.add_development_dependency 'rake'
|
27
|
+
spec.add_development_dependency 'rspec'
|
28
|
+
# required for active admin
|
29
|
+
spec.add_development_dependency 'sass-rails'
|
30
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
ActiveAdmin.register_page 'Import' do
|
4
|
+
menu parent: 'Exportable'
|
5
|
+
controller do
|
6
|
+
private
|
7
|
+
|
8
|
+
def import_options
|
9
|
+
allow_update = ActiveModel::Type::Boolean.new.cast(params[:import][:allow_update])
|
10
|
+
file_path = params[:import][:file]&.path
|
11
|
+
format = params[:import][:format]
|
12
|
+
raise 'Format is required.' if format.blank?
|
13
|
+
raise 'File is required.' if file_path.blank?
|
14
|
+
|
15
|
+
{ path: file_path, format: format, allow_update: allow_update }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
content do
|
20
|
+
columns do
|
21
|
+
column do
|
22
|
+
div do
|
23
|
+
active_admin_form_for 'import', url: 'import/upload' do |f|
|
24
|
+
f.inputs name: 'Import', class: 'inputs' do
|
25
|
+
f.input :format, collection: %i[json yaml], input_html: { value: 'json' }
|
26
|
+
f.input :allow_update, as: :boolean
|
27
|
+
f.input :file, as: :file
|
28
|
+
f.action :submit
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
page_action :upload, method: :post do
|
37
|
+
imported = ActiveAdmin::Exportable::Importer.new(**import_options)
|
38
|
+
imported.import
|
39
|
+
redirect_to admin_import_path, notice: 'Imported'
|
40
|
+
rescue StandardError => e
|
41
|
+
redirect_to admin_import_path, flash: { error: e.message }
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_admin'
|
4
|
+
require 'active_admin/exportable/version'
|
5
|
+
require 'active_admin/exportable/engine'
|
6
|
+
require 'active_admin/exportable/exporter'
|
7
|
+
require 'active_admin/exportable/importer'
|
8
|
+
|
9
|
+
module ActiveAdmin
|
10
|
+
module Exportable
|
11
|
+
extend ActiveSupport::Concern
|
12
|
+
|
13
|
+
def exportable(options = {})
|
14
|
+
includes = options.fetch(:includes) { [] }
|
15
|
+
remove_ids = options.fetch(:remove_ids, true)
|
16
|
+
filename_method = options[:filename_method]
|
17
|
+
format = options.fetch(:format, :json)
|
18
|
+
enable_resource_exportion(includes: includes, remove_ids: remove_ids, filename_method: filename_method,
|
19
|
+
format: format)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def enable_resource_exportion(includes:, remove_ids:, filename_method:, format:)
|
25
|
+
action_item(*compatible_action_item_parameters) do
|
26
|
+
if controller.action_methods.include?('new') && authorized?(ActiveAdmin::Auth::CREATE,
|
27
|
+
active_admin_config.resource_class)
|
28
|
+
link_to(
|
29
|
+
I18n.t(
|
30
|
+
:export_model,
|
31
|
+
default: 'Export %{model}',
|
32
|
+
scope: [:active_admin],
|
33
|
+
model: active_admin_config.resource_label
|
34
|
+
),
|
35
|
+
{ action: :export }
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
member_action :export do
|
41
|
+
resource = resource_class.find(params[:id])
|
42
|
+
|
43
|
+
authorize! ActiveAdmin::Auth::CREATE, resource
|
44
|
+
|
45
|
+
exported = ActiveAdmin::Exportable::Exporter.new(resource, includes: includes,
|
46
|
+
remove_ids: remove_ids).export.send("to_#{format}")
|
47
|
+
filename = "#{resource.send(filename_method)}.#{format}" if filename_method.present?
|
48
|
+
filename ||= "#{resource_class.name.downcase}_#{resource.id}.#{format}"
|
49
|
+
|
50
|
+
send_data exported, type: "application/#{format}", filename: filename
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# For ActiveAdmin `action_item` compatibility.
|
55
|
+
#
|
56
|
+
# - When ActiveAdmin is less than 1.0.0.pre1 exclude name parameter from
|
57
|
+
# calls to `action_item` for compatibility.
|
58
|
+
# - When 1.0.0.pre1 or greater provide name to `action_item` to avoid the
|
59
|
+
# warning message, and later an error.
|
60
|
+
#
|
61
|
+
# Returns Array of parameters.
|
62
|
+
def compatible_action_item_parameters
|
63
|
+
parameters = [{ only: %i[show edit] }]
|
64
|
+
parameters.unshift(:exportable_export) if action_item_name_required?
|
65
|
+
parameters
|
66
|
+
end
|
67
|
+
|
68
|
+
def action_item_name_required?
|
69
|
+
method(:action_item).parameters.count == 3
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
ActiveAdmin::ResourceDSL.include ActiveAdmin::Exportable
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveAdmin
|
4
|
+
module Exportable
|
5
|
+
class Engine < Rails::Engine
|
6
|
+
initializer 'active_admin' do |_app|
|
7
|
+
ActiveAdmin.before_load do |app|
|
8
|
+
app.load_paths << File.expand_path('../../../app/admin', __dir__)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveAdmin
|
4
|
+
module Exportable
|
5
|
+
class Exporter
|
6
|
+
# TODO: extract this to other gem.
|
7
|
+
def initialize(record, includes: [], remove_ids: false)
|
8
|
+
unless record.is_a?(ActiveRecord::Relation) || record.is_a?(ActiveRecord::Base)
|
9
|
+
raise ArgumentError, 'You need provide an ActiveRecord record as argument.'
|
10
|
+
end
|
11
|
+
|
12
|
+
@root = RootStruct.new(node: record, includes: includes, remove_ids: remove_ids)
|
13
|
+
end
|
14
|
+
|
15
|
+
def export
|
16
|
+
@root.data
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_file(path:)
|
20
|
+
File.open(path, 'w') { |f| f.write to_json }
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_json(*_args)
|
24
|
+
export.to_json
|
25
|
+
end
|
26
|
+
|
27
|
+
class RootStruct
|
28
|
+
def initialize(node:, remove_ids:, includes: [])
|
29
|
+
@node = node
|
30
|
+
@includes = includes
|
31
|
+
@remove_ids = remove_ids
|
32
|
+
end
|
33
|
+
|
34
|
+
def data
|
35
|
+
if @node.respond_to?(:size)
|
36
|
+
@node.map do |n|
|
37
|
+
generate_data(node: n, includes: @includes)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
generate_data(node: @node, includes: @includes)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def generate_data(node:, includes:)
|
47
|
+
attributes = node.attributes
|
48
|
+
a_data = associations_data(node: node, includes: includes)
|
49
|
+
|
50
|
+
if @remove_ids
|
51
|
+
attributes.delete('id')
|
52
|
+
a_data.map do |a|
|
53
|
+
[a[:foreign_type], a[:foreign_key]] if a[:kind] == 'belongs_to'
|
54
|
+
end.flatten.compact.each do |key|
|
55
|
+
attributes.delete(key)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
{
|
60
|
+
class_name: node.class.name,
|
61
|
+
attributes: attributes,
|
62
|
+
associations: a_data
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def associations_data(node:, includes:)
|
67
|
+
association_data = generate_association_data(node: node, includes: includes)
|
68
|
+
return [association_data.data] if association_data.instance_of?(AssociationStruct)
|
69
|
+
|
70
|
+
association_data.flatten.map(&:data)
|
71
|
+
end
|
72
|
+
|
73
|
+
def generate_association_data(node:, includes:)
|
74
|
+
return [] if includes.nil? || includes.empty?
|
75
|
+
|
76
|
+
case includes
|
77
|
+
when Hash
|
78
|
+
generate_association_data_for_hash(node: node, includes: includes)
|
79
|
+
when Array
|
80
|
+
generate_association_data_for_array(node: node, includes: includes)
|
81
|
+
when Symbol
|
82
|
+
generate_association_data_for_symbol(node: node, includes: includes)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def generate_association_data_for_hash(node:, includes:)
|
87
|
+
includes.map do |association_name, inner_includes|
|
88
|
+
AssociationStruct.new(record: node, association_name: association_name, remove_ids: @remove_ids,
|
89
|
+
next_level_includes: inner_includes)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def generate_association_data_for_array(node:, includes:)
|
94
|
+
includes.map do |association_name|
|
95
|
+
# It needs to "pass" again because we don't know the element's kinds.
|
96
|
+
generate_association_data(node: node, includes: association_name)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def generate_association_data_for_symbol(node:, includes:)
|
101
|
+
if node.respond_to?(:size)
|
102
|
+
node.map do |inner_node|
|
103
|
+
AssociationStruct.new(record: inner_node, association_name: includes, remove_ids: @remove_ids)
|
104
|
+
end
|
105
|
+
else
|
106
|
+
AssociationStruct.new(record: node, association_name: includes, remove_ids: @remove_ids)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class AssociationStruct
|
112
|
+
def initialize(record:, association_name:, remove_ids:, next_level_includes: nil)
|
113
|
+
@next_level_includes = next_level_includes
|
114
|
+
@reflection = record.association(association_name).reflection
|
115
|
+
@association = record.send(association_name)
|
116
|
+
@remove_ids = remove_ids
|
117
|
+
end
|
118
|
+
|
119
|
+
def data
|
120
|
+
{
|
121
|
+
name: @reflection.name,
|
122
|
+
inverse: @reflection.inverse_of&.name,
|
123
|
+
foreign_key: @reflection.foreign_key,
|
124
|
+
foreign_type: @reflection.foreign_type,
|
125
|
+
kind: @reflection.class.name.match(/.*::(?<shortname>\w+)Reflection/)['shortname'].underscore,
|
126
|
+
content: RootStruct.new(node: @association, includes: @next_level_includes, remove_ids: @remove_ids).data
|
127
|
+
}.compact
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveAdmin
|
4
|
+
module Exportable
|
5
|
+
class Importer
|
6
|
+
def initialize(path: nil, data: nil, json: nil, yaml: nil, allow_update: false, ignore_ids: false, format: nil)
|
7
|
+
@data = data if data.present?
|
8
|
+
@data ||= path_to_data(path, format: format) if path.present?
|
9
|
+
@data ||= json_to_data(json) if json.present?
|
10
|
+
@data ||= yaml_to_data(yaml) if yaml.present?
|
11
|
+
@allow_update = allow_update
|
12
|
+
@ignore_ids = ignore_ids
|
13
|
+
end
|
14
|
+
|
15
|
+
def import
|
16
|
+
ActiveRecord::Base.transaction do
|
17
|
+
process_associations_content(@data)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def path_to_data(path, format: nil)
|
24
|
+
format ||= path.match(/.(?<format>\w+)$/)[:format]
|
25
|
+
case format.to_s
|
26
|
+
when 'json'
|
27
|
+
json_to_data(File.open(path).read)
|
28
|
+
when 'yaml'
|
29
|
+
yaml_to_data(File.open(path).read)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def json_to_data(json)
|
34
|
+
JSON.parse(json)
|
35
|
+
end
|
36
|
+
|
37
|
+
def yaml_to_data(yaml)
|
38
|
+
YAML.safe_load(yaml)
|
39
|
+
end
|
40
|
+
|
41
|
+
def process_associations_content(content, relation: nil, relation_name: nil)
|
42
|
+
if content.is_a?(Array)
|
43
|
+
content.each do |c|
|
44
|
+
process_data(c.with_indifferent_access, relation: relation, relation_name: relation_name)
|
45
|
+
end
|
46
|
+
else
|
47
|
+
process_data(content.with_indifferent_access, relation: relation, relation_name: relation_name)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def process_data(data, relation: nil, relation_name: nil)
|
52
|
+
record = update_or_create_record_from_data(data)
|
53
|
+
assign_relation_if_needed(record, relation, relation_name)
|
54
|
+
create_and_assign_belongs_to_associations(record, data)
|
55
|
+
record.save!
|
56
|
+
create_non_belongs_to_associations(record, data)
|
57
|
+
record
|
58
|
+
rescue StandardError => e
|
59
|
+
raise "#{e.message} - details: #{record.inspect}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def update_or_create_record_from_data(data)
|
63
|
+
record = find_or_initialize_from_data(data)
|
64
|
+
return record if !@allow_update && !record.new_record?
|
65
|
+
|
66
|
+
record.attributes = data[:attributes]
|
67
|
+
record
|
68
|
+
end
|
69
|
+
|
70
|
+
def find_or_initialize_from_data(data)
|
71
|
+
klass = data[:class_name].constantize
|
72
|
+
data[:attributes][:id] = nil if @ignore_ids
|
73
|
+
where_opts = if data[:attributes][:id].present?
|
74
|
+
{ id: data[:attributes][:id] }
|
75
|
+
elsif klass.respond_to?(:exportable_search_attributes)
|
76
|
+
klass.exportable_search_attributes.to_h { |x| [x, data[:attributes][x]] }
|
77
|
+
end
|
78
|
+
record = klass.where(where_opts).take if where_opts.present?
|
79
|
+
record || klass.new
|
80
|
+
end
|
81
|
+
|
82
|
+
def assign_relation_if_needed(record, relation, relation_name)
|
83
|
+
record.send("#{relation_name}=", relation) if relation.present?
|
84
|
+
end
|
85
|
+
|
86
|
+
def create_and_assign_belongs_to_associations(record, data)
|
87
|
+
filter_belongs_to_associations(data).each do |association_data|
|
88
|
+
association_record = process_data(association_data[:content])
|
89
|
+
record.send("#{association_data[:name]}=", association_record)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def create_non_belongs_to_associations(record, data)
|
94
|
+
filter_non_belongs_to_associations(data).each do |association_data|
|
95
|
+
process_associations_content(association_data[:content], relation: record,
|
96
|
+
relation_name: association_data[:inverse])
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def filter_belongs_to_associations(data)
|
101
|
+
data[:associations].select { |a| a[:kind] == 'belongs_to' }
|
102
|
+
end
|
103
|
+
|
104
|
+
def filter_non_belongs_to_associations(data)
|
105
|
+
data[:associations].reject { |a| a[:kind] == 'belongs_to' }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ActiveAdmin::Exportable do
|
6
|
+
describe 'extends ResourceDSL' do
|
7
|
+
it 'by adding #exportable' do
|
8
|
+
dsl = ActiveAdmin::ResourceDSL
|
9
|
+
|
10
|
+
expect(dsl.public_instance_methods).to include(:exportable)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'enables form-based exportion by default' do
|
15
|
+
dsl = ActiveAdmin::ResourceDSL.new(double('config'))
|
16
|
+
|
17
|
+
expect(dsl).to receive(:enable_resource_exportion_via_form)
|
18
|
+
|
19
|
+
dsl.exportable
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'enables save-based exportion with option `via: :save`' do
|
23
|
+
dsl = ActiveAdmin::ResourceDSL.new(double('config'))
|
24
|
+
|
25
|
+
expect(dsl).to receive(:enable_resource_exportion_via_save)
|
26
|
+
|
27
|
+
dsl.exportable(via: :save)
|
28
|
+
end
|
29
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_admin/exportable'
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
# These two settings work together to allow you to limit a spec run
|
7
|
+
# to individual examples or groups you care about by tagging them with
|
8
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
9
|
+
# get run.
|
10
|
+
config.filter_run :focus
|
11
|
+
config.run_all_when_everything_filtered = true
|
12
|
+
|
13
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
14
|
+
# file, and it's useful to allow more verbose output when running an
|
15
|
+
# individual spec file.
|
16
|
+
if config.files_to_run.one?
|
17
|
+
# Use the documentation formatter for detailed output,
|
18
|
+
# unless a formatter has already been configured
|
19
|
+
# (e.g. via a command-line flag).
|
20
|
+
config.default_formatter = 'doc'
|
21
|
+
end
|
22
|
+
|
23
|
+
# Run specs in random order to surface order dependencies. If you find an
|
24
|
+
# order dependency and want to debug it, you can fix the order by providing
|
25
|
+
# the seed, which is printed after each run.
|
26
|
+
# --seed 1234
|
27
|
+
config.order = :random
|
28
|
+
|
29
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
30
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
31
|
+
# test failures related to randomization by passing the same `--seed` value
|
32
|
+
# as the one that triggered the failure.
|
33
|
+
Kernel.srand config.seed
|
34
|
+
|
35
|
+
config.expect_with :rspec do |expectations|
|
36
|
+
expectations.syntax = :expect
|
37
|
+
end
|
38
|
+
|
39
|
+
config.mock_with :rspec do |mocks|
|
40
|
+
mocks.syntax = :expect
|
41
|
+
mocks.verify_partial_doubles = true
|
42
|
+
end
|
43
|
+
end
|
metadata
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: active_admin-exportable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Wagner Caixeta
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-05-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activeadmin
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activerecord
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: sass-rails
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Allow user to export/import of ActiveRecord records and associated records
|
98
|
+
in ActiveAdmin.
|
99
|
+
email:
|
100
|
+
- wagner.caixeta@gmail.com.com
|
101
|
+
executables: []
|
102
|
+
extensions: []
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- ".gitignore"
|
106
|
+
- CHANGELOG.md
|
107
|
+
- Gemfile
|
108
|
+
- LICENSE.txt
|
109
|
+
- README.md
|
110
|
+
- Rakefile
|
111
|
+
- active_admin-exportable.gemspec
|
112
|
+
- app/admin/exportable/import.rb
|
113
|
+
- lib/active_admin/exportable.rb
|
114
|
+
- lib/active_admin/exportable/engine.rb
|
115
|
+
- lib/active_admin/exportable/exporter.rb
|
116
|
+
- lib/active_admin/exportable/importer.rb
|
117
|
+
- lib/active_admin/exportable/version.rb
|
118
|
+
- spec/exportable_spec.rb
|
119
|
+
- spec/spec_helper.rb
|
120
|
+
homepage: https://github.com/zorab47/active_admin-exportable
|
121
|
+
licenses:
|
122
|
+
- MIT
|
123
|
+
metadata: {}
|
124
|
+
post_install_message:
|
125
|
+
rdoc_options: []
|
126
|
+
require_paths:
|
127
|
+
- lib
|
128
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
requirements: []
|
139
|
+
rubygems_version: 3.2.17
|
140
|
+
signing_key:
|
141
|
+
specification_version: 4
|
142
|
+
summary: A export/import tool for ActiveAdmin.
|
143
|
+
test_files:
|
144
|
+
- spec/exportable_spec.rb
|
145
|
+
- spec/spec_helper.rb
|