gem-dependent 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -4,5 +4,4 @@ group :dev do
4
4
  gem 'rake'
5
5
  gem 'rspec', '~>2'
6
6
  gem 'jeweler'
7
- gem 'parallel', '>= 0.5.1'
8
- end
7
+ end
data/Gemfile.lock CHANGED
@@ -9,7 +9,6 @@ GEM
9
9
  git (>= 1.2.5)
10
10
  rubyforge (>= 2.0.0)
11
11
  json_pure (1.4.6)
12
- parallel (0.5.1)
13
12
  rake (0.8.7)
14
13
  rspec (2.0.1)
15
14
  rspec-core (~> 2.0.1)
@@ -29,6 +28,5 @@ PLATFORMS
29
28
 
30
29
  DEPENDENCIES
31
30
  jeweler
32
- parallel (>= 0.5.1)
33
31
  rake
34
32
  rspec (~> 2)
data/Rakefile CHANGED
@@ -12,19 +12,6 @@ begin
12
12
  gem.email = "grosser.michael@gmail.com"
13
13
  gem.homepage = "http://github.com/grosser/#{gem.name}"
14
14
  gem.authors = ["Michael Grosser"]
15
-
16
-
17
- # if parallel was a dependency, every requirement of rubygems would
18
- # load parallel, which causes overhead and problems
19
- gem.post_install_message = <<-POST_INSTALL_MESSAGE
20
- #{'*'*50}
21
-
22
- Since parallel cannot be a dependency, please install by hand:
23
-
24
- gem install parallel
25
-
26
- #{'*'*50}
27
- POST_INSTALL_MESSAGE
28
15
  end
29
16
 
30
17
  Jeweler::GemcutterTasks.new
data/Readme.md CHANGED
@@ -5,6 +5,7 @@ Install
5
5
  Usage
6
6
  =====
7
7
  The first run can take looooong, but after the caches are filled, its pretty fast.
8
+
8
9
  gem dependent my_gem
9
10
 
