db_to_file 1.2.4

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d5be7f37b637cad13b6c873c22fd7513697f39c9
4
+ data.tar.gz: bb24640e221dfd4bb0bc126e312181279b11f91b
5
+ SHA512:
6
+ metadata.gz: ac169b0f083355209e9f2d4281fc5b9b57e3ab4d34dd50d8caafa1a3f5152937416e153ecf6ee635f8f51e2e9372eb099ec1d6fa2a031b2ac244b0725a08cb27
7
+ data.tar.gz: 8d56efd2cad35ed1a07f8457845274719c11353bd7608baaa0100fd79c0af337e252e0db44250a17b4da04aaf7a48394f8cbec5ffef1decc46379f55d0312ee1
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in db_to_file.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Ewout Quax
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.
@@ -0,0 +1,29 @@
1
+ # DbToFile
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'db_to_file'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install db_to_file
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it ( http://github.com/<my-github-username>/db_to_file/fork )
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'lib/db_to_file'
6
+ t.test_files = FileList[
7
+ 'test/lib/db_to_file/**/*_test.rb'
8
+ ]
9
+ t.verbose = true
10
+ end
11
+
@@ -0,0 +1,2 @@
1
+ adapter: 'sqlite3'
2
+ database: 'db/test.sqlite3'
Binary file
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'db_to_file/version'
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "db_to_file"
7
+ spec.version = DbToFile::VERSION
8
+ spec.authors = ["Ewout Quax"]
9
+ spec.email = ["ewout.quax@quicknet.nl"]
10
+ spec.summary = %q{Unload and upload database-tables to a file-system}
11
+ spec.description = %q{Unload and upload database-tables to a file-system}
12
+ spec.homepage = ""
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency 'activerecord', '> 3.2.0'
21
+ spec.add_dependency 'activesupport', '> 3.2.0'
22
+ spec.add_dependency 'git', '> 1.2.6'
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.5'
25
+ spec.add_development_dependency 'rake'
26
+ spec.add_development_dependency 'pry'
27
+ spec.add_development_dependency "minitest", "~> 4.7.3"
28
+ spec.add_development_dependency 'minitest-reporters', '>= 0.5.0'
29
+ spec.add_development_dependency "mocha"
30
+ spec.add_development_dependency 'sqlite3'
31
+ spec.add_development_dependency 'turn'
32
+ spec.add_development_dependency 'simplecov'
33
+ end
@@ -0,0 +1,21 @@
1
+ require 'active_record'
2
+ require 'active_support/inflector'
3
+ require 'git'
4
+ require 'db_to_file/config'
5
+ require 'db_to_file/version'
6
+ require 'db_to_file/version_controller'
7
+ require 'db_to_file/unloader'
8
+ require 'db_to_file/uploader'
9
+ require 'db_to_file/values_normalizer/object_to_hash'
10
+ require 'db_to_file/values_normalizer/value_into_object'
11
+ require 'db_to_file/system_executer'
12
+
13
+ module DbToFile
14
+ if defined?(Rails)
15
+ require 'db_to_file/railtie'
16
+ else
17
+ require 'yaml'
18
+ dbconfig = YAML::load(File.open('db/database.yml'))
19
+ ActiveRecord::Base.establish_connection(dbconfig)
20
+ end
21
+ end
@@ -0,0 +1,56 @@
1
+ require 'singleton'
2
+
3
+ module DbToFile
4
+ class Config
5
+ include Singleton
6
+
7
+ def self.instance
8
+ @@instance ||= new
9
+ end
10
+
11
+ def initialize
12
+ @data = load_config
13
+ end
14
+
15
+ def tables
16
+ data['tables'].keys
17
+ end
18
+
19
+ def field_extension(table_name, field_name)
20
+ begin
21
+ data['tables'][table_name]['field_extensions'][field_name]
22
+ rescue NoMethodError
23
+ nil
24
+ end
25
+ end
26
+
27
+ def ignore_columns(table_name)
28
+ begin
29
+ data['tables'][table_name]['ignore_columns']
30
+ rescue NoMethodError
31
+ nil
32
+ end
33
+ end
34
+
35
+ def directory_prefix(table_name)
36
+ begin
37
+ data['tables'][table_name]['directory_prefix']
38
+ rescue NoMethodError
39
+ nil
40
+ end
41
+ end
42
+
43
+ def data
44
+ @data
45
+ end
46
+
47
+ private
48
+ def load_config
49
+ YAML::load(File.read(config_file))
50
+ end
51
+
52
+ def config_file
53
+ 'config/db_to_file.yml'
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,12 @@
1
+ require 'db_to_file'
2
+ require 'rails'
3
+ module DbToFile
4
+ class Railtie < Rails::Railtie
5
+ railtie_name :db_to_file
6
+
7
+ rake_tasks do
8
+ load "tasks/unloader.rake"
9
+ load "tasks/uploader.rake"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ module DbToFile
2
+ class SystemExecuter
3
+ def initialize(command)
4
+ self.command = command
5
+ end
6
+
7
+ def command=(command)
8
+ @command = command
9
+ end
10
+
11
+ def execute
12
+ puts "Execute command: #{@command}"
13
+ `#{@command}`
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,151 @@
1
+ module DbToFile
2
+ class Unloader
3
+ def initialize
4
+ # Load config and build database connection, before stashing possible changes
5
+ @config ||= config
6
+ ActiveRecord::Base.connection.tables
7
+ end
8
+
9
+ def unload
10
+ prepare_code_version
11
+ unload_tables
12
+ update_code_version
13
+ restore_local_stash
14
+ end
15
+
16
+ private
17
+ def prepare_code_version
18
+ version_controller.prepare_code_version
19
+ end
20
+
21
+ def unload_tables
22
+ puts 'Start downloading tables'
23
+ tables.each do |table_name|
24
+ puts "Downloading table '#{table_name}'"
25
+ Table.new(table_name, self).unload
26
+ end
27
+ puts 'Done downloading tables'
28
+ end
29
+
30
+ def update_code_version
31
+ puts 'Start updating code version'
32
+ version_controller.update_code_version
33
+ puts 'Done updating code version'
34
+ end
35
+
36
+ def restore_local_stash
37
+ version_controller.restore_local_stash
38
+ end
39
+
40
+ def version_controller
41
+ @version_controller ||= VersionController.new
42
+ end
43
+
44
+ def tables
45
+ config.tables
46
+ end
47
+
48
+ def config
49
+ Config.instance
50
+ end
51
+
52
+ class Table
53
+ def initialize(table_name, unloader)
54
+ @table = table_name.singularize.classify.constantize
55
+ @unloader = unloader
56
+ end
57
+
58
+ def unload
59
+ @table.all.each do |row|
60
+ Record.new(row, self).fields_to_files
61
+ end
62
+ end
63
+
64
+ private
65
+ class Record
66
+ def initialize(row, table)
67
+ @row = row
68
+ @table = table
69
+ end
70
+
71
+ def fields_to_files
72
+ build_directory
73
+
74
+ normalized_hash = DbToFile::ValuesNormalizer::ObjectToHash.new(@row).normalize
75
+ normalized_hash.except(*ignore_columns).each_pair do |field_name, value|
76
+ Field.new(field_name, self).write_value(value)
77
+ end
78
+ end
79
+
80
+ private
81
+ def build_directory
82
+ FileUtils.mkdir_p(base_dir)
83
+ end
84
+
85
+ def ignore_columns
86
+ config.ignore_columns(table_name)
87
+ end
88
+
89
+ def base_dir
90
+ "db/db_to_file/#{table_name}/#{row_name}"
91
+ end
92
+
93
+ def row_name
94
+ [directory_prefix, @row.id.to_s].compact.reject(&:empty?).join('_')
95
+ end
96
+
97
+ def directory_prefix
98
+ if config_directory_prefix.present?
99
+ (@row.send(config_directory_prefix) || '').parameterize
100
+ end
101
+ end
102
+
103
+ def config_directory_prefix
104
+ config.directory_prefix(table_name)
105
+ end
106
+
107
+ def table_name
108
+ @row.class.table_name
109
+ end
110
+
111
+ def config
112
+ Config.instance
113
+ end
114
+
115
+ class Field
116
+ def initialize(field_name, record)
117
+ @field_name = field_name
118
+ @record = record
119
+ end
120
+
121
+ def write_value(value)
122
+ handle = File.open(full_file_path, 'w')
123
+ handle.write(value)
124
+ handle.close
125
+ end
126
+
127
+ private
128
+ def full_file_path
129
+ File.join(@record.send(:base_dir), file_with_extension)
130
+ end
131
+
132
+ def file_with_extension
133
+ if (extension = config_field_extension).present?
134
+ "#{@field_name}.#{extension}"
135
+ else
136
+ @field_name
137
+ end
138
+ end
139
+
140
+ def config_field_extension
141
+ config.field_extension(@record.send(:table_name), @field_name)
142
+ end
143
+
144
+ def config
145
+ Config.instance
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,128 @@
1
+ module DbToFile
2
+ class Uploader
3
+ def upload(commit_message)
4
+ if can_continue?
5
+ invoke_unloader
6
+ end
7
+ if can_continue?
8
+ write_objects_to_db
9
+ update_code_version(commit_message)
10
+ end
11
+ end
12
+
13
+ def force_upload
14
+ write_objects_to_db
15
+ end
16
+
17
+ def force_changed
18
+ write_changed_objects_to_db
19
+ end
20
+
21
+ private
22
+ def invoke_unloader
23
+ Unloader.new.unload
24
+ end
25
+
26
+ def can_continue?
27
+ !merge_conflicts_present?
28
+ end
29
+
30
+ def merge_conflicts_present?
31
+ version_controller.merge_conflicts_present?
32
+ end
33
+
34
+ def write_objects_to_db
35
+ objects.each(&:save!)
36
+ end
37
+
38
+ def write_changed_objects_to_db
39
+ changed_objects.each(&:save!)
40
+ end
41
+
42
+ def update_code_version(commit_message)
43
+ version_controller.update_code_version(commit_message)
44
+ end
45
+
46
+ def objects
47
+ @objects ||= build_objects
48
+ end
49
+
50
+ def changed_objects
51
+ @changed_objects ||= build_objects(true)
52
+ end
53
+
54
+ def build_objects(changed_only = false)
55
+ objects = []
56
+ files = changed_only ? (version_controller.get_modified_file_list & read_files) : read_files
57
+ files.each do |model_field_file|
58
+ data_segments = extract_data_segments(model_field_file)
59
+ model = data_segments[:model]
60
+ # find existing object
61
+ object = objects.detect do |existing_object|
62
+ existing_object.class == model && existing_object.id == data_segments[:id]
63
+ end
64
+ # build new object
65
+ unless object
66
+ object = model.find_by_id(data_segments[:id]) || model.new(id: data_segments[:id])
67
+ objects << object
68
+ end
69
+ # set field-value to model
70
+ update_object_with_field_value(object, data_segments[:field], model_field_file)
71
+ end
72
+
73
+ objects
74
+ end
75
+
76
+ def update_object_with_field_value(object, field, model_field_file)
77
+ value = file_value(model_field_file)
78
+ DbToFile::ValuesNormalizer::ValueIntoObject.new(object).normalize(field, value)
79
+ end
80
+
81
+ def file_value(model_field_file)
82
+ File.read(model_field_file)
83
+ end
84
+
85
+ def extract_data_segments(model_field_file)
86
+ matches = model_field_file.split('/').last(3)
87
+
88
+ {
89
+ model: matches[0].singularize.classify.constantize,
90
+ id: matches[1].split('_').last.to_i,
91
+ field: strip_extension(matches[2])
92
+ }
93
+ end
94
+
95
+ def read_files
96
+ files_in_dir(File.join('db', 'db_to_file'))
97
+ end
98
+
99
+ def files_in_dir(folder)
100
+ files = Dir.entries(folder)
101
+
102
+ found_files = []
103
+ files.each do |file|
104
+ full_file = File.join([folder, file])
105
+ if File.directory?(full_file) && file[0] != '.'
106
+ subdir = File.join(folder, file)
107
+ files_in_dir(subdir).each do |subdirfile|
108
+ found_files << subdirfile
109
+ end
110
+ end
111
+ if File.file?(full_file)
112
+ found_files << full_file
113
+ end
114
+ end
115
+
116
+ found_files
117
+ end
118
+
119
+ def version_controller
120
+ @version_controller ||= VersionController.new
121
+ end
122
+
123
+ private
124
+ def strip_extension(file_with_extension)
125
+ file_with_extension.split('.')[0]
126
+ end
127
+ end
128
+ end