roll 0.8.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.
- data/LICENSE +408 -0
- data/README +17 -0
- data/bin/roll +44 -0
- data/demo/bench/bench_load.rb +7 -0
- data/demo/sample/inspect.rb +12 -0
- data/demo/sample/tryme/1.0/lib/tryme.rb +1 -0
- data/demo/sample/tryme/1.1/lib/tryme.rb +1 -0
- data/lib/oll.rb +2 -0
- data/lib/roll/attributes.rb +72 -0
- data/lib/roll/library.rb +635 -0
- data/lib/roll/package.rb +300 -0
- data/lib/roll/remote.rb +37 -0
- data/lib/roll/version.rb +127 -0
- data/meta/ROLL-0.8.0.roll +21 -0
- data/task/rdoc +9 -0
- data/task/setup +1616 -0
- data/task/test +5 -0
- data/test/test_library.rb +10 -0
- data/test/test_version.rb +71 -0
- metadata +79 -0
data/lib/roll/package.rb
ADDED
@@ -0,0 +1,300 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'roll/attributes'
|
3
|
+
|
4
|
+
module Roll
|
5
|
+
|
6
|
+
class Package
|
7
|
+
include Attributes
|
8
|
+
|
9
|
+
# Read package information from a YAML file.
|
10
|
+
def self.open(file=nil, options={})
|
11
|
+
unless file
|
12
|
+
file = Dir.glob(filename, File::FNM_CASEFOLD).first
|
13
|
+
raise "Manifest file is required." unless file
|
14
|
+
end
|
15
|
+
data = YAML::load(File.open(file))
|
16
|
+
data.update(options)
|
17
|
+
data.update(:file => file)
|
18
|
+
new(data)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Possible file name (was for Fileable).
|
22
|
+
def self.filename
|
23
|
+
'{,meta/}*.roll'
|
24
|
+
end
|
25
|
+
|
26
|
+
# New Package. Pass in a data hash to populate the object.
|
27
|
+
# TODO Support self setter block?
|
28
|
+
def initialize(data={}) #, &yld)
|
29
|
+
data.each do |k,v|
|
30
|
+
send( "#{k}=", v ) rescue nil
|
31
|
+
end
|
32
|
+
#if yld
|
33
|
+
# yld.to_h.each do |k,v|
|
34
|
+
# send( "#{k}=", v ) rescue nil
|
35
|
+
# end
|
36
|
+
#end
|
37
|
+
|
38
|
+
base = File.basename(file).chomp('.roll')
|
39
|
+
if base.index('-') # Just in case you want to load ir from a non-conforming file.
|
40
|
+
name, version = base.split('-')
|
41
|
+
name = name.downcase # TODO Is this too restrictive?
|
42
|
+
@name = name
|
43
|
+
@version = version
|
44
|
+
end
|
45
|
+
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
# Name of the package file (if used).
|
50
|
+
attr_accessor :file
|
51
|
+
|
52
|
+
# Path to the project file (if used).
|
53
|
+
def location
|
54
|
+
File.dirname(@file) if @file
|
55
|
+
end
|
56
|
+
|
57
|
+
# Indicates if this project information was read from a file.
|
58
|
+
# Returns the file's name if so.
|
59
|
+
def read? ; file ; end
|
60
|
+
|
61
|
+
#---------------------#
|
62
|
+
# GENERAL INFORMATION #
|
63
|
+
#---------------------#
|
64
|
+
|
65
|
+
# The title of the project (free-form, defaults to name).
|
66
|
+
attr_accessor :title
|
67
|
+
|
68
|
+
# One-line description of the package (Max 60 chars.)
|
69
|
+
attr_accessor :subtitle, :brief
|
70
|
+
|
71
|
+
# More detailed description of the package.
|
72
|
+
attr_accessor :description, :summary
|
73
|
+
|
74
|
+
# "Unix" name of the project.
|
75
|
+
attr_accessor :name, :project
|
76
|
+
|
77
|
+
# The date the project was started.
|
78
|
+
attr_accessor :created
|
79
|
+
|
80
|
+
# Copyright notice.
|
81
|
+
attr_accessor :copyright
|
82
|
+
|
83
|
+
# Distribution License.
|
84
|
+
attr_accessor :license
|
85
|
+
|
86
|
+
# Project slogan or "trademark" phrase.
|
87
|
+
attr_accessor :slogan
|
88
|
+
|
89
|
+
# General one-word software category.
|
90
|
+
attr_accessor :category
|
91
|
+
|
92
|
+
# Author(s) of this project.
|
93
|
+
# (Usually in "name <email>" format.)
|
94
|
+
attr_accessor :author
|
95
|
+
|
96
|
+
# Contact(s) (defaults to authors).
|
97
|
+
attr_accessor :contact
|
98
|
+
|
99
|
+
# Gerenal Email address (defaults to first contact's address, if given).
|
100
|
+
attr_accessor :email
|
101
|
+
|
102
|
+
# Official domain associated with this package.
|
103
|
+
attr_accessor :domain
|
104
|
+
|
105
|
+
# Project's homepage.
|
106
|
+
attr_accessor :homepage
|
107
|
+
|
108
|
+
# Project's development site.
|
109
|
+
attr_accessor :development, :devsite
|
110
|
+
|
111
|
+
# Internet address(es) to documentation pages.
|
112
|
+
attr_accessor :documentation, :docs
|
113
|
+
|
114
|
+
# Internet address(es) to downloadable packages.
|
115
|
+
attr_accessor :download
|
116
|
+
|
117
|
+
# Internet address for project wiki.
|
118
|
+
attr_accessor :wiki
|
119
|
+
|
120
|
+
# Project's mailing list or other contact email.
|
121
|
+
attr_accessor :list
|
122
|
+
|
123
|
+
# Generate documentation on installation?
|
124
|
+
attr_accessor :document
|
125
|
+
|
126
|
+
def name ; @name || project ; end
|
127
|
+
def title ; @title || project ; end
|
128
|
+
def contact ; @contact || author ; end
|
129
|
+
def email ; @email || contact ; end
|
130
|
+
def license ; @license || 'Ruby' ; end
|
131
|
+
|
132
|
+
# Subtitle is limited to 60 characters.
|
133
|
+
def subtitle ; @subtitle.to_s[0..59] ; end
|
134
|
+
|
135
|
+
# Returns a standard taguri id for the library and release.
|
136
|
+
def project_taguri
|
137
|
+
"tag:#{name}.#{domain},#{created}" # or released?
|
138
|
+
end
|
139
|
+
|
140
|
+
# Version Information
|
141
|
+
|
142
|
+
module Version
|
143
|
+
include Attributes
|
144
|
+
|
145
|
+
# Version number (eg. '1.0.0').
|
146
|
+
attr_accessor :version
|
147
|
+
|
148
|
+
# Status of this release: alpha, beta, RC1, etc.
|
149
|
+
attr_accessor :status
|
150
|
+
|
151
|
+
# Date of release (defaults to Time.now).
|
152
|
+
attr_accessor :released
|
153
|
+
|
154
|
+
# Build number (if true, defaults to a number based on current date-time).
|
155
|
+
attr_accessor :buildno
|
156
|
+
|
157
|
+
# Current release code name.
|
158
|
+
attr_accessor :codename
|
159
|
+
|
160
|
+
# If buildno is set to true, than returns a time stamp.
|
161
|
+
def buildno
|
162
|
+
bn = stamp.buildno if stamp
|
163
|
+
unless bn
|
164
|
+
if TrueClass === @buildno
|
165
|
+
bn = Time.now.strftime("%H*60+%M")
|
166
|
+
else
|
167
|
+
bn = @buildno
|
168
|
+
end
|
169
|
+
end
|
170
|
+
return bn
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
include Version
|
175
|
+
|
176
|
+
# Content Descriptions
|
177
|
+
|
178
|
+
module Contents
|
179
|
+
include Attributes
|
180
|
+
|
181
|
+
# Files in this package that are executables.
|
182
|
+
attr_accessor :executable, :executables
|
183
|
+
|
184
|
+
# Library files in this package that are *public*.
|
185
|
+
# This is akin to load_path but specifies specific files
|
186
|
+
# that can be loaded from the outside --where as those
|
187
|
+
# not listed are considerd *private*.
|
188
|
+
#
|
189
|
+
# NOTE: This is not enforced --and may never be. It
|
190
|
+
# complicates library loading. Ie. how to distinguish public
|
191
|
+
# loading from external loading. But it something that can be
|
192
|
+
# consider more carfully in the future. For now it can serve
|
193
|
+
# as an optional reference.
|
194
|
+
attr_accessor :library, :libraries
|
195
|
+
|
196
|
+
# Location(s) of executables.
|
197
|
+
attr_accessor :bin_path, :bin_paths
|
198
|
+
|
199
|
+
# Root location(s) of libraries (used by Rolls).
|
200
|
+
# If you plan to support Gems, this would be something like:
|
201
|
+
#
|
202
|
+
# 'lib/facets'
|
203
|
+
#
|
204
|
+
# If not, then the default ('lib') is nice b/c it means one less
|
205
|
+
# layer in your project heirarchy.
|
206
|
+
attr_accessor :lib_path, :lib_paths, :load_path, :load_paths
|
207
|
+
|
208
|
+
# Traditional load path (used by RubyGems).
|
209
|
+
# The default is 'lib', which is usually fine.
|
210
|
+
attr_accessor :gem_path, :gem_paths
|
211
|
+
|
212
|
+
# Default lib to load when requiring only on a package name. Eg.
|
213
|
+
#
|
214
|
+
# require 'facets'
|
215
|
+
#
|
216
|
+
attr_accessor :index_library
|
217
|
+
|
218
|
+
# Root location(s) of libraries.
|
219
|
+
#--
|
220
|
+
# TODO This is an intersting idea. Instead of fixed locations in
|
221
|
+
# the file system one could register "virtual locations" which map
|
222
|
+
# to real locations. Worth the added flexability?
|
223
|
+
#++
|
224
|
+
#attr_accessor :register
|
225
|
+
|
226
|
+
def executable ; [@executable || 'bin/**/*'].flatten ; end
|
227
|
+
def library ; [@library || 'lib/**/*'].flatten ; end
|
228
|
+
|
229
|
+
def gem_path ; [@gem_path || 'lib'].flatten ; end
|
230
|
+
def lib_path ; [@lib_path || 'lib'].flatten ; end
|
231
|
+
|
232
|
+
def index_library ; @index_library || 'index.rb' ; end
|
233
|
+
|
234
|
+
#def register ; @register || { name => 'lib' } ; end
|
235
|
+
end
|
236
|
+
|
237
|
+
include Contents
|
238
|
+
|
239
|
+
# Security Information
|
240
|
+
|
241
|
+
module Security
|
242
|
+
include Attributes
|
243
|
+
|
244
|
+
# Encryption digest type used.
|
245
|
+
# (md5, sha1, sha128, sha256, sha512).
|
246
|
+
attr_accessor :digest
|
247
|
+
|
248
|
+
# Public key file associated with this library. This is useful
|
249
|
+
# for security purposes especially remote loading. [pubkey.pem]
|
250
|
+
attr_accessor :public_key
|
251
|
+
|
252
|
+
# Private key file associated with this library. This is useful
|
253
|
+
# for security purposes especially remote loading. [_privkey.pem]
|
254
|
+
attr_accessor :private_key
|
255
|
+
|
256
|
+
def digest ; @digest || 'md5' ; end
|
257
|
+
def public_key ; @public_key || 'pubkey.pem' ; end
|
258
|
+
def private_key ; @private_key || '_privkey.pem' ; end
|
259
|
+
end
|
260
|
+
|
261
|
+
include Security
|
262
|
+
|
263
|
+
# Source Control Managment Information
|
264
|
+
|
265
|
+
module Control
|
266
|
+
include Attributes
|
267
|
+
|
268
|
+
# Specifices the type of revision control system used.
|
269
|
+
# darcs, svn, cvs, etc.
|
270
|
+
attr_accessor :scm
|
271
|
+
|
272
|
+
# Files that are tracked under revision control.
|
273
|
+
# Default is all less standard exceptions.
|
274
|
+
# '+' and '-' prefixes can be used to augment the list
|
275
|
+
# rather than fully override it.
|
276
|
+
attr_accessor :track, :scm_files
|
277
|
+
|
278
|
+
# Internet address to source code repository.
|
279
|
+
# (http://, ftp://, etc.)
|
280
|
+
attr_accessor :repository, :repo
|
281
|
+
|
282
|
+
# Changelog file.
|
283
|
+
attr_accessor :changelog
|
284
|
+
end
|
285
|
+
|
286
|
+
include Control
|
287
|
+
|
288
|
+
# Validation
|
289
|
+
|
290
|
+
validate "name is required" do
|
291
|
+
name
|
292
|
+
end
|
293
|
+
|
294
|
+
validate "version is required" do
|
295
|
+
version
|
296
|
+
end
|
297
|
+
|
298
|
+
end
|
299
|
+
|
300
|
+
end
|
data/lib/roll/remote.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module Roll
|
2
|
+
|
3
|
+
# Remote install cache (hmmm...should this be optional feature?)
|
4
|
+
REMOTE_CACHE = File.expand_path( '~/.lib/site_ruby/1.8/' )
|
5
|
+
FileUtils.mkdir_p REMOTE_CACHE unless File.directory? REMOTE_CACHE
|
6
|
+
$:.unshift REMOTE_CACHE
|
7
|
+
|
8
|
+
#
|
9
|
+
|
10
|
+
def remote_install(fname)
|
11
|
+
# Bit of a shortcoming here since it's not very efficient to
|
12
|
+
# be searching a remote location for multiple matches.
|
13
|
+
# .so suffix must be specified explicity on the remote end.
|
14
|
+
fname = fname + '.rb' unless fname =~ /\.rb$/ or fname =~ /\.so$/
|
15
|
+
|
16
|
+
# get signiture
|
17
|
+
url = File.join( source, 'meta', 'signitures', fname )
|
18
|
+
$stderr << "remote signiture -- " + url if $DEBUG
|
19
|
+
sig = URI.parse( url ).read
|
20
|
+
|
21
|
+
# get file
|
22
|
+
url = File.join( source, fname )
|
23
|
+
$stderr << "remote file -- " + url if $DEBUG
|
24
|
+
file = URI.parse( url ).read
|
25
|
+
|
26
|
+
# verify file and signiture
|
27
|
+
if Signer.verify?( pubkey, sig, file )
|
28
|
+
infile = File.join( REMOTE_CACHE, fname )
|
29
|
+
indir = File.dirname( infile )
|
30
|
+
FileUtils.mkdir_p indir
|
31
|
+
File.open( infile, 'w' ) { |f| f << file }
|
32
|
+
else
|
33
|
+
raise
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
data/lib/roll/version.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
# TITLE:
|
2
|
+
#
|
3
|
+
# VersionNumber
|
4
|
+
#
|
5
|
+
# DESCRIPTION:
|
6
|
+
#
|
7
|
+
# VersionNumber is a simplified form of a Tuple class
|
8
|
+
# desgined specifically for dealing with version numbers.
|
9
|
+
#
|
10
|
+
# AUTHOR:
|
11
|
+
#
|
12
|
+
# - Thomas Sawyer (7rans)
|
13
|
+
#
|
14
|
+
# LICENSE:
|
15
|
+
#
|
16
|
+
# Copyright (c) 2005 Thomas Sawyer
|
17
|
+
#
|
18
|
+
# Ruby License
|
19
|
+
#
|
20
|
+
# This module is free software. You may use, modify, and/or redistribute this
|
21
|
+
# software under the same terms as Ruby.
|
22
|
+
#
|
23
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
24
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
25
|
+
# FOR A PARTICULAR PURPOSE.
|
26
|
+
#
|
27
|
+
# TODO:
|
28
|
+
#
|
29
|
+
# - Maybe add Kernel method #version ?
|
30
|
+
#
|
31
|
+
# - If Tuple were standard part of Ruby this probably would
|
32
|
+
# not be needed or at least might be simple sublcass instead.
|
33
|
+
|
34
|
+
|
35
|
+
# = VersionNumber
|
36
|
+
#
|
37
|
+
# VersionNumber is a simplified form of a Tuple class
|
38
|
+
# desgined specifically for dealing with version numbers.
|
39
|
+
#
|
40
|
+
class VersionNumber #< Tuple
|
41
|
+
|
42
|
+
#include Enumerable
|
43
|
+
include Comparable
|
44
|
+
|
45
|
+
def initialize( *args )
|
46
|
+
args = args.join('.').split(/\W+/)
|
47
|
+
@self = args.collect { |i| i.to_i }
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_s ; @self.join('.') ; end
|
51
|
+
|
52
|
+
# This is here only becuase File.join calls it instead of to_s.
|
53
|
+
|
54
|
+
def to_str ; @self.join('.') ; end
|
55
|
+
|
56
|
+
def inspect
|
57
|
+
@self.to_s
|
58
|
+
end
|
59
|
+
|
60
|
+
def [](i)
|
61
|
+
@self.fetch(i,0)
|
62
|
+
end
|
63
|
+
|
64
|
+
# "Spaceship" comparsion operator.
|
65
|
+
|
66
|
+
def <=>( other )
|
67
|
+
#other = other.to_t
|
68
|
+
[@self.size, other.size].max.times do |i|
|
69
|
+
c = @self[i] <=> other[i]
|
70
|
+
return c if c != 0
|
71
|
+
end
|
72
|
+
0
|
73
|
+
end
|
74
|
+
|
75
|
+
# For pessimistic constraint (like '~>' in gems)
|
76
|
+
|
77
|
+
def =~( other )
|
78
|
+
#other = other.to_t
|
79
|
+
upver = other.dup
|
80
|
+
upver[0] += 1
|
81
|
+
@self >= other and @self < upver
|
82
|
+
end
|
83
|
+
|
84
|
+
# Major is the first number in the version series.
|
85
|
+
|
86
|
+
def major ; @self[0] ; end
|
87
|
+
|
88
|
+
# Minor is the second number in the version series.
|
89
|
+
|
90
|
+
def minor ; @self[1] || 0 ; end
|
91
|
+
|
92
|
+
# Teeny is third number in the version series.
|
93
|
+
|
94
|
+
def teeny ; @self[2] || 0 ; end
|
95
|
+
|
96
|
+
# Delegate to the array.
|
97
|
+
|
98
|
+
def method_missing( sym, *args, &blk )
|
99
|
+
@self.send(sym, *args, &blk ) rescue super
|
100
|
+
end
|
101
|
+
|
102
|
+
# Parses a string constraint returning the operation as a lambda.
|
103
|
+
|
104
|
+
def self.constraint_lambda( constraint )
|
105
|
+
op, val = *parse_constraint( constraint )
|
106
|
+
lambda { |t| t.send(op, val) }
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.parse_constraint( constraint )
|
110
|
+
constraint = constraint.strip
|
111
|
+
re = %r{^(=~|~>|<=|>=|==|=|<|>)?\s*(\d+(:?[-.]\d+)*)$}
|
112
|
+
if md = re.match( constraint )
|
113
|
+
if op = md[1]
|
114
|
+
op = '=~' if op == '~>'
|
115
|
+
op = '==' if op == '='
|
116
|
+
val = new( *md[2].split(/\W+/) )
|
117
|
+
else
|
118
|
+
op = '=='
|
119
|
+
val = new( *constraint.split(/\W+/) )
|
120
|
+
end
|
121
|
+
else
|
122
|
+
raise ArgumentError, "invalid constraint"
|
123
|
+
end
|
124
|
+
return op, val
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|