turbodep 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.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/bin/dep +386 -0
  3. data/test/dep.rb +126 -0
  4. metadata +50 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 151d0651ac1d5be8161d650c80784a56992a56fb
4
+ data.tar.gz: 740f03a4a1cd363a51f978db11b050428e697f0a
5
+ SHA512:
6
+ metadata.gz: 74b0a41b7fdb269b5a03e3eab39a91eee274c4372ad6d06d853b6aabba81ed51f36a56a2c59c0966549b20031e575ccf22b24a6fe808219a85607a0431db2dc9
7
+ data.tar.gz: 4e35aacd315e929b01e6e6708e44b42d6e36681f77e9594703fa1ef753e9e6cb4902f58dbd575eb9b79b13919cd63c0bc505ef099bfb107ee9cb7bef2bf528a4
data/bin/dep ADDED
@@ -0,0 +1,386 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "fileutils"
4
+
5
+ module Dep
6
+ class List
7
+ attr :path
8
+
9
+ def initialize(path)
10
+ @path = path
11
+ end
12
+
13
+ def add(lib)
14
+ remove(lib)
15
+ libraries.push(lib)
16
+ end
17
+
18
+ def remove(lib)
19
+ libraries.delete_if { |e| e.name == lib.name }
20
+ end
21
+
22
+ def libraries
23
+ @libraries ||= File.readlines(path).map { |line| Lib[line] }
24
+ end
25
+
26
+ def missing_libraries
27
+ libraries.reject(&:available?)
28
+ end
29
+
30
+ def save
31
+ File.open(path, "w") do |file|
32
+ libraries.each do |lib|
33
+ file.puts lib.to_s
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ class Lib < Struct.new(:name, :version)
40
+ def self.[](line)
41
+ if line.strip =~ /^(\S+) -v (\S+)$/
42
+ return new($1, $2)
43
+ elsif line.strip =~ /^(\S+) --git '?([^']+)'?$/
44
+ return GitLib.new($1, $2)
45
+ elsif line.strip =~ /^(\S+) --github '?([^']+)'?$/
46
+ return GitHubLib.new($1, $2)
47
+ elsif line.strip =~ /^(\S+) -v (\S+) --source '?([^']+)'?$/
48
+ return SourceLib.new($1, $2, $3)
49
+ else
50
+ abort("Invalid requirement found: #{line}")
51
+ end
52
+ end
53
+
54
+ def available?
55
+ Gem::Specification.find_by_name(name, version)
56
+ rescue Gem::LoadError
57
+ return false
58
+ end
59
+
60
+ def to_s
61
+ "#{name} -v #{version}"
62
+ end
63
+
64
+ def ==(other)
65
+ to_s == other.to_s
66
+ end
67
+
68
+ def install
69
+ "gem install #{to_s}"
70
+ end
71
+ end
72
+
73
+ class SourceLib < Lib
74
+ attr_accessor :source
75
+
76
+ def initialize(name, version, source)
77
+ self.name = name
78
+ self.version = version
79
+ self.source = source
80
+ end
81
+
82
+ def to_s
83
+ super + "--source #{source}"
84
+ end
85
+
86
+ def install
87
+ super + "--source #{source}"
88
+ end
89
+ end
90
+
91
+ class GitLib < Lib
92
+ attr_accessor :uri, :gemspec_path
93
+
94
+ def initialize(name, data)
95
+ self.name = name
96
+ if data.strip =~ /^(\S+) (\S+)$/
97
+ @uri = $1
98
+ @gemspec_path = $2
99
+ else
100
+ @uri = data
101
+ @gemspec_path = nil
102
+ end
103
+ self
104
+ end
105
+
106
+ def to_s
107
+ "#{name} --git #{@gemspec_path ? "'#{@uri} #{@gemspec_path}'" : @uri}"
108
+ end
109
+
110
+ def install
111
+ tmp_path = "/tmp/dep-#{name}"
112
+ %{rm -rf #{tmp_path} &&
113
+ git clone #{uri} #{tmp_path} &&
114
+ (cd #{tmp_path}/#{@gemspec_path} && gem build #{name}.gemspec) &&
115
+ gem install `ls #{tmp_path}/#{@gemspec_path}/#{name}-*.gem | head -n 1`}
116
+ end
117
+ end
118
+
119
+ class GitHubLib < GitLib
120
+ attr_accessor :repository
121
+
122
+ def initialize(*)
123
+ super
124
+ @repository = @uri
125
+ @uri = "git://github.com/#{repository}.git"
126
+ self
127
+ end
128
+
129
+ def to_s
130
+ "#{name} --github #{@gemspec_path ? "'#{@repository} #{@gemspec_path}'" : @repository}"
131
+ end
132
+ end
133
+
134
+ module CLI
135
+ class << self
136
+ attr_accessor :prerelease, :list, :file, :git
137
+ end
138
+
139
+ def self.add(name)
140
+ if @git
141
+ lib = Dep::GitLib.new(name, @git)
142
+ else
143
+ dependency = Gem::Dependency.new(name)
144
+ fetcher = Gem::SpecFetcher.fetcher
145
+
146
+ if fetcher.respond_to?(:spec_for_dependency)
147
+ dependency.prerelease = @prerelease
148
+ res, _ = fetcher.spec_for_dependency(dependency)
149
+ else
150
+ res = fetcher.fetch(dependency, false, true, @prerelease)
151
+ end
152
+
153
+ abort("Unable to find #{name}") if res.empty?
154
+
155
+ spec = res[-1][0]
156
+ lib = Dep::Lib.new(spec.name, spec.version)
157
+ end
158
+
159
+ @list.add(lib)
160
+ @list.save
161
+
162
+ puts "dep: added #{lib}"
163
+ end
164
+
165
+ def self.rm(name)
166
+ @list.remove(Dep::Lib.new(name))
167
+ @list.save
168
+
169
+ puts "dep: removed #{name}"
170
+ end
171
+
172
+ def self.check
173
+ if @list.missing_libraries.empty?
174
+ puts "dep: all cool"
175
+ else
176
+ puts "dep: the following libraries are missing"
177
+
178
+ @list.missing_libraries.each do |lib|
179
+ puts " %s" % lib
180
+ end
181
+
182
+ exit(1)
183
+ end
184
+ end
185
+
186
+ def self.install
187
+ if @list.missing_libraries.empty?
188
+ puts "dep: nothing to install"
189
+ exit
190
+ end
191
+
192
+ @list.missing_libraries.each do |lib|
193
+ run lib.install
194
+ end
195
+ end
196
+
197
+ def self.run(cmd)
198
+ puts " #{cmd}"
199
+ `#{cmd}`
200
+ end
201
+
202
+ def self.abort
203
+ abort("error: dep --help for more info")
204
+ end
205
+ end
206
+ end
207
+
208
+ module Kernel
209
+ private
210
+ def on(flag, &block)
211
+ if index = ARGV.index(flag)
212
+ _ = ARGV.delete_at(index)
213
+
214
+ case block.arity
215
+ when 1 then block.call(ARGV.delete_at(index))
216
+ when 0 then block.call
217
+ else
218
+ Dep::CLI.abort
219
+ end
220
+ end
221
+ end
222
+ end
223
+
224
+ # So originally, this was just $0 == __FILE__, but
225
+ # since rubygems wraps the actual bin file in a loader
226
+ # script, we have to instead rely on a different condition.
227
+ if File.basename($0) == "dep"
228
+
229
+ Dep::CLI.file = File.join(Dir.pwd, ".gems")
230
+ Dep::CLI.prerelease = false
231
+ Dep::CLI.git = false
232
+ Dep::CLI.github = false
233
+ Dep::CLI.source = false
234
+
235
+ on("-f") do |file|
236
+ Dep::CLI.file = file
237
+ end
238
+
239
+ on("--pre") do
240
+ Dep::CLI.prerelease = true
241
+ end
242
+
243
+ on("--git") do |git|
244
+ Dep::CLI.git = git
245
+ end
246
+
247
+ on("--github") do |github|
248
+ Dep::CLI.github = github
249
+ end
250
+
251
+ on("--source") do |source|
252
+ Dep::CLI.source = source
253
+ end
254
+
255
+ on("--help") do
256
+
257
+ # We can't use DATA.read because rubygems does a wrapper script.
258
+ help = File.read(__FILE__).split(/^__END__/)[1]
259
+
260
+ IO.popen("less", "w") { |f| f.write(help) }
261
+ exit
262
+ end
263
+
264
+ Dep::CLI.list = Dep::List.new(Dep::CLI.file)
265
+
266
+ FileUtils.touch(Dep::CLI.list.path) unless File.exist?(Dep::CLI.list.path)
267
+
268
+ case ARGV[0]
269
+ when "add"
270
+ Dep::CLI.add(ARGV[1])
271
+ when "rm"
272
+ Dep::CLI.rm(ARGV[1])
273
+ when "install", "i"
274
+ Dep::CLI.install
275
+ when nil
276
+ Dep::CLI.check
277
+ else
278
+ Dep::CLI.abort
279
+ end
280
+
281
+ end
282
+
283
+ __END__
284
+ DEP(1)
285
+
286
+ NAME
287
+ dep -- Basic dependency tracking
288
+
289
+ SYNOPSIS
290
+ dep
291
+ dep add libname [--pre]|[--git]|[--github][--source]
292
+ dep rm libname
293
+ dep install
294
+
295
+ DESCRIPTION
296
+ dep
297
+ Checks that all dependencies are met.
298
+
299
+ dep add [gemname]
300
+ Fetches the latest version of `gemname`
301
+ and automatically adds it to your .gems file.
302
+
303
+ If you specify `--git` it will take a git repository followed
304
+ by a gemspec path if it's different than the repository's root.
305
+
306
+ If you specify `--github` it will take a github repository
307
+ (e.g. user/repository_name) followed by a gemspec path if it's
308
+ different than the repository's root.
309
+
310
+ If you specify `--source` it will use the given URL as the remote
311
+ source for gems.
312
+
313
+ rm
314
+ Removes the corresponding entry in your .gems file.
315
+
316
+ install
317
+ Installs all the missing dependencies for you. An important
318
+ point here is that it simply does a `gem install` for each
319
+ dependency you have. Dep assumes that you use some form of
320
+ sandboxing like gs, rbenv-gemset or RVM gemsets.
321
+
322
+
323
+ INSTALLATION
324
+ $ wget -qO- http://amakawa.org/sh/install.sh | sh
325
+
326
+ # or
327
+
328
+ $ gem install dep
329
+
330
+ HISTORY
331
+ dep is actually more of a workflow than a tool. If you think about
332
+ package managers and the problem of dependencies, you can summarize
333
+ what you absolutely need from them in just two points:
334
+
335
+ 1. When you build an application which relies on 3rd party libraries,
336
+ it's best to explicitly declare the version numbers of these
337
+ libraries.
338
+
339
+ 2. You can either bundle the specific library version together with
340
+ your application, or you can have a list of versions.
341
+
342
+ The first approach is handled by vendoring the library. The second
343
+ approach typically is done using Bundler. But why do you need such
344
+ a complicated tool when all you need is simply listing version numbers?
345
+
346
+ We dissected what we were doing and eventually reached the following
347
+ workflow:
348
+
349
+ 1. We maintain a .gems file for every application which lists the
350
+ libraries and the version numbers.
351
+ 2. We omit dependencies of dependencies in that file, the reason being
352
+ is that that should already be handled by the package manager
353
+ (typically rubygems).
354
+ 3. Whenever we add a new library, we add the latest version.
355
+ 4. When we pull the latest changes, we want to be able to rapidly
356
+ check if the dependencies we have is up to date and matches what
357
+ we just pulled.
358
+
359
+ So after doing this workflow manually for a while, we decided to
360
+ build the simplest tool to aid us with our workflow.
361
+
362
+ The first point is handled implicitly by dep. You can also specify
363
+ a different file by doing dep -f.
364
+
365
+ The second point is more of an implementation detail. We thought about
366
+ doing dependencies, but then, why re-implement something that's already
367
+ done for you by rubygems?
368
+
369
+ The third point (and also the one which is most inconvenient), is
370
+ handled by dep add.
371
+
372
+ The manual workflow for that would be:
373
+
374
+ gem search -r "^ohm$" [--pre] # check and remember the version number
375
+ echo "ohm -v X.x.x" >> .gems
376
+
377
+ If you try doing that repeatedly, it will quickly become cumbersome.
378
+
379
+ The fourth and final point is handled by typing dep check or simply dep.
380
+ Practically speaking it's just:
381
+
382
+ git pull
383
+ dep
384
+
385
+ And that's it. The dep command typically happens in 0.2 seconds which
386
+ is something we LOVE.
data/test/dep.rb ADDED
@@ -0,0 +1,126 @@
1
+ require "cutest"
2
+
3
+ load File.expand_path("../../bin/dep", __FILE__)
4
+
5
+ # Dep::Lib
6
+ scope do
7
+ test "parsing" do
8
+ lib = Dep::Lib["ohm-contrib -v 1.0.rc1"]
9
+
10
+ assert_equal "ohm-contrib", lib.name
11
+ assert_equal "1.0.rc1", lib.version
12
+
13
+ lib = Dep::Lib["dep --git git://github.com/cyx/dep.git"]
14
+
15
+ assert_equal "dep", lib.name
16
+ assert_equal "git://github.com/cyx/dep.git", lib.uri
17
+
18
+ lib = Dep::Lib["dep --github cyx/dep"]
19
+ assert_equal "dep", lib.name
20
+ assert_equal "cyx/dep", lib.repository
21
+ assert_equal "git://github.com/cyx/dep.git", lib.uri
22
+
23
+ lib = Dep::Lib["ohm-contrib -v 1.0.rc1 --source https://rubygems.org/"]
24
+ assert_equal "ohm-contrib", lib.name
25
+ assert_equal "1.0.rc1", lib.version
26
+ assert_equal "https://rubygems.org/", lib.source
27
+ end
28
+
29
+ test "availability of existing gem" do
30
+ lib = Dep::Lib.new("cutest", "1.2.0")
31
+ assert lib.available?
32
+ end
33
+
34
+ test "non-availability of missing gem" do
35
+ lib = Dep::Lib.new("rails", "3.0")
36
+ assert ! lib.available?
37
+ end
38
+
39
+ test "to_s" do
40
+ lib = Dep::Lib.new("cutest", "1.1.3")
41
+ assert_equal "cutest -v 1.1.3", lib.to_s
42
+ end
43
+ end
44
+
45
+ # Dep::GitLib
46
+ scope do
47
+ test "parsing" do
48
+ lib = Dep::Lib["dep --git git://github.com/cyx/dep.git"]
49
+
50
+ assert_equal "dep", lib.name
51
+ assert_equal "git://github.com/cyx/dep.git", lib.uri
52
+ assert_equal nil, lib.gemspec_path
53
+
54
+ lib = Dep::Lib["dep --git 'git://github.com/cyx/dep.git path/to/gemspec'"]
55
+
56
+ assert_equal "dep", lib.name
57
+ assert_equal "git://github.com/cyx/dep.git", lib.uri
58
+ assert_equal "path/to/gemspec", lib.gemspec_path
59
+ end
60
+
61
+ test "to_s" do
62
+ lib = Dep::GitLib.new("dep", "git://github.com/cyx/dep.git")
63
+ assert_equal "dep --git git://github.com/cyx/dep.git", lib.to_s
64
+
65
+ lib = Dep::GitLib.new("dep", "git://github.com/cyx/dep.git path/to/gemspec")
66
+ assert_equal "dep --git 'git://github.com/cyx/dep.git path/to/gemspec'", lib.to_s
67
+ end
68
+ end
69
+
70
+ # Dep::GitHubLib
71
+ scope do
72
+ test "parsing" do
73
+ lib = Dep::Lib["dep --github cyx/dep"]
74
+
75
+ assert_equal "dep", lib.name
76
+ assert_equal "git://github.com/cyx/dep.git", lib.uri
77
+ assert_equal nil, lib.gemspec_path
78
+ assert_equal "cyx/dep", lib.repository
79
+
80
+ lib = Dep::Lib["dep --github 'cyx/dep path/to/gemspec'"]
81
+
82
+ assert_equal "dep", lib.name
83
+ assert_equal "git://github.com/cyx/dep.git", lib.uri
84
+ assert_equal "path/to/gemspec", lib.gemspec_path
85
+ assert_equal "cyx/dep", lib.repository
86
+ end
87
+
88
+ test "to_s" do
89
+ lib = Dep::GitHubLib.new("dep", "cyx/dep")
90
+ assert_equal "dep --github cyx/dep", lib.to_s
91
+
92
+ lib = Dep::GitHubLib.new("dep", "cyx/dep path/to/gemspec")
93
+ assert_equal "dep --github 'cyx/dep path/to/gemspec'", lib.to_s
94
+ end
95
+ end
96
+
97
+ # Dep::List
98
+ scope do
99
+ setup do
100
+ Dep::List.new(File.expand_path(".gems", File.dirname(__FILE__)))
101
+ end
102
+
103
+ test do |list|
104
+ lib1 = Dep::Lib.new("ohm-contrib", "1.0.rc1")
105
+ lib2 = Dep::Lib.new("cutest", "1.2.0")
106
+ lib3 = Dep::GitLib.new("padrino-performance", "git://github.com/padrino/padrino-framework.git padrino-performance")
107
+ lib4 = Dep::GitHubLib.new("shield", "cyx/shield shield")
108
+
109
+ assert list.libraries.include?(lib1)
110
+ assert list.libraries.include?(lib2)
111
+ assert list.libraries.include?(lib3)
112
+ assert list.libraries.include?(lib4)
113
+
114
+ assert_equal 3, list.missing_libraries.size
115
+ assert list.missing_libraries.include?(lib1)
116
+ assert list.missing_libraries.include?(lib3)
117
+ assert list.missing_libraries.include?(lib4)
118
+ end
119
+
120
+ test do |list|
121
+ list.add(Dep::Lib.new("cutest", "2.0"))
122
+
123
+ assert ! list.libraries.include?(Dep::Lib.new("cutest", "1.1.3"))
124
+ assert list.libraries.include?(Dep::Lib.new("cutest", "2.0"))
125
+ end
126
+ end
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: turbodep
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Francesco Rodriguez
8
+ - Cyril David
9
+ - Michel Martens
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2013-05-22 00:00:00.000000000 Z
14
+ dependencies: []
15
+ description: Adds support for gems on git/github repositories or different sources
16
+ to dep
17
+ email:
18
+ - lrodriguezsanc@gmail.com
19
+ executables:
20
+ - dep
21
+ extensions: []
22
+ extra_rdoc_files: []
23
+ files:
24
+ - bin/dep
25
+ - test/dep.rb
26
+ homepage: https://github.com/frodsan/turbodep
27
+ licenses:
28
+ - MIT
29
+ metadata: {}
30
+ post_install_message:
31
+ rdoc_options: []
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ required_rubygems_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ requirements: []
45
+ rubyforge_project:
46
+ rubygems_version: 2.0.2
47
+ signing_key:
48
+ specification_version: 4
49
+ summary: Turbo-dependencies manager
50
+ test_files: []