data_seeder 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +256 -0
- data/Rakefile +34 -0
- data/app/models/data_seeder/seed_file.rb +34 -0
- data/db/migrate/20150306195118_create_data_seeder_seed_files.rb +9 -0
- data/lib/data_seeder.rb +68 -0
- data/lib/data_seeder/config.rb +41 -0
- data/lib/data_seeder/engine.rb +5 -0
- data/lib/data_seeder/loader.rb +122 -0
- data/lib/data_seeder/loader/csv.rb +15 -0
- data/lib/data_seeder/loader/json.rb +20 -0
- data/lib/data_seeder/loader/txt.rb +23 -0
- data/lib/data_seeder/loader/yaml.rb +23 -0
- data/lib/data_seeder/logger.rb +15 -0
- data/lib/data_seeder/version.rb +3 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/models/app.rb +3 -0
- data/test/dummy/app/models/app_error.rb +3 -0
- data/test/dummy/app/models/app_error_data_seeder.rb +52 -0
- data/test/dummy/app/models/country.rb +14 -0
- data/test/dummy/app/models/state.rb +2 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +26 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +12 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/test.rb +42 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/db/migrate/20150313022149_create_countries.rb +8 -0
- data/test/dummy/db/migrate/20150313022228_create_states.rb +8 -0
- data/test/dummy/db/migrate/20150313172634_create_apps.rb +7 -0
- data/test/dummy/db/migrate/20150313172719_create_app_errors.rb +10 -0
- data/test/dummy/db/schema.rb +45 -0
- data/test/dummy/db/seed.test/bar.err +3 -0
- data/test/dummy/db/seed.test/countries.txt +249 -0
- data/test/dummy/db/seed.test/foo.err +3 -0
- data/test/dummy/db/seed.test/states.csv +51 -0
- data/test/dummy/db/seed.test/states.json +153 -0
- data/test/dummy/db/seed.test/states.txt +51 -0
- data/test/dummy/db/seed.test/states.yml +101 -0
- data/test/dummy/db/seed.test/zulu.err +2 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +39 -0
- data/test/dummy/log/test.log +68768 -0
- data/test/models/data_seeder_test.rb +147 -0
- data/test/test_helper.rb +12 -0
- metadata +159 -0
@@ -0,0 +1,122 @@
|
|
1
|
+
module DataSeeder
|
2
|
+
module Loader
|
3
|
+
attr_accessor :file_config, :key_attribute
|
4
|
+
attr_reader :path, :path_minus_ext
|
5
|
+
|
6
|
+
def initialize(options={})
|
7
|
+
@only = options[:only]
|
8
|
+
@except = options[:except]
|
9
|
+
if options.has_key?(:purge)
|
10
|
+
@purge = options[:purge]
|
11
|
+
else
|
12
|
+
@purge = true
|
13
|
+
end
|
14
|
+
@old_keys = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def config
|
18
|
+
DataSeeder.config
|
19
|
+
end
|
20
|
+
|
21
|
+
def logger
|
22
|
+
DataSeeder.logger
|
23
|
+
end
|
24
|
+
|
25
|
+
def klass
|
26
|
+
# This should always translate to a class except for custom loaders
|
27
|
+
@path_minus_ext.classify.constantize rescue nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def process(path)
|
31
|
+
@path = path
|
32
|
+
dot_index = @path.rindex('.')
|
33
|
+
@path_minus_ext = @path[0, dot_index]
|
34
|
+
@file_config = {}
|
35
|
+
File.open(@path, 'r') do |fin|
|
36
|
+
load_file_config(fin)
|
37
|
+
setup
|
38
|
+
load(fin)
|
39
|
+
teardown
|
40
|
+
end
|
41
|
+
call_file_method(:teardown)
|
42
|
+
end
|
43
|
+
|
44
|
+
def setup
|
45
|
+
@key_attribute = self.file_config[:key_attribute] || :id
|
46
|
+
@old_keys = self.klass.all.pluck(@key_attribute).map(&:to_s) if @purge
|
47
|
+
logger.info { "Loading #{@path}" }
|
48
|
+
call_file_method(:setup)
|
49
|
+
end
|
50
|
+
|
51
|
+
def teardown
|
52
|
+
@old_keys.each do |key|
|
53
|
+
if model = self.klass.find_by(@key_attribute => key)
|
54
|
+
logger.info { " Destroying #{model_info(model)}"}
|
55
|
+
model.destroy
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# The information displayed when creating, updating, or destroying a model.
|
61
|
+
# The changes argument will be the model.changes on an update.
|
62
|
+
def model_info(model, changes=nil)
|
63
|
+
if changes
|
64
|
+
attr = @file_config[:update_display_method] || @key_attribute
|
65
|
+
"#{model.send(attr)}: #{changes.inspect}"
|
66
|
+
else
|
67
|
+
model.inspect
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def load_file_config(fin)
|
72
|
+
config_line = fin.readline
|
73
|
+
if match = config_line.match(/^\s*#\s*config:(.*)/)
|
74
|
+
@file_config = eval(match[1])
|
75
|
+
else
|
76
|
+
fin.seek(0)
|
77
|
+
if self.klass && self.klass.respond_to?(:data_seeder_config)
|
78
|
+
@file_config = self.klass.data_seeder_config
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def load(fin)
|
84
|
+
throw 'Must override load'
|
85
|
+
end
|
86
|
+
|
87
|
+
def save(attr)
|
88
|
+
key = attr[@key_attribute.to_s] || attr[@key_attribute.to_sym]
|
89
|
+
raise "No #{@key_attribute} in #{attr.inspect}" unless key
|
90
|
+
@old_keys.delete(key.to_s)
|
91
|
+
model = self.klass.find_or_initialize_by(@key_attribute => key)
|
92
|
+
model.attributes = attr
|
93
|
+
save_model(model)
|
94
|
+
end
|
95
|
+
|
96
|
+
def save_model(model)
|
97
|
+
if model.new_record?
|
98
|
+
logger.info { " Saving #{model_info(model)}" }
|
99
|
+
else
|
100
|
+
changes = model.changes
|
101
|
+
return if changes.empty?
|
102
|
+
logger.info { " Updating #{model_info(model, changes)}" }
|
103
|
+
end
|
104
|
+
model.save!
|
105
|
+
end
|
106
|
+
|
107
|
+
def call_file_method(name, *args)
|
108
|
+
if method = @file_config[name]
|
109
|
+
return method.call(*args)
|
110
|
+
else
|
111
|
+
class_method = "data_seeder_#{name}"
|
112
|
+
return self.klass.send(class_method, *args) if @klass.respond_to?(class_method)
|
113
|
+
end
|
114
|
+
return nil
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
require 'data_seeder/loader/csv'
|
120
|
+
require 'data_seeder/loader/json'
|
121
|
+
require 'data_seeder/loader/yaml'
|
122
|
+
require 'data_seeder/loader/txt'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module DataSeeder
|
4
|
+
module Loader
|
5
|
+
class JSON
|
6
|
+
include Loader
|
7
|
+
def load(io)
|
8
|
+
json = ::JSON.parse(io.read)
|
9
|
+
if json.kind_of?(Hash)
|
10
|
+
json.each do |key, attr|
|
11
|
+
attr[self.key_attribute] = key if self.key_attribute
|
12
|
+
save(attr)
|
13
|
+
end
|
14
|
+
else
|
15
|
+
Array(json).each { |attr| save(attr) }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module DataSeeder
|
2
|
+
module Loader
|
3
|
+
class Txt
|
4
|
+
include Loader
|
5
|
+
|
6
|
+
def load(io)
|
7
|
+
if method = self.file_config[:line]
|
8
|
+
io.each_line do |line|
|
9
|
+
next if line.blank? || line.match(/^\s*#/)
|
10
|
+
save(method.call(line))
|
11
|
+
end
|
12
|
+
elsif self.klass.respond_to?(:data_seeder_line)
|
13
|
+
io.each_line do |line|
|
14
|
+
next if line.blank? || line.match(/^\s*#/)
|
15
|
+
save(self.klass.send(:data_seeder_line, line))
|
16
|
+
end
|
17
|
+
else
|
18
|
+
raise "No line method defined for #{self.klass.name}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module DataSeeder
|
4
|
+
module Loader
|
5
|
+
class YAML
|
6
|
+
include Loader
|
7
|
+
|
8
|
+
def load(io)
|
9
|
+
yaml = ::YAML.load(io.read)
|
10
|
+
if yaml.kind_of?(Hash)
|
11
|
+
yaml.each do |key, attr|
|
12
|
+
attr[self.key_attribute] = key if self.key_attribute
|
13
|
+
save(attr)
|
14
|
+
end
|
15
|
+
elsif yaml.kind_of?(Array)
|
16
|
+
yaml.each { |attr| save(attr) }
|
17
|
+
else
|
18
|
+
raise "Don't know how to interpret #{self.path}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module DataSeeder
|
2
|
+
class Logger < ::Logger
|
3
|
+
attr_accessor :verbose
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
super($stdout)
|
7
|
+
@verbose = true
|
8
|
+
self.formatter = ->(severity, datetime, progname, msg) { "#{msg}\n" }
|
9
|
+
end
|
10
|
+
|
11
|
+
def info(arg='', &block)
|
12
|
+
super if @verbose
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/test/dummy/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'data_seeder'
|
2
|
+
|
3
|
+
class AppErrorDataSeeder
|
4
|
+
include ::DataSeeder::Loader
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@app = App.find_or_initialize_by(name: self.path_minus_ext)
|
8
|
+
@existing_errors = {}
|
9
|
+
if @app.new_record?
|
10
|
+
logger.info "Loading errors for new App: #{@app.name}"
|
11
|
+
@app.save!
|
12
|
+
else
|
13
|
+
logger.info "Loading errors for existing App: #{@app.name}"
|
14
|
+
@app.app_errors.each do |app_error|
|
15
|
+
@existing_errors[app_error.code] = app_error
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def teardown
|
21
|
+
unless @existing_errors.empty?
|
22
|
+
logger.info { " The following are begin removed:" }
|
23
|
+
@existing_errors.each do |code, app_error|
|
24
|
+
logger.info " #{code}: #{app_error.message}"
|
25
|
+
app_error.destroy
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def load(io)
|
31
|
+
io.each_line do |line|
|
32
|
+
line.strip!
|
33
|
+
next if line.blank? || line[0] == ?#
|
34
|
+
space_i = line.index(' ')
|
35
|
+
raise "Invalid line: #{line}" unless space_i
|
36
|
+
code = line[0,space_i].strip
|
37
|
+
message = line[space_i+1..-1].strip
|
38
|
+
app_error = @existing_errors[code]
|
39
|
+
if app_error
|
40
|
+
@existing_errors.delete(code)
|
41
|
+
app_error.message = message
|
42
|
+
unless app_error.changes.empty?
|
43
|
+
logger.info { " Changing #{code}: #{app_error.changes}" }
|
44
|
+
app_error.save!
|
45
|
+
end
|
46
|
+
else
|
47
|
+
logger.info { " Creating #{code}: #{message}" }
|
48
|
+
@app.app_errors.create!(code: code, message: message)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/test/dummy/bin/rake
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.expand_path('../boot', __FILE__)
|
2
|
+
|
3
|
+
require 'rails/all'
|
4
|
+
|
5
|
+
Bundler.require(*Rails.groups)
|
6
|
+
require "data_seeder"
|
7
|
+
|
8
|
+
module Dummy
|
9
|
+
class Application < Rails::Application
|
10
|
+
# Settings in config/environments/* take precedence over those specified here.
|
11
|
+
# Application configuration should go into files in config/initializers
|
12
|
+
# -- all .rb files in that directory are automatically loaded.
|
13
|
+
|
14
|
+
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
|
15
|
+
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
|
16
|
+
# config.time_zone = 'Central Time (US & Canada)'
|
17
|
+
|
18
|
+
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
|
19
|
+
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
|
20
|
+
# config.i18n.default_locale = :de
|
21
|
+
|
22
|
+
config.generators do |g|
|
23
|
+
g.test_framework :mini_test, :spec => true, :fixture => false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
Rails.application.configure do
|
2
|
+
# Settings specified here will take precedence over those in config/application.rb.
|
3
|
+
|
4
|
+
# In the development environment your application's code is reloaded on
|
5
|
+
# every request. This slows down response time but is perfect for development
|
6
|
+
# since you don't have to restart the web server when you make code changes.
|
7
|
+
config.cache_classes = false
|
8
|
+
|
9
|
+
# Do not eager load code on boot.
|
10
|
+
config.eager_load = false
|
11
|
+
|
12
|
+
# Show full error reports and disable caching.
|
13
|
+
config.consider_all_requests_local = true
|
14
|
+
config.action_controller.perform_caching = false
|
15
|
+
|
16
|
+
# Don't care if the mailer can't send.
|
17
|
+
config.action_mailer.raise_delivery_errors = false
|
18
|
+
|
19
|
+
# Print deprecation notices to the Rails logger.
|
20
|
+
config.active_support.deprecation = :log
|
21
|
+
|
22
|
+
# Raise an error on page load if there are pending migrations.
|
23
|
+
config.active_record.migration_error = :page_load
|
24
|
+
|
25
|
+
# Debug mode disables concatenation and preprocessing of assets.
|
26
|
+
# This option may cause significant delays in view rendering with a large
|
27
|
+
# number of complex assets.
|
28
|
+
config.assets.debug = true
|
29
|
+
|
30
|
+
# Adds additional error checking when serving assets at runtime.
|
31
|
+
# Checks for improperly declared sprockets dependencies.
|
32
|
+
# Raises helpful error messages.
|
33
|
+
config.assets.raise_runtime_errors = true
|
34
|
+
|
35
|
+
# Raises error for missing translations
|
36
|
+
# config.action_view.raise_on_missing_translations = true
|
37
|
+
end
|