tern 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/.gitignore +4 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.markdown +189 -0
  4. data/Rakefile +2 -0
  5. data/bin/tern +6 -0
  6. data/lib/tern.rb +207 -0
  7. data/lib/tern/app.rb +65 -0
  8. data/lib/tern/generators/change.sql +4 -0
  9. data/lib/tern/generators/new/alterations/.empty_directory +0 -0
  10. data/lib/tern/generators/new/alterations/001_create_people.sql.example +16 -0
  11. data/lib/tern/generators/new/config.yml.tt +15 -0
  12. data/lib/tern/generators/new/definitions/sequence.yml +18 -0
  13. data/lib/tern/generators/new/definitions/ultimate_answer.sql.example +7 -0
  14. data/spec/projects/alterations/alterations/001_create_people.sql +8 -0
  15. data/spec/projects/alterations/alterations/002_create_animals.sql +8 -0
  16. data/spec/projects/alterations/alterations/003_create_plants.sql +8 -0
  17. data/spec/projects/alterations/config.yml +15 -0
  18. data/spec/projects/alterations/definitions/sequence.yml +18 -0
  19. data/spec/projects/alterations/definitions/ultimate_answer.sql.example +7 -0
  20. data/spec/projects/definitions/alterations/001_create_people.sql.example +16 -0
  21. data/spec/projects/definitions/config.yml +15 -0
  22. data/spec/projects/definitions/definitions/sequence.yml +19 -0
  23. data/spec/projects/definitions/definitions/ultimate_answer.sql +7 -0
  24. data/spec/projects/dependencies_1/alterations/001_create_widgets.sql +9 -0
  25. data/spec/projects/dependencies_1/config.yml +15 -0
  26. data/spec/projects/dependencies_1/definitions/sequence.yml +19 -0
  27. data/spec/projects/dependencies_1/definitions/ultimate_answer.sql.example +7 -0
  28. data/spec/projects/dependencies_1/definitions/widgets_view.sql +5 -0
  29. data/spec/projects/dependencies_2/alterations/001_create_widgets.sql +9 -0
  30. data/spec/projects/dependencies_2/config.yml +15 -0
  31. data/spec/projects/dependencies_2/definitions/sequence.yml +18 -0
  32. data/spec/projects/dependencies_2/definitions/ultimate_answer.sql.example +7 -0
  33. data/spec/projects/duplicated_alteration/alterations/001_create_people.sql +8 -0
  34. data/spec/projects/duplicated_alteration/alterations/002_create_animals.sql +8 -0
  35. data/spec/projects/duplicated_alteration/alterations/002_create_plants.sql +8 -0
  36. data/spec/projects/duplicated_alteration/config.yml +15 -0
  37. data/spec/projects/duplicated_alteration/definitions/sequence.yml +18 -0
  38. data/spec/projects/duplicated_alteration/definitions/ultimate_answer.sql.example +7 -0
  39. data/spec/projects/irreversible_alterations/alterations/001_create_people.sql +8 -0
  40. data/spec/projects/irreversible_alterations/alterations/002_create_animals.sql +4 -0
  41. data/spec/projects/irreversible_alterations/alterations/003_create_plants.sql +8 -0
  42. data/spec/projects/irreversible_alterations/config.yml +15 -0
  43. data/spec/projects/irreversible_alterations/definitions/sequence.yml +18 -0
  44. data/spec/projects/irreversible_alterations/definitions/ultimate_answer.sql.example +7 -0
  45. data/spec/projects/missing_alteration/alterations/001_create_people.sql +8 -0
  46. data/spec/projects/missing_alteration/alterations/003_create_plants.sql +8 -0
  47. data/spec/projects/missing_alteration/config.yml +15 -0
  48. data/spec/projects/missing_alteration/definitions/sequence.yml +18 -0
  49. data/spec/projects/missing_alteration/definitions/ultimate_answer.sql.example +7 -0
  50. data/spec/projects/multiple_sequences/alterations/001_create_people.sql.example +16 -0
  51. data/spec/projects/multiple_sequences/config.yml +15 -0
  52. data/spec/projects/multiple_sequences/definitions/create_view_a.sql +5 -0
  53. data/spec/projects/multiple_sequences/definitions/create_view_b.sql +5 -0
  54. data/spec/projects/multiple_sequences/definitions/sequence.yml +21 -0
  55. data/spec/projects/multiple_sequences/definitions/ultimate_answer.sql.example +7 -0
  56. data/spec/projects/new/alterations/001_create_people.sql.example +16 -0
  57. data/spec/projects/new/config.yml +15 -0
  58. data/spec/projects/new/definitions/sequence.yml +18 -0
  59. data/spec/projects/new/definitions/ultimate_answer.sql.example +7 -0
  60. data/spec/projects/no_alterations/alterations/001_create_people.sql.example +16 -0
  61. data/spec/projects/no_alterations/config.yml +15 -0
  62. data/spec/projects/no_alterations/definitions/sequence.yml +18 -0
  63. data/spec/projects/no_alterations/definitions/ultimate_answer.sql.example +7 -0
  64. data/spec/tern_spec.rb +215 -0
  65. data/tern.gemspec +23 -0
  66. metadata +174 -0
