fudge 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/bin/fudge +9 -0
  2. data/lib/fudge.rb +16 -0
  3. data/lib/fudge/build.rb +31 -0
  4. data/lib/fudge/cli.rb +29 -0
  5. data/lib/fudge/description.rb +53 -0
  6. data/lib/fudge/exceptions.rb +31 -0
  7. data/lib/fudge/helpers.rb +5 -0
  8. data/lib/fudge/helpers/bundle_aware.rb +14 -0
  9. data/lib/fudge/parser.rb +12 -0
  10. data/lib/fudge/railtie.rb +17 -0
  11. data/lib/fudge/runner.rb +25 -0
  12. data/lib/fudge/task_dsl.rb +41 -0
  13. data/lib/fudge/tasks.rb +31 -0
  14. data/lib/fudge/tasks/clean_bundler_env.rb +24 -0
  15. data/lib/fudge/tasks/composite_task.rb +24 -0
  16. data/lib/fudge/tasks/each_directory.rb +34 -0
  17. data/lib/fudge/tasks/in_directory.rb +24 -0
  18. data/lib/fudge/tasks/rake.rb +19 -0
  19. data/lib/fudge/tasks/rspec.rb +29 -0
  20. data/lib/fudge/tasks/shell.rb +76 -0
  21. data/lib/fudge/tasks/task.rb +18 -0
  22. data/lib/fudge/tasks/yard.rb +29 -0
  23. data/lib/fudge/version.rb +3 -0
  24. data/lib/tasks/fudge.rake +7 -0
  25. data/spec/lib/fudge/build_spec.rb +5 -0
  26. data/spec/lib/fudge/cli_spec.rb +76 -0
  27. data/spec/lib/fudge/description_spec.rb +241 -0
  28. data/spec/lib/fudge/exceptions_spec.rb +36 -0
  29. data/spec/lib/fudge/parser_spec.rb +23 -0
  30. data/spec/lib/fudge/runner_spec.rb +25 -0
  31. data/spec/lib/fudge/tasks/bundler_spec.rb +39 -0
  32. data/spec/lib/fudge/tasks/composite_task_spec.rb +60 -0
  33. data/spec/lib/fudge/tasks/each_directory_spec.rb +57 -0
  34. data/spec/lib/fudge/tasks/in_directory_spec.rb +39 -0
  35. data/spec/lib/fudge/tasks/rake_spec.rb +18 -0
  36. data/spec/lib/fudge/tasks/rspec_spec.rb +36 -0
  37. data/spec/lib/fudge/tasks/shell_spec.rb +38 -0
  38. data/spec/lib/fudge/tasks/task_spec.rb +14 -0
  39. data/spec/lib/fudge/tasks/yard_spec.rb +25 -0
  40. data/spec/lib/fudge/tasks_spec.rb +33 -0
  41. data/spec/spec_helper.rb +22 -0
  42. data/spec/support/dummy_task.rb +40 -0
  43. data/spec/support/matchers.rb +47 -0
  44. data/spec/support/tmpdir.rb +12 -0
  45. metadata +302 -0
