migr8 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 kuwata-lab.com
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,356 @@
1
+ Migr8.rb
2
+ ========
3
+
4
+ Migr8.rb is a database schema version management tool.
5
+
6
+ * Easy to install, easy to setup, and easy to start
7
+ * No configuration file; instead, only two environment variables
8
+ * Designed carefully to suit Git or Mercurial
9
+ * Supports SQLite3, PostgreSQL, and MySQL
10
+ * Written in Ruby (>= 1.8)
11
+
12
+
13
+ Quick Start
14
+ -----------
15
+
16
+ 1. Donwload migr8.rb.
17
+
18
+ $ curl -Lo migr8.rb http://bit.ly/migr8_rb
19
+ $ chmod a+x migr8.rb
20
+ ### or
21
+ $ gem install migr8
22
+
23
+ 2. Set environment variables: $MIGR8_COMMAND and $MIGR8_EDITOR.
24
+
25
+ $ export MIGR8_COMMAND="sqlite3 dbfile1" # for SQLite3
26
+ $ export MIGR8_COMMAND="psql -q -U user1 dbname1" # for PostgreSQL
27
+ $ export MIGR8_COMMAND="mysql -s -u user1 dbname1" # for MySQL
28
+
29
+ $ export MIGR8_EDITOR="open -a TextMate" # for TextMate (MacOSX)
30
+ $ export MIGR8_EDITOR="emacsclient" # for Emacs
31
+ $ export MIGR8_EDITOR="vim" # for Vim
32
+
33
+ 3. Create managiment files and table.
34
+
35
+ $ ./migr8.rb init # create files in current directory,
36
+ # and create a table in DB.
37
+
38
+ 4. Now you can manage DB schema versions.
39
+
40
+ $ ./migr8.rb # show current status
41
+ $ ./migr8.rb new -m "create 'users' table" # create a migration
42
+ # or ./migr8.rb new --table=users
43
+ $ ./migr8.rb # show status again
44
+ $ ./migr8.rb up # apply migration
45
+ $ ./migr8.rb # show status again
46
+ $ ./migr8.rb hist # list history
47
+
48
+ 5. You may got confliction error when `git rebase` or `git pull`.
49
+ In this case, you must resolve it by hand.
50
+ (This is intended design.)
51
+
52
+ $ git rebase master # confliction!
53
+ $ ./migr8.rb hist -o # open 'migr8/history.txt', and
54
+ # resolve confliction manually
55
+ $ ./migr8.rb hist # check whether history file is valid
56
+ $ git add migr8/history.txt
57
+ $ git rebase --continue
58
+
59
+
60
+ Templating
61
+ ----------
62
+
63
+ (!!Attention!! this is experimental feature and may be changed in the future.)
64
+
65
+ It is possible to embed eRuby code into `up` and `down` scripts.
66
+
67
+ Syntax:
68
+
69
+ * `<% ... %>` : Ruby statement
70
+ * `<%= ... %>` : Ruby expression, escaping `'` into `''` (or `\'` on MySQL)
71
+ * `<%== ... %>` : Ruby expression, no escaping
72
+
73
+ For example:
74
+
75
+ vars:
76
+ - table: users
77
+
78
+ up: |
79
+ insert into ${table}(name) values
80
+ <% comma = " " %>
81
+ <% for name in ["Haruhi", "Mikuru", "Yuki"] %>
82
+ <%= comma %>('<%= name %>')
83
+ <% comma = ", " %>
84
+ <% end %>
85
+ ;
86
+
87
+ down: |
88
+ <% for name in ["Haruhi", "Mikuru", "Yuki"] %>
89
+ delete from ${table} where name = '<%= name %>';
90
+ <% end %>
91
+
92
+ The above is the same as the following:
93
+
94
+ up: |
95
+ insert into users(name) values
96
+ ('Haruhi')
97
+ , ('Mikuru')
98
+ , ('Yuki')
99
+ ;
100
+
101
+ down: |
102
+ delete from users where name = 'Haruhi';
103
+ delete from users where name = 'Mikuru';
104
+ delete from users where name = 'Yuki';
105
+
106
+ In eRuby code, values in `vars` are available as instance variables.
107
+ For example:
108
+
109
+ version: uhtu4853
110
+ desc: register members
111
+ author: kyon
112
+ vars:
113
+ - table: users
114
+ - members: [Haruhi, Mikuru, Yuki]
115
+
116
+ up: |
117
+ <% for member in @members %>
118
+ insert into ${table}(name) values ('<%= member %>');
119
+ <% end %>
120
+
121
+ down: |
122
+ <% for member in @members %>
123
+ delete from ${table} where name = '<%= member %>';
124
+ <% end %>
125
+
126
+ If you want to see up and down scripts rendered, run `migr8.rb show` action.
127
+ For example:
128
+
129
+ $ ./migr8.rb show uhtu4853
130
+ version: uhtu4853
131
+ desc: register members
132
+ author: kyon
133
+ vars:
134
+ - table: "users"
135
+ - members: ["Haruhi", "Mikuru", "Yuki"]
136
+
137
+ up: |
138
+ insert into users(name) values ('Haruhi');
139
+ insert into users(name) values ('Mikuru');
140
+ insert into users(name) values ('Yuki');
141
+
142
+ down: |
143
+ delete from users where name = 'Haruhi';
144
+ delete from users where name = 'Mikuru';
145
+ delete from users where name = 'Yuki';
146
+
147
+
148
+ Notice that migration file using eRuby code is not compatible with other
149
+ Migr8 implemtation.
150
+
151
+
152
+ Tips
153
+ ----
154
+
155
+ * `migr8.rb up -a` applys all migrations, while `migr8.rb up` applys a
156
+ migration.
157
+
158
+ * `migr8.rb -D up` saves SQL executed into `migr8/history.txt` file.
159
+
160
+ * `migr8.rb redo` is equivarent to `migr8.rb down; migr8.rb up`.
161
+
162
+ * `migr8.rb new -p` generates migration file with plain skeleton, and
163
+ `migr8.rb new --table=name` generates with table name.
164
+
165
+ * `migr8.rb unapply -x` unapplies migration which is applied in DB but
166
+ corresponding migration file doesn't exist.
167
+ (Describing in detail, `migr8.rb unapply -x abcd1234` runs `down` script
168
+ in `_migr_history` table, while `migr8.rb unapply abcd1234` runs `down`
169
+ script in `migr8/migrations/abcd1234.yaml` file.)
170
+ This may help you when switching Git/Hg branch.
171
+
172
+ * `migr8.rb` generates sql file and run it with sql command such as `psql`
173
+ (PostgreSQL), `sqlite3` (SQLite3) or `mysql` (MySQL). Therefore you can
174
+ use non-sql command in migration file.
175
+ For example:
176
+
177
+ up: |
178
+ -- read data from CSV file and insert into DB (PostgreSQL)
179
+ \copy table1 from 'file1.csv' with csv;
180
+
181
+ * **MySQL doesn't support transactional DDL**.
182
+ It will cause troubles when you have errors in migration script
183
+ (See https://www.google.com/search?q=transactional+DDL for details).
184
+ On the other hand, SQLite3 and PostgreSQL support transactional DDL,
185
+ and DDL will be rollbacked when error occurred in migration script.
186
+ Very good.
187
+
188
+
189
+ <!--
190
+
191
+ Trouble Shooting
192
+ ----------------
193
+
194
+ * Command `migr8.rb unapply -x` unapplies migration which is applied in DB
195
+ but it's migration file doesn't exist in 'migr8/migrations' directory.
196
+ You may face this case when switching Git branch.
197
+
198
+ There can be migrations which are applied in DB but migration file does
199
+ not exit in 'migr8/migrations' directory. You my face this case when
200
+ switching Git/Hg branch.
201
+
202
+ For example, the following shows that migration 'uhtu4853' is applied
203
+ in DB, but file 'migr8/migrations/uhtu4853.yaml' does not exist.
204
+
205
+ $ ./migr8.rb
206
+ ## Status: all applied
207
+ ## Recent history:
208
+ ssgc3376 2013-11-18 10:04:40 # [kwatch] create 'groups' table
209
+ ## !!! The following migrations are applied to DB, but files are not found.
210
+ ## !!! (Try `migr8.rb unapply -x abcd1234` to unapply them.)
211
+ uhtu4853 2013-11-18 10:04:46 # [kwatch] create 'users' table
212
+
213
+ You may try to unapply 'uhtu4853', but will be refused because migration
214
+ file does not exist.
215
+
216
+ $ ./migr8.rb unapply uhtu4853
217
+ ERROR[migr8.rb] unapply: uhtu4853: no such version in history file.
218
+
219
+ In this case, `migr8.rb new -x` is the answer.
220
+
221
+ $ ./migr8.rb unapply -x uhtu4853
222
+ ## unapplying uhtu4853 # [kwatch] create 'users' table
223
+ $ ./migr8.rb
224
+ ## Status: all applied
225
+ ## Recent history:
226
+ ssgc3376 2013-11-20 10:04:46 # [kwatch] create 'groups' table
227
+
228
+ -->
229
+
230
+
231
+ Usage and Actions
232
+ -----------------
233
+
234
+ Usage: migr8.rb [global-options] [action [options] [...]]
235
+ -h, --help : show help
236
+ -v, --version : show version
237
+ -D, --debug : not remove sql file ('migr8/tmp.sql') for debug
238
+
239
+ Actions: (default: status)
240
+ readme : !!READ ME AT FIRST!!
241
+ help [action] : show help message of action, or list action names
242
+ init : create necessary files and a table
243
+ hist : list history of versions
244
+ -o : open history file with $MIGR8_EDITOR
245
+ -b : rebuild history file from migration files
246
+ new : create new migration file and open it by $MIGR8_EDITOR
247
+ -m text : description message (mandatory)
248
+ -u user : author name (default: current user)
249
+ -v version : specify version number instead of random string
250
+ -p : plain skeleton
251
+ -e editor : editr command (such as 'emacsclient', 'open', ...)
252
+ --table=table : skeleton to create table
253
+ --column=tbl.col : skeleton to add column
254
+ --index=tbl.col : skeleton to create index
255
+ --unique=tbl.col : skeleton to add unique constraint
256
+ show [version] : show migration file with expanding variables
257
+ -x : load values of migration from history table in DB
258
+ edit [version] : open migration file by $MIGR8_EDITOR
259
+ -r N : edit N-th file from latest version
260
+ -e editor : editr command (such as 'emacsclient', 'open', ...)
261
+ status : show status
262
+ up : apply next migration
263
+ -n N : apply N migrations
264
+ -a : apply all migrations
265
+ down : unapply current migration
266
+ -n N : unapply N migrations
267
+ --ALL : unapply all migrations
268
+ redo : do migration down, and up it again
269
+ -n N : redo N migrations
270
+ --ALL : redo all migrations
271
+ apply version ... : apply specified migrations
272
+ unapply version ... : unapply specified migrations
273
+ -x : unapply versions with down-script in DB, not in file
274
+ delete version ... : delete unapplied migration file
275
+ --Imsure : you must specify this option to delete migration
276
+
277
+
278
+ TODO
279
+ ----
280
+
281
+ * [_] write more tests
282
+ * [_] test on windows
283
+ * [_] implement in Python
284
+ * [_] implement in JavaScript
285
+
286
+
287
+ Changes
288
+ -------
289
+
290
+ ### Release 0.4.0 (2013-11-28) ###
291
+
292
+ * [enhance] RubyGems package available.
293
+ You can install migr8.rb by `gem install migr8`.
294
+ * [enhance] eRuby templating `up` and `down` script.
295
+ See 'Templating' section of README file for details.
296
+ * [enhance] Add new action 'show' which shows migration attributes
297
+ with expanding variables (ex: `${table}`) and renderting template.
298
+ * [enhance] Add new action 'delete' which deletes unapplied migration file.
299
+ Note: this action can't delete migration which is already applied.
300
+ * [enhance] Add new option 'new -v version' in order to specify version
301
+ number by yourself instead of auto-generated random string.
302
+ * [bufix] Action 'edit version' now can open migration file even when
303
+ version number in migration file is wrong.
304
+
305
+
306
+ ### Release 0.3.1 (2013-11-24) ###
307
+
308
+ * [bugfix] Fix 'hist' action not to raise error.
309
+
310
+
311
+ ### Release 0.3.0 (2013-11-22) ###
312
+
313
+ * [enhance] Add `-x` option to `unapply` action which unapplies migrations
314
+ by down-script in DB, not in migration file.
315
+ You can unapply migrations which files are missing in some reason.
316
+ * [change] Eliminate indentation from output of 'readme' action.
317
+
318
+
319
+ ### Release 0.2.1 (2013-11-20) ###
320
+
321
+ * [bugfix] Fix `new --table=name` action to set table name correctly
322
+
323
+
324
+ ### Release 0.2.0 (2013-11-14) ###
325
+
326
+ * [enhance] Add new options to `new` action for some skeletons
327
+ * `new --table=table` : create table
328
+ * `new --column=tbl.col` : add column to table
329
+ * `new --index=tbl.col` : create index on column
330
+ * `new --unique=tbl.col` : add unique constraint on column
331
+ * [enhance] Add new option `hist -b` action which re-generate history file.
332
+ * [change] Change several error messages
333
+ * [change] Tweak SQL generated on SQLite3
334
+
335
+
336
+ ### Release 0.1.1 (2013-11-12) ###
337
+
338
+ * [IMPORTANT] Change history table schema: SORRY, YOU MUST RE-CREATE HISTORY TABLE.
339
+ * [enhance] Fix 'up' action to save both up and down script into history table.
340
+
341
+
342
+ ### Release 0.1.0 (2013-11-11) ###
343
+
344
+ * Public release
345
+
346
+
347
+ License
348
+ -------
349
+
350
+ $License: MIT License $
351
+
352
+
353
+ Copyright
354
+ ---------
355
+
356
+ $Copyright: copyright(c) 2013 kuwata-lab.com all rights reserved $
@@ -0,0 +1,133 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ project = "migr8"
4
+ release = ENV['RELEASE'] || "0.4.0"
5
+ copyright = "copyright(c) 2013 kuwata-lab.com all rights reserved"
6
+ license = "MIT License"
7
+
8
+ require 'fileutils'
9
+ include FileUtils
10
+
11
+ require 'rake/clean'
12
+ CLEAN.include("build/#{project}-#{release}")
13
+ CLOBBER.include("build")
14
+
15
+
16
+ task :default => :test
17
+
18
+ def _do_test
19
+ Dir.glob('test/*_test.rb').each do |fname|
20
+ sh "ruby #{fname} -sp"
21
+ end
22
+ end
23
+
24
+ desc "do test"
25
+ task :test do
26
+ _do_test()
27
+ end
28
+
29
+ desc "do test for 1.8.6, 1.8.7, and 1.9.2"
30
+ task :test_all do
31
+ orig_path = ENV['PATH']
32
+ %w[1.8.6-p369 1.8.7-p334 1.9.2-p180].each do |ruby_ver|
33
+ ruby_path = "/usr/local/ruby/#{ruby_ver}/bin"
34
+ ENV['PATH'] = ruby_path + ':' + orig_path
35
+ puts "*** ruby_path=#{ruby_path}"
36
+ sh "ruby -v"
37
+ _do_test()
38
+ end
39
+ end
40
+
41
+
42
+ desc "embed 'README.md' into 'lib/migr8.rb'"
43
+ task :readme do
44
+ content = File.read('README.md')
45
+ content.gsub!(/^ /, '')
46
+ content.gsub!(/\n\n<!--.*\n-->\n/m, '')
47
+ File.open('lib/migr8.rb', 'r+') do |f|
48
+ text = f.read()
49
+ text.sub!(/('README_DOCUMENT'\n)(.*)(^README_DOCUMENT\n)/m) {
50
+ "'README_DOCUMENT'\n#{content}README_DOCUMENT\n"
51
+ }
52
+ f.rewind()
53
+ f.truncate(0)
54
+ f.write(text)
55
+ end
56
+ end
57
+
58
+
59
+ def edit_files(*filenames, &block)
60
+ filenames.flatten.each do |fname|
61
+ Dir.glob(fname).each do |fpath|
62
+ next unless File.file?(fpath)
63
+ s = File.open(fpath, 'rb') {|f| f.read() }
64
+ s = block.arity == 2 ? yield(s, fpath) : yield(s)
65
+ File.open(fpath, 'wb') {|f| f.write(s) }
66
+ end
67
+ end
68
+ end
69
+
70
+
71
+ desc "update '$Release$', '$License$', and '$Copyright$'"
72
+ task :edit do
73
+ files = ['README.md', 'MIT-LICENSE', 'lib/*.rb', 'bin/*.rb', 'test/*.rb', 'migr8.gemspec']
74
+ edit_files(files) do |s, filename|
75
+ next s if filename == 'test/oktest.rb'
76
+ next s if filename == 'Rakefile'
77
+ s.gsub!(/\$Release:.*\$/, "$Release: #{release} $")
78
+ s.gsub!(/\$License:.*\$/, "$License: #{license} $")
79
+ s.gsub!(/\$Copyright:.*\$/, "$Copyright: #{copyright} $")
80
+ s.gsub!(/\$Release\$/, release)
81
+ s.gsub!(/\$License\$/, license)
82
+ s.gsub!(/\$Copyright\$/, copyright)
83
+ s
84
+ end
85
+ end
86
+
87
+
88
+ desc "copy 'oktest.rb' into 'test/'"
89
+ task :oktest => "test/oktest.rb"
90
+
91
+
92
+ file "test/oktest.rb" => File.expand_path("~/src/oktest/ruby/lib/oktest.rb") do |t|
93
+ cp t.prerequisites, "test"
94
+ end
95
+
96
+ #task "availables" do |t|
97
+ # methods = t.methods.sort - Object.new.methods
98
+ # $stderr.puts "\033[0;31m*** debug: methods=#{methods.inspect}\033[0m"
99
+ #end
100
+
101
+
102
+ desc "generates rubygems package"
103
+ task :gem => :dist do
104
+ dir = "dist/#{project}-#{release}"
105
+ Dir.chdir dir do
106
+ sh "gem build migr8.gemspec"
107
+ end
108
+ mv Dir.glob("#{dir}/migr8-#{release}.gem"), "dist"
109
+ puts "**"
110
+ puts "** created: dist/migr8-#{release}.gem"
111
+ puts "**"
112
+ end
113
+
114
+
115
+ desc "create 'dist' directory and copy files to it"
116
+ task :dist do
117
+ dir = "dist/#{project}-#{release}"
118
+ files = %w[README.md MIT-LICENSE migr8.gemspec Rakefile setup.rb]
119
+ rm_rf dir
120
+ mkdir_p dir
121
+ mkdir_p "#{dir}/bin"
122
+ mkdir_p "#{dir}/lib"
123
+ mkdir_p "#{dir}/test"
124
+ cp files, dir
125
+ cp Dir.glob("lib/*"), "#{dir}/lib"
126
+ cp Dir.glob("bin/*"), "#{dir}/bin"
127
+ cp Dir.glob("test/*"), "#{dir}/test"
128
+ chmod 0644, *Dir.glob("#{dir}/lib/*")
129
+ chmod 0755, *Dir.glob("#{dir}/bin/*")
130
+ Dir.chdir dir do
131
+ sh "rake edit"
132
+ end
133
+ end