stickler 0.1.1 → 2.0.0a
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY.rdoc +5 -2
- data/Rakefile +31 -0
- data/examples/config.ru +9 -0
- data/examples/gemcutter_repo.ru +19 -0
- data/examples/index_repo.ru +15 -0
- data/examples/local_repo.ru +14 -0
- data/examples/mirror_repo.ru +16 -0
- data/examples/not_found.ru +8 -0
- data/lib/stickler/error.rb +3 -0
- data/lib/stickler/middleware/compression.rb +30 -0
- data/lib/stickler/middleware/gemcutter.rb +62 -0
- data/lib/stickler/middleware/helpers.rb +84 -0
- data/lib/stickler/middleware/index.rb +137 -0
- data/lib/stickler/middleware/local.rb +38 -0
- data/lib/stickler/middleware/mirror.rb +60 -0
- data/lib/stickler/middleware/not_found.rb +62 -0
- data/lib/stickler/middleware.rb +4 -0
- data/lib/stickler/repository/api.rb +167 -0
- data/lib/stickler/repository/index.rb +97 -0
- data/lib/stickler/repository/local.rb +251 -0
- data/lib/stickler/repository/mirror.rb +48 -0
- data/lib/stickler/repository/null.rb +58 -0
- data/lib/stickler/repository/remote.rb +235 -0
- data/lib/stickler/repository.rb +7 -499
- data/lib/stickler/spec_lite.rb +60 -14
- data/lib/stickler/version.rb +6 -6
- data/lib/stickler/web.rb +19 -0
- data/spec/data/gems/bar-1.0.0.gem +0 -0
- data/spec/data/gems/foo-1.0.0.gem +0 -0
- data/spec/data/specifications/bar-1.0.0.gemspec +31 -0
- data/spec/data/specifications/foo-1.0.0.gemspec +31 -0
- data/spec/middleware/common_gem_server_helpers.rb +67 -0
- data/spec/middleware/index_spec.rb +26 -0
- data/spec/middleware/legacy_gem_server_behavior.rb +33 -0
- data/spec/middleware/local_spec.rb +25 -0
- data/spec/middleware/modern_gem_server_behavior.rb +20 -0
- data/spec/middleware/not_found_spec.rb +25 -0
- data/spec/repository/api_behavior.rb +162 -0
- data/spec/repository/api_spec.rb +38 -0
- data/spec/repository/index_spec.rb +32 -0
- data/spec/repository/local_spec.rb +36 -0
- data/spec/repository/null_spec.rb +17 -0
- data/spec/repository/remote_spec.rb +49 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +15 -3
- data/spec/spec_lite_spec.rb +62 -0
- data/stickler.gemspec +60 -0
- data/views/index.erb +19 -0
- data/views/layout.erb +39 -0
- metadata +93 -63
- data/COPYING +0 -339
- data/bin/stickler +0 -58
- data/data/stickler.yml +0 -14
- data/gemspec.rb +0 -62
- data/lib/stickler/cli.rb +0 -302
- data/lib/stickler/configuration.rb +0 -74
- data/lib/stickler/console.rb +0 -72
- data/lib/stickler/paths.rb +0 -62
- data/lib/stickler/source.rb +0 -75
- data/lib/stickler/source_group.rb +0 -365
- data/lib/stickler.rb +0 -19
- data/spec/configuration_spec.rb +0 -68
- data/spec/paths_spec.rb +0 -25
- data/spec/repository_spec.rb +0 -55
- data/spec/version_spec.rb +0 -17
- data/tasks/announce.rake +0 -39
- data/tasks/config.rb +0 -107
- data/tasks/distribution.rake +0 -45
- data/tasks/documentation.rake +0 -31
- data/tasks/rspec.rake +0 -29
- data/tasks/rubyforge.rake +0 -51
- data/tasks/utils.rb +0 -80
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'stickler/repository'
|
2
|
+
require 'stickler/spec_lite'
|
3
|
+
|
4
|
+
module Stickler::Repository
|
5
|
+
#
|
6
|
+
# The API that all Stickler Repository classes MUST implement.
|
7
|
+
# This file is here to document the API
|
8
|
+
#
|
9
|
+
module Api
|
10
|
+
#
|
11
|
+
# :call-seq:
|
12
|
+
# repo.uri -> URI
|
13
|
+
#
|
14
|
+
# Return the URI of the repo
|
15
|
+
#
|
16
|
+
def uri
|
17
|
+
raise NotImplementedError, not_implemented_msg( :uri )
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# :call-seq:
|
22
|
+
# repo.gems_uri -> URI
|
23
|
+
#
|
24
|
+
# Return the URI to the location holding all the +.gem+ files.
|
25
|
+
#
|
26
|
+
#
|
27
|
+
def gems_uri
|
28
|
+
raise NotImplementedError, not_implemented_msg( :gems_uri )
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# :call-seq:
|
33
|
+
# repo.uri_for_gem( spec ) -> URI
|
34
|
+
#
|
35
|
+
# Given a SpecLite like object, return a URI that can be used
|
36
|
+
# to directly access the gem in the repository.
|
37
|
+
#
|
38
|
+
def uri_for_gem( spec )
|
39
|
+
raise NotImplementedError, not_implemented_msg( :uri_for_gem )
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# :call-seq:
|
44
|
+
# repo.search_for( spec ) -> Array
|
45
|
+
#
|
46
|
+
# +match+ MUST be an object that responds to +name+, +version+ and
|
47
|
+
# +platform+.
|
48
|
+
#
|
49
|
+
# The Array that is returned will be +empty?+ if no gems are found that
|
50
|
+
# match +match+.
|
51
|
+
#
|
52
|
+
# When one or matches is found, the Array will contain contain
|
53
|
+
# Stickler::SpecLite instances.
|
54
|
+
#
|
55
|
+
def search_for( spec )
|
56
|
+
raise NotImplementedError, not_implemented_msg( :search_for )
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# :call-seq:
|
61
|
+
# repo.push( path_to_gem_file ) -> Stickler::SpecLite
|
62
|
+
#
|
63
|
+
# Push, in the sense of the gem commandline command <tt>gem push</tt>.
|
64
|
+
# +path_to_gem_file+ must be a file system location to a .gem file
|
65
|
+
# that is then _pushed_ into the repository.
|
66
|
+
#
|
67
|
+
# The SpecLite returned can be used to retrieve the gem
|
68
|
+
# from the repo using the #get() method. A direct URI to the
|
69
|
+
# gem may be obtained using the #uri_for() method.
|
70
|
+
#
|
71
|
+
# If the gem pushed already exists, then a Stickler::Repository::Error is
|
72
|
+
# raised.
|
73
|
+
#
|
74
|
+
def push( path_to_gem_file )
|
75
|
+
raise NotImplementedError, not_implemented_msg( :push )
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
# :call-seq:
|
80
|
+
# repo.delete( spec ) -> true|false
|
81
|
+
#
|
82
|
+
# Remove the gem matching the spec completely from the respository.
|
83
|
+
# Return +true+ if the gem was removed, +false+ otherwise
|
84
|
+
#
|
85
|
+
def delete( spec )
|
86
|
+
raise NotImplementedError, not_implemented_msg( :delete)
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
# :call-seq:
|
91
|
+
# repo.yank( spec ) -> Stickler::SpecLite
|
92
|
+
#
|
93
|
+
# "yank" in the sense of
|
94
|
+
# http://update.gemcutter.org/2010/03/05/february-changelog.html.
|
95
|
+
# This means, remove the gem matching +spec+ from the index, so it will not
|
96
|
+
# be found when searching, but do not remove the gem physically from the
|
97
|
+
# server. It can still be downloaded directly.
|
98
|
+
#
|
99
|
+
# The SpecLite instance that is returned will have the information that may
|
100
|
+
# be used in the #get() or #uri_for_gem() methods to retrieve the actual
|
101
|
+
# gemfile.
|
102
|
+
#
|
103
|
+
# After a gem has been 'yanked' it MUST not longer be found via the
|
104
|
+
# #search_for() method, nor can it's specification be retrieved via the
|
105
|
+
# #uri_for_specification() method.
|
106
|
+
#
|
107
|
+
# If the gem described by spec does not exist, nil is returned.
|
108
|
+
#
|
109
|
+
def yank( spec )
|
110
|
+
raise NotImplementedError, not_implemented_msg( :yank )
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
# :call-seq:
|
115
|
+
# repo.get( spec ) -> bytes
|
116
|
+
#
|
117
|
+
# Retrieve the gem matching the spec from the repository. The bytes
|
118
|
+
# returned MUST be something that would be acceptable to be written
|
119
|
+
# directly to disk as a .gem file.
|
120
|
+
#
|
121
|
+
# If the gem described by spec does not exist, nil is returned.
|
122
|
+
#
|
123
|
+
def get( spec )
|
124
|
+
raise NotImplementedError, not_implemented_msg( :get )
|
125
|
+
end
|
126
|
+
|
127
|
+
#
|
128
|
+
# :call-seq:
|
129
|
+
# repo.open( spec ) -> reader
|
130
|
+
# repo.open( spec ) { |reader| block }
|
131
|
+
#
|
132
|
+
# Open the gem in a readonly manner, similar to that of File.open.
|
133
|
+
# the +reader+ object that is returned MUST respond to +read+,
|
134
|
+
# +close+ and +rewind+. These methods behave like their corresponding
|
135
|
+
# IO#read, IO#close and IO#rewind methods.
|
136
|
+
#
|
137
|
+
# If the gem described by spec does not exist, nil is returned.
|
138
|
+
# If the gem described by spec does not exist, the block is not called.
|
139
|
+
#
|
140
|
+
def open( spec, &block )
|
141
|
+
raise NotImplementedError, not_implemented_msg( :open )
|
142
|
+
end
|
143
|
+
|
144
|
+
# :stopdoc:
|
145
|
+
def self.api_methods
|
146
|
+
%w[
|
147
|
+
delete
|
148
|
+
gems_uri
|
149
|
+
get
|
150
|
+
open
|
151
|
+
push
|
152
|
+
search_for
|
153
|
+
uri
|
154
|
+
uri_for_gem
|
155
|
+
yank
|
156
|
+
]
|
157
|
+
end
|
158
|
+
# :startdoc:
|
159
|
+
|
160
|
+
private
|
161
|
+
# :stopdoc:
|
162
|
+
def not_implemented_msg( method )
|
163
|
+
"Please implement #{self.class.name}##{method}"
|
164
|
+
end
|
165
|
+
# :startdoc:
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'stickler/spec_lite'
|
2
|
+
require 'stickler/repository'
|
3
|
+
|
4
|
+
module Stickler::Repository
|
5
|
+
#
|
6
|
+
# A repository index is a container holding all the SpecLite elements
|
7
|
+
# in the repository. All the gem specs that this index holds are derived
|
8
|
+
# from actual files on the file system.
|
9
|
+
#
|
10
|
+
# It is modelled somewhat like a Gem::SourceIndex.
|
11
|
+
#
|
12
|
+
class Index
|
13
|
+
class Error < ::Stickler::Repository::Error; end
|
14
|
+
|
15
|
+
# The list of specs in the index
|
16
|
+
attr_reader :specs
|
17
|
+
|
18
|
+
# The directory the specs live
|
19
|
+
attr_reader :spec_dir
|
20
|
+
|
21
|
+
#
|
22
|
+
|
23
|
+
def initialize( spec_dir )
|
24
|
+
@specs = []
|
25
|
+
@spec_dir = spec_dir
|
26
|
+
load_specs
|
27
|
+
end
|
28
|
+
|
29
|
+
def specs
|
30
|
+
load_specs if reload_necessary?
|
31
|
+
return @specs
|
32
|
+
end
|
33
|
+
|
34
|
+
def latest_specs
|
35
|
+
latest = {}
|
36
|
+
specs.each do |s|
|
37
|
+
if old_spec = latest[s.name] then
|
38
|
+
if old_spec.version < s.version then
|
39
|
+
latest[s.name] = s
|
40
|
+
end
|
41
|
+
else
|
42
|
+
latest[s.name] = s
|
43
|
+
end
|
44
|
+
end
|
45
|
+
latest.values
|
46
|
+
end
|
47
|
+
|
48
|
+
def load_specs
|
49
|
+
load_specs_in_dir( self.spec_dir )
|
50
|
+
end
|
51
|
+
|
52
|
+
def reload_necessary?
|
53
|
+
return true
|
54
|
+
# return true unless @last_modified_time
|
55
|
+
# current_modified_time = File.stat( self.spec_dir ).mtime
|
56
|
+
# return (current_modified_time > @last_modified_time )
|
57
|
+
end
|
58
|
+
|
59
|
+
def last_modified_time
|
60
|
+
File.stat( self.spec_dir ).mtime
|
61
|
+
end
|
62
|
+
|
63
|
+
def spec_dir=( d )
|
64
|
+
raise Error, "#{d} is not a directory" unless File.directory?( d )
|
65
|
+
@spec_dir = d
|
66
|
+
@last_modified_time = File.stat( d ).mtime
|
67
|
+
end
|
68
|
+
|
69
|
+
def load_specs_in_dir( spec_dir )
|
70
|
+
return nil unless File.directory?( spec_dir )
|
71
|
+
@specs.clear
|
72
|
+
self.spec_dir = spec_dir
|
73
|
+
|
74
|
+
Dir.foreach( spec_dir ) do |entry|
|
75
|
+
next unless entry =~ /\.gemspec\Z/
|
76
|
+
add_spec_from_file( File.join( spec_dir, entry ) )
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def add_spec_from_file( path )
|
81
|
+
return nil unless File.exist?( path )
|
82
|
+
code = File.read( path )
|
83
|
+
full_spec = eval( code, binding, path )
|
84
|
+
raise Error, "File #{path} is not Gem::Specification in ruby format" unless full_spec.is_a?( Gem::Specification )
|
85
|
+
|
86
|
+
full_spec.untaint
|
87
|
+
spec = Stickler::SpecLite.new( full_spec.name, full_spec.version, full_spec.platform )
|
88
|
+
@specs << spec
|
89
|
+
end
|
90
|
+
|
91
|
+
def search( find_spec )
|
92
|
+
specs.select do |spec|
|
93
|
+
spec =~ find_spec
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,251 @@
|
|
1
|
+
require 'stickler/spec_lite'
|
2
|
+
require 'stickler/repository'
|
3
|
+
require 'stickler/repository/api'
|
4
|
+
require 'stickler/repository/index'
|
5
|
+
require 'addressable/uri'
|
6
|
+
require 'tempfile'
|
7
|
+
|
8
|
+
module Stickler::Repository
|
9
|
+
#
|
10
|
+
# A local repository of gems. It implements the Repository::Api
|
11
|
+
# and stores all the gems and specifications local to a root directory.
|
12
|
+
#
|
13
|
+
# It currently has two subdirectories:
|
14
|
+
#
|
15
|
+
# gems/ -> holding the .gem files
|
16
|
+
# specifications/ -> holding the .gemspec files
|
17
|
+
#
|
18
|
+
#
|
19
|
+
class Local
|
20
|
+
class Error < ::Stickler::Repository::Error; end
|
21
|
+
|
22
|
+
# the root directory of the repository
|
23
|
+
attr_reader :root_dir
|
24
|
+
|
25
|
+
# the directory containing the .gem files
|
26
|
+
attr_reader :gems_dir
|
27
|
+
|
28
|
+
# the directory containing the .gemspec files
|
29
|
+
attr_reader :specifications_dir
|
30
|
+
|
31
|
+
# a temporary directory for odds and ends
|
32
|
+
attr_reader :temp_dir
|
33
|
+
|
34
|
+
# the index of the repository
|
35
|
+
attr_reader :index
|
36
|
+
|
37
|
+
def initialize( root_dir )
|
38
|
+
@root_dir = File.expand_path( root_dir ) + File::SEPARATOR
|
39
|
+
@gems_dir = File.join( @root_dir, 'gems/' )
|
40
|
+
@specifications_dir = File.join( @root_dir, 'specifications/' )
|
41
|
+
@temp_dir = File.join( @root_dir, "tmp/" )
|
42
|
+
@index = ::Stickler::Repository::Index.new( @specifications_dir )
|
43
|
+
setup_dirs
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# See Api#uri
|
48
|
+
#
|
49
|
+
def uri
|
50
|
+
@uri ||= Addressable::URI.convert_path( root_dir )
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# See Api#gems_uri
|
55
|
+
#
|
56
|
+
def gems_uri
|
57
|
+
@gems_uri ||= Addressable::URI.convert_path( gems_dir )
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# See Api#uri_from_gem
|
62
|
+
#
|
63
|
+
def uri_for_gem( spec )
|
64
|
+
return nil unless gem_file_exist?( spec )
|
65
|
+
return self.gems_uri.join( spec.file_name )
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# A list of all the specs in the repo
|
70
|
+
#
|
71
|
+
def specs
|
72
|
+
@index.specs
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
# A list of just the latests specs in the repo
|
77
|
+
#
|
78
|
+
def latest_specs
|
79
|
+
@index.latest_specs
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# The last time this index was modified
|
84
|
+
#
|
85
|
+
def last_modified_time
|
86
|
+
@index.last_modified_time
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
# See Api#search_for
|
91
|
+
#
|
92
|
+
def search_for( spec )
|
93
|
+
return index.search( spec )
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
# See Api#delete
|
98
|
+
#
|
99
|
+
def delete( spec )
|
100
|
+
uninstall( spec )
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# See Api#yank
|
105
|
+
#
|
106
|
+
def yank( spec )
|
107
|
+
uninstall_specification( spec ) if specification_file_exist?( spec )
|
108
|
+
return uri_for_gem( spec )
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
#
|
113
|
+
# :call-seq:
|
114
|
+
# repo.add( io ) -> Stickler::SpecLite
|
115
|
+
#
|
116
|
+
# A lower level version of #push. The item passed in is an IO like object
|
117
|
+
# that contains the binary stream that is a .gem file. IO must respond to
|
118
|
+
# #read and #rewind.
|
119
|
+
#
|
120
|
+
def add( io )
|
121
|
+
# spooling to a temp file because Gem::Format.from_io() closes the io
|
122
|
+
# stream it is sent. Why it does this, I do not know.
|
123
|
+
tempfile = Tempfile.new( "uploaded-gem.", temp_dir )
|
124
|
+
tempfile.write( io.read )
|
125
|
+
tempfile.rewind
|
126
|
+
|
127
|
+
format = Gem::Format.from_file_by_path( tempfile.path )
|
128
|
+
spec = Stickler::SpecLite.new( format.spec.name, format.spec.version, format.spec.platform )
|
129
|
+
specs = search_for( spec )
|
130
|
+
|
131
|
+
raise Error, "gem #{spec.full_name} already exists" unless specs.empty?
|
132
|
+
|
133
|
+
tempfile.rewind
|
134
|
+
return install( spec, tempfile )
|
135
|
+
ensure
|
136
|
+
tempfile.close!
|
137
|
+
end
|
138
|
+
|
139
|
+
#
|
140
|
+
# See Api#push
|
141
|
+
#
|
142
|
+
def push( path )
|
143
|
+
spec = specification_from_gem_file( path )
|
144
|
+
result = nil
|
145
|
+
File.open( path ) do |io|
|
146
|
+
result = add( io )
|
147
|
+
end
|
148
|
+
return result
|
149
|
+
end
|
150
|
+
|
151
|
+
#
|
152
|
+
# See Api#get
|
153
|
+
#
|
154
|
+
def get( spec )
|
155
|
+
return IO.read( full_path_to_gem( spec ) ) if gem_file_exist?( spec )
|
156
|
+
return nil
|
157
|
+
end
|
158
|
+
|
159
|
+
#
|
160
|
+
# See Api#open
|
161
|
+
#
|
162
|
+
def open( spec, &block )
|
163
|
+
return nil unless gem_file_exist?( spec )
|
164
|
+
path = full_path_to_gem( spec )
|
165
|
+
f = File.open( path, "rb" )
|
166
|
+
if block_given? then
|
167
|
+
begin
|
168
|
+
yield f
|
169
|
+
ensure
|
170
|
+
f.close
|
171
|
+
end
|
172
|
+
else
|
173
|
+
return f
|
174
|
+
end
|
175
|
+
return nil
|
176
|
+
end
|
177
|
+
|
178
|
+
def full_path_to_gem( spec )
|
179
|
+
File.join( gems_dir, spec.file_name )
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
private
|
184
|
+
|
185
|
+
def setup_dirs
|
186
|
+
[ root_dir, specifications_dir, gems_dir, temp_dir ].each do |dir|
|
187
|
+
FileUtils.mkdir_p( dir ) unless File.directory?( dir )
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def full_path_to_specification( spec )
|
192
|
+
File.join( specifications_dir, spec.spec_file_name )
|
193
|
+
end
|
194
|
+
|
195
|
+
def install( spec, io )
|
196
|
+
install_gem( spec, io )
|
197
|
+
install_specification( spec )
|
198
|
+
end
|
199
|
+
|
200
|
+
def install_gem( spec, io )
|
201
|
+
File.open( full_path_to_gem( spec ) , "w+" ) do |of|
|
202
|
+
io.each do |str|
|
203
|
+
of.write( str )
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def install_specification( spec )
|
209
|
+
gemspec = specification_from_gem_file( full_path_to_gem( spec ) )
|
210
|
+
File.open( full_path_to_specification( spec ) , "w+" ) do |f|
|
211
|
+
f.write( gemspec.to_ruby )
|
212
|
+
end
|
213
|
+
return speclite_from_specification( gemspec )
|
214
|
+
end
|
215
|
+
|
216
|
+
def uninstall( spec )
|
217
|
+
uninstall_gem( spec )
|
218
|
+
uninstall_specification( spec )
|
219
|
+
end
|
220
|
+
|
221
|
+
def uninstall_gem( spec )
|
222
|
+
remove_file( full_path_to_gem( spec ) )
|
223
|
+
end
|
224
|
+
|
225
|
+
def uninstall_specification( spec )
|
226
|
+
remove_file( full_path_to_specification( spec ) )
|
227
|
+
end
|
228
|
+
|
229
|
+
def remove_file( path )
|
230
|
+
return false unless File.exist?( path )
|
231
|
+
return true if File.unlink( path ) > 0
|
232
|
+
end
|
233
|
+
|
234
|
+
def gem_file_exist?( spec )
|
235
|
+
File.exist?( full_path_to_gem( spec ) )
|
236
|
+
end
|
237
|
+
|
238
|
+
def specification_file_exist?( spec )
|
239
|
+
File.exist?( full_path_to_specification( spec ) )
|
240
|
+
end
|
241
|
+
|
242
|
+
def specification_from_gem_file( path )
|
243
|
+
format = Gem::Format.from_file_by_path( path )
|
244
|
+
return format.spec
|
245
|
+
end
|
246
|
+
|
247
|
+
def speclite_from_specification( spec )
|
248
|
+
Stickler::SpecLite.new( spec.name, spec.version.to_s, spec.platform )
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'stickler/repository'
|
2
|
+
require 'stickler/repository/remote'
|
3
|
+
require 'stickler/repository/local'
|
4
|
+
require 'forwardable'
|
5
|
+
|
6
|
+
module Stickler::Repository
|
7
|
+
#
|
8
|
+
# A Mirror mirrors gems in a Repository::Remote to a Repository::Local
|
9
|
+
# All of the Repository::Api methods are delegated to the Local instance
|
10
|
+
# and a new method #mirror() is added to pull gems from a Remote location
|
11
|
+
# and store in the Local instance
|
12
|
+
#
|
13
|
+
class Mirror
|
14
|
+
class Error < ::Stickler::Repository::Error ; end
|
15
|
+
|
16
|
+
extend Forwardable
|
17
|
+
|
18
|
+
def initialize( root_dir )
|
19
|
+
@root_dir = root_dir
|
20
|
+
@local_repo = ::Stickler::Repository::Local.new( @root_dir )
|
21
|
+
@remote_repos = {}
|
22
|
+
end
|
23
|
+
def_delegators :@local_repo, :uri, :gems_uri, :uri_for_gem, :search_for,
|
24
|
+
:push, :delete, :get, :open
|
25
|
+
|
26
|
+
#
|
27
|
+
# :call-seq:
|
28
|
+
# repo.mirror( spec, host = "rubygems.org" ) -> SpecLite
|
29
|
+
#
|
30
|
+
# Mirror the gem described by spec on the +host+. If no +host+
|
31
|
+
# is given, it is assumed to be http://rubygems.org/.
|
32
|
+
#
|
33
|
+
def mirror( host, spec )
|
34
|
+
specs = @local_repo.search_for( spec )
|
35
|
+
raise Error, "gem #{spec.full_name} already exists" unless specs.empty?
|
36
|
+
|
37
|
+
repo = remote_repo_for( host )
|
38
|
+
repo.open( spec ) do |io|
|
39
|
+
@local_repo.add( io )
|
40
|
+
end
|
41
|
+
return spec
|
42
|
+
end
|
43
|
+
|
44
|
+
def remote_repo_for( host )
|
45
|
+
@remote_repos[host] ||= ::Stickler::Repository::Remote.new( host )
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'stickler/repository'
|
2
|
+
|
3
|
+
module Stickler::Repository
|
4
|
+
#
|
5
|
+
# A null repository. It is in most respecs like a Repository::Local that has
|
6
|
+
# nothing in it.
|
7
|
+
#
|
8
|
+
# The response to +root_dir+ is set by default to be the class name, or
|
9
|
+
# whatever is passed to the initializer.
|
10
|
+
#
|
11
|
+
class Null
|
12
|
+
# the root directory of the repository, this is set in the constructor
|
13
|
+
attr_reader :root_dir
|
14
|
+
|
15
|
+
def initialize( root_dir = self.class.name )
|
16
|
+
@root_dir = root_dir
|
17
|
+
end
|
18
|
+
|
19
|
+
def empty_string( junk = "" )
|
20
|
+
""
|
21
|
+
end
|
22
|
+
alias :uri :empty_string
|
23
|
+
alias :gems_uri :empty_string
|
24
|
+
|
25
|
+
def nilish( junk = nil, &block )
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
alias :push :nilish
|
29
|
+
alias :delete :nilish
|
30
|
+
alias :yank :nilish
|
31
|
+
alias :get :nilish
|
32
|
+
alias :open :nilish
|
33
|
+
alias :uri_for_gem :nilish
|
34
|
+
alias :full_path_to_gem :nilish
|
35
|
+
|
36
|
+
def last_modified_time
|
37
|
+
Time.now
|
38
|
+
end
|
39
|
+
|
40
|
+
def empty_array( junk = nil )
|
41
|
+
[]
|
42
|
+
end
|
43
|
+
alias :specs :empty_array
|
44
|
+
alias :latest_specs :empty_array
|
45
|
+
alias :search_for :empty_array
|
46
|
+
|
47
|
+
def
|
48
|
+
|
49
|
+
def specs
|
50
|
+
Array.new
|
51
|
+
end
|
52
|
+
|
53
|
+
def latest_specs
|
54
|
+
Array.new
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|