Ruby Training

From bibbleWiki
Jump to navigation Jump to search

Introduction

This is to summarised what I learn on the ruby Training

Returning if true

Quite liked this. On do the return if true

  def remaining_minutes_in_oven(actual_minutes_in_oven)
    return EXPECTED_MINUTES_IN_OVEN - actual_minutes_in_oven if actual_minutes_in_oven < EXPECTED_MINUTES_IN_OVEN
    raise 'Please implement the Lasagna#remaining_minutes_in_oven method'
  end

Symbols

Symbols are just an identifier

class User
  STATES = [:active, :inactive, :banned]
...

Provide you pass the same thing, they are the same thing e.g.

User.new(:active)


Predicate Method

So in ruby you have a predicate method of a method that returns true or false which is, by convention, not enforced, a method with a ?. The example below returns true if status equal true.

class User
  STATES = [:active, :inactive, :banned]

  def initialize(status)
    raise "Invalid state" unless STATES.include?(status)
    @status = status
  end

  def active?
    @status == :active
  end
end

This became a bit more obvious the usage with the example with ranges

module Chess
  RANKS = 1..8
  FILES = 'A'..'H'

  def self.valid_square?(rank, file)
    RANKS.include?(rank) && FILES.include?(file)
  end
end

Interpolation

This is how to interpolation works

my_var1 = "HELLO"
my_var2 = "FRED WAS HERE #{my_var1}"
puts my_var2

LINQ in Ruby?

So this is what was given

class BirdCount
  def self.last_week
    [0, 2, 5, 3, 7, 8, 4]
  end

  def initialize(birds_per_day)
    @birds_per_day = birds_per_day
  end

  def yesterday
    @birds_per_day[-2]
  end

  def total
    @birds_per_day.sum
  end

  def busy_days
    @birds_per_day.count { |day| day >= 5 }
  end

  def day_without_birds?
    @birds_per_day.any? { |day| day == 0 }
  end
end

Which looks canna complicated but really it just like this in C#

public class BirdCount
{
    public int[] BirdsPerDay { get; }

    public BirdCount(int[] birdsPerDay)
    {
        BirdsPerDay = birdsPerDay;
    }
}

var birds = new BirdCount(new[] { 0, 2, 5, 3, 7, 8, 4 });
birds.Where(day => day >= 5).Count();


Other functions include

fibonacci = [0, 1, 1, 2, 3, 5, 8, 13]

fibonacci.count  { |number| number == 1 }   #=> 2
fibonacci.any?   { |number| number == 6 }   #=> false
fibonacci.select { |number| number.odd? }   #=> [1, 1, 3, 5, 13]
fibonacci.all?   { |number| number < 20 }   #=> true
fibonacci.map    { |number| number * 2  }   #=> [0, 2, 2, 4, 6, 10, 16, 26]

Mapping over arrays

This was quite hard for me. I think just not used to syntax. Once I see the answer it is obvious. compact removes the nil values in an array.

# So an example of the data is

    shoes = { price: 30.00, name: "Shoes", quantity_by_size: { s: 1, xl: 4 } }
    coat = { price: 65.00, name: "Coat", quantity_by_size: { s: 2 } }
    handkerchief = { price: 19.99, name: "Handkerchief", quantity_by_size: { m: 3, l: 2 } }
    items = [shoes, coat, handkerchief]

class BoutiqueInventory
  def initialize(items)
    @items = items
  end

  def item_names
    @items.map { |item| item[:name] }.flatten.sort
  end

  def cheap
    @items.map { |item| item[:price] < 30.00 ? item : nil }.compact
  end

  def out_of_stock
    @items.map { |item| item[:quantity_by_size].empty? ? item : nil }.compact
  end

  def stock_for_item(name)
    @items.map { |item| item[:name] == name ? item[:quantity_by_size] : nil }.compact.first || {}
  end

  def total_stock
    @items.map { |item| item[:quantity_by_size].values.sum }.sum
  end

  private
  attr_reader :items
end

Openstruct

With this we create an object from a hashes. E.g.

attributes = { name: "Jeremy Walker", age: 21, location: "Nomadic" }
person = OpenStruct.new(attributes)

# You can now do
person.name
#=> Jeremy Walker

person.location
#=> Nomadic

So for out example above we can do

# So an example of the data is

    shoes = { price: 30.00, name: "Shoes", quantity_by_size: { s: 1, xl: 4 } }
    coat = { price: 65.00, name: "Coat", quantity_by_size: { s: 2 } }
    handkerchief = { price: 19.99, name: "Handkerchief", quantity_by_size: { m: 3, l: 2 } }
    items = [shoes, coat, handkerchief]

class BoutiqueInventory
  def initialize(items)
    # @items = items
    @items = items.map { |item| OpenStruct.new(item) }
  end

  def item_names
    # @items.map { |item| item[:name] }.flatten.sort
    # First Bash
    # items.map { |item| item.name }.sort
    items.map(&:name).sort
  end

  def total_stock
    # @items.map { |item| item[:quantity_by_size].values.sum }.sum
    # First Bash
    # items.sum do |item|
    #  item.quantity_by_size.values.sum
    # end
    items.map(&:quantity_by_size).map(&:values).map(&:sum).sum
  end

  private
  attr_reader :items
end

Decomposing Arrays and Hashes

I think it probably is just arrays as hashes will need to be converted to arrays

>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> x, y, z = fruits_inventory
>> x
=> {:apple=>6, :banana=>2, :cherry=>3}
>> y
=> nil

Instead use to_a

>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> fruits_inventory.to_a
=> [[:apple, 6], [:banana, 2], [:cherry, 3]]
>> x, y, z = fruits_inventory.to_a
>> x
=> [:apple, 6]

Once a array, we can decompose stuff any way we choose using splats to capture remaining. Did actually quite like this.

>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>> x, *middle, y, z = fruits
>> y
=> "melon"
>> middle
=> ["banana", "cherry", "orange", "kiwi"]

Composing Arrays and Hashes

Composing can be done with single splat (*) for arrays

>> fruits = ["apple", "banana", "cherry"]
>> more_fruits = ["orange", "kiwi", "melon", "mango"]

# fruits and more_fruits are unpacked and then their elements are packed into combined_fruits
>> combined_fruits = *fruits, *more_fruits

>> combined_fruits
=> ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]

And a double splat (**)for hashes

>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> more_fruits_inventory = {orange: 4, kiwi: 1, melon: 2, mango: 3}

# fruits_inventory and more_fruits_inventory are unpacked into key-values pairs and combined.
>> combined_fruits_inventory = {**fruits_inventory, **more_fruits_inventory}

# then the pairs are packed into combined_fruits_inventory
>> combined_fruits_inventory
=> {:apple=>6, :banana=>2, :cherry=>3, :orange=>4, :kiwi=>1, :melon=>2, :mango=>3}

Splats with Arguments

Maybe the only sensible thing to say about this is like Gary Eats ("let's give a go"). If they can fit it in they will. It suggests
def my_method(<positional_arguments>, *arguments, <positional_arguments>, <keyword_arguments>, **keyword_arguments)

def my_method(a, b, *arguments,c, symbol_bash:, **d)
  p a
  p b
  p arguments
  p c
  p symbol_bash
  p d
end

my_method(1, 2, 3, 4, 5, 6, symbol_bash: 100, a: 1, b: 2)

Ruby String functions

translate characters (tr)

Just substitute a for b

"metal-oxide".tr("-", " ")

global substitute with regex

So will never learn regex just to difficult for me. Know how to solve so not an issue. Here is replace anything not matching a-z or A-Z with ""

phrase.gsub(/[^a-zA-Z\s]/, "")

Exceptions

So quick reminder of mainly rescue and exceptions

class SimpleCalculator
  ALLOWED_OPERATIONS = ['+', '/', '*'].freeze

  class UnsupportedOperation < StandardError; end

  def self.calculate(first_operand, second_operand, operation)
    unless ALLOWED_OPERATIONS.include?(operation)
      raise UnsupportedOperation, "Operation must be one of #{ALLOWED_OPERATIONS.join(', ')}"
    end

    raise ArgumentError, 'Operands must be numeric' unless first_operand.is_a?(Numeric) && second_operand.is_a?(Numeric)

    case operation
    when '+'
      "#{first_operand} + #{second_operand} = #{first_operand + second_operand}"
    when '/'
      begin
        "#{first_operand} / #{second_operand} = #{first_operand / second_operand}"
      rescue ZeroDivisionError
        'Division by zero is not allowed.'
      end
    when '*'
      "#{first_operand} * #{second_operand} = #{first_operand * second_operand}"
    end
  end
end