rails 2.3.18 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rails might be problematic. Click here for more details.

Files changed (215) hide show
  1. data/bin/rails +8 -20
  2. metadata +74 -251
  3. data/CHANGELOG +0 -2193
  4. data/MIT-LICENSE +0 -20
  5. data/README +0 -243
  6. data/Rakefile +0 -365
  7. data/bin/about +0 -4
  8. data/bin/console +0 -3
  9. data/bin/dbconsole +0 -3
  10. data/bin/destroy +0 -3
  11. data/bin/generate +0 -3
  12. data/bin/performance/benchmarker +0 -3
  13. data/bin/performance/profiler +0 -3
  14. data/bin/plugin +0 -3
  15. data/bin/runner +0 -3
  16. data/bin/server +0 -3
  17. data/builtin/rails_info/rails/info.rb +0 -131
  18. data/builtin/rails_info/rails/info_controller.rb +0 -9
  19. data/builtin/rails_info/rails/info_helper.rb +0 -2
  20. data/builtin/rails_info/rails_info_controller.rb +0 -2
  21. data/configs/databases/frontbase.yml +0 -28
  22. data/configs/databases/ibm_db.yml +0 -62
  23. data/configs/databases/mysql.yml +0 -60
  24. data/configs/databases/oracle.yml +0 -39
  25. data/configs/databases/postgresql.yml +0 -51
  26. data/configs/databases/sqlite2.yml +0 -19
  27. data/configs/databases/sqlite3.yml +0 -22
  28. data/configs/empty.log +0 -0
  29. data/configs/initializers/backtrace_silencers.rb +0 -7
  30. data/configs/initializers/cookie_verification_secret.rb +0 -7
  31. data/configs/initializers/inflections.rb +0 -10
  32. data/configs/initializers/mime_types.rb +0 -5
  33. data/configs/initializers/new_rails_defaults.rb +0 -21
  34. data/configs/initializers/session_store.rb +0 -15
  35. data/configs/locales/en.yml +0 -5
  36. data/configs/routes.rb +0 -43
  37. data/configs/seeds.rb +0 -7
  38. data/dispatches/config.ru +0 -7
  39. data/dispatches/dispatch.fcgi +0 -24
  40. data/dispatches/dispatch.rb +0 -10
  41. data/dispatches/gateway.cgi +0 -97
  42. data/doc/README_FOR_APP +0 -2
  43. data/environments/boot.rb +0 -114
  44. data/environments/development.rb +0 -17
  45. data/environments/environment.rb +0 -41
  46. data/environments/production.rb +0 -28
  47. data/environments/test.rb +0 -28
  48. data/fresh_rakefile +0 -10
  49. data/helpers/application_controller.rb +0 -10
  50. data/helpers/application_helper.rb +0 -3
  51. data/helpers/performance_test.rb +0 -9
  52. data/helpers/test_helper.rb +0 -38
  53. data/html/404.html +0 -30
  54. data/html/422.html +0 -30
  55. data/html/500.html +0 -30
  56. data/html/favicon.ico +0 -0
  57. data/html/images/rails.png +0 -0
  58. data/html/index.html +0 -275
  59. data/html/javascripts/application.js +0 -2
  60. data/html/javascripts/controls.js +0 -963
  61. data/html/javascripts/dragdrop.js +0 -973
  62. data/html/javascripts/effects.js +0 -1128
  63. data/html/javascripts/prototype.js +0 -4320
  64. data/html/robots.txt +0 -5
  65. data/lib/code_statistics.rb +0 -107
  66. data/lib/commands.rb +0 -17
  67. data/lib/commands/about.rb +0 -3
  68. data/lib/commands/console.rb +0 -45
  69. data/lib/commands/dbconsole.rb +0 -87
  70. data/lib/commands/destroy.rb +0 -6
  71. data/lib/commands/generate.rb +0 -6
  72. data/lib/commands/ncgi/listener +0 -86
  73. data/lib/commands/ncgi/tracker +0 -69
  74. data/lib/commands/performance/benchmarker.rb +0 -24
  75. data/lib/commands/performance/profiler.rb +0 -50
  76. data/lib/commands/plugin.rb +0 -968
  77. data/lib/commands/runner.rb +0 -54
  78. data/lib/commands/server.rb +0 -114
  79. data/lib/commands/update.rb +0 -4
  80. data/lib/console_app.rb +0 -30
  81. data/lib/console_sandbox.rb +0 -6
  82. data/lib/console_with_helpers.rb +0 -5
  83. data/lib/dispatcher.rb +0 -24
  84. data/lib/fcgi_handler.rb +0 -239
  85. data/lib/initializer.rb +0 -1152
  86. data/lib/performance_test_help.rb +0 -5
  87. data/lib/rails/backtrace_cleaner.rb +0 -54
  88. data/lib/rails/gem_builder.rb +0 -21
  89. data/lib/rails/gem_dependency.rb +0 -317
  90. data/lib/rails/plugin.rb +0 -179
  91. data/lib/rails/plugin/loader.rb +0 -198
  92. data/lib/rails/plugin/locator.rb +0 -100
  93. data/lib/rails/rack.rb +0 -8
  94. data/lib/rails/rack/debugger.rb +0 -23
  95. data/lib/rails/rack/log_tailer.rb +0 -35
  96. data/lib/rails/rack/metal.rb +0 -51
  97. data/lib/rails/rack/static.rb +0 -46
  98. data/lib/rails/vendor_gem_source_index.rb +0 -140
  99. data/lib/rails/version.rb +0 -9
  100. data/lib/rails_generator.rb +0 -46
  101. data/lib/rails_generator/base.rb +0 -266
  102. data/lib/rails_generator/commands.rb +0 -621
  103. data/lib/rails_generator/generated_attribute.rb +0 -47
  104. data/lib/rails_generator/generators/applications/app/USAGE +0 -9
  105. data/lib/rails_generator/generators/applications/app/app_generator.rb +0 -266
  106. data/lib/rails_generator/generators/applications/app/scm/git.rb +0 -18
  107. data/lib/rails_generator/generators/applications/app/scm/scm.rb +0 -8
  108. data/lib/rails_generator/generators/applications/app/scm/svn.rb +0 -7
  109. data/lib/rails_generator/generators/applications/app/template_runner.rb +0 -401
  110. data/lib/rails_generator/generators/components/controller/USAGE +0 -30
  111. data/lib/rails_generator/generators/components/controller/controller_generator.rb +0 -43
  112. data/lib/rails_generator/generators/components/controller/templates/controller.rb +0 -7
  113. data/lib/rails_generator/generators/components/controller/templates/functional_test.rb +0 -8
  114. data/lib/rails_generator/generators/components/controller/templates/helper.rb +0 -2
  115. data/lib/rails_generator/generators/components/controller/templates/helper_test.rb +0 -4
  116. data/lib/rails_generator/generators/components/controller/templates/view.html.erb +0 -2
  117. data/lib/rails_generator/generators/components/helper/USAGE +0 -24
  118. data/lib/rails_generator/generators/components/helper/helper_generator.rb +0 -25
  119. data/lib/rails_generator/generators/components/helper/templates/helper.rb +0 -2
  120. data/lib/rails_generator/generators/components/helper/templates/helper_test.rb +0 -4
  121. data/lib/rails_generator/generators/components/integration_test/USAGE +0 -8
  122. data/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb +0 -16
  123. data/lib/rails_generator/generators/components/integration_test/templates/integration_test.rb +0 -10
  124. data/lib/rails_generator/generators/components/mailer/USAGE +0 -16
  125. data/lib/rails_generator/generators/components/mailer/mailer_generator.rb +0 -30
  126. data/lib/rails_generator/generators/components/mailer/templates/fixture.erb +0 -3
  127. data/lib/rails_generator/generators/components/mailer/templates/fixture.rhtml +0 -0
  128. data/lib/rails_generator/generators/components/mailer/templates/mailer.rb +0 -15
  129. data/lib/rails_generator/generators/components/mailer/templates/unit_test.rb +0 -20
  130. data/lib/rails_generator/generators/components/mailer/templates/view.erb +0 -3
  131. data/lib/rails_generator/generators/components/mailer/templates/view.rhtml +0 -0
  132. data/lib/rails_generator/generators/components/metal/USAGE +0 -8
  133. data/lib/rails_generator/generators/components/metal/metal_generator.rb +0 -8
  134. data/lib/rails_generator/generators/components/metal/templates/metal.rb +0 -12
  135. data/lib/rails_generator/generators/components/migration/USAGE +0 -29
  136. data/lib/rails_generator/generators/components/migration/migration_generator.rb +0 -20
  137. data/lib/rails_generator/generators/components/migration/templates/migration.rb +0 -11
  138. data/lib/rails_generator/generators/components/model/USAGE +0 -27
  139. data/lib/rails_generator/generators/components/model/model_generator.rb +0 -52
  140. data/lib/rails_generator/generators/components/model/templates/fixtures.yml +0 -23
  141. data/lib/rails_generator/generators/components/model/templates/migration.rb +0 -16
  142. data/lib/rails_generator/generators/components/model/templates/model.rb +0 -5
  143. data/lib/rails_generator/generators/components/model/templates/unit_test.rb +0 -8
  144. data/lib/rails_generator/generators/components/observer/USAGE +0 -13
  145. data/lib/rails_generator/generators/components/observer/observer_generator.rb +0 -16
  146. data/lib/rails_generator/generators/components/observer/templates/observer.rb +0 -2
  147. data/lib/rails_generator/generators/components/observer/templates/unit_test.rb +0 -8
  148. data/lib/rails_generator/generators/components/performance_test/USAGE +0 -8
  149. data/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb +0 -16
  150. data/lib/rails_generator/generators/components/performance_test/templates/performance_test.rb +0 -9
  151. data/lib/rails_generator/generators/components/plugin/USAGE +0 -25
  152. data/lib/rails_generator/generators/components/plugin/plugin_generator.rb +0 -39
  153. data/lib/rails_generator/generators/components/plugin/templates/MIT-LICENSE +0 -20
  154. data/lib/rails_generator/generators/components/plugin/templates/README +0 -13
  155. data/lib/rails_generator/generators/components/plugin/templates/Rakefile +0 -23
  156. data/lib/rails_generator/generators/components/plugin/templates/USAGE +0 -8
  157. data/lib/rails_generator/generators/components/plugin/templates/generator.rb +0 -8
  158. data/lib/rails_generator/generators/components/plugin/templates/init.rb +0 -1
  159. data/lib/rails_generator/generators/components/plugin/templates/install.rb +0 -1
  160. data/lib/rails_generator/generators/components/plugin/templates/plugin.rb +0 -1
  161. data/lib/rails_generator/generators/components/plugin/templates/tasks.rake +0 -4
  162. data/lib/rails_generator/generators/components/plugin/templates/test_helper.rb +0 -4
  163. data/lib/rails_generator/generators/components/plugin/templates/uninstall.rb +0 -1
  164. data/lib/rails_generator/generators/components/plugin/templates/unit_test.rb +0 -8
  165. data/lib/rails_generator/generators/components/resource/USAGE +0 -23
  166. data/lib/rails_generator/generators/components/resource/resource_generator.rb +0 -76
  167. data/lib/rails_generator/generators/components/resource/templates/controller.rb +0 -2
  168. data/lib/rails_generator/generators/components/resource/templates/functional_test.rb +0 -8
  169. data/lib/rails_generator/generators/components/resource/templates/helper.rb +0 -2
  170. data/lib/rails_generator/generators/components/resource/templates/helper_test.rb +0 -4
  171. data/lib/rails_generator/generators/components/scaffold/USAGE +0 -29
  172. data/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb +0 -103
  173. data/lib/rails_generator/generators/components/scaffold/templates/controller.rb +0 -83
  174. data/lib/rails_generator/generators/components/scaffold/templates/functional_test.rb +0 -45
  175. data/lib/rails_generator/generators/components/scaffold/templates/helper.rb +0 -2
  176. data/lib/rails_generator/generators/components/scaffold/templates/helper_test.rb +0 -4
  177. data/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb +0 -17
  178. data/lib/rails_generator/generators/components/scaffold/templates/style.css +0 -54
  179. data/lib/rails_generator/generators/components/scaffold/templates/view_edit.html.erb +0 -18
  180. data/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb +0 -24
  181. data/lib/rails_generator/generators/components/scaffold/templates/view_new.html.erb +0 -17
  182. data/lib/rails_generator/generators/components/scaffold/templates/view_show.html.erb +0 -10
  183. data/lib/rails_generator/generators/components/session_migration/USAGE +0 -10
  184. data/lib/rails_generator/generators/components/session_migration/session_migration_generator.rb +0 -18
  185. data/lib/rails_generator/generators/components/session_migration/templates/migration.rb +0 -16
  186. data/lib/rails_generator/lookup.rb +0 -249
  187. data/lib/rails_generator/manifest.rb +0 -53
  188. data/lib/rails_generator/options.rb +0 -150
  189. data/lib/rails_generator/scripts.rb +0 -89
  190. data/lib/rails_generator/scripts/destroy.rb +0 -29
  191. data/lib/rails_generator/scripts/generate.rb +0 -7
  192. data/lib/rails_generator/scripts/update.rb +0 -12
  193. data/lib/rails_generator/secret_key_generator.rb +0 -24
  194. data/lib/rails_generator/simple_logger.rb +0 -46
  195. data/lib/rails_generator/spec.rb +0 -44
  196. data/lib/railties_path.rb +0 -1
  197. data/lib/ruby_version_check.rb +0 -17
  198. data/lib/rubyprof_ext.rb +0 -35
  199. data/lib/source_annotation_extractor.rb +0 -102
  200. data/lib/tasks/annotations.rake +0 -20
  201. data/lib/tasks/databases.rake +0 -436
  202. data/lib/tasks/documentation.rake +0 -93
  203. data/lib/tasks/framework.rake +0 -146
  204. data/lib/tasks/gems.rake +0 -78
  205. data/lib/tasks/log.rake +0 -9
  206. data/lib/tasks/middleware.rake +0 -7
  207. data/lib/tasks/misc.rake +0 -63
  208. data/lib/tasks/rails.rb +0 -14
  209. data/lib/tasks/routes.rake +0 -18
  210. data/lib/tasks/statistics.rake +0 -17
  211. data/lib/tasks/testing.rake +0 -139
  212. data/lib/tasks/tmp.rake +0 -37
  213. data/lib/test_help.rb +0 -38
  214. data/lib/webrick_server.rb +0 -156
  215. data/railties.gemspec +0 -22
