sashimi 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/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