sqrbl 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/History.txt +4 -0
  2. data/LICENSE.txt +621 -0
  3. data/README.txt +85 -0
  4. data/Rakefile +32 -0
  5. data/TODO.txt +11 -0
  6. data/bin/sqrbl +8 -0
  7. data/lib/core_exts/object.rb +44 -0
  8. data/lib/core_exts/symbol.rb +16 -0
  9. data/lib/sqrbl/base_migration_writer.rb +52 -0
  10. data/lib/sqrbl/call_stack.rb +21 -0
  11. data/lib/sqrbl/group.rb +42 -0
  12. data/lib/sqrbl/individual_migration_writer.rb +48 -0
  13. data/lib/sqrbl/migration.rb +62 -0
  14. data/lib/sqrbl/mixins/expects_block_with_new.rb +27 -0
  15. data/lib/sqrbl/mixins/has_todos.rb +65 -0
  16. data/lib/sqrbl/mixins/method_missing_delegation.rb +83 -0
  17. data/lib/sqrbl/step.rb +192 -0
  18. data/lib/sqrbl/step_pair.rb +56 -0
  19. data/lib/sqrbl/unified_migration_writer.rb +34 -0
  20. data/lib/sqrbl.rb +83 -0
  21. data/spec/README.txt +4 -0
  22. data/spec/functional/base_migration_writer_spec.rb +12 -0
  23. data/spec/functional/individual_migration_writer_spec.rb +172 -0
  24. data/spec/functional/unified_migration_writer_spec.rb +97 -0
  25. data/spec/spec_helper.rb +31 -0
  26. data/spec/unit/group_spec.rb +85 -0
  27. data/spec/unit/migration_spec.rb +68 -0
  28. data/spec/unit/sqrbl_spec.rb +28 -0
  29. data/spec/unit/step_pair_spec.rb +110 -0
  30. data/spec/unit/step_spec.rb +154 -0
  31. data/tasks/ann.rake +80 -0
  32. data/tasks/bones.rake +20 -0
  33. data/tasks/gem.rake +201 -0
  34. data/tasks/git.rake +40 -0
  35. data/tasks/notes.rake +27 -0
  36. data/tasks/post_load.rake +34 -0
  37. data/tasks/rdoc.rake +51 -0
  38. data/tasks/rubyforge.rake +55 -0
  39. data/tasks/setup.rb +292 -0
  40. data/tasks/spec.rake +54 -0
  41. data/tasks/svn.rake +47 -0
  42. data/tasks/test.rake +40 -0
  43. data/tasks/zentest.rake +36 -0
  44. metadata +115 -0
