Slide 1

Slide 1 text

Writing Ruby Gems Liz Abinante • @feministy Senior Software Engineer, New Relic [email protected]

Slide 2

Slide 2 text

View slides online! tinylinx.biz/ruby

Slide 3

Slide 3 text

View example gem tinylinx.biz/sample_gem

Slide 4

Slide 4 text

Requirements

Slide 5

Slide 5 text

You have familiarity with: •Ruby •Command line tools •git

Slide 6

Slide 6 text

If you want to follow along… •A computer that has: ̣ Ruby 2.3.1 ̣ bundler 1.12 ̣ rspec 3.0 ̣ git

Slide 7

Slide 7 text

If you want to follow along… •Accounts and command line access for: ̣ Rubygems.org ̣ GitHub

Slide 8

Slide 8 text

Overview & Goals

Slide 9

Slide 9 text

CLI Tools for Development Overview of Bundler

Slide 10

Slide 10 text

Gem dependencies What are they? How do they work?

Slide 11

Slide 11 text

File structure WHY ARE THERE SO MANY FOLDERS

Slide 12

Slide 12 text

Versioning & licensing Semantic versioning overview & OSS rant

Slide 13

Slide 13 text

Testing patterns & CI Happy code is tested code

Slide 14

Slide 14 text

Publishing versions (and yanking versions)

Slide 15

Slide 15 text

Configuration blocks Letting your users configure your gem

Slide 16

Slide 16 text

What’s next Ideas for small gems you can write today!

Slide 17

Slide 17 text

Slide formatting

Slide 18

Slide 18 text

Terminal > this is text printed out this is a command you type in

Slide 19

Slide 19 text

Ruby code def wee_method(i_am) if i_am == "SUPER COOL" puts "✨ ✨" end end

Slide 20

Slide 20 text

LET’S GO!

Slide 21

Slide 21 text

CLI Tools

Slide 22

Slide 22 text

Bundler •Why bundler? •Creating a new gem •Creation options •Caveats

Slide 23

Slide 23 text

Why bundler? •provides a simple, well documented CLI •supportive community •used with Gemfiles

Slide 24

Slide 24 text

Why bundler? •helps you get started with best practices •prompts you to think about testing •prompts you to think about contribution guidelines

Slide 25

Slide 25 text

Create a new gem bundler gem cryptozoologist

Slide 26

Slide 26 text

Create a new gem bundler gem cryptozoologist bundler gem cryptozoologist bundler gem my_cool_gem

Slide 27

Slide 27 text

Create a new gem > Do you want to generate tests with your gem? rspec

Slide 28

Slide 28 text

Create a new gem > Do you want to license your code permissively under the MIT license? y

Slide 29

Slide 29 text

Create a new gem > Do you want to include a code of conduct in gems you generate? y

Slide 30

Slide 30 text

Create a new gem create cryptozoologist/Gemfile create cryptozoologist/.gitignore create cryptozoologist/lib/cryptozoologist.rb create cryptozoologist/lib/cryptozoologist/version.rb create cryptozoologist/cryptozoologist.gemspec create cryptozoologist/Rakefile create cryptozoologist/README.md create cryptozoologist/bin/console create cryptozoologist/bin/setup create cryptozoologist/.travis.yml create cryptozoologist/.rspec create cryptozoologist/spec/spec_helper.rb create cryptozoologist/spec/cryptozoologist_spec.rb create cryptozoologist/LICENSE.txt create cryptozoologist/CODE_OF_CONDUCT.md

Slide 31

Slide 31 text

Caveats •bundler will only ask these questions once •gem names must be unique •alternative licenses are available •bundler doesn't set a Ruby version ̣ but we can do this ourselves!

Slide 32

Slide 32 text

Commit your baby gem! git add . git commit -m “✨ gem scaffold”

Slide 33

Slide 33 text

Gem creation commit tinylinx.biz/new_gem

Slide 34

Slide 34 text

Gem dependencies

Slide 35

Slide 35 text

Gemfiles How you list dependencies and pin versions for Ruby applications

Slide 36

Slide 36 text

Quick run down •lists dependencies for apps •pins versions for dependencies •lists sources for dependencies •can include ruby version! •paired with a Gemfile.lock

Slide 37

Slide 37 text

Gemfiles for gems In a Ruby gem, you also have a Gemfile! ... but it's used differently

