dep-cj 1.2.0

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