Skip to content

Instantly share code, notes, and snippets.

@MrLeebo
Last active January 21, 2022 18:03
Show Gist options
  • Save MrLeebo/c05f78a68ab2976cf6af300728f2415c to your computer and use it in GitHub Desktop.
Save MrLeebo/c05f78a68ab2976cf6af300728f2415c to your computer and use it in GitHub Desktop.

ruby from a js dev perspective

This isn't an exhaustive introduction to the ruby language. Instead I wanted to talk about what makes ruby different from JavaScript and other languages that you may have experience with

destructing arrays and objects

in JS, we can deconstruct both arrays and objects into variables

// array destructuring
const [first, second, third, ...losers] = racers;

// object destructuring
const { id, title } = props;

these concepts still apply to a degree in ruby, though the syntax is slightly different

first, second, third, &losers = racers

id, title = props.values_at(:id, :title)

blocks

you can write ruby blocks three ways; multi-line, single-line, and there's a shorthand for a single property access

# these are all the same
names = specifications.map do |spec|
  spec.name
end

names = specifications.map{|spec| spec.name}

names = specifications.map(&:name)

statements and expressions

in JS, there are if statements, try..catch statements, throw statements, etc... In Ruby, all of these can be used as expressions also

# if statements

# instead of writing your guard clause this way
if !valid? then return
# you can use a conditional expression
return if !valid?
# ruby also an "unless" keyword since "if not valid!" doesn't exactly roll of the tongue
return unless valid?

# error handling

# instead of
lunch =
  begin
    pick_lunch(choices) # sometimes fails
  rescue StandardError => err
    "ramen"
  end

# try
lunch = pick_lunch(choices) rescue "ramen"

# throw/raise

# classic statement-style raise exception
can_vote = age >= 18 
unless can_vote then raise "too young"
# raise can be part of an expression
can_vote or raise "too young"

# OK, that last example is a little gross, but I'm just illustrating the point

symbols

symbols are essentially light-weight immutable strings, but even though strings are mutable in ruby, it generally isn't a good practice to do so. prefer string concatenation / template strings like you would use in other languages. Unlike JS, same symbols are equal to each other

Symbol("test") !== Symbol("test")
:test == :test

array shorthand

ruby has a shorthand syntax for declaring arrays of strings or symbols that are simple words with no spaces or special characters, you'll see it often used for lists of column names or attribute names

# instead of 
symbols = [:id, :title, :name, :address]
# you can write
symbols = %i[id title name address]

# and instead of
racers = ["marshal", "chad", "emmy", "matt"]
# try
racers = %w[marshal chad emmy matt]

blocks vs lambdas vs JS arrow functions

blocks act like callback functions, but you're still in the context of the parent method, so return will break you out of not just the block, but the method containing it as well. to "pass a value" from a block, you can use next "value" to pass control back to the code that is yielding the block, or break to pass control back to your parent method (e.g. to break out of a loop)

lambdas are similar to arrow functions in JS

// in JS
providers.forEach((provider) => {
  confirmedProviders.push(provider)
})
# ruby block syntax
providers.each do |provider|
  confirmed_providers << provider
end

# lambda
before_action -> { authorize_action_for(Feature.ManageUsers) }

active record

ActiveRecord is an ORM/query builder for Rails. Interacting with and manipulating ActiveRecord models makes up a lot of the business logic in Network360. Whenever you execute ActiveRecord code in dev, the SQL that was generated will appear in the logs. You can also see what SQL a hypothetical ActiveRequest query would produce by building a query and calling .to_sql on it.

# example: return the name of the first global access spec, sorted alphabetically

# bad, pulls the entire table and performs all sorting, filtering, and formatting in memory
AccessibilitySpecification.all
  .sort{|a,b| a.name.downcase <=> b.name.downcase}
  .filter{|spec| spec.is_global}
  .map(&:name)
  .first # SELECT * from accessibility_specifications

# good, the generated SQL is the same as what you'd write by hand
AccessibilitySpecification.order(:name)
  .where(is_global: true)
  .select(:name)
  .first.name # SELECT name from accessibility_specifications WHERE is_global ORDER BY name LIMIT 1

meta-programming

meta-programming describes the pattern in ruby of using code to write code for you. Rails provides tons of useful helpers to help you implement common patterns in web applications. These helpers can define methods for you, can set up before/after action callbacks for validating data or formatting HTTP responses, and have lots of other uses

class MyController < ApplicationController
  protect_from_forgery # csrf protection using a meta-programming helper

  def index
    render json: { message: "we did it!" }
  end
end

while it's common in ruby for meta-programming to apply at the class definition level, you can also apply it to individual object instances. each instance creates a copy of its parent class, called the metaclass

class MyModel; end
model = MyModel.new

# the syntax for getting a reference to the metaclass is a bit... peculiar
metaclass = class << model; self; end

# but once we have it, we can modify the class of a specific object instance
metaclass.define_method(:speak) do |words|
  puts("You said: #{words}")
end

model.speak "hello, world"

# calling the speak method on a different instance raises an undefined method error
MyModel.new.speak "sorry"

the ability to access the metaclass of an instance can be useful for several purposes:

  • mocking out dependencies in unit tests
  • dynamic data-driven objects (e.g. an XML file parser)
  • creating a finite state machine

this is not the sort of thing you would write often (if ever), but it is the driver behind a lot of "rails magic" so it's important to recognize where you might see these techniques show up if you want to learn how Rails accomplishes what it does

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment