databasion 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.
- data/LICENSE +21 -0
- data/README.md +144 -0
- data/Rakefile +23 -0
- data/bin/databasion +6 -0
- data/config/example.google.yml +13 -0
- data/databasion.gemspec +78 -0
- data/features/databasion/googlize.feature +13 -0
- data/features/databasion/migitize.feature +8 -0
- data/features/databasion/step_definitions/googlize_steps.rb +32 -0
- data/features/databasion/step_definitions/migitize_steps.rb +26 -0
- data/features/databasion/step_definitions/yamalize_steps.rb +29 -0
- data/features/databasion/yamalize.feature +9 -0
- data/features/databasion.feature +23 -0
- data/features/step_definitions/databasion_steps.rb +35 -0
- data/lib/databasion/applcize.rb +86 -0
- data/lib/databasion/csvilize.rb +11 -0
- data/lib/databasion/excelize.rb +11 -0
- data/lib/databasion/googlize.rb +142 -0
- data/lib/databasion/migitize.rb +175 -0
- data/lib/databasion/yamalize.rb +71 -0
- data/lib/databasion.rb +103 -0
- data/lib/migration_helpers/MIT-LICENSE +20 -0
- data/lib/migration_helpers/README.markdown +92 -0
- data/lib/migration_helpers/init.rb +4 -0
- data/lib/migration_helpers/lib/migration_helper.rb +51 -0
- data/lib/tasks/databasion.rake +13 -0
- data/lib/tasks/test.rake +4 -0
- data/lib/trollop.rb +781 -0
- metadata +160 -0
@@ -0,0 +1,142 @@
|
|
1
|
+
require "google_spreadsheet"
|
2
|
+
|
3
|
+
module Databasion
|
4
|
+
|
5
|
+
class GooglizeError < StandardError; end
|
6
|
+
|
7
|
+
class Googlize
|
8
|
+
|
9
|
+
@@master_sheet = 'Database'
|
10
|
+
|
11
|
+
def self.config?
|
12
|
+
raise 'Googlize cannot load without a config.' unless defined?(@@config)
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.config=(data)
|
17
|
+
@@config = data
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.config
|
21
|
+
config?
|
22
|
+
@@config
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.session
|
26
|
+
@@session
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.master_sheet=(master)
|
30
|
+
@@master_sheet = master
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.master_sheet
|
34
|
+
@@master_sheet
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.login
|
38
|
+
begin
|
39
|
+
@@session = GoogleSpreadsheet.login(@@config['login']['username'], @@config['login']['password'])
|
40
|
+
rescue
|
41
|
+
raise GooglizeError, "Couldn't log into Google."
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.googlebate
|
46
|
+
config?
|
47
|
+
login
|
48
|
+
|
49
|
+
Databasion::LOGGER.info "Googlizing..."
|
50
|
+
process
|
51
|
+
Databasion::LOGGER.info "Googlized!"
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def self.process
|
56
|
+
@@config['sheets'].each do |token|
|
57
|
+
spreadsheet = @@session.spreadsheet_by_key(token['key'])
|
58
|
+
master_list = get_master(spreadsheet)
|
59
|
+
spreadsheet.worksheets.each do |worksheet|
|
60
|
+
next unless master_list.collect { |row| row['spreadsheet'] }.include?(worksheet.title)
|
61
|
+
data_hash = parse(worksheet)
|
62
|
+
data_hash['connection'] = master_list.collect { |row| row if row['spreadsheet'] == data_hash['name'] }.reject { |d| d.nil? }[0]
|
63
|
+
Databasion::Yamalize.yamlbate(data_hash, @@config['output']['yaml_path'])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.get_master(spreadsheet)
|
69
|
+
master_list = []
|
70
|
+
header_info = nil
|
71
|
+
spreadsheet.worksheets.each do |worksheet|
|
72
|
+
if worksheet.title == @@master_sheet
|
73
|
+
worksheet.rows.each_with_index do |row, index|
|
74
|
+
if index == 0
|
75
|
+
header_info = row
|
76
|
+
next
|
77
|
+
end
|
78
|
+
r = {}
|
79
|
+
header_info.each_with_index do |h, i|
|
80
|
+
r[h.strip] = row[i]
|
81
|
+
end
|
82
|
+
master_list.push r
|
83
|
+
end
|
84
|
+
break
|
85
|
+
end
|
86
|
+
end
|
87
|
+
raise GooglizeError, "There was no master sheet defined in the spreadsheet %s." % token['name'] if master_list.size == 0
|
88
|
+
master_list
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.parse(worksheet)
|
92
|
+
name = ''
|
93
|
+
plural = true
|
94
|
+
fields = []
|
95
|
+
types = []
|
96
|
+
data = []
|
97
|
+
|
98
|
+
ignore_cols = []
|
99
|
+
|
100
|
+
worksheet.rows.each_with_index do |row, index|
|
101
|
+
next if (row.reject { |s| s.strip.empty? }).size == 0
|
102
|
+
|
103
|
+
case row[0]
|
104
|
+
when "table"
|
105
|
+
if d = row[1].split(",")
|
106
|
+
name = d[0]
|
107
|
+
plural = false if d[1] == 'false'
|
108
|
+
else
|
109
|
+
name = row[1]
|
110
|
+
end
|
111
|
+
when "field"
|
112
|
+
row.each do |field|
|
113
|
+
fields.push field unless field.empty?
|
114
|
+
end
|
115
|
+
when "type"
|
116
|
+
row.each do |type|
|
117
|
+
types.push type unless type.empty?
|
118
|
+
end
|
119
|
+
when "ignore"
|
120
|
+
row.each_with_index do |ignore, i|
|
121
|
+
ignore_cols.push i-1 unless ignore.empty? or i == 0
|
122
|
+
end
|
123
|
+
else
|
124
|
+
if row[0].empty?
|
125
|
+
data.push row[1..row.size] unless (row.reject { |s| s.strip.empty? }).size == 0
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
{
|
131
|
+
'name' => name,
|
132
|
+
'plural' => plural,
|
133
|
+
'fields' => fields[1..fields.size],
|
134
|
+
'types' => types[1..types.size],
|
135
|
+
'data' => data,
|
136
|
+
'ignore_cols' => ignore_cols
|
137
|
+
}
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
|
3
|
+
module Databasion
|
4
|
+
|
5
|
+
class MigitizeError < StandardError; end
|
6
|
+
|
7
|
+
class Migitize
|
8
|
+
|
9
|
+
@@migration_start = 100
|
10
|
+
|
11
|
+
def self.migrabate(file_list=[], config=nil)
|
12
|
+
raise MigitizeError, 'Databasion::Migitize requires an array list of files. Try Yamalizing first.' if file_list.empty?
|
13
|
+
raise MigitizeError, 'Databasion::Migitize requires a parsed YAML config.' if config.nil?
|
14
|
+
@@config = config
|
15
|
+
|
16
|
+
Databasion::LOGGER.info "Migrabating..."
|
17
|
+
configure_start
|
18
|
+
parse(file_list)
|
19
|
+
Databasion::LOGGER.info "Migrabated!"
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def self.configure_start
|
24
|
+
files = Dir[@@config['output']['migrations']['path'] + "/**/*.rb"].collect { |file| file.split("/").pop }.sort
|
25
|
+
@@migration_start = files[files.size-1].split("_")[0].to_i if files.size > 0
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.parse(file_list)
|
29
|
+
database_configs = []
|
30
|
+
file_list.each do |file|
|
31
|
+
meta = YAML.load(File.open(file))['meta']
|
32
|
+
database_configs.push meta['connection']
|
33
|
+
process(meta)
|
34
|
+
end
|
35
|
+
write_database_yaml(database_configs)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.process(meta)
|
39
|
+
write_migration(migration_class(meta), meta['name'], meta['connection']['dbname'])
|
40
|
+
write_ruby(ruby_model(meta), meta['name'])
|
41
|
+
Databasion::LOGGER.info "Migrabated %s..." % meta['name']
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.migration_class(meta)
|
45
|
+
migration = "class %sMigration < ActiveRecord::Migration\n" % meta['name'].camelize
|
46
|
+
migration += migration_up(meta)
|
47
|
+
migration += migration_down(meta)
|
48
|
+
migration += "end\n"
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.migration_up(meta)
|
52
|
+
migration = " def self.up\n"
|
53
|
+
if meta['fields'].collect {|f| f['field']}.include?('id')
|
54
|
+
migration += " create_table :%s, :id => false do |t|\n" % set_table_name(meta)
|
55
|
+
else
|
56
|
+
migration += " create_table :%s do |t|\n" % set_table_name(meta)
|
57
|
+
end
|
58
|
+
migration += migration_up_fields(meta)
|
59
|
+
migration += " end\n"
|
60
|
+
migration += " end\n"
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.migration_up_fields(meta)
|
64
|
+
migration = ''
|
65
|
+
meta['fields'].each do |field|
|
66
|
+
if field['field'] == 'id'
|
67
|
+
migration += ' t.integer :id, :options => "PRIMARY KEY"' + "\n"
|
68
|
+
else
|
69
|
+
migration += " t.%s :%s" % [field['type'], field['field']]
|
70
|
+
migration += ", :limit => %s" % field['size'] if field['size']
|
71
|
+
migration += ", :default => %s" % field['default'] if field['default']
|
72
|
+
migration += "\n"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
migration
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.migration_down(meta)
|
79
|
+
migration = " def self.down\n"
|
80
|
+
migration += " drop_table :%s\n" % meta['name'].pluralize
|
81
|
+
migration += " end\n"
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.set_table_name(meta)
|
85
|
+
return meta['name'].pluralize if meta['plural']
|
86
|
+
meta['name'].pluralize
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.ruby_model(meta)
|
90
|
+
model = "class %s < ActiveRecord::Base\n" % ruby_model_name(meta)
|
91
|
+
model += ruby_model_table_name(meta)
|
92
|
+
model += "end\n"
|
93
|
+
model += ruby_model_connection(meta)
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.ruby_model_name(meta)
|
97
|
+
meta['plural'] ? meta['name'].camelize.pluralize : meta['name'].camelize
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.ruby_model_table_name(meta)
|
101
|
+
return "set_table_name %s\n" % meta['name'] unless meta['plural']
|
102
|
+
''
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.ruby_model_connection(meta)
|
106
|
+
model = "%s.establish_connection(\n" % ruby_model_name(meta)
|
107
|
+
count = 0
|
108
|
+
meta['connection'].each do |key, value|
|
109
|
+
count += 1
|
110
|
+
next if value.nil?
|
111
|
+
next if ['spreadsheet', 'options', 'dbname'].include?(key)
|
112
|
+
model += " :" + key + " => " + '"' + value + '"'
|
113
|
+
model += "," unless meta['connection'].size == count
|
114
|
+
model += "\n"
|
115
|
+
end
|
116
|
+
model += ")\n"
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.write_migration(migration, file_name, sub_path)
|
120
|
+
path = @@config['output']['migrations']['path'] + "/" + sub_path
|
121
|
+
check_output_path(path)
|
122
|
+
unless migration_exists?(file_name)
|
123
|
+
f = File.new("%s/%s_%s_migration.rb" % [path, @@migration_start, file_name], 'w')
|
124
|
+
f.write(migration)
|
125
|
+
f.close
|
126
|
+
@@migration_start += 1
|
127
|
+
else
|
128
|
+
f = File.open(find_migration_file(file_name), 'w')
|
129
|
+
f.write(migration)
|
130
|
+
f.close
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.write_ruby(model, file_name)
|
135
|
+
check_output_path(@@config['output']['migrations']['models'])
|
136
|
+
f = File.new("%s/%s.rb" % [@@config['output']['migrations']['models'], file_name], 'w')
|
137
|
+
f.write(model)
|
138
|
+
f.close
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.write_database_yaml(database_configs)
|
142
|
+
output = {}
|
143
|
+
database_configs.uniq.compact.collect do |config|
|
144
|
+
dbname = config['dbname']
|
145
|
+
config.delete('dbname')
|
146
|
+
output[dbname] = config
|
147
|
+
end
|
148
|
+
f = File.open("config/database.yml", 'w')
|
149
|
+
f.write(YAML.dump(output))
|
150
|
+
f.close
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.migration_exists?(file_name)
|
154
|
+
return true if find_migration_file(file_name)
|
155
|
+
false
|
156
|
+
end
|
157
|
+
|
158
|
+
def self.find_migration_file(file_name)
|
159
|
+
files = Dir[@@config['output']['migrations']['path'] + "/**/*.rb"]
|
160
|
+
files.each do |file|
|
161
|
+
chunks = file.split("/").pop.split(".")[0].split("_")
|
162
|
+
return file if chunks[1..chunks.size-2].join("_") == file_name
|
163
|
+
end
|
164
|
+
false
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.check_output_path(path)
|
168
|
+
unless File.exist?(path)
|
169
|
+
FileUtils.mkdir_p(path)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Databasion
|
2
|
+
|
3
|
+
class YamalizeError < StandardError; end
|
4
|
+
|
5
|
+
class Yamalize
|
6
|
+
|
7
|
+
def self.yamlbate(data_hash, output_path=nil)
|
8
|
+
raise YamalizeError, 'Databasion::Yamalize requires an output path.' if output_path.nil?
|
9
|
+
@@output_path = output_path
|
10
|
+
|
11
|
+
Databasion::LOGGER.info "Yamlbating %s..." % data_hash['name']
|
12
|
+
|
13
|
+
yaml_output = process(data_hash)
|
14
|
+
write(data_hash['name'], yaml_output)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def self.process(data_hash)
|
19
|
+
yaml_output = database_meta(data_hash)
|
20
|
+
yaml_output += database_rows(data_hash)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.database_meta(data_hash)
|
24
|
+
yaml_output = "meta: \n name: %s\n plural: %s\n fields: \n" % [data_hash['name'], data_hash['plural']]
|
25
|
+
data_hash['fields'].each_with_index do |field, index|
|
26
|
+
next if data_hash['ignore_cols'].include?(index)
|
27
|
+
type_data = data_hash['types'][index].split(',')
|
28
|
+
yaml_output += " - field: %s\n type: %s\n" % [field, type_data[0]]
|
29
|
+
yaml_output += " size: %s\n" % type_data[1] if type_data[1]
|
30
|
+
yaml_output += " default: %s\n" % type_data[2] if type_data[2]
|
31
|
+
end
|
32
|
+
yaml_output += " connection:\n"
|
33
|
+
data_hash['connection'].each do |key, value|
|
34
|
+
yaml_output += " %s: %s\n" % [key, value] unless ['spreadsheet', 'options'].include?(key)
|
35
|
+
end
|
36
|
+
yaml_output += "\n"
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.database_rows(data_hash)
|
40
|
+
yaml_output = "data: \n"
|
41
|
+
data_hash['data'].each do |row|
|
42
|
+
yaml_row = String.new
|
43
|
+
row.each_with_index do |col, index|
|
44
|
+
next if data_hash['ignore_cols'].include?(index)
|
45
|
+
if yaml_row.size == 0
|
46
|
+
yaml_row += " - %s: %s\n" % [data_hash['fields'][index], col]
|
47
|
+
else
|
48
|
+
yaml_row += " %s: %s\n" % [data_hash['fields'][index], col]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
yaml_output += "%s\n" % yaml_row
|
52
|
+
end
|
53
|
+
yaml_output += "\n"
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.write(file_name, yaml_output)
|
57
|
+
check_output_path
|
58
|
+
f = File.new("%s/%s.yml" % [@@output_path, file_name], 'w')
|
59
|
+
f.write(yaml_output)
|
60
|
+
f.close
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.check_output_path
|
64
|
+
unless File.exist?(@@output_path)
|
65
|
+
FileUtils.mkdir_p(@@output_path)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
data/lib/databasion.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'logger'
|
3
|
+
require 'active_record'
|
4
|
+
|
5
|
+
APP_PATH = File.dirname(File.expand_path(__FILE__))
|
6
|
+
$: << APP_PATH
|
7
|
+
Dir["#{APP_PATH}/**/lib"].each { |p| $: << p }
|
8
|
+
|
9
|
+
module Databasion
|
10
|
+
|
11
|
+
LOGGER = Logger.new $stderr
|
12
|
+
|
13
|
+
class DatabasionError < StandardError; end
|
14
|
+
|
15
|
+
@@config = nil
|
16
|
+
|
17
|
+
def self.databate(system, config=nil)
|
18
|
+
LOGGER.level = Logger::INFO
|
19
|
+
|
20
|
+
raise DatabasionError, 'Databasion requires a YAML config file path.' if config.nil?
|
21
|
+
@@config = YAML.load(File.open(config))
|
22
|
+
|
23
|
+
case system
|
24
|
+
when "google"
|
25
|
+
googlize
|
26
|
+
when "excel"
|
27
|
+
excelize
|
28
|
+
when "migrate"
|
29
|
+
migrate
|
30
|
+
when "update"
|
31
|
+
load_yaml
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.googlize
|
36
|
+
Databasion::Googlize.config = @@config
|
37
|
+
Databasion::Googlize.googlebate
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.excelize
|
41
|
+
Databasion::Excelize.excelbate
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.migrate
|
45
|
+
require 'migration_helpers/init'
|
46
|
+
|
47
|
+
files = Dir["%s/*.yml" % @@config['output']['yaml_path']]
|
48
|
+
Databasion::Migitize.migrabate(files, @@config)
|
49
|
+
|
50
|
+
Databasion::LOGGER.info "Migrating..."
|
51
|
+
|
52
|
+
set_ar_logger
|
53
|
+
|
54
|
+
YAML.load_file('config/database.yml').each do |config|
|
55
|
+
ActiveRecord::Base.establish_connection(config[1])
|
56
|
+
path = @@config['output']['migrations']['path'] + "/" + config[0]
|
57
|
+
ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
|
58
|
+
ActiveRecord::Migrator.migrate(path, ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.load_yaml
|
63
|
+
Databasion::LOGGER.info "Updating from YAML..."
|
64
|
+
|
65
|
+
set_ar_logger
|
66
|
+
|
67
|
+
models = Dir[@@config['output']['migrations']['models'] + "/*.rb"].each { |file| load file }
|
68
|
+
|
69
|
+
models.each do |model|
|
70
|
+
f = model.split('/')
|
71
|
+
plural_name = f[f.size-1].split(".")[0].pluralize
|
72
|
+
camel_name = f[f.size-1].split(".")[0].camelize
|
73
|
+
|
74
|
+
Databasion::LOGGER.info "Loading %s into database..." % camel_name
|
75
|
+
|
76
|
+
yaml_file = YAML.load_file('%s/%s.yml' % [@@config['output']['yaml_path'], plural_name])
|
77
|
+
|
78
|
+
for row in yaml_file['data']
|
79
|
+
klass = eval("%s.new" % camel_name)
|
80
|
+
model = camel_name.constantize.find(:first, :conditions => ['id = ?', row['id']])
|
81
|
+
if model
|
82
|
+
camel_name.constantize.update(model.id, row)
|
83
|
+
else
|
84
|
+
klass.id = row['id']
|
85
|
+
klass.update_attributes(row)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
def self.set_ar_logger
|
93
|
+
ActiveRecord::Base.logger = Databasion::LOGGER
|
94
|
+
end
|
95
|
+
|
96
|
+
autoload :Applcize, APP_PATH + '/databasion/applcize.rb'
|
97
|
+
autoload :Googlize, APP_PATH + '/databasion/googlize.rb'
|
98
|
+
autoload :Yamalize, APP_PATH + '/databasion/yamalize.rb'
|
99
|
+
autoload :Excelize, APP_PATH + '/databasion/excelize.rb'
|
100
|
+
autoload :Csvilize, APP_PATH + '/databasion/csvilize.rb'
|
101
|
+
autoload :Migitize, APP_PATH + '/databasion/migitize.rb'
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Jesús García Sáez
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,92 @@
|
|
1
|
+
|
2
|
+
DESCRIPTION
|
3
|
+
===========
|
4
|
+
|
5
|
+
Helpers for migrations of ActiveRecord for dealing with foreign keys and primary keys.
|
6
|
+
|
7
|
+
FEATURES
|
8
|
+
========
|
9
|
+
|
10
|
+
* **foreign keys**
|
11
|
+
* foreign_key(table, field, referenced_table, referenced_field, on_cascade)
|
12
|
+
* drop_foreign_key(table, field)
|
13
|
+
* **primary keys**
|
14
|
+
* primary_key(table, field)
|
15
|
+
|
16
|
+
Examples
|
17
|
+
========
|
18
|
+
|
19
|
+
Typical use:
|
20
|
+
|
21
|
+
def self.up
|
22
|
+
create_table :profiles do |t|
|
23
|
+
t.string :first_name
|
24
|
+
t.string :last_name
|
25
|
+
t.string :email
|
26
|
+
t.boolean :is_disabled
|
27
|
+
end
|
28
|
+
create_table :users do |t|
|
29
|
+
t.string :login
|
30
|
+
t.string :crypted_password
|
31
|
+
t.string :salt
|
32
|
+
t.integer :profile_id
|
33
|
+
end
|
34
|
+
|
35
|
+
foreign_key :users, :profile_id, :profiles
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.down
|
39
|
+
drop_foreign_key :users, :profile_id
|
40
|
+
drop_table :users
|
41
|
+
drop_table :profiles
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
Also, if we don't defined a common :id (exactly it's rails who define it), we should create a primary key:
|
46
|
+
|
47
|
+
def self.up
|
48
|
+
create_table :foo, :id => false do |t|
|
49
|
+
t.string :foo, :bar
|
50
|
+
end
|
51
|
+
|
52
|
+
primary_key :foo, [ :foo, :bar ]
|
53
|
+
end
|
54
|
+
|
55
|
+
In the parameter where a field is required (like the second parameter in *primary_key*) you can specified and symbol (or string) or an array of symbols (or strings).
|
56
|
+
|
57
|
+
|
58
|
+
REQUIREMENTS
|
59
|
+
============
|
60
|
+
|
61
|
+
* It's been tested with Mysql adapter and Jdbcmysql adapter
|
62
|
+
|
63
|
+
INSTALL
|
64
|
+
=======
|
65
|
+
|
66
|
+
* script/plugin install git://github.com/blaxter/migration_helpers.git
|
67
|
+
|
68
|
+
LICENSE
|
69
|
+
=======
|
70
|
+
|
71
|
+
(The MIT License)
|
72
|
+
|
73
|
+
Copyright (c) 2008 Jesús García Sáez <jgarcia@warp.es>
|
74
|
+
|
75
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
76
|
+
a copy of this software and associated documentation files (the
|
77
|
+
'Software'), to deal in the Software without restriction, including
|
78
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
79
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
80
|
+
permit persons to whom the Software is furnished to do so, subject to
|
81
|
+
the following conditions:
|
82
|
+
|
83
|
+
The above copyright notice and this permission notice shall be
|
84
|
+
included in all copies or substantial portions of the Software.
|
85
|
+
|
86
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
87
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
88
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
89
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
90
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
91
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
92
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module MigrationConstraintHelpers
|
2
|
+
|
3
|
+
# Creates a foreign key from +table+.+field+ against referenced_table.referenced_field
|
4
|
+
#
|
5
|
+
# table: The tablename
|
6
|
+
# field: A field of the table
|
7
|
+
# referenced_table: The table which contains the field referenced
|
8
|
+
# referenced_field: The field (which should be part of the primary key) of the referenced table
|
9
|
+
# cascade: delete & update on cascade?
|
10
|
+
def foreign_key(table, field, referenced_table, referenced_field = :id, cascade = true)
|
11
|
+
execute "ALTER TABLE #{table} ADD CONSTRAINT #{constraint_name(table, field)}
|
12
|
+
FOREIGN KEY #{constraint_name(table, field)} (#{field_list(field)})
|
13
|
+
REFERENCES #{referenced_table}(#{field_list(referenced_field)})
|
14
|
+
#{(cascade ? 'ON DELETE CASCADE ON UPDATE CASCADE' : '')}"
|
15
|
+
end
|
16
|
+
|
17
|
+
# Drops a foreign key from +table+.+field+ that has been created before with
|
18
|
+
# foreign_key method
|
19
|
+
#
|
20
|
+
# table: The table name
|
21
|
+
# field: A field (or array of fields) of the table
|
22
|
+
def drop_foreign_key(table, field)
|
23
|
+
execute "ALTER TABLE #{table} DROP FOREIGN KEY #{constraint_name(table, field)}"
|
24
|
+
end
|
25
|
+
|
26
|
+
# Creates a primary key for +table+, which right now HAS NOT primary key defined
|
27
|
+
#
|
28
|
+
# table: The table name
|
29
|
+
# field: A field (or array of fields) of the table that will be part of the primary key
|
30
|
+
def primary_key(table, field)
|
31
|
+
execute "ALTER TABLE #{table} ADD PRIMARY KEY(#{field_list(field)})"
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# Creates a constraint name for table and field given as parameters
|
37
|
+
#
|
38
|
+
# table: The table name
|
39
|
+
# field: A field of the table
|
40
|
+
def constraint_name(table, field)
|
41
|
+
"fk_#{table}_#{field_list_name(field)}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def field_list(fields)
|
45
|
+
fields.is_a?(Array) ? fields.join(',') : fields
|
46
|
+
end
|
47
|
+
|
48
|
+
def field_list_name(fields)
|
49
|
+
fields.is_a?(Array) ? fields.join('_') : fields
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'lib/databasion'
|
2
|
+
|
3
|
+
namespace :databasion do
|
4
|
+
desc "Run Databasion for Google Spreadsheets"
|
5
|
+
task :google do
|
6
|
+
Databasion.databate('google', 'config/google.yml')
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "Run Ruby Migration scripts"
|
10
|
+
task :migrate do
|
11
|
+
Databasion.databate('migrate', 'config/google.yml')
|
12
|
+
end
|
13
|
+
end
|
data/lib/tasks/test.rake
ADDED