@@ -0,0 +1,18 @@
1
+ # Put the relative path to your definition files in the sequence they should run.
2
+ #
3
+ # Most definitions should go in the default list. These will be dropped and
4
+ # recreated every migration.
5
+ #
6
+ # Definitions that should not automatically be dropped and created can be put in
7
+ # different lists. This is useful for definitions that make take prohibitively
8
+ # to drop and create every migration such as check constraints.
9
+ #
10
+ # Run alternative sequences by specifying sequences in order to migrate command.
11
+ # Example: rake migrate sequences=expensive,default
12
+ #
13
+ # default:
14
+ # - ultimate_answer.sql
15
+ # expensive:
16
+ # - super_slow_check_constraint.sql
17
+
18
+ default: []
@@ -0,0 +1,7 @@
1
+ CREATE FUNCTION ultimate_answer() RETURNS integer AS $$
2
+ SELECT 42;
3
+ $$ LANGUAGE SQL;
4
+
5
+ ---- CREATE above / DROP below ----
6
+
7
+ DROP FUNCTION ultimate_answer();
@@ -0,0 +1,8 @@
1
+ CREATE TABLE people(
2
+ id serial PRIMARY KEY,
3
+ name varchar NOT NULL
4
+ );
5
+
6
+ ---- CREATE above / DROP below ----
7
+
8
+ DROP TABLE people;
@@ -0,0 +1,8 @@
1
+ CREATE TABLE plants(
2
+ id serial PRIMARY KEY,
3
+ name varchar NOT NULL
4
+ );
5
+
6
+ ---- CREATE above / DROP below ----
7
+
8
+ DROP TABLE plants;
@@ -0,0 +1,15 @@
1
+ alterations:
2
+ table: tern_alterations
3
+ column: version
4
+ definitions:
5
+ table: tern_definitions
6
+ environments:
7
+ development:
8
+ adapter: postgres
9
+ database: tern_test_development
10
+ test:
11
+ adapter: postgres
12
+ database: tern_test_test
13
+ production:
14
+ adapter: postgres
15
+ database: tern_test_production
@@ -0,0 +1,18 @@
1
+ # Put the relative path to your definition files in the sequence they should run.
2
+ #
3
+ # Most definitions should go in the default list. These will be dropped and
4
+ # recreated every migration.
5
+ #
6
+ # Definitions that should not automatically be dropped and created can be put in
7
+ # different lists. This is useful for definitions that make take prohibitively
8
+ # to drop and create every migration such as check constraints.
9
+ #
10
+ # Run alternative sequences by specifying sequences in order to migrate command.
11
+ # Example: rake migrate sequences=expensive,default
12
+ #
13
+ # default:
14
+ # - ultimate_answer.sql
15
+ # expensive:
16
+ # - super_slow_check_constraint.sql
17
+
18
+ default: []
@@ -0,0 +1,7 @@
1
+ CREATE FUNCTION ultimate_answer() RETURNS integer AS $$
2
+ SELECT 42;
3
+ $$ LANGUAGE SQL;
4
+
5
+ ---- CREATE above / DROP below ----
6
+
7
+ DROP FUNCTION ultimate_answer();
@@ -0,0 +1,16 @@
1
+ Sequel.migration do
2
+ up do
3
+ run <<-END_SQL
4
+ CREATE TABLE people(
5
+ id serial PRIMARY KEY,
6
+ name varchar NOT NULL
7
+ );
8
+ END_SQL
9
+ end
10
+
11
+ down do
12
+ run <<-END_SQL
13
+ DROP TABLE people;
14
+ END_SQL
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ alterations:
2
+ table: tern_alterations
3
+ column: version
4
+ definitions:
5
+ table: tern_definitions
6
+ environments:
7
+ development:
8
+ adapter: postgres
9
+ database: tern_test_development
10
+ test:
11
+ adapter: postgres
12
+ database: tern_test_test
13
+ production:
14
+ adapter: postgres
15
+ database: tern_test_production
@@ -0,0 +1,5 @@
1
+ CREATE VIEW a AS SELECT 42;
2
+
3
+ ---- CREATE above / DROP below ----
4
+
5
+ DROP VIEW a;
@@ -0,0 +1,5 @@
1
+ CREATE VIEW b AS SELECT * FROM a;
2
+
3
+ ---- CREATE above / DROP below ----
4
+
5
+ DROP VIEW b;
@@ -0,0 +1,21 @@
1
+ # Put the relative path to your definition files in the sequence they should run.
2
+ #
3
+ # Most definitions should go in the default list. These will be dropped and
4
+ # recreated every migration.
5
+ #
6
+ # Definitions that should not automatically be dropped and created can be put in
7
+ # different lists. This is useful for definitions that make take prohibitively
8
+ # to drop and create every migration such as check constraints.
9
+ #
10
+ # Run alternative sequences by specifying sequences in order to migrate command.
11
+ # Example: rake migrate sequences=expensive,default
12
+ #
13
+ # default:
14
+ # - ultimate_answer.sql
15
+ # expensive:
16
+ # - super_slow_check_constraint.sql
17
+
18
+ default:
19
+ - create_view_b.sql
20
+ expensive:
21
+ - create_view_a.sql
@@ -0,0 +1,7 @@
1
+ CREATE FUNCTION ultimate_answer() RETURNS integer AS $$
2
+ SELECT 42;
3
+ $$ LANGUAGE SQL;
4
+
5
+ ---- CREATE above / DROP below ----
6
+
7
+ DROP FUNCTION ultimate_answer();
@@ -0,0 +1,16 @@
1
+ Sequel.migration do
2
+ up do
3
+ run <<-END_SQL
4
+ CREATE TABLE people(
5
+ id serial PRIMARY KEY,
6
+ name varchar NOT NULL
7
+ );
8
+ END_SQL
9
+ end
10
+
11
+ down do
12
+ run <<-END_SQL
13
+ DROP TABLE people;
14
+ END_SQL
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ alterations:
2
+ table: tern_alterations
3
+ column: version
4
+ definitions:
5
+ table: tern_definitions
6
+ environments:
7
+ development:
8
+ adapter: postgres
9
+ database: tern_test_development
10
+ test:
11
+ adapter: postgres
12
+ database: tern_test_test
13
+ production:
14
+ adapter: postgres
15
+ database: tern_test_production
@@ -0,0 +1,18 @@
1
+ # Put the relative path to your definition files in the sequence they should run.
2
+ #
3
+ # Most definitions should go in the default list. These will be dropped and
4
+ # recreated every migration.
5
+ #
6
+ # Definitions that should not automatically be dropped and created can be put in
7
+ # different lists. This is useful for definitions that make take prohibitively
8
+ # to drop and create every migration such as check constraints.
9
+ #
10
+ # Run alternative sequences by specifying sequences in order to migrate command.
11
+ # Example: rake migrate sequences=expensive,default
12
+ #
13
+ # default:
14
+ # - ultimate_answer.sql
15
+ # expensive:
16
+ # - super_slow_check_constraint.sql
17
+
18
+ default: []
@@ -0,0 +1,7 @@
1
+ CREATE FUNCTION ultimate_answer() RETURNS integer AS $$
2
+ SELECT 42;
3
+ $$ LANGUAGE SQL;
4
+
5
+ ---- CREATE above / DROP below ----
6
+
7
+ DROP FUNCTION ultimate_answer();
@@ -0,0 +1,16 @@
1
+ Sequel.migration do
2
+ up do
3
+ run <<-END_SQL
4
+ CREATE TABLE people(
5
+ id serial PRIMARY KEY,
6
+ name varchar NOT NULL
7
+ );
8
+ END_SQL
9
+ end
10
+
11
+ down do
12
+ run <<-END_SQL
13
+ DROP TABLE people;
14
+ END_SQL
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ alterations:
2
+ table: tern_alterations
3
+ column: version
4
+ definitions:
5
+ table: tern_definitions
6
+ environments:
7
+ development:
8
+ adapter: postgres
9
+ database: tern_test_development
10
+ test:
11
+ adapter: postgres
12
+ database: tern_test_test
13
+ production:
14
+ adapter: postgres
15
+ database: tern_test_production
@@ -0,0 +1,18 @@
1
+ # Put the relative path to your definition files in the sequence they should run.
2
+ #
3
+ # Most definitions should go in the default list. These will be dropped and
4
+ # recreated every migration.
5
+ #
6
+ # Definitions that should not automatically be dropped and created can be put in
7
+ # different lists. This is useful for definitions that make take prohibitively
8
+ # to drop and create every migration such as check constraints.
9
+ #
10
+ # Run alternative sequences by specifying sequences in order to migrate command.
11
+ # Example: rake migrate sequences=expensive,default
12
+ #
13
+ # default:
14
+ # - ultimate_answer.sql
15
+ # expensive:
16
+ # - super_slow_check_constraint.sql
17
+
18
+ default: []
@@ -0,0 +1,7 @@
1
+ CREATE FUNCTION ultimate_answer() RETURNS integer AS $$
2
+ SELECT 42;
3
+ $$ LANGUAGE SQL;
4
+
5
+ ---- CREATE above / DROP below ----
6
+
7
+ DROP FUNCTION ultimate_answer();
@@ -0,0 +1,215 @@
1
+ require 'rspec'
2
+ require 'fileutils'
3
+ require 'sequel'
4
+ require 'tern'
5
+
6
+ $tmpdir = 'spec/tmp/' + Time.now.strftime("%Y-%m-%d-%H-%M-%S")
7
+ FileUtils.mkdir_p $tmpdir
8
+
9
+ RSpec.configure do |config|
10
+ def tern(args="")
11
+ lib = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
12
+ bin = File.expand_path(File.join(File.dirname(__FILE__), '..', 'bin', 'tern'))
13
+ `ruby -I #{lib} #{bin} #{args}`
14
+ end
15
+
16
+ def tern_migrate(path, args="")
17
+ Dir.chdir(path) do
18
+ tern "migrate #{args}"
19
+ end
20
+ end
21
+
22
+ def tern_generate(path, args="")
23
+ Dir.chdir(path) do
24
+ tern "generate #{args}"
25
+ end
26
+ end
27
+
28
+ def tmpdir
29
+ $tmpdir
30
+ end
31
+ end
32
+
33
+ describe "Change" do
34
+ describe "parse" do
35
+ it "splits on SPLIT_MARKER" do
36
+ ::Change.parse("create #{::Change::SPLIT_MARKER} drop").should == ["create ", " drop"]
37
+ end
38
+
39
+ it "returns entire string as create if no SPLIT_MARKER not found" do
40
+ ::Change.parse("create").should == ["create", nil]
41
+ end
42
+ end
43
+ end
44
+
45
+ describe "tern" do
46
+ it "displays task list when called without arguments" do
47
+ tern.should match(/Tasks/)
48
+ end
49
+
50
+ describe "new" do
51
+ it "creates a new project at path" do
52
+ Dir.chdir(tmpdir) do
53
+ tern "new tern_spec"
54
+ File.exist?("tern_spec").should be_true
55
+ File.exist?("tern_spec/alterations").should be_true
56
+ File.exist?("tern_spec/definitions").should be_true
57
+ File.exist?("tern_spec/config.yml").should be_true
58
+ end
59
+ end
60
+ end
61
+
62
+ describe "generate" do
63
+ before(:each) do |example|
64
+ @project_path = File.expand_path(File.join(tmpdir, example.object_id.to_s))
65
+ tern "new #{@project_path}"
66
+ end
67
+
68
+ it "prints an error message if config.yml is not found" do
69
+ tern_generate("spec/tmp", "alteration create_people").should match(/This directory does not appear to be a Tern project. config.yml not found./)
70
+ end
71
+
72
+ describe "alteration" do
73
+ it "creates alteration file with next available number prefixing name" do
74
+ tern_generate(@project_path, "alteration create_widgets")
75
+ File.exist?(File.join(@project_path, "alterations", "001_create_widgets.sql")).should be_true
76
+ tern_generate(@project_path, "alteration create_sprockets")
77
+ File.exist?(File.join(@project_path, "alterations", "002_create_sprockets.sql")).should be_true
78
+ end
79
+ end
80
+
81
+ describe "definition" do
82
+ it "creates definition file" do
83
+ tern_generate(@project_path, "definition create_a_view")
84
+ File.exist?(File.join(@project_path, "definitions", "create_a_view.sql")).should be_true
85
+ end
86
+ end
87
+ end
88
+
89
+ describe "migrate" do
90
+ before(:each) do
91
+ `createdb tern_test_development`
92
+ @dev_db = Sequel.connect :adapter => :postgres, :database => 'tern_test_development'
93
+ end
94
+
95
+ after(:each) do
96
+ @dev_db.disconnect
97
+ `dropdb tern_test_development`
98
+ end
99
+
100
+ it "prints an error message if config.yml is not found" do
101
+ tern_migrate("spec/tmp").should match(/This directory does not appear to be a Tern project. config.yml not found./)
102
+ end
103
+
104
+ it "works without any alterations" do
105
+ tern_migrate("spec/projects/no_alterations").should be_empty
106
+ end
107
+
108
+ it "creates definitions table on first run" do
109
+ tern_migrate "spec/projects/new"
110
+ @dev_db.tables.should include(:tern_definitions)
111
+ end
112
+
113
+ it "creates alterations table on first run" do
114
+ tern_migrate "spec/projects/new"
115
+ @dev_db.tables.should include(:tern_alterations)
116
+ end
117
+
118
+ it "applies alterations" do
119
+ tern_migrate "spec/projects/alterations"
120
+ @dev_db.tables.should include(:people, :animals, :plants)
121
+ end
122
+
123
+ it "applies alterations to version n" do
124
+ tern_migrate "spec/projects/alterations", "-a 1"
125
+ @dev_db.tables.should include(:people)
126
+ tern_migrate "spec/projects/alterations", "-a 3"
127
+ @dev_db.tables.should include(:people, :animals, :plants)
128
+ end
129
+
130
+ it "reverts all alterations" do
131
+ tern_migrate "spec/projects/alterations"
132
+ tern_migrate "spec/projects/alterations", "-a 0"
133
+ @dev_db.tables.should_not include(:people, :animals, :plants)
134
+ end
135
+
136
+ it "reverts alterations to version n" do
137
+ tern_migrate "spec/projects/alterations"
138
+ tern_migrate "spec/projects/alterations", "-a 1"
139
+ @dev_db.tables.should include(:people)
140
+ @dev_db.tables.should_not include(:animals, :plants)
141
+ end
142
+
143
+ it "prints an error message when attempting to revert an irreversible alteration" do
144
+ tern_migrate "spec/projects/irreversible_alterations"
145
+ tern_migrate("spec/projects/irreversible_alterations", "-a 0").should match /Alteration 002 is irreversible/
146
+ end
147
+
148
+ it "prints an error message when missing alteration" do
149
+ tern_migrate("spec/projects/missing_alteration").should match /Alteration 002 is missing/
150
+ end
151
+
152
+ it "prints an error message when alteration version is duplicated" do
153
+ tern_migrate("spec/projects/duplicated_alteration").should match /Alteration 002 is duplicated/
154
+ end
155
+
156
+ it "uses environment parameter" do
157
+ `createdb tern_test_test`
158
+ test_db = Sequel.connect :adapter => :postgres, :database => 'tern_test_test'
159
+
160
+ tern_migrate "spec/projects/new", "-e test"
161
+ test_db.tables.should include(:tern_definitions)
162
+ @dev_db.tables.should_not include(:tern_definitions)
163
+
164
+ test_db.disconnect
165
+ `dropdb tern_test_test`
166
+ end
167
+
168
+ it "creates definitions" do
169
+ tern_migrate "spec/projects/definitions"
170
+ @dev_db.get{ultimate_answer{}}.should == 42
171
+ end
172
+
173
+ it "drops definitions" do
174
+ tern_migrate "spec/projects/definitions"
175
+ tern_migrate "spec/projects/new"
176
+ expect { @dev_db.get{ultimate_answer{}} }.to raise_error(Sequel::DatabaseError)
177
+ end
178
+
179
+ it "creates definitions after alterations" do
180
+ tern_migrate "spec/projects/dependencies_1"
181
+ @dev_db.tables.should include(:widgets)
182
+ expect { @dev_db[:widgets_view].all }.to_not raise_error(Sequel::DatabaseError)
183
+ end
184
+
185
+ it "drops existing definitions" do
186
+ tern_migrate "spec/projects/dependencies_1"
187
+ tern_migrate "spec/projects/dependencies_2"
188
+ expect { @dev_db[:widgets_view].all }.to raise_error(Sequel::DatabaseError)
189
+ end
190
+
191
+ context "multiple definition sequences" do
192
+ it "creates specified definition sequences in order" do
193
+ tern_migrate "spec/projects/multiple_sequences", "-d expensive default"
194
+ expect { @dev_db[:a].all }.to_not raise_error(Sequel::DatabaseError)
195
+ expect { @dev_db[:b].all }.to_not raise_error(Sequel::DatabaseError)
196
+ end
197
+
198
+ it "creates specified definition sequence" do
199
+ tern_migrate "spec/projects/multiple_sequences", "-d expensive"
200
+ expect { @dev_db[:a].all }.to_not raise_error(Sequel::DatabaseError)
201
+ end
202
+
203
+ it "ignores unspecified definition sequences" do
204
+ tern_migrate "spec/projects/multiple_sequences", "-d expensive"
205
+ expect { @dev_db[:b].all }.to raise_error(Sequel::DatabaseError)
206
+ end
207
+
208
+ it "removes specified definition sequence from database that does not exist in definitions" do
209
+ tern_migrate "spec/projects/multiple_sequences", "-d expensive"
210
+ tern_migrate "spec/projects/new", "-d expensive"
211
+ expect { @dev_db[:a].all }.to raise_error(Sequel::DatabaseError)
212
+ end
213
+ end
214
+ end
215
+ end