rubygems_snapshot 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -1,25 +1,79 @@
1
- h1. rubygems_snapshot
1
+ h1. Snapshot: A Rubygems plugin
2
2
 
3
- Adds snapshot command to gem. This command allow import/export gems.
3
+ *Export and Import* your gems.
4
+ Snapshot, copy your current gems in a single file, providing a fast and secure way to import your gems anywhere.
4
5
 
5
- h2. Description
6
+ Tested with:
7
+ * ruby-1.8.6-p399 [ i386 ]
8
+ * ruby-1.8.7-p249 [ i386 ]
9
+ * 1.3.6 gem version
6
10
 
7
- This gem is a plugin for rubygems.
8
- Based on ["Import/Export Patch":http://rubyforge.org/tracker/?atid=577&group_id=126&func=browse] from ["Peer Allan":http://rubyforge.org/users/pallan].
11
+ Version 1 was based on ["Import/Export Patch":http://rubyforge.org/tracker/?atid=577&group_id=126&func=browse] from ["Peer Allan":http://rubyforge.org/users/pallan].
9
12
 
10
13
  h2. Install/Usage
11
14
 
12
15
  @gem install rubygems_snapshot@
13
- Hosted at ["Gemcutter":http://gemcutter.org/gems/rubygems_snapshot]
14
16
 
15
17
  After install, you can use:
16
18
 
17
- @gem snapshot export result_file.yml@ to export your gems
19
+ @gem snapshot export gems.tar@ to export your gems
18
20
 
19
- @gem snapshot import file_exported.yml@ to import (install) gems.
21
+ @gem snapshot import gems.tar@ to import (install) gems.
20
22
 
21
- *Important*: When importing, don't forget to use sudo if necessary.
22
- *Important(2)*: When importing, pay attention to errors relative to native build. I don't know how to deal with that. :X
23
+ *Important*: When importing, don't forget to use sudo if necessary *and* pay attention to errors relative to native build.
24
+ I don't know how to deal with that yet! :X
25
+ *Hosted* at ["Gemcutter":http://gemcutter.org/gems/rubygems_snapshot]
26
+
27
+ h3. Others formats
28
+
29
+ Nowadays, it supports two formats:
30
+ * tar (default)
31
+ * yml
32
+
33
+ You can chose the format, using -f parameter. For example:
34
+
35
+ @gem snapshot export gems.yml -f yml@
36
+ @gem snapshot import gems.yml -f yml@
37
+
38
+ For aditional help, execute:
39
+ @gem help snapshot@
40
+
41
+ h3. How it Works?
42
+
43
+ It's very simple.
44
+ *When exporting*, get all gems (like "gem list"), puts in a yml file.
45
+ If gem file exists at cache folder from rubygems, copy to tar file too. (tar format)
46
+
47
+ *When importing*, copy all gems to rubygems cache folder.
48
+ Read the yml file, install the gem file from cache folder or do a simple "gem install" if gem file does not exists.
49
+
50
+ h2. For Developers
51
+
52
+ h3. Using as API
53
+
54
+ <pre>
55
+ require "rubygems"
56
+ GemsSnapshot::Exporter.export("example.tar", :format => :tar)
57
+ </pre>
58
+
59
+ --
60
+
61
+ <pre>
62
+ require "rubygems"
63
+ GemsSnapshot::Importer.import("example.tar", :format => :tar)
64
+ </pre>
65
+
66
+
67
+ h3. Use the sources Luke!
68
+
69
+ If you want help to improve Snapshot, after git clone, you'll need the following gems:
70
+ * rake
71
+ * rspec
72
+ * cucumber
73
+
74
+ Use "rake -T" to see the available tasks.
75
+ I highly recomend using ["RVM":http://rvm.beginrescueend.com/] during development.
76
+ Anything, questions, suggestions etc., send me message or leave a issue.
23
77
 
24
78
  h2. License
25
79
 
@@ -1,17 +1,24 @@
1
1
  require 'rubygems/command'
2
- require 'yaml'
3
2
 
4
3
  class Gem::Commands::SnapshotCommand < Gem::Command
5
4
 
6
5
  def initialize
7
- super 'snapshot', 'Export/Import your installed gems'
6
+ super 'snapshot', 'Export/Import your gems.', :format => "tar"
7
+
8
+ add_option('-f', '--format FORMAT', 'Snapshot format. Default is "tar". Can be "yml" too.') do |value, options|
9
+ options[:format] = value
10
+ end
11
+
12
+ end
13
+
14
+ def defaults_str # :nodoc:
15
+ '--format "tar"'
8
16
  end
9
17
 
10
18
  def arguments # :nodoc:
11
19
  args = <<-EOF
12
- export export your installed gems to yml file
13
- import install allgems from yml file
14
- filename yml file used to export/import actions
20
+ ACTION 'export' or 'import' as action arguments
21
+ FILENAME file used to export/import actions
15
22
  EOF
16
23
  return args.gsub(/^\s+/, '')
17
24
  end
@@ -19,111 +26,51 @@ class Gem::Commands::SnapshotCommand < Gem::Command
19
26
  def description # :nodoc:
20
27
  <<-EOF
21
28
  Describe here what snapshot does.
29
+ Updated description at: http://github.com/rogerleite/rubygems_snapshot
22
30
  EOF
23
31
  end
24
32
 
25
33
  def usage # :nodoc:
26
- "#{program_name} action(export|import) filename"
34
+ "#{program_name} ACTION(export|import) FILENAME"
27
35
  end
28
36
 
29
37
  def execute
30
- action, filename = get_and_check_arguments(options[:args])
38
+ action, filename = extract_action(options[:args])
39
+ validate_options(options_without_args)
31
40
 
32
41
  if action == "export"
33
- export(action, filename)
42
+ export(filename)
34
43
  else
35
- import(action, filename)
44
+ import(filename)
36
45
  end
37
46
  end
38
47
 
39
48
  private
40
49
 
41
- def import(action, filename)
42
- #TODO: check if is a valid yml file
43
-
44
- require 'rubygems/dependency_installer'
45
-
46
- main_hash = nil
47
- File.open(filename, "r") do |file|
48
- main_hash = YAML.load(file)
49
- end
50
-
51
- main_hash['sources'].each do |source|
52
- Gem.sources << source unless Gem.sources.include?(source)
53
- end
54
-
55
- options = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({
56
- :generate_rdoc => true,
57
- :generate_ri => true,
58
- :format_executable => false,
59
- :test => false,
60
- :version => Gem::Requirement.default,
61
- #aditional default parameters
62
- :ignore_dependencies => true,
63
- :verbose => true
64
- })
65
-
66
- main_hash['gems'].each do |hash_gem|
67
- gem_name = hash_gem['name']
68
- hash_gem['versions'].each do |version|
69
-
70
- say "Going to install #{gem_name} -v#{version} ... wish me luck!"
71
- begin
72
- gem_install(options, gem_name, version)
73
-
74
- rescue Gem::InstallError => e
75
- alert_error("Error installing #{gem_name}:\n\t#{e.message}")
76
- rescue Gem::GemNotFoundException => e
77
- alert_error(e.message)
78
- end
79
-
80
- end
81
- end
82
-
50
+ def import(filename)
51
+ GemsSnapshot::Importer.import(filename, options_without_args)
52
+ say "Gems imported successfully."
83
53
  end
84
54
 
85
- def gem_install(options, gem_name, version)
86
- inst = Gem::DependencyInstaller.new(options)
87
- inst.install(gem_name, version)
88
-
89
- inst.installed_gems.each do |spec|
90
- say "Successfully installed #{spec.full_name}"
91
- end
55
+ def export(filename)
56
+ say "Say CHEESE to snapshot! :P"
57
+ filename = GemsSnapshot::Exporter.export(filename, options_without_args)
58
+ say "Gems exported to #{filename} successfully."
92
59
  end
93
60
 
94
- def list_installed_gems
95
- name = /^/i #get all local gems
96
- dep = Gem::Dependency.new(name, Gem::Requirement.default)
97
- specs = Gem.source_index.search(dep)
61
+ def options_without_args
62
+ options.reject { |key, value| key == :args }
98
63
  end
99
64
 
100
- def export(action, filename)
101
- say "Say CHEESE to snapshot! :P"
102
-
103
- specs = list_installed_gems
104
-
105
- hash_specs = {}
106
- specs.each do |spec|
107
- versions = hash_specs[spec.name.to_s] || []
108
- versions << spec.version.to_s
109
- hash_specs[spec.name.to_s] = versions
65
+ def validate_options(options)
66
+ format = options[:format]
67
+ format = format.downcase
68
+ unless %w(tar yml).include?(format)
69
+ raise Gem::CommandLineError, "invalid format \"#{format}\" argument.\nUsage:\n#{usage}"
110
70
  end
111
-
112
- gems = []
113
- hash_specs.each do |spec_name, versions|
114
- gems << {'name' => spec_name, 'versions' => versions}
115
- end
116
-
117
- main_hash = {'gems' => gems, 'sources' => Gem.sources}
118
- #say main_hash.to_yaml.to_s #for debug only :P
119
-
120
- File.open(filename, "w") do |file|
121
- file.puts(main_hash.to_yaml)
122
- end
123
- say "Gems exported to #{filename} successfully."
124
71
  end
125
72
 
126
- def get_and_check_arguments(args)
73
+ def extract_action(args)
127
74
  action = args[0]
128
75
  raise Gem::CommandLineError, "Snapshot needs an action argument.\nUsage:\n#{usage}" if action.nil? or action.empty?
129
76
 
@@ -132,24 +79,15 @@ Describe here what snapshot does.
132
79
  raise Gem::CommandLineError, "invalid action \"#{action}\" argument.\nUsage:\n#{usage}"
133
80
  end
134
81
 
135
- filename = nil
136
- if action == "export"
137
- filename = args[1] || "gems_#{Time.now.strftime("%Y%m%d_%H%M")}.yml"
138
- filename = filename.downcase
139
- filename.concat(".yml") unless end_with?(filename, ".yml")
140
- else
141
- filename = args[1]
142
- raise Gem::CommandLineError, "Snapshot needs an filename argument for import action.\nUsage:\n#{usage}" if filename.nil? or filename.empty?
82
+ filename = args[1]
83
+ raise Gem::CommandLineError, "Snapshot needs an filename argument for #{action} action.\nUsage:\n#{usage}" if filename.nil? or filename.empty?
84
+
85
+ if action == "import"
143
86
  raise Gem::Exception, "File not found. :( \nUsage:\n#{usage}" unless File.exist?(filename)
144
87
  end
145
88
 
146
89
  [action, filename]
147
90
  end
148
91
 
149
- def end_with?(target, suffix)
150
- suffix = suffix.to_s
151
- target[-suffix.length, suffix.length] == suffix
152
- end
153
-
154
92
  end
155
93
 
@@ -0,0 +1,37 @@
1
+
2
+ module GemsSnapshot
3
+
4
+ module ExporterHelper
5
+
6
+ # List all installed gems
7
+ def installed_gems
8
+ dep = Gem::Dependency.new(/^/i, Gem::Requirement.default) #get all local gems
9
+ Gem.source_index.search(dep)
10
+ end
11
+
12
+ end
13
+
14
+ #require all "exporters" from exporter folder
15
+ Dir["#{File.dirname(__FILE__)}/exporter/*_exporter.rb"].each do |file|
16
+ require "gems_snapshot/exporter/#{File.basename(file, ".rb")}"
17
+ end
18
+
19
+ class Exporter
20
+
21
+ def self.export(filename, options = {})
22
+ options = {:format => :tar}.merge(options)
23
+ format = options.delete(:format)
24
+
25
+ begin
26
+ exporter = GemsSnapshot.const_get("#{format.to_s.capitalize}Exporter").send(:new)
27
+ result = exporter.export(filename)
28
+ rescue => ex
29
+ raise Gem::Exception, "Ops! An unexpected error occurred: #{ex.message}"
30
+ end
31
+
32
+ result
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,55 @@
1
+ require "tmpdir" #necessary to use Dir.tmp
2
+ require "rubygems/package" #necessary to use Gem::Package::TarWriter
3
+
4
+ module GemsSnapshot
5
+
6
+ class TarExporter
7
+
8
+ include ExporterHelper
9
+
10
+ def export(filename)
11
+ filename = "snapshot.gems" if filename.nil?
12
+
13
+ files = []
14
+ installed_gems.each do |gem|
15
+ files << {:name => "gems/#{gem.full_name}.gem", :path => "#{gem.installation_path}/cache/#{gem.full_name}.gem"}
16
+ end
17
+ files << {:name => "gems.yml", :path => export_to_yml("#{Dir.tmpdir}/gems.yml")}
18
+
19
+ create_tar_file(filename, files)
20
+ filename
21
+ end
22
+
23
+ def export_to_yml(filename)
24
+ yml_exporter = GemsSnapshot::YmlExporter.new
25
+ yml_exporter.export("#{Dir.tmpdir}/gems.yml")
26
+ end
27
+
28
+ # Create a tar file with Gem::Package::TarWriter from Rubygems.
29
+ # +filename+ file destination.
30
+ # +files+ Array of Hash. Each hash have to be +:name+ and +:path+ keys.
31
+ def create_tar_file(filename, files)
32
+ File.open(filename, "w+") do |file|
33
+ Gem::Package::TarWriter.new(file) do |tar_file|
34
+
35
+ files.each do |hash|
36
+ next unless File.exists?(hash[:path])
37
+
38
+ filepath = hash[:path]
39
+ filename = hash[:name]
40
+
41
+ stat = File.stat(filepath)
42
+ tar_file.add_file_simple(filename, stat.mode, stat.size) do |tar_io|
43
+ File.open(filepath, "rb") do |file_io|
44
+ tar_io.write(file_io.read(4096)) until file_io.eof?
45
+ end
46
+ end
47
+ end
48
+
49
+ end
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,29 @@
1
+ module GemsSnapshot
2
+
3
+ class YmlExporter
4
+
5
+ include ExporterHelper
6
+
7
+ def export(filename)
8
+ hash_specs = {}
9
+ installed_gems.each do |spec|
10
+ versions = hash_specs[spec.name.to_s] || []
11
+ versions << spec.version.to_s
12
+ hash_specs[spec.name.to_s] = versions
13
+ end
14
+
15
+ gems = []
16
+ hash_specs.each do |spec_name, versions|
17
+ gems << {'name' => spec_name, 'versions' => versions}
18
+ end
19
+
20
+ main_hash = {'gems' => gems, 'sources' => Gem.sources}
21
+ #puts main_hash.to_yaml.to_s #for debug only :P
22
+
23
+ File.open(filename, "w") { |file| file.puts(main_hash.to_yaml) }
24
+ filename
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,25 @@
1
+ module GemsSnapshot
2
+
3
+ #require all "importers" from importer folder
4
+ Dir["#{File.dirname(__FILE__)}/importer/*_importer.rb"].each do |file|
5
+ require "gems_snapshot/importer/#{File.basename(file, ".rb")}"
6
+ end
7
+
8
+ class Importer
9
+
10
+ def self.import(filename, options = {})
11
+ options = {:format => :tar}.merge(options)
12
+ format = options.delete(:format)
13
+
14
+ begin
15
+ importer = GemsSnapshot.const_get("#{format.to_s.capitalize}Importer").send(:new)
16
+ importer.import(filename)
17
+ rescue => ex
18
+ raise Gem::Exception, "Ops! An unexpected error occurred: #{ex.message}"
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,55 @@
1
+ require "tmpdir" #necessary to use Dir.tmp
2
+
3
+ module GemsSnapshot
4
+
5
+ class TarImporter
6
+
7
+ def import(filename)
8
+ files = extract_files_from_tar(filename)
9
+ copy_gems_to_cache_directory files
10
+ yml_metadata = get_metadata(files)
11
+
12
+ yml_importer = GemsSnapshot::YmlImporter.new
13
+ yml_importer.import(yml_metadata)
14
+ end
15
+
16
+ private
17
+
18
+ def extract_files_from_tar(filename)
19
+ files = []
20
+
21
+ tmp_dir = "#{Dir.tmpdir}/#{Time.now.to_i}"
22
+ FileUtils.rm_rf tmp_dir
23
+ FileUtils.mkdir_p tmp_dir
24
+
25
+ begin
26
+ File.open(filename, "r") do |file|
27
+ Gem::Package::TarReader.new(file).each_entry do |entry|
28
+ destination_file = "#{tmp_dir}/#{entry.full_name}"
29
+ FileUtils.mkdir_p File.dirname(destination_file)
30
+
31
+ File.open(destination_file, "w+") { |f| f.write entry.read }
32
+ files << destination_file
33
+ end
34
+ end
35
+ rescue ex
36
+ raise "An error occurred while extracting files. #{ex.message}"
37
+ end
38
+ files
39
+ end
40
+
41
+ def get_metadata(files)
42
+ result = files.select { |file| file =~ /gems.yml$/ }
43
+ raise "File gems.yml not found!" if result.nil? or result.size == 0
44
+ result.first
45
+ end
46
+
47
+ def copy_gems_to_cache_directory(files)
48
+ cache_directory = "#{Gem.path.first}/cache"
49
+ files.select { |file| file =~ /.gem$/ }.each do |file|
50
+ FileUtils.cp file, "#{cache_directory}/#{File.basename(file)}"
51
+ end
52
+ end
53
+ end
54
+
55
+ end
@@ -0,0 +1,72 @@
1
+ require 'rubygems/dependency_installer'
2
+ require 'yaml'
3
+
4
+ module GemsSnapshot
5
+
6
+ class YmlImporter
7
+
8
+ attr :errors
9
+
10
+ def import(filename)
11
+ yml_hash = YAML.load(File.read(filename)) #TODO: validate file content someday
12
+
13
+ yml_hash['gems'].each do |hash_gem|
14
+ gem_name = hash_gem['name']
15
+ hash_gem['versions'].each do |version|
16
+
17
+ if Gem.available? gem_name, version
18
+ puts "#{gem_name}-#{version} already available!"
19
+ next
20
+ end
21
+
22
+ gem_name = check_for_cache(gem_name, version)
23
+
24
+ puts "Going to install #{gem_name} -v#{version} ... wish me luck!"
25
+ begin
26
+ gem_install(gem_name, version)
27
+ rescue Gem::InstallError => e
28
+ errors << "Error installing #{gem_name}:\n\t#{e.message}"
29
+ rescue Gem::GemNotFoundException => e
30
+ errors << "Gem not found #{e.message}"
31
+ end
32
+
33
+ end
34
+ end
35
+ end
36
+
37
+ protected
38
+
39
+ #Return gem file if exist at cache folder.
40
+ def check_for_cache(gem_name, version)
41
+ gem_file = "#{gem_name}-#{version}.gem"
42
+ Gem.path.each do |gem_path|
43
+ complete_path = "#{gem_path}/cache/#{gem_file}"
44
+ return complete_path if File.exist? complete_path
45
+ end
46
+ gem_name
47
+ end
48
+
49
+ def installer_options
50
+ @installer_options ||= Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({
51
+ :generate_rdoc => true,
52
+ :generate_ri => true,
53
+ :format_executable => false,
54
+ :test => false,
55
+ :version => Gem::Requirement.default,
56
+ #aditional default parameters
57
+ :ignore_dependencies => true,
58
+ :verbose => true
59
+ })
60
+ end
61
+
62
+ def gem_install(gem_name, version, options = installer_options)
63
+ inst = Gem::DependencyInstaller.new(options)
64
+ inst.install(gem_name, version)
65
+ inst.installed_gems.each do |spec|
66
+ puts "Successfully installed #{spec.full_name}"
67
+ end
68
+ end
69
+
70
+ end
71
+
72
+ end
@@ -1,4 +1,8 @@
1
1
  require 'rubygems/command_manager'
2
2
  require 'commands/snapshot_command'
3
+
4
+ require "gems_snapshot/exporter"
5
+ require "gems_snapshot/importer"
6
+
3
7
  Gem::CommandManager.instance.register_command :snapshot
4
8
 
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubygems_snapshot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ - 0
9
+ version: 0.2.0
5
10
  platform: ruby
6
11
  authors:
7
12
  - Roger Leite
@@ -9,7 +14,7 @@ autorequire:
9
14
  bindir: bin
10
15
  cert_chain: []
11
16
 
12
- date: 2010-02-05 00:00:00 -02:00
17
+ date: 2010-05-06 00:00:00 -03:00
13
18
  default_executable:
14
19
  dependencies: []
15
20
 
@@ -23,22 +28,28 @@ extra_rdoc_files: []
23
28
 
24
29
  files:
25
30
  - lib/commands/snapshot_command.rb
31
+ - lib/gems_snapshot/importer/tar_importer.rb
32
+ - lib/gems_snapshot/importer/yml_importer.rb
33
+ - lib/gems_snapshot/exporter/tar_exporter.rb
34
+ - lib/gems_snapshot/exporter/yml_exporter.rb
35
+ - lib/gems_snapshot/exporter.rb
36
+ - lib/gems_snapshot/importer.rb
26
37
  - lib/rubygems_plugin.rb
27
38
  - README.textile
28
39
  has_rdoc: true
29
40
  homepage: http://github.com/rogerleite/rubygems_snapshot
30
41
  licenses: []
31
42
 
32
- post_install_message: |+
33
-
34
- ========================================================================
35
-
36
- Thanks for installing RubygemsSnapshot! You can now run:
37
-
38
- gem snapshot import/export your gems
39
-
40
- ========================================================================
41
-
43
+ post_install_message: |
44
+ ===============================================================================
45
+ Thanks for installing RubygemsSnapshot! You can now run:
46
+ gem snapshot export example
47
+ gem snapshot import example
48
+ ***
49
+ gem help snapshot for help! ;)
50
+ OR http://github.com/rogerleite/rubygems_snapshot
51
+ ===============================================================================
52
+
42
53
  rdoc_options: []
43
54
 
44
55
  require_paths:
@@ -47,18 +58,22 @@ required_ruby_version: !ruby/object:Gem::Requirement
47
58
  requirements:
48
59
  - - ">="
49
60
  - !ruby/object:Gem::Version
61
+ segments:
62
+ - 0
50
63
  version: "0"
51
- version:
52
64
  required_rubygems_version: !ruby/object:Gem::Requirement
53
65
  requirements:
54
66
  - - ">="
55
67
  - !ruby/object:Gem::Version
68
+ segments:
69
+ - 1
70
+ - 3
71
+ - 5
56
72
  version: 1.3.5
57
- version:
58
73
  requirements: []
59
74
 
60
75
  rubyforge_project: rubygems_snapshot
61
- rubygems_version: 1.3.5
76
+ rubygems_version: 1.3.6
62
77
  signing_key:
63
78
  specification_version: 3
64
79
  summary: Command to import/export gems