sqrbl 0.1.0

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.
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