radiant-import_export-extension 1.0.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.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.textile +47 -0
- data/Rakefile +26 -0
- data/app/controllers/admin/export_controller.rb +5 -0
- data/import_export_extension.rb +15 -0
- data/lib/exporter.rb +25 -0
- data/lib/import_export/version.rb +3 -0
- data/lib/loader.rb +30 -0
- data/lib/radiant_setup_create_records_patch.rb +52 -0
- data/lib/tasks/import_export_extension_tasks.rake +88 -0
- data/radiant-import_export-extension.gemspec +18 -0
- data/test/functional/import_export_extension_test.rb +10 -0
- data/test/test_helper.rb +18 -0
- metadata +86 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Andrew vonderLuft
|
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.textile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
h1. Import Export
|
2
|
+
|
3
|
+
The *Import Export* extension supports an enhanced version of exporting your Radiant database, and
|
4
|
+
adds the ability to import the results of an export operation.
|
5
|
+
|
6
|
+
h2. Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile: @gem 'radiant-import_export-extension'@
|
9
|
+
And then execute: @$ bundle@
|
10
|
+
Or install it yourself as: @$ gem install radiant-import_export-extension@
|
11
|
+
|
12
|
+
h2. History
|
13
|
+
|
14
|
+
* 2014-04-07 - Gemify [Andrew vonderLuft]
|
15
|
+
* 2013-12-31 - added capability to import/export join tables [Andrew vonderLuft]
|
16
|
+
* since then - "tweaks and enhancements":https://github.com/radiant/radiant-import-export-extension/commits/master
|
17
|
+
* 2009-09-22 - Skip addition (ability to skip tables) [Johannes Fahrenkrug]
|
18
|
+
* 2007-04-18 - First release [Sean Cribbs]
|
19
|
+
|
20
|
+
h2. Usage
|
21
|
+
|
22
|
+
* Import: $ rake db:import [TEMPLATE=path/to/export.yml]
|
23
|
+
* Export: $ rake db:export [SKIP="tabelname1,tablename2"] [TEMPLATE=path/to/export.yml] (or browse to /admin/export )
|
24
|
+
|
25
|
+
h2. Contributing
|
26
|
+
|
27
|
+
# Fork it
|
28
|
+
# Create your feature branch (`git checkout -b my-new-feature`)
|
29
|
+
# Commit your changes (`git commit -am 'Add some feature'`)
|
30
|
+
# Push to the branch (`git push origin my-new-feature`)
|
31
|
+
# Create new Pull Request
|
32
|
+
|
33
|
+
h2. To do
|
34
|
+
|
35
|
+
* Import still not working with Oracle
|
36
|
+
* Add more tests
|
37
|
+
|
38
|
+
h2. "Contributors":https://github.com/radiant/radiant-import-export-extension/graphs/contributors
|
39
|
+
|
40
|
+
* "Sean Cribbs":https://github.com/seancribbs
|
41
|
+
* "Johannes Fahrenkrug":https://github.com/jfahrenkrug
|
42
|
+
* "Drew Neil":https://github.com/nelstrom
|
43
|
+
* "Istvan Hoka":https://github.com/ihoka
|
44
|
+
* "Chris Parrish":https://github.com/chrisparrish
|
45
|
+
* "Andrew vonderLuft":https://github.com/avonderluft
|
46
|
+
* "et al.":https://github.com/radiant/radiant-import-export-extension/graphs/contributors
|
47
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'bundler/gem_tasks'
|
5
|
+
|
6
|
+
desc 'Default: run unit tests.'
|
7
|
+
task :default => :test
|
8
|
+
|
9
|
+
desc 'Test the import_export extension.'
|
10
|
+
Rake::TestTask.new(:test) do |t|
|
11
|
+
t.libs << 'lib'
|
12
|
+
t.pattern = 'test/**/*_test.rb'
|
13
|
+
t.verbose = true
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Generate documentation for the import_export extension.'
|
17
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
18
|
+
rdoc.rdoc_dir = 'rdoc'
|
19
|
+
rdoc.title = 'ImportExportExtension'
|
20
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
21
|
+
rdoc.rdoc_files.include('README')
|
22
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
23
|
+
end
|
24
|
+
|
25
|
+
# Load any custom rakefiles for extension
|
26
|
+
Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# Uncomment this if you reference any of your controllers in activate
|
2
|
+
# require_dependency 'application'
|
3
|
+
|
4
|
+
class ImportExportExtension < Radiant::Extension
|
5
|
+
version ImportExport::VERSION
|
6
|
+
description "Supports more flexible import and export to Radiant databases."
|
7
|
+
url "https://github.com/radiant/radiant-import-export-extension"
|
8
|
+
|
9
|
+
def activate
|
10
|
+
end
|
11
|
+
|
12
|
+
def deactivate
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
data/lib/exporter.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
class Exporter
|
2
|
+
extend Loader
|
3
|
+
cattr_accessor :models, :join_tables
|
4
|
+
self.models = models_from_database
|
5
|
+
self.join_tables = join_tables_from_database
|
6
|
+
|
7
|
+
def self.export
|
8
|
+
hash = {'name' => 'Last export', 'description' => "Backup of the database as of #{Time.now.to_s(:rfc822)}"}
|
9
|
+
hash['records'] = {}
|
10
|
+
self.models.each do |klass|
|
11
|
+
recs = klass.find(:all)
|
12
|
+
if recs
|
13
|
+
hash['records'][klass.name.pluralize] = recs.inject({}) { |h, record| h[record.id.to_s] = record.attributes; h }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
self.join_tables.each do |table|
|
17
|
+
recs = ActiveRecord::Base.connection.select_all("SELECT * FROM #{table}")
|
18
|
+
if recs
|
19
|
+
i = 0
|
20
|
+
hash['records'][table.camelize] = recs.inject({}) { |h, record| h[i += 1] = record; h }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
hash.to_yaml
|
24
|
+
end
|
25
|
+
end
|
data/lib/loader.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Loader
|
2
|
+
# In most cases singularizing a table's name yields the model's name but in
|
3
|
+
# special cases where the model's name is already seen as plural by rails, it
|
4
|
+
# expects the table's name to then also be plural (so the model: 'MyData' goes
|
5
|
+
# with the table: 'my_data')
|
6
|
+
def models_from_database
|
7
|
+
tables = ActiveRecord::Base.connection.tables
|
8
|
+
tables.delete "config"
|
9
|
+
if ENV['SKIP']
|
10
|
+
table_names = ENV['SKIP'].split(',')
|
11
|
+
table_names.each do |table_to_skip|
|
12
|
+
puts "Skipping #{table_to_skip}"
|
13
|
+
tables.delete table_to_skip.strip
|
14
|
+
end
|
15
|
+
end
|
16
|
+
models = tables.collect { |table| table.camelize.singularize.constantize rescue nil || table.camelize.constantize rescue nil }.compact
|
17
|
+
models << Radiant::Config
|
18
|
+
end
|
19
|
+
|
20
|
+
def join_tables_from_database
|
21
|
+
join_tables = []
|
22
|
+
excluded_tables = ["schema_migrations"]
|
23
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
24
|
+
unless ActiveRecord::Base.connection.columns(table).map { |c| c.name }.include?("id")
|
25
|
+
join_tables << table unless excluded_tables.include?(table)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
join_tables.compact
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Radiant
|
2
|
+
class Setup
|
3
|
+
def create_records(template)
|
4
|
+
records = template['records']
|
5
|
+
if records
|
6
|
+
puts
|
7
|
+
records.keys.each do |key|
|
8
|
+
table = key.underscore
|
9
|
+
# assume this is an ActiveRecord model if table has column 'id'
|
10
|
+
if key == "Radiant::Configs" || ActiveRecord::Base.connection.columns(table).map { |c| c.name }.include?("id")
|
11
|
+
feedback "Importing '#{key.to_s.underscore.humanize.titleize}' table data" do
|
12
|
+
model = model(key)
|
13
|
+
model.reset_column_information
|
14
|
+
record_pairs = order_by_id(records[key])
|
15
|
+
step do
|
16
|
+
record_pairs.each do |id, record|
|
17
|
+
begin
|
18
|
+
#puts "i: #{id}"
|
19
|
+
#puts "r: #{record}"
|
20
|
+
#puts
|
21
|
+
r = model.new(record)
|
22
|
+
r.id = id
|
23
|
+
r.save
|
24
|
+
# UserActionObserver sets user to null, so we have to update explicitly
|
25
|
+
model.update_all({:created_by_id => record['created_by_id']}, {:id => r.id}) if r.respond_to? :created_by_id
|
26
|
+
rescue Exception => e
|
27
|
+
puts "Failed to create record #{id}. Reason: #{e}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
else
|
33
|
+
feedback "Importing '#{key.to_s.underscore.humanize.titleize}' join table data" do
|
34
|
+
table = key.underscore
|
35
|
+
record_pairs = records[key].sort
|
36
|
+
step do
|
37
|
+
record_pairs.each do |id, record|
|
38
|
+
begin
|
39
|
+
sql = "INSERT INTO #{table} (#{record.keys.join(", ")}) VALUES (#{record.values.join(", ")})"
|
40
|
+
ActiveRecord::Base.connection.execute(sql)
|
41
|
+
rescue Exception => e
|
42
|
+
puts "Failed to create record #{id}. Reason: #{e}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
namespace :db do
|
2
|
+
desc "Import a database template from db/export.yml. Specify the TEMPLATE environment variable to load a different template. This is not intended for new installations, but restoration from previous exports."
|
3
|
+
task :import do
|
4
|
+
require 'highline/import'
|
5
|
+
say "ERROR: Specify a template to load with the TEMPLATE environment variable." and exit unless (ENV['TEMPLATE'] and File.exists?(ENV['TEMPLATE']))
|
6
|
+
Rake::Task["db:schema:load"].invoke
|
7
|
+
# Use what Radiant::Setup for the heavy lifting
|
8
|
+
require 'radiant/setup'
|
9
|
+
require 'lib/radiant_setup_create_records_patch'
|
10
|
+
setup = Radiant::Setup.new
|
11
|
+
|
12
|
+
# Load the data from the export file
|
13
|
+
data = YAML.load_file(ENV['TEMPLATE'] || "#{RAILS_ROOT}/db/export.yml")
|
14
|
+
|
15
|
+
# Reorder Radiant::Config table to ensure ids are sequential with no gaps, to prevent load error
|
16
|
+
configs_in_order = data['records']['Radiant::Configs'].sort_by { |k,v| v['id'] }
|
17
|
+
new_configs = {}
|
18
|
+
configs_in_order.each do |attributes|
|
19
|
+
rec_id = configs_in_order.index(attributes) + 1
|
20
|
+
new_configs[rec_id.to_s] = {'id' => rec_id}
|
21
|
+
Radiant::Config.column_names.delete_if { |el| el == "id" }.each do |att|
|
22
|
+
new_configs[rec_id.to_s][att] = attributes[1][att]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
data['records']['Radiant::Configs'].replace(new_configs)
|
26
|
+
|
27
|
+
# Load the users first so created_by fields can be updated
|
28
|
+
users_only = {'records' => {'Users' => data['records'].delete('Users')}}
|
29
|
+
passwords = []
|
30
|
+
users_only['records']['Users'].each do |id, attributes|
|
31
|
+
if attributes['password']
|
32
|
+
passwords << [attributes['id'], attributes['password'], attributes['salt']]
|
33
|
+
attributes['password'] = 'radiant'
|
34
|
+
attributes['password_confirmation'] = 'radiant'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
setup.send :create_records, users_only
|
38
|
+
|
39
|
+
# Hack to get passwords transferred correctly.
|
40
|
+
passwords.each do |id, password, salt|
|
41
|
+
User.update_all({:password => password, :salt => salt}, ['id = ?', id])
|
42
|
+
end
|
43
|
+
|
44
|
+
# Now load the created users into the hash and load the rest of the data
|
45
|
+
data['records'].each do |klass, records|
|
46
|
+
records.each do |key, attributes|
|
47
|
+
if attributes.has_key? 'created_by'
|
48
|
+
attributes['created_by'] = User.find(attributes['created_by']) rescue nil
|
49
|
+
end
|
50
|
+
if attributes.has_key? 'updated_by'
|
51
|
+
attributes['updated_by'] = User.find(attributes['updated_by']) rescue nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
setup.send :create_records, data
|
56
|
+
|
57
|
+
# if env set, adjust the auto increment counter, needed for DB2
|
58
|
+
if ENV['FIXIDS']
|
59
|
+
puts
|
60
|
+
data['records'].each do |klass, records|
|
61
|
+
tabname = klass.to_s.singularize.constantize.table_name
|
62
|
+
|
63
|
+
if ActiveRecord::Base.connection and !ActiveRecord::Base.connection.schema.to_s.empty?
|
64
|
+
tabname = ActiveRecord::Base.connection.schema.to_s + '.' + tabname
|
65
|
+
end
|
66
|
+
|
67
|
+
res = ActiveRecord::Base.connection.select_value("SELECT max(id)+1 from #{tabname};")
|
68
|
+
|
69
|
+
if res
|
70
|
+
puts "Adjusting auto increment value for the id column of #{tabname}..."
|
71
|
+
ActiveRecord::Base.connection.execute("alter table #{tabname} alter column id restart with #{res};")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
puts 'Done.'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
desc "Export a database template to db/export_TIME.yml. Specify the TEMPLATE environment variable to use a different file."
|
79
|
+
task :export do
|
80
|
+
require "activerecord" if !defined?(ActiveRecord)
|
81
|
+
require "#{RAILS_ROOT}/config/environment.rb"
|
82
|
+
ActiveRecord::Base.establish_connection
|
83
|
+
require "#{File.expand_path(File.dirname(__FILE__) + '/../')}/loader.rb"
|
84
|
+
require "#{File.expand_path(File.dirname(__FILE__) + '/../')}/exporter.rb"
|
85
|
+
template_name = ENV['TEMPLATE'] || "#{RAILS_ROOT}/db/export_#{Time.now.utc.strftime("%Y%m%d%H%M%S")}.yml"
|
86
|
+
File.open(template_name, "w") {|f| f.write Exporter.export }
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'import_export/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "radiant-import_export-extension"
|
8
|
+
gem.version = ImportExport::VERSION
|
9
|
+
gem.authors = ["Sean Cribbs","Johannes Fahrenkrug","Drew Neil","Istvan Hoka","Chris Parrish","Andrew vonderLuft"]
|
10
|
+
gem.email = ["avonderluft@avlux.net"]
|
11
|
+
gem.description = %q{Enhanced version of exporting and importing your Radiant database tables to/from YAML files}
|
12
|
+
gem.summary = %q{Supports more flexible import and export to Radiant databases.}
|
13
|
+
gem.homepage = "https://github.com/radiant/radiant-import-export-extension"
|
14
|
+
gem.files = `git ls-files`.split($/)
|
15
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class ImportExportExtensionTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def test_initialization
|
6
|
+
assert_equal File.join(File.expand_path(RADIANT_ROOT), 'vendor', 'extensions', 'import_export'), ImportExportExtension.root
|
7
|
+
assert_equal 'Import Export', ImportExportExtension.extension_name
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# Load the environment
|
2
|
+
unless defined? RADIANT_ROOT
|
3
|
+
ENV["RAILS_ENV"] = "test"
|
4
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../")}/config/environment"
|
5
|
+
end
|
6
|
+
require "#{RADIANT_ROOT}/test/test_helper"
|
7
|
+
|
8
|
+
class Test::Unit::TestCase
|
9
|
+
|
10
|
+
# Include a helper to make testing Radius tags easier
|
11
|
+
test_helper :extension_tags
|
12
|
+
|
13
|
+
# Add the fixture directory to the fixture path
|
14
|
+
self.fixture_path << File.dirname(__FILE__) + "/fixtures"
|
15
|
+
|
16
|
+
# Add more helper methods to be used by all extension tests here...
|
17
|
+
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: radiant-import_export-extension
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Sean Cribbs
|
14
|
+
- Johannes Fahrenkrug
|
15
|
+
- Drew Neil
|
16
|
+
- Istvan Hoka
|
17
|
+
- Chris Parrish
|
18
|
+
- Andrew vonderLuft
|
19
|
+
autorequire:
|
20
|
+
bindir: bin
|
21
|
+
cert_chain: []
|
22
|
+
|
23
|
+
date: 2014-04-07 00:00:00 Z
|
24
|
+
dependencies: []
|
25
|
+
|
26
|
+
description: Enhanced version of exporting and importing your Radiant database tables to/from YAML files
|
27
|
+
email:
|
28
|
+
- avonderluft@avlux.net
|
29
|
+
executables: []
|
30
|
+
|
31
|
+
extensions: []
|
32
|
+
|
33
|
+
extra_rdoc_files: []
|
34
|
+
|
35
|
+
files:
|
36
|
+
- .gitignore
|
37
|
+
- Gemfile
|
38
|
+
- LICENSE.txt
|
39
|
+
- README.textile
|
40
|
+
- Rakefile
|
41
|
+
- app/controllers/admin/export_controller.rb
|
42
|
+
- import_export_extension.rb
|
43
|
+
- lib/exporter.rb
|
44
|
+
- lib/import_export/version.rb
|
45
|
+
- lib/loader.rb
|
46
|
+
- lib/radiant_setup_create_records_patch.rb
|
47
|
+
- lib/tasks/import_export_extension_tasks.rake
|
48
|
+
- radiant-import_export-extension.gemspec
|
49
|
+
- test/functional/import_export_extension_test.rb
|
50
|
+
- test/test_helper.rb
|
51
|
+
homepage: https://github.com/radiant/radiant-import-export-extension
|
52
|
+
licenses: []
|
53
|
+
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options: []
|
56
|
+
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
hash: 3
|
65
|
+
segments:
|
66
|
+
- 0
|
67
|
+
version: "0"
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
hash: 3
|
74
|
+
segments:
|
75
|
+
- 0
|
76
|
+
version: "0"
|
77
|
+
requirements: []
|
78
|
+
|
79
|
+
rubyforge_project:
|
80
|
+
rubygems_version: 1.8.25
|
81
|
+
signing_key:
|
82
|
+
specification_version: 3
|
83
|
+
summary: Supports more flexible import and export to Radiant databases.
|
84
|
+
test_files:
|
85
|
+
- test/functional/import_export_extension_test.rb
|
86
|
+
- test/test_helper.rb
|