-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmerb.thor
822 lines (721 loc) · 27.6 KB
/
merb.thor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
#!/usr/bin/env ruby
require 'rubygems'
require 'rubygems/dependency_installer'
require 'rubygems/uninstaller'
require 'thor'
require 'fileutils'
require 'yaml'
module MerbThorHelper
private
# The current working directory, or Merb app root (--merb-root option).
def working_dir
@_working_dir ||= File.expand_path(options['merb-root'] || Dir.pwd)
end
# We should have a ./src dir for local and system-wide management.
def source_dir
@_source_dir ||= File.join(working_dir, 'src')
create_if_missing(@_source_dir)
@_source_dir
end
# If a local ./gems dir is found, it means we are in a Merb app.
def application?
gem_dir
end
# If a local ./gems dir is found, return it.
def gem_dir
if File.directory?(dir = File.join(working_dir, 'gems'))
dir
end
end
# If we're in a Merb app, we can have a ./bin directory;
# create it if it's not there.
def bin_dir
@_bin_dir ||= begin
if gem_dir
dir = File.join(working_dir, 'bin')
create_if_missing(dir)
dir
end
end
end
# Helper to create dir unless it exists.
def create_if_missing(path)
FileUtils.mkdir(path) unless File.exists?(path)
end
# Create a modified executable wrapper in the app's ./bin directory.
def ensure_local_bin_for(*gems)
if bin_dir && File.directory?(bin_dir)
gems.each do |gem|
if gemspec_path = Dir[File.join(gem_dir, 'specifications', "#{gem}-*.gemspec")].last
spec = Gem::Specification.load(gemspec_path)
spec.executables.each do |exec|
if File.exists?(executable = File.join(gem_dir, 'bin', exec))
local_executable = File.join(bin_dir, exec)
puts "Adding local executable #{local_executable}"
File.open(local_executable, 'w', 0755) do |f|
f.write(executable_wrapper(spec, exec))
end
end
end
end
end
end
end
def executable_wrapper(spec, bin_file_name)
<<-TEXT
#!/usr/bin/env #{RbConfig::CONFIG["ruby_install_name"]}
#
# This file was generated by merb.thor.
#
# The application '#{spec.name}' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'rubygems'
if File.directory?(gems_dir = File.join(File.dirname(__FILE__), '..', 'gems'))
$BUNDLE = true; Gem.clear_paths; Gem.path.unshift(gems_dir)
end
version = "#{Gem::Requirement.default}"
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
version = $1
ARGV.shift
end
gem '#{spec.name}', version
load '#{bin_file_name}'
TEXT
end
end
# TODO
# - a task to figure out an app's dependencies
# - pulling a specific UUID/Tag (gitspec hash) with clone/update
# - a 'deploy' task (in addition to 'redeploy' ?)
# - eventually take a --orm option for the 'merb-stack' type of tasks
class Merb < Thor
class SourcePathMissing < Exception
end
class GemPathMissing < Exception
end
class GemInstallError < Exception
end
class GemUninstallError < Exception
end
# Install a Merb stack from stable RubyForge gems. Optionally install a
# suitable Rack adapter/server when setting --adapter to one of the
# following: mongrel, emongrel, thin or ebb.
desc 'stable', 'Install extlib, merb-core and merb-more from rubygems'
method_options "--merb-root" => :optional,
"--adapter" => :optional
def stable
adapters = %w[mongrel emongrel thin ebb]
stable = Stable.new
stable.options = options
if stable.core && stable.more
puts "Installed extlib, merb-core and merb-more"
if options[:adapter] && adapters.include?(options[:adapter]) &&
stable.refresh_from_gems(options[:adapter])
puts "Installed #{options[:adapter]}"
elsif options[:adapter]
puts "Please specify one of the following adapters: #{adapters.join(' ')}"
end
end
end
class Stable < Thor
# The Stable tasks deal with known -stable- gems; available
# as shortcuts to Merb and DataMapper gems.
#
# These are pulled from rubyforge and installed into into the
# desired gems dir (either system-wide or into the application's
# gems directory).
include MerbThorHelper
# Gets latest gem versions from RubyForge and installs them.
#
# Examples:
#
# thor merb:edge:core
# thor merb:edge:core --merb-root ./path/to/your/app
# thor merb:edge:core --sources ./path/to/sources.yml
desc 'core', 'Install extlib and merb-core from git HEAD'
method_options "--merb-root" => :optional
def core
refresh_from_gems 'extlib', 'merb-core'
ensure_local_bin_for('merb-core', 'rake', 'rspec', 'thor')
end
desc 'more', 'Install merb-more from rubygems'
method_options "--merb-root" => :optional
def more
refresh_from_gems 'merb-more'
ensure_local_bin_for('merb-gen')
end
desc 'plugins', 'Install merb-plugins from rubygems'
method_options "--merb-root" => :optional
def plugins
refresh_from_gems 'merb-plugins'
end
desc 'dm_core', 'Install dm-core from rubygems'
method_options "--merb-root" => :optional
def dm_core
refresh_from_gems 'extlib', 'dm-core'
end
desc 'dm_more', 'Install dm-more from rubygems'
method_options "--merb-root" => :optional
def dm_more
refresh_from_gems 'extlib', 'dm-core', 'dm-more'
end
# Pull from RubyForge and install.
def refresh_from_gems(*components)
gems = Gems.new
gems.options = options
components.all? { |name| gems.install(name) }
end
end
# Retrieve latest Merb versions from git and optionally install them.
#
# Note: the --sources option takes a path to a YAML file
# with a regular Hash mapping gem names to git urls.
#
# Examples:
#
# thor merb:edge
# thor merb:edge --install
# thor merb:edge --merb-root ./path/to/your/app
# thor merb:edge --sources ./path/to/sources.yml
desc 'edge', 'Install extlib, merb-core and merb-more from git HEAD'
method_options "--merb-root" => :optional,
"--sources" => :optional,
"--install" => :boolean
def edge
edge = Edge.new
edge.options = options
edge.core
edge.more
end
class Edge < Thor
# The Edge tasks deal with known gems from the bleeding edge; available
# as shortcuts to Merb and DataMapper gems.
#
# These are pulled from git and optionally installed into into the
# desired gems dir (either system-wide or into the application's
# gems directory).
include MerbThorHelper
# Gets latest gem versions from git - optionally installs them.
#
# Note: the --sources option takes a path to a YAML file
# with a regular Hash mapping gem names to git urls,
# allowing pulling forks of the official repositories.
#
# Examples:
#
# thor merb:edge:core
# thor merb:edge:core --install
# thor merb:edge:core --merb-root ./path/to/your/app
# thor merb:edge:core --sources ./path/to/sources.yml
desc 'core', 'Update extlib and merb-core from git HEAD'
method_options "--merb-root" => :optional,
"--sources" => :optional,
"--install" => :boolean
def core
refresh_from_source 'thor', 'extlib', 'merb-core'
ensure_local_bin_for('merb-core', 'rake', 'rspec', 'thor')
end
desc 'more', 'Update merb-more from git HEAD'
method_options "--merb-root" => :optional,
"--sources" => :optional,
"--install" => :boolean
def more
refresh_from_source 'merb-more'
ensure_local_bin_for('merb-gen')
end
desc 'plugins', 'Update merb-plugins from git HEAD'
method_options "--merb-root" => :optional,
"--sources" => :optional,
"--install" => :boolean
def plugins
refresh_from_source 'merb-plugins'
end
desc 'dm_core', 'Update dm-core from git HEAD'
method_options "--merb-root" => :optional,
"--sources" => :optional,
"--install" => :boolean
def dm_core
refresh_from_source 'extlib', 'dm-core'
end
desc 'dm_more', 'Update dm-more from git HEAD'
method_options "--merb-root" => :optional,
"--sources" => :optional,
"--install" => :boolean
def dm_more
refresh_from_source 'extlib', 'dm-core', 'dm-more'
end
private
# Pull from git and optionally install the resulting gems.
def refresh_from_source(*components)
source = Source.new
source.options = options
components.each do |name|
source.clone(name)
source.install(name) if options[:install]
end
end
end
class Source < Thor
# The Source tasks deal with gem source packages - mainly from github.
# Any directory inside ./src is regarded as a gem that can be packaged
# and installed from there into the desired gems dir (either system-wide
# or into the application's gems directory).
include MerbThorHelper
# Install a particular gem from source.
#
# If a local ./gems dir is found, or --merb-root is given
# the gems will be installed locally into that directory.
#
# Note that this task doesn't retrieve any (new) source from git;
# To update and install you'd execute the following two tasks:
#
# thor merb:source:update merb-core
# thor merb:source:install merb-core
#
# Alternatively, look at merb:edge and merb:edge:* with --install.
#
# Examples:
#
# thor merb:source:install merb-core
# thor merb:source:install merb-more
# thor merb:source:install merb-more/merb-slices
# thor merb:source:install merb-plugins/merb_helpers
# thor merb:source:install merb-core --merb-root ./path/to/your/app
desc 'install GEM_NAME', 'Install a rubygem from (git) source'
method_options "--merb-root" => :optional
def install(name)
puts "Installing #{name}..."
gem_src_dir = File.join(source_dir, name)
opts = {}
opts[:install_dir] = gem_dir if gem_dir
Merb.install_gem_from_src(gem_src_dir, opts)
rescue Merb::SourcePathMissing
puts "Missing rubygem source path: #{gem_src_dir}"
rescue Merb::GemPathMissing
puts "Missing rubygems path: #{gem_dir}"
rescue => e
puts "Failed to install #{name} (#{e.message})"
end
# Clone a git repository into ./src. The repository can be
# a direct git url or a known -named- repository.
#
# Examples:
#
# thor merb:source:clone dm-core
# thor merb:source:clone dm-core --sources ./path/to/sources.yml
# thor merb:source:clone git://github.com/sam/dm-core.git
desc 'clone REPOSITORY', 'Clone a git repository into ./src'
method_options "--sources" => :optional
def clone(repository)
if repository =~ /^git:\/\//
repository_url = repository
elsif url = Merb.repos(options[:sources])[repository]
repository_url = url
end
if repository_url
repository_name = repository_url[/([\w+|-]+)\.git/u, 1]
fork_name = repository_url[/.com\/+?(.+)\/.+\.git/u, 1]
local_repo_path = "#{source_dir}/#{repository_name}"
if File.directory?(local_repo_path)
puts "\n#{repository_name} repository exists, updating or branching instead of cloning..."
FileUtils.cd(local_repo_path) do
# to avoid conflicts we need to set a remote branch for non official repos
existing_repos = `git remote -v`.split("\n").map{|branch| branch.split(/\s+/)}
origin_repo_url = existing_repos.detect{ |r| r.first == "origin" }.last
# pull from the original repository - no branching needed
if repository_url == origin_repo_url
puts "Pulling from #{repository_url}"
system %{
git fetch
git checkout master
git rebase origin/master
}
# update and switch to a branch for a particular github fork
elsif existing_repos.map{ |r| r.last }.include?(repository_url)
puts "Switching to remote branch: #{fork_name}"
`git checkout -b #{fork_name} #{fork_name}/master`
`git rebase #{fork_name}/master`
# create a new remote branch for a particular github fork
else
puts "Add a new remote branch: #{fork_name}"
`git remote add -f #{fork_name} #{repository_url}`
`git checkout -b#{fork_name} #{fork_name}/master`
end
end
else
FileUtils.cd(source_dir) do
puts "\nCloning #{repository_name} repository from #{repository_url}..."
system("git clone --depth=1 #{repository_url} ")
end
end
else
puts "No valid repository url given"
end
end
# Update a specific gem source directory from git. See #clone.
desc 'update REPOSITORY_URL', 'Update a git repository in ./src'
alias :update :clone
# Update all gem sources from git - based on the current branch.
desc 'refresh', 'Pull fresh copies of all source gems'
def refresh
repos = Dir["#{source_dir}/*"]
repos.each do |repo|
next unless File.directory?(repo) && File.exists?(File.join(repo, '.git'))
FileUtils.cd(repo) do
puts "Refreshing #{File.basename(repo)}"
branch = `git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1) /'`[/\* (.+)/, 1]
system %{git rebase #{branch}}
end
end
end
end
class Gems < Thor
# The Gems tasks deal directly with rubygems, either through remotely
# available sources (rubyforge for example) or by searching the
# system-wide gem cache for matching gems. The gems are installed from
# there into the desired gems dir (either system-wide or into the
# application's gems directory).
include MerbThorHelper
# Install a gem and its dependencies.
#
# If a local ./gems dir is found, or --merb-root is given
# the gems will be installed locally into that directory.
#
# The option --cache will look in the system's gem cache
# for the latest version and install it in the apps' gems.
# This is particularly handy for gems that aren't available
# through rubyforge.org - like in-house merb slices etc.
#
# Examples:
#
# thor merb:gems:install merb-core
# thor merb:gems:install merb-core --cache
# thor merb:gems:install merb-core --version 0.9.7
# thor merb:gems:install merb-core --merb-root ./path/to/your/app
desc 'install GEM_NAME', 'Install a gem from rubygems'
method_options "--version" => :optional,
"--merb-root" => :optional,
"--cache" => :boolean
def install(name)
puts "Installing #{name}..."
opts = {}
opts[:version] = options[:version]
opts[:cache] = options[:cache] if gem_dir
opts[:install_dir] = gem_dir if gem_dir
Merb.install_gem(name, opts)
rescue => e
puts "Failed to install #{name} (#{e.message})"
end
# Update a gem and its dependencies.
#
# If a local ./gems dir is found, or --merb-root is given
# the gems will be installed locally into that directory.
#
# The option --cache will look in the system's gem cache
# for the latest version and install it in the apps' gems.
# This is particularly handy for gems that aren't available
# through rubyforge.org - like in-house merb slices etc.
#
# Examples:
#
# thor merb:gems:update merb-core
# thor merb:gems:update merb-core --cache
# thor merb:gems:update merb-core --merb-root ./path/to/your/app
desc 'update GEM_NAME', 'Update a gem from rubygems'
method_options "--merb-root" => :optional,
"--cache" => :boolean
def update(name)
puts "Updating #{name}..."
opts = {}
if gem_dir
if gemspec_path = Dir[File.join(gem_dir, 'specifications', "#{name}-*.gemspec")].last
gemspec = Gem::Specification.load(gemspec_path)
opts[:version] = Gem::Requirement.new [">#{gemspec.version}"]
end
opts[:install_dir] = gem_dir
opts[:cache] = options[:cache]
end
Merb.install_gem(name, opts)
rescue => e
puts "Failed to update #{name} (#{e.message})"
end
# Uninstall a gem - ignores dependencies.
#
# If a local ./gems dir is found, or --merb-root is given
# the gems will be uninstalled locally from that directory.
#
# Examples:
#
# thor merb:gems:uninstall merb-core
# thor merb:gems:uninstall merb-core --all
# thor merb:gems:uninstall merb-core --version 0.9.7
# thor merb:gems:uninstall merb-core --merb-root ./path/to/your/app
desc 'install GEM_NAME', 'Install a gem from rubygems'
desc 'uninstall GEM_NAME', 'Uninstall a gem'
method_options "--version" => :optional,
"--merb-root" => :optional,
"--all" => :boolean
def uninstall(name)
puts "Uninstalling #{name}..."
opts = {}
opts[:ignore] = true
opts[:all] = options[:all]
opts[:executables] = true
opts[:version] = options[:version]
opts[:install_dir] = gem_dir if gem_dir
Merb.uninstall_gem(name, opts)
rescue => e
puts "Failed to uninstall #{name} (#{e.message})"
end
# Completely remove a gem and all its versions - ignores dependencies.
#
# If a local ./gems dir is found, or --merb-root is given
# the gems will be uninstalled locally from that directory.
#
# Examples:
#
# thor merb:gems:wipe merb-core
# thor merb:gems:wipe merb-core --merb-root ./path/to/your/app
desc 'wipe GEM_NAME', 'Remove a gem completely'
method_options "--merb-root" => :optional
def wipe(name)
puts "Wiping #{name}..."
opts = {}
opts[:ignore] = true
opts[:all] = true
opts[:executables] = true
opts[:install_dir] = gem_dir if gem_dir
Merb.uninstall_gem(name, opts)
rescue => e
puts "Failed to wipe #{name} (#{e.message})"
end
# This task should be executed as part of a deployment setup, where
# the deployment system runs this after the app has been installed.
# Usually triggered by Capistrano, God...
#
# It will regenerate gems from the bundled gems cache for any gem
# that has C extensions - which need to be recompiled for the target
# deployment platform.
desc 'redeploy', 'Recreate any binary gems on the target deployment platform'
def redeploy
require 'tempfile' # for
if File.directory?(specs_dir = File.join(gem_dir, 'specifications')) &&
File.directory?(cache_dir = File.join(gem_dir, 'cache'))
Dir[File.join(specs_dir, '*.gemspec')].each do |gemspec_path|
unless (gemspec = Gem::Specification.load(gemspec_path)).extensions.empty?
if File.exists?(gem_file = File.join(cache_dir, "#{gemspec.full_name}.gem"))
gem_file_copy = File.join(Dir::tmpdir, File.basename(gem_file))
# Copy the gem to a temporary file, because otherwise RubyGems/FileUtils
# will complain about copying identical files (same source/destination).
FileUtils.cp(gem_file, gem_file_copy)
Merb.install_gem(gem_file_copy, :install_dir => gem_dir)
File.delete(gem_file_copy)
end
end
end
else
puts "No application local gems directory found"
end
end
end
class << self
# Default Git repositories - pass source_config option
# to load a yaml configuration file.
def repos(source_config = nil)
@_repos ||= begin
repositories = {
'merb-core' => "git://github.com/wycats/merb-core.git",
'merb-more' => "git://github.com/wycats/merb-more.git",
'merb-plugins' => "git://github.com/wycats/merb-plugins.git",
'extlib' => "git://github.com/sam/extlib.git",
'dm-core' => "git://github.com/sam/dm-core.git",
'dm-more' => "git://github.com/sam/dm-more.git",
'thor' => "git://github.com/wycats/thor.git"
}
end
if source_config && File.exists?(source_config)
@_repos.merge(YAML.load(File.read(source_config)))
else
@_repos
end
end
# Install a gem - looks remotely and local gem cache;
# won't process rdoc or ri options.
def install_gem(gem, options = {})
from_cache = (options.key?(:cache) && options.delete(:cache))
if from_cache
install_gem_from_cache(gem, options)
else
version = options.delete(:version)
Gem.configuration.update_sources = false
installer = Gem::DependencyInstaller.new(options.merge(:user_install => false))
exception = nil
begin
installer.install gem, version
rescue Gem::InstallError => e
exception = e
rescue Gem::GemNotFoundException => e
if from_cache && gem_file = find_gem_in_cache(gem, version)
puts "Located #{gem} in gem cache..."
installer.install gem_file
else
exception = e
end
rescue => e
exception = e
end
if installer.installed_gems.empty? && exception
puts "Failed to install gem '#{gem}' (#{exception.message})"
end
installer.installed_gems.each do |spec|
puts "Successfully installed #{spec.full_name}"
end
end
end
# Install a gem - looks in the system's gem cache instead of remotely;
# won't process rdoc or ri options.
def install_gem_from_cache(gem, options = {})
version = options.delete(:version)
Gem.configuration.update_sources = false
installer = Gem::DependencyInstaller.new(options.merge(:user_install => false))
exception = nil
begin
if gem_file = find_gem_in_cache(gem, version)
puts "Located #{gem} in gem cache..."
installer.install gem_file
else
raise Gem::InstallError, "Unknown gem #{gem}"
end
rescue Gem::InstallError => e
exception = e
end
if installer.installed_gems.empty? && exception
puts "Failed to install gem '#{gem}' (#{e.message})"
end
installer.installed_gems.each do |spec|
puts "Successfully installed #{spec.full_name}"
end
end
# Install a gem from source - builds and packages it first then installs it.
def install_gem_from_src(gem_src_dir, options = {})
raise SourcePathMissing unless File.directory?(gem_src_dir)
raise GemPathMissing if options[:install_dir] && !File.directory?(options[:install_dir])
gem_name = File.basename(gem_src_dir)
gem_pkg_dir = File.expand_path(File.join(gem_src_dir, 'pkg'))
# We need to use local bin executables if available.
thor = which('thor')
rake = which('rake')
# Handle pure Thor installation instead of Rake
if File.exists?(File.join(gem_src_dir, 'Thorfile'))
# Remove any existing packages.
FileUtils.rm_rf(gem_pkg_dir) if File.directory?(gem_pkg_dir)
# Create the package.
FileUtils.cd(gem_src_dir) { system("#{thor} :package") }
# Install the package using rubygems.
if package = Dir[File.join(gem_pkg_dir, "#{gem_name}-*.gem")].last
FileUtils.cd(File.dirname(package)) do
install_gem(File.basename(package), options.dup)
return
end
else
raise Merb::GemInstallError, "No package found for #{gem_name}"
end
# Handle standard installation through Rake
else
# Clean and regenerate any subgems for meta gems.
Dir[File.join(gem_src_dir, '*', 'Rakefile')].each do |rakefile|
FileUtils.cd(File.dirname(rakefile)) { system("#{rake} clobber_package; #{rake} package") }
end
# Handle the main gem install.
if File.exists?(File.join(gem_src_dir, 'Rakefile'))
# Remove any existing packages.
FileUtils.cd(gem_src_dir) { system("#{rake} clobber_package") }
# Create the main gem pkg dir if it doesn't exist.
FileUtils.mkdir_p(gem_pkg_dir) unless File.directory?(gem_pkg_dir)
# Copy any subgems to the main gem pkg dir.
Dir[File.join(gem_src_dir, '**', 'pkg', '*.gem')].each do |subgem_pkg|
FileUtils.cp(subgem_pkg, gem_pkg_dir)
end
# Finally generate the main package and install it; subgems
# (dependencies) are local to the main package.
FileUtils.cd(gem_src_dir) do
system("#{rake} package")
if package = Dir[File.join(gem_pkg_dir, "#{gem_name}-*.gem")].last
FileUtils.cd(File.dirname(package)) do
install_gem(File.basename(package), options.dup)
return
end
else
raise Merb::GemInstallError, "No package found for #{gem_name}"
end
end
end
end
raise Merb::GemInstallError, "No Rakefile found for #{gem_name}"
end
# Uninstall a gem.
def uninstall_gem(gem, options = {})
if options[:version] && !options[:version].is_a?(Gem::Requirement)
options[:version] = Gem::Requirement.new ["= #{version}"]
end
begin
Gem::Uninstaller.new(gem, options).uninstall
rescue => e
raise GemUninstallError, "Failed to uninstall #{gem}"
end
end
# Will prepend sudo on a suitable platform.
def sudo
@_sudo ||= begin
windows = PLATFORM =~ /win32|cygwin/ rescue nil
windows ? "" : "sudo "
end
end
# Use the local bin/* executables if available.
def which(executable)
if File.executable?(exec = File.join(Dir.pwd, 'bin', executable))
exec
else
executable
end
end
private
def find_gem_in_cache(gem, version)
spec = if version
version = Gem::Requirement.new ["= #{version}"] unless version.is_a?(Gem::Requirement)
Gem.source_index.find_name(gem, version).first
else
Gem.source_index.find_name(gem).sort_by { |g| g.version }.last
end
if spec && File.exists?(gem_file = "#{spec.installation_path}/cache/#{spec.full_name}.gem")
gem_file
end
end
end
class Tasks < Thor
include MerbThorHelper
# Install Thor, Rake and RSpec into the local gems dir, by copying it from
# the system-wide rubygems cache - which is OK since we needed it to run
# this task already.
#
# After this we don't need the system-wide rubygems anymore, as all required
# executables are available in the local ./bin directory.
#
# RSpec is needed here because source installs might fail when running
# rake tasks where spec/rake/spectask has been required.
desc 'setup', 'Install Thor, Rake and RSpec in the local gems dir'
method_options "--merb-root" => :optional
def setup
if $0 =~ /^(\.\/)?bin\/thor$/
puts "You cannot run the setup from #{$0} - try #{File.basename($0)} merb:tasks:setup instead"
return
end
create_if_missing(File.join(working_dir, 'gems'))
Merb.install_gem('thor', :cache => true, :install_dir => gem_dir)
Merb.install_gem('rake', :cache => true, :install_dir => gem_dir)
Merb.install_gem('rspec', :cache => true, :install_dir => gem_dir)
ensure_local_bin_for('thor', 'rake', 'rspec')
end
end
end