Slide 1

Slide 1 text

Womenjika

Slide 2

Slide 2 text

Writing Ruby Gems Pat Allan - @pat
 https://freelancing-gods.com

Slide 3

Slide 3 text

What is a gem?

Slide 4

Slide 4 text

gem |jem| noun a precious or semiprecious stone, esp. when cut and polished or engraved.

Slide 5

Slide 5 text

gem |jem| noun a packaged code library written in the Ruby programming language.

Slide 6

Slide 6 text

gem |jem| noun a command line executable distributed with the RubyGems package manager.

Slide 7

Slide 7 text

rubygems |ˈro ͞ obējemz| noun a package manager for Ruby libraries.

Slide 8

Slide 8 text

rubygems |ˈro ͞ obējemz| noun a website that stores published versions of Ruby gems.

Slide 9

Slide 9 text

Why write a gem?

Slide 10

Slide 10 text

To re-use code Why write a gem?

Slide 11

Slide 11 text

To re-use code To make code easy to install Why write a gem?

Slide 12

Slide 12 text

To re-use code Why write a gem? To make code easy to install To make code easy to share

Slide 13

Slide 13 text

How do I write a gem?

Slide 14

Slide 14 text

gemspec

Slide 15

Slide 15 text

melb_ruby.gemspec

Slide 16

Slide 16 text

Gem::Specification.new do |spec| spec.name = "melb_ruby" spec.version = "0.0.1" spec.authors = ["Pat Allan"] spec.email = ["[email protected]"] spec.summary = "A gem that does something" spec.homepage = "https://github.com/pat/melb_ruby" spec.license = "MIT" # ... end

Slide 17

Slide 17 text

Gem::Specification.new do |spec| spec.name = "melb_ruby" spec.version = "0.0.1" spec.authors = ["Pat Allan"] spec.email = ["[email protected]"] spec.summary = "A gem that does something" spec.homepage = "https://github.com/pat/melb_ruby" spec.license = "MIT" # ... end

Slide 18

Slide 18 text

Gem::Specification.new do |spec| spec.name = "melb_ruby" spec.version = "0.0.1" spec.authors = ["Pat Allan"] spec.email = ["[email protected]"] spec.summary = "A gem that does something" spec.homepage = "https://github.com/pat/melb_ruby" spec.license = "MIT" # ... end

Slide 19

Slide 19 text

Gem::Specification.new do |spec| spec.name = "melb_ruby" spec.version = "0.0.1" spec.authors = ["Pat Allan"] spec.email = ["[email protected]"] spec.summary = "A gem that does something" spec.homepage = "https://github.com/pat/melb_ruby" spec.license = "MIT" # ... end

Slide 20

Slide 20 text

Gem::Specification.new do |spec| spec.name = "melb_ruby" spec.version = "0.0.1" spec.authors = ["Pat Allan"] spec.email = ["[email protected]"] spec.summary = "A gem that does something" spec.homepage = "https://github.com/pat/melb_ruby" spec.license = "MIT" # ... end

Slide 21

Slide 21 text

Gem::Specification.new do |spec| spec.name = "melb_ruby" spec.version = "0.0.1" spec.authors = ["Pat Allan"] spec.email = ["[email protected]"] spec.summary = "A gem that does something" spec.homepage = "https://github.com/pat/melb_ruby" spec.license = "MIT" # ... end

Slide 22

Slide 22 text

Gem::Specification.new do |spec| spec.name = "melb_ruby" spec.version = "0.0.1" spec.authors = ["Pat Allan"] spec.email = ["[email protected]"] spec.summary = "A gem that does something" spec.homepage = "https://github.com/pat/melb_ruby" spec.license = "MIT" # ... end

Slide 23

Slide 23 text

Where do I put files?

Slide 24

Slide 24 text

melb_ruby/lib # code goes here melb_ruby/exe # executables melb_ruby/bin # development commands melb_ruby/spec # or melb_ruby/test melb_ruby/README.markdown melb_ruby/CHANGELOG.markdown

Slide 25

Slide 25 text

melb_ruby/lib # code goes here melb_ruby/exe # executables melb_ruby/bin # development commands melb_ruby/spec # or melb_ruby/test melb_ruby/README.markdown melb_ruby/CHANGELOG.markdown

Slide 26

Slide 26 text

melb_ruby/lib # code goes here melb_ruby/exe # executables melb_ruby/bin # development commands melb_ruby/spec # or melb_ruby/test melb_ruby/README.markdown melb_ruby/CHANGELOG.markdown

Slide 27

Slide 27 text

melb_ruby/lib # code goes here melb_ruby/exe # executables melb_ruby/bin # development commands melb_ruby/spec # or melb_ruby/test melb_ruby/README.markdown melb_ruby/CHANGELOG.markdown

Slide 28

Slide 28 text

melb_ruby/lib # code goes here melb_ruby/exe # executables melb_ruby/bin # development commands melb_ruby/spec # or melb_ruby/test melb_ruby/README.markdown melb_ruby/CHANGELOG.markdown

Slide 29

Slide 29 text

melb_ruby/lib # code goes here melb_ruby/exe # executables melb_ruby/bin # development commands melb_ruby/spec # or melb_ruby/test melb_ruby/README.markdown melb_ruby/CHANGELOG.markdown

Slide 30

Slide 30 text

# A list of all files in the gem: spec.files = ["lib/melb_ruby.rb"] # This is the default: spec.require_paths = ["lib"] # This is *not* the default, but maybe should be: spec.bindir = "exe" # And then you need to explicitly list each # executable: spec.executables = ["melburnian"]

Slide 31

Slide 31 text

# A list of all files in the gem: spec.files = ["lib/melb_ruby.rb"] # This is the default: spec.require_paths = ["lib"] # This is *not* the default, but maybe should be: spec.bindir = "exe" # And then you need to explicitly list each # executable: spec.executables = ["melburnian"]

Slide 32

Slide 32 text

# A list of all files in the gem: spec.files = ["lib/melb_ruby.rb"] # This is the default: spec.require_paths = ["lib"] # This is *not* the default, but maybe should be: spec.bindir = "exe" # And then you need to explicitly list each # executable: spec.executables = ["melburnian"]

Slide 33

Slide 33 text

# A list of all files in the gem: spec.files = ["lib/melb_ruby.rb"] # This is the default: spec.require_paths = ["lib"] # This is *not* the default, but maybe should be: spec.bindir = "exe" # And then you need to explicitly list each # executable: spec.executables = ["melburnian"]

Slide 34

Slide 34 text

spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end

Slide 35

Slide 35 text

spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }

Slide 36

Slide 36 text

Naming is hard

Slide 37

Slide 37 text

Naming is hard Use underscores between words

Slide 38

Slide 38 text

MelbRuby

Slide 39

Slide 39 text

melb_ruby

Slide 40

Slide 40 text

Naming is hard Use underscores between words Use dashes for namespacing

Slide 41

Slide 41 text

RSpec::Rails

Slide 42

Slide 42 text

rspec-rails

Slide 43

Slide 43 text

MelbRuby::Burgers

Slide 44

Slide 44 text

melb_ruby-burgers

Slide 45

Slide 45 text

File structure should match the gem name Naming is hard Use underscores between words Use dashes for namespacing

Slide 46

Slide 46 text

# contains MelbRuby: lib/melb_ruby.rb # contains MelbRuby::Burgers lib/melb_ruby/burgers.rb # contains MelbRuby::HackNight lib/melb_ruby/hack_night.rb

Slide 47

Slide 47 text

# contains MelbRuby: lib/melb_ruby.rb # contains MelbRuby::Burgers lib/melb_ruby/burgers.rb # contains MelbRuby::HackNight lib/melb_ruby/hack_night.rb

Slide 48

Slide 48 text

# contains MelbRuby: lib/melb_ruby.rb # contains MelbRuby::Burgers lib/melb_ruby/burgers.rb # contains MelbRuby::HackNight lib/melb_ruby/hack_night.rb

Slide 49

Slide 49 text

# contains MelbRuby: lib/melb_ruby.rb # contains MelbRuby::Burgers lib/melb_ruby/burgers.rb # contains MelbRuby::HackNight lib/melb_ruby/hack_night.rb

Slide 50

Slide 50 text

# don't put MelbRuby::Burgers in lib/burgers.rb # because a gem called Burgers # probably has this file too, and # Ruby will get confused.

