From f25a76f696743ee1abe2d4b07b70a4adacb5f77a Mon Sep 17 00:00:00 2001 From: Howard Abrams Date: Fri, 2 Sep 2022 16:14:10 -0700 Subject: [PATCH] Initial configuration for Ruby programming --- ha-programming-ruby.org | 226 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 ha-programming-ruby.org diff --git a/ha-programming-ruby.org b/ha-programming-ruby.org new file mode 100644 index 0000000..99fb32b --- /dev/null +++ b/ha-programming-ruby.org @@ -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 + ;; 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 "" + :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 +* 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