@@ -0,0 +1,85 @@
1
+ require File.join(File.dirname(__FILE__), %w[.. spec_helper])
2
+
3
+ include Sqrbl
4
+
5
+ describe Group do
6
+ before(:each) do
7
+ @mig = mock('Migration')
8
+ end
9
+
10
+ it "should evaluate the block in its own context" do
11
+ group = Group.new(@mig, "foo", :skip_block_evaluation => true) do
12
+ hello_world()
13
+ end
14
+ group.should_receive(:hello_world)
15
+ group.send(:evaluate_block!)
16
+ end
17
+
18
+ it "should delegate method_missing calls back to the migration object" do
19
+ @mig.should_receive(:goodbye_world)
20
+ group = Group.new(@mig, "foo") do
21
+ goodbye_world()
22
+ end
23
+ end
24
+
25
+ it "methods defined in the group block should become available as methods on the Group instance" do
26
+ group = Group.new(@mig, "foo") do
27
+ def hello_world
28
+ "Hello, world!"
29
+ end
30
+ end
31
+ group.should respond_to(:hello_world)
32
+ group.hello_world.should == "Hello, world!"
33
+ end
34
+
35
+ describe "when #todo is called" do
36
+ it "should create a Todo object that saves the message" do
37
+ group = Group.new(@mig, 'foo') { todo 'bar' }
38
+ group.todos.map(&:message).should == ['bar']
39
+ end
40
+
41
+ it "should create a Todo object that saves the location of the #todo call" do
42
+ group = Group.new(@mig, 'foo') { todo 'bar' }; line_num = __LINE__
43
+ group.todos.first.location.should == [__FILE__, line_num].join(':')
44
+ end
45
+ end
46
+
47
+ describe "when #step is called" do
48
+ it "should create a StepPair object and pass it the group, the description, and the block arg" do
49
+ test_self = self
50
+ group = Group.new(@mig, "foo") do
51
+ StepPair.should_receive(:new).with(self, 'do something').and_yield # Look weird? See ./README.txt
52
+ step('do something') {}
53
+ end
54
+ end
55
+ end
56
+
57
+ describe :valid? do
58
+ it "should return false if steps is empty" do
59
+ group = Group.new(@mig, "foo") {}
60
+ group.should_not be_valid
61
+ end
62
+
63
+ it "should return false if steps is not empty, but any step is invalid" do
64
+ group = Group.new(@mig, "foo") { step("foo") {} }
65
+ group.steps.map(&:valid?).should include(false)
66
+ group.should_not be_valid
67
+ end
68
+
69
+ it "should return true if steps is not empty, and all steps are themselves valid" do
70
+ group = Group.new(@mig, "foo") do
71
+ step("foo") do
72
+ up {}
73
+ down {}
74
+ end
75
+ end
76
+ group.should be_valid
77
+ end
78
+ end
79
+
80
+ it "should have a unix_name property that consists of the regular name with characters sanitized" do
81
+ group = Group.new(@mig, "This be a test, matey! ARRR!!") {}
82
+ group.unix_name.should == 'this_be_a_test_matey_arrr'
83
+ end
84
+
85
+ end
@@ -0,0 +1,68 @@
1
+ require File.join(File.dirname(__FILE__), %w[.. spec_helper])
2
+
3
+ include Sqrbl
4
+
5
+ describe Migration do
6
+ describe "A new migration" do
7
+ it "should know the file that created it" do
8
+ mig = Migration.new
9
+ mig.creating_file.should == File.expand_path(__FILE__)
10
+ end
11
+ end
12
+
13
+ describe "A migration built with Migration.build" do
14
+ it "should still know the file that created it" do
15
+ mig = Migration.build {}
16
+ mig.creating_file.should == File.expand_path(__FILE__)
17
+ end
18
+
19
+ describe "when the block calls #group once" do
20
+ it "should have one group with the proper description" do
21
+ mig = Migration.build do
22
+ group "first group" do
23
+ 'hello world'
24
+ end
25
+ end
26
+ mig.groups.all? { |group| group.should be_kind_of(Group) }
27
+ mig.groups.map(&:description).should == ['first group']
28
+ end
29
+ end
30
+
31
+ describe "when the block calls #group twice" do
32
+ it "should have two group with the proper descriptions" do
33
+ mig = Migration.build do
34
+ group "first group" do
35
+ 'hello world'
36
+ end
37
+ group "second group" do
38
+ 'goodbye world'
39
+ end
40
+ end
41
+ mig.groups.all? { |group| group.should be_kind_of(Group) }
42
+ mig.groups.map(&:description).should == ['first group', 'second group']
43
+ end
44
+ end
45
+
46
+ it "should pass itself as a dependency to any group it builds" do
47
+ mig = Migration.build do
48
+ group("first group" ) { '' }
49
+ group("second group" ) { '' }
50
+ group("group the third") { '' }
51
+ end
52
+
53
+ mig.groups.all? { |group| group.migration.should == mig }
54
+ end
55
+ end
56
+
57
+ describe "when #todo is called" do
58
+ it "should create a Todo object that saves the message" do
59
+ mig = Migration.build { todo 'foo' }
60
+ mig.todos.map(&:message).should == ['foo']
61
+ end
62
+
63
+ it "should create a Todo object that saves the location of the #todo call" do
64
+ mig = Migration.build { todo 'foo' }; line_num = __LINE__
65
+ mig.todos.first.location.should == [__FILE__, line_num].join(':')
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,28 @@
1
+ require File.join(File.dirname(__FILE__), %w[.. spec_helper])
2
+
3
+ include Sqrbl
4
+
5
+ describe Sqrbl do
6
+ it ".build_migration should return an instance of Sqrbl::Migration" do
7
+ migration = Sqrbl.build_migration { 'hello, world!' }
8
+ migration.should be_kind_of(Sqrbl::Migration)
9
+ end
10
+
11
+ it ".migration should take the return value from Sqrbl::Migration.build and pass it to write_migration" do
12
+ mig = mock('Migration')
13
+ Migration.should_receive(:build).and_yield.and_return(mig)
14
+ Sqrbl.should_receive(:write_migration!).with(mig)
15
+ Sqrbl.migration { 'Hello, world!' }
16
+ end
17
+
18
+ describe :write_migration! do
19
+ it "should take a migration object and pass it off to any and all writers that exist" do
20
+ mig = mock('Migration')
21
+ BaseMigrationWriter.subclasses.each do |writer_class|
22
+ writer_class.should_receive(:write_migration!).with(mig)
23
+ end
24
+ Sqrbl.write_migration!(mig)
25
+ end
26
+ end
27
+
28
+ end
@@ -0,0 +1,110 @@
1
+ require File.join(File.dirname(__FILE__), %w[.. spec_helper])
2
+
3
+ include Sqrbl
4
+
5
+ describe StepPair do
6
+ before(:each) do
7
+ @group = mock('Group')
8
+ end
9
+
10
+ it "should evaluate the block in its own context" do
11
+ step_pair = StepPair.new(@group, "foo", :skip_block_evaluation => true) do
12
+ hello_world()
13
+ end
14
+ step_pair.should_receive(:hello_world)
15
+ step_pair.send(:evaluate_block!)
16
+ end
17
+
18
+ it "should delegate method_missing calls back to the group object" do
19
+ @group.should_receive(:goodbye_world)
20
+ step_pair = StepPair.new(@group, "foo") do
21
+ goodbye_world()
22
+ end
23
+ end
24
+
25
+ it "methods defined in the step_pair block should become available as methods on the StepPair instance" do
26
+ step_pair = StepPair.new(@group, "foo") do
27
+ def hello_world
28
+ "Hello, world!"
29
+ end
30
+ end
31
+ step_pair.should respond_to(:hello_world)
32
+ step_pair.hello_world.should == "Hello, world!"
33
+ end
34
+
35
+ describe "when #up is called" do
36
+ it "should create a Step object and pass it the StepPair and the block arg" do
37
+ step_pair = StepPair.new(@group, "foo") do
38
+ Step.should_receive(:new).with(self).and_yield # Look weird? See ./README.txt
39
+ up {}
40
+ end
41
+ end
42
+
43
+ it "should put the Step object in its up_step attribute" do
44
+ step_pair = StepPair.new(@group, "foo") do
45
+ up {}
46
+ end
47
+ step_pair.up_step.should be_kind_of(Step)
48
+ end
49
+ end
50
+
51
+ describe "when #down is called" do
52
+ it "should create a Step object and pass it the StepPair and the block arg" do
53
+ step_pair = StepPair.new(@group, "foo") do
54
+ Step.should_receive(:new).with(self).and_yield # Look weird? See ./README.txt
55
+ down {}
56
+ end
57
+ end
58
+
59
+ it "should put the Step object in its down_step attribute" do
60
+ step_pair = StepPair.new(@group, "foo") do
61
+ down {}
62
+ end
63
+ step_pair.down_step.should be_kind_of(Step)
64
+ end
65
+ end
66
+
67
+ describe :valid? do
68
+ it "should return false if up_step is nil" do
69
+ step_pair = StepPair.new(@group, "foo") do
70
+ end
71
+ step_pair.should_not be_valid
72
+ end
73
+ it "should return false if up_step is nil and down_step is not nil" do
74
+ step_pair = StepPair.new(@group, "foo") do
75
+ down {}
76
+ end
77
+ step_pair.should_not be_valid
78
+ end
79
+ it "should return false if up_step is not nil and down_step is nil" do
80
+ step_pair = StepPair.new(@group, "foo") do
81
+ up {}
82
+ end
83
+ step_pair.should_not be_valid
84
+ end
85
+ it "should return true if neither up_step and down_step is nil" do
86
+ step_pair = StepPair.new(@group, "foo") do
87
+ up {}
88
+ down {}
89
+ end
90
+ step_pair.should be_valid
91
+ end
92
+ end
93
+
94
+ describe "when #todo is called" do
95
+ it "should create a Todo object that saves the message" do
96
+ pair = StepPair.new(@group, 'foo') { todo 'bar' }
97
+ pair.todos.map(&:message).should == ['bar']
98
+ end
99
+
100
+ it "should create a Todo object that saves the location of the #todo call" do
101
+ pair = StepPair.new(@group, 'foo') { todo 'bar' }; line_num = __LINE__
102
+ pair.todos.first.location.should == [__FILE__, line_num].join(':')
103
+ end
104
+ end
105
+
106
+ it "should have a unix_name property that consists of the regular name with characters sanitized" do
107
+ pair = StepPair.new(@group, "This be a test, matey! ARRR!!") {}
108
+ pair.unix_name.should == 'this_be_a_test_matey_arrr'
109
+ end
110
+ end
@@ -0,0 +1,154 @@
1
+ require File.join(File.dirname(__FILE__), %w[.. spec_helper])
2
+
3
+ include Sqrbl
4
+
5
+ describe Step do
6
+ before(:each) do
7
+ @pair = mock('StepPair')
8
+ end
9
+
10
+ it "should evaluate the block in its own context" do
11
+ step = Step.new(@pair, :skip_block_evaluation => true) do
12
+ hello_world()
13
+ end
14
+ step.should_receive(:hello_world)
15
+ step.send(:evaluate_block!)
16
+ end
17
+
18
+ it "should delegate method_missing calls back to the step_pair object" do
19
+ @pair.should_receive(:goodbye_world)
20
+ step = Step.new(@pair) do
21
+ goodbye_world()
22
+ end
23
+ end
24
+
25
+ it "methods defined in the step's block should become available as methods on the Step instance" do
26
+ step = Step.new(@pair) do
27
+ def hello_world
28
+ "Hello, world!"
29
+ end
30
+ end
31
+ step.should respond_to(:hello_world)
32
+ step.hello_world.should == "Hello, world!"
33
+ end
34
+
35
+ describe "when #todo is called" do
36
+ it "should create a Todo object that saves the message" do
37
+ step = Step.new(@pair) { todo 'foo' }
38
+ step.todos.map(&:message).should == ['foo']
39
+ end
40
+
41
+ it "should create a Todo object that saves the location of the #todo call" do
42
+ step = Step.new(@pair) { todo 'foo' }; line_num = __LINE__
43
+ step.todos.first.location.should == [__FILE__, line_num].join(':')
44
+ end
45
+
46
+ it "should prepend its argument with '--> todo ([file], line [line #]):' and put it in #output" do
47
+ line_num = 0
48
+ step = Step.new(@pair) do
49
+ todo 'Fix a terrible issue'; line_num = __LINE__
50
+ end
51
+
52
+ step.output.should =~ /^--> todo \(.*\)\:/i
53
+ step.output.should include(__FILE__)
54
+ step.output.should =~ Regexp.new("line #{line_num}")
55
+ step.output.should include('Fix a terrible issue')
56
+ end
57
+ end
58
+
59
+ describe "when #comment is called" do
60
+ it "should prepend its argument with '-- ' and put it in #output" do
61
+ message = "Here's something you should probably be aware of."
62
+ step = Step.new(@pair) { comment(message) }
63
+ step.output.should =~ Regexp.new('^-- ' + message)
64
+ end
65
+ end
66
+
67
+ describe "when #block_comment is called" do
68
+ it "should surround its argument with /* and */ on newlines and put the whole thing in #output" do
69
+ message = "Here's something you should probably be aware of."
70
+ step = Step.new(@pair) { block_comment { message } }
71
+ step.output.should =~ /^\/\*/
72
+ step.output.should =~ Regexp.new('^ ' + message)
73
+ step.output.should =~ /^\*\//
74
+ end
75
+
76
+ describe "and given a string with extra spaces (as, e.g., a heredoc)" do
77
+ it "should unindent the argument before putting it in #output" do
78
+ step = Step.new(@pair) do
79
+ block_comment do
80
+ <<-EOF
81
+ Foo.
82
+ Bar.
83
+ Baz.
84
+ EOF
85
+ end
86
+ end
87
+ step.output.should =~ /^ Foo./
88
+ step.output.should =~ /^ Bar./
89
+ step.output.should =~ /^ Baz./
90
+ end
91
+ end
92
+ end
93
+
94
+ describe "when #warning is called" do
95
+ it "should create a Todo object with type=:warning that saves the message" do
96
+ step = Step.new(@pair) { warning 'Danger, Will Robinson!' }
97
+ step.todos.map(&:type).should == [:warning]
98
+ step.todos.map(&:message).should == ['Danger, Will Robinson!']
99
+ end
100
+
101
+ it "should write a giant ASCII warning to #output" do
102
+ line_num = 0
103
+ step = Step.new(@pair) do
104
+ warning 'Danger, Will Robinson!'; line_num = __LINE__
105
+ end
106
+ step.output.should include(Step::GiantWarningSeparator)
107
+ step.output.should include(Step::GiantWarningText)
108
+ step.output.should include('Danger, Will Robinson!')
109
+ step.output.should =~ Regexp.new("line #{line_num}")
110
+ end
111
+ end
112
+
113
+ describe "when #action is called with a string and a block" do
114
+ before(:each) do
115
+ @step = Step.new(@pair) do
116
+ action "Do something" do
117
+ <<-EOF
118
+ Hello world!
119
+ EOF
120
+ end
121
+ end
122
+ end
123
+
124
+ it "should treat the string as a comment" do
125
+ @step.output.should =~ /^-- Do something/
126
+ end
127
+
128
+ it "should unindent the return value from the block and put it in #output" do
129
+ @step.output.should =~ /^Hello world!/
130
+ end
131
+ end
132
+
133
+ describe "#insert_into" do
134
+ it "should produce an INSERT INTO fragment" do
135
+ @step = Step.new(@pair) {}
136
+ insert_fragment = @step.insert_into 'new_widgets', {
137
+ :name => 'widget_name',
138
+ :part_num => 'CONCAT("X_", part_number)',
139
+ :note => '"Imported from old_widgets"',
140
+ }
141
+ # puts '<pre>%s</pre>' % insert_fragment
142
+
143
+ insert_fragment.should include('new_widgets')
144
+
145
+ insert_fragment.should =~ /^\s*name/
146
+ insert_fragment.should =~ /^\s*part_num/
147
+ insert_fragment.should =~ /^\s*note/
148
+
149
+ insert_fragment.should include('widget_name AS name')
150
+ insert_fragment.should include('CONCAT("X_", part_number) AS part_num')
151
+ insert_fragment.should include('"Imported from old_widgets" AS note')
152
+ end
153
+ end
154
+ end
data/tasks/ann.rake ADDED
@@ -0,0 +1,80 @@
1
+
2
+ begin
3
+ require 'bones/smtp_tls'
4
+ rescue LoadError
5
+ require 'net/smtp'
6
+ end
7
+ require 'time'
8
+
9
+ namespace :ann do
10
+
11
+ # A prerequisites task that all other tasks depend upon
12
+ task :prereqs
13
+
14
+ file PROJ.ann.file do
15
+ ann = PROJ.ann
16
+ puts "Generating #{ann.file}"
17
+ File.open(ann.file,'w') do |fd|
18
+ fd.puts("#{PROJ.name} version #{PROJ.version}")
19
+ fd.puts(" by #{Array(PROJ.authors).first}") if PROJ.authors
20
+ fd.puts(" #{PROJ.url}") if PROJ.url.valid?
21
+ fd.puts(" (the \"#{PROJ.release_name}\" release)") if PROJ.release_name
22
+ fd.puts
23
+ fd.puts("== DESCRIPTION")
24
+ fd.puts
25
+ fd.puts(PROJ.description)
26
+ fd.puts
27
+ fd.puts(PROJ.changes.sub(%r/^.*$/, '== CHANGES'))
28
+ fd.puts
29
+ ann.paragraphs.each do |p|
30
+ fd.puts "== #{p.upcase}"
31
+ fd.puts
32
+ fd.puts paragraphs_of(PROJ.readme_file, p).join("\n\n")
33
+ fd.puts
34
+ end
35
+ fd.puts ann.text if ann.text
36
+ end
37
+ end
38
+
39
+ desc "Create an announcement file"
40
+ task :announcement => ['ann:prereqs', PROJ.ann.file]
41
+
42
+ desc "Send an email announcement"
43
+ task :email => ['ann:prereqs', PROJ.ann.file] do
44
+ ann = PROJ.ann
45
+ from = ann.email[:from] || Array(PROJ.authors).first || PROJ.email
46
+ to = Array(ann.email[:to])
47
+
48
+ ### build a mail header for RFC 822
49
+ rfc822msg = "From: #{from}\n"
50
+ rfc822msg << "To: #{to.join(',')}\n"
51
+ rfc822msg << "Subject: [ANN] #{PROJ.name} #{PROJ.version}"
52
+ rfc822msg << " (#{PROJ.release_name})" if PROJ.release_name
53
+ rfc822msg << "\n"
54
+ rfc822msg << "Date: #{Time.new.rfc822}\n"
55
+ rfc822msg << "Message-Id: "
56
+ rfc822msg << "<#{"%.8f" % Time.now.to_f}@#{ann.email[:domain]}>\n\n"
57
+ rfc822msg << File.read(ann.file)
58
+
59
+ params = [:server, :port, :domain, :acct, :passwd, :authtype].map do |key|
60
+ ann.email[key]
61
+ end
62
+
63
+ params[3] = PROJ.email if params[3].nil?
64
+
65
+ if params[4].nil?
66
+ STDOUT.write "Please enter your e-mail password (#{params[3]}): "
67
+ params[4] = STDIN.gets.chomp
68
+ end
69
+
70
+ ### send email
71
+ Net::SMTP.start(*params) {|smtp| smtp.sendmail(rfc822msg, from, to)}
72
+ end
73
+ end # namespace :ann
74
+
75
+ desc 'Alias to ann:announcement'
76
+ task :ann => 'ann:announcement'
77
+
78
+ CLOBBER << PROJ.ann.file
79
+
80
+ # EOF
data/tasks/bones.rake ADDED
@@ -0,0 +1,20 @@
1
+
2
+ if HAVE_BONES
3
+
4
+ namespace :bones do
5
+
6
+ desc 'Show the PROJ open struct'
7
+ task :debug do |t|
8
+ atr = if t.application.top_level_tasks.length == 2
9
+ t.application.top_level_tasks.pop
10
+ end
11
+
12
+ if atr then Bones::Debug.show_attr(PROJ, atr)
13
+ else Bones::Debug.show PROJ end
14
+ end
15
+
16
+ end # namespace :bones
17
+
18
+ end # HAVE_BONES
19
+
20
+ # EOF