@@ -0,0 +1,19 @@
1
+ module Fudge
2
+ module Tasks
3
+ class Rake < Shell
4
+ include Helpers::BundleAware
5
+
6
+ def self.name
7
+ :rake
8
+ end
9
+
10
+ private
11
+
12
+ def cmd(options={})
13
+ bundle_cmd("rake #{arguments}", options)
14
+ end
15
+ end
16
+
17
+ register Rake
18
+ end
19
+ end
@@ -0,0 +1,29 @@
1
+ module Fudge
2
+ module Tasks
3
+ class Rspec < Shell
4
+ include Helpers::BundleAware
5
+
6
+ attr_accessor :color, :coverage
7
+
8
+ def self.name
9
+ :rspec
10
+ end
11
+
12
+ private
13
+ def cmd(options={})
14
+ self.arguments = 'spec/' if arguments.blank?
15
+ bundle_cmd("rspec#{' --tty' unless color == false} #{arguments}", options)
16
+ end
17
+
18
+ def check_for
19
+ if coverage
20
+ [/(\d+\.\d+)%\) covered/, lambda { |matches| matches[1].to_f >= coverage ? true : 'Insufficient Coverage.' }]
21
+ else
22
+ super
23
+ end
24
+ end
25
+ end
26
+
27
+ register Rspec
28
+ end
29
+ end
@@ -0,0 +1,76 @@
1
+ module Fudge
2
+ module Tasks
3
+ class Shell < Task
4
+ attr_accessor :arguments, :check_for
5
+
6
+ def self.name
7
+ :shell
8
+ end
9
+
10
+ def initialize(*args)
11
+ self.arguments = super.join(' ')
12
+ end
13
+
14
+ def run(options={})
15
+ @output, success = run_command(cmd(options))
16
+ return false unless success
17
+ return check_for_output
18
+ end
19
+
20
+ private
21
+
22
+ def run_command(cmd)
23
+ output = ''
24
+ IO.popen(cmd) do |f|
25
+ until f.eof?
26
+ bit = f.getc
27
+ output << bit
28
+ putc bit
29
+ end
30
+ end
31
+
32
+ [output, $?.success?]
33
+ end
34
+
35
+ def check_for_output
36
+ return true unless check_for # We're ok if no output defined
37
+
38
+ # Check if we have a callable to parse the regex matches
39
+ if check_for.is_a? Enumerable
40
+ regex, pass_if = check_for[0], check_for[1]
41
+ else
42
+ regex, pass_if = check_for, nil
43
+ end
44
+
45
+ # Do regex match and fail if no match
46
+ match = @output.match(regex)
47
+ unless match
48
+ puts "Output didn't match #{regex}."
49
+ return false
50
+ end
51
+
52
+ # If we've got a callable, call it to check on regex matches
53
+ if pass_if
54
+ passed = pass_if.call(match)
55
+ unless passed == true
56
+ if passed.is_a? String
57
+ puts passed
58
+ else
59
+ puts "Output matched #{check_for} but condition failed."
60
+ end
61
+ return false
62
+ end
63
+ end
64
+
65
+ true
66
+ end
67
+
68
+ # Defines the command to run
69
+ def cmd(options={})
70
+ arguments
71
+ end
72
+ end
73
+
74
+ register Shell
75
+ end
76
+ end
@@ -0,0 +1,18 @@
1
+ module Fudge
2
+ module Tasks
3
+ class Task
4
+ attr_reader :args
5
+
6
+ def initialize(*args)
7
+ @args = args.dup
8
+ options = args.extract_options!
9
+
10
+ options.each do |k,v|
11
+ send("#{k}=", v) if respond_to?("#{k}=")
12
+ end
13
+
14
+ args
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,29 @@
1
+ module Fudge
2
+ module Tasks
3
+ class Yard < Shell
4
+ include Helpers::BundleAware
5
+
6
+ attr_accessor :coverage
7
+
8
+ def self.name
9
+ :yard
10
+ end
11
+
12
+ private
13
+
14
+ def cmd(options={})
15
+ bundle_cmd("yard #{arguments}", options)
16
+ end
17
+
18
+ def check_for
19
+ if coverage
20
+ [/(\d+\.\d+)% documented/, lambda { |matches| matches[1].to_f >= coverage ? true : 'Insufficient Documentation.' }]
21
+ else
22
+ super
23
+ end
24
+ end
25
+ end
26
+
27
+ register Yard
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module Fudge
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,7 @@
1
+
2
+ namespace :fudge do
3
+ desc "Run the build with Fudge"
4
+ task :fudge do
5
+ `fudge build`
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fudge::Build do
4
+ it { should be_a Fudge::Tasks::CompositeTask }
5
+ end
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ class AnotherDummyTask < DummyTask
4
+ def self.name
5
+ :another_dummy
6
+ end
7
+
8
+ def run(options={})
9
+ self.class.ran = true
10
+ end
11
+ end
12
+ Fudge::Tasks.register(AnotherDummyTask)
13
+
14
+ describe Fudge::Cli do
15
+ use_tmp_dir
16
+
17
+ after :each do
18
+ AnotherDummyTask.ran = false
19
+ end
20
+
21
+ describe :build do
22
+ before :each do
23
+ File.open('Fudgefile', 'w') do |f|
24
+ contents = <<-RUBY
25
+ build :default do
26
+ dummy
27
+ end
28
+
29
+ build :other do
30
+ another_dummy
31
+ end
32
+ RUBY
33
+ f.write(contents)
34
+ end
35
+ end
36
+
37
+ it "should run the default build" do
38
+ subject.build 'default'
39
+ DummyTask.ran.should be_true
40
+ end
41
+
42
+ it "should accept a build name to run" do
43
+ subject.build 'other'
44
+ DummyTask.ran.should be_false
45
+ AnotherDummyTask.ran.should be_true
46
+ end
47
+ end
48
+
49
+ describe :init do
50
+ it "should create a new FudgeFile in the current directory" do
51
+ File.should_not be_exists('Fudgefile')
52
+
53
+ subject.init
54
+
55
+ File.should be_exists('Fudgefile')
56
+ end
57
+
58
+ it "should contain a default build" do
59
+ subject.init
60
+
61
+ File.open('Fudgefile', 'r') do |f|
62
+ f.read.should include "build :default do\n task :rspec\nend"
63
+ end
64
+ end
65
+
66
+ it "should not modify the Fudgefile if one exists" do
67
+ File.open('Fudgefile', 'w') do |f|
68
+ f.write('foo')
69
+ end
70
+
71
+ subject.init
72
+
73
+ File.open('Fudgefile') { |f| f.read }.should == 'foo'
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,241 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fudge::Description do
4
+ let(:build) { subject.builds.values.first }
5
+ let(:build_tasks) { build.tasks.map }
6
+
7
+ def make_build
8
+ subject.build :default do
9
+ subject.task :dummy
10
+ yield
11
+ end
12
+ build.callbacks = callbacks
13
+ end
14
+
15
+ let(:input) { '' }
16
+ let(:file) { StringIO.new(input).tap { |s| s.stub(:path).and_return('') } }
17
+ subject { described_class.new file }
18
+
19
+ describe :initialize do
20
+ let(:input) { 'build :foo do; end' }
21
+
22
+ it "should add the builds in the given string" do
23
+ subject.builds[:foo].should be_a Fudge::Build
24
+ end
25
+ end
26
+
27
+ describe :build do
28
+ it "should create a new build and add it to the builds array" do
29
+ subject.builds.should be_empty
30
+
31
+ subject.build :some_branch do
32
+ end
33
+
34
+ subject.builds.should have(1).item
35
+ subject.builds[:some_branch].should be_a Fudge::Build
36
+ end
37
+ end
38
+
39
+ describe :task do
40
+ it "should add a task to the current scope" do
41
+ subject.build :default do
42
+ subject.task :dummy
43
+ end
44
+
45
+ build_tasks.should have(1).item
46
+ build_tasks.first.should be_a DummyTask
47
+ end
48
+
49
+ it "should pass arguments to the initializer" do
50
+ subject.build :default do
51
+ subject.task :dummy, :foo, :bar
52
+ end
53
+
54
+ build_tasks.first.args.should == [:foo, :bar]
55
+ end
56
+
57
+ it "should forward missing methods to task" do
58
+ subject.build :default do
59
+ subject.dummy :foo, :bar
60
+ end
61
+
62
+ build_tasks.first.args.should == [:foo, :bar]
63
+ end
64
+
65
+ it "should super method_missing if no task found" do
66
+ expect { subject.non_existeng_task :foo, :bar }.to raise_error(NoMethodError)
67
+ end
68
+
69
+ it "should add tasks recursively to composite tasks" do
70
+ subject.build :default do
71
+ subject.dummy_composite do
72
+ subject.dummy
73
+ end
74
+ end
75
+
76
+ build_tasks.first.tasks.first.should be_a DummyTask
77
+ end
78
+ end
79
+
80
+ describe :task_group do
81
+ it "should add a task group and allow it to be used in a build" do
82
+ subject.task_group :group1 do
83
+ subject.task :dummy
84
+ end
85
+
86
+ subject.build :default do
87
+ subject.task_group :group1
88
+ end
89
+
90
+ subject.builds[:default].run
91
+ DummyTask.ran.should be_true
92
+ end
93
+
94
+ it "should allow passing arguments to task groups" do
95
+ subject.task_group :special_group do |which|
96
+ subject.task which
97
+ end
98
+
99
+ subject.build :default do
100
+ subject.task_group :special_group, :dummy
101
+ end
102
+
103
+ subject.builds[:default].run
104
+ DummyTask.ran.should be_true
105
+ end
106
+
107
+ it "should raise an exception if task group not found" do
108
+ expect do
109
+ subject.build :default do
110
+ subject.task_group :unkown_group
111
+ end
112
+ end.to raise_error Fudge::Exceptions::TaskGroupNotFound
113
+ end
114
+
115
+ it "should allow the use of task groups through nested layers of composite tasks" do
116
+ subject.task_group :group1 do
117
+ subject.task :dummy
118
+ end
119
+
120
+ subject.build :default do
121
+ subject.task :dummy_composite do
122
+ subject.task_group :group1
123
+ end
124
+ end
125
+
126
+ subject.builds[:default].run
127
+ DummyTask.ran.should be_true
128
+ end
129
+
130
+ it "should allow the use of task groups through nested layers of composite nodes when options are given" do
131
+ subject.task_group :group1 do
132
+ subject.task :dummy
133
+ end
134
+
135
+ subject.build :default do
136
+ subject.task :dummy_composite, :hello, :foobar => true do
137
+ subject.task_group :group1
138
+ end
139
+ end
140
+
141
+ subject.builds[:default].run
142
+
143
+ # Check that the options are maintained through the call
144
+ build_tasks.first.args.should have(2).items
145
+ build_tasks.first.args[1][:foobar].should be_true
146
+ DummyTask.ran.should be_true
147
+ end
148
+ end
149
+
150
+ describe "Callback Hooks" do
151
+ before :each do
152
+ @ran = []
153
+ Fudge::Tasks::Shell.any_instance.stub(:run_command) do |cmd|
154
+ @ran << cmd
155
+ ['', cmd != 'fail']
156
+ end
157
+ end
158
+
159
+ describe :on_success do
160
+ context "when callbacks is set to true" do
161
+ let(:callbacks) { true }
162
+
163
+ it "should add success hooks that run after the build is successful" do
164
+ make_build do
165
+ subject.on_success { subject.shell 'FOO' }
166
+ subject.on_success { subject.shell 'BAR' }
167
+ end
168
+
169
+ build.run.should be_true
170
+ @ran.should == ['FOO', 'BAR']
171
+ end
172
+
173
+ it "should fail the build and stop running callbacks if any of the success hooks fail" do
174
+ make_build do
175
+ subject.on_success { subject.shell 'fail'; subject.shell 'FOO' }
176
+ subject.on_success { subject.shell 'BAR' }
177
+ end
178
+
179
+ build.run.should be_false
180
+ @ran.should == ['fail']
181
+ end
182
+ end
183
+
184
+ context "when callbacks is set to false" do
185
+ let(:callbacks) { false }
186
+
187
+ it "should not run the callbacks if the build succeeds" do
188
+ make_build do
189
+ subject.on_success { subject.shell 'echo "WOOP"' }
190
+ end
191
+
192
+ build.run.should be_true
193
+ @ran.should == []
194
+ end
195
+ end
196
+ end
197
+
198
+ describe :on_failure do
199
+ before :each do
200
+ DummyTask.any_instance.stub(:run).and_return(false)
201
+ end
202
+
203
+ context "when callbacks is set to true" do
204
+ let(:callbacks) { true }
205
+
206
+ it "should add failure hooks that run after the build fails" do
207
+ make_build do
208
+ subject.on_failure { subject.shell 'WOOP' }
209
+ subject.on_failure { subject.shell 'BAR' }
210
+ end
211
+
212
+ build.run.should be_false
213
+ @ran.should == ['WOOP', 'BAR']
214
+ end
215
+
216
+ it "should fail the build and stop running callbacks if any of the failure hooks fail" do
217
+ make_build do
218
+ subject.on_failure { subject.shell 'fail'; subject.shell 'FOO' }
219
+ subject.on_failure { subject.shell 'BAR' }
220
+ end
221
+
222
+ build.run.should be_false
223
+ @ran.should == ['fail']
224
+ end
225
+ end
226
+
227
+ context "when callbacks is set to false" do
228
+ let(:callbacks) { false }
229
+
230
+ it "should not run the callbacks if the build fails" do
231
+ make_build do
232
+ subject.on_failure { subject.shell 'WOOP' }
233
+ end
234
+
235
+ build.run.should be_false
236
+ @ran.should == []
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end