merb-gen 0.9.7 → 0.9.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. data/LICENSE +1 -1
  2. data/Rakefile +27 -9
  3. data/bin/merb-gen +21 -3
  4. data/lib/generators/merb/merb_flat.rb +83 -14
  5. data/lib/generators/merb/merb_full.rb +66 -20
  6. data/lib/generators/merb/merb_very_flat.rb +51 -10
  7. data/lib/generators/migration.rb +5 -2
  8. data/lib/generators/resource_controller.rb +2 -2
  9. data/lib/generators/templates/application/{merb → common}/Rakefile +15 -7
  10. data/lib/generators/templates/application/common/dotgitignore +18 -0
  11. data/lib/generators/templates/application/common/merb.thor +1221 -0
  12. data/lib/generators/templates/application/merb/app/views/exceptions/not_acceptable.html.erb +1 -1
  13. data/lib/generators/templates/application/merb/app/views/exceptions/not_found.html.erb +1 -1
  14. data/lib/generators/templates/application/merb/config/environments/development.rb +1 -0
  15. data/lib/generators/templates/application/merb/config/environments/staging.rb +7 -0
  16. data/lib/generators/templates/application/merb/config/environments/test.rb +2 -1
  17. data/lib/generators/templates/application/merb/config/init.rb +1 -4
  18. data/lib/generators/templates/application/merb/config/router.rb +13 -7
  19. data/lib/generators/templates/application/merb/public/favicon.ico +0 -0
  20. data/lib/generators/templates/application/merb/public/robots.txt +5 -0
  21. data/lib/generators/templates/application/merb_flat/{application.rb → application.rbt} +1 -1
  22. data/lib/generators/templates/application/merb_flat/config/init.rb +119 -2
  23. data/lib/generators/templates/application/merb_flat/test/test_helper.rb +19 -0
  24. data/lib/generators/templates/application/merb_plugin/Rakefile +17 -22
  25. data/lib/generators/templates/application/merb_plugin/spec/spec_helper.rb +0 -1
  26. data/lib/generators/templates/application/merb_plugin/test/test_helper.rb +0 -1
  27. data/lib/generators/templates/application/merb_very_flat/application.rbt +36 -2
  28. data/lib/generators/templates/application/merb_very_flat/test/test_helper.rb +19 -0
  29. data/spec/merb_flat_spec.rb +4 -4
  30. data/spec/spec_helper.rb +0 -1
  31. metadata +16 -7
  32. 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