merb-gen 0.9.7 → 0.9.8
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 +1 -1
- data/Rakefile +27 -9
- data/bin/merb-gen +21 -3
- data/lib/generators/merb/merb_flat.rb +83 -14
- data/lib/generators/merb/merb_full.rb +66 -20
- data/lib/generators/merb/merb_very_flat.rb +51 -10
- data/lib/generators/migration.rb +5 -2
- data/lib/generators/resource_controller.rb +2 -2
- data/lib/generators/templates/application/{merb → common}/Rakefile +15 -7
- data/lib/generators/templates/application/common/dotgitignore +18 -0
- data/lib/generators/templates/application/common/merb.thor +1221 -0
- data/lib/generators/templates/application/merb/app/views/exceptions/not_acceptable.html.erb +1 -1
- data/lib/generators/templates/application/merb/app/views/exceptions/not_found.html.erb +1 -1
- data/lib/generators/templates/application/merb/config/environments/development.rb +1 -0
- data/lib/generators/templates/application/merb/config/environments/staging.rb +7 -0
- data/lib/generators/templates/application/merb/config/environments/test.rb +2 -1
- data/lib/generators/templates/application/merb/config/init.rb +1 -4
- data/lib/generators/templates/application/merb/config/router.rb +13 -7
- data/lib/generators/templates/application/merb/public/favicon.ico +0 -0
- data/lib/generators/templates/application/merb/public/robots.txt +5 -0
- data/lib/generators/templates/application/merb_flat/{application.rb → application.rbt} +1 -1
- data/lib/generators/templates/application/merb_flat/config/init.rb +119 -2
- data/lib/generators/templates/application/merb_flat/test/test_helper.rb +19 -0
- data/lib/generators/templates/application/merb_plugin/Rakefile +17 -22
- data/lib/generators/templates/application/merb_plugin/spec/spec_helper.rb +0 -1
- data/lib/generators/templates/application/merb_plugin/test/test_helper.rb +0 -1
- data/lib/generators/templates/application/merb_very_flat/application.rbt +36 -2
- data/lib/generators/templates/application/merb_very_flat/test/test_helper.rb +19 -0
- data/spec/merb_flat_spec.rb +4 -4
- data/spec/spec_helper.rb +0 -1
- metadata +16 -7
- data/lib/generators/templates/application/merb/merb.thor +0 -822
@@ -0,0 +1,1221 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'thor'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
# TODO
|
8
|
+
# - pulling a specific UUID/Tag (gitspec hash) with clone/update
|
9
|
+
# - a 'deploy' task (in addition to 'redeploy' ?)
|
10
|
+
# - add merb:gems:refresh to refresh all gems (from specifications)
|
11
|
+
# - merb:gems:uninstall should remove local bin/ entries
|
12
|
+
|
13
|
+
##############################################################################
|
14
|
+
#
|
15
|
+
# GemManagement
|
16
|
+
#
|
17
|
+
# The following code is also used by Merb core, but we can't rely on it as a
|
18
|
+
# dependency, since merb.thor should be completely selfcontained (except for
|
19
|
+
# Thor itself). Therefore, the code below is copied here. Should you work on
|
20
|
+
# this code, be sure to edit the original code to keep them in sync.
|
21
|
+
#
|
22
|
+
# You can find the original here: merb-core/tasks/gem_management.rb
|
23
|
+
#
|
24
|
+
##############################################################################
|
25
|
+
|
26
|
+
require 'rubygems'
|
27
|
+
require 'rubygems/dependency_installer'
|
28
|
+
require 'rubygems/uninstaller'
|
29
|
+
require 'rubygems/dependency'
|
30
|
+
|
31
|
+
module ColorfulMessages
|
32
|
+
|
33
|
+
# red
|
34
|
+
def error(*messages)
|
35
|
+
puts messages.map { |msg| "\033[1;31m#{msg}\033[0m" }
|
36
|
+
end
|
37
|
+
|
38
|
+
# yellow
|
39
|
+
def warning(*messages)
|
40
|
+
puts messages.map { |msg| "\033[1;33m#{msg}\033[0m" }
|
41
|
+
end
|
42
|
+
|
43
|
+
# green
|
44
|
+
def success(*messages)
|
45
|
+
puts messages.map { |msg| "\033[1;32m#{msg}\033[0m" }
|
46
|
+
end
|
47
|
+
|
48
|
+
alias_method :message, :success
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
module GemManagement
|
53
|
+
|
54
|
+
include ColorfulMessages
|
55
|
+
|
56
|
+
# Install a gem - looks remotely and local gem cache;
|
57
|
+
# won't process rdoc or ri options.
|
58
|
+
def install_gem(gem, options = {})
|
59
|
+
refresh = options.delete(:refresh) || []
|
60
|
+
from_cache = (options.key?(:cache) && options.delete(:cache))
|
61
|
+
if from_cache
|
62
|
+
install_gem_from_cache(gem, options)
|
63
|
+
else
|
64
|
+
version = options.delete(:version)
|
65
|
+
Gem.configuration.update_sources = false
|
66
|
+
|
67
|
+
update_source_index(options[:install_dir]) if options[:install_dir]
|
68
|
+
|
69
|
+
installer = Gem::DependencyInstaller.new(options.merge(:user_install => false))
|
70
|
+
|
71
|
+
# Exclude gems to refresh from index - force (re)install of new version
|
72
|
+
# def installer.source_index; @source_index; end
|
73
|
+
unless refresh.empty?
|
74
|
+
source_index = installer.instance_variable_get(:@source_index)
|
75
|
+
source_index.gems.each do |name, spec|
|
76
|
+
source_index.gems.delete(name) if refresh.include?(spec.name)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
exception = nil
|
81
|
+
begin
|
82
|
+
installer.install gem, version
|
83
|
+
rescue Gem::InstallError => e
|
84
|
+
exception = e
|
85
|
+
rescue Gem::GemNotFoundException => e
|
86
|
+
if from_cache && gem_file = find_gem_in_cache(gem, version)
|
87
|
+
puts "Located #{gem} in gem cache..."
|
88
|
+
installer.install gem_file
|
89
|
+
else
|
90
|
+
exception = e
|
91
|
+
end
|
92
|
+
rescue => e
|
93
|
+
exception = e
|
94
|
+
end
|
95
|
+
if installer.installed_gems.empty? && exception
|
96
|
+
error "Failed to install gem '#{gem} (#{version})' (#{exception.message})"
|
97
|
+
end
|
98
|
+
installer.installed_gems.each do |spec|
|
99
|
+
success "Successfully installed #{spec.full_name}"
|
100
|
+
end
|
101
|
+
return !installer.installed_gems.empty?
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Install a gem - looks in the system's gem cache instead of remotely;
|
106
|
+
# won't process rdoc or ri options.
|
107
|
+
def install_gem_from_cache(gem, options = {})
|
108
|
+
version = options.delete(:version)
|
109
|
+
Gem.configuration.update_sources = false
|
110
|
+
installer = Gem::DependencyInstaller.new(options.merge(:user_install => false))
|
111
|
+
exception = nil
|
112
|
+
begin
|
113
|
+
if gem_file = find_gem_in_cache(gem, version)
|
114
|
+
puts "Located #{gem} in gem cache..."
|
115
|
+
installer.install gem_file
|
116
|
+
else
|
117
|
+
raise Gem::InstallError, "Unknown gem #{gem}"
|
118
|
+
end
|
119
|
+
rescue Gem::InstallError => e
|
120
|
+
exception = e
|
121
|
+
end
|
122
|
+
if installer.installed_gems.empty? && exception
|
123
|
+
error "Failed to install gem '#{gem}' (#{e.message})"
|
124
|
+
end
|
125
|
+
installer.installed_gems.each do |spec|
|
126
|
+
success "Successfully installed #{spec.full_name}"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Install a gem from source - builds and packages it first then installs.
|
131
|
+
def install_gem_from_src(gem_src_dir, options = {})
|
132
|
+
if !File.directory?(gem_src_dir)
|
133
|
+
raise "Missing rubygem source path: #{gem_src_dir}"
|
134
|
+
end
|
135
|
+
if options[:install_dir] && !File.directory?(options[:install_dir])
|
136
|
+
raise "Missing rubygems path: #{options[:install_dir]}"
|
137
|
+
end
|
138
|
+
|
139
|
+
gem_name = File.basename(gem_src_dir)
|
140
|
+
gem_pkg_dir = File.expand_path(File.join(gem_src_dir, 'pkg'))
|
141
|
+
|
142
|
+
# We need to use local bin executables if available.
|
143
|
+
thor = "#{Gem.ruby} -S #{which('thor')}"
|
144
|
+
rake = "#{Gem.ruby} -S #{which('rake')}"
|
145
|
+
|
146
|
+
# Handle pure Thor installation instead of Rake
|
147
|
+
if File.exists?(File.join(gem_src_dir, 'Thorfile'))
|
148
|
+
# Remove any existing packages.
|
149
|
+
FileUtils.rm_rf(gem_pkg_dir) if File.directory?(gem_pkg_dir)
|
150
|
+
# Create the package.
|
151
|
+
FileUtils.cd(gem_src_dir) { system("#{thor} :package") }
|
152
|
+
# Install the package using rubygems.
|
153
|
+
if package = Dir[File.join(gem_pkg_dir, "#{gem_name}-*.gem")].last
|
154
|
+
FileUtils.cd(File.dirname(package)) do
|
155
|
+
install_gem(File.basename(package), options.dup)
|
156
|
+
return true
|
157
|
+
end
|
158
|
+
else
|
159
|
+
raise Gem::InstallError, "No package found for #{gem_name}"
|
160
|
+
end
|
161
|
+
# Handle elaborate installation through Rake
|
162
|
+
else
|
163
|
+
# Clean and regenerate any subgems for meta gems.
|
164
|
+
Dir[File.join(gem_src_dir, '*', 'Rakefile')].each do |rakefile|
|
165
|
+
FileUtils.cd(File.dirname(rakefile)) do
|
166
|
+
system("#{rake} clobber_package; #{rake} package")
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Handle the main gem install.
|
171
|
+
if File.exists?(File.join(gem_src_dir, 'Rakefile'))
|
172
|
+
subgems = []
|
173
|
+
# Remove any existing packages.
|
174
|
+
FileUtils.cd(gem_src_dir) { system("#{rake} clobber_package") }
|
175
|
+
# Create the main gem pkg dir if it doesn't exist.
|
176
|
+
FileUtils.mkdir_p(gem_pkg_dir) unless File.directory?(gem_pkg_dir)
|
177
|
+
# Copy any subgems to the main gem pkg dir.
|
178
|
+
Dir[File.join(gem_src_dir, '*', 'pkg', '*.gem')].each do |subgem_pkg|
|
179
|
+
if name = File.basename(subgem_pkg, '.gem')[/^(.*?)-([\d\.]+)$/, 1]
|
180
|
+
subgems << name
|
181
|
+
end
|
182
|
+
dest = File.join(gem_pkg_dir, File.basename(subgem_pkg))
|
183
|
+
FileUtils.copy_entry(subgem_pkg, dest, true, false, true)
|
184
|
+
end
|
185
|
+
|
186
|
+
# Finally generate the main package and install it; subgems
|
187
|
+
# (dependencies) are local to the main package.
|
188
|
+
FileUtils.cd(gem_src_dir) do
|
189
|
+
system("#{rake} package")
|
190
|
+
FileUtils.cd(gem_pkg_dir) do
|
191
|
+
if package = Dir[File.join(gem_pkg_dir, "#{gem_name}-*.gem")].last
|
192
|
+
# If the (meta) gem has it's own package, install it.
|
193
|
+
install_gem(File.basename(package), options.merge(:refresh => subgems))
|
194
|
+
else
|
195
|
+
# Otherwise install each package seperately.
|
196
|
+
Dir["*.gem"].each { |gem| install_gem(gem, options.dup) }
|
197
|
+
end
|
198
|
+
end
|
199
|
+
return true
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
raise Gem::InstallError, "No Rakefile found for #{gem_name}"
|
204
|
+
end
|
205
|
+
|
206
|
+
# Uninstall a gem.
|
207
|
+
def uninstall_gem(gem, options = {})
|
208
|
+
if options[:version] && !options[:version].is_a?(Gem::Requirement)
|
209
|
+
options[:version] = Gem::Requirement.new ["= #{options[:version]}"]
|
210
|
+
end
|
211
|
+
update_source_index(options[:install_dir]) if options[:install_dir]
|
212
|
+
Gem::Uninstaller.new(gem, options).uninstall
|
213
|
+
end
|
214
|
+
|
215
|
+
# Use the local bin/* executables if available.
|
216
|
+
def which(executable)
|
217
|
+
if File.executable?(exec = File.join(Dir.pwd, 'bin', executable))
|
218
|
+
exec
|
219
|
+
else
|
220
|
+
executable
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# Create a modified executable wrapper in the specified bin directory.
|
225
|
+
def ensure_bin_wrapper_for(gem_dir, bin_dir, *gems)
|
226
|
+
if bin_dir && File.directory?(bin_dir)
|
227
|
+
gems.each do |gem|
|
228
|
+
if gemspec_path = Dir[File.join(gem_dir, 'specifications', "#{gem}-*.gemspec")].last
|
229
|
+
spec = Gem::Specification.load(gemspec_path)
|
230
|
+
spec.executables.each do |exec|
|
231
|
+
executable = File.join(bin_dir, exec)
|
232
|
+
message "Writing executable wrapper #{executable}"
|
233
|
+
File.open(executable, 'w', 0755) do |f|
|
234
|
+
f.write(executable_wrapper(spec, exec))
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
private
|
243
|
+
|
244
|
+
def executable_wrapper(spec, bin_file_name)
|
245
|
+
<<-TEXT
|
246
|
+
#!/usr/bin/env ruby
|
247
|
+
#
|
248
|
+
# This file was generated by Merb's GemManagement
|
249
|
+
#
|
250
|
+
# The application '#{spec.name}' is installed as part of a gem, and
|
251
|
+
# this file is here to facilitate running it.
|
252
|
+
|
253
|
+
begin
|
254
|
+
require 'minigems'
|
255
|
+
rescue LoadError
|
256
|
+
require 'rubygems'
|
257
|
+
end
|
258
|
+
|
259
|
+
if File.directory?(gems_dir = File.join(Dir.pwd, 'gems')) ||
|
260
|
+
File.directory?(gems_dir = File.join(File.dirname(__FILE__), '..', 'gems'))
|
261
|
+
$BUNDLE = true; Gem.clear_paths; Gem.path.unshift(gems_dir)
|
262
|
+
end
|
263
|
+
|
264
|
+
version = "#{Gem::Requirement.default}"
|
265
|
+
|
266
|
+
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
|
267
|
+
version = $1
|
268
|
+
ARGV.shift
|
269
|
+
end
|
270
|
+
|
271
|
+
gem '#{spec.name}', version
|
272
|
+
load '#{bin_file_name}'
|
273
|
+
TEXT
|
274
|
+
end
|
275
|
+
|
276
|
+
def find_gem_in_cache(gem, version)
|
277
|
+
spec = if version
|
278
|
+
version = Gem::Requirement.new ["= #{version}"] unless version.is_a?(Gem::Requirement)
|
279
|
+
Gem.source_index.find_name(gem, version).first
|
280
|
+
else
|
281
|
+
Gem.source_index.find_name(gem).sort_by { |g| g.version }.last
|
282
|
+
end
|
283
|
+
if spec && File.exists?(gem_file = "#{spec.installation_path}/cache/#{spec.full_name}.gem")
|
284
|
+
gem_file
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
def update_source_index(dir)
|
289
|
+
Gem.source_index.load_gems_in(File.join(dir, 'specifications'))
|
290
|
+
end
|
291
|
+
|
292
|
+
end
|
293
|
+
|
294
|
+
|
295
|
+
##############################################################################
|
296
|
+
|
297
|
+
module MerbThorHelper
|
298
|
+
|
299
|
+
include ColorfulMessages
|
300
|
+
|
301
|
+
DO_ADAPTERS = %w[mysql postgres sqlite3]
|
302
|
+
|
303
|
+
private
|
304
|
+
|
305
|
+
# The current working directory, or Merb app root (--merb-root option).
|
306
|
+
def working_dir
|
307
|
+
@_working_dir ||= File.expand_path(options['merb-root'] || Dir.pwd)
|
308
|
+
end
|
309
|
+
|
310
|
+
# We should have a ./src dir for local and system-wide management.
|
311
|
+
def source_dir
|
312
|
+
@_source_dir ||= File.join(working_dir, 'src')
|
313
|
+
create_if_missing(@_source_dir)
|
314
|
+
@_source_dir
|
315
|
+
end
|
316
|
+
|
317
|
+
# If a local ./gems dir is found, it means we are in a Merb app.
|
318
|
+
def application?
|
319
|
+
gem_dir
|
320
|
+
end
|
321
|
+
|
322
|
+
# If a local ./gems dir is found, return it.
|
323
|
+
def gem_dir
|
324
|
+
if File.directory?(dir = File.join(working_dir, 'gems'))
|
325
|
+
dir
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
# If we're in a Merb app, we can have a ./bin directory;
|
330
|
+
# create it if it's not there.
|
331
|
+
def bin_dir
|
332
|
+
@_bin_dir ||= begin
|
333
|
+
if gem_dir
|
334
|
+
dir = File.join(working_dir, 'bin')
|
335
|
+
create_if_missing(dir)
|
336
|
+
dir
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
def config_dir
|
342
|
+
@_config_dir ||= File.join(working_dir, 'config')
|
343
|
+
end
|
344
|
+
|
345
|
+
def config_file
|
346
|
+
@_config_file ||= File.join(config_dir, 'dependencies.yml')
|
347
|
+
end
|
348
|
+
|
349
|
+
# Find the latest merb-core and gather its dependencies.
|
350
|
+
# We check for 0.9.8 as a minimum release version.
|
351
|
+
def core_dependencies
|
352
|
+
@_core_dependencies ||= begin
|
353
|
+
if gem_dir
|
354
|
+
Gem.clear_paths; Gem.path.unshift(gem_dir)
|
355
|
+
end
|
356
|
+
deps = []
|
357
|
+
merb_core = Gem::Dependency.new('merb-core', '>= 0.9.8')
|
358
|
+
if gemspec = Gem.source_index.search(merb_core).last
|
359
|
+
deps << Gem::Dependency.new('merb-core', gemspec.version)
|
360
|
+
deps += gemspec.dependencies
|
361
|
+
end
|
362
|
+
Gem.clear_paths
|
363
|
+
deps
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
# Find local gems and return matched version numbers.
|
368
|
+
def find_dependency_versions(dependency)
|
369
|
+
versions = []
|
370
|
+
specs = Dir[File.join(gem_dir, 'specifications', "#{dependency.name}-*.gemspec")]
|
371
|
+
unless specs.empty?
|
372
|
+
specs.inject(versions) do |versions, gemspec_path|
|
373
|
+
versions << gemspec_path[/-([\d\.]+)\.gemspec$/, 1]
|
374
|
+
end
|
375
|
+
end
|
376
|
+
versions.sort.reverse
|
377
|
+
end
|
378
|
+
|
379
|
+
# Helper to create dir unless it exists.
|
380
|
+
def create_if_missing(path)
|
381
|
+
FileUtils.mkdir(path) unless File.exists?(path)
|
382
|
+
end
|
383
|
+
|
384
|
+
def neutralise_sudo_for(dir)
|
385
|
+
if ENV['SUDO_UID'] && ENV['SUDO_GID']
|
386
|
+
FileUtils.chown_R(ENV['SUDO_UID'], ENV['SUDO_GID'], dir)
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
def ensure_bin_wrapper_for(*gems)
|
391
|
+
Merb.ensure_bin_wrapper_for(gem_dir, bin_dir, *gems)
|
392
|
+
end
|
393
|
+
|
394
|
+
def ensure_bin_wrapper_for_core_components
|
395
|
+
ensure_bin_wrapper_for('merb-core', 'rake', 'rspec', 'thor', 'merb-gen')
|
396
|
+
end
|
397
|
+
|
398
|
+
end
|
399
|
+
|
400
|
+
##############################################################################
|
401
|
+
|
402
|
+
class Merb < Thor
|
403
|
+
|
404
|
+
extend GemManagement
|
405
|
+
|
406
|
+
# Default Git repositories
|
407
|
+
def self.default_repos
|
408
|
+
@_default_repos ||= {
|
409
|
+
'merb-core' => "git://github.com/wycats/merb-core.git",
|
410
|
+
'merb-more' => "git://github.com/wycats/merb-more.git",
|
411
|
+
'merb-plugins' => "git://github.com/wycats/merb-plugins.git",
|
412
|
+
'extlib' => "git://github.com/sam/extlib.git",
|
413
|
+
'dm-core' => "git://github.com/sam/dm-core.git",
|
414
|
+
'dm-more' => "git://github.com/sam/dm-more.git",
|
415
|
+
'do' => "git://github.com/sam/do.git",
|
416
|
+
'thor' => "git://github.com/wycats/thor.git",
|
417
|
+
'minigems' => "git://github.com/fabien/minigems.git"
|
418
|
+
}
|
419
|
+
end
|
420
|
+
|
421
|
+
# Git repository sources - pass source_config option to load a yaml
|
422
|
+
# configuration file - defaults to ./config/git-sources.yml and
|
423
|
+
# ~/.merb/git-sources.yml - which need to create yourself if desired.
|
424
|
+
#
|
425
|
+
# Example of contents:
|
426
|
+
#
|
427
|
+
# merb-core: git://github.com/myfork/merb-core.git
|
428
|
+
# merb-more: git://github.com/myfork/merb-more.git
|
429
|
+
def self.repos(source_config = nil)
|
430
|
+
source_config ||= begin
|
431
|
+
local_config = File.join(Dir.pwd, 'config', 'git-sources.yml')
|
432
|
+
user_config = File.join(ENV["HOME"] || ENV["APPDATA"], '.merb', 'git-sources.yml')
|
433
|
+
File.exists?(local_config) ? local_config : user_config
|
434
|
+
end
|
435
|
+
if source_config && File.exists?(source_config)
|
436
|
+
default_repos.merge(YAML.load(File.read(source_config)))
|
437
|
+
else
|
438
|
+
default_repos
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
class Dependencies < Thor
|
443
|
+
|
444
|
+
include MerbThorHelper
|
445
|
+
|
446
|
+
# List all dependencies by extracting them from the actual application;
|
447
|
+
# will differentiate between locally available gems and system gems.
|
448
|
+
# Local gems will be shown with the installed gem version numbers.
|
449
|
+
|
450
|
+
desc 'list', 'List all application dependencies'
|
451
|
+
method_options "--merb-root" => :optional,
|
452
|
+
"--local" => :boolean,
|
453
|
+
"--system" => :boolean
|
454
|
+
def list
|
455
|
+
partitioned = { :local => [], :system => [] }
|
456
|
+
(core_dependencies + extract_dependencies).each do |dependency|
|
457
|
+
if gem_dir && !(versions = find_dependency_versions(dependency)).empty?
|
458
|
+
partitioned[:local] << "#{dependency} [#{versions.join(', ')}]"
|
459
|
+
else
|
460
|
+
partitioned[:system] << "#{dependency}"
|
461
|
+
end
|
462
|
+
end
|
463
|
+
none = options[:system].nil? && options[:local].nil?
|
464
|
+
if (options[:system] || none) && !partitioned[:system].empty?
|
465
|
+
message "System dependencies:"
|
466
|
+
partitioned[:system].each { |str| puts "- #{str}" }
|
467
|
+
end
|
468
|
+
if (options[:local] || none) && !partitioned[:local].empty?
|
469
|
+
message "Local dependencies:"
|
470
|
+
partitioned[:local].each { |str| puts "- #{str}" }
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
# Install the gems listed in dependencies.yml from RubyForge (stable).
|
475
|
+
# Will also install local bin wrappers for known components.
|
476
|
+
|
477
|
+
desc 'install', 'Install the gems listed in ./config/dependencies.yml'
|
478
|
+
method_options "--merb-root" => :optional,
|
479
|
+
"--cache" => :boolean,
|
480
|
+
"--binaries" => :boolean
|
481
|
+
def install
|
482
|
+
if File.exists?(config_file)
|
483
|
+
dependencies = parse_dependencies_yaml(File.read(config_file))
|
484
|
+
gems = Gems.new
|
485
|
+
gems.options = options
|
486
|
+
dependencies.each do |dependency|
|
487
|
+
v = dependency.version_requirements
|
488
|
+
gems.install_gem(dependency.name, :version => v, :cache => options[:cache])
|
489
|
+
end
|
490
|
+
# if options[:binaries] is set this is already taken care of - skip it
|
491
|
+
ensure_bin_wrapper_for_core_components unless options[:binaries]
|
492
|
+
else
|
493
|
+
error "No configuration file found at #{config_file}"
|
494
|
+
error "Please run merb:dependencies:configure first."
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
# Retrieve all application dependencies and store them in a local
|
499
|
+
# configuration file at ./config/dependencies.yml
|
500
|
+
#
|
501
|
+
# The format of this YAML file is as follows:
|
502
|
+
# - merb_helpers (>= 0, runtime)
|
503
|
+
# - merb-slices (> 0.9.4, runtime)
|
504
|
+
|
505
|
+
desc 'configure', 'Retrieve and store dependencies in ./config/dependencies.yml'
|
506
|
+
method_options "--merb-root" => :optional,
|
507
|
+
"--force" => :boolean
|
508
|
+
def configure
|
509
|
+
entries = (core_dependencies + extract_dependencies).map { |d| d.to_s }
|
510
|
+
FileUtils.mkdir_p(config_dir) unless File.directory?(config_dir)
|
511
|
+
config = YAML.dump(entries)
|
512
|
+
puts "#{config}\n"
|
513
|
+
if File.exists?(config_file) && !options[:force]
|
514
|
+
error "File already exists! Use --force to overwrite."
|
515
|
+
else
|
516
|
+
File.open(config_file, 'w') { |f| f.write config }
|
517
|
+
success "Written #{config_file}:"
|
518
|
+
end
|
519
|
+
rescue
|
520
|
+
error "Failed to write to #{config_file}"
|
521
|
+
end
|
522
|
+
|
523
|
+
protected
|
524
|
+
|
525
|
+
# Extract the runtime dependencies by starting the application in runner mode.
|
526
|
+
def extract_dependencies
|
527
|
+
FileUtils.cd(working_dir) do
|
528
|
+
cmd = ["require 'yaml';"]
|
529
|
+
cmd << "dependencies = Merb::BootLoader::Dependencies.dependencies"
|
530
|
+
cmd << "entries = dependencies.map { |d| d.to_s }"
|
531
|
+
cmd << "puts YAML.dump(entries)"
|
532
|
+
output = `merb -r "#{cmd.join("\n")}"`
|
533
|
+
if index = (lines = output.split(/\n/)).index('--- ')
|
534
|
+
yaml = lines.slice(index, lines.length - 1).join("\n")
|
535
|
+
return parse_dependencies_yaml(yaml)
|
536
|
+
end
|
537
|
+
end
|
538
|
+
return []
|
539
|
+
end
|
540
|
+
|
541
|
+
# Parse the basic YAML config data, and process Gem::Dependency output.
|
542
|
+
# Formatting example: merb_helpers (>= 0.9.8, runtime)
|
543
|
+
def parse_dependencies_yaml(yaml)
|
544
|
+
dependencies = []
|
545
|
+
entries = YAML.load(yaml) rescue []
|
546
|
+
entries.each do |entry|
|
547
|
+
if matches = entry.match(/^(\S+) \(([^,]+)?, ([^\)]+)\)/)
|
548
|
+
name, version_req, type = matches.captures
|
549
|
+
dependencies << Gem::Dependency.new(name, version_req, type.to_sym)
|
550
|
+
else
|
551
|
+
error "Invalid entry: #{entry}"
|
552
|
+
end
|
553
|
+
end
|
554
|
+
dependencies
|
555
|
+
end
|
556
|
+
|
557
|
+
end
|
558
|
+
|
559
|
+
# Install a Merb stack from stable RubyForge gems. Optionally install a
|
560
|
+
# suitable Rack adapter/server when setting --adapter to one of the
|
561
|
+
# following: mongrel, emongrel, thin or ebb. Or with --orm, install a
|
562
|
+
# supported ORM: datamapper, activerecord or sequel
|
563
|
+
|
564
|
+
desc 'stable', 'Install extlib, merb-core and merb-more from rubygems'
|
565
|
+
method_options "--merb-root" => :optional,
|
566
|
+
"--adapter" => :optional,
|
567
|
+
"--orm" => :optional
|
568
|
+
def stable
|
569
|
+
adapters = {
|
570
|
+
'mongrel' => ['mongrel'],
|
571
|
+
'emongrel' => ['emongrel'],
|
572
|
+
'thin' => ['thin'],
|
573
|
+
'ebb' => ['ebb']
|
574
|
+
}
|
575
|
+
orms = {
|
576
|
+
'datamapper' => ['dm-core'],
|
577
|
+
'activerecord' => ['activerecord'],
|
578
|
+
'sequel' => ['sequel']
|
579
|
+
}
|
580
|
+
stable = Stable.new
|
581
|
+
stable.options = options
|
582
|
+
if stable.core && stable.more
|
583
|
+
success "Installed extlib, merb-core and merb-more"
|
584
|
+
if options[:adapter] && adapters[options[:adapter]]
|
585
|
+
stable.refresh_from_gems(*adapters[options[:adapter]])
|
586
|
+
success "Installed #{options[:adapter]}"
|
587
|
+
elsif options[:adapter]
|
588
|
+
error "Please specify one of the following adapters: #{adapters.keys.join(' ')}"
|
589
|
+
end
|
590
|
+
if options[:orm] && orms[options[:orm]]
|
591
|
+
stable.refresh_from_gems(*orms[options[:orm]])
|
592
|
+
success "Installed #{options[:orm]}"
|
593
|
+
elsif options[:orm]
|
594
|
+
error "Please specify one of the following orms: #{orms.keys.join(' ')}"
|
595
|
+
end
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
class Stable < Thor
|
600
|
+
|
601
|
+
# The Stable tasks deal with known -stable- gems; available
|
602
|
+
# as shortcuts to Merb and DataMapper gems.
|
603
|
+
#
|
604
|
+
# These are pulled from rubyforge and installed into into the
|
605
|
+
# desired gems dir (either system-wide or into the application's
|
606
|
+
# gems directory).
|
607
|
+
|
608
|
+
include MerbThorHelper
|
609
|
+
|
610
|
+
# Gets latest gem versions from RubyForge and installs them.
|
611
|
+
#
|
612
|
+
# Examples:
|
613
|
+
#
|
614
|
+
# thor merb:edge:core
|
615
|
+
# thor merb:edge:core --merb-root ./path/to/your/app
|
616
|
+
# thor merb:edge:core --sources ./path/to/sources.yml
|
617
|
+
|
618
|
+
desc 'core', 'Install extlib and merb-core from rubygems'
|
619
|
+
method_options "--merb-root" => :optional
|
620
|
+
def core
|
621
|
+
refresh_from_gems 'extlib', 'merb-core'
|
622
|
+
ensure_bin_wrapper_for('merb-core', 'rake', 'rspec', 'thor')
|
623
|
+
end
|
624
|
+
|
625
|
+
desc 'more', 'Install merb-more from rubygems'
|
626
|
+
method_options "--merb-root" => :optional
|
627
|
+
def more
|
628
|
+
refresh_from_gems 'merb-more'
|
629
|
+
ensure_bin_wrapper_for('merb-gen')
|
630
|
+
end
|
631
|
+
|
632
|
+
desc 'plugins', 'Install merb-plugins from rubygems'
|
633
|
+
method_options "--merb-root" => :optional
|
634
|
+
def plugins
|
635
|
+
refresh_from_gems 'merb-plugins'
|
636
|
+
end
|
637
|
+
|
638
|
+
desc 'dm_core', 'Install dm-core from rubygems'
|
639
|
+
method_options "--merb-root" => :optional
|
640
|
+
def dm_core
|
641
|
+
refresh_from_gems 'extlib', 'dm-core'
|
642
|
+
end
|
643
|
+
|
644
|
+
desc 'dm_more', 'Install dm-more from rubygems'
|
645
|
+
method_options "--merb-root" => :optional
|
646
|
+
def dm_more
|
647
|
+
refresh_from_gems 'extlib', 'dm-core', 'dm-more'
|
648
|
+
end
|
649
|
+
|
650
|
+
desc 'do [ADAPTER, ...]', 'Install data_objects and optional adapter'
|
651
|
+
method_options "--merb-root" => :optional
|
652
|
+
def do(*adapters)
|
653
|
+
refresh_from_gems 'data_objects'
|
654
|
+
adapters.each do |adapter|
|
655
|
+
if adapter && DO_ADAPTERS.include?(adapter)
|
656
|
+
refresh_from_gems "do_#{adapter}"
|
657
|
+
elsif adapter
|
658
|
+
error "Unknown DO adapter '#{adapter}'"
|
659
|
+
end
|
660
|
+
end
|
661
|
+
end
|
662
|
+
|
663
|
+
desc 'minigems', 'Install minigems (system-wide)'
|
664
|
+
def minigems
|
665
|
+
if Merb.install_gem('minigems')
|
666
|
+
system("#{Gem.ruby} -S minigem install")
|
667
|
+
end
|
668
|
+
end
|
669
|
+
|
670
|
+
# Pull from RubyForge and install.
|
671
|
+
def refresh_from_gems(*components)
|
672
|
+
opts = components.last.is_a?(Hash) ? components.pop : {}
|
673
|
+
gems = Gems.new
|
674
|
+
gems.options = options
|
675
|
+
components.all? { |name| gems.install_gem(name, opts) }
|
676
|
+
end
|
677
|
+
|
678
|
+
end
|
679
|
+
|
680
|
+
# Retrieve latest Merb versions from git and optionally install them.
|
681
|
+
#
|
682
|
+
# Note: the --sources option takes a path to a YAML file
|
683
|
+
# with a regular Hash mapping gem names to git urls.
|
684
|
+
#
|
685
|
+
# Examples:
|
686
|
+
#
|
687
|
+
# thor merb:edge
|
688
|
+
# thor merb:edge --install
|
689
|
+
# thor merb:edge --merb-root ./path/to/your/app
|
690
|
+
# thor merb:edge --sources ./path/to/sources.yml
|
691
|
+
|
692
|
+
desc 'edge', 'Install extlib, merb-core and merb-more from git HEAD'
|
693
|
+
method_options "--merb-root" => :optional,
|
694
|
+
"--sources" => :optional,
|
695
|
+
"--install" => :boolean
|
696
|
+
def edge
|
697
|
+
edge = Edge.new
|
698
|
+
edge.options = options
|
699
|
+
edge.core
|
700
|
+
edge.more
|
701
|
+
edge.custom
|
702
|
+
end
|
703
|
+
|
704
|
+
class Edge < Thor
|
705
|
+
|
706
|
+
# The Edge tasks deal with known gems from the bleeding edge; available
|
707
|
+
# as shortcuts to Merb and DataMapper gems.
|
708
|
+
#
|
709
|
+
# These are pulled from git and optionally installed into into the
|
710
|
+
# desired gems dir (either system-wide or into the application's
|
711
|
+
# gems directory).
|
712
|
+
|
713
|
+
include MerbThorHelper
|
714
|
+
|
715
|
+
# Gets latest gem versions from git - optionally installs them.
|
716
|
+
#
|
717
|
+
# Note: the --sources option takes a path to a YAML file
|
718
|
+
# with a regular Hash mapping gem names to git urls,
|
719
|
+
# allowing pulling forks of the official repositories.
|
720
|
+
#
|
721
|
+
# Examples:
|
722
|
+
#
|
723
|
+
# thor merb:edge:core
|
724
|
+
# thor merb:edge:core --install
|
725
|
+
# thor merb:edge:core --merb-root ./path/to/your/app
|
726
|
+
# thor merb:edge:core --sources ./path/to/sources.yml
|
727
|
+
|
728
|
+
desc 'core', 'Update extlib and merb-core from git HEAD'
|
729
|
+
method_options "--merb-root" => :optional,
|
730
|
+
"--sources" => :optional,
|
731
|
+
"--install" => :boolean
|
732
|
+
def core
|
733
|
+
refresh_from_source 'thor'
|
734
|
+
refresh_from_source 'extlib', 'merb-core'
|
735
|
+
ensure_bin_wrapper_for('merb-core', 'rake', 'rspec', 'thor')
|
736
|
+
end
|
737
|
+
|
738
|
+
desc 'more', 'Update merb-more from git HEAD'
|
739
|
+
method_options "--merb-root" => :optional,
|
740
|
+
"--sources" => :optional,
|
741
|
+
"--install" => :boolean
|
742
|
+
def more
|
743
|
+
refresh_from_source 'merb-more'
|
744
|
+
ensure_bin_wrapper_for('merb-gen')
|
745
|
+
end
|
746
|
+
|
747
|
+
desc 'plugins', 'Update merb-plugins from git HEAD'
|
748
|
+
method_options "--merb-root" => :optional,
|
749
|
+
"--sources" => :optional,
|
750
|
+
"--install" => :boolean
|
751
|
+
def plugins
|
752
|
+
refresh_from_source 'merb-plugins'
|
753
|
+
end
|
754
|
+
|
755
|
+
desc 'dm_core', 'Update dm-core from git HEAD'
|
756
|
+
method_options "--merb-root" => :optional,
|
757
|
+
"--sources" => :optional,
|
758
|
+
"--install" => :boolean
|
759
|
+
def dm_core
|
760
|
+
refresh_from_source 'extlib', 'dm-core'
|
761
|
+
end
|
762
|
+
|
763
|
+
desc 'dm_more', 'Update dm-more from git HEAD'
|
764
|
+
method_options "--merb-root" => :optional,
|
765
|
+
"--sources" => :optional,
|
766
|
+
"--install" => :boolean
|
767
|
+
def dm_more
|
768
|
+
refresh_from_source 'extlib', 'dm-core', 'dm-more'
|
769
|
+
end
|
770
|
+
|
771
|
+
desc 'do [ADAPTER, ...]', 'Install data_objects and optional adapter'
|
772
|
+
method_options "--merb-root" => :optional,
|
773
|
+
"--sources" => :optional,
|
774
|
+
"--install" => :boolean
|
775
|
+
def do(*adapters)
|
776
|
+
source = Source.new
|
777
|
+
source.options = options
|
778
|
+
source.clone('do')
|
779
|
+
source.install('do/data_objects')
|
780
|
+
adapters.each do |adapter|
|
781
|
+
if adapter && DO_ADAPTERS.include?(adapter)
|
782
|
+
source.install("do/do_#{adapter}")
|
783
|
+
elsif adapter
|
784
|
+
error "Unknown DO adapter '#{adapter}'"
|
785
|
+
end
|
786
|
+
end
|
787
|
+
end
|
788
|
+
|
789
|
+
desc 'custom', 'Update all the custom repos from git HEAD'
|
790
|
+
method_options "--merb-root" => :optional,
|
791
|
+
"--sources" => :optional,
|
792
|
+
"--install" => :boolean
|
793
|
+
def custom
|
794
|
+
custom_repos = Merb.repos.keys - Merb.default_repos.keys
|
795
|
+
refresh_from_source *custom_repos
|
796
|
+
end
|
797
|
+
|
798
|
+
desc 'minigems', 'Install minigems from git HEAD (system-wide)'
|
799
|
+
method_options "--merb-root" => :optional,
|
800
|
+
"--sources" => :optional
|
801
|
+
def minigems
|
802
|
+
source = Source.new
|
803
|
+
source.options = options
|
804
|
+
source.clone('minigems')
|
805
|
+
if Merb.install_gem_from_src(File.join(source_dir, 'minigems'))
|
806
|
+
system("#{Gem.ruby} -S minigem install")
|
807
|
+
end
|
808
|
+
end
|
809
|
+
|
810
|
+
# Pull from git and optionally install the resulting gems.
|
811
|
+
def refresh_from_source(*components)
|
812
|
+
opts = components.last.is_a?(Hash) ? components.pop : {}
|
813
|
+
source = Source.new
|
814
|
+
source.options = options
|
815
|
+
components.each do |name|
|
816
|
+
source.clone(name)
|
817
|
+
source.install_from_src(name, opts) if options[:install]
|
818
|
+
end
|
819
|
+
end
|
820
|
+
|
821
|
+
end
|
822
|
+
|
823
|
+
class Source < Thor
|
824
|
+
|
825
|
+
# The Source tasks deal with gem source packages - mainly from github.
|
826
|
+
# Any directory inside ./src is regarded as a gem that can be packaged
|
827
|
+
# and installed from there into the desired gems dir (either system-wide
|
828
|
+
# or into the application's gems directory).
|
829
|
+
|
830
|
+
include MerbThorHelper
|
831
|
+
|
832
|
+
# Install a particular gem from source.
|
833
|
+
#
|
834
|
+
# If a local ./gems dir is found, or --merb-root is given
|
835
|
+
# the gems will be installed locally into that directory.
|
836
|
+
#
|
837
|
+
# Note that this task doesn't retrieve any (new) source from git;
|
838
|
+
# To update and install you'd execute the following two tasks:
|
839
|
+
#
|
840
|
+
# thor merb:source:update merb-core
|
841
|
+
# thor merb:source:install merb-core
|
842
|
+
#
|
843
|
+
# Alternatively, look at merb:edge and merb:edge:* with --install.
|
844
|
+
#
|
845
|
+
# Examples:
|
846
|
+
#
|
847
|
+
# thor merb:source:install merb-core
|
848
|
+
# thor merb:source:install merb-more
|
849
|
+
# thor merb:source:install merb-more/merb-slices
|
850
|
+
# thor merb:source:install merb-plugins/merb_helpers
|
851
|
+
# thor merb:source:install merb-core --merb-root ./path/to/your/app
|
852
|
+
|
853
|
+
desc 'install GEM_NAME', 'Install a rubygem from (git) source'
|
854
|
+
method_options "--merb-root" => :optional
|
855
|
+
def install(name)
|
856
|
+
message "Installing #{name}..."
|
857
|
+
install_from_src(name)
|
858
|
+
rescue => e
|
859
|
+
error "Failed to install #{name} (#{e.message})"
|
860
|
+
end
|
861
|
+
|
862
|
+
# Clone a git repository into ./src. The repository can be
|
863
|
+
# a direct git url or a known -named- repository.
|
864
|
+
#
|
865
|
+
# Examples:
|
866
|
+
#
|
867
|
+
# thor merb:source:clone dm-core
|
868
|
+
# thor merb:source:clone dm-core --sources ./path/to/sources.yml
|
869
|
+
# thor merb:source:clone git://github.com/sam/dm-core.git
|
870
|
+
|
871
|
+
desc 'clone REPOSITORY', 'Clone a git repository into ./src'
|
872
|
+
method_options "--sources" => :optional
|
873
|
+
def clone(repository)
|
874
|
+
if repository =~ /^git:\/\//
|
875
|
+
repository_url = repository
|
876
|
+
elsif url = Merb.repos(options[:sources])[repository]
|
877
|
+
repository_url = url
|
878
|
+
end
|
879
|
+
|
880
|
+
if repository_url
|
881
|
+
repository_name = repository_url[/([\w+|-]+)\.git/u, 1]
|
882
|
+
fork_name = repository_url[/.com\/+?(.+)\/.+\.git/u, 1]
|
883
|
+
local_repo_path = "#{source_dir}/#{repository_name}"
|
884
|
+
|
885
|
+
if File.directory?(local_repo_path)
|
886
|
+
puts "\n#{repository_name} repository exists, updating or branching instead of cloning..."
|
887
|
+
FileUtils.cd(local_repo_path) do
|
888
|
+
|
889
|
+
# To avoid conflicts we need to set a remote branch for non official repos
|
890
|
+
existing_repos = `git remote -v`.split("\n").map{|branch| branch.split(/\s+/)}
|
891
|
+
origin_repo_url = existing_repos.detect{ |r| r.first == "origin" }.last
|
892
|
+
|
893
|
+
# Pull from the original repository - no branching needed
|
894
|
+
if repository_url == origin_repo_url
|
895
|
+
puts "Pulling from #{repository_url}"
|
896
|
+
system %{
|
897
|
+
git fetch
|
898
|
+
git checkout master
|
899
|
+
git rebase origin/master
|
900
|
+
}
|
901
|
+
# Update and switch to a branch for a particular github fork
|
902
|
+
elsif existing_repos.map{ |r| r.last }.include?(repository_url)
|
903
|
+
puts "Switching to remote branch: #{fork_name}"
|
904
|
+
`git checkout -b #{fork_name} #{fork_name}/master`
|
905
|
+
`git rebase #{fork_name}/master`
|
906
|
+
# Create a new remote branch for a particular github fork
|
907
|
+
else
|
908
|
+
puts "Add a new remote branch: #{fork_name}"
|
909
|
+
`git remote add -f #{fork_name} #{repository_url}`
|
910
|
+
`git checkout -b#{fork_name} #{fork_name}/master`
|
911
|
+
end
|
912
|
+
end
|
913
|
+
else
|
914
|
+
FileUtils.cd(source_dir) do
|
915
|
+
puts "\nCloning #{repository_name} repository from #{repository_url}..."
|
916
|
+
system("git clone --depth 1 #{repository_url} ")
|
917
|
+
end
|
918
|
+
end
|
919
|
+
|
920
|
+
# We don't want to have our source directory under sudo permissions
|
921
|
+
# even if clone (with --install) is called with sudo. Maybe there's
|
922
|
+
# a better way, but this works.
|
923
|
+
neutralise_sudo_for(local_repo_path)
|
924
|
+
else
|
925
|
+
error "No valid repository url given"
|
926
|
+
end
|
927
|
+
end
|
928
|
+
|
929
|
+
# Update a specific gem source directory from git. See #clone.
|
930
|
+
|
931
|
+
desc 'update REPOSITORY', 'Update a git repository in ./src'
|
932
|
+
alias :update :clone
|
933
|
+
|
934
|
+
# Update all gem sources from git - based on the current branch.
|
935
|
+
# Optionally install the gems when --install is specified.
|
936
|
+
|
937
|
+
desc 'refresh', 'Pull fresh copies of all source gems'
|
938
|
+
method_options "--install" => :boolean
|
939
|
+
def refresh
|
940
|
+
repos = Dir["#{source_dir}/*"]
|
941
|
+
repos.each do |repo|
|
942
|
+
next unless File.directory?(repo) && File.exists?(File.join(repo, '.git'))
|
943
|
+
FileUtils.cd(repo) do
|
944
|
+
gem_name = File.basename(repo)
|
945
|
+
message "Refreshing #{gem_name}"
|
946
|
+
system %{git fetch}
|
947
|
+
branch = `git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1) /'`[/\* (.+)/, 1]
|
948
|
+
system %{git rebase #{branch}}
|
949
|
+
install(gem_name) if options[:install]
|
950
|
+
end
|
951
|
+
neutralise_sudo_for(repo)
|
952
|
+
end
|
953
|
+
end
|
954
|
+
|
955
|
+
def install_from_src(name, opts = {})
|
956
|
+
gem_src_dir = File.join(source_dir, name)
|
957
|
+
defaults = {}
|
958
|
+
defaults[:install_dir] = gem_dir if gem_dir
|
959
|
+
Merb.install_gem_from_src(gem_src_dir, defaults.merge(opts))
|
960
|
+
end
|
961
|
+
|
962
|
+
end
|
963
|
+
|
964
|
+
class Gems < Thor
|
965
|
+
|
966
|
+
# The Gems tasks deal directly with rubygems, either through remotely
|
967
|
+
# available sources (rubyforge for example) or by searching the
|
968
|
+
# system-wide gem cache for matching gems. The gems are installed from
|
969
|
+
# there into the desired gems dir (either system-wide or into the
|
970
|
+
# application's gems directory).
|
971
|
+
|
972
|
+
include MerbThorHelper
|
973
|
+
|
974
|
+
# Install a gem and its dependencies.
|
975
|
+
#
|
976
|
+
# If a local ./gems dir is found, or --merb-root is given
|
977
|
+
# the gems will be installed locally into that directory.
|
978
|
+
#
|
979
|
+
# The option --cache will look in the system's gem cache
|
980
|
+
# for the latest version and install it in the apps' gems.
|
981
|
+
# This is particularly handy for gems that aren't available
|
982
|
+
# through rubyforge.org - like in-house merb slices etc.
|
983
|
+
#
|
984
|
+
# Examples:
|
985
|
+
#
|
986
|
+
# thor merb:gems:install merb-core
|
987
|
+
# thor merb:gems:install merb-core --cache
|
988
|
+
# thor merb:gems:install merb-core --version 0.9.7
|
989
|
+
# thor merb:gems:install merb-core --merb-root ./path/to/your/app
|
990
|
+
|
991
|
+
desc 'install GEM_NAME', 'Install a gem from rubygems'
|
992
|
+
method_options "--version" => :optional,
|
993
|
+
"--merb-root" => :optional,
|
994
|
+
"--cache" => :boolean,
|
995
|
+
"--binaries" => :boolean
|
996
|
+
def install(name)
|
997
|
+
message "Installing #{name}..."
|
998
|
+
install_gem(name, :version => options[:version], :cache => options[:cache])
|
999
|
+
ensure_bin_wrapper_for(name) if options[:binaries]
|
1000
|
+
rescue => e
|
1001
|
+
error "Failed to install #{name} (#{e.message})"
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
# Update a gem and its dependencies.
|
1005
|
+
#
|
1006
|
+
# If a local ./gems dir is found, or --merb-root is given
|
1007
|
+
# the gems will be installed locally into that directory.
|
1008
|
+
#
|
1009
|
+
# The option --cache will look in the system's gem cache
|
1010
|
+
# for the latest version and install it in the apps' gems.
|
1011
|
+
# This is particularly handy for gems that aren't available
|
1012
|
+
# through rubyforge.org - like in-house merb slices etc.
|
1013
|
+
#
|
1014
|
+
# Examples:
|
1015
|
+
#
|
1016
|
+
# thor merb:gems:update merb-core
|
1017
|
+
# thor merb:gems:update merb-core --cache
|
1018
|
+
# thor merb:gems:update merb-core --merb-root ./path/to/your/app
|
1019
|
+
|
1020
|
+
desc 'update GEM_NAME', 'Update a gem from rubygems'
|
1021
|
+
method_options "--merb-root" => :optional,
|
1022
|
+
"--cache" => :boolean,
|
1023
|
+
"--binaries" => :boolean
|
1024
|
+
def update(name)
|
1025
|
+
message "Updating #{name}..."
|
1026
|
+
version = nil
|
1027
|
+
if gem_dir && (gemspec_path = Dir[File.join(gem_dir, 'specifications', "#{name}-*.gemspec")].last)
|
1028
|
+
gemspec = Gem::Specification.load(gemspec_path)
|
1029
|
+
version = Gem::Requirement.new [">#{gemspec.version}"]
|
1030
|
+
end
|
1031
|
+
if install_gem(name, :version => version, :cache => options[:cache])
|
1032
|
+
ensure_bin_wrapper_for(name) if options[:binaries]
|
1033
|
+
elsif gemspec
|
1034
|
+
message "current version is: #{gemspec.version}"
|
1035
|
+
end
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
# Uninstall a gem - ignores dependencies.
|
1039
|
+
#
|
1040
|
+
# If a local ./gems dir is found, or --merb-root is given
|
1041
|
+
# the gems will be uninstalled locally from that directory.
|
1042
|
+
#
|
1043
|
+
# The --all option indicates that all versions of the gem should be
|
1044
|
+
# uninstalled. If --version isn't set, and multiple versions are
|
1045
|
+
# available, you will be prompted to pick one - or all.
|
1046
|
+
#
|
1047
|
+
# Examples:
|
1048
|
+
#
|
1049
|
+
# thor merb:gems:uninstall merb-core
|
1050
|
+
# thor merb:gems:uninstall merb-core --all
|
1051
|
+
# thor merb:gems:uninstall merb-core --version 0.9.7
|
1052
|
+
# thor merb:gems:uninstall merb-core --merb-root ./path/to/your/app
|
1053
|
+
|
1054
|
+
desc 'uninstall GEM_NAME', 'Uninstall a gem'
|
1055
|
+
method_options "--version" => :optional,
|
1056
|
+
"--merb-root" => :optional,
|
1057
|
+
"--all" => :boolean
|
1058
|
+
def uninstall(name)
|
1059
|
+
message "Uninstalling #{name}..."
|
1060
|
+
uninstall_gem(name, :version => options[:version], :all => options[:all])
|
1061
|
+
rescue => e
|
1062
|
+
error "Failed to uninstall #{name} (#{e.message})"
|
1063
|
+
end
|
1064
|
+
|
1065
|
+
# Completely remove a gem and all its versions - ignores dependencies.
|
1066
|
+
#
|
1067
|
+
# If a local ./gems dir is found, or --merb-root is given
|
1068
|
+
# the gems will be uninstalled locally from that directory.
|
1069
|
+
#
|
1070
|
+
# Examples:
|
1071
|
+
#
|
1072
|
+
# thor merb:gems:wipe merb-core
|
1073
|
+
# thor merb:gems:wipe merb-core --merb-root ./path/to/your/app
|
1074
|
+
|
1075
|
+
desc 'wipe GEM_NAME', 'Remove a gem completely'
|
1076
|
+
method_options "--merb-root" => :optional
|
1077
|
+
def wipe(name)
|
1078
|
+
message "Wiping #{name}..."
|
1079
|
+
uninstall_gem(name, :all => true, :executables => true)
|
1080
|
+
rescue => e
|
1081
|
+
error "Failed to wipe #{name} (#{e.message})"
|
1082
|
+
end
|
1083
|
+
|
1084
|
+
# Refresh all local gems by uninstalling them and subsequently reinstall
|
1085
|
+
# the latest versions from stable sources.
|
1086
|
+
#
|
1087
|
+
# A local ./gems dir is required, or --merb-root is given
|
1088
|
+
# the gems will be uninstalled locally from that directory.
|
1089
|
+
#
|
1090
|
+
# Examples:
|
1091
|
+
#
|
1092
|
+
# thor merb:gems:refresh
|
1093
|
+
# thor merb:gems:refresh --merb-root ./path/to/your/app
|
1094
|
+
|
1095
|
+
desc 'refresh', 'Refresh all local gems by installing only the most recent versions'
|
1096
|
+
method_options "--merb-root" => :optional,
|
1097
|
+
"--cache" => :boolean,
|
1098
|
+
"--binaries" => :boolean
|
1099
|
+
def refresh
|
1100
|
+
if gem_dir
|
1101
|
+
gem_names = []
|
1102
|
+
local_gemspecs.each do |spec|
|
1103
|
+
gem_names << spec.name unless gem_names.include?(spec.name)
|
1104
|
+
uninstall_gem(spec.name, :version => spec.version)
|
1105
|
+
end
|
1106
|
+
gem_names.each { |name| install_gem(name) }
|
1107
|
+
# if options[:binaries] is set this is already taken care of - skip it
|
1108
|
+
ensure_bin_wrapper_for_core_components unless options[:binaries]
|
1109
|
+
else
|
1110
|
+
error "The refresh task only works with local gems"
|
1111
|
+
end
|
1112
|
+
end
|
1113
|
+
|
1114
|
+
# This task should be executed as part of a deployment setup, where
|
1115
|
+
# the deployment system runs this after the app has been installed.
|
1116
|
+
# Usually triggered by Capistrano, God...
|
1117
|
+
#
|
1118
|
+
# It will regenerate gems from the bundled gems cache for any gem
|
1119
|
+
# that has C extensions - which need to be recompiled for the target
|
1120
|
+
# deployment platform.
|
1121
|
+
|
1122
|
+
desc 'redeploy', 'Recreate any binary gems on the target deployment platform'
|
1123
|
+
def redeploy
|
1124
|
+
require 'tempfile' # for
|
1125
|
+
if gem_dir && File.directory?(cache_dir = File.join(gem_dir, 'cache'))
|
1126
|
+
local_gemspecs.each do |gemspec|
|
1127
|
+
unless gemspec.extensions.empty?
|
1128
|
+
if File.exists?(gem_file = File.join(cache_dir, "#{gemspec.full_name}.gem"))
|
1129
|
+
gem_file_copy = File.join(Dir::tmpdir, File.basename(gem_file))
|
1130
|
+
# Copy the gem to a temporary file, because otherwise RubyGems/FileUtils
|
1131
|
+
# will complain about copying identical files (same source/destination).
|
1132
|
+
FileUtils.cp(gem_file, gem_file_copy)
|
1133
|
+
install_gem(gem_file_copy, :install_dir => gem_dir)
|
1134
|
+
File.delete(gem_file_copy)
|
1135
|
+
end
|
1136
|
+
end
|
1137
|
+
end
|
1138
|
+
else
|
1139
|
+
error "No application local gems directory found"
|
1140
|
+
end
|
1141
|
+
end
|
1142
|
+
|
1143
|
+
# Install gem with some default options.
|
1144
|
+
def install_gem(name, opts = {})
|
1145
|
+
defaults = {}
|
1146
|
+
defaults[:cache] = false unless gem_dir
|
1147
|
+
defaults[:install_dir] = gem_dir if gem_dir
|
1148
|
+
Merb.install_gem(name, defaults.merge(opts))
|
1149
|
+
end
|
1150
|
+
|
1151
|
+
# Uninstall gem with some default options.
|
1152
|
+
def uninstall_gem(name, opts = {})
|
1153
|
+
defaults = { :ignore => true, :executables => true }
|
1154
|
+
defaults[:install_dir] = gem_dir if gem_dir
|
1155
|
+
Merb.uninstall_gem(name, defaults.merge(opts))
|
1156
|
+
end
|
1157
|
+
|
1158
|
+
protected
|
1159
|
+
|
1160
|
+
def local_gemspecs(directory = gem_dir)
|
1161
|
+
if File.directory?(specs_dir = File.join(directory, 'specifications'))
|
1162
|
+
Dir[File.join(specs_dir, '*.gemspec')].map do |gemspec_path|
|
1163
|
+
gemspec = Gem::Specification.load(gemspec_path)
|
1164
|
+
gemspec.loaded_from = gemspec_path
|
1165
|
+
gemspec
|
1166
|
+
end
|
1167
|
+
else
|
1168
|
+
[]
|
1169
|
+
end
|
1170
|
+
end
|
1171
|
+
|
1172
|
+
end
|
1173
|
+
|
1174
|
+
class Tasks < Thor
|
1175
|
+
|
1176
|
+
include MerbThorHelper
|
1177
|
+
|
1178
|
+
# Install Thor, Rake and RSpec into the local gems dir, by copying it from
|
1179
|
+
# the system-wide rubygems cache - which is OK since we needed it to run
|
1180
|
+
# this task already.
|
1181
|
+
#
|
1182
|
+
# After this we don't need the system-wide rubygems anymore, as all required
|
1183
|
+
# executables are available in the local ./bin directory.
|
1184
|
+
#
|
1185
|
+
# RSpec is needed here because source installs might fail when running
|
1186
|
+
# rake tasks where spec/rake/spectask has been required.
|
1187
|
+
|
1188
|
+
desc 'setup', 'Install Thor, Rake and RSpec in the local gems dir'
|
1189
|
+
method_options "--merb-root" => :optional
|
1190
|
+
def setup
|
1191
|
+
if $0 =~ /^(\.\/)?bin\/thor$/
|
1192
|
+
error "You cannot run the setup from #{$0} - try #{File.basename($0)} merb:tasks:setup instead"
|
1193
|
+
return
|
1194
|
+
end
|
1195
|
+
create_if_missing(File.join(working_dir, 'gems'))
|
1196
|
+
Merb.install_gem('thor', :cache => true, :install_dir => gem_dir)
|
1197
|
+
Merb.install_gem('rake', :cache => true, :install_dir => gem_dir)
|
1198
|
+
Merb.install_gem('rspec', :cache => true, :install_dir => gem_dir)
|
1199
|
+
ensure_bin_wrapper_for('thor', 'rake', 'rspec')
|
1200
|
+
end
|
1201
|
+
|
1202
|
+
# Get the latest merb.thor and install it into the working dir.
|
1203
|
+
|
1204
|
+
desc 'update', 'Fetch the latest merb.thor and install it locally'
|
1205
|
+
def update
|
1206
|
+
require 'open-uri'
|
1207
|
+
url = 'http://merbivore.com/merb.thor'
|
1208
|
+
remote_file = open(url)
|
1209
|
+
File.open(File.join(working_dir, 'merb.thor'), 'w') do |f|
|
1210
|
+
f.write(remote_file.read)
|
1211
|
+
end
|
1212
|
+
success "Installed the latest merb.thor"
|
1213
|
+
rescue OpenURI::HTTPError
|
1214
|
+
error "Error opening #{url}"
|
1215
|
+
rescue => e
|
1216
|
+
error "An error occurred (#{e.message})"
|
1217
|
+
end
|
1218
|
+
|
1219
|
+
end
|
1220
|
+
|
1221
|
+
end
|