10
11
  --source URL Query this source (e.g. http://rubygems.org)
@@ -16,11 +17,11 @@ The first run can take looooong, but after the caches are filled, its pretty fas
16
17
  Output
17
18
  ======
18
19
 
19
- $ gem dependent my_gem --source http://rubygems.org
20
+ gem dependent my_gem --source http://rubygems.org
20
21
  other_gem >= 1.2.1
21
22
  even_more = 0.0.1
22
23
 
23
- $ gem dependent XXX --source http://rubygems.org --no-progress | wc -l
24
+ gem dependent XXX --source http://rubygems.org --no-progress | wc -l
24
25
 
25
26
  # Fun-facts from 2010-11-03
26
27
  bundler: 263
@@ -36,12 +37,12 @@ Output
36
37
 
37
38
  TODO
38
39
  =====
39
- - include reverse dependencies (a > b > c --> a = [b,c])
40
+ - include nested dependencies (a > b > c --> a = [b,c])
40
41
  - add tests for cli interface
41
42
  - add `--type development` support
42
43
 
43
44
  Author
44
45
  ======
45
- [Michael Grosser](http://grosser.it)
46
- grosser.michael@gmail.com
46
+ [Michael Grosser](http://grosser.it)<br/>
47
+ michael@grosse.it<br/>
47
48
  Hereby placed under public domain, do what you want, just do not hold me accountable...
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.6
1
+ 0.1.7
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{gem-dependent}
8
- s.version = "0.1.6"
8
+ s.version = "0.1.7"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Michael Grosser"]
12
- s.date = %q{2011-05-09}
12
+ s.date = %q{2011-08-21}
13
13
  s.email = %q{grosser.michael@gmail.com}
14
14
  s.files = [
15
15
  "Gemfile",
@@ -20,19 +20,12 @@ Gem::Specification.new do |s|
20
20
  "gem-dependent.gemspec",
21
21
  "lib/rubygems/commands/dependent_command.rb",
22
22
  "lib/rubygems/dependent.rb",
23
+ "lib/rubygems/dependent_parallel.rb",
23
24
  "lib/rubygems_plugin.rb",
24
25
  "spec/dependent_spec.rb",
25
26
  "spec/fixtures/gemcutter_specs.yml"
26
27
  ]
27
28
  s.homepage = %q{http://github.com/grosser/gem-dependent}
28
- s.post_install_message = %q{**************************************************
29
-
30
- Since parallel cannot be a dependency, please install by hand:
31
-
32
- gem install parallel
33
-
34
- **************************************************
35
- }
36
29
  s.rdoc_options = ["--charset=UTF-8"]
37
30
  s.require_paths = ["lib"]
38
31
  s.rubygems_version = %q{1.6.2}
@@ -1,9 +1,6 @@
1
- require 'parallel'
1
+ require 'rubygems/dependent_parallel'
2
2
  require 'rubygems/spec_fetcher'
3
3
 
4
- # older parallel versions can produce strange bugs
5
- puts "update parallel gem" if Parallel::VERSION < '0.5.1'
6
-
7
4
  module Gem
8
5
  class Dependent
9
6
  VERSION = File.read( File.join(File.dirname(__FILE__),'..','..','VERSION') ).strip
@@ -34,7 +31,7 @@ module Gem
34
31
 
35
32
  def self.fetch_all_dependencies(specs_and_sources, options)
36
33
  parallel = (options[:parallel] || 15)
37
- Parallel.map(specs_and_sources, :in_processes => parallel) do |spec, source|
34
+ Gem::Dependent::Parallel.map(specs_and_sources, :in_processes => parallel) do |spec, source|
38
35
  yield if block_given?
39
36
  name = spec.first
40
37
  dependencies = fetch_dependencies(spec, source)
@@ -0,0 +1,272 @@
1
+ # if parallel was a dependency, every requirement of rubygems would
2
+ # load parallel, which causes overhead and problems
3
+ # copied from https://github.com/grosser/parallel/blob/master/lib/parallel.rb
4
+
5
+ require 'thread' # to get Thread.exclusive
6
+ require 'base64'
7
+ require 'rbconfig'
8
+
9
+ module Gem;end
10
+ class Gem::Dependent;end
11
+ class Gem::Dependent::Parallel
12
+ VERSION = '0.5.8'
13
+
14
+ def self.in_threads(options={:count => 2})
15
+ count, options = extract_count_from_options(options)
16
+
17
+ out = []
18
+ threads = []
19
+
20
+ count.times do |i|
21
+ threads[i] = Thread.new do
22
+ out[i] = yield(i)
23
+ end
24
+ end
25
+
26
+ wait_for_threads(threads)
27
+
28
+ out
29
+ end
30
+
31
+ def self.in_processes(options = {}, &block)
32
+ count, options = extract_count_from_options(options)
33
+ count ||= processor_count
34
+ map(0...count, options.merge(:in_processes => count), &block)
35
+ end
36
+
37
+ def self.each(array, options={}, &block)
38
+ map(array, options.merge(:preserve_results => false), &block)
39
+ array
40
+ end
41
+
42
+ def self.each_with_index(array, options={}, &block)
43
+ each(array, options.merge(:with_index => true), &block)
44
+ end
45
+
46
+ def self.map(array, options = {}, &block)
47
+ array = array.to_a # turn Range and other Enumerable-s into an Array
48
+
49
+ if options[:in_threads]
50
+ method = :in_threads
51
+ size = options[method]
52
+ else
53
+ method = :in_processes
54
+ size = options[method] || processor_count
55
+ end
56
+ size = [array.size, size].min
57
+
58
+ return work_direct(array, options, &block) if size == 0
59
+
60
+ if method == :in_threads
61
+ work_in_threads(array, options.merge(:count => size), &block)
62
+ else
63
+ work_in_processes(array, options.merge(:count => size), &block)
64
+ end
65
+ end
66
+
67
+ def self.map_with_index(array, options={}, &block)
68
+ map(array, options.merge(:with_index => true), &block)
69
+ end
70
+
71
+ def self.processor_count
72
+ case RbConfig::CONFIG['host_os']
73
+ when /darwin9/
74
+ `hwprefs cpu_count`.to_i
75
+ when /darwin/
76
+ (hwprefs_available? ? `hwprefs thread_count` : `sysctl -n hw.ncpu`).to_i
77
+ when /linux/
78
+ `grep -c processor /proc/cpuinfo`.to_i
79
+ when /freebsd/
80
+ `sysctl -n hw.ncpu`.to_i
81
+ when /mswin|mingw/
82
+ require 'win32ole'
83
+ wmi = WIN32OLE.connect("winmgmts://")
84
+ cpu = wmi.ExecQuery("select NumberOfLogicalProcessors from Win32_Processor")
85
+ cpu.to_enum.first.NumberOfLogicalProcessors
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def self.work_direct(array, options)
92
+ results = []
93
+ array.each_with_index do |e,i|
94
+ results << (options[:with_index] ? yield(e,i) : yield(e))
95
+ end
96
+ results
97
+ end
98
+
99
+ def self.hwprefs_available?
100
+ `which hwprefs` != ''
101
+ end
102
+
103
+ def self.work_in_threads(items, options, &block)
104
+ results = []
105
+ current = -1
106
+ exception = nil
107
+
108
+ in_threads(options[:count]) do
109
+ # as long as there are more items, work on one of them
110
+ loop do
111
+ break if exception
112
+
113
+ index = Thread.exclusive{ current+=1 }
114
+ break if index >= items.size
115
+
116
+ begin
117
+ results[index] = call_with_index(items, index, options, &block)
118
+ rescue Exception => e
119
+ exception = e
120
+ break
121
+ end
122
+ end
123
+ end
124
+
125
+ raise exception if exception
126
+
127
+ results
128
+ end
129
+
130
+ def self.work_in_processes(items, options, &blk)
131
+ current_index = -1
132
+ results = []
133
+ pids = []
134
+ exception = nil
135
+
136
+ kill_on_ctrl_c(pids)
137
+
138
+ in_threads(options[:count]) do |i|
139
+ x = i
140
+ worker = worker(items, options, &blk)
141
+ pids[i] = worker[:pid]
142
+
143
+ begin
144
+ loop do
145
+ break if exception
146
+ index = Thread.exclusive{ current_index += 1 }
147
+ break if index >= items.size
148
+
149
+ write_to_pipe(worker[:write], index)
150
+ output = decode(worker[:read].gets.chomp)
151
+
152
+ if ExceptionWrapper === output
153
+ exception = output.exception
154
+ else
155
+ results[index] = output
156
+ end
157
+ end
158
+ ensure
159
+ worker[:read].close
160
+ worker[:write].close
161
+
162
+ # if it goes zombie, rather wait here to be able to debug
163
+ wait_for_process worker[:pid]
164
+ end
165
+ end
166
+
167
+ raise exception if exception
168
+
169
+ results
170
+ end
171
+
172
+ def self.worker(items, options, &block)
173
+ # use less memory on REE
174
+ GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=)
175
+
176
+ child_read, parent_write = IO.pipe
177
+ parent_read, child_write = IO.pipe
178
+
179
+ pid = Process.fork do
180
+ begin
181
+ parent_write.close
182
+ parent_read.close
183
+
184
+ process_incoming_jobs(child_read, child_write, items, options, &block)
185
+ ensure
186
+ child_read.close
187
+ child_write.close
188
+ end
189
+ end
190
+
191
+ child_read.close
192
+ child_write.close
193
+
194
+ {:read => parent_read, :write => parent_write, :pid => pid}
195
+ end
196
+
197
+ def self.process_incoming_jobs(read, write, items, options, &block)
198
+ while input = read.gets and input != "\n"
199
+ index = decode(input.chomp)
200
+ begin
201
+ result = call_with_index(items, index, options, &block)
202
+ result = nil if options[:preserve_results] == false
203
+ rescue Exception => e
204
+ result = ExceptionWrapper.new(e)
205
+ end
206
+ write_to_pipe(write, result)
207
+ end
208
+ end
209
+
210
+ def self.write_to_pipe(pipe, item)
211
+ pipe.write(encode(item))
212
+ end
213
+
214
+ def self.wait_for_threads(threads)
215
+ threads.compact.each do |t|
216
+ begin
217
+ t.join
218
+ rescue Interrupt
219
+ # thread died, do not stop other threads
220
+ end
221
+ end
222
+ end
223
+
224
+ def self.wait_for_process(pid)
225
+ begin
226
+ Process.wait(pid)
227
+ rescue Interrupt
228
+ # process died
229
+ end
230
+ end
231
+
232
+ def self.encode(obj)
233
+ Base64.encode64(Marshal.dump(obj)).split("\n").join + "\n"
234
+ end
235
+
236
+ def self.decode(str)
237
+ Marshal.load(Base64.decode64(str))
238
+ end
239
+
240
+ # options is either a Integer or a Hash with :count
241
+ def self.extract_count_from_options(options)
242
+ if options.is_a?(Hash)
243
+ count = options[:count]
244
+ else
245
+ count = options
246
+ options = {}
247
+ end
248
+ [count, options]
249
+ end
250
+
251
+ # kill all these processes (children) if user presses Ctrl+c
252
+ def self.kill_on_ctrl_c(pids)
253
+ Signal.trap :SIGINT do
254
+ $stderr.puts 'Parallel execution interrupted, exiting ...'
255
+ pids.each { |pid| Process.kill(:KILL, pid) if pid }
256
+ exit 1 # Quit with 'failed' signal
257
+ end
258
+ end
259
+
260
+ def self.call_with_index(array, index, options, &block)
261
+ args = [array[index]]
262
+ args << index if options[:with_index]
263
+ block.call(*args)
264
+ end
265
+
266
+ class ExceptionWrapper
267
+ attr_reader :exception
268
+ def initialize(exception)
269
+ @exception = exception
270
+ end
271
+ end
272
+ end
@@ -53,7 +53,7 @@ describe Gem::Dependent do
53
53
 
54
54
  it "obeys parallel option" do
55
55
  stub_source
56
- Parallel.should_receive(:map).with(anything, :in_processes => 3).and_return []
56
+ Gem::Dependent::Parallel.should_receive(:map).with(anything, :in_processes => 3).and_return []
57
57
  Gem::Dependent.find('hoe', :parallel => 3)
58
58
  end
59
59
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gem-dependent
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 6
10
- version: 0.1.6
9
+ - 7
10
+ version: 0.1.7
11
11
  platform: ruby
12
12
  authors:
13
13
  - Michael Grosser
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-05-09 00:00:00 +02:00
18
+ date: 2011-08-21 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies: []
21
21
 
@@ -36,6 +36,7 @@ files:
36
36
  - gem-dependent.gemspec
37
37
  - lib/rubygems/commands/dependent_command.rb
38
38
  - lib/rubygems/dependent.rb
39
+ - lib/rubygems/dependent_parallel.rb
39
40
  - lib/rubygems_plugin.rb
40
41
  - spec/dependent_spec.rb
41
42
  - spec/fixtures/gemcutter_specs.yml
@@ -43,15 +44,7 @@ has_rdoc: true
43
44
  homepage: http://github.com/grosser/gem-dependent
44
45
  licenses: []
45
46
 
46
- post_install_message: |
47
- **************************************************
48
-
49
- Since parallel cannot be a dependency, please install by hand:
50
-
51
- gem install parallel
52
-
53
- **************************************************
54
-
47
+ post_install_message:
55
48
  rdoc_options:
56
49
  - --charset=UTF-8
57
50
  require_paths: