dep-cj 1.2.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 +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: