/ emacs.d / rails / rails-core.el
rails-core.el
  1  ;;; rails-core.el --- core helper functions and macros for emacs-rails
  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-core.el $
 10  ;; $Id: rails-core.el 186 2007-04-20 15:34:51Z 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 'rails-lib))
 30  
 31  (defvar rails-core:class-dirs
 32    '("app/controllers"
 33      "app/views"
 34      "app/models"
 35      "app/helpers"
 36      "test/unit"
 37      "test/functional"
 38      "test/fixtures")
 39    "Directories with Rails classes")
 40  
 41  (defun rails-core:class-by-file (filename)
 42    "Return the class associated with FILENAME.
 43     <rails-root>/(app/models|app/controllers|app/helpers|test/unit|test/functional)/foo/bar_baz
 44                  --> Foo::BarBaz"
 45    (let* ((case-fold-search nil)
 46           (path (replace-regexp-in-string
 47                  (format
 48                   "\\(.*\\(%s\\)/\\)?\\([^\.]+\\)\\(.*\\)?"
 49                   (strings-join "\\|" rails-core:class-dirs)) "\\3" filename))
 50           (path (replace-regexp-in-string "/" "  " path))
 51           (path (replace-regexp-in-string "_" " " path)))
 52      (replace-regexp-in-string
 53       " " ""
 54       (replace-regexp-in-string
 55        "  " "::"
 56        (if (string-match "^ *\\([0-9]+ *\\)?[A-Z]" path)
 57            path
 58          (capitalize path))))))
 59  
 60  (defun rails-core:file-by-class (classname &optional do-not-append-ext)
 61    "Return the filename associated with CLASSNAME.
 62  If the optional parameter DO-NOT-APPEND-EXT is set this function
 63  will not append \".rb\" to result."
 64    (let* ((case-fold-search nil)
 65           (path (replace-regexp-in-string "::" "/" classname))
 66           (path (replace-regexp-in-string "\\([A-Z]+\\)\\([A-Z][a-z]\\)" "\\1_\\2" path))
 67           (path (replace-regexp-in-string "\\([a-z\\d]\\)\\([A-Z]\\)" "\\1_\\2" path)))
 68      (concat (downcase path)
 69              (unless do-not-append-ext ".rb"))))
 70  
 71  ;;;;;;;;;; Files ;;;;;;;;;;
 72  
 73  (defun rails-core:file (file-name)
 74    "Return the full path for FILE-NAME in a Rails directory."
 75    (when file-name
 76      (if (file-name-absolute-p file-name)
 77          file-name
 78        (rails-project:with-root
 79         (root)
 80         (concat root file-name)))))
 81  
 82  (defun rails-core:quoted-file (file-name)
 83    "Return the quoted full path for FILE-NAME in a Rails directory."
 84    (concat "\"" (rails-core:file file-name) "\""))
 85  
 86  (defun rails-core:find-file (file-name)
 87    "Open the file named FILE_NAME in a Rails directory."
 88    (when-bind (file (rails-core:file file-name))
 89         (find-file file)))
 90  
 91  (defun rails-core:find-file-if-exist (file-name)
 92    "Open the file named FILE-NAME in a Rails directory only if the file exists."
 93    (let ((file-name (rails-core:file file-name)))
 94      (when (file-exists-p file-name)
 95        (find-file file-name))))
 96  
 97  (defun rails-core:find-or-ask-to-create (question file)
 98    "Open the file named FILE in a Rails directory if it exists. If
 99  it does not exist, ask to create it using QUESTION as a prompt."
100    (find-or-ask-to-create question (rails-core:file file)))
101  
102  ;; Funtions, that retrun Rails objects full pathes
103  
104  (defun rails-core:model-file (model-name)
105    "Return the model file from the model name."
106    (when model-name
107      (concat "app/models/" (rails-core:file-by-class model-name))))
108  
109  (defun rails-core:model-exist-p (model-name)
110    "Return t if controller CONTROLLER-NAME exists."
111    (when model-name
112      (and (file-exists-p
113            (rails-core:file
114             (rails-core:model-file model-name)))
115           (not (rails-core:observer-p model-name))
116           (not (rails-core:mailer-p model-name)))))
117  
118  (defun rails-core:controller-file (controller-name)
119    "Return the path to the controller CONTROLLER-NAME."
120    (when controller-name
121      (concat "app/controllers/"
122              (rails-core:file-by-class
123               (rails-core:short-controller-name controller-name) t)
124              (unless (string-equal controller-name "Application") "_controller")
125              ".rb")))
126  
127  (defun rails-core:controller-exist-p (controller-name)
128    "Return t if controller CONTROLLER-NAME exist."
129    (when controller-name
130      (file-exists-p
131       (rails-core:file
132        (rails-core:controller-file controller-name)))))
133  
134  (defun rails-core:controller-file-by-model (model)
135    (when model
136      (let ((controller (pluralize-string model)))
137        (when (rails-core:controller-exist-p controller)
138          (rails-core:controller-file controller)))))
139  
140  (defun rails-core:observer-file (observer-name)
141    "Return the path to the observer OBSERVER-NAME."
142    (when observer-name
143      (rails-core:model-file (concat observer-name "Observer"))))
144  
145  (defun rails-core:mailer-file (mailer)
146    (when (and mailer
147               (rails-core:mailer-p mailer))
148      (rails-core:model-file mailer)))
149  
150  (defun rails-core:mailer-exist-p (mailer)
151    (when mailer
152      (file-exists-p (rails-core:file (rails-core:mailer-file mailer)))))
153  
154  (defun rails-core:migration-file (migration-name)
155    "Return the model file from the MIGRATION-NAME."
156    (when migration-name
157      (let ((dir "db/migrate/")
158            (name (replace-regexp-in-string
159                   " " "_"
160                   (rails-core:file-by-class migration-name))))
161        (when (string-match "^[^0-9]+[^_]" name) ; try search when the name without migration number
162          (let ((files (directory-files (rails-core:file dir)
163                                        nil
164                                        (concat "[0-9]+_" name "$"))))
165            (setq name (if files
166                           (car files)
167                         nil))))
168        (when name
169          (concat dir name)))))
170  
171  (defun rails-core:migration-file-by-model (model)
172    (when model
173      (rails-core:migration-file
174       (concat "Create" (rails-core:class-by-file (pluralize-string model))))))
175  
176  (defun rails-core:model-by-migration-filename (migration-filename)
177    (when migration-filename
178      (let ((model-name (singularize-string
179                         (string=~ "[0-9]+_create_\\(\\w+\\)\.rb" (buffer-name) $1))))
180        (when (and model-name
181                   (rails-core:model-exist-p model-name))
182          model-name))))
183  
184  (defun rails-core:plugin-file (plugin file)
185    "Return the path to the FILE in Rails PLUGIN."
186    (concat "vendor/plugins/" plugin "/" file))
187  
188  (defun rails-core:layout-file (layout)
189    "Return the path to the layout file named LAYOUT."
190    (let ((its rails-templates-list)
191          filename)
192      (while (and (car its)
193                  (not filename))
194        (when (file-exists-p (format "%sapp/views/layouts/%s.%s" (rails-project:root) layout (car its)))
195          (setq filename (format "app/views/layouts/%s.%s" layout (car its))))
196        (setq its (cdr its)))
197      filename))
198  
199  (defun rails-core:js-file (js)
200    "Return the path to the JavaScript file named JS."
201    (concat "public/javascripts/" js ".js"))
202  
203  (defun rails-core:partial-name (name)
204    "Return the file name of partial NAME."
205    (if (string-match "/" name)
206        (concat "app/views/"
207          (replace-regexp-in-string "\\([^/]*\\)$" "_\\1.rhtml" name))
208      (concat (rails-core:views-dir (rails-core:current-controller))
209        "_" name ".rhtml")))
210  
211  (defun rails-core:view-name (name)
212    "Return the file name of view NAME."
213    (concat (rails-core:views-dir (rails-core:current-controller))
214            name ".rhtml")) ;; BUG: will fix it
215  
216  (defun rails-core:helper-file (controller)
217    "Return the helper file name for the controller named
218  CONTROLLER."
219    (if (string= "Test/TestHelper" controller)
220        (rails-core:file (rails-core:file-by-class "Test/TestHelper"))
221      (when controller
222        (format "app/helpers/%s_helper.rb"
223                (replace-regexp-in-string "_controller" ""
224                                          (rails-core:file-by-class controller t))))))
225  
226  (defun rails-core:functional-test-file (controller)
227    "Return the functional test file name for the controller named
228  CONTROLLER."
229    (when controller
230      (format "test/functional/%s_test.rb"
231              (rails-core:file-by-class (rails-core:long-controller-name controller) t))))
232  
233  (defun rails-core:unit-test-file (model)
234    "Return the unit test file name for the model named MODEL."
235    (when model
236      (format "test/unit/%s_test.rb" (rails-core:file-by-class model t))))
237  
238  (defun rails-core:unit-test-exist-p (model)
239    "Return the unit test file name for the model named MODEL."
240    (let ((test (rails-core:unit-test-file model)))
241      (when test
242        (file-exists-p (rails-core:file test)))))
243  
244  (defun rails-core:fixture-file (model)
245    "Return the fixtures file name for the model named MODEL."
246    (when model
247      (format "test/fixtures/%s.yml" (pluralize-string (rails-core:file-by-class model t)))))
248  
249  (defun rails-core:fixture-exist-p (model)
250    (when model
251      (file-exists-p
252       (rails-core:file (rails-core:fixture-file model)))))
253  
254  (defun rails-core:views-dir (controller)
255    "Return the view directory name for the controller named CONTROLLER."
256    (format "app/views/%s/" (replace-regexp-in-string "_controller" "" (rails-core:file-by-class controller t))))
257  
258  (defun rails-core:stylesheet-name (name)
259    "Return the file name of the stylesheet named NAME."
260    (concat "public/stylesheets/" name ".css"))
261  
262  (defun rails-core:controller-name (controller-file)
263    "Return the class name of the controller named CONTROLLER.
264     Bar in Foo dir -> Foo::Bar"
265    (rails-core:class-by-file
266     (if (eq (elt controller-file 0) 47) ;;; 47 == '/'
267         (subseq controller-file 1)
268       (let ((current-controller (rails-core:current-controller)))
269         (if (string-match ":" current-controller)
270       (concat (replace-regexp-in-string "[^:]*$" "" current-controller)
271         controller-file)
272     controller-file)))))
273  
274  (defun rails-core:short-controller-name (controller)
275    "Convert FooController -> Foo."
276    (remove-postfix  controller "Controller" ))
277  
278  (defun rails-core:long-controller-name (controller)
279    "Convert Foo/FooController -> FooController."
280    (if  (string-match "Controller$" controller)
281        controller
282      (concat controller "Controller")))
283  
284  ;;;;;;;;;; Functions that return collection of Rails objects  ;;;;;;;;;;
285  (defun rails-core:observer-p (name)
286    (when name
287      (if (string-match "\\(Observer\\|_observer\\(\\.rb\\)?\\)$" name)
288          t nil)))
289  
290  (defun rails-core:mailer-p (name)
291    (when name
292      (if (string-match "\\(Mailer\\|Notifier\\|_mailer\\|_notifier\\(\\.rb\\)?\\)$" name)
293          t nil)))
294  
295  (defun rails-core:controllers (&optional cut-contoller-suffix)
296    "Return a list of Rails controllers. Remove the '_controller'
297  suffix if CUT-CONTOLLER-SUFFIX is non nil."
298    (mapcar
299     #'(lambda (controller)
300         (rails-core:class-by-file
301          (if cut-contoller-suffix
302              (replace-regexp-in-string "_controller\\." "." controller)
303            controller)))
304     (delete-if-not
305      #'(lambda (controller)
306          (string-match "\\(application\\|[a-z0-9_]+_controller\\)\\.rb$"
307                        controller))
308      (find-recursive-files "\\.rb$" (rails-core:file "app/controllers/")))))
309  
310  (defun rails-core:functional-tests ()
311    "Return a list of Rails functional tests."
312    (mapcar
313     #'(lambda(it)
314         (remove-postfix (rails-core:class-by-file it)
315                         "ControllerTest"))
316     (find-recursive-files "\\.rb$" (rails-core:file "test/functional/"))))
317  
318  (defun rails-core:models ()
319    "Return a list of Rails models."
320    (mapcar
321     #'rails-core:class-by-file
322     (delete-if
323      #'(lambda (file) (or (rails-core:observer-p file)
324                           (rails-core:mailer-p file)))
325      (find-recursive-files "\\.rb$" (rails-core:file "app/models/")))))
326  
327  (defun rails-core:unit-tests ()
328    "Return a list of Rails functional tests."
329    (mapcar
330     #'(lambda(it)
331         (remove-postfix (rails-core:class-by-file it)
332                         "Test"))
333     (find-recursive-files "\\.rb$" (rails-core:file "test/unit/"))))
334  
335  (defun rails-core:observers ()
336    "Return a list of Rails observers."
337    (mapcar
338     #'(lambda (observer) (replace-regexp-in-string "Observer$" "" observer))
339     (mapcar
340      #'rails-core:class-by-file
341      (find-recursive-files "\\(_observer\\)\\.rb$" (rails-core:file "app/models/")))))
342  
343  (defun rails-core:mailers ()
344    "Return a list of Rails mailers."
345    (mapcar
346     #'rails-core:class-by-file
347     (find-recursive-files "\\(_mailer\\|_notifier\\)\\.rb$" (rails-core:file "app/models/"))))
348  
349  (defun rails-core:helpers ()
350    "Return a list of Rails helpers."
351    (append
352     (mapcar
353      #'(lambda (helper) (replace-regexp-in-string "Helper$" "" helper))
354      (mapcar
355       #'rails-core:class-by-file
356       (find-recursive-files "_helper\\.rb$" (rails-core:file "app/helpers/"))))
357     (list "Test/TestHelper")))
358  
359  (defun rails-core:migrations (&optional strip-numbers)
360    "Return a list of Rails migrations."
361    (let (migrations)
362      (setq
363       migrations
364       (reverse
365        (mapcar
366         #'(lambda (migration)
367             (replace-regexp-in-string "^\\([0-9]+\\)" "\\1 " migration))
368         (mapcar
369          #'rails-core:class-by-file
370          (find-recursive-files "^[0-9]+_.*\\.rb$" (rails-core:file "db/migrate/"))))))
371      (if strip-numbers
372          (mapcar #'(lambda(i) (car (last (split-string i " "))))
373                  migrations)
374        migrations)))
375  
376  (defun rails-core:migration-versions (&optional with-zero)
377    "Return a list of migtaion versions as the list of strings. If
378  second argument WITH-ZERO is present, append the \"000\" version
379  of migration."
380    (let ((ver (mapcar
381                #'(lambda(it) (car (split-string it " ")))
382                (rails-core:migrations))))
383      (if with-zero
384          (append ver '("000"))
385        ver)))
386  
387  (defun rails-core:plugins ()
388    "Return a list of Rails plugins."
389    (mapcar
390     #'file-name-nondirectory
391     (delete-if-not
392      #'file-directory-p
393      (directory-files (rails-core:file "vendor/plugins") t "^[^\\.]"))))
394  
395  (defun rails-core:plugin-files (plugin)
396    "Return a list of files in specific Rails plugin."
397    (find-recursive-files  "^[^.]" (rails-core:file (concat "vendor/plugins/" plugin))))
398  
399  (defun rails-core:layouts ()
400    "Return a list of Rails layouts."
401    (mapcar
402     #'(lambda (l)
403         (replace-regexp-in-string "\\.[^.]+$" "" l))
404     (find-recursive-files (rails-core:regex-for-match-view) (rails-core:file "app/views/layouts"))))
405  
406  (defun rails-core:fixtures ()
407    "Return a list of Rails fixtures."
408    (mapcar
409     #'(lambda (l)
410         (replace-regexp-in-string "\\.[^.]+$" "" l))
411     (find-recursive-files "\\.yml$" (rails-core:file "test/fixtures/"))))
412  
413  (defun rails-core:regex-for-match-view ()
414    "Return a regex to match Rails view templates.
415  The file extensions used for views are defined in `rails-templates-list'."
416    (format "\\.\\(%s\\)$" (strings-join "\\|" rails-templates-list)))
417  
418  (defun rails-core:get-view-files (controller-class &optional action)
419    "Retun a list containing the view file for CONTROLLER-CLASS#ACTION.
420  If the action is nil, return all views for the controller."
421      (rails-project:with-root
422       (root)
423       (directory-files
424        (rails-core:file
425         (rails-core:views-dir
426          (rails-core:short-controller-name controller-class))) t
427          (if action
428              (concat "^" action (rails-core:regex-for-match-view))
429            (rails-core:regex-for-match-view)))))
430  
431  (defun rails-core:extract-ancestors (classes)
432    "Return the parent classes from a list of classes named CLASSES."
433    (delete ""
434     (uniq-list
435     (mapcar (lambda (class)
436         (replace-regexp-in-string
437          "::[^:]*$" "::"
438          (replace-regexp-in-string "^[^:]*$" "" class)))
439       classes))))
440  
441  (defun rails-core:models-ancestors ()
442    "Return the parent classes of models."
443    (rails-core:extract-ancestors (rails-core:models)))
444  
445  (defun rails-core:controllers-ancestors ()
446    "Return the parent classes of controllers."
447    (rails-core:extract-ancestors (rails-core:controllers)))
448  
449  ;;;;;;;;;; Getting Controllers/Model/Action from current buffer ;;;;;;;;;;
450  
451  (defun rails-core:current-controller ()
452    "Return the current Rails controller."
453    (let* ((file-class (rails-core:class-by-file (buffer-file-name))))
454      (unless (rails-core:mailer-p file-class)
455        (case (rails-core:buffer-type)
456          (:controller (rails-core:short-controller-name file-class))
457          (:view (rails-core:class-by-file
458                  (directory-file-name (directory-of-file (buffer-file-name)))))
459          (:helper (remove-postfix file-class "Helper"))
460          (:functional-test (remove-postfix file-class "ControllerTest"))))))
461  
462  (defun rails-core:current-model ()
463    "Return the current Rails model."
464    (let* ((file-class (rails-core:class-by-file (buffer-file-name))))
465      (unless (rails-core:mailer-p file-class)
466        (case (rails-core:buffer-type)
467          (:migration (rails-core:model-by-migration-filename (buffer-name)))
468          (:model file-class)
469          (:unit-test (remove-postfix file-class "Test"))
470          (:fixture (singularize-string file-class))))))
471  
472  (defun rails-core:current-mailer ()
473    "Return the current Rails Mailer, else return nil."
474    (let* ((file-class (rails-core:class-by-file (buffer-file-name)))
475           (test (remove-postfix file-class "Test")))
476      (when (or (rails-core:mailer-p file-class)
477                (rails-core:mailer-p test))
478        (case (rails-core:buffer-type)
479          (:mailer    file-class)
480          (:unit-test test)
481          (:view (rails-core:class-by-file
482                  (directory-file-name (directory-of-file (buffer-file-name)))))))))
483  
484  (defun rails-core:current-action ()
485    "Return the current action in the current Rails controller."
486    (case (rails-core:buffer-type)
487      (:controller (rails-core:current-method-name))
488      (:mailer (rails-core:current-method-name))
489      (:view (string-match "/\\([a-z0-9_]+\\)\.[a-z]+$" (buffer-file-name))
490             (match-string 1 (buffer-file-name)))))
491  
492  (defun rails-core:current-helper ()
493    "Return the current helper"
494    (rails-core:current-controller))
495  
496  (defun rails-core:current-plugin ()
497    "Return the current plugin name."
498    (let ((name (buffer-file-name)))
499      (when (string-match "vendor\\/plugins\\/\\([^\\/]+\\)" name)
500        (match-string 1 name))))
501  
502  (defun rails-core:current-method-name ()
503    (save-excursion
504      (when (search-backward-regexp "^[ ]*def \\([a-z0-9_]+\\)" nil t)
505        (match-string-no-properties 1))))
506  
507  ;;;;;;;;;; Determination of buffer type ;;;;;;;;;;
508  
509  (defun rails-core:buffer-file-match (regexp)
510    "Match the current buffer file name to RAILS_ROOT + REGEXP."
511    (when-bind (file (rails-core:file regexp))
512               (string-match file
513                             (buffer-file-name (current-buffer)))))
514  
515  (defun rails-core:buffer-type ()
516    "Return the type of the current Rails file or nil if the type
517  cannot be determinated."
518    (loop for (type dir func) in rails-directory<-->types
519          when (and (rails-core:buffer-file-match dir)
520                    (if func
521                        (apply func (list (buffer-file-name (current-buffer))))
522                      t))
523          do (return type)))
524  
525  
526  ;;;;;;;;;; Rails minor mode Buttons ;;;;;;;;;;
527  
528  (define-button-type 'rails-button
529    'follow-link t
530    'action #'rails-core:button-action)
531  
532  (defun rails-core:button-action (button)
533    (let* ((file-name (button-get button :rails:file-name))
534           (line-number (button-get button :rails:line-number))
535           (file (rails-core:file file-name)))
536      (when (and file
537                 (file-exists-p file))
538        (find-file-other-window file)
539        (when line-number
540          (goto-line line-number)))))
541  
542  
543  ;;;;;;;;;; Rails minor mode logs ;;;;;;;;;;
544  
545  (defun rails-log-add (message)
546    "Add MESSAGE to the Rails minor mode log in RAILS_ROOT."
547    (rails-project:with-root
548     (root)
549     (append-string-to-file (rails-core:file "log/rails-minor-mode.log")
550                            (format "%s: %s\n"
551                                    (format-time-string "%Y/%m/%d %H:%M:%S") message))))
552  
553  (defun rails-logged-shell-command (command buffer)
554    "Execute a shell command in the buffer and write the results to
555  the Rails minor mode log."
556    (shell-command (format "%s %s" rails-ruby-command command) buffer)
557    (rails-log-add
558     (format "\n%s> %s\n%s" (rails-project:name)
559             command (buffer-string-by-name buffer))))
560  
561  ;;;;;;;;;; Rails menu ;;;;;;;;;;
562  
563  (defun rails-core:menu-separator ()
564    (unless (rails-use-text-menu) 'menu (list "--" "--")))
565  
566  (if (fboundp 'completion-posn-at-point-as-event)
567      (defun rails-core:menu-position ()
568        (completion-posn-at-point-as-event nil nil nil (+ (frame-char-height) 2)))
569    (defun rails-core:menu-position ()
570      (list '(300 50) (get-buffer-window (current-buffer)))))
571  
572  (defun rails-core:menu (menu)
573    "Show a menu."
574    (let ((result
575           (if (rails-use-text-menu)
576               (tmm-prompt menu)
577             (x-popup-menu (rails-core:menu-position)
578                           (rails-core:prepare-menu  menu)))))
579      (if (listp result)
580          (first result)
581        result)))
582  
583  (defvar rails-core:menu-letters-list
584    (let ((res '()))
585      (loop for i from (string-to-char "1") upto (string-to-char "9")
586            do (add-to-list 'res (char-to-string i) t))
587      (loop for i from (string-to-char "a") upto (string-to-char "z")
588            do (add-to-list 'res (char-to-string i) t))
589      res)
590    "List contains 0-9a-z letter")
591  
592  (defun rails-core:prepare-menu (menu)
593    "Append a prefix to each label of menu-item from MENU."
594    (let ((title (car menu))
595          (menu (cdr menu))
596          (result '())
597          (result-line '())
598          (letter 0))
599      (dolist (line menu)
600        (setq result-line '())
601        (dolist (it line)
602          (typecase it
603            (cons
604             (rails-core:menu-separator)
605             (if (and (string= (car (rails-core:menu-separator)) (car it))
606                      (string= (cadr (rails-core:menu-separator)) (cadr it)))
607                 (add-to-list 'result-line it t)
608               (progn
609                 (add-to-list 'result-line (cons (format "%s) %s" (nth letter rails-core:menu-letters-list) (car it))
610                                                 (cdr it)) t)
611                 (setq letter (+ 1 letter)))))
612            (t
613             (add-to-list 'result-line it t))))
614        (add-to-list 'result result-line t))
615      (cons title result)))
616  
617  ;;;;;;;;;; Misc ;;;;;;;;;;
618  
619  (defun rails-core:erb-block-string ()
620    "Return the contents of the current ERb block."
621    (save-excursion
622      (save-match-data
623        (let ((start (point)))
624          (search-backward-regexp "<%[=]?")
625          (let ((from (match-end 0)))
626            (search-forward "%>")
627            (let ((to (match-beginning 0)))
628              (when (>= to start)
629                (buffer-substring-no-properties from to))))))))
630  
631  (defun rails-core:rhtml-buffer-p ()
632    "Return non nil if the current buffer is rhtml file."
633    (string-match "\\.rhtml$" (buffer-file-name)))
634  
635  (provide 'rails-core)