rails_csi 2.3.5.p6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (320) hide show
  1. data/CHANGELOG +2172 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README +243 -0
  4. data/Rakefile +368 -0
  5. data/bin/about +4 -0
  6. data/bin/console +3 -0
  7. data/bin/dbconsole +3 -0
  8. data/bin/destroy +3 -0
  9. data/bin/generate +3 -0
  10. data/bin/performance/benchmarker +3 -0
  11. data/bin/performance/profiler +3 -0
  12. data/bin/plugin +3 -0
  13. data/bin/rails +20 -0
  14. data/bin/runner +3 -0
  15. data/bin/server +3 -0
  16. data/builtin/rails_info/rails/info.rb +131 -0
  17. data/builtin/rails_info/rails/info_controller.rb +9 -0
  18. data/builtin/rails_info/rails/info_helper.rb +2 -0
  19. data/builtin/rails_info/rails_info_controller.rb +2 -0
  20. data/configs/databases/frontbase.yml +28 -0
  21. data/configs/databases/ibm_db.yml +62 -0
  22. data/configs/databases/mysql.yml +60 -0
  23. data/configs/databases/oracle.yml +39 -0
  24. data/configs/databases/postgresql.yml +51 -0
  25. data/configs/databases/sqlite2.yml +19 -0
  26. data/configs/databases/sqlite3.yml +22 -0
  27. data/configs/empty.log +0 -0
  28. data/configs/initializers/backtrace_silencers.rb +7 -0
  29. data/configs/initializers/inflections.rb +10 -0
  30. data/configs/initializers/mime_types.rb +5 -0
  31. data/configs/initializers/new_rails_defaults.rb +21 -0
  32. data/configs/initializers/session_store.rb +15 -0
  33. data/configs/locales/en.yml +5 -0
  34. data/configs/routes.rb +43 -0
  35. data/configs/seeds.rb +7 -0
  36. data/dispatches/config.ru +7 -0
  37. data/dispatches/dispatch.fcgi +24 -0
  38. data/dispatches/dispatch.rb +10 -0
  39. data/dispatches/gateway.cgi +97 -0
  40. data/doc/README_FOR_APP +2 -0
  41. data/environments/boot.rb +110 -0
  42. data/environments/development.rb +17 -0
  43. data/environments/environment.rb +41 -0
  44. data/environments/production.rb +28 -0
  45. data/environments/test.rb +28 -0
  46. data/fresh_rakefile +10 -0
  47. data/guides/files/javascripts/code_highlighter.js +188 -0
  48. data/guides/files/javascripts/guides.js +8 -0
  49. data/guides/files/javascripts/highlighters.js +90 -0
  50. data/guides/files/stylesheets/main.css +441 -0
  51. data/guides/files/stylesheets/print.css +52 -0
  52. data/guides/files/stylesheets/reset.css +43 -0
  53. data/guides/files/stylesheets/style.css +13 -0
  54. data/guides/files/stylesheets/syntax.css +31 -0
  55. data/guides/images/belongs_to.png +0 -0
  56. data/guides/images/book_icon.gif +0 -0
  57. data/guides/images/bullet.gif +0 -0
  58. data/guides/images/chapters_icon.gif +0 -0
  59. data/guides/images/check_bullet.gif +0 -0
  60. data/guides/images/credits_pic_blank.gif +0 -0
  61. data/guides/images/csrf.png +0 -0
  62. data/guides/images/customized_error_messages.png +0 -0
  63. data/guides/images/error_messages.png +0 -0
  64. data/guides/images/feature_tile.gif +0 -0
  65. data/guides/images/footer_tile.gif +0 -0
  66. data/guides/images/fxn.jpg +0 -0
  67. data/guides/images/grey_bullet.gif +0 -0
  68. data/guides/images/habtm.png +0 -0
  69. data/guides/images/has_many.png +0 -0
  70. data/guides/images/has_many_through.png +0 -0
  71. data/guides/images/has_one.png +0 -0
  72. data/guides/images/has_one_through.png +0 -0
  73. data/guides/images/header_backdrop.png +0 -0
  74. data/guides/images/header_tile.gif +0 -0
  75. data/guides/images/i18n/demo_localized_pirate.png +0 -0
  76. data/guides/images/i18n/demo_translated_en.png +0 -0
  77. data/guides/images/i18n/demo_translated_pirate.png +0 -0
  78. data/guides/images/i18n/demo_translation_missing.png +0 -0
  79. data/guides/images/i18n/demo_untranslated.png +0 -0
  80. data/guides/images/icons/README +5 -0
  81. data/guides/images/icons/callouts/1.png +0 -0
  82. data/guides/images/icons/callouts/10.png +0 -0
  83. data/guides/images/icons/callouts/11.png +0 -0
  84. data/guides/images/icons/callouts/12.png +0 -0
  85. data/guides/images/icons/callouts/13.png +0 -0
  86. data/guides/images/icons/callouts/14.png +0 -0
  87. data/guides/images/icons/callouts/15.png +0 -0
  88. data/guides/images/icons/callouts/2.png +0 -0
  89. data/guides/images/icons/callouts/3.png +0 -0
  90. data/guides/images/icons/callouts/4.png +0 -0
  91. data/guides/images/icons/callouts/5.png +0 -0
  92. data/guides/images/icons/callouts/6.png +0 -0
  93. data/guides/images/icons/callouts/7.png +0 -0
  94. data/guides/images/icons/callouts/8.png +0 -0
  95. data/guides/images/icons/callouts/9.png +0 -0
  96. data/guides/images/icons/caution.png +0 -0
  97. data/guides/images/icons/example.png +0 -0
  98. data/guides/images/icons/home.png +0 -0
  99. data/guides/images/icons/important.png +0 -0
  100. data/guides/images/icons/next.png +0 -0
  101. data/guides/images/icons/note.png +0 -0
  102. data/guides/images/icons/prev.png +0 -0
  103. data/guides/images/icons/tip.png +0 -0
  104. data/guides/images/icons/up.png +0 -0
  105. data/guides/images/icons/warning.png +0 -0
  106. data/guides/images/nav_arrow.gif +0 -0
  107. data/guides/images/polymorphic.png +0 -0
  108. data/guides/images/posts_index.png +0 -0
  109. data/guides/images/rails_guides_logo.gif +0 -0
  110. data/guides/images/rails_logo_remix.gif +0 -0
  111. data/guides/images/rails_welcome.png +0 -0
  112. data/guides/images/session_fixation.png +0 -0
  113. data/guides/images/tab_grey.gif +0 -0
  114. data/guides/images/tab_info.gif +0 -0
  115. data/guides/images/tab_note.gif +0 -0
  116. data/guides/images/tab_red.gif +0 -0
  117. data/guides/images/tab_yellow.gif +0 -0
  118. data/guides/images/tab_yellow.png +0 -0
  119. data/guides/images/validation_error_messages.png +0 -0
  120. data/guides/rails_guides.rb +42 -0
  121. data/guides/rails_guides/generator.rb +138 -0
  122. data/guides/rails_guides/helpers.rb +34 -0
  123. data/guides/rails_guides/indexer.rb +55 -0
  124. data/guides/rails_guides/textile_extensions.rb +41 -0
  125. data/guides/source/2_2_release_notes.textile +422 -0
  126. data/guides/source/2_3_release_notes.textile +610 -0
  127. data/guides/source/action_controller_overview.textile +776 -0
  128. data/guides/source/action_mailer_basics.textile +424 -0
  129. data/guides/source/active_record_basics.textile +135 -0
  130. data/guides/source/active_record_querying.textile +969 -0
  131. data/guides/source/activerecord_validations_callbacks.textile +1086 -0
  132. data/guides/source/association_basics.textile +1781 -0
  133. data/guides/source/caching_with_rails.textile +524 -0
  134. data/guides/source/command_line.textile +589 -0
  135. data/guides/source/configuring.textile +234 -0
  136. data/guides/source/contribute.textile +71 -0
  137. data/guides/source/contributing_to_rails.textile +239 -0
  138. data/guides/source/credits.erb.textile +52 -0
  139. data/guides/source/debugging_rails_applications.textile +709 -0
  140. data/guides/source/form_helpers.textile +766 -0
  141. data/guides/source/getting_started.textile +1297 -0
  142. data/guides/source/i18n.textile +912 -0
  143. data/guides/source/index.erb.textile +124 -0
  144. data/guides/source/layout.html.erb +103 -0
  145. data/guides/source/layouts_and_rendering.textile +979 -0
  146. data/guides/source/migrations.textile +591 -0
  147. data/guides/source/nested_model_forms.textile +222 -0
  148. data/guides/source/performance_testing.textile +531 -0
  149. data/guides/source/plugins.textile +1512 -0
  150. data/guides/source/rails_on_rack.textile +309 -0
  151. data/guides/source/routing.textile +903 -0
  152. data/guides/source/security.textile +986 -0
  153. data/guides/source/testing.textile +951 -0
  154. data/helpers/application_controller.rb +10 -0
  155. data/helpers/application_helper.rb +3 -0
  156. data/helpers/performance_test.rb +9 -0
  157. data/helpers/test_helper.rb +38 -0
  158. data/html/404.html +30 -0
  159. data/html/422.html +30 -0
  160. data/html/500.html +30 -0
  161. data/html/favicon.ico +0 -0
  162. data/html/images/rails.png +0 -0
  163. data/html/index.html +275 -0
  164. data/html/javascripts/application.js +2 -0
  165. data/html/javascripts/controls.js +963 -0
  166. data/html/javascripts/dragdrop.js +973 -0
  167. data/html/javascripts/effects.js +1128 -0
  168. data/html/javascripts/prototype.js +4320 -0
  169. data/html/robots.txt +5 -0
  170. data/lib/code_statistics.rb +107 -0
  171. data/lib/commands.rb +17 -0
  172. data/lib/commands/about.rb +3 -0
  173. data/lib/commands/console.rb +45 -0
  174. data/lib/commands/dbconsole.rb +87 -0
  175. data/lib/commands/destroy.rb +6 -0
  176. data/lib/commands/generate.rb +6 -0
  177. data/lib/commands/ncgi/listener +86 -0
  178. data/lib/commands/ncgi/tracker +69 -0
  179. data/lib/commands/performance/benchmarker.rb +24 -0
  180. data/lib/commands/performance/profiler.rb +50 -0
  181. data/lib/commands/plugin.rb +968 -0
  182. data/lib/commands/runner.rb +54 -0
  183. data/lib/commands/server.rb +114 -0
  184. data/lib/commands/update.rb +4 -0
  185. data/lib/console_app.rb +30 -0
  186. data/lib/console_sandbox.rb +6 -0
  187. data/lib/console_with_helpers.rb +5 -0
  188. data/lib/dispatcher.rb +24 -0
  189. data/lib/fcgi_handler.rb +239 -0
  190. data/lib/initializer.rb +1128 -0
  191. data/lib/performance_test_help.rb +5 -0
  192. data/lib/rails/backtrace_cleaner.rb +54 -0
  193. data/lib/rails/gem_builder.rb +21 -0
  194. data/lib/rails/gem_dependency.rb +311 -0
  195. data/lib/rails/plugin.rb +179 -0
  196. data/lib/rails/plugin/loader.rb +198 -0
  197. data/lib/rails/plugin/locator.rb +100 -0
  198. data/lib/rails/rack.rb +8 -0
  199. data/lib/rails/rack/debugger.rb +23 -0
  200. data/lib/rails/rack/log_tailer.rb +35 -0
  201. data/lib/rails/rack/metal.rb +51 -0
  202. data/lib/rails/rack/static.rb +46 -0
  203. data/lib/rails/vendor_gem_source_index.rb +140 -0
  204. data/lib/rails/version.rb +9 -0
  205. data/lib/rails_generator.rb +43 -0
  206. data/lib/rails_generator/base.rb +266 -0
  207. data/lib/rails_generator/commands.rb +621 -0
  208. data/lib/rails_generator/generated_attribute.rb +46 -0
  209. data/lib/rails_generator/generators/applications/app/USAGE +9 -0
  210. data/lib/rails_generator/generators/applications/app/app_generator.rb +263 -0
  211. data/lib/rails_generator/generators/applications/app/scm/git.rb +18 -0
  212. data/lib/rails_generator/generators/applications/app/scm/scm.rb +8 -0
  213. data/lib/rails_generator/generators/applications/app/scm/svn.rb +7 -0
  214. data/lib/rails_generator/generators/applications/app/template_runner.rb +401 -0
  215. data/lib/rails_generator/generators/components/controller/USAGE +30 -0
  216. data/lib/rails_generator/generators/components/controller/controller_generator.rb +43 -0
  217. data/lib/rails_generator/generators/components/controller/templates/controller.rb +7 -0
  218. data/lib/rails_generator/generators/components/controller/templates/functional_test.rb +8 -0
  219. data/lib/rails_generator/generators/components/controller/templates/helper.rb +2 -0
  220. data/lib/rails_generator/generators/components/controller/templates/helper_test.rb +4 -0
  221. data/lib/rails_generator/generators/components/controller/templates/view.html.erb +2 -0
  222. data/lib/rails_generator/generators/components/helper/USAGE +24 -0
  223. data/lib/rails_generator/generators/components/helper/helper_generator.rb +25 -0
  224. data/lib/rails_generator/generators/components/helper/templates/helper.rb +2 -0
  225. data/lib/rails_generator/generators/components/helper/templates/helper_test.rb +4 -0
  226. data/lib/rails_generator/generators/components/integration_test/USAGE +8 -0
  227. data/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb +16 -0
  228. data/lib/rails_generator/generators/components/integration_test/templates/integration_test.rb +10 -0
  229. data/lib/rails_generator/generators/components/mailer/USAGE +16 -0
  230. data/lib/rails_generator/generators/components/mailer/mailer_generator.rb +30 -0
  231. data/lib/rails_generator/generators/components/mailer/templates/fixture.erb +3 -0
  232. data/lib/rails_generator/generators/components/mailer/templates/fixture.rhtml +0 -0
  233. data/lib/rails_generator/generators/components/mailer/templates/mailer.rb +15 -0
  234. data/lib/rails_generator/generators/components/mailer/templates/unit_test.rb +20 -0
  235. data/lib/rails_generator/generators/components/mailer/templates/view.erb +3 -0
  236. data/lib/rails_generator/generators/components/mailer/templates/view.rhtml +0 -0
  237. data/lib/rails_generator/generators/components/metal/USAGE +8 -0
  238. data/lib/rails_generator/generators/components/metal/metal_generator.rb +8 -0
  239. data/lib/rails_generator/generators/components/metal/templates/metal.rb +12 -0
  240. data/lib/rails_generator/generators/components/migration/USAGE +29 -0
  241. data/lib/rails_generator/generators/components/migration/migration_generator.rb +20 -0
  242. data/lib/rails_generator/generators/components/migration/templates/migration.rb +11 -0
  243. data/lib/rails_generator/generators/components/model/USAGE +27 -0
  244. data/lib/rails_generator/generators/components/model/model_generator.rb +52 -0
  245. data/lib/rails_generator/generators/components/model/templates/fixtures.yml +19 -0
  246. data/lib/rails_generator/generators/components/model/templates/migration.rb +16 -0
  247. data/lib/rails_generator/generators/components/model/templates/model.rb +5 -0
  248. data/lib/rails_generator/generators/components/model/templates/unit_test.rb +8 -0
  249. data/lib/rails_generator/generators/components/observer/USAGE +13 -0
  250. data/lib/rails_generator/generators/components/observer/observer_generator.rb +16 -0
  251. data/lib/rails_generator/generators/components/observer/templates/observer.rb +2 -0
  252. data/lib/rails_generator/generators/components/observer/templates/unit_test.rb +8 -0
  253. data/lib/rails_generator/generators/components/performance_test/USAGE +8 -0
  254. data/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb +16 -0
  255. data/lib/rails_generator/generators/components/performance_test/templates/performance_test.rb +9 -0
  256. data/lib/rails_generator/generators/components/plugin/USAGE +25 -0
  257. data/lib/rails_generator/generators/components/plugin/plugin_generator.rb +39 -0
  258. data/lib/rails_generator/generators/components/plugin/templates/MIT-LICENSE +20 -0
  259. data/lib/rails_generator/generators/components/plugin/templates/README +13 -0
  260. data/lib/rails_generator/generators/components/plugin/templates/Rakefile +23 -0
  261. data/lib/rails_generator/generators/components/plugin/templates/USAGE +8 -0
  262. data/lib/rails_generator/generators/components/plugin/templates/generator.rb +8 -0
  263. data/lib/rails_generator/generators/components/plugin/templates/init.rb +1 -0
  264. data/lib/rails_generator/generators/components/plugin/templates/install.rb +1 -0
  265. data/lib/rails_generator/generators/components/plugin/templates/plugin.rb +1 -0
  266. data/lib/rails_generator/generators/components/plugin/templates/tasks.rake +4 -0
  267. data/lib/rails_generator/generators/components/plugin/templates/test_helper.rb +3 -0
  268. data/lib/rails_generator/generators/components/plugin/templates/uninstall.rb +1 -0
  269. data/lib/rails_generator/generators/components/plugin/templates/unit_test.rb +8 -0
  270. data/lib/rails_generator/generators/components/resource/USAGE +23 -0
  271. data/lib/rails_generator/generators/components/resource/resource_generator.rb +76 -0
  272. data/lib/rails_generator/generators/components/resource/templates/controller.rb +2 -0
  273. data/lib/rails_generator/generators/components/resource/templates/functional_test.rb +8 -0
  274. data/lib/rails_generator/generators/components/resource/templates/helper.rb +2 -0
  275. data/lib/rails_generator/generators/components/resource/templates/helper_test.rb +4 -0
  276. data/lib/rails_generator/generators/components/scaffold/USAGE +29 -0
  277. data/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb +103 -0
  278. data/lib/rails_generator/generators/components/scaffold/templates/controller.rb +85 -0
  279. data/lib/rails_generator/generators/components/scaffold/templates/functional_test.rb +45 -0
  280. data/lib/rails_generator/generators/components/scaffold/templates/helper.rb +2 -0
  281. data/lib/rails_generator/generators/components/scaffold/templates/helper_test.rb +4 -0
  282. data/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb +17 -0
  283. data/lib/rails_generator/generators/components/scaffold/templates/style.css +54 -0
  284. data/lib/rails_generator/generators/components/scaffold/templates/view_edit.html.erb +18 -0
  285. data/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb +24 -0
  286. data/lib/rails_generator/generators/components/scaffold/templates/view_new.html.erb +17 -0
  287. data/lib/rails_generator/generators/components/scaffold/templates/view_show.html.erb +10 -0
  288. data/lib/rails_generator/generators/components/session_migration/USAGE +10 -0
  289. data/lib/rails_generator/generators/components/session_migration/session_migration_generator.rb +18 -0
  290. data/lib/rails_generator/generators/components/session_migration/templates/migration.rb +16 -0
  291. data/lib/rails_generator/lookup.rb +249 -0
  292. data/lib/rails_generator/manifest.rb +53 -0
  293. data/lib/rails_generator/options.rb +150 -0
  294. data/lib/rails_generator/scripts.rb +89 -0
  295. data/lib/rails_generator/scripts/destroy.rb +29 -0
  296. data/lib/rails_generator/scripts/generate.rb +7 -0
  297. data/lib/rails_generator/scripts/update.rb +12 -0
  298. data/lib/rails_generator/secret_key_generator.rb +24 -0
  299. data/lib/rails_generator/simple_logger.rb +46 -0
  300. data/lib/rails_generator/spec.rb +44 -0
  301. data/lib/railties_path.rb +1 -0
  302. data/lib/ruby_version_check.rb +17 -0
  303. data/lib/rubyprof_ext.rb +35 -0
  304. data/lib/source_annotation_extractor.rb +102 -0
  305. data/lib/tasks/annotations.rake +20 -0
  306. data/lib/tasks/databases.rake +436 -0
  307. data/lib/tasks/documentation.rake +88 -0
  308. data/lib/tasks/framework.rake +143 -0
  309. data/lib/tasks/gems.rake +78 -0
  310. data/lib/tasks/log.rake +9 -0
  311. data/lib/tasks/middleware.rake +7 -0
  312. data/lib/tasks/misc.rake +63 -0
  313. data/lib/tasks/rails.rb +9 -0
  314. data/lib/tasks/routes.rake +18 -0
  315. data/lib/tasks/statistics.rake +17 -0
  316. data/lib/tasks/testing.rake +139 -0
  317. data/lib/tasks/tmp.rake +37 -0
  318. data/lib/test_help.rb +38 -0
  319. data/lib/webrick_server.rb +156 -0
  320. metadata +495 -0
@@ -0,0 +1,24 @@
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
@@ -0,0 +1,50 @@
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
@@ -0,0 +1,968 @@
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!