gauntlet 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2008-12-04
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
@@ -0,0 +1,7 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ bin/gauntlet
6
+ lib/gauntlet.rb
7
+ test/test_gauntlet.rb
@@ -0,0 +1,67 @@
1
+ = gauntlet
2
+
3
+ * http://rubyforge.org/projects/seattlerb
4
+
5
+ == DESCRIPTION:
6
+
7
+ Gauntlet is a pluggable means of running code against all the latest
8
+ gems and storing off the data.
9
+
10
+ == FEATURES/PROBLEMS:
11
+
12
+ * Downloads all the latest gems and converts them to tarballs for easy access.
13
+ * Iterates through all downloaded gems, unpacks them, and then runs your code.
14
+ * Automates storage of results to YAML files.
15
+ * Easily skips over projects that already have results (overridable).
16
+ * gauntlet commandline locates your gauntlet library via rubygems:
17
+ * eg. `gauntlet flog` finds gauntlet_flog.rb in the flog gem.
18
+
19
+ == SYNOPSIS:
20
+
21
+ require 'gauntlet'
22
+
23
+ class MyGauntlet < Gauntlet
24
+ def run name
25
+ data[name] = Dir["**/*.rb"]
26
+ self.dirty = true
27
+ end
28
+ end
29
+
30
+ filter = ARGV.shift
31
+ filter = Regexp.new filter if filter
32
+
33
+ gauntlet = MyGauntlet.new
34
+ gauntlet.run_the_gauntlet filter
35
+
36
+ == REQUIREMENTS:
37
+
38
+ * rubygems
39
+
40
+ == INSTALL:
41
+
42
+ * sudo gem install gauntlet
43
+
44
+ == LICENSE:
45
+
46
+ (The MIT License)
47
+
48
+ Copyright (c) 2008 Ryan Davis, Seattle.rb
49
+
50
+ Permission is hereby granted, free of charge, to any person obtaining
51
+ a copy of this software and associated documentation files (the
52
+ 'Software'), to deal in the Software without restriction, including
53
+ without limitation the rights to use, copy, modify, merge, publish,
54
+ distribute, sublicense, and/or sell copies of the Software, and to
55
+ permit persons to whom the Software is furnished to do so, subject to
56
+ the following conditions:
57
+
58
+ The above copyright notice and this permission notice shall be
59
+ included in all copies or substantial portions of the Software.
60
+
61
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
62
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
63
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
64
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
65
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
66
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
67
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,12 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/gauntlet.rb'
6
+
7
+ Hoe.new('gauntlet', Gauntlet::VERSION) do |p|
8
+ p.rubyforge_name = 'seattlerb'
9
+ p.developer('Ryan Davis', 'ryand-ruby@zenspider.com')
10
+ end
11
+
12
+ # vim: syntax=Ruby
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/ruby -ws
2
+
3
+ if ARGV.empty? then
4
+ warn "usage: gauntlet <project>"
5
+ warn " runs gauntlet_<project>.rb"
6
+ exit 1
7
+ end
8
+
9
+ name = ARGV.shift
10
+
11
+ require "rubygems"
12
+ require "gauntlet_#{name}"
@@ -0,0 +1,356 @@
1
+ require 'rubygems/remote_fetcher'
2
+ require 'thread'
3
+ require 'yaml'
4
+
5
+ $u ||= false
6
+ $f ||= false
7
+ $F ||= false
8
+
9
+ Thread.abort_on_exception = true
10
+
11
+ class Gauntlet
12
+ VERSION = '1.0.0'
13
+
14
+ GEMURL = URI.parse 'http://gems.rubyforge.org'
15
+ GEMDIR = File.expand_path "~/.gauntlet"
16
+ DATADIR = File.expand_path "~/.gauntlet/data"
17
+
18
+ # stupid dups usually because of "dash" renames
19
+ STUPID_GEMS = %w(ajax-scaffold-generator
20
+ extract_curves
21
+ flickr-fu
22
+ hpricot-scrub
23
+ html_me
24
+ merb-builder
25
+ merb-jquery
26
+ merb-parts
27
+ merb_exceptions
28
+ merb_helpers
29
+ merb_param_protection
30
+ model_graph
31
+ not_naughty
32
+ rfeedparser-ictv
33
+ spec-converter
34
+ spec_unit)
35
+
36
+ attr_accessor :dirty, :data_file, :data
37
+
38
+ def initialize
39
+ name = self.class.name.downcase.sub(/gauntlet/, '')
40
+ self.data_file = "#{DATADIR}/#{name}-data.yml"
41
+ self.dirty = false
42
+ end
43
+
44
+ def initialize_dir
45
+ Dir.mkdir GEMDIR unless File.directory? GEMDIR
46
+ Dir.mkdir DATADIR unless File.directory? DATADIR
47
+ in_gem_dir do
48
+ File.symlink ".", "cache" unless File.exist? "cache"
49
+ end
50
+ end
51
+
52
+ def source_index
53
+ @index ||= in_gem_dir do
54
+ dump = if ($u and not $F) or not File.exist? '.source_index' then
55
+ url = GEMURL + "Marshal.#{Gem.marshal_version}.Z"
56
+ dump = Gem::RemoteFetcher.fetcher.fetch_path url
57
+ require 'zlib'
58
+ dump = Gem.inflate dump
59
+ open '.source_index', 'wb' do |io| io.write dump end
60
+ dump
61
+ else
62
+ open '.source_index', 'rb' do |io| io.read end
63
+ end
64
+
65
+ Marshal.load dump
66
+ end
67
+ end
68
+
69
+ def latest_gems
70
+ @cache ||= source_index.latest_specs
71
+ end
72
+
73
+ def gems_by_name
74
+ @by_name ||= Hash[*latest_gems.map { |gem|
75
+ [gem.name, gem, gem.full_name, gem]
76
+ }.flatten]
77
+ end
78
+
79
+ def dependencies_of name
80
+ index = source_index
81
+ gems_by_name[name].dependencies.map { |dep| index.search(dep).last }
82
+ end
83
+
84
+ def dependent_upon name
85
+ latest_gems.find_all { |gem|
86
+ gem.dependencies.any? { |dep| dep.name == name }
87
+ }
88
+ end
89
+
90
+ def find_stupid_gems
91
+ gems = Hash.new { |h,k| h[k] = [] }
92
+ stupid = []
93
+ latest = {}
94
+
95
+ latest_gems.each do |spec|
96
+ name = spec.name.gsub(/-/, '_')
97
+ next unless name =~ /_/
98
+ gems[name] << spec
99
+ end
100
+
101
+ gems.reject! { |k,v| v.size == 1 || v.map { |s| s.name }.uniq.size == 1 }
102
+
103
+ gems.each do |k,v|
104
+ sorted = v.sort_by { |spec| spec.version }
105
+ latest[sorted.last.name] = true
106
+ sorted.each do |spec|
107
+ stupid << spec.name unless latest[spec.name]
108
+ end
109
+ end
110
+
111
+ stupid.uniq
112
+ end
113
+
114
+ def update_gem_tarballs
115
+ initialize_dir
116
+
117
+ latest = self.latest_gems
118
+
119
+ puts "updating mirror"
120
+
121
+ in_gem_dir do
122
+ gems = Dir["*.gem"]
123
+ tgzs = Dir["*.tgz"]
124
+
125
+ old = tgzs - latest.map { |spec| "#{spec.full_name}.tgz" }
126
+ unless old.empty? then
127
+ puts "deleting #{old.size} tgzs"
128
+ old.each do |tgz|
129
+ File.unlink tgz
130
+ end
131
+ end
132
+
133
+ tasks = Queue.new
134
+ latest.sort!
135
+ latest.reject! { |spec| tgzs.include? "#{spec.full_name}.tgz" }
136
+ tasks.push(latest.shift) until latest.empty? # LAME
137
+
138
+ puts "fetching #{tasks.size} gems"
139
+
140
+ threads = []
141
+ 10.times do
142
+ threads << Thread.new do
143
+ fetcher = Gem::RemoteFetcher.new nil # fuck proxies
144
+ until tasks.empty? do
145
+ spec = tasks.shift
146
+ full_name = spec.full_name
147
+ tgz_name = "#{full_name}.tgz"
148
+ gem_name = "#{full_name}.gem"
149
+
150
+ unless gems.include? gem_name then
151
+ begin
152
+ warn "downloading #{full_name}"
153
+ fetcher.download(spec, GEMURL, Dir.pwd)
154
+ rescue Gem::RemoteFetcher::FetchError
155
+ warn " failed"
156
+ next
157
+ end
158
+ end
159
+
160
+ warn " converting #{gem_name} to tarball"
161
+
162
+ unless File.directory? full_name then
163
+ system "gem unpack cache/#{gem_name} &> /dev/null"
164
+ system "gem spec -l cache/#{gem_name} > #{full_name}/gemspec"
165
+ end
166
+
167
+ system "chmod -R u+rwX #{full_name} && tar zmcf #{tgz_name} #{full_name} && rm -rf #{full_name} #{gem_name}"
168
+ end
169
+ end
170
+ end
171
+
172
+ threads.each do |thread|
173
+ thread.join
174
+ end
175
+ end
176
+ rescue Interrupt
177
+ warn "user cancelled... quitting"
178
+ exit 1
179
+ end
180
+
181
+ def each_gem filter = nil
182
+ filter ||= /^[\w-]+-\d+(\.\d+)*\.tgz$/
183
+ in_gem_dir do
184
+ Dir["*.tgz"].each do |tgz|
185
+ next unless tgz =~ filter
186
+
187
+ yield File.basename(tgz, ".tgz")
188
+ end
189
+ end
190
+ end
191
+
192
+ def with_gem name
193
+ in_gem_dir do
194
+ begin
195
+ system "tar zxmf #{name}.tgz 2> /dev/null"
196
+ Dir.chdir name do
197
+ yield name
198
+ end
199
+ ensure
200
+ system "rm -r #{name}"
201
+ end
202
+ end
203
+ end
204
+
205
+ def load_yaml path, default = {}
206
+ YAML.load(File.read(path)) rescue default
207
+ end
208
+
209
+ def save_yaml path, data
210
+ File.open("#{path}.new", 'w') do |f|
211
+ warn "*** saving #{path}"
212
+ YAML.dump data, f
213
+ end
214
+ File.rename "#{path}.new", path
215
+ end
216
+
217
+ def in_gem_dir
218
+ Dir.chdir GEMDIR do
219
+ yield
220
+ end
221
+ end
222
+
223
+ ##
224
+ # Override this to customize gauntlet. See run_the_gauntlet for
225
+ # other ways of adding behavior.
226
+
227
+ def run name
228
+ raise "subclass responsibility"
229
+ end
230
+
231
+ ##
232
+ # Override this to return true if the gem should be skipped.
233
+
234
+ def should_skip? name
235
+ self.data[name]
236
+ end
237
+
238
+ ##
239
+ # This is the main driver for gauntlet. filter allows you to pass in
240
+ # a regexp to only run against a subset of the gems available. You
241
+ # can pass a block to run_the_gauntlet or it will call run. Both are
242
+ # passed the name of the gem and are executed within the gem
243
+ # directory.
244
+
245
+ def run_the_gauntlet filter = nil
246
+ initialize_dir
247
+ update_gem_tarballs if $u
248
+
249
+ self.data ||= load_yaml data_file
250
+
251
+ each_gem filter do |name|
252
+ next if should_skip? name
253
+ with_gem name do
254
+ if block_given? then
255
+ yield name
256
+ else
257
+ run name
258
+ end
259
+ end
260
+ end
261
+ rescue Interrupt
262
+ warn "user cancelled. quitting"
263
+ ensure
264
+ save_yaml data_file, data if dirty
265
+ end
266
+ end
267
+
268
+ ############################################################
269
+ # Extensions and Overrides
270
+
271
+ # bug in RemoteFetcher#download prevents multithreading. Remove after 1.3.2
272
+ class Gem::RemoteFetcher
273
+ def download(spec, source_uri, install_dir = Gem.dir)
274
+ if File.writable?(install_dir)
275
+ cache_dir = File.join install_dir, 'cache'
276
+ else
277
+ cache_dir = File.join(Gem.user_dir, 'cache')
278
+ end
279
+
280
+ gem_file_name = "#{spec.full_name}.gem"
281
+ local_gem_path = File.join cache_dir, gem_file_name
282
+
283
+ FileUtils.mkdir_p cache_dir rescue nil unless File.exist? cache_dir
284
+
285
+ source_uri = URI.parse source_uri unless URI::Generic === source_uri
286
+ scheme = source_uri.scheme
287
+
288
+ # URI.parse gets confused by MS Windows paths with forward slashes.
289
+ scheme = nil if scheme =~ /^[a-z]$/i
290
+
291
+ case scheme
292
+ when 'http', 'https' then
293
+ unless File.exist? local_gem_path then
294
+ begin
295
+ say "Downloading gem #{gem_file_name}" if
296
+ Gem.configuration.really_verbose
297
+
298
+ remote_gem_path = source_uri + "gems/#{gem_file_name}"
299
+
300
+ gem = self.fetch_path remote_gem_path
301
+ rescue Gem::RemoteFetcher::FetchError
302
+ raise if spec.original_platform == spec.platform
303
+
304
+ alternate_name = "#{spec.original_name}.gem"
305
+
306
+ say "Failed, downloading gem #{alternate_name}" if
307
+ Gem.configuration.really_verbose
308
+
309
+ remote_gem_path = source_uri + "gems/#{alternate_name}"
310
+
311
+ gem = self.fetch_path remote_gem_path
312
+ end
313
+
314
+ File.open local_gem_path, 'wb' do |fp|
315
+ fp.write gem
316
+ end
317
+ end
318
+ when nil, 'file' then # TODO test for local overriding cache
319
+ begin
320
+ FileUtils.cp source_uri.to_s, local_gem_path
321
+ rescue Errno::EACCES
322
+ local_gem_path = source_uri.to_s
323
+ end
324
+
325
+ say "Using local gem #{local_gem_path}" if
326
+ Gem.configuration.really_verbose
327
+ else
328
+ raise Gem::InstallError, "unsupported URI scheme #{source_uri.scheme}"
329
+ end
330
+
331
+ local_gem_path
332
+ end
333
+ end
334
+
335
+ class Array
336
+ def sum
337
+ sum = 0
338
+ self.each { |i| sum += i }
339
+ sum
340
+ end
341
+
342
+ def average
343
+ return self.sum / self.length.to_f
344
+ end
345
+
346
+ def sample_variance
347
+ avg = self.average
348
+ sum = 0
349
+ self.each { |i| sum += (i - avg) ** 2 }
350
+ return (1 / self.length.to_f * sum)
351
+ end
352
+
353
+ def stddev
354
+ return Math.sqrt(self.sample_variance)
355
+ end
356
+ end
File without changes
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gauntlet
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Ryan Davis
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-12-04 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.8.2
24
+ version:
25
+ description: Gauntlet is a pluggable means of running code against all the latest gems and storing off the data.
26
+ email:
27
+ - ryand-ruby@zenspider.com
28
+ executables:
29
+ - gauntlet
30
+ extensions: []
31
+
32
+ extra_rdoc_files:
33
+ - History.txt
34
+ - Manifest.txt
35
+ - README.txt
36
+ files:
37
+ - History.txt
38
+ - Manifest.txt
39
+ - README.txt
40
+ - Rakefile
41
+ - bin/gauntlet
42
+ - lib/gauntlet.rb
43
+ - test/test_gauntlet.rb
44
+ has_rdoc: true
45
+ homepage: http://rubyforge.org/projects/seattlerb
46
+ post_install_message:
47
+ rdoc_options:
48
+ - --main
49
+ - README.txt
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ requirements: []
65
+
66
+ rubyforge_project: seattlerb
67
+ rubygems_version: 1.3.1
68
+ signing_key:
69
+ specification_version: 2
70
+ summary: Gauntlet is a pluggable means of running code against all the latest gems and storing off the data.
71
+ test_files:
72
+ - test/test_gauntlet.rb