da0919d558
At least to read feature files.
233 lines
7.1 KiB
Org Mode
233 lines
7.1 KiB
Org Mode
#+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, we’ll 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
|
||
* Auxiliary Support
|
||
** Cucumber
|
||
Seems that to understand and edit Cucumber /feature/ definitions, you need [[https://github.com/michaelklishin/cucumber.el][cucumber.el]]:
|
||
#+begin_src emacs-lisp
|
||
(use-package feature-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
|