Initial configuration for Ruby programming

This commit is contained in:
Howard Abrams 2022-09-02 16:14:10 -07:00
parent 06239b0963
commit f25a76f696

226
ha-programming-ruby.org Normal file
View file

@ -0,0 +1,226 @@
#+TITLE: Programming in Ruby
#+AUTHOR: Howard X. Abrams
#+DATE: 2022-09-01
#+FILETAGS: :emacs:
A literate programming file for configuring Emacs to support the Ruby programming language.
#+begin_src emacs-lisp :exports none
;;; ha-programming-ruby --- Ruby configuration. -*- lexical-binding: t; -*-
;;
;; © 2022 Howard X. Abrams
;; Licensed under a Creative Commons Attribution 4.0 International License.
;; See http://creativecommons.org/licenses/by/4.0/
;;
;; Author: Howard X. Abrams <http://gitlab.com/howardabrams>
;; Maintainer: Howard X. Abrams
;; Created: September 1, 2022
;;
;; While obvious, GNU Emacs does not include this file or project.
;;
;; *NB:* Do not edit this file. Instead, edit the original literate file at:
;; /Users/howard.abrams/other/hamacs/ha-programming-ruby.org
;; And tangle the file to recreate this one.
;;
;;; Code:
#+end_src
* Getting Started
Ruby is probably already installed on the system, and if not, we certainly can download it from [[https://www.ruby-lang.org/en/downloads/][ruby-lang.org]], but since I need to juggle different versions for each project, I use [[https://direnv.net/docs/ruby.html][direnv]] and [[https://www.ruby-lang.org/en/documentation/installation/#ruby-install][ruby-install]]:
#+begin_src sh
brew install ruby-install
#+end_src
And then install one or more versions:
#+begin_src sh
ruby-install -U
ruby-install ruby 3
#+end_src
** Per Project
While we /could/ use a large project templating system, I keep it simple. For each project, create the following directory structure:
#+begin_example
├── Gemfile
├── Rakefile
├── lib
│   └── hello_world.rb
└── test
└── hello_world_test.rb
#+end_example
For instance:
#+begin_src sh
mkdir ~/other/ruby-xp # Change me
cd ~/other/ruby-xp
mkdir lib test
#+end_src
Now, do the following steps.
1. Create a =.envrc= file with the Ruby you want to use:
#+begin_src sh :tangle ~/other/ruby-xp/.envrc
use ruby 2.6.8
#+end_src
2. Next, get Bundler:
#+begin_src sh
gem install bundle
#+end_src
3. Create a minimal =Gemfile=:
#+begin_src ruby :tangle ~/other/ruby-xp/Gemfile
source 'https://rubygems.org'
gem 'rake', group: :development
gem 'rubocop', group: :development
gem 'solargraph', group: :development
#+end_src
4. Grab all the dependencies:
#+begin_src sh
bundle install
#+end_src
5. Create a minimal =Rakefile=:
#+begin_src ruby :tangle ~/other/ruby-xp/Rakefile
task default: %w[test]
task :run do
ruby 'lib/hello_world.rb'
end
task :test do
ruby 'test/hello_world_test.rb'
end
#+end_src
6. Create the first program:
#+begin_src ruby :tangle ~/other/ruby-xp/lib/hello_world.rb
class HelloWorld
attr_reader :name
def initialize(name = nil)
@name = name
end
def greeting
if @name
"Hello there, #{@name}"
else
'Hello World!'
end
end
end
puts HelloWorld.new.greeting
#+end_src
7. Create the first test:
#+begin_src ruby :tangle ~/other/ruby-xp/test/hello_world_test.rb
require 'test/unit'
require_relative '../lib/hello_world'
class TestHelloWorld < Test::Unit::TestCase
def test_default
assert_equal 'Hello World!', HelloWorld.new.greeting
end
def test_name
assert_equal 'Hello there, Bob', HelloWorld.new('Bob').greeting
end
end
#+end_src
Or something like that.
* Configuration
Ruby-specific commands are attached to the =ha-ruby-leader=, bound to ~SPC m~:
#+begin_src emacs-lisp
(general-create-definer ha-ruby-leader
:states '(normal visual motion)
:keymaps 'ruby-mode-map
:prefix "SPC m"
:global-prefix "<f17>"
:non-normal-prefix "S-SPC")
#+end_src
While Emacs supplies a Ruby editing environment, well still use =use-package= to grab the latest:
#+begin_src emacs-lisp
(use-package ruby-mode
:after projectile
:mode (rx ".rb" eos)
:mode (rx "Rakefile" eos)
:mode (rx "Gemfile" eos)
:mode (rx "Berksfile" eos)
:mode (rx "Vagrantfile" eos)
:interpreter "ruby"
:init
(setq ruby-indent-level 2
ruby-indent-tabs-mode nil)
:hook (ruby-mode . superword-mode))
#+end_src
** Ruby REPL
I am not sure I can learn a new language without a REPL connected to my editor, and for Ruby, this is [[https://github.com/nonsequitur/inf-ruby][inf-ruby]]:
#+BEGIN_SRC elisp
(use-package inf-ruby
:config
(ha-ruby-leader
"R" '("REPL" . inf-ruby)))
#+END_SRC
** Electric Ruby
The [[https://melpa.org/#/ruby-electric][ruby-electric]] project is a minor mode that aims to add the /extra syntax/ when typing Ruby code.
#+begin_src emacs-lisp
(use-package ruby-electric
:hook (ruby-mode . ruby-electric-mode))
#+end_src
** Testing
The [[https://github.com/r0man/ruby-test-mode][ruby-test-mode]] project aims a running Ruby test from Emacs seemless:
#+begin_src emacs-lisp
(use-package ruby-test-mode
:hook (ruby-mode . ruby-test-mode)
:config
(ha-ruby-leader
"t" '(:ignore t :which-key "test")
"t t" '("test one" . ruby-test-run-at-point)
"t g" '("toggle code/test" . ruby-test-toggle-implementation-and-specification)
"t A" '("test all" . ruby-test-run)
"t a" '("retest" . ruby-test-rerun)))
#+end_src
** Rubocop?
The lint-like style checker of choice for Ruby is [[https://github.com/bbatsov/rubocop][Rubocop]]. The [[https://github.com/bbatsov/rubocop-emacs][rubocop.el]] mode should work with [[https://github.com/flycheck/flycheck][Flycheck]]. First install it with:
#+begin_src sh
gem install rubocop
#+end_src
And then we may or may not need to enable the =rubocop-mode=:
#+BEGIN_SRC elisp :tangle no
(use-package rubocop
:hook (ruby-mode . rubocop-mode))
#+END_SRC
* LSP
Need to install [[https://github.com/castwide/solargraph][Solargraph]] for the LSP server experience:
#+begin_src sh
gem install solargraph
#+end_src
Or add it to your =Gemfile=:
#+begin_src ruby
gem 'solargraph', group: :development
#+end_src
* Technical Artifacts :noexport:
Let's =provide= a name so we can =require= this file:
#+begin_src emacs-lisp :exports none
(provide 'ha-programming-ruby)
;;; ha-programming-ruby.el ends here
#+end_src
#+DESCRIPTION: configuring Emacs to support the Ruby programming language.
#+PROPERTY: header-args:sh :tangle no
#+PROPERTY: header-args:emacs-lisp :tangle yes
#+PROPERTY: header-args :results none :eval no-export :comments no mkdirp yes
#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil date:nil
#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil
#+INFOJS_OPT: view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js