Slide 38

Slide 38 text

Gemfile source 'https://rubygems.org' # Specify your gem's dependencies in cryptozoologist.gemspec gemspec

Slide 39

Slide 39 text

Gemfile source 'https://rubygems.org' # Specify your gem's dependencies in cryptozoologist.gemspec gemspec the only part that matters

Slide 40

Slide 40 text

Gemfile source 'https: //rubygems.org' # Specify your gem's dependencies in cryptozoologist.gemspec gemspec ok this matters, too

Slide 41

Slide 41 text

View Gemfile tinylinx.biz/gemfile

Slide 42

Slide 42 text

Gemspec Gemspecs cover a lot of things, we’re only looking at dependencies right now.

Slide 43

Slide 43 text

Gemspec # coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'cryptozoologist/version' Gem::Specification.new do |spec| spec.name = "cryptozoologist" spec.version = Cryptozoologist::VERSION spec.authors = ["Liz Abinante"] spec.email = ["[email protected]"] spec.summary = "Generates random strings from animal, clothing item, and color pairings." spec.description = "Cryptozoologist generates random strings from animal, clothing item, and color pairings." spec.homepage = "https://github.com/feministy/cryptozoologist" spec.license = "MIT" spec.files = Dir["CODE_OF_CONDUCT.md", "CHANGELOG.md", "LICENSE.txt", "README.md", "lib/**/*.rb"] spec.test_files = Dir["spec/**/*.rb"] spec.require_paths = ["lib"] spec.add_development_dependency "bundler", "~> 1.12" spec.add_development_dependency "rake", "~> 10.0" spec.add_development_dependency "rspec", "~> 3.4" spec.add_development_dependency "pry", "~> 0.10.3" spec.add_development_dependency "pry-nav", "~> 0.2.4" end

Slide 44

Slide 44 text

Gemspec # blah blah blah Gem::Specification.new do |spec| # stuff we don’t care abut (right now) spec.add_development_dependency "bundler", "~> 1.12" spec.add_development_dependency "rake", "~> 10.0" spec.add_development_dependency "rspec", "~> 3.4" spec.add_development_dependency "pry", "~> 0.10.3" spec.add_development_dependency "pry-nav", "~> 0.2.4" end

Slide 45

Slide 45 text

add_development_dependency This does not require the listed dependency if you're running the gem in your application!

Slide 46

Slide 46 text

File structure

Slide 47

Slide 47 text

Remember all these files? create cryptozoologist/Gemfile create cryptozoologist/.gitignore create cryptozoologist/lib/cryptozoologist.rb create cryptozoologist/lib/cryptozoologist/version.rb create cryptozoologist/cryptozoologist.gemspec create cryptozoologist/Rakefile create cryptozoologist/README.md create cryptozoologist/bin/console create cryptozoologist/bin/setup create cryptozoologist/.travis.yml create cryptozoologist/.rspec create cryptozoologist/spec/spec_helper.rb create cryptozoologist/spec/cryptozoologist_spec.rb create cryptozoologist/LICENSE.txt create cryptozoologist/CODE_OF_CONDUCT.md

Slide 48

Slide 48 text

Folders bin

Slide 49

Slide 49 text

bin •contains executables •things you’d run in the command line •console automatically starts IRB •setup automatically runs bundle install •we won’t be using these today

Slide 50

Slide 50 text

Folders lib

Slide 51

Slide 51 text

lib •actual files for the gem! •only 2 things go in your lib root folder: ̣ cryptozoologist.rb ̣ cryptozoologist/

Slide 52

Slide 52 text

lib/cryptozoologist.rb •requires all of your other files •where you create your gem namespace (Cryptozoologist) using parent module

Slide 53

Slide 53 text

lib/cryptozoologist •subfolder! •where you put everything you want to use in your gem •everything we add will go here - except for tests

Slide 54

Slide 54 text

lib/cryptozoologist/version.rb •sets the VERSION constant for your gem •required by your gemspec to set your gem version number

Slide 55

Slide 55 text

Folders spec

Slide 56

Slide 56 text

spec •where your tests go! •follows the same naming and folder patterns a lib •add _spec to the end of your file name

Slide 57

Slide 57 text

lib/cryptozoologist.rb spec/cryptozoologist_spec.rb FILE: TESTS:

Slide 58

Slide 58 text

lib/cryptozoologist/dictionary.rb spec/cryptozoologist/dictionary_spec.rb FILE: TESTS:

Slide 59

Slide 59 text

Versioning

Slide 60

Slide 60 text

semver semantic versioning

Slide 61

Slide 61 text

Version levels •major: 1.0.0 •minor: 1.1.0 •patch: 1.1.1

Slide 62

Slide 62 text

Other types of versions •release candidate: rc ̣ 4.2.5.rc2 ̣ 4.2.5.rc.2

Slide 63

Slide 63 text

Other types of versions •alpha: alpha ̣ 1.1.0.alpha.1 ̣ 1.1.0.alpha1

Slide 64

Slide 64 text

Other types of versions •beta: beta ̣ 1.1.0.beta.1 ̣ 1.1.0.beta1

Slide 65

Slide 65 text

Patch •1.1.3, 2.19.6, etc •should be small •usually does not introduce new functionality

Slide 66

Slide 66 text

Patch •small bug fixes ̣ backwards compatible ̣ do not break existing functionality •added documentation

Slide 67

Slide 67 text

Patch •people rarely pin to patch versions •this makes it easier for people to get your new patches without thinking about it •patches are great!

Slide 68

Slide 68 text

Patch •patches are also great for: ̣ security fixes you want everyone to have ̣ small improvements or additions that aren’t required ̣ adding incremental upgrades and deprecation messaging for future versions

Slide 69

Slide 69 text

Minor •1.3.10, 2.18.0, etc •range in size from small to large •add new features or functionality without: ̣ breaking previous versions ̣ changing behavior of current version

Slide 70

Slide 70 text

Minor •can include: ̣ small: adding one new method that establishes a new pattern ̣ large: adding a new feature, such as covering a new API endpoint

Slide 71

Slide 71 text

Minor •it’s not uncommon to see a lot of minor bumps in less popular gems as features get added •larger, more stable projects like Rails don’t use as many minor versions

Slide 72

Slide 72 text

Minor •you can also you minor changes to help with: ̣ slow, early deprecation warnings alongside your new changes or features ̣ easier upgrade process for your next major version

Slide 73

Slide 73 text

Major •1.10.0, 3.0.2, etc •usually very big changes that: ̣ are not backwards compatible ̣ change your API contract ̣ require code changes to upgrade

Slide 74

Slide 74 text

Major •don’t be hesitant to make breaking changes ̣ but if you do, be sure you label them as breaking! •starting your gem on a major version of 0 makes it difficult to bump your major version

Slide 75

Slide 75 text

API contracts •you should be providing your users with a consistent interface •if your public interface (aka your gem’s API) is not consistent, people won’t use your gem

Slide 76

Slide 76 text

API contracts •your gem will be easier to use if you: ̣ don’t change the type of object returned ̣ establish a consistent pattern ̣ document your code

Slide 77

Slide 77 text

Example version •your gem provides a method that tells someone if a user is logged in ̣ version 1.3.5 returns a boolean ̣ your next version returns a User object if they’re logged in, and nil if they’re not

Slide 78

Slide 78 text

What should your next version be? 2.0.0

Slide 79

Slide 79 text

Licensing

Slide 80

Slide 80 text

IANAL I am not a lawyer.

Slide 81

Slide 81 text

By default, all gems have an MIT license.

Slide 82

Slide 82 text

MIT licenses are a core foundational of OSS.

Slide 83

Slide 83 text

MIT licenses let people do anything, as long as you get credit & are not liable.

Slide 84

Slide 84 text

Reasons you might not want MIT •you’re creating a gem for: ̣ work and it’s internal use only ̣ you’re using a data source or API that doesn’t allow for a permissive license ̣ you don’t want others profiting off of your free labor

Slide 85

Slide 85 text

If you don’t use MIT… •your code can’t be used in closed source, for profit things •your code can only be used in other free, open source projects that have the same, or more restrictive, license •I won’t judge you because OSS lets big companies with deep pockets profit off of great code for free instead of investing in community software

Slide 86

Slide 86 text

Common alternatives •GPL, version 2 or 3 ̣ “copyleft” •Apache 2.0 ̣ similar to MIT but talks about patents •see choosealicense.com for more

Slide 87

Slide 87 text

Use a license. I don’t care which one. Just do it.

Slide 88

Slide 88 text

Testing patterns & CI

