Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
329 lines (281 sloc) 13.8 KB
;;; rails-scripts.el --- emacs-rails integraions with rails script/* scripts
;; Copyright (C) 2006 Dmitry Galinsky <dima dot exe at gmail dot com>
;; Authors: Dmitry Galinsky <dima dot exe at gmail dot com>,
;; Rezikov Peter <crazypit13 (at)>
;; Keywords: ruby rails languages oop
;; $URL$
;; $Id$
;;; License
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License
;; as published by the Free Software Foundation; either version 2
;; of the License, or (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program; if not, write to the Free Software
;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
(require 'inf-ruby)
(require 'ruby-mode))
(defvar rails-script:generators-list
'("controller" "model" "scaffold" "migration" "plugin" "mailer" "observer" "resource"))
(defvar rails-script:destroy-list rails-script:generators-list)
(defvar rails-script:generate-params-list
"Add parameters to script/generate.
For example -s to keep existing files and -c to add new files into svn.")
(defvar rails-script:destroy-params-list
"Add parameters to script/destroy.
For example -c to remove files from svn.")
(defvar rails-script:buffer-name "*ROutput*")
(defvar rails-script:running-script-name nil
"Curently running the script name")
(defvar rails-script:history (list))
(defvar rails-script:history-of-generate (list))
(defvar rails-script:history-of-destroy (list))
;; output-mode
(defconst rails-script:font-lock-ketwords
'("^\\(\(in [^\)]+\)\\)$" 1 font-lock-builtin-face)
'(" \\(rm\\|rmdir\\) " 1 font-lock-warning-face)
'(" \\(missing\\|notempty\\|exists\\) " 1 font-lock-warning-face)
'(" \\(create\\|dependency\\) " 1 font-lock-function-name-face)))
(defconst rails-script:button-regexp
" \\(create\\) + \\([^ ]+\\.\\w+\\)")
(defvar rails-script:output-mode-ret-value nil)
(defvar rails-script:run-after-stop-hook nil)
(defvar rails-script:show-buffer-hook nil)
(defun rails-script:make-buttons (start end len)
(let ((buffer-read-only nil))
(goto-char start)
(while (re-search-forward rails-script:button-regexp end t)
(make-button (match-beginning 2) (match-end 2)
:type 'rails-button
:rails:file-name (match-string 2))))))
(defun rails-script:popup-buffer (&optional do-not-scroll-to-top)
"Popup output buffer."
(unless (buffer-visible-p rails-script:buffer-name)
(display-buffer rails-script:buffer-name t))
(let ((win (get-buffer-window-list rails-script:buffer-name)))
(when win
(unless do-not-scroll-to-top
(mapcar #'(lambda(w) (set-window-point w 0)) win))
(get-buffer-window rails-script:buffer-name))
(run-hooks 'rails-script:show-buffer-hook))))
(defun rails-script:push-first-button ()
(let (file-name)
(with-current-buffer (get-buffer rails-script:buffer-name)
(let ((button (next-button 1)))
(when button
(setq file-name (button-get button :rails:file-name)))))
(when file-name
(rails-core:find-file-if-exist file-name))))
(defun rails-script:toggle-output-window ()
(let ((current (current-buffer))
(buf (get-buffer rails-script:buffer-name)))
(if buf
(if (buffer-visible-p rails-script:buffer-name)
(delete-windows-on buf)
(pop-to-buffer rails-script:buffer-name t t)
(pop-to-buffer current t t)
(get-buffer-window rails-script:buffer-name))
(run-hooks 'rails-script:show-buffer-hook)))
(message "No output window found. Try running a script or a rake task before."))))
(defun rails-script:setup-output-buffer ()
"Setup default variables and values for the output buffer."
(set (make-local-variable 'font-lock-keywords-only) t)
(make-local-variable 'font-lock-defaults)
(set (make-local-variable 'scroll-margin) 0)
(set (make-local-variable 'scroll-preserve-screen-position) nil)
(make-local-variable 'after-change-functions)
(rails-minor-mode t))
(define-derived-mode rails-script:output-mode fundamental-mode "ROutput"
"Major mode to Rails Script Output."
(setq font-lock-defaults '((rails-script:font-lock-ketwords) nil t))
(setq buffer-read-only t)
(rails-script:make-buttons (point-min) (point-max) (point-max))
(add-hook 'rails-script:run-after-stop-hook 'rails-script:popup-buffer t t)
(add-hook 'rails-script:run-after-stop-hook 'rails-script:push-first-button t t)
(add-hook 'after-change-functions 'rails-script:make-buttons nil t)
(run-hooks 'rails-script:output-mode-hook))
(defun rails-script:running-p ()
(get-buffer-process rails-script:buffer-name))
(defun rails-script:sentinel-proc (proc msg)
(let* ((name rails-script:running-script-name)
(ret-val (process-exit-status proc))
(buf (get-buffer rails-script:buffer-name))
(ret-message (if (zerop ret-val) "successful" "failure")))
(with-current-buffer buf
(set (make-local-variable 'rails-script:output-mode-ret-value) ret-val))
(when (memq (process-status proc) '(exit signal))
(setq rails-script:running-script-name nil
msg (format "%s was stopped (%s)." name ret-message)))
(message (replace-regexp-in-string "\n" "" msg))
(with-current-buffer buf
(run-hooks 'rails-script:run-after-stop-hook))))
(defun rails-script:run (command parameters &optional buffer-major-mode)
"Run a Rails script COMMAND with PARAMETERS with
BUFFER-MAJOR-MODE and process-sentinel SENTINEL."
(unless (listp parameters)
(error "rails-script:run PARAMETERS must be the list"))
(let ((proc (rails-script:running-p)))
(if proc
(message "Only one instance rails-script allowed")
(let* ((default-directory root)
(proc (rails-cmd-proxy:start-process rails-script:buffer-name
(strings-join " " parameters))))
(with-current-buffer (get-buffer rails-script:buffer-name)
(let ((buffer-read-only nil)
(win (get-buffer-window-list rails-script:buffer-name)))
(kill-region (point-min) (point-max)))
(if buffer-major-mode
(apply buffer-major-mode (list))
(add-hook 'after-change-functions 'rails-cmd-proxy:convert-buffer-from-remote nil t))
(set-process-coding-system proc 'utf-8-dos 'utf-8-dos)
(set-process-sentinel proc 'rails-script:sentinel-proc)
(setq rails-script:running-script-name
(if (= 1 (length parameters))
(format "%s %s" command (first parameters))
(format "%s %s" (first parameters) (first (cdr parameters)))))
(message "Starting %s." rails-script:running-script-name))))))
;;;;;;;;;; Destroy stuff ;;;;;;;;;;
(defun rails-script:run-destroy (what &rest parameters)
"Run the destroy script using WHAT and PARAMETERS."
(rails-script:run rails-ruby-command
(append (list (format "script/destroy %s" what))
(defun rails-script:destroy (what)
"Run destroy WHAT"
(interactive (rails-completing-read "What destroy" rails-script:destroy-list
'rails-script:history-of-destroy nil))
(let ((name (intern (concat "rails-script:destroy-"
(replace-regexp-in-string "_" "-" what)))))
(when (fboundp name)
(call-interactively name))))
(defmacro rails-script:gen-destroy-function (name &optional completion completion-arg)
(let ((func (intern (format "rails-script:destroy-%s" name)))
(param (intern (concat name "-name"))))
`(defun ,func (&optional ,param)
(list (completing-read ,(concat "Destroy "
(replace-regexp-in-string "[^a-z0-9]" " " name)
": ")
,(if completion
,(if completion-arg
`(,completion ,completion-arg)
(when (string-not-empty ,param)
(rails-script:run-destroy ,(replace-regexp-in-string "-" "_" name) ,param)))))
(rails-script:gen-destroy-function "controller" rails-core:controllers t)
(rails-script:gen-destroy-function "model" rails-core:models)
(rails-script:gen-destroy-function "scaffold")
(rails-script:gen-destroy-function "migration" rails-core:migrations t)
(rails-script:gen-destroy-function "mailer" rails-core:mailers)
(rails-script:gen-destroy-function "plugin" rails-core:plugins)
(rails-script:gen-destroy-function "observer" rails-core:observers)
(rails-script:gen-destroy-function "resource")
;;;;;;;;;; Generators stuff ;;;;;;;;;;
(defun rails-script:run-generate (what &rest parameters)
"Run the generate script using WHAT and PARAMETERS."
(rails-script:run rails-ruby-command
(append (list (format "script/generate %s" what))
(defun rails-script:generate (what)
"Run generate WHAT"
(interactive (rails-completing-read "What generate" rails-script:generators-list
'rails-script:history-of-generate nil))
(let ((name (intern (concat "rails-script:generate-"
(replace-regexp-in-string "_" "-" what)))))
(when (fboundp name)
(call-interactively name))))
(defmacro rails-script:gen-generate-function (name &optional completion completion-arg)
(let ((func (intern (format "rails-script:generate-%s" name)))
(param (intern (concat name "-name"))))
`(defun ,func (&optional ,param)
(list (completing-read ,(concat "Generate "
(replace-regexp-in-string "[^a-z0-9]" " " name)
": ")
,(if completion
,(if completion-arg
`(,completion ,completion-arg)
(when (string-not-empty ,param)
(rails-script:run-generate ,(replace-regexp-in-string "-" "_" name) ,param)))))
(defun rails-script:generate-controller (&optional controller-name actions)
"Generate a controller and open the controller file."
(interactive (list
(completing-read "Controller name (use autocomplete) : "
(list->alist (rails-core:controllers-ancestors)))
(read-string "Actions (or return to skip): ")))
(when (string-not-empty controller-name)
(rails-script:run-generate "controller" controller-name actions)))
(defun rails-script:generate-scaffold (&optional model-name controller-name actions)
"Generate a scaffold and open the controller file."
"MModel name: \nMController (or return to skip): \nMActions (or return to skip): ")
(when (string-not-empty model-name)
(if (string-not-empty controller-name)
(rails-script:run-generate "scaffold" model-name controller-name actions)
(rails-script:run-generate "scaffold" model-name))))
(rails-script:gen-generate-function "model" rails-core:models-ancestors)
(rails-script:gen-generate-function "migration")
(rails-script:gen-generate-function "plugin")
(rails-script:gen-generate-function "mailer")
(rails-script:gen-generate-function "observer")
(rails-script:gen-generate-function "resource")
;;;;;;;;;; Rails create project ;;;;;;;;;;
(defun rails-script:create-project (dir)
"Create a new project in a directory named DIR."
(interactive "FNew Rails project directory: ")
(make-directory dir t)
(let ((default-directory (concat (expand-file-name dir) "/")))
(flet ((rails-project:root () default-directory))
(rails-script:run "rails" (list "--skip" (rails-project:root))))))
;;;;;;;;;; Shells ;;;;;;;;;;
(defun rails-script:run-interactive (name script &optional params)
"Run an interactive shell with SCRIPT in a buffer named
(let ((buffer-name (format "rails-%s-%s" (rails-project:name) name))
(script (rails-core:file script)))
(run-ruby-in-buffer buffer-name
(setq ruby-buffer buffer-name))
(rails-minor-mode t)))
(defun rails-script:console ()
"Run script/console."
(rails-script:run-interactive (format "console at (%s)" rails-default-environment)
(defun rails-script:breakpointer ()
"Run script/breakpointer."
(rails-script:run-interactive "breakpointer" "script/breakpointer"))
(provide 'rails-scripts)