data_seeder 0.0.1

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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +256 -0
  3. data/Rakefile +34 -0
  4. data/app/models/data_seeder/seed_file.rb +34 -0
  5. data/db/migrate/20150306195118_create_data_seeder_seed_files.rb +9 -0
  6. data/lib/data_seeder.rb +68 -0
  7. data/lib/data_seeder/config.rb +41 -0
  8. data/lib/data_seeder/engine.rb +5 -0
  9. data/lib/data_seeder/loader.rb +122 -0
  10. data/lib/data_seeder/loader/csv.rb +15 -0
  11. data/lib/data_seeder/loader/json.rb +20 -0
  12. data/lib/data_seeder/loader/txt.rb +23 -0
  13. data/lib/data_seeder/loader/yaml.rb +23 -0
  14. data/lib/data_seeder/logger.rb +15 -0
  15. data/lib/data_seeder/version.rb +3 -0
  16. data/test/dummy/Rakefile +6 -0
  17. data/test/dummy/app/models/app.rb +3 -0
  18. data/test/dummy/app/models/app_error.rb +3 -0
  19. data/test/dummy/app/models/app_error_data_seeder.rb +52 -0
  20. data/test/dummy/app/models/country.rb +14 -0
  21. data/test/dummy/app/models/state.rb +2 -0
  22. data/test/dummy/bin/bundle +3 -0
  23. data/test/dummy/bin/rails +4 -0
  24. data/test/dummy/bin/rake +4 -0
  25. data/test/dummy/config.ru +4 -0
  26. data/test/dummy/config/application.rb +26 -0
  27. data/test/dummy/config/boot.rb +5 -0
  28. data/test/dummy/config/database.yml +12 -0
  29. data/test/dummy/config/environment.rb +5 -0
  30. data/test/dummy/config/environments/development.rb +37 -0
  31. data/test/dummy/config/environments/test.rb +42 -0
  32. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  33. data/test/dummy/config/initializers/inflections.rb +16 -0
  34. data/test/dummy/db/migrate/20150313022149_create_countries.rb +8 -0
  35. data/test/dummy/db/migrate/20150313022228_create_states.rb +8 -0
  36. data/test/dummy/db/migrate/20150313172634_create_apps.rb +7 -0
  37. data/test/dummy/db/migrate/20150313172719_create_app_errors.rb +10 -0
  38. data/test/dummy/db/schema.rb +45 -0
  39. data/test/dummy/db/seed.test/bar.err +3 -0
  40. data/test/dummy/db/seed.test/countries.txt +249 -0
  41. data/test/dummy/db/seed.test/foo.err +3 -0
  42. data/test/dummy/db/seed.test/states.csv +51 -0
  43. data/test/dummy/db/seed.test/states.json +153 -0
  44. data/test/dummy/db/seed.test/states.txt +51 -0
  45. data/test/dummy/db/seed.test/states.yml +101 -0
  46. data/test/dummy/db/seed.test/zulu.err +2 -0
  47. data/test/dummy/db/test.sqlite3 +0 -0
  48. data/test/dummy/log/development.log +39 -0
  49. data/test/dummy/log/test.log +68768 -0
  50. data/test/models/data_seeder_test.rb +147 -0
  51. data/test/test_helper.rb +12 -0
  52. metadata +159 -0
@@ -0,0 +1,5 @@
1
+ module DataSeeder
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace DataSeeder
4
+ end
5
+ end
@@ -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,15 @@
1
+ require 'csv'
2
+
3
+ module DataSeeder
4
+ module Loader
5
+ class CSV
6
+ include Loader
7
+
8
+ def load(io)
9
+ ::CSV.foreach(io, headers: true) do |row|
10
+ save(row.to_hash)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module DataSeeder
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,3 @@
1
+ class App < ActiveRecord::Base
2
+ has_many :app_errors, inverse_of: :app
3
+ end
@@ -0,0 +1,3 @@
1
+ class AppError < ActiveRecord::Base
2
+ belongs_to :app, inverse_of: :app_errors
3
+ end
@@ -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
@@ -0,0 +1,14 @@
1
+ class Country < ActiveRecord::Base
2
+ def self.data_seeder_line(line)
3
+ {
4
+ code: line[0,2],
5
+ name: line[3...-1],
6
+ }
7
+ end
8
+
9
+ def self.data_seeder_config
10
+ {
11
+ key_attribute: :code
12
+ }
13
+ end
14
+ end
@@ -0,0 +1,2 @@
1
+ class State < ActiveRecord::Base
2
+ end
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3
+ load Gem.bin_path('bundler', 'bundle')
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
3
+ require_relative '../config/boot'
4
+ require 'rails/commands'
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../config/boot'
3
+ require 'rake'
4
+ Rake.application.run
@@ -0,0 +1,4 @@
1
+ # This file is used by Rack-based servers to start the application.
2
+
3
+ require ::File.expand_path('../config/environment', __FILE__)
4
+ run Rails.application
@@ -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,5 @@
1
+ # Set up gems listed in the Gemfile.
2
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__)
3
+
4
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
5
+ $LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__)
@@ -0,0 +1,12 @@
1
+ default: &default
2
+ adapter: sqlite3
3
+ pool: 5
4
+ timeout: 5000
5
+
6
+ development:
7
+ <<: *default
8
+ database: db/development.sqlite3
9
+
10
+ test:
11
+ <<: *default
12
+ database: db/test.sqlite3
@@ -0,0 +1,5 @@
1
+ # Load the Rails application.
2
+ require File.expand_path('../application', __FILE__)
3
+
4
+ # Initialize the Rails application.
5
+ Rails.application.initialize!
@@ -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