Slide 89

Slide 89 text

CI with Travis

Slide 90

Slide 90 text

Running tests with CI •Travis runs with every push, PR, and merge •free for open source projects! •highly configurable

Slide 91

Slide 91 text

Sample .travis.yml language: ruby rvm: - 2.3.1 before_install: gem install bundler -v 1.12.3 notifications: email: false

Slide 92

Slide 92 text

Testing patterns

Slide 93

Slide 93 text

How to test? •the more object oriented your code is, the easier it will be to test •testing the underlying private API is just as important as the public one •sometimes your public tests are really small

Slide 94

Slide 94 text

Public API module Cryptozoologist def self.generate string = "" # determine string order # determine any special configs that need to be added order.each do |library| # get a word, add a delimiter # squish together a string end return string end end /lib/cryptozoologist.rb

Slide 95

Slide 95 text

Testing the public API describe Cryptozoologist do # some other tests context '#generate' do it 'returns a string' do expect(Cryptozoologist.generate).to be_instance_of(String) end it 'returns a string with the delimeter' do expect(Cryptozoologist.generate.match('-')).to be_instance_of(MatchData) end end end /spec/cryptozoologist_spec.rb

Slide 96

Slide 96 text

Private API module Cryptozoologist def self.generate string = "" # determine string order order.unshift(:quantity) if @configuration.include_quantity? order.each do |library| # get a word, add a delimiter # squish together a string end return string end end /lib/cryptozoologist.rb

Slide 97

Slide 97 text

Testing the private API describe Cryptozoologist ::Configuration do context ‘#include' do it 'sets valid inclusions' do Cryptozoologist.configure do |config| config.include = [:quantity] end expect(Cryptozoologist.configuration.include_quantity?).to be true end end end /spec/cryptozoologist/configuration_spec.rb

Slide 98

Slide 98 text

Publishing

Slide 99

Slide 99 text

Before publishing •update your version number •commit and push your changes to GitHub

Slide 100

Slide 100 text

Publishing gem build cryptozoologist.gemspec Successfully built RubyGem Name: cryptozoologist Version: 2.0.0 File: cryptozoologist-2.0.0.gem gem push cryptozoologist-2.0.0.gem

Slide 101

Slide 101 text

Yanking

Slide 102

Slide 102 text

What does yanking do? •removes the gem from rubygems.org ̣ users can’t install it using gem install ̣ removes the gem file completely •permanently removes that version number: you cannot republish using that version number!

Slide 103

Slide 103 text

Yanking gem yank cryptozoologist -v 2.0.0

Slide 104

Slide 104 text

Configuration blocks

Slide 105

Slide 105 text

Why configuration? •allows things to be set once ̣ API keys, usernames, passwords ̣ callback URLs •change usage options ̣ test order in RSpec!

Slide 106

Slide 106 text

Configuration for our sample gem •exclude word lists •specify delimiter type •include quantity words •order of words

Slide 107

Slide 107 text

Example configuration Changing delimiter

Slide 108

Slide 108 text

Applying configuration Cryptozoologist.configure do |config| config.include = [:quantity] config.delimiter = “_” end in your application somewhere…

Slide 109

Slide 109 text

Configuration module Cryptozoologist class << self attr_accessor :configuration end def self.configuration @configuration ||= Configuration.new end def self.configure yield(configuration) end end /lib/cryptozoologist.rb

Slide 110

Slide 110 text

Configuration module Cryptozoologist class Configuration attr_reader :delimiter def initialize @delimiter = "-" end def delimiter=(string) raise Errors::Configuration, "delimiter must be a a string" unless string.is_a?(String) @delimiter = string end end end /lib/cryptozoologist/configuration.rb

Slide 111

Slide 111 text

Configuration tutorials tinylinx.biz/gem_config tinylinx.biz/gem_errors

Slide 112

Slide 112 text

What next?

Slide 113

Slide 113 text

Make a gem! Wait, really? Yes, really.

Slide 114

Slide 114 text

Ideas •random strings •long password generator •fake address creator •convert hex colors to RGB ̣ tinylinx.biz/hex_to_rgb

Slide 115

Slide 115 text

Your gem does not have to provide unique functionality.

Slide 116

Slide 116 text

tinylinx.biz/ruby Liz Abinante • @feministy Senior Software Engineer, New Relic [email protected]