Table of Contents
1. Ruby Development Configuration
Configuration for Ruby and Ruby on Rails development.
1.1. Overview
This module provides comprehensive Ruby development support:
- Language Support: ruby-mode with enhanced features
- Rails Integration: Full Ruby on Rails support
- REPL: inf-ruby for interactive development
- Testing: RSpec and minitest integration
- Code Quality: RuboCop integration
- Package Management: Bundler support
1.2. Key Features
1.2.1. Ruby Mode
- Syntax highlighting
- Intelligent indentation
- Code navigation
- Symbol navigation
1.2.2. Ruby on Rails
- Rails-specific features
- Generator integration
- Migration helpers
- View/controller switching
1.2.3. Development Tools
| Tool | Purpose |
|---|---|
ruby-mode |
Core Ruby language support |
inf-ruby |
Interactive Ruby REPL |
rspec-mode |
RSpec test runner |
rubocop |
Ruby style checker |
bundler |
Gem dependency management |
1.2.4. Workflow Integration
- Project detection (Rails vs. pure Ruby)
- Bundler gem management
- Test runner integration
- Code formatting and linting
1.3. Ruby
;;; prog-ruby-conf.el --- Ruby development configuration -*- lexical-binding: t; -*- ;;; Commentary: ;;; Configuration for Ruby and Ruby on Rails development including LSP support, ;;; testing, formatting, REPL, and Rails-specific tools. ;;; Code: ;; Ruby mode settings (use-package ruby-mode :ensure nil :mode (("\\.rb\\'" . ruby-mode) ("Rakefile\\'" . ruby-mode) ("Gemfile\\'" . ruby-mode) ("Berksfile\\'" . ruby-mode) ("Vagrantfile\\'" . ruby-mode) ("\\.rake\\'" . ruby-mode) ("\\.gemspec\\'" . ruby-mode) ("\\.ru\\'" . ruby-mode) ("Guardfile\\'" . ruby-mode) ("Capfile\\'" . ruby-mode) ("\\.cap\\'" . ruby-mode) ("\\.thor\\'" . ruby-mode) ("\\.rabl\\'" . ruby-mode) ("Thorfile\\'" . ruby-mode) ("\\.jbuilder\\'" . ruby-mode) ("Podfile\\'" . ruby-mode) ("\\.podspec\\'" . ruby-mode) ("Puppetfile\\'" . ruby-mode) ("\\.arb\\'" . ruby-mode)) :interpreter "ruby" :custom (ruby-indent-level 2) (ruby-indent-tabs-mode nil) (ruby-insert-encoding-magic-comment nil) ; Don't auto-insert encoding comment (ruby-align-chained-calls nil) (ruby-deep-indent-paren nil) :config ;; Remove trailing whitespace on save (add-hook 'ruby-mode-hook (lambda () (add-hook 'before-save-hook #'delete-trailing-whitespace nil t))) ;; Enhance Ruby mode with additional features (add-hook 'ruby-mode-hook (lambda () (setq-local comment-auto-fill-only-comments t) (auto-fill-mode 1)))) ;; inf-ruby: Ruby REPL integration (use-package inf-ruby :after ruby-mode :hook ((ruby-mode . inf-ruby-minor-mode) (compilation-filter . inf-ruby-auto-enter)) :bind (:map ruby-mode-map ("C-c C-s" . inf-ruby) ("C-c C-z" . inf-ruby-switch-from-compilation) ("C-c C-c" . fu-ruby-send-region-or-buffer) ("C-c C-d" . fu-ruby-send-definition) ("C-c C-l" . ruby-load-file) ("C-c C-r" . ruby-send-region)) :config ;; Automatically start REPL when evaluating code (defun fu-ruby-send-region-or-buffer () "Send the current region to Ruby REPL, or whole buffer if no region is active." (interactive) (unless (get-buffer-process inf-ruby-buffer) (inf-ruby)) (if (use-region-p) (ruby-send-region (region-beginning) (region-end)) (ruby-send-buffer))) (defun fu-ruby-send-definition () "Send the current definition to Ruby REPL." (interactive) (unless (get-buffer-process inf-ruby-buffer) (inf-ruby)) (save-excursion (ruby-end-of-defun) (let ((end (point))) (ruby-beginning-of-defun) (ruby-send-region (point) end))))) ;; RuboCop: Linting and formatting (use-package rubocop :defer t :hook (ruby-mode . rubocop-mode) :bind (:map rubocop-mode-map ("C-c r a" . rubocop-autocorrect-current-file) ("C-c r A" . rubocop-autocorrect-project) ("C-c r f" . rubocop-check-current-file) ("C-c r p" . rubocop-check-project)) :custom (rubocop-autocorrect-on-save nil) ; Don't auto-correct on save by default (rubocop-prefer-system-executable t) :config ;; Auto-format with RuboCop if available (defun fu-ruby-rubocop-autocorrect-on-save () "Auto-correct current file with RuboCop on save if rubocop is available." (when (and (eq major-mode 'ruby-mode) (executable-find "rubocop")) (rubocop-autocorrect-current-file))) ;; Uncomment to enable auto-format on save ;; (add-hook 'after-save-hook #'fu-ruby-rubocop-autocorrect-on-save)) ) ;; RSpec: Testing framework (use-package rspec-mode :after ruby-mode :hook (ruby-mode . rspec-mode) :bind (:map ruby-mode-map ("C-c t t" . rspec-verify) ("C-c t f" . rspec-verify-single) ("C-c t m" . rspec-verify-matching) ("C-c t a" . rspec-verify-all) ("C-c t r" . rspec-rerun) ("C-c t c" . rspec-verify-continue) ("C-c t s" . rspec-toggle-spec-and-target)) :custom (rspec-use-rake-when-possible nil) (rspec-spec-command "rspec") (rspec-use-spring-when-possible t) :config ;; Use bundle exec if Gemfile exists (defun fu-ruby-use-bundler-for-rspec () "Use bundle exec for RSpec if Gemfile exists." (when (locate-dominating-file default-directory "Gemfile") (setq rspec-spec-command "bundle exec rspec"))) (add-hook 'ruby-mode-hook #'fu-ruby-use-bundler-for-rspec)) ;; Bundler: Dependency management ;; Note: bundler package is no longer available on MELPA ;; Using custom functions with compilation-mode instead (defun fu-bundle-install () "Run bundle install in the project root." (interactive) (let ((default-directory (or (locate-dominating-file default-directory "Gemfile") default-directory))) (compile "bundle install"))) (defun fu-bundle-update () "Run bundle update in the project root." (interactive) (let ((default-directory (or (locate-dominating-file default-directory "Gemfile") default-directory))) (compile "bundle update"))) (defun fu-bundle-exec (command) "Run COMMAND via bundle exec in the project root." (interactive "sBundle exec: ") (let ((default-directory (or (locate-dominating-file default-directory "Gemfile") default-directory))) (compile (concat "bundle exec " command)))) ;; Rake: Task runner (use-package rake :after ruby-mode :commands (rake rake-find-task rake-rerun) :bind (:map ruby-mode-map ("C-c k r" . rake) ("C-c k f" . rake-find-task) ("C-c k R" . rake-rerun))) ;; YAML mode for Rails config files (use-package yaml-mode :mode (("\\.yml\\'" . yaml-mode) ("\\.yaml\\'" . yaml-mode) ("config/database.yml" . yaml-mode) ("config/routes.rb" . ruby-mode))) ;; Enhanced Ruby refactoring (use-package ruby-refactor :defer t :hook (ruby-mode . ruby-refactor-mode-launch) :bind (:map ruby-refactor-mode-map ("C-c C-e m" . ruby-refactor-extract-to-method) ("C-c C-e v" . ruby-refactor-extract-local-variable) ("C-c C-e c" . ruby-refactor-extract-constant) ("C-c C-e l" . ruby-refactor-extract-to-let))) ;; Ruby block manipulation ;; Note: ruby-block package is no longer available on MELPA ;; show-paren-mode provides similar functionality for matching blocks ;; YARD documentation (use-package yard-mode :after ruby-mode :hook (ruby-mode . yard-mode) :bind (:map ruby-mode-map ("C-c y" . yard-mode))) ;; Ruby debugging with realgud (use-package realgud :defer t :commands (realgud:byebug realgud:pry)) ;; Chruby for Ruby version management (if you use chruby) (use-package chruby :defer t :commands (chruby-use chruby-use-corresponding) :config ;; Auto-detect Ruby version from .ruby-version (defun fu-ruby-chruby-auto () "Auto-select Ruby version based on .ruby-version file." (when (and (executable-find "chruby-exec") (locate-dominating-file default-directory ".ruby-version")) (chruby-use-corresponding))) (add-hook 'ruby-mode-hook #'fu-ruby-chruby-auto)) ;; RVM support (alternative to chruby) ;; (use-package rvm ;; :defer t ;; :config ;; (rvm-use-default)) ;; rbenv support (alternative to chruby/rvm) ;; (use-package rbenv ;; :defer t ;; :config ;; (global-rbenv-mode)) ;; ERB (Embedded Ruby) mode (use-package web-mode :mode (("\\.erb\\'" . web-mode) ("\\.html\\.erb\\'" . web-mode)) :config (setq web-mode-engines-alist '(("erb" . "\\.html\\.erb\\'")))) ;; Haml support (use-package haml-mode :mode "\\.haml\\'") ;; Slim template support (use-package slim-mode :mode "\\.slim\\'") ;; Rails console in Emacs ;; (defun fu-rails-console () ;; "Start Rails console in inf-ruby." ;; (interactive) ;; (let ((default-directory (or (projectile-rails-root) ;; default-directory))) ;; (if (file-exists-p "bin/rails") ;; (inf-ruby-console-run "bin/rails console" "rails") ;; (inf-ruby-console-run "bundle exec rails console" "rails")))) ;; (defun fu-rails-server () ;; "Start Rails server in async shell." ;; (interactive) ;; (let ((default-directory (or (projectile-rails-root) ;; default-directory))) ;; (async-shell-command ;; (if (file-exists-p "bin/rails") ;; "bin/rails server" ;; "bundle exec rails server") ;; "*rails-server*"))) ;; Keybindings for Rails ;; (with-eval-after-load 'ruby-mode ;; (define-key ruby-mode-map (kbd "C-c R r") 'fu-rails-console) ;; (define-key ruby-mode-map (kbd "C-c R s") 'fu-rails-server)) ;; Ruby hash syntax conversion helpers (defun fu-ruby-toggle-hash-syntax () "Toggle between old and new Ruby hash syntax." (interactive) (save-excursion (let ((bounds (bounds-of-thing-at-point 'symbol))) (when bounds (let* ((start (car bounds)) (end (cdr bounds)) (symbol (buffer-substring-no-properties start end))) (cond ;; Convert :symbol => to symbol: ((looking-back ":\\([a-z_][a-z0-9_]*\\) =>" (line-beginning-position)) (let ((match-start (match-beginning 0))) (delete-region match-start (point)) (insert (match-string 1) ":"))) ;; Convert symbol: to :symbol => ((looking-at "\\([a-z_][a-z0-9_]*\\):") (let ((symbol (match-string 1))) (delete-region (match-beginning 0) (match-end 0)) (insert ":" symbol " =>"))))))))) (with-eval-after-load 'ruby-mode (define-key ruby-mode-map (kbd "C-c h") 'fu-ruby-toggle-hash-syntax)) ;; LSP configuration with Eglot (with-eval-after-load 'eglot ;; Configure ruby-lsp server (preferred) or solargraph (add-to-list 'eglot-server-programs '((ruby-mode ruby-ts-mode) "ruby-lsp")) ;; Alternative: Use solargraph instead ;; (add-to-list 'eglot-server-programs ;; '((ruby-mode ruby-ts-mode) "solargraph" "stdio")) ;; Enable eglot for Ruby modes (add-hook 'ruby-mode-hook #'eglot-ensure) (add-hook 'ruby-ts-mode-hook #'eglot-ensure)) ;; Ruby end-of-line comments alignment (defun fu-ruby-align-end-comments () "Align end-of-line comments in Ruby." (interactive) (align-regexp (region-beginning) (region-end) "\\(\\s-*\\)#" 1 1 t)) (provide 'prog-ruby-conf) ;;; prog-ruby-conf.el ends here