rails-scripts.el
1 ;;; rails-scripts.el --- emacs-rails integraions with rails script/* scripts 2 3 ;; Copyright (C) 2006 Dmitry Galinsky <dima dot exe at gmail dot com> 4 5 ;; Authors: Dmitry Galinsky <dima dot exe at gmail dot com>, 6 ;; Rezikov Peter <crazypit13 (at) gmail.com> 7 8 ;; Keywords: ruby rails languages oop 9 ;; $URL: svn://rubyforge.org/var/svn/emacs-rails/trunk/rails-scripts.el $ 10 ;; $Id: rails-scripts.el 192 2007-05-03 11:54:30Z dimaexe $ 11 12 ;;; License 13 14 ;; This program is free software; you can redistribute it and/or 15 ;; modify it under the terms of the GNU General Public License 16 ;; as published by the Free Software Foundation; either version 2 17 ;; of the License, or (at your option) any later version. 18 19 ;; This program is distributed in the hope that it will be useful, 20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 ;; GNU General Public License for more details. 23 24 ;; You should have received a copy of the GNU General Public License 25 ;; along with this program; if not, write to the Free Software 26 ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 27 28 (eval-when-compile 29 (require 'inf-ruby) 30 (require 'ruby-mode)) 31 32 (defvar rails-script:generators-list 33 '("controller" "model" "scaffold" "migration" "plugin" "mailer" "observer" "resource")) 34 35 (defvar rails-script:destroy-list rails-script:generators-list) 36 37 (defvar rails-script:generate-params-list 38 '("-f") 39 "Add parameters to script/generate. 40 For example -s to keep existing files and -c to add new files into svn.") 41 42 (defvar rails-script:destroy-params-list 43 '("-f") 44 "Add parameters to script/destroy. 45 For example -c to remove files from svn.") 46 47 (defvar rails-script:buffer-name "*ROutput*") 48 49 (defvar rails-script:running-script-name nil 50 "Curently running the script name") 51 52 (defvar rails-script:history (list)) 53 (defvar rails-script:history-of-generate (list)) 54 (defvar rails-script:history-of-destroy (list)) 55 56 ;; output-mode 57 58 (defconst rails-script:font-lock-ketwords 59 (list 60 '("^\\(\(in [^\)]+\)\\)$" 1 font-lock-builtin-face) 61 '(" \\(rm\\|rmdir\\) " 1 font-lock-warning-face) 62 '(" \\(missing\\|notempty\\|exists\\) " 1 font-lock-warning-face) 63 '(" \\(create\\|dependency\\) " 1 font-lock-function-name-face))) 64 65 (defconst rails-script:button-regexp 66 " \\(create\\) + \\([^ ]+\\.\\w+\\)") 67 68 (defvar rails-script:output-mode-ret-value nil) 69 (defvar rails-script:run-after-stop-hook nil) 70 (defvar rails-script:show-buffer-hook nil) 71 72 (defun rails-script:make-buttons (start end len) 73 (save-excursion 74 (let ((buffer-read-only nil)) 75 (goto-char start) 76 (while (re-search-forward rails-script:button-regexp end t) 77 (make-button (match-beginning 2) (match-end 2) 78 :type 'rails-button 79 :rails:file-name (match-string 2)))))) 80 81 (defun rails-script:popup-buffer (&optional do-not-scroll-to-top) 82 "Popup output buffer." 83 (unless (buffer-visible-p rails-script:buffer-name) 84 (display-buffer rails-script:buffer-name t)) 85 (let ((win (get-buffer-window-list rails-script:buffer-name))) 86 (when win 87 (unless do-not-scroll-to-top 88 (mapcar #'(lambda(w) (set-window-point w 0)) win)) 89 (shrink-window-if-larger-than-buffer 90 (get-buffer-window rails-script:buffer-name)) 91 (run-hooks 'rails-script:show-buffer-hook)))) 92 93 (defun rails-script:push-first-button () 94 (let (file-name) 95 (with-current-buffer (get-buffer rails-script:buffer-name) 96 (let ((button (next-button 1))) 97 (when button 98 (setq file-name (button-get button :rails:file-name))))) 99 (when file-name 100 (rails-core:find-file-if-exist file-name)))) 101 102 (defun rails-script:toggle-output-window () 103 (interactive) 104 (let ((current (current-buffer)) 105 (buf (get-buffer rails-script:buffer-name))) 106 (if buf 107 (if (buffer-visible-p rails-script:buffer-name) 108 (delete-windows-on buf) 109 (progn 110 (pop-to-buffer rails-script:buffer-name t t) 111 (pop-to-buffer current t t) 112 (shrink-window-if-larger-than-buffer 113 (get-buffer-window rails-script:buffer-name)) 114 (run-hooks 'rails-script:show-buffer-hook))) 115 (message "No output window found. Try running a script or a rake task before.")))) 116 117 (defun rails-script:setup-output-buffer () 118 "Setup default variables and values for the output buffer." 119 (set (make-local-variable 'font-lock-keywords-only) t) 120 (make-local-variable 'font-lock-defaults) 121 (set (make-local-variable 'scroll-margin) 0) 122 (set (make-local-variable 'scroll-preserve-screen-position) nil) 123 (make-local-hook 'rails-script:run-after-stop-hook) 124 (make-local-hook 'rails-script:show-buffer-hook) 125 (make-local-variable 'after-change-functions) 126 (rails-minor-mode t)) 127 128 (define-derived-mode rails-script:output-mode fundamental-mode "ROutput" 129 "Major mode to Rails Script Output." 130 (rails-script:setup-output-buffer) 131 (setq font-lock-defaults '((rails-script:font-lock-ketwords) nil t)) 132 (buffer-disable-undo) 133 (setq buffer-read-only t) 134 (rails-script:make-buttons (point-min) (point-max) (point-max)) 135 (add-hook 'rails-script:run-after-stop-hook 'rails-script:popup-buffer t t) 136 (add-hook 'rails-script:run-after-stop-hook 'rails-script:push-first-button t t) 137 (add-hook 'after-change-functions 'rails-script:make-buttons nil t) 138 (run-hooks 'rails-script:output-mode-hook)) 139 140 (defun rails-script:running-p () 141 (get-buffer-process rails-script:buffer-name)) 142 143 (defun rails-script:sentinel-proc (proc msg) 144 (let* ((name rails-script:running-script-name) 145 (ret-val (process-exit-status proc)) 146 (buf (get-buffer rails-script:buffer-name)) 147 (ret-message (if (zerop ret-val) "successful" "failure"))) 148 (with-current-buffer buf 149 (set (make-local-variable 'rails-script:output-mode-ret-value) ret-val)) 150 (when (memq (process-status proc) '(exit signal)) 151 (setq rails-script:running-script-name nil 152 msg (format "%s was stopped (%s)." name ret-message))) 153 (message (replace-regexp-in-string "\n" "" msg)) 154 (with-current-buffer buf 155 (run-hooks 'rails-script:run-after-stop-hook)))) 156 157 (defun rails-script:run (command parameters &optional buffer-major-mode) 158 "Run a Rails script COMMAND with PARAMETERS with 159 BUFFER-MAJOR-MODE and process-sentinel SENTINEL." 160 (unless (listp parameters) 161 (error "rails-script:run PARAMETERS must be the list")) 162 (rails-project:with-root 163 (root) 164 (save-some-buffers) 165 (let ((proc (rails-script:running-p))) 166 (if proc 167 (message "Only one instance rails-script allowed") 168 (let* ((default-directory root) 169 (proc (rails-cmd-proxy:start-process rails-script:buffer-name 170 rails-script:buffer-name 171 command 172 (strings-join " " parameters)))) 173 (with-current-buffer (get-buffer rails-script:buffer-name) 174 (let ((buffer-read-only nil) 175 (win (get-buffer-window-list rails-script:buffer-name))) 176 (kill-region (point-min) (point-max))) 177 (if buffer-major-mode 178 (apply buffer-major-mode (list)) 179 (rails-script:output-mode)) 180 (add-hook 'after-change-functions 'rails-cmd-proxy:convert-buffer-from-remote nil t)) 181 (set-process-coding-system proc 'utf-8-dos 'utf-8-dos) 182 (set-process-sentinel proc 'rails-script:sentinel-proc) 183 (setq rails-script:running-script-name 184 (if (= 1 (length parameters)) 185 (format "%s %s" command (first parameters)) 186 (format "%s %s" (first parameters) (first (cdr parameters))))) 187 (message "Starting %s." rails-script:running-script-name)))))) 188 189 ;;;;;;;;;; Destroy stuff ;;;;;;;;;; 190 191 (defun rails-script:run-destroy (what &rest parameters) 192 "Run the destroy script using WHAT and PARAMETERS." 193 (rails-script:run rails-ruby-command 194 (append (list (format "script/destroy %s" what)) 195 parameters 196 rails-script:destroy-params-list))) 197 198 (defun rails-script:destroy (what) 199 "Run destroy WHAT" 200 (interactive (rails-completing-read "What destroy" rails-script:destroy-list 201 'rails-script:history-of-destroy nil)) 202 (let ((name (intern (concat "rails-script:destroy-" 203 (replace-regexp-in-string "_" "-" what))))) 204 (when (fboundp name) 205 (call-interactively name)))) 206 207 (defmacro rails-script:gen-destroy-function (name &optional completion completion-arg) 208 (let ((func (intern (format "rails-script:destroy-%s" name))) 209 (param (intern (concat name "-name")))) 210 `(defun ,func (&optional ,param) 211 (interactive 212 (list (completing-read ,(concat "Destroy " 213 (replace-regexp-in-string "[^a-z0-9]" " " name) 214 ": ") 215 ,(if completion 216 `(list->alist 217 ,(if completion-arg 218 `(,completion ,completion-arg) 219 `(,completion))) 220 nil)))) 221 (when (string-not-empty ,param) 222 (rails-script:run-destroy ,(replace-regexp-in-string "-" "_" name) ,param))))) 223 224 (rails-script:gen-destroy-function "controller" rails-core:controllers t) 225 (rails-script:gen-destroy-function "model" rails-core:models) 226 (rails-script:gen-destroy-function "scaffold") 227 (rails-script:gen-destroy-function "migration" rails-core:migrations t) 228 (rails-script:gen-destroy-function "mailer" rails-core:mailers) 229 (rails-script:gen-destroy-function "plugin" rails-core:plugins) 230 (rails-script:gen-destroy-function "observer" rails-core:observers) 231 (rails-script:gen-destroy-function "resource") 232 233 ;;;;;;;;;; Generators stuff ;;;;;;;;;; 234 235 (defun rails-script:run-generate (what &rest parameters) 236 "Run the generate script using WHAT and PARAMETERS." 237 (rails-script:run rails-ruby-command 238 (append (list (format "script/generate %s" what)) 239 parameters 240 rails-script:generate-params-list))) 241 242 (defun rails-script:generate (what) 243 "Run generate WHAT" 244 (interactive (rails-completing-read "What generate" rails-script:generators-list 245 'rails-script:history-of-generate nil)) 246 (let ((name (intern (concat "rails-script:generate-" 247 (replace-regexp-in-string "_" "-" what))))) 248 (when (fboundp name) 249 (call-interactively name)))) 250 251 (defmacro rails-script:gen-generate-function (name &optional completion completion-arg) 252 (let ((func (intern (format "rails-script:generate-%s" name))) 253 (param (intern (concat name "-name")))) 254 `(defun ,func (&optional ,param) 255 (interactive 256 (list (completing-read ,(concat "Generate " 257 (replace-regexp-in-string "[^a-z0-9]" " " name) 258 ": ") 259 ,(if completion 260 `(list->alist 261 ,(if completion-arg 262 `(,completion ,completion-arg) 263 `(,completion))) 264 nil)))) 265 (when (string-not-empty ,param) 266 (rails-script:run-generate ,(replace-regexp-in-string "-" "_" name) ,param))))) 267 268 (defun rails-script:generate-controller (&optional controller-name actions) 269 "Generate a controller and open the controller file." 270 (interactive (list 271 (completing-read "Controller name (use autocomplete) : " 272 (list->alist (rails-core:controllers-ancestors))) 273 (read-string "Actions (or return to skip): "))) 274 (when (string-not-empty controller-name) 275 (rails-script:run-generate "controller" controller-name actions))) 276 277 (defun rails-script:generate-scaffold (&optional model-name controller-name actions) 278 "Generate a scaffold and open the controller file." 279 (interactive 280 "MModel name: \nMController (or return to skip): \nMActions (or return to skip): ") 281 (when (string-not-empty model-name) 282 (if (string-not-empty controller-name) 283 (rails-script:run-generate "scaffold" model-name controller-name actions) 284 (rails-script:run-generate "scaffold" model-name)))) 285 286 (rails-script:gen-generate-function "model" rails-core:models-ancestors) 287 (rails-script:gen-generate-function "migration") 288 (rails-script:gen-generate-function "plugin") 289 (rails-script:gen-generate-function "mailer") 290 (rails-script:gen-generate-function "observer") 291 (rails-script:gen-generate-function "resource") 292 293 ;;;;;;;;;; Rails create project ;;;;;;;;;; 294 295 (defun rails-script:create-project (dir) 296 "Create a new project in a directory named DIR." 297 (interactive "FNew Rails project directory: ") 298 (make-directory dir t) 299 (let ((default-directory (concat (expand-file-name dir) "/"))) 300 (flet ((rails-project:root () default-directory)) 301 (rails-script:run "rails" (list "--skip" (rails-project:root)))))) 302 303 ;;;;;;;;;; Shells ;;;;;;;;;; 304 305 (defun rails-script:run-interactive (name script) 306 "Run an interactive shell with SCRIPT in a buffer named 307 *rails-<project-name>-<name>*." 308 (rails-project:with-root 309 (root) 310 (run-ruby-in-buffer (rails-core:file script) 311 (format "rails-%s-%s" (rails-project:name) name)) 312 (rails-minor-mode t))) 313 314 (defun rails-script:console () 315 "Run script/console." 316 (interactive) 317 (rails-script:run-interactive "console" "script/console")) 318 319 (defun rails-script:breakpointer () 320 "Run script/breakpointer." 321 (interactive) 322 (rails-script:run-interactive "breakpointer" "script/breakpointer")) 323 324 (provide 'rails-scripts)