@@ -1,24 +0,0 @@
1
- if ARGV.empty?
2
- puts "Usage: ./script/performance/benchmarker [times] 'Person.expensive_way' 'Person.another_expensive_way' ..."
3
- exit 1
4
- end
5
-
6
- begin
7
- N = Integer(ARGV.first)
8
- ARGV.shift
9
- rescue ArgumentError
10
- N = 1
11
- end
12
-
13
- require RAILS_ROOT + '/config/environment'
14
- require 'benchmark'
15
- include Benchmark
16
-
17
- # Don't include compilation in the benchmark
18
- ARGV.each { |expression| eval(expression) }
19
-
20
- bm(6) do |x|
21
- ARGV.each_with_index do |expression, idx|
22
- x.report("##{idx + 1}") { N.times { eval(expression) } }
23
- end
24
- end
@@ -1,50 +0,0 @@
1
- if ARGV.empty?
2
- $stderr.puts "Usage: ./script/performance/profiler 'Person.expensive_method(10)' [times] [flat|graph|graph_html]"
3
- exit(1)
4
- end
5
-
6
- # Keep the expensive require out of the profile.
7
- $stderr.puts 'Loading Rails...'
8
- require RAILS_ROOT + '/config/environment'
9
-
10
- # Define a method to profile.
11
- if ARGV[1] and ARGV[1].to_i > 1
12
- eval "def profile_me() #{ARGV[1]}.times { #{ARGV[0]} } end"
13
- else
14
- eval "def profile_me() #{ARGV[0]} end"
15
- end
16
-
17
- # Use the ruby-prof extension if available. Fall back to stdlib profiler.
18
- begin
19
- begin
20
- require "ruby-prof"
21
- $stderr.puts 'Using the ruby-prof extension.'
22
- RubyProf.measure_mode = RubyProf::WALL_TIME
23
- RubyProf.start
24
- profile_me
25
- results = RubyProf.stop
26
- if ARGV[2]
27
- printer_class = RubyProf.const_get((ARGV[2] + "_printer").classify)
28
- else
29
- printer_class = RubyProf::FlatPrinter
30
- end
31
- printer = printer_class.new(results)
32
- printer.print($stderr)
33
- rescue LoadError
34
- require "prof"
35
- $stderr.puts 'Using the old ruby-prof extension.'
36
- Prof.clock_mode = Prof::GETTIMEOFDAY
37
- Prof.start
38
- profile_me
39
- results = Prof.stop
40
- require 'rubyprof_ext'
41
- Prof.print_profile(results, $stderr)
42
- end
43
- rescue LoadError
44
- require 'profiler'
45
- $stderr.puts 'Using the standard Ruby profiler.'
46
- Profiler__.start_profile
47
- profile_me
48
- Profiler__.stop_profile
49
- Profiler__.print_profile($stderr)
50
- end
@@ -1,968 +0,0 @@
1
- # Rails Plugin Manager.
2
- #
3
- # Listing available plugins:
4
- #
5
- # $ ./script/plugin list
6
- # continuous_builder http://dev.rubyonrails.com/svn/rails/plugins/continuous_builder
7
- # asset_timestamping http://svn.aviditybytes.com/rails/plugins/asset_timestamping
8
- # enumerations_mixin http://svn.protocool.com/rails/plugins/enumerations_mixin/trunk
9
- # calculations http://techno-weenie.net/svn/projects/calculations/
10
- # ...
11
- #
12
- # Installing plugins:
13
- #
14
- # $ ./script/plugin install continuous_builder asset_timestamping
15
- #
16
- # Finding Repositories:
17
- #
18
- # $ ./script/plugin discover
19
- #
20
- # Adding Repositories:
21
- #
22
- # $ ./script/plugin source http://svn.protocool.com/rails/plugins/
23
- #
24
- # How it works:
25
- #
26
- # * Maintains a list of subversion repositories that are assumed to have
27
- # a plugin directory structure. Manage them with the (source, unsource,
28
- # and sources commands)
29
- #
30
- # * The discover command scrapes the following page for things that
31
- # look like subversion repositories with plugins:
32
- # http://wiki.rubyonrails.org/rails/pages/Plugins
33
- #
34
- # * Unless you specify that you want to use svn, script/plugin uses plain old
35
- # HTTP for downloads. The following bullets are true if you specify
36
- # that you want to use svn.
37
- #
38
- # * If `vendor/plugins` is under subversion control, the script will
39
- # modify the svn:externals property and perform an update. You can
40
- # use normal subversion commands to keep the plugins up to date.
41
- #
42
- # * Or, if `vendor/plugins` is not under subversion control, the
43
- # plugin is pulled via `svn checkout` or `svn export` but looks
44
- # exactly the same.
45
- #
46
- # Specifying revisions:
47
- #
48
- # * Subversion revision is a single integer.
49
- #
50
- # * Git revision format:
51
- # - full - 'refs/tags/1.8.0' or 'refs/heads/experimental'
52
- # - short: 'experimental' (equivalent to 'refs/heads/experimental')
53
- # 'tag 1.8.0' (equivalent to 'refs/tags/1.8.0')
54
- #
55
- #
56
- # This is Free Software, copyright 2005 by Ryan Tomayko (rtomayko@gmail.com)
57
- # and is licensed MIT: (http://www.opensource.org/licenses/mit-license.php)
58
-
59
- $verbose = false
60
-
61
-
62
- require 'open-uri'
63
- require 'fileutils'
64
- require 'tempfile'
65
-
66
- include FileUtils
67
-
68
- class RailsEnvironment
69
- attr_reader :root
70
-
71
- def initialize(dir)
72
- @root = dir
73
- end
74
-
75
- def self.find(dir=nil)
76
- dir ||= pwd
77
- while dir.length > 1
78
- return new(dir) if File.exist?(File.join(dir, 'config', 'environment.rb'))
79
- dir = File.dirname(dir)
80
- end
81
- end
82
-
83
- def self.default
84
- @default ||= find
85
- end
86
-
87
- def self.default=(rails_env)
88
- @default = rails_env
89
- end
90
-
91
- def install(name_uri_or_plugin)
92
- if name_uri_or_plugin.is_a? String
93
- if name_uri_or_plugin =~ /:\/\//
94
- plugin = Plugin.new(name_uri_or_plugin)
95
- else
96
- plugin = Plugins[name_uri_or_plugin]
97
- end
98
- else
99
- plugin = name_uri_or_plugin
100
- end
101
- unless plugin.nil?
102
- plugin.install
103
- else
104
- puts "Plugin not found: #{name_uri_or_plugin}"
105
- end
106
- end
107
-
108
- def use_svn?
109
- require 'active_support/core_ext/kernel'
110
- silence_stderr {`svn --version` rescue nil}
111
- !$?.nil? && $?.success?
112
- end
113
-
114
- def use_externals?
115
- use_svn? && File.directory?("#{root}/vendor/plugins/.svn")
116
- end
117
-
118
- def use_checkout?
119
- # this is a bit of a guess. we assume that if the rails environment
120
- # is under subversion then they probably want the plugin checked out
121
- # instead of exported. This can be overridden on the command line
122
- File.directory?("#{root}/.svn")
123
- end
124
-
125
- def best_install_method
126
- return :http unless use_svn?
127
- case
128
- when use_externals? then :externals
129
- when use_checkout? then :checkout
130
- else :export
131
- end
132
- end
133
-
134
- def externals
135
- return [] unless use_externals?
136
- ext = `svn propget svn:externals "#{root}/vendor/plugins"`
137
- lines = ext.respond_to?(:lines) ? ext.lines : ext
138
- lines.reject{ |line| line.strip == '' }.map do |line|
139
- line.strip.split(/\s+/, 2)
140
- end
141
- end
142
-
143
- def externals=(items)
144
- unless items.is_a? String
145
- items = items.map{|name,uri| "#{name.ljust(29)} #{uri.chomp('/')}"}.join("\n")
146
- end
147
- Tempfile.open("svn-set-prop") do |file|
148
- file.write(items)
149
- file.flush
150
- system("svn propset -q svn:externals -F \"#{file.path}\" \"#{root}/vendor/plugins\"")
151
- end
152
- end
153
-
154
- end
155
-
156
- class Plugin
157
- attr_reader :name, :uri
158
-
159
- def initialize(uri, name=nil)
160
- @uri = uri
161
- guess_name(uri)
162
- end
163
-
164
- def self.find(name)
165
- name =~ /\// ? new(name) : Repositories.instance.find_plugin(name)
166
- end
167
-
168
- def to_s
169
- "#{@name.ljust(30)}#{@uri}"
170
- end
171
-
172
- def svn_url?
173
- @uri =~ /svn(?:\+ssh)?:\/\/*/
174
- end
175
-
176
- def git_url?
177
- @uri =~ /^git:\/\// || @uri =~ /\.git$/
178
- end
179
-
180
- def installed?
181
- File.directory?("#{rails_env.root}/vendor/plugins/#{name}") \
182
- or rails_env.externals.detect{ |name, repo| self.uri == repo }
183
- end
184
-
185
- def install(method=nil, options = {})
186
- method ||= rails_env.best_install_method?
187
- if :http == method
188
- method = :export if svn_url?
189
- method = :git if git_url?
190
- end
191
-
192
- uninstall if installed? and options[:force]
193
-
194
- unless installed?
195
- send("install_using_#{method}", options)
196
- run_install_hook
197
- else
198
- puts "already installed: #{name} (#{uri}). pass --force to reinstall"
199
- end
200
- end
201
-
202
- def uninstall
203
- path = "#{rails_env.root}/vendor/plugins/#{name}"
204
- if File.directory?(path)
205
- puts "Removing 'vendor/plugins/#{name}'" if $verbose
206
- run_uninstall_hook
207
- rm_r path
208
- else
209
- puts "Plugin doesn't exist: #{path}"
210
- end
211
- # clean up svn:externals
212
- externals = rails_env.externals
213
- externals.reject!{|n,u| name == n or name == u}
214
- rails_env.externals = externals
215
- end
216
-
217
- def info
218
- tmp = "#{rails_env.root}/_tmp_about.yml"
219
- if svn_url?
220
- cmd = "svn export #{@uri} \"#{rails_env.root}/#{tmp}\""
221
- puts cmd if $verbose
222
- system(cmd)
223
- end
224
- open(svn_url? ? tmp : File.join(@uri, 'about.yml')) do |stream|
225
- stream.read
226
- end rescue "No about.yml found in #{uri}"
227
- ensure
228
- FileUtils.rm_rf tmp if svn_url?
229
- end
230
-
231
- private
232
-
233
- def run_install_hook
234
- install_hook_file = "#{rails_env.root}/vendor/plugins/#{name}/install.rb"
235
- load install_hook_file if File.exist? install_hook_file
236
- end
237
-
238
- def run_uninstall_hook
239
- uninstall_hook_file = "#{rails_env.root}/vendor/plugins/#{name}/uninstall.rb"
240
- load uninstall_hook_file if File.exist? uninstall_hook_file
241
- end
242
-
243
- def install_using_export(options = {})
244
- svn_command :export, options
245
- end
246
-
247
- def install_using_checkout(options = {})
248
- svn_command :checkout, options
249
- end
250
-
251
- def install_using_externals(options = {})
252
- externals = rails_env.externals
253
- externals.push([@name, uri])
254
- rails_env.externals = externals
255
- install_using_checkout(options)
256
- end
257
-
258
- def install_using_http(options = {})
259
- root = rails_env.root
260
- mkdir_p "#{root}/vendor/plugins/#{@name}"
261
- Dir.chdir "#{root}/vendor/plugins/#{@name}" do
262
- puts "fetching from '#{uri}'" if $verbose
263
- fetcher = RecursiveHTTPFetcher.new(uri, -1)
264
- fetcher.quiet = true if options[:quiet]
265
- fetcher.fetch
266
- end
267
- end
268
-
269
- def install_using_git(options = {})
270
- root = rails_env.root
271
- mkdir_p(install_path = "#{root}/vendor/plugins/#{name}")
272
- Dir.chdir install_path do
273
- init_cmd = "git init"
274
- init_cmd += " -q" if options[:quiet] and not $verbose
275
- puts init_cmd if $verbose
276
- system(init_cmd)
277
- base_cmd = "git pull --depth 1 #{uri}"
278
- base_cmd += " -q" if options[:quiet] and not $verbose
279
- base_cmd += " #{options[:revision]}" if options[:revision]
280
- puts base_cmd if $verbose
281
- if system(base_cmd)
282
- puts "removing: .git .gitignore" if $verbose
283
- rm_rf %w(.git .gitignore)
284
- else
285
- rm_rf install_path
286
- end
287
- end
288
- end
289
-
290
- def svn_command(cmd, options = {})
291
- root = rails_env.root
292
- mkdir_p "#{root}/vendor/plugins"
293
- base_cmd = "svn #{cmd} #{uri} \"#{root}/vendor/plugins/#{name}\""
294
- base_cmd += ' -q' if options[:quiet] and not $verbose
295
- base_cmd += " -r #{options[:revision]}" if options[:revision]
296
- puts base_cmd if $verbose
297
- system(base_cmd)
298
- end
299
-
300
- def guess_name(url)
301
- @name = File.basename(url)
302
- if @name == 'trunk' || @name.empty?
303
- @name = File.basename(File.dirname(url))
304
- end
305
- @name.gsub!(/\.git$/, '') if @name =~ /\.git$/
306
- end
307
-
308
- def rails_env
309
- @rails_env || RailsEnvironment.default
310
- end
311
- end
312
-
313
- class Repositories
314
- include Enumerable
315
-
316
- def initialize(cache_file = File.join(find_home, ".rails-plugin-sources"))
317
- @cache_file = File.expand_path(cache_file)
318
- load!
319
- end
320
-
321
- def each(&block)
322
- @repositories.each(&block)
323
- end
324
-
325
- def add(uri)
326
- unless find{|repo| repo.uri == uri }
327
- @repositories.push(Repository.new(uri)).last
328
- end
329
- end
330
-
331
- def remove(uri)
332
- @repositories.reject!{|repo| repo.uri == uri}
333
- end
334
-
335
- def exist?(uri)
336
- @repositories.detect{|repo| repo.uri == uri }
337
- end
338
-
339
- def all
340
- @repositories
341
- end
342
-
343
- def find_plugin(name)
344
- @repositories.each do |repo|
345
- repo.each do |plugin|
346
- return plugin if plugin.name == name
347
- end
348
- end
349
- return nil
350
- end
351
-
352
- def load!
353
- contents = File.exist?(@cache_file) ? File.read(@cache_file) : defaults
354
- contents = defaults if contents.empty?
355
- @repositories = contents.split(/\n/).reject do |line|
356
- line =~ /^\s*#/ or line =~ /^\s*$/
357
- end.map { |source| Repository.new(source.strip) }
358
- end
359
-
360
- def save
361
- File.open(@cache_file, 'w') do |f|
362
- each do |repo|
363
- f.write(repo.uri)
364
- f.write("\n")
365
- end
366
- end
367
- end
368
-
369
- def defaults
370
- <<-DEFAULTS
371
- http://dev.rubyonrails.com/svn/rails/plugins/
372
- DEFAULTS
373
- end
374
-
375
- def find_home
376
- ['HOME', 'USERPROFILE'].each do |homekey|
377
- return ENV[homekey] if ENV[homekey]
378
- end
379
- if ENV['HOMEDRIVE'] && ENV['HOMEPATH']
380
- return "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}"
381
- end
382
- begin
383
- File.expand_path("~")
384
- rescue StandardError => ex
385
- if File::ALT_SEPARATOR
386
- "C:/"
387
- else
388
- "/"
389
- end
390
- end
391
- end
392
-
393
- def self.instance
394
- @instance ||= Repositories.new
395
- end
396
-
397
- def self.each(&block)
398
- self.instance.each(&block)
399
- end
400
- end
401
-
402
- class Repository
403
- include Enumerable
404
- attr_reader :uri, :plugins
405
-
406
- def initialize(uri)
407
- @uri = uri.chomp('/') << "/"
408
- @plugins = nil
409
- end
410
-
411
- def plugins
412
- unless @plugins
413
- if $verbose
414
- puts "Discovering plugins in #{@uri}"
415
- puts index
416
- end
417
-
418
- @plugins = index.reject{ |line| line !~ /\/$/ }
419
- @plugins.map! { |name| Plugin.new(File.join(@uri, name), name) }
420
- end
421
-
422
- @plugins
423
- end
424
-
425
- def each(&block)
426
- plugins.each(&block)
427
- end
428
-
429
- private
430
- def index
431
- @index ||= RecursiveHTTPFetcher.new(@uri).ls
432
- end
433
- end
434
-
435
-
436
- # load default environment and parse arguments
437
- require 'optparse'
438
- module Commands
439
-
440
- class Plugin
441
- attr_reader :environment, :script_name, :sources
442
- def initialize
443
- @environment = RailsEnvironment.default
444
- @rails_root = RailsEnvironment.default.root
445
- @script_name = File.basename($0)
446
- @sources = []
447
- end
448
-
449
- def environment=(value)
450
- @environment = value
451
- RailsEnvironment.default = value
452
- end
453
-
454
- def options
455
- OptionParser.new do |o|
456
- o.set_summary_indent(' ')
457
- o.banner = "Usage: #{@script_name} [OPTIONS] command"
458
- o.define_head "Rails plugin manager."
459
-
460
- o.separator ""
461
- o.separator "GENERAL OPTIONS"
462
-
463
- o.on("-r", "--root=DIR", String,
464
- "Set an explicit rails app directory.",
465
- "Default: #{@rails_root}") { |rails_root| @rails_root = rails_root; self.environment = RailsEnvironment.new(@rails_root) }
466
- o.on("-s", "--source=URL1,URL2", Array,
467
- "Use the specified plugin repositories instead of the defaults.") { |sources| @sources = sources}
468
-
469
- o.on("-v", "--verbose", "Turn on verbose output.") { |verbose| $verbose = verbose }
470
- o.on("-h", "--help", "Show this help message.") { puts o; exit }
471
-
472
- o.separator ""
473
- o.separator "COMMANDS"
474
-
475
- o.separator " discover Discover plugin repositories."
476
- o.separator " list List available plugins."
477
- o.separator " install Install plugin(s) from known repositories or URLs."
478
- o.separator " update Update installed plugins."
479
- o.separator " remove Uninstall plugins."
480
- o.separator " source Add a plugin source repository."
481
- o.separator " unsource Remove a plugin repository."
482
- o.separator " sources List currently configured plugin repositories."
483
-
484
- o.separator ""
485
- o.separator "EXAMPLES"
486
- o.separator " Install a plugin:"
487
- o.separator " #{@script_name} install continuous_builder\n"
488
- o.separator " Install a plugin from a subversion URL:"
489
- o.separator " #{@script_name} install http://dev.rubyonrails.com/svn/rails/plugins/continuous_builder\n"
490
- o.separator " Install a plugin from a git URL:"
491
- o.separator " #{@script_name} install git://github.com/SomeGuy/my_awesome_plugin.git\n"
492
- o.separator " Install a plugin and add a svn:externals entry to vendor/plugins"
493
- o.separator " #{@script_name} install -x continuous_builder\n"
494
- o.separator " List all available plugins:"
495
- o.separator " #{@script_name} list\n"
496
- o.separator " List plugins in the specified repository:"
497
- o.separator " #{@script_name} list --source=http://dev.rubyonrails.com/svn/rails/plugins/\n"
498
- o.separator " Discover and prompt to add new repositories:"
499
- o.separator " #{@script_name} discover\n"
500
- o.separator " Discover new repositories but just list them, don't add anything:"
501
- o.separator " #{@script_name} discover -l\n"
502
- o.separator " Add a new repository to the source list:"
503
- o.separator " #{@script_name} source http://dev.rubyonrails.com/svn/rails/plugins/\n"
504
- o.separator " Remove a repository from the source list:"
505
- o.separator " #{@script_name} unsource http://dev.rubyonrails.com/svn/rails/plugins/\n"
506
- o.separator " Show currently configured repositories:"
507
- o.separator " #{@script_name} sources\n"
508
- end
509
- end
510
-
511
- def parse!(args=ARGV)
512
- general, sub = split_args(args)
513
- options.parse!(general)
514
-
515
- command = general.shift
516
- if command =~ /^(list|discover|install|source|unsource|sources|remove|update|info)$/
517
- command = Commands.const_get(command.capitalize).new(self)
518
- command.parse!(sub)
519
- else
520
- puts "Unknown command: #{command}"
521
- puts options
522
- exit 1
523
- end
524
- end
525
-
526
- def split_args(args)
527
- left = []
528
- left << args.shift while args[0] and args[0] =~ /^-/
529
- left << args.shift if args[0]
530
- return [left, args]
531
- end
532
-
533
- def self.parse!(args=ARGV)
534
- Plugin.new.parse!(args)
535
- end
536
- end
537
-
538
-
539
- class List
540
- def initialize(base_command)
541
- @base_command = base_command
542
- @sources = []
543
- @local = false
544
- @remote = true
545
- end
546
-
547
- def options
548
- OptionParser.new do |o|
549
- o.set_summary_indent(' ')
550
- o.banner = "Usage: #{@base_command.script_name} list [OPTIONS] [PATTERN]"
551
- o.define_head "List available plugins."
552
- o.separator ""
553
- o.separator "Options:"
554
- o.separator ""
555
- o.on( "-s", "--source=URL1,URL2", Array,
556
- "Use the specified plugin repositories.") {|sources| @sources = sources}
557
- o.on( "--local",
558
- "List locally installed plugins.") {|local| @local, @remote = local, false}
559
- o.on( "--remote",
560
- "List remotely available plugins. This is the default behavior",
561
- "unless --local is provided.") {|remote| @remote = remote}
562
- end
563
- end
564
-
565
- def parse!(args)
566
- options.order!(args)
567
- unless @sources.empty?
568
- @sources.map!{ |uri| Repository.new(uri) }
569
- else
570
- @sources = Repositories.instance.all
571
- end
572
- if @remote
573
- @sources.map{|r| r.plugins}.flatten.each do |plugin|
574
- if @local or !plugin.installed?
575
- puts plugin.to_s
576
- end
577
- end
578
- else
579
- cd "#{@base_command.environment.root}/vendor/plugins"
580
- Dir["*"].select{|p| File.directory?(p)}.each do |name|
581
- puts name
582
- end
583
- end
584
- end
585
- end
586
-
587
-
588
- class Sources
589
- def initialize(base_command)
590
- @base_command = base_command
591
- end
592
-
593
- def options
594
- OptionParser.new do |o|
595
- o.set_summary_indent(' ')
596
- o.banner = "Usage: #{@base_command.script_name} sources [OPTIONS] [PATTERN]"
597
- o.define_head "List configured plugin repositories."
598
- o.separator ""
599
- o.separator "Options:"
600
- o.separator ""
601
- o.on( "-c", "--check",
602
- "Report status of repository.") { |sources| @sources = sources}
603
- end
604
- end
605
-
606
- def parse!(args)
607
- options.parse!(args)
608
- Repositories.each do |repo|
609
- puts repo.uri
610
- end
611
- end
612
- end
613
-
614
-
615
- class Source
616
- def initialize(base_command)
617
- @base_command = base_command
618
- end
619
-
620
- def options
621
- OptionParser.new do |o|
622
- o.set_summary_indent(' ')
623
- o.banner = "Usage: #{@base_command.script_name} source REPOSITORY [REPOSITORY [REPOSITORY]...]"
624
- o.define_head "Add new repositories to the default search list."
625
- end
626
- end
627
-
628
- def parse!(args)
629
- options.parse!(args)
630
- count = 0
631
- args.each do |uri|
632
- if Repositories.instance.add(uri)
633
- puts "added: #{uri.ljust(50)}" if $verbose
634
- count += 1
635
- else
636
- puts "failed: #{uri.ljust(50)}"
637
- end
638
- end
639
- Repositories.instance.save
640
- puts "Added #{count} repositories."
641
- end
642
- end
643
-
644
-
645
- class Unsource
646
- def initialize(base_command)
647
- @base_command = base_command
648
- end
649
-
650
- def options
651
- OptionParser.new do |o|
652
- o.set_summary_indent(' ')
653
- o.banner = "Usage: #{@base_command.script_name} unsource URI [URI [URI]...]"
654
- o.define_head "Remove repositories from the default search list."
655
- o.separator ""
656
- o.on_tail("-h", "--help", "Show this help message.") { puts o; exit }
657
- end
658
- end
659
-
660
- def parse!(args)
661
- options.parse!(args)
662
- count = 0
663
- args.each do |uri|
664
- if Repositories.instance.remove(uri)
665
- count += 1
666
- puts "removed: #{uri.ljust(50)}"
667
- else
668
- puts "failed: #{uri.ljust(50)}"
669
- end
670
- end
671
- Repositories.instance.save
672
- puts "Removed #{count} repositories."
673
- end
674
- end
675
-
676
-
677
- class Discover
678
- def initialize(base_command)
679
- @base_command = base_command
680
- @list = false
681
- @prompt = true
682
- end
683
-
684
- def options
685
- OptionParser.new do |o|
686
- o.set_summary_indent(' ')
687
- o.banner = "Usage: #{@base_command.script_name} discover URI [URI [URI]...]"
688
- o.define_head "Discover repositories referenced on a page."
689
- o.separator ""
690
- o.separator "Options:"
691
- o.separator ""
692
- o.on( "-l", "--list",
693
- "List but don't prompt or add discovered repositories.") { |list| @list, @prompt = list, !@list }
694
- o.on( "-n", "--no-prompt",
695
- "Add all new repositories without prompting.") { |v| @prompt = !v }
696
- end
697
- end
698
-
699
- def parse!(args)
700
- options.parse!(args)
701
- args = ['http://wiki.rubyonrails.org/rails/pages/Plugins'] if args.empty?
702
- args.each do |uri|
703
- scrape(uri) do |repo_uri|
704
- catch(:next_uri) do
705
- if @prompt
706
- begin
707
- $stdout.print "Add #{repo_uri}? [Y/n] "
708
- throw :next_uri if $stdin.gets !~ /^y?$/i
709
- rescue Interrupt
710
- $stdout.puts
711
- exit 1
712
- end
713
- elsif @list
714
- puts repo_uri
715
- throw :next_uri
716
- end
717
- Repositories.instance.add(repo_uri)
718
- puts "discovered: #{repo_uri}" if $verbose or !@prompt
719
- end
720
- end
721
- end
722
- Repositories.instance.save
723
- end
724
-
725
- def scrape(uri)
726
- require 'open-uri'
727
- puts "Scraping #{uri}" if $verbose
728
- dupes = []
729
- content = open(uri).each do |line|
730
- begin
731
- if line =~ /<a[^>]*href=['"]([^'"]*)['"]/ || line =~ /(svn:\/\/[^<|\n]*)/
732
- uri = $1
733
- if uri =~ /^\w+:\/\// && uri =~ /\/plugins\// && uri !~ /\/browser\// && uri !~ /^http:\/\/wiki\.rubyonrails/ && uri !~ /http:\/\/instiki/
734
- uri = extract_repository_uri(uri)
735
- yield uri unless dupes.include?(uri) || Repositories.instance.exist?(uri)
736
- dupes << uri
737
- end
738
- end
739
- rescue
740
- puts "Problems scraping '#{uri}': #{$!.to_s}"
741
- end
742
- end
743
- end
744
-
745
- def extract_repository_uri(uri)
746
- uri.match(/(svn|https?):.*\/plugins\//i)[0]
747
- end
748
- end
749
-
750
- class Install
751
- def initialize(base_command)
752
- @base_command = base_command
753
- @method = :http
754
- @options = { :quiet => false, :revision => nil, :force => false }
755
- end
756
-
757
- def options
758
- OptionParser.new do |o|
759
- o.set_summary_indent(' ')
760
- o.banner = "Usage: #{@base_command.script_name} install PLUGIN [PLUGIN [PLUGIN] ...]"
761
- o.define_head "Install one or more plugins."
762
- o.separator ""
763
- o.separator "Options:"
764
- o.on( "-x", "--externals",
765
- "Use svn:externals to grab the plugin.",
766
- "Enables plugin updates and plugin versioning.") { |v| @method = :externals }
767
- o.on( "-o", "--checkout",
768
- "Use svn checkout to grab the plugin.",
769
- "Enables updating but does not add a svn:externals entry.") { |v| @method = :checkout }
770
- o.on( "-e", "--export",
771
- "Use svn export to grab the plugin.",
772
- "Exports the plugin, allowing you to check it into your local repository. Does not enable updates, or add an svn:externals entry.") { |v| @method = :export }
773
- o.on( "-q", "--quiet",
774
- "Suppresses the output from installation.",
775
- "Ignored if -v is passed (./script/plugin -v install ...)") { |v| @options[:quiet] = true }
776
- o.on( "-r REVISION", "--revision REVISION",
777
- "Checks out the given revision from subversion or git.",
778
- "Ignored if subversion/git is not used.") { |v| @options[:revision] = v }
779
- o.on( "-f", "--force",
780
- "Reinstalls a plugin if it's already installed.") { |v| @options[:force] = true }
781
- o.separator ""
782
- o.separator "You can specify plugin names as given in 'plugin list' output or absolute URLs to "
783
- o.separator "a plugin repository."
784
- end
785
- end
786
-
787
- def determine_install_method
788
- best = @base_command.environment.best_install_method
789
- @method = :http if best == :http and @method == :export
790
- case
791
- when (best == :http and @method != :http)
792
- msg = "Cannot install using subversion because `svn' cannot be found in your PATH"
793
- when (best == :export and (@method != :export and @method != :http))
794
- msg = "Cannot install using #{@method} because this project is not under subversion."
795
- when (best != :externals and @method == :externals)
796
- msg = "Cannot install using externals because vendor/plugins is not under subversion."
797
- end
798
- if msg
799
- puts msg
800
- exit 1
801
- end
802
- @method
803
- end
804
-
805
- def parse!(args)
806
- options.parse!(args)
807
- environment = @base_command.environment
808
- install_method = determine_install_method
809
- puts "Plugins will be installed using #{install_method}" if $verbose
810
- args.each do |name|
811
- ::Plugin.find(name).install(install_method, @options)
812
- end
813
- rescue StandardError => e
814
- puts "Plugin not found: #{args.inspect}"
815
- puts e.inspect if $verbose
816
- exit 1
817
- end
818
- end
819
-
820
- class Update
821
- def initialize(base_command)
822
- @base_command = base_command
823
- end
824
-
825
- def options
826
- OptionParser.new do |o|
827
- o.set_summary_indent(' ')
828
- o.banner = "Usage: #{@base_command.script_name} update [name [name]...]"
829
- o.on( "-r REVISION", "--revision REVISION",
830
- "Checks out the given revision from subversion.",
831
- "Ignored if subversion is not used.") { |v| @revision = v }
832
- o.define_head "Update plugins."
833
- end
834
- end
835
-
836
- def parse!(args)
837
- options.parse!(args)
838
- root = @base_command.environment.root
839
- cd root
840
- args = Dir["vendor/plugins/*"].map do |f|
841
- File.directory?("#{f}/.svn") ? File.basename(f) : nil
842
- end.compact if args.empty?
843
- cd "vendor/plugins"
844
- args.each do |name|
845
- if File.directory?(name)
846
- puts "Updating plugin: #{name}"
847
- system("svn #{$verbose ? '' : '-q'} up \"#{name}\" #{@revision ? "-r #{@revision}" : ''}")
848
- else
849
- puts "Plugin doesn't exist: #{name}"
850
- end
851
- end
852
- end
853
- end
854
-
855
- class Remove
856
- def initialize(base_command)
857
- @base_command = base_command
858
- end
859
-
860
- def options
861
- OptionParser.new do |o|
862
- o.set_summary_indent(' ')
863
- o.banner = "Usage: #{@base_command.script_name} remove name [name]..."
864
- o.define_head "Remove plugins."
865
- end
866
- end
867
-
868
- def parse!(args)
869
- options.parse!(args)
870
- root = @base_command.environment.root
871
- args.each do |name|
872
- ::Plugin.new(name).uninstall
873
- end
874
- end
875
- end
876
-
877
- class Info
878
- def initialize(base_command)
879
- @base_command = base_command
880
- end
881
-
882
- def options
883
- OptionParser.new do |o|
884
- o.set_summary_indent(' ')
885
- o.banner = "Usage: #{@base_command.script_name} info name [name]..."
886
- o.define_head "Shows plugin info at {url}/about.yml."
887
- end
888
- end
889
-
890
- def parse!(args)
891
- options.parse!(args)
892
- args.each do |name|
893
- puts ::Plugin.find(name).info
894
- puts
895
- end
896
- end
897
- end
898
- end
899
-
900
- class RecursiveHTTPFetcher
901
- attr_accessor :quiet
902
- def initialize(urls_to_fetch, level = 1, cwd = ".")
903
- @level = level
904
- @cwd = cwd
905
- @urls_to_fetch = RUBY_VERSION >= '1.9' ? urls_to_fetch.lines : urls_to_fetch.to_a
906
- @quiet = false
907
- end
908
-
909
- def ls
910
- @urls_to_fetch.collect do |url|
911
- if url =~ /^svn(\+ssh)?:\/\/.*/
912
- `svn ls #{url}`.split("\n").map {|entry| "/#{entry}"} rescue nil
913
- else
914
- open(url) do |stream|
915
- links("", stream.read)
916
- end rescue nil
917
- end
918
- end.flatten
919
- end
920
-
921
- def push_d(dir)
922
- @cwd = File.join(@cwd, dir)
923
- FileUtils.mkdir_p(@cwd)
924
- end
925
-
926
- def pop_d
927
- @cwd = File.dirname(@cwd)
928
- end
929
-
930
- def links(base_url, contents)
931
- links = []
932
- contents.scan(/href\s*=\s*\"*[^\">]*/i) do |link|
933
- link = link.sub(/href="/i, "")
934
- next if link =~ /svnindex.xsl$/
935
- next if link =~ /^(\w*:|)\/\// || link =~ /^\./
936
- links << File.join(base_url, link)
937
- end
938
- links
939
- end
940
-
941
- def download(link)
942
- puts "+ #{File.join(@cwd, File.basename(link))}" unless @quiet
943
- open(link) do |stream|
944
- File.open(File.join(@cwd, File.basename(link)), "wb") do |file|
945
- file.write(stream.read)
946
- end
947
- end
948
- end
949
-
950
- def fetch(links = @urls_to_fetch)
951
- links.each do |l|
952
- (l =~ /\/$/ || links == @urls_to_fetch) ? fetch_dir(l) : download(l)
953
- end
954
- end
955
-
956
- def fetch_dir(url)
957
- @level += 1
958
- push_d(File.basename(url)) if @level > 0
959
- open(url) do |stream|
960
- contents = stream.read
961
- fetch(links(url, contents))
962
- end
963
- pop_d if @level > 0
964
- @level -= 1
965
- end
966
- end
967
-
968
- Commands::Plugin.parse!