Slide 51

Slide 51 text

#!/usr/bin/env ruby require "melb_ruby/cli" MelbRuby::CLI.call

Slide 52

Slide 52 text

#!/usr/bin/env ruby require "melb_ruby/cli" MelbRuby::CLI.call

Slide 53

Slide 53 text

exe/melburnian

Slide 54

Slide 54 text

chmod +x exe/melburnian

Slide 55

Slide 55 text

Dependencies

Slide 56

Slide 56 text

Runtime Dependencies

Slide 57

Slide 57 text

Development Dependencies

Slide 58

Slide 58 text

spec.add_runtime_dependency "thor", ">= 0.14" spec.add_development_dependency "rspec", "~> 3.0"

Slide 59

Slide 59 text

Building a gem

Slide 60

Slide 60 text

melb_ruby-0.0.1.gem

Slide 61

Slide 61 text

gem build melb_ruby.gemspec

Slide 62

Slide 62 text

gem install melb_ruby-0.0.1.gem

Slide 63

Slide 63 text

Publishing a gem

Slide 64

Slide 64 text

Get an account on rubygems.org

Slide 65

Slide 65 text

gem push melb_ruby-0.0.1.gem

Slide 66

Slide 66 text

Published gem versions cannot be changed

Slide 67

Slide 67 text

Published gem versions can be removed… but probably shouldn't be.

Slide 68

Slide 68 text

Okay, take a breath.

Slide 69

Slide 69 text

Use Bundler to generate a gem

Slide 70

Slide 70 text

bundle gem melb_ruby

Slide 71

Slide 71 text

rake release

Slide 72

Slide 72 text

# Gemfile source "https://rubygems.org" gemspec

Slide 73

Slide 73 text

Code of Conduct

Slide 74

Slide 74 text

Support

Slide 75

Slide 75 text

Support GitHub Issues

Slide 76

Slide 76 text

Stack Overflow GitHub Issues Support

Slide 77

Slide 77 text

README Support Stack Overflow GitHub Issues

Slide 78

Slide 78 text

Versioning

Slide 79

Slide 79 text

Semantic Versioning

Slide 80

Slide 80 text

6.0.1

Slide 81

Slide 81 text

6.0.1 Major

Slide 82

Slide 82 text

6.0.1 Minor

Slide 83

Slide 83 text

6.0.1 Patch

Slide 84

Slide 84 text

New Patch Versions Patch 6.0.2

Slide 85

Slide 85 text

New Minor Versions Minor 6.1.0

Slide 86

Slide 86 text

Major Versions Major 7.0.0

Slide 87

Slide 87 text

Looking for examples?

Slide 88

Slide 88 text

Most gems are on GitHub

Slide 89

Slide 89 text

bundle open rspec

Slide 90

Slide 90 text

Gems that use Rails

Slide 91

Slide 91 text

Rails Engines

Slide 92

Slide 92 text

melb_ruby/app/models/post.rb melb_ruby/db/migrate/1_create_posts.rb

Slide 93

Slide 93 text

# lib/melb_ruby/engine.rb class MelbRuby::Engine < Rails::Engine engine_name :melb_ruby end

Slide 94

Slide 94 text

# within lib/melb_ruby.rb require "melb_ruby/engine"

Slide 95

Slide 95 text

Railties

Slide 96

Slide 96 text

# lib/melb_ruby/railtie.rb class MelbRuby::Railtie < Rails::Railtie config.to_prepare do MelbRuby::Prepare.call end initializer 'melb_ruby.initialisation' do # ... end rake_tasks do load File.expand_path('../tasks.rb', __FILE__) end end

Slide 97

Slide 97 text

# within lib/melb_ruby.rb require "melb_ruby/railtie"

Slide 98

Slide 98 text

Other General Advice

Slide 99

Slide 99 text

Don't Monkeypatch Core Ruby

Slide 100

Slide 100 text

Don't Monkeypatch Rails

Slide 101

Slide 101 text

Use fewer modules

Slide 102

Slide 102 text

Use Appraisal

Slide 103

Slide 103 text

Use CI

Slide 104

Slide 104 text

Use Combustion (for Rails engines)

Slide 105

Slide 105 text

And that's it…