library 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,398 @@
1
+ class Library
2
+
3
+ # The Metadata call encapsulates a library's information, in particular
4
+ # `name`, `version` and `load_path`.
5
+ #
6
+ class Metadata
7
+
8
+ #
9
+ # Setup new metadata object.
10
+ #
11
+ # @param [String] location
12
+ # Location of project on disc.
13
+ #
14
+ # @param [Hash] metadata
15
+ # Manual metadata settings.
16
+ #
17
+ # @option metadata [Boolean] :load
18
+ # Set to +false+ will prevent metadata being loaded
19
+ # from .ruby or .gemspec file, but a LoadError will
20
+ # be raised without `:name` and `:version`.
21
+ #
22
+ def initialize(location, metadata={})
23
+ @location = location
24
+
25
+ @load = metadata.delete(:load)
26
+ @load = true if @load.nil? # default is true
27
+
28
+ @data = {}
29
+
30
+ update(metadata)
31
+
32
+ if @load
33
+ if not (@data['name'] && @data['version'] && @data['load_path'])
34
+ load_metadata
35
+ end
36
+ else
37
+ raise LoadError unless data['name'] && data['version'] # todo: just name ?
38
+ end
39
+ end
40
+
41
+ #
42
+ # Update metadata with data hash.
43
+ #
44
+ # @param [Hash] data
45
+ # Data to merge into metadata table.
46
+ #
47
+ def update(data)
48
+ data = data.rekey
49
+
50
+ @data.update(data)
51
+
52
+ self.name = data[:name] if data[:name]
53
+ self.version = data[:version] if data[:version]
54
+ self.load_path = data[:load_path] if data[:load_path]
55
+ self.date = data[:date] if data[:date]
56
+ self.omit = data[:omit]
57
+ end
58
+
59
+ #
60
+ # Location of library.
61
+ #
62
+ attr :location
63
+
64
+ #
65
+ # Local load paths.
66
+ #
67
+ def load_path
68
+ @data[:load_path] || ['lib']
69
+ end
70
+
71
+ alias_method :loadpath, :load_path
72
+
73
+ #
74
+ # Set the loadpath.
75
+ #
76
+ def load_path=(path)
77
+ case path
78
+ when nil
79
+ path = ['lib']
80
+ when String
81
+ path = path.strip.split(/[,;:\ \n\t]/).map{|s| s.strip}
82
+ end
83
+ @data[:load_path] = path
84
+ end
85
+
86
+ alias_method :loadpath=, :load_path=
87
+
88
+ #
89
+ # Name of library.
90
+ #
91
+ def name
92
+ @data[:name]
93
+ end
94
+
95
+ #
96
+ # Set name.
97
+ #
98
+ def name=(string)
99
+ @data[:name] = string.to_s if string
100
+ end
101
+
102
+ #
103
+ # Version number.
104
+ #
105
+ # Technically, a library should not appear in a ledger list if it lacks
106
+ # a version. However, just in case this occurs (say by a hand edited
107
+ # environment) we fallback to a version of '0.0.0'.
108
+ #
109
+ def version
110
+ @data[:version] ||= Version.new('0.0.0')
111
+ end
112
+
113
+ #
114
+ # Set version, converts string into Version number class.
115
+ #
116
+ def version=(string)
117
+ @data[:version] = Version.new(string) if string
118
+ end
119
+
120
+ #
121
+ # Release date.
122
+ #
123
+ def date
124
+ @data[:date] || (load_metadata; @data[:data])
125
+ end
126
+
127
+ alias_method :released, :date
128
+
129
+ # TODO: Should we convert date to Time object?
130
+
131
+ #
132
+ # Set the date.
133
+ #
134
+ def date=(date)
135
+ @data[:date] = date
136
+ end
137
+
138
+ alias_method :released=, :date=
139
+
140
+ #
141
+ # Runtime and development requirements combined.
142
+ #
143
+ def requirements
144
+ @data[:requirements] || (load_metadata; @data[:requirements])
145
+ end
146
+
147
+ #
148
+ # Runtime requirements.
149
+ #
150
+ def runtime_requirements
151
+ @runtime_requirements ||= requirements.reject{ |r| r['development'] }
152
+ end
153
+
154
+ #
155
+ # Development requirements.
156
+ #
157
+ def development_requirements
158
+ @development_requirements ||= requirements.select{ |r| r['development'] }
159
+ end
160
+
161
+ #
162
+ # Access to non-primary metadata.
163
+ #
164
+ def [](name)
165
+ @data[name.to_sym]
166
+ end
167
+
168
+ # TODO: Should we support +omit+ setting, or should we add a way to
169
+ # exclude loctions via environment setting?
170
+
171
+ #
172
+ # Omit from any ledger?
173
+ #
174
+ def omit?
175
+ @omit
176
+ end
177
+
178
+ #
179
+ # Set omit.
180
+ #
181
+ def omit=(boolean)
182
+ @omit = boolean
183
+ end
184
+
185
+ #
186
+ # Does this location have .ruby entries?
187
+ #
188
+ def dotruby?
189
+ @_dotruby ||= File.exist?(File.join(location, '.ruby'))
190
+ end
191
+
192
+ #
193
+ # Deterime if the location is a gem location. It does this by looking
194
+ # for the corresponding `gems/specification/*.gemspec` file.
195
+ #
196
+ def gemspec?
197
+ #return true if Dir[File.join(location, '*.gemspec')].first
198
+ pkgname = File.basename(location)
199
+ gemsdir = File.dirname(location)
200
+ specdir = File.join(File.dirname(gemsdir), 'specifications')
201
+ Dir[File.join(specdir, "#{pkgname}.gemspec")].first
202
+ end
203
+
204
+ #
205
+ # Access to complete gemspec. This is for use with extended metadata.
206
+ #
207
+ def gemspec
208
+ @_gemspec ||= (
209
+ require 'rubygems'
210
+ ::Gem::Specification.load(gemspec_file)
211
+ )
212
+ end
213
+
214
+ #
215
+ # Verify that a library's requirements are all available in the ledger.
216
+ # Returns a list of `[name, version]` of Libraries that were not found.
217
+ #
218
+ # @return [Array<String,String>] List of missing requirements.
219
+ #
220
+ def missing_requirements(development=false) #verbose=false)
221
+ libs, fail = [], []
222
+ reqs = development ? requirements : runtime_requirements
223
+ reqs.each do |req|
224
+ name = req['name']
225
+ vers = req['version']
226
+ lib = Library[name, vers]
227
+ if lib
228
+ libs << lib
229
+ #$stdout.puts " [LOAD] #{name} #{vers}" if verbose
230
+ unless libs.include?(lib) or fail.include?([lib,vers])
231
+ lib.verify_requirements(development) #verbose)
232
+ end
233
+ else
234
+ fail << [name, vers]
235
+ #$stdout.puts " [FAIL] #{name} #{vers}" if verbose
236
+ end
237
+ end
238
+ return fail
239
+ end
240
+
241
+ #
242
+ # Like {#missing_requirements} but returns `true`/`false`.
243
+ #
244
+ def missing_requirements?(development=false)
245
+ list = missing_requirements(development=false)
246
+ list.empty? ? false : true
247
+ end
248
+
249
+ #
250
+ # Returns hash of primary metadata.
251
+ #
252
+ # @return [Hash] primary metdata
253
+ #
254
+ def to_h
255
+ { 'location' => location,
256
+ 'name' => name,
257
+ 'version' => version.to_s,
258
+ 'date' => date.to_s,
259
+ 'load_path' => load_path,
260
+ 'requirements' => requirements,
261
+ 'omit' => omit
262
+ }
263
+ end
264
+
265
+ private
266
+
267
+ # Load metadata.
268
+ def load_metadata
269
+ return unless @load
270
+
271
+ if dotruby?
272
+ load_dotruby
273
+ elsif gemspec?
274
+ load_gemspec
275
+ end
276
+
277
+ @load = false # loading is complete
278
+ end
279
+
280
+ # Load metadata for .ruby file.
281
+ def load_dotruby
282
+ require 'yaml'
283
+
284
+ data = YAML.load_file(File.join(location, '.ruby'))
285
+
286
+ update(data)
287
+
288
+ #if Hash === data
289
+ # self.name = data['name']
290
+ # self.version = data['version'] #|| '0.0.0'
291
+ # self.load_path = data['load_path'] || ['lib']
292
+ #
293
+ # self.title = data['title'] || data['name'].capitalize
294
+ # self.date = data['date']
295
+ #
296
+ # reqs = data['requirements'] || []
297
+ # reqs.each do |req|
298
+ # if req['development']
299
+ # self.development_requirements << [req['name'], req['version']]
300
+ # else
301
+ # self.runtime_requirements << [req['name'], req['version']]
302
+ # end
303
+ # end
304
+ #end
305
+ end
306
+
307
+ # Load metadata from a gemspec. This is a fallback option. It is highly
308
+ # recommended that a project have a `.ruby` file instead.
309
+ #
310
+ # This method requires that the `dotruby` gem be installed.
311
+ #
312
+ def load_gemspec
313
+ #require 'rubygems'
314
+ require 'dotruby/rubygems'
315
+
316
+ text = File.read(gemspec_file)
317
+ if text =~ /\A---/ # TODO: improve
318
+ require 'yaml'
319
+ spec = YAML.load(text)
320
+ else
321
+ spec = eval(text)
322
+ end
323
+
324
+ dotruby = DotRuby::Spec.parse_gemspec(spec)
325
+
326
+ data = dotruby.to_h
327
+
328
+ udpate(data)
329
+
330
+ return
331
+
332
+ #self.name = spec.name
333
+ #self.version = spec.version.to_s
334
+ #self.load_path = spec.require_paths
335
+ #self.date = spec.date
336
+ #self.title = spec.name.capitalize # for lack of better way
337
+
338
+ #spec.dependencies.each do |dep|
339
+ # if dep.development?
340
+ # self.development_requirements < [dep.name, dep.version]
341
+ # else
342
+ # self.runtime_requirements < [dep.name, dep.verison]
343
+ # end
344
+ #end
345
+ end
346
+
347
+ #
348
+ # Returns the path to the .gemspec file.
349
+ #
350
+ def gemspec_file
351
+ gemspec_system_file || gemspec_local_file
352
+ end
353
+
354
+ #
355
+ # Returns the path to a gemspec file located in the project location,
356
+ # if it exists. Otherwise returns +nil+.
357
+ #
358
+ def gemspec_file_local
359
+ @_gemspec_file_local ||= Dir[File.join(location, '*.gemspec')].first
360
+ end
361
+
362
+ #
363
+ # Returns the path to a gemspec file located in the gems/specifications
364
+ # directory, if it exists. Otherwise returns +nil+.
365
+ #
366
+ def gemspec_file_system
367
+ @_gemspec_file_system ||= (
368
+ pkgname = File.basename(location)
369
+ gemsdir = File.dirname(location)
370
+ specdir = File.join(File.dirname(gemsdir), 'specifications')
371
+ Dir[File.join(specdir, "#{pkgname}.gemspec")].first
372
+ )
373
+ end
374
+
375
+ end
376
+
377
+ end
378
+
379
+
380
+
381
+ # Fake
382
+ #module Gem
383
+ # class Specification < Hash
384
+ # def initialize
385
+ # yield(self)
386
+ # end
387
+ # def method_missing(s,v=nil,*a,&b)
388
+ # case s.to_s
389
+ # when /=$/
390
+ # self[s.to_s.chomp('=').to_sym] = v
391
+ # else
392
+ # self[s]
393
+ # end
394
+ # end
395
+ # end
396
+ #end
397
+
398
+
@@ -0,0 +1,65 @@
1
+ require 'rbconfig'
2
+
3
+ module ::RbConfig
4
+
5
+ #
6
+ # Return the path to the data directory associated with the given
7
+ # library name.
8
+ #
9
+ # Normally this is just:
10
+ #
11
+ # "#{Config::CONFIG['datadir']}/#{name}"
12
+ #
13
+ # But it may be modified by packages like RubyGems and Rolls to handle
14
+ # versioned data directories.
15
+ #
16
+ def self.datadir(name, versionless=false)
17
+ if lib = Library.instance(name)
18
+ lib.datadir(versionless)
19
+ elsif defined?(super)
20
+ super(name)
21
+ else
22
+ File.join(CONFIG['datadir'], name)
23
+ end
24
+ end
25
+
26
+ #
27
+ # Return the path to the configuration directory.
28
+ #
29
+ def self.confdir(name)
30
+ if lib = Roll::Library.instance(name)
31
+ lib.confdir
32
+ else
33
+ File.join(CONFIG['confdir'], name)
34
+ end
35
+ end
36
+
37
+ #
38
+ # Patterns used to identiy a Windows platform.
39
+ #
40
+ WIN_PATTERNS = [
41
+ /bccwin/i,
42
+ /cygwin/i,
43
+ /djgpp/i,
44
+ /mingw/i,
45
+ /mswin/i,
46
+ /wince/i,
47
+ ]
48
+
49
+ #WINDOWS_PLATFORM = !!WIN_PATTERNS.find{ |r| RUBY_PLATFORM =~ r }
50
+
51
+ #
52
+ # Is this a windows platform? This method compares the entires
53
+ # in +WIN_PATTERNS+ against +RUBY_PLATFORM+.
54
+ #
55
+ def self.windows_platform?
56
+ case RUBY_PLATFORM
57
+ when *WIN_PATTERNS
58
+ true
59
+ else
60
+ false
61
+ end
62
+ end
63
+
64
+ end
65
+