sake 1.0.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.
- data/LICENSE +18 -0
- data/Manifest.txt +8 -0
- data/README +76 -0
- data/Rakefile +23 -0
- data/bin/sake +5 -0
- data/lib/help.rb +32 -0
- data/lib/sake.rb +529 -0
- data/lib/server.rb +42 -0
- metadata +61 -0
data/LICENSE
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Copyright (c) 2007 Chris Wanstrath
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
7
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
8
|
+
subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
15
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
16
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest.txt
ADDED
data/README
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
= Sake. Best served warm.
|
2
|
+
|
3
|
+
Sick of copy & pasting your badass custom Rakefiles into every new Rails app
|
4
|
+
you start? Fed up with writing one-off admistrative scripts and leaving them
|
5
|
+
everything?
|
6
|
+
|
7
|
+
No longer. Sake is a tool which helps you maintain a set of system level Rake tasks.
|
8
|
+
|
9
|
+
Get started:
|
10
|
+
|
11
|
+
$ sudo gem install sake
|
12
|
+
$ sake -h
|
13
|
+
|
14
|
+
Show all Sake tasks (but no local Rake tasks), optionally only those matching a pattern.
|
15
|
+
$ sake -T
|
16
|
+
$ sake -T db
|
17
|
+
|
18
|
+
Show tasks in a Rakefile, optionally only those matching a pattern.
|
19
|
+
$ sake -T file.rake
|
20
|
+
$ sake -T file.rake db
|
21
|
+
|
22
|
+
Install tasks from a Rakefile, optionally specifying specific tasks.
|
23
|
+
$ sake -i Rakefile
|
24
|
+
$ sake -i Rakefile db:remigrate
|
25
|
+
$ sake -i Rakefile db:remigrate routes
|
26
|
+
|
27
|
+
Examine the source of a Rake task.
|
28
|
+
$ sake -e routes
|
29
|
+
|
30
|
+
Want to share just a few tasks, bundle them up?
|
31
|
+
$ sake -e routes remigrate > my_tasks.rake
|
32
|
+
|
33
|
+
You can also examine the source of a task not yet installed.
|
34
|
+
$ sake -e Rakefile db:remigrate
|
35
|
+
|
36
|
+
Uninstall an installed task.
|
37
|
+
$ sake -u db:remigrate
|
38
|
+
|
39
|
+
Can be passed one or more tasks.
|
40
|
+
|
41
|
+
Invoke a Sake task.
|
42
|
+
$ sake <taskname>
|
43
|
+
|
44
|
+
Some Sake tasks may depend on tasks which exist only locally.
|
45
|
+
|
46
|
+
For instance, you may have a db:version sake task which depends
|
47
|
+
on the 'environment' Rake task. The 'environment' Rake task is one
|
48
|
+
defined by Rails to load its environment. This db:version task will
|
49
|
+
work when your current directory is within a Rails app because
|
50
|
+
Sake knows how to find Rake tasks. This task will not work,
|
51
|
+
however, in any other directory (unless a task named 'environment'
|
52
|
+
indeed exists).
|
53
|
+
|
54
|
+
Sake can also serve its tasks over a network by launching a Mongrel handler.
|
55
|
+
Pass the -S switch to start Sake in server mode.
|
56
|
+
|
57
|
+
$ sake -S
|
58
|
+
|
59
|
+
You can, of course, specify a port.
|
60
|
+
$ sake -S -p 1111
|
61
|
+
|
62
|
+
You can also daemonize your server for long term serving fun.
|
63
|
+
$ sake -S -d
|
64
|
+
|
65
|
+
== Special Thanks
|
66
|
+
|
67
|
+
* Ryan Davis
|
68
|
+
* Eric Hodel
|
69
|
+
* Josh Susser
|
70
|
+
* Brian Donovan
|
71
|
+
* Zack Chandler
|
72
|
+
|
73
|
+
== Author
|
74
|
+
|
75
|
+
>> Chris Wanstrath
|
76
|
+
=> chris@ozmm.org
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
require 'lib/sake' unless defined? Sake
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'echoe'
|
8
|
+
|
9
|
+
Echoe.new('sake', Sake::Version::String) do |p|
|
10
|
+
p.rubyforge_name = 'err'
|
11
|
+
p.summary = "Sake tastes great and helps maintain system-level Rake files."
|
12
|
+
p.description = "Sake tastes great and helps maintain system-level Rake files."
|
13
|
+
p.url = "http://errtheblog.com/"
|
14
|
+
p.author = 'Chris Wanstrath'
|
15
|
+
p.email = "chris@ozmm.org"
|
16
|
+
p.extra_deps << ['ruby2ruby', '>=1.1.6']
|
17
|
+
p.test_globs = 'test/*_test.rb'
|
18
|
+
end
|
19
|
+
|
20
|
+
rescue LoadError => boom
|
21
|
+
puts "You are missing a dependency required for meta-operations on this gem."
|
22
|
+
puts "#{boom.to_s.capitalize}."
|
23
|
+
end
|
data/bin/sake
ADDED
data/lib/help.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
class Sake
|
2
|
+
module Help
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def display
|
6
|
+
die <<-end_help
|
7
|
+
Usage: sake [options]
|
8
|
+
|
9
|
+
Any <file> can be either a local file or a remote URL (such as a web page).
|
10
|
+
|
11
|
+
-T Show installed Sake tasks.
|
12
|
+
-T pattern Show installed Sake tasks matching <pattern>.
|
13
|
+
-T file Show tasks in <file>.
|
14
|
+
-T file pattern Show tasks in <file>.
|
15
|
+
|
16
|
+
-i file Install all tasks from <file>.
|
17
|
+
-i file tasks Install tasks from <file>. Can be one or more tasks.
|
18
|
+
|
19
|
+
-u tasks Uninstalls one or more tasks.
|
20
|
+
|
21
|
+
-e file Show the source for all tasks from <file>.
|
22
|
+
-e file tasks Show the source for <task> as defined in <file>.
|
23
|
+
|
24
|
+
-S Start a Mongrel handler and serve your installed Sake tasks
|
25
|
+
over port 4567.
|
26
|
+
-p Set the port to serve Sake tasks on. Defaults to 4567. Only
|
27
|
+
works with -S.
|
28
|
+
-d Start and daemonize a Sake server. Only works with -S.
|
29
|
+
end_help
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/sake.rb
ADDED
@@ -0,0 +1,529 @@
|
|
1
|
+
##
|
2
|
+
# Sake. Best served warm.
|
3
|
+
#
|
4
|
+
# >> Chris Wanstrath
|
5
|
+
# => chris@ozmm.org
|
6
|
+
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rake'
|
9
|
+
require 'fileutils'
|
10
|
+
require 'open-uri'
|
11
|
+
begin
|
12
|
+
require 'ruby2ruby'
|
13
|
+
rescue LoadError
|
14
|
+
die "# Sake requires the ruby2ruby gem and Ruby 1.8.6."
|
15
|
+
end
|
16
|
+
require File.dirname(__FILE__) + '/help'
|
17
|
+
|
18
|
+
##
|
19
|
+
# Show all Sake tasks (but no local Rake tasks), optionally only those matching a pattern.
|
20
|
+
# $ sake -T
|
21
|
+
# $ sake -T db
|
22
|
+
#
|
23
|
+
# Show tasks in a Rakefile, optionally only those matching a pattern.
|
24
|
+
# $ sake -T file.rake
|
25
|
+
# $ sake -T file.rake db
|
26
|
+
#
|
27
|
+
# Install tasks from a Rakefile, optionally specifying specific tasks.
|
28
|
+
# $ sake -i Rakefile
|
29
|
+
# $ sake -i Rakefile db:remigrate
|
30
|
+
# $ sake -i Rakefile db:remigrate routes
|
31
|
+
#
|
32
|
+
# Examine the source of a Rake task.
|
33
|
+
# $ sake -e routes
|
34
|
+
#
|
35
|
+
# You can also examine the source of a task not yet installed.
|
36
|
+
# $ sake -e Rakefile db:remigrate
|
37
|
+
#
|
38
|
+
# Uninstall an installed task.
|
39
|
+
# $ sake -u db:remigrate
|
40
|
+
#
|
41
|
+
# Can be passed one or more tasks.
|
42
|
+
#
|
43
|
+
# Invoke a Sake task.
|
44
|
+
# $ sake <taskname>
|
45
|
+
#
|
46
|
+
# Some Sake tasks may depend on tasks which exist only locally.
|
47
|
+
#
|
48
|
+
# For instance, you may have a db:version sake task which depends
|
49
|
+
# on the 'environment' Rake task. The 'environment' Rake task is one
|
50
|
+
# defined by Rails to load its environment. This db:version task will
|
51
|
+
# work when your current directory is within a Rails app because
|
52
|
+
# Sake knows how to find Rake tasks. This task will not work,
|
53
|
+
# however, in any other directory (unless a task named 'environment'
|
54
|
+
# indeed exists).
|
55
|
+
#
|
56
|
+
# Sake can also serve its tasks over a network by launching a Mongrel handler.
|
57
|
+
# Pass the -S switch to start Sake in server mode.
|
58
|
+
#
|
59
|
+
# $ sake -S
|
60
|
+
#
|
61
|
+
# You can, of course, specify a port.
|
62
|
+
# $ sake -S -p 1111
|
63
|
+
#
|
64
|
+
# You can also daemonize your server for long term serving fun.
|
65
|
+
# $ sake -S -d
|
66
|
+
#
|
67
|
+
class Sake
|
68
|
+
module Version
|
69
|
+
Major = '1'
|
70
|
+
Minor = '0'
|
71
|
+
Tweak = '0'
|
72
|
+
String = [ Major, Minor, Tweak ].join('.')
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# The `application' class, this is basically the controller
|
77
|
+
# which decides what to do then executes.
|
78
|
+
def initialize(args)
|
79
|
+
@args = args
|
80
|
+
Rake.application
|
81
|
+
Rake.application.options.silent = true
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# This method figures out what to do and does it.
|
86
|
+
# Basically a big switch. Note the seemingly random
|
87
|
+
# return statements: return if you don't want run_rake invoked.
|
88
|
+
# Some actions do want it invoked, however, so they don't return
|
89
|
+
# (like version, which prints a Sake version then trusts Rake to do
|
90
|
+
# likewise).
|
91
|
+
def run
|
92
|
+
##
|
93
|
+
# Show Sake tasks in the store or in a file, optionally searching for a pattern.
|
94
|
+
# $ sake -T
|
95
|
+
# $ sake -T db
|
96
|
+
# $ sake -T file.rake
|
97
|
+
# $ sake -T file.rake db
|
98
|
+
if index = @args.index('-T')
|
99
|
+
begin
|
100
|
+
tasks = TasksFile.parse(@args[index + 1]).tasks
|
101
|
+
pattern = @args[index + 2]
|
102
|
+
rescue
|
103
|
+
tasks = Store.tasks.sort
|
104
|
+
pattern = @args[index + 1]
|
105
|
+
end
|
106
|
+
|
107
|
+
return show_tasks(tasks, pattern)
|
108
|
+
|
109
|
+
##
|
110
|
+
# Install a Rakefile or a single Rake task
|
111
|
+
# $ sake -i Rakefile
|
112
|
+
# $ sake -i Rakefile db:migrate
|
113
|
+
elsif index = @args.index('-i')
|
114
|
+
return install(index)
|
115
|
+
|
116
|
+
##
|
117
|
+
# Uninstall one or more Rake tasks from the Sake store.
|
118
|
+
elsif index = @args.index('-u')
|
119
|
+
return uninstall(index)
|
120
|
+
|
121
|
+
##
|
122
|
+
# Examine a Rake task
|
123
|
+
# $ sake -e routes
|
124
|
+
# $ sake -e Rakefile db:remigrate
|
125
|
+
elsif index = @args.index('-e')
|
126
|
+
return examine(index)
|
127
|
+
|
128
|
+
##
|
129
|
+
# Start a Mongrel handler which will serve local Rake tasks
|
130
|
+
# to anyone who wants them.
|
131
|
+
#
|
132
|
+
# $ sake -S
|
133
|
+
#
|
134
|
+
# Set a port
|
135
|
+
# $ sake -S -p 1111
|
136
|
+
#
|
137
|
+
# Daemonize
|
138
|
+
# $ sake -S -d
|
139
|
+
elsif @args.include? '-S'
|
140
|
+
return serve_tasks
|
141
|
+
|
142
|
+
##
|
143
|
+
# Prints Sake and Rake versions.
|
144
|
+
elsif @args.include? '--version'
|
145
|
+
version
|
146
|
+
|
147
|
+
##
|
148
|
+
# Prints out the help screen.
|
149
|
+
elsif @args.include? '-h' or @args.include? '--help'
|
150
|
+
return Help.display
|
151
|
+
end
|
152
|
+
|
153
|
+
##
|
154
|
+
# Runs Rake proper, including our ~/.sake tasks.
|
155
|
+
run_rake
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
def show_tasks(tasks = [], pattern = nil)
|
161
|
+
Rake.application.show(tasks, pattern)
|
162
|
+
end
|
163
|
+
|
164
|
+
def install(index)
|
165
|
+
die "# I need a Rakefile." unless file = @args[index+1]
|
166
|
+
|
167
|
+
tasks = TasksFile.parse(file).tasks
|
168
|
+
|
169
|
+
# We may want to install a specific task
|
170
|
+
unless (target_tasks = @args[index + 2..-1]).empty?
|
171
|
+
tasks = tasks.select { |task| target_tasks.include? task.name }
|
172
|
+
end
|
173
|
+
|
174
|
+
# No duplicates.
|
175
|
+
tasks.each do |task|
|
176
|
+
if Store.has_task? task
|
177
|
+
puts "# Task `#{task}' already exists in #{Store.path}"
|
178
|
+
else
|
179
|
+
puts "# Installing task `#{task}'"
|
180
|
+
Store.add_task task
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# Commit.
|
185
|
+
Store.save!
|
186
|
+
end
|
187
|
+
|
188
|
+
def uninstall(index)
|
189
|
+
die "# -u option needs one or more installed tasks" if (tasks = @args[index+1..-1]).empty?
|
190
|
+
|
191
|
+
tasks.each do |name|
|
192
|
+
if task = Store.tasks[name]
|
193
|
+
puts "# Uninstalling `#{task}'. Here it is, for reference:", task.to_ruby, ''
|
194
|
+
Store.remove_task(task)
|
195
|
+
else
|
196
|
+
puts "# You don't have task `#{name}' installed.", ''
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
Store.save!
|
201
|
+
end
|
202
|
+
|
203
|
+
##
|
204
|
+
# There is a lot of guesswork inside this method. Sorry.
|
205
|
+
def examine(index)
|
206
|
+
# Can be -e file task or -e task, which defaults to Store.path
|
207
|
+
if @args[index + 2]
|
208
|
+
file = @args[index + 1]
|
209
|
+
task = @args[index + 2]
|
210
|
+
else
|
211
|
+
task = @args[index + 1]
|
212
|
+
end
|
213
|
+
|
214
|
+
# They didn't pass any args in, so just show the ~/.sake file
|
215
|
+
unless task
|
216
|
+
die Store.tasks.to_ruby
|
217
|
+
end
|
218
|
+
|
219
|
+
# Try to find the task we think they asked for.
|
220
|
+
tasks = file ? TasksFile.parse(file).tasks : Store.tasks
|
221
|
+
|
222
|
+
if tasks[task]
|
223
|
+
die tasks[task].to_ruby
|
224
|
+
end
|
225
|
+
|
226
|
+
# Didn't find the task. See if it's a file and, if so, spit
|
227
|
+
# it out.
|
228
|
+
unless (tasks = TasksFile.parse(task).tasks).empty?
|
229
|
+
die tasks.to_ruby
|
230
|
+
end
|
231
|
+
|
232
|
+
# Failure. On all counts.
|
233
|
+
error = "# Can't find task (or file) `#{task}'"
|
234
|
+
error << " in #{file}" if file
|
235
|
+
die error
|
236
|
+
end
|
237
|
+
|
238
|
+
def serve_tasks
|
239
|
+
require File.dirname(__FILE__) + '/server'
|
240
|
+
Server.start(@args)
|
241
|
+
end
|
242
|
+
|
243
|
+
def version
|
244
|
+
puts "sake, version #{Version::String}"
|
245
|
+
end
|
246
|
+
|
247
|
+
def run_rake
|
248
|
+
import Sake::Store.path
|
249
|
+
Rake.application.run
|
250
|
+
end
|
251
|
+
|
252
|
+
##
|
253
|
+
# Lets us do:
|
254
|
+
# tasks = TasksFile.parse('Rakefile').tasks
|
255
|
+
# task = tasks['db:remigrate']
|
256
|
+
class TasksArray < Array
|
257
|
+
def [](name_or_index)
|
258
|
+
if name_or_index.is_a? String
|
259
|
+
detect { |task| task.name == name_or_index }
|
260
|
+
else
|
261
|
+
super
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
##
|
266
|
+
# The source of all these tasks.
|
267
|
+
def to_ruby
|
268
|
+
map { |task| task.to_ruby }.join("\n")
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
##
|
273
|
+
# This class represents a Rake task file, in the traditional sense.
|
274
|
+
# It takes on parameter: the path to a Rakefile. When instantiated,
|
275
|
+
# it will read the file and parse out the rake tasks, storing them in
|
276
|
+
# a 'tasks' array. This array can be accessed directly:
|
277
|
+
#
|
278
|
+
# file = Sake::TasksFile.parse('Rakefile')
|
279
|
+
# puts file.tasks.inspect
|
280
|
+
#
|
281
|
+
# The parse method also works with remote files, as its implementation
|
282
|
+
# uses open-uri's open().
|
283
|
+
#
|
284
|
+
# Sake::TasksFile.parse('Rakefile')
|
285
|
+
# Sake::TasksFile.parse('http://errtheblog.com/code/errake')
|
286
|
+
class TasksFile
|
287
|
+
attr_reader :tasks
|
288
|
+
|
289
|
+
##
|
290
|
+
# The idea here is that we may be sucking in Rakefiles from an untrusted
|
291
|
+
# source. While we're happy to let the user audit the code of any Rake
|
292
|
+
# task before running it, we'd rather not be responsible for executing a
|
293
|
+
# `rm -rf` in the Rakefile itself. To ensure this, we need to set a
|
294
|
+
# safelevel before parsing the Rakefile in question.
|
295
|
+
def self.parse(file)
|
296
|
+
body = open(file).read
|
297
|
+
|
298
|
+
instance = new
|
299
|
+
Thread.new { instance.instance_eval "$SAFE = 3\n#{body}" }.join
|
300
|
+
instance
|
301
|
+
end
|
302
|
+
|
303
|
+
def initialize
|
304
|
+
@namespace = []
|
305
|
+
@tasks = TasksArray.new
|
306
|
+
@comment = nil
|
307
|
+
end
|
308
|
+
|
309
|
+
##
|
310
|
+
# We fake out an approximation of the Rake DSL in order to build
|
311
|
+
# our tasks array.
|
312
|
+
private
|
313
|
+
|
314
|
+
##
|
315
|
+
# Set a namespace for the duration of the block. Namespaces can be
|
316
|
+
# nested.
|
317
|
+
def namespace(name)
|
318
|
+
@namespace << name
|
319
|
+
yield
|
320
|
+
@namespace.delete name
|
321
|
+
end
|
322
|
+
|
323
|
+
##
|
324
|
+
# Describe the following task.
|
325
|
+
def desc(comment)
|
326
|
+
@comment = comment
|
327
|
+
end
|
328
|
+
|
329
|
+
##
|
330
|
+
# Define a task and any dependencies it may have.
|
331
|
+
def task(name, &block)
|
332
|
+
# If we're passed a hash, we know it has one key (the name of
|
333
|
+
# the task) pointing to a single or multiple dependencies.
|
334
|
+
if name.is_a? Hash
|
335
|
+
deps = name.values.first
|
336
|
+
name = name.keys.first
|
337
|
+
end
|
338
|
+
|
339
|
+
# Our namespace is really just a convenience method. Essentially,
|
340
|
+
# a namespace is just part of the task name.
|
341
|
+
name = [ @namespace, name ].flatten * ':'
|
342
|
+
|
343
|
+
# Sake's version of a rake task
|
344
|
+
task = Task.new(name, deps, @comment, &block)
|
345
|
+
|
346
|
+
@tasks << task
|
347
|
+
|
348
|
+
# We sucked up the last 'desc' declaration if it existed, so now clear
|
349
|
+
# it -- we don't want tasks without a description given one.
|
350
|
+
@comment = nil
|
351
|
+
end
|
352
|
+
|
353
|
+
public
|
354
|
+
|
355
|
+
##
|
356
|
+
# Call to_ruby on all our tasks and return a concat'd string of them.
|
357
|
+
def to_ruby
|
358
|
+
@tasks.to_ruby
|
359
|
+
end
|
360
|
+
|
361
|
+
##
|
362
|
+
# Add tasks to this TasksFile. Can accept another TasksFile object or
|
363
|
+
# an array of Task objects.
|
364
|
+
def add_tasks(tasks)
|
365
|
+
Array(tasks.is_a?(TasksFile) ? tasks.tasks : tasks).each do |task|
|
366
|
+
add_task task
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
##
|
371
|
+
# Single task version of add_tasks
|
372
|
+
def add_task(task)
|
373
|
+
@tasks << task
|
374
|
+
end
|
375
|
+
|
376
|
+
##
|
377
|
+
# Does this task exist?
|
378
|
+
def has_task?(task)
|
379
|
+
@tasks.map { |t| t.to_s }.include? task.to_s
|
380
|
+
end
|
381
|
+
|
382
|
+
##
|
383
|
+
# Hunt for and remove a particular task.
|
384
|
+
def remove_task(target_task)
|
385
|
+
@tasks.reject! { |task| task.name == target_task.name }
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
##
|
390
|
+
# This is Sake's version of a Rake task. Please handle with care.
|
391
|
+
class Task
|
392
|
+
attr_reader :name, :comment
|
393
|
+
|
394
|
+
def initialize(name, deps = nil, comment = nil, &block)
|
395
|
+
@name = name
|
396
|
+
@comment = comment
|
397
|
+
@deps = Array(deps)
|
398
|
+
@body = block
|
399
|
+
end
|
400
|
+
|
401
|
+
##
|
402
|
+
# Turn ourselves back into Rake task plaintext.
|
403
|
+
def to_ruby
|
404
|
+
out = ''
|
405
|
+
out << "desc '#{@comment}'\n" if @comment
|
406
|
+
out << "task '#{@name}'"
|
407
|
+
|
408
|
+
if @deps.any?
|
409
|
+
deps = @deps.map { |dep| "'#{dep}'" }.join(', ')
|
410
|
+
out << " => [ #{deps} ]"
|
411
|
+
end
|
412
|
+
|
413
|
+
out << " do\n"
|
414
|
+
|
415
|
+
# get rid of the proc { / } lines
|
416
|
+
out << @body.to_ruby.split("\n")[1...-1].join("\n")
|
417
|
+
|
418
|
+
out << "\nend\n"
|
419
|
+
end
|
420
|
+
|
421
|
+
##
|
422
|
+
# String-ish duck typing
|
423
|
+
def <=>(other)
|
424
|
+
to_s <=> other.to_s
|
425
|
+
end
|
426
|
+
|
427
|
+
def to_s; @name end
|
428
|
+
def inspect; @name.inspect end
|
429
|
+
end
|
430
|
+
|
431
|
+
##
|
432
|
+
# The store is, as of writing, a single Rakefile: ~/.sake
|
433
|
+
# When we add new tasks, we just re-build this file. Over
|
434
|
+
# and over.
|
435
|
+
module Store
|
436
|
+
extend self
|
437
|
+
|
438
|
+
##
|
439
|
+
# Everything we can't catch gets sent to our tasks_file.
|
440
|
+
# Common examples are #tasks or #add_task.
|
441
|
+
def method_missing(*args, &block)
|
442
|
+
tasks_file.send(*args, &block)
|
443
|
+
end
|
444
|
+
|
445
|
+
def tasks_file
|
446
|
+
FileUtils.touch(path) unless path.is_file?
|
447
|
+
@tasks_file ||= TasksFile.parse(path)
|
448
|
+
end
|
449
|
+
|
450
|
+
def path
|
451
|
+
File.join(File.expand_path('~'), '.sake')
|
452
|
+
end
|
453
|
+
|
454
|
+
def save!
|
455
|
+
File.open(path, 'w') do |file|
|
456
|
+
file.puts tasks_file.to_ruby
|
457
|
+
end
|
458
|
+
end
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
module Rake # :nodoc: all
|
463
|
+
class Application
|
464
|
+
##
|
465
|
+
# Show the tasks as 'sake' tasks.
|
466
|
+
def printf(*args)
|
467
|
+
args[0].sub!('rake', 'sake') if args[0].is_a? String
|
468
|
+
super
|
469
|
+
end
|
470
|
+
|
471
|
+
##
|
472
|
+
# Show tasks that don't have comments'
|
473
|
+
def display_tasks_and_comments(tasks = nil, pattern = nil)
|
474
|
+
tasks ||= self.tasks
|
475
|
+
|
476
|
+
if pattern ||= options.show_task_pattern
|
477
|
+
tasks = tasks.select { |t| t.name[pattern] || t.comment.to_s[pattern] }
|
478
|
+
end
|
479
|
+
|
480
|
+
width = tasks.collect { |t| t.name.length }.max
|
481
|
+
|
482
|
+
tasks.each do |t|
|
483
|
+
comment = " # #{t.comment}" if t.comment
|
484
|
+
printf "sake %-#{width}s#{comment}\n", t.name
|
485
|
+
end
|
486
|
+
end
|
487
|
+
alias_method :show, :display_tasks_and_comments
|
488
|
+
|
489
|
+
##
|
490
|
+
# Run Sake even if no Rakefile exists in the current directory.
|
491
|
+
alias_method :sake_original_have_rakefile, :have_rakefile
|
492
|
+
def have_rakefile(*args)
|
493
|
+
@rakefile ||= ''
|
494
|
+
sake_original_have_rakefile(*args) || true
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
class Task
|
499
|
+
##
|
500
|
+
# We want only run a Sake task -- not any other matching
|
501
|
+
# or duplicate tasks.
|
502
|
+
def enhance(deps=nil, &block)
|
503
|
+
@prerequisites |= deps if deps
|
504
|
+
@actions = [block] if block_given?
|
505
|
+
self
|
506
|
+
end
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
##
|
511
|
+
# Hacks which give us "Rakefile".is_file?
|
512
|
+
class String # :nodoc:
|
513
|
+
def is_file?
|
514
|
+
File.exists? self
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
class Nil # :nodoc:
|
519
|
+
def is_file?
|
520
|
+
false
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
def die(*message) # :nodoc:
|
525
|
+
puts message
|
526
|
+
exit
|
527
|
+
end
|
528
|
+
|
529
|
+
Sake.new(ARGV).run if $0 == __FILE__
|
data/lib/server.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'sake' unless defined? Sake
|
2
|
+
require 'mongrel'
|
3
|
+
|
4
|
+
class Sake
|
5
|
+
module Server
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def start(args)
|
9
|
+
if index = args.index('-p')
|
10
|
+
port = args[index+1].to_i
|
11
|
+
else
|
12
|
+
port = 4567
|
13
|
+
end
|
14
|
+
|
15
|
+
daemoned = args.include? '-d'
|
16
|
+
|
17
|
+
config = Mongrel::Configurator.new :host => "127.0.0.1" do
|
18
|
+
daemonize(:cwd => '.', :log_file => 'sake.log') if daemoned
|
19
|
+
listener(:port => port) { uri "/", :handler => Handler.new }
|
20
|
+
run
|
21
|
+
end
|
22
|
+
|
23
|
+
puts "# Serving warm sake tasks on port #{port}..." unless daemoned
|
24
|
+
config.join
|
25
|
+
end
|
26
|
+
|
27
|
+
class Handler < Mongrel::HttpHandler
|
28
|
+
def process(request, response)
|
29
|
+
uri = request.params['PATH_INFO'].sub(/^\//, '')
|
30
|
+
status = uri.empty? ? 200 : 404
|
31
|
+
body = status == 200 ? Store.to_ruby : 'Not Found'
|
32
|
+
|
33
|
+
response.start(status) do |headers, output|
|
34
|
+
headers['Content-Type'] = 'text/plain'
|
35
|
+
output.write body
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
Sake::Server.start(ARGV) if $0 == __FILE__
|
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.0
|
3
|
+
specification_version: 1
|
4
|
+
name: sake
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 1.0.0
|
7
|
+
date: 2007-06-23 00:00:00 -07:00
|
8
|
+
summary: Sake tastes great and helps maintain system-level Rake files.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: chris@ozmm.org
|
12
|
+
homepage: http://errtheblog.com/
|
13
|
+
rubyforge_project: err
|
14
|
+
description: Sake tastes great and helps maintain system-level Rake files.
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Chris Wanstrath
|
31
|
+
files:
|
32
|
+
- ./bin/sake
|
33
|
+
- ./lib/sake.rb
|
34
|
+
- ./lib/server.rb
|
35
|
+
- ./lib/help.rb
|
36
|
+
- ./Manifest.txt
|
37
|
+
- ./Rakefile
|
38
|
+
- ./README
|
39
|
+
- ./LICENSE
|
40
|
+
test_files: []
|
41
|
+
|
42
|
+
rdoc_options: []
|
43
|
+
|
44
|
+
extra_rdoc_files: []
|
45
|
+
|
46
|
+
executables:
|
47
|
+
- sake
|
48
|
+
extensions: []
|
49
|
+
|
50
|
+
requirements: []
|
51
|
+
|
52
|
+
dependencies:
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: ruby2ruby
|
55
|
+
version_requirement:
|
56
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 1.1.6
|
61
|
+
version:
|