sashimi 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,57 @@
1
+ * *v0.1.0*
2
+
3
+ * Print on the stdout the plugin name on each command execution
4
+
5
+ * Fixed shell help output in commands.rb: make clear each command accepts multiple params. Deleted duplicated Update class from commands.rb.
6
+
7
+ * Fixed sashimi.gemspec: sashimi executable is now available after gem setup
8
+
9
+ * Made AbstractRepository#remove_hidden_folders platform independent
10
+
11
+ * Fixed loading in sashimi
12
+
13
+ * Fixed lib loading in sashimi.rb
14
+
15
+ * Added setup.rb
16
+
17
+ * Added PluginNotFound to improve OOP in exceptions handling
18
+
19
+ * Added version.rb
20
+
21
+ * Added installation instructions, examples and copyright to README [#9 state:resolved]
22
+
23
+ * Created gemspec file [#8 state:resolved]
24
+
25
+ * Added MIT-LICENSE [#10 state:resolved]
26
+
27
+ * Add an installed plugin to a Rails app [#7 state:resolved]
28
+
29
+ * Show plugin summary in list command [#6 state:resolved]
30
+
31
+ * Improved tests for Plugin#about
32
+
33
+ * Cache summary stored in plugin about.yml [#5 state:resolved]
34
+
35
+ * Plugin serialization [#4 state:resolved]
36
+
37
+ * List all installed plugins [#3 state:resolved]
38
+
39
+ * Update an installed plugin [#1 state:resolved]
40
+
41
+ * Refactored Plugin and AbstractRepository to separate class responsabilities
42
+
43
+ * Remove a plugin from cache when uninstalled
44
+
45
+ * Created a plugins cache to store the repository type (svn or git)
46
+
47
+ * Improved shell commands help
48
+
49
+ * Basic plugin uninstall process
50
+
51
+ * Basic plugin install process
52
+
53
+ * Introducing Plugin, AbstractRepository, SvnRepository and GitRepository
54
+
55
+ * Adding Rakefile
56
+
57
+ * Adding CHANGELOG
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Luca Guidi
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,73 @@
1
+ Sashimi
2
+ =======
3
+
4
+ Sashimi is a Rails plugins manager.
5
+ It allows to install your favourite plugins in a local repository and add them to your Rails apps.
6
+ The main goal is to allow the plugins offline installation.
7
+
8
+
9
+
10
+ Installation
11
+ ============
12
+
13
+ $ (sudo) gem install jodosha-sashimi --source=http://gems.github.com
14
+
15
+
16
+
17
+ Usage
18
+ =====
19
+
20
+ Install a plugin from a subversion URL:
21
+ $ sashimi install http://dev.rubyonrails.com/svn/rails/plugins/continuous_builder
22
+
23
+ Install a plugin from a git URL:
24
+ $ sashimi install git://github.com/jodosha/click-to-globalize.git
25
+
26
+ Uninstall a plugin:
27
+ $ sashimi uninstall continuous_builder
28
+
29
+ Update a plugin:
30
+ $ sashimi update click-to-globalize
31
+
32
+ List all installed plugins:
33
+ $ sashimi list
34
+
35
+ Add installed plugin(s) to a Rails app:
36
+ $ sashimi add click-to-globalize
37
+
38
+
39
+
40
+ Known And Common Issues
41
+ =======================
42
+
43
+ * When add a plugin to an app, make sure your current directory is the rails root.
44
+
45
+ * Only Subversion and Git repositories are currently supported.
46
+
47
+
48
+ Contribute
49
+ ==========
50
+
51
+ * Check out the code and test it:
52
+ $ git clone git://github.com/jodosha/sashimi.git
53
+ $ rake
54
+
55
+ * Create a ticket to: http://sushistar.lighthouseapp.com
56
+
57
+ * Create a patch and add as attachement to the ticket.
58
+
59
+
60
+ Credits
61
+ =======
62
+
63
+ Partially inspired to RaPT[http://rapt.rubyforge.org/].
64
+
65
+
66
+
67
+ Home Page
68
+ =========
69
+ http://lucaguidi.com/pages/sashimi
70
+
71
+
72
+
73
+ Copyright (c) 2008 Luca Guidi - http://lucaguidi.com, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the sashimi gem.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.pattern = 'test/**/*_test.rb'
12
+ t.verbose = true
13
+ end
14
+
15
+ desc 'Generate documentation for the sashimi gem.'
16
+ Rake::RDocTask.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_dir = 'rdoc'
18
+ rdoc.title = 'Sashimi'
19
+ rdoc.options << '--line-numbers' << '--inline-source'
20
+ rdoc.rdoc_files.include('README')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
data/bin/sashimi ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
3
+ require 'sashimi'
4
+ require 'sashimi/commands'
5
+
6
+ $rails_app = Dir.pwd
7
+
8
+ Sashimi::Commands::Command.parse!
@@ -0,0 +1,183 @@
1
+ require 'optparse'
2
+
3
+ module Sashimi
4
+ module Commands
5
+ class Command
6
+ attr_reader :script_name
7
+
8
+ def initialize
9
+ @script_name = File.basename($0)
10
+ end
11
+
12
+ def options
13
+ OptionParser.new do |o|
14
+ o.set_summary_indent(' ')
15
+ o.banner = "Usage: #{@script_name} [OPTIONS] command"
16
+ o.define_head "Rails plugin manager."
17
+
18
+ o.separator ""
19
+ o.separator "GENERAL OPTIONS"
20
+
21
+ o.on("-v", "--verbose", "Turn on verbose output.") { |$verbose| }
22
+ o.on("-h", "--help", "Show this help message.") { puts o; exit }
23
+
24
+ o.separator ""
25
+ o.separator "COMMANDS"
26
+
27
+ o.separator " install Install plugin(s) from known URL(s)."
28
+ o.separator " uninstall Uninstall plugin(s) from local repository."
29
+ o.separator " update Update installed plugin(s)."
30
+ o.separator " list List all installed plugins."
31
+ o.separator " add Add installed plugin(s) to a Rails app."
32
+
33
+ o.separator ""
34
+ o.separator "EXAMPLES"
35
+ o.separator " Install a plugin from a subversion URL:"
36
+ o.separator " #{@script_name} install http://dev.rubyonrails.com/svn/rails/plugins/continuous_builder\n"
37
+ o.separator " Install a plugin from a git URL:"
38
+ o.separator " #{@script_name} install git://github.com/jodosha/click-to-globalize.git\n"
39
+ o.separator " Uninstall a plugin:"
40
+ o.separator " #{@script_name} uninstall continuous_builder\n"
41
+ o.separator " Update a plugin:"
42
+ o.separator " #{@script_name} update click-to-globalize\n"
43
+ o.separator " List all installed plugins:"
44
+ o.separator " #{@script_name} list\n"
45
+ o.separator " Add installed plugin(s) to a Rails app:"
46
+ o.separator " #{@script_name} add click-to-globalize\n"
47
+ end
48
+ end
49
+
50
+ def parse!(args=ARGV)
51
+ general, sub = split_args(args)
52
+ options.parse!(general)
53
+
54
+ command = general.shift
55
+ if command =~ /^(install|uninstall|update|list|add)$/
56
+ command = Commands.const_get(command.capitalize).new(self)
57
+ command.parse!(sub)
58
+ else
59
+ puts "Unknown command: #{command}"
60
+ puts options
61
+ exit 1
62
+ end
63
+ end
64
+
65
+ def split_args(args)
66
+ left = []
67
+ left << args.shift while args[0] and args[0] =~ /^-/
68
+ left << args.shift if args[0]
69
+ return [left, args]
70
+ end
71
+
72
+ def self.parse!(args=ARGV)
73
+ Command.new.parse!(args)
74
+ rescue Exception => e
75
+ puts e.message
76
+ exit 1
77
+ end
78
+ end
79
+
80
+ class Install
81
+ def initialize(base_command)
82
+ @base_command = base_command
83
+ end
84
+
85
+ def options
86
+ OptionParser.new do |o|
87
+ o.set_summary_indent(' ')
88
+ o.banner = "Usage: #{@base_command.script_name} install URL [URL2, URL3]"
89
+ o.define_head "Install plugin(s) from known URL(s)."
90
+ end
91
+ end
92
+
93
+ def parse!(args)
94
+ options.parse!(args)
95
+ args.each do |url|
96
+ Plugin.new(nil, url).install
97
+ end
98
+ end
99
+ end
100
+
101
+ class Uninstall
102
+ def initialize(base_command)
103
+ @base_command = base_command
104
+ end
105
+
106
+ def options
107
+ OptionParser.new do |o|
108
+ o.set_summary_indent(' ')
109
+ o.banner = "Usage: #{@base_command.script_name} uninstall PLUGIN [PLUGIN2, PLUGIN3]"
110
+ o.define_head "Uninstall plugin(s) from local repository."
111
+ end
112
+ end
113
+
114
+ def parse!(args)
115
+ options.parse!(args)
116
+ args.each do |name|
117
+ Plugin.new(name).uninstall
118
+ end
119
+ end
120
+ end
121
+
122
+ class Update
123
+ def initialize(base_command)
124
+ @base_command = base_command
125
+ end
126
+
127
+ def options
128
+ OptionParser.new do |o|
129
+ o.set_summary_indent(' ')
130
+ o.banner = "Usage: #{@base_command.script_name} update PLUGIN [PLUGIN2, PLUGIN3]"
131
+ o.define_head "Update installed plugin(s)."
132
+ end
133
+ end
134
+
135
+ def parse!(args)
136
+ options.parse!(args)
137
+ args.each do |name|
138
+ Plugin.new(name).update
139
+ end
140
+ end
141
+ end
142
+
143
+ class List
144
+ def initialize(base_command)
145
+ @base_command = base_command
146
+ end
147
+
148
+ def options
149
+ OptionParser.new do |o|
150
+ o.set_summary_indent(' ')
151
+ o.banner = "Usage: #{@base_command.script_name} list"
152
+ o.define_head "List all installed plugins."
153
+ end
154
+ end
155
+
156
+ def parse!(args)
157
+ options.parse!(args)
158
+ puts Plugin.list
159
+ end
160
+ end
161
+
162
+ class Add
163
+ def initialize(base_command)
164
+ @base_command = base_command
165
+ end
166
+
167
+ def options
168
+ OptionParser.new do |o|
169
+ o.set_summary_indent(' ')
170
+ o.banner = "Usage: #{@base_command.script_name} add PLUGIN"
171
+ o.define_head "Add installed plugin(s) to a Rails app."
172
+ end
173
+ end
174
+
175
+ def parse!(args)
176
+ options.parse!(args)
177
+ args.each do |name|
178
+ Plugin.new(name).add
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,76 @@
1
+ module Sashimi
2
+ class Plugin
3
+ attr_reader :url, :name
4
+
5
+ def initialize(name, url = '')
6
+ @url = URI::parse(url).to_s
7
+ @name = name
8
+ end
9
+
10
+ # Install the plugin
11
+ def install
12
+ repository.install
13
+ end
14
+
15
+ # Uninstall the plugin
16
+ def uninstall
17
+ repository.uninstall
18
+ end
19
+
20
+ # Update the plugin
21
+ def update
22
+ repository.update
23
+ end
24
+
25
+ # Add to a Rails app
26
+ def add
27
+ repository.add
28
+ end
29
+
30
+ def repository #:nodoc:
31
+ @repository ||= instantiate_repository
32
+ end
33
+
34
+ # Read the content of the <tt>about.yml</tt>.
35
+ # New feature of Rails 2.1.x http:://dev.rubyonrails.org/changeset/9098
36
+ def about
37
+ @about ||= repository.about
38
+ end
39
+
40
+ # Return the plugin summary.
41
+ def summary
42
+ about['summary']
43
+ end
44
+
45
+ # Try to guess the plugin name.
46
+ def guess_name
47
+ name = File.basename(@url)
48
+ if name == 'trunk' || name.empty?
49
+ name = File.basename(File.dirname(@url))
50
+ end
51
+ name.gsub!(/\.git$/, '') if name =~ /\.git$/
52
+ name
53
+ end
54
+
55
+ # Serialize the plugin to Hash format.
56
+ def to_hash
57
+ { self.guess_name =>
58
+ { 'type' => repository.scm_type,
59
+ 'summary' => self.summary } }
60
+ end
61
+
62
+ class << self
63
+ # List all installed plugins.
64
+ def list
65
+ AbstractRepository.list
66
+ end
67
+ end
68
+
69
+ private
70
+ # Instantiate the repository.
71
+ # Look at <tt>AbstractRepository#instantiate_repository</tt> documentation.
72
+ def instantiate_repository
73
+ AbstractRepository.instantiate_repository(self)
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,231 @@
1
+ module Sashimi
2
+ class PluginNotFound < StandardError #:nodoc:
3
+ def initialize(plugin_name, message = nil)
4
+ @plugin_name = plugin_name
5
+ @message = message
6
+ end
7
+
8
+ def to_s
9
+ @message || @plugin_name + " isn't in the local repository."
10
+ end
11
+ end
12
+
13
+ class AbstractRepository
14
+ @@local_repository_sub_path = File.join('.rails', 'plugins')
15
+ @@cache_file = '.plugins'
16
+ attr_accessor :plugin
17
+
18
+ def initialize(plugin)
19
+ self.plugin = plugin
20
+ end
21
+
22
+ # Remove the repository
23
+ def uninstall
24
+ change_dir_to_local_repository
25
+ raise PluginNotFound.new(plugin.name) unless File.exists?(plugin.name)
26
+ FileUtils.rm_rf(plugin.name)
27
+ remove_from_cache
28
+ end
29
+
30
+ # Add to a Rails app.
31
+ def add
32
+ puts plugin.name.titleize + "\n"
33
+ copy_plugin_to_rails_app
34
+ remove_hidden_folders
35
+ run_install_hook
36
+ end
37
+
38
+ class << self
39
+ def instantiate_repository(plugin)
40
+ unless plugin.name.nil?
41
+ instantiate_repository_by_cache(plugin)
42
+ else
43
+ instantiate_repository_by_url(plugin)
44
+ end.new(plugin)
45
+ end
46
+
47
+ # Return all installed plugin names
48
+ def list
49
+ cache_content.sort.collect do |plugin, contents|
50
+ "#{plugin}\t\t#{contents['summary']}"
51
+ end.join("\n")
52
+ end
53
+
54
+ def local_repository_path #:nodoc:
55
+ @local_repository_path ||= File.join(find_home, @@local_repository_sub_path)
56
+ end
57
+
58
+ def cache_file #:nodoc:
59
+ @@cache_file
60
+ end
61
+
62
+ # Return the path to the Rails app where the user launched sashimi command.
63
+ def path_to_rails_app
64
+ $rails_app
65
+ end
66
+
67
+ # Read the cache file and return the content as an <tt>Hash</tt>.
68
+ def cache_content
69
+ change_dir_to_local_repository
70
+ @@cache_content ||= (YAML::load_file(cache_file) || {}).to_hash
71
+ end
72
+
73
+ def instantiate_repository_by_url(plugin)
74
+ git_url?(plugin.url) ? GitRepository : SvnRepository
75
+ end
76
+
77
+ def instantiate_repository_by_cache(plugin)
78
+ cached_plugin = cache_content[plugin.name]
79
+ raise PluginNotFound.new(plugin.name) if cached_plugin.nil?
80
+ cached_plugin['type'] == 'git' ? GitRepository : SvnRepository
81
+ end
82
+
83
+ # Changes the current directory with the given one
84
+ def change_dir(dir)
85
+ FileUtils.cd(dir)
86
+ end
87
+
88
+ # Change the current directory with local_repository_path
89
+ def change_dir_to_local_repository
90
+ change_dir(local_repository_path)
91
+ end
92
+
93
+ # Rails app plugins dir
94
+ def plugins_dir
95
+ @@plugins_dir ||= File.join('vendor', 'plugins')
96
+ end
97
+
98
+ # Find the user home directory
99
+ def find_home
100
+ ['HOME', 'USERPROFILE'].each do |homekey|
101
+ return ENV[homekey] if ENV[homekey]
102
+ end
103
+ if ENV['HOMEDRIVE'] && ENV['HOMEPATH']
104
+ return "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}"
105
+ end
106
+ begin
107
+ File.expand_path("~")
108
+ rescue StandardError => ex
109
+ if File::ALT_SEPARATOR
110
+ "C:/"
111
+ else
112
+ "/"
113
+ end
114
+ end
115
+ end
116
+
117
+ # Try to guess if it's a Git url.
118
+ def git_url?(url)
119
+ url =~ /^git:\/\// || url =~ /\.git$/
120
+ end
121
+ end
122
+
123
+ # Return the SCM type
124
+ #
125
+ # Subversion # => svn
126
+ # Git # => git
127
+ def scm_type
128
+ self.class.name.demodulize.gsub(/Repository$/, '').downcase
129
+ end
130
+
131
+ # Read the content of the <tt>about.yml</tt>.
132
+ # New feature of Rails 2.1.x http:://dev.rubyonrails.org/changeset/9098
133
+ def about
134
+ change_dir_to_plugin_path
135
+ return {} unless File.exists?('about.yml')
136
+ (YAML::load_file('about.yml') || {}).to_hash
137
+ end
138
+
139
+ private
140
+ # Proxy for <tt>AbstractRepository#change_dir</tt>
141
+ def change_dir(dir)
142
+ self.class.change_dir(dir)
143
+ end
144
+
145
+ # Proxy for <tt>AbstractRepository#change_dir_to_local_repository</tt>
146
+ def change_dir_to_local_repository
147
+ self.class.change_dir_to_local_repository
148
+ end
149
+
150
+ # Change the current directory with the plugin one
151
+ def change_dir_to_plugin_path
152
+ change_dir(File.join(local_repository_path, plugin.name || plugin.guess_name))
153
+ end
154
+
155
+ # Proxy for <tt>AbstractRepository#local_repository_path</tt>
156
+ def local_repository_path
157
+ self.class.local_repository_path
158
+ end
159
+
160
+ # Proxy for <tt>AbstractRepository#cache_file</tt>
161
+ def cache_file
162
+ self.class.cache_file
163
+ end
164
+
165
+ # Proxy for <tt>AbstractRepository#cache_content</tt>
166
+ def cache_content
167
+ self.class.cache_content
168
+ end
169
+
170
+ # Proxy for <tt>AbstractRepository#plugins_dir</tt>
171
+ def plugins_dir
172
+ self.class.plugins_dir
173
+ end
174
+
175
+ # Proxy for <tt>AbstractRepository#path_to_rails_app</tt>
176
+ def path_to_rails_app
177
+ self.class.path_to_rails_app
178
+ end
179
+
180
+ # Prepare the plugin installation
181
+ def prepare_installation
182
+ FileUtils.mkdir_p(local_repository_path)
183
+ change_dir_to_local_repository
184
+ FileUtils.touch(cache_file)
185
+ end
186
+
187
+ # Add a plugin into the cache
188
+ def add_to_cache(plugin)
189
+ write_to_cache cache_content.to_hash.merge!(plugin)
190
+ end
191
+
192
+ # Remove a plugin from the cache
193
+ def remove_from_cache
194
+ cache_content.delete(plugin.name)
195
+ write_to_cache cache_content
196
+ end
197
+
198
+ # Write all the plugins into the cache
199
+ def write_to_cache(plugins)
200
+ change_dir_to_local_repository
201
+ FileUtils.mv(cache_file, "#{cache_file}-backup")
202
+ File.open(cache_file, 'w'){|f| f.write(plugins.to_yaml)}
203
+ FileUtils.rm("#{cache_file}-backup")
204
+ end
205
+
206
+ # Copy a plugin to a Rails app.
207
+ def copy_plugin_to_rails_app
208
+ change_dir(path_to_rails_app)
209
+ FileUtils.mkdir_p(plugins_dir)
210
+ FileUtils.cp_r(File.join(local_repository_path, plugin.name), File.join(plugins_dir, plugin.name))
211
+ end
212
+
213
+ # Remove SCM hidden folders.
214
+ def remove_hidden_folders
215
+ require 'find'
216
+ change_dir(File.join(path_to_rails_app, plugins_dir, plugin.name))
217
+ Find.find('./') do |path|
218
+ if File.basename(path) == '.'+scm_type
219
+ FileUtils.remove_dir(path, true)
220
+ Find.prune
221
+ end
222
+ end
223
+ end
224
+
225
+ # Run the plugin install hook.
226
+ def run_install_hook
227
+ install_hook_file = File.join(plugins_dir, plugin.name, 'install.rb')
228
+ load install_hook_file if File.exist? install_hook_file
229
+ end
230
+ end
231
+ end
@@ -0,0 +1,16 @@
1
+ module Sashimi
2
+ class GitRepository < AbstractRepository
3
+ def install
4
+ prepare_installation
5
+ puts plugin.guess_name.titleize + "\n\n"
6
+ system("git clone #{plugin.url}")
7
+ add_to_cache(plugin.to_hash)
8
+ end
9
+
10
+ def update
11
+ puts plugin.name.titleize + "\n\n"
12
+ change_dir_to_plugin_path
13
+ system('git pull')
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module Sashimi
2
+ class SvnRepository < AbstractRepository
3
+ def install
4
+ prepare_installation
5
+ puts plugin.guess_name.titleize + "\n\n"
6
+ system("svn co #{plugin.url} #{plugin.guess_name}")
7
+ add_to_cache(plugin.to_hash)
8
+ end
9
+
10
+ def update
11
+ puts plugin.name.titleize + "\n\n"
12
+ change_dir_to_plugin_path
13
+ system("svn up")
14
+ end
15
+ end
16
+ end