fudge 0.0.1

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