Code Monkey home page Code Monkey logo

phase-3-metaprogramming-keyword-arguments-and-mass-assignment's Introduction

Keyword Arguments

Learning Goals

  • Define keyword arguments and learn why they are useful
  • Define mass assignment and learn why it is useful
  • Practice using keyword arguments and mass assignment

Introduction

At this point, we're very familiar with the fact that methods can be defined to take in arguments. We also know that methods can be defined to take in multiple arguments. Let's take a look at an example:

def print_name_and_greeting(greeting, name)
  puts "#{greeting}, #{name}"
end

print_name_and_greeting("'sup", "Hillary")
# 'sup, Hillary
# => nil

As it currently stands, whoever uses our method needs to remember exactly what order to pass in the arguments. They need to know that the first argument is greeting and the second argument is a name. Arguments defined this way are known as positional arguments, because the position they are passed in makes a difference when the function is called.

What happens if another developer who is working on our code base doesn't see the file where the method is defined, but only sees the method being invoked? Maybe it's not clear to them which argument is which โ€” after all, when you invoke methods, the arguments aren't labeled or anything. Let's take a look at what type of disaster would befall us:

print_name_and_greeting("Kanye", "hello")
# Kanye, hello
# => nil

That would be a weird way to greet Kanye. Let's take a look at another example, this time using arguments of two different data types:

def happy_birthday(name, current_age)
  puts "Happy Birthday, #{name}"
  current_age += 1
  puts "You are now #{current_age} years old"
end

happy_birthday("Beyonce", 31)
# Happy Birthday, Beyonce
# You are now 32 years old
# => nil

But what happens if we accidentally pass the arguments in in the wrong order?

happy_birthday(31, "Beyonce")
# Happy Birthday, 31
# TypeError: no implicit conversion of Fixnum into String

Oh no! We broke our program! Clearly, we have a need to regulate the passing in of multiple arguments. It would be especially helpful if we could name the arguments that we pass in, when we invoke the method. Guess what? We can! (Okay, you probably saw that one coming.)

Using Keyword Arguments

Keyword arguments are a special way of passing arguments into a method. They behave like hashes, pairing a key that functions as the parameter name, with its value. Let's walk through it together and refactor our happy_birthday method:

def happy_birthday(name: "Beyonce", current_age: 31)
  puts "Happy Birthday, #{name}"
  current_age += 1
  puts "You are now #{current_age} years old"
end

Here, we've defined our method to take in keyword arguments. Our keyword arguments consist of two key/value pairs, :name and :current_age. We've given our keyword arguments default values of "Beyonce" and 31, but we didn't have to:

def happy_birthday(name:, current_age:)
  puts "Happy Birthday, #{name}"
  current_age += 1
  puts "You are now #{current_age} years old"
end

Notice that we can reference name and current_age inside our method body, as if they were barewords, even though they are the keys in our keyword arguments. That's how keyword arguments work, they allow you to name the arguments that you pass in as keys in a hash. Then, the method body can use the values of those keys, referenced by their name. Let's call our method:

happy_birthday(current_age: 31, name: "Carmelo Anthony")
# Happy Birthday, Carmelo Anthony
# You are now 32 years old

Notice that even though we changed the order of our key/value pairs, our method didn't break! Not only is this method more robust (i.e. more resistant to breakage) than the version using positional arguments, it is also more explicit. Anyone looking at its invocation can tell exactly what kind of data you are passing in.

While there are differences in the way they are implemented, Ruby's keyword arguments feature provides similar functionality to parameter destructuring in JavaScript, like you've seen with props destructuring in React. For example, if we define a function in JavaScript that accepts an object as an argument, we can destructure the parameters of that argument in the function definition to make it clear to the callers of that function what data it needs:

function happyBirthday({ name, currentAge }) {
  console.log(`Happy Birthday, ${name}`);
  currentAge++;
  console.log(`You are now ${currentAge} years old`);
}

happyBirthday({ currentAge: 31, name: "Carmelo Anthony" });
// Happy Birthday, Carmelo Anthony
// You are now 32 years old

Mass Assignment

Another benefit of using keyword arguments is the ability to "mass assign" attributes to an object. Let's revisit our Person class from an earlier lesson. We'd like to initialize individual people with a name and an age:

class Person
  attr_accessor :name, :age

  def initialize(name, age)
    @name = name
    @age = age
  end
end

As it stands, our initialize method is vulnerable to the same issues we discussed above. Let's refactor it to take in a person's attributes as keyword arguments:

class Person
  attr_accessor :name, :age

  def initialize(name:, age:)
    @name = name
    @age = age
  end
end

Now, we have the added benefit of being able to use something called mass assignment to instantiate new people objects. If a method is defined to accept keyword arguments, we can create the hash that the method is expecting to accept as an argument, assign that hash to a variable, and simply pass the variable in to the method as an argument. Let's take a look:

person_attributes = { name: "Sophie", age: 26 }
sophie = Person.new(person_attributes)
# => #<Person:0x007f9bd5814ae8 @name="Sophie", @age=26>

Again, in order for this to work, we have to set up the initialize method to use keyword arguments. If it was set up to take positional arguments instead, calling the method as we did above would return an argument error:

ArgumentError: wrong number of arguments (given 1, expected 2)

At this point, it might not seem particularly useful to be able to pass in a hash as an argument. However, as we'll learn in the next lesson, one thing it enables us to do is write our code for initializing instances of a class in a more abstract, flexible and error-proof way. When we start building web applications, we'll understand more about how helpful this approach really is.

Note: Starting with Ruby version 3, the use of a hash for keyword arguments will be deprecated, so the code above will throw an error message. To get around this error, you can use the double splat operator (**) to convert a hash to keyword arguments:

person_attributes = { name: "Sophie", age: 26 }
sophie = Person.new(**person_attributes)

Conclusion

Using keyword arguments makes our Ruby methods more flexible by allowing arguments to be passed in any order, and by allowing our methods to receive a hash of key-value pairs as an argument.

Resources

phase-3-metaprogramming-keyword-arguments-and-mass-assignment's People

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

phase-3-metaprogramming-keyword-arguments-and-mass-assignment's Issues

Using The Double Splat Operator Results In An Error Within Ruby 2.7

Canvas Link

https://learning.flatironschool.com/courses/5286/pages/keyword-arguments?module_item_id=376697

Concern

The following code results in an error:

Related Code:

def print_name_and_greeting(greeting, name)
  puts "#{greeting}, #{name}"
end

def happy_birthday(name, current_age)
  puts "Happy Birthday, #{name}"
  current_age += 1
  puts "You are now #{current_age} years old"
end

# NOTE: This is just to show that we use 'keyword arguments' with values ahead of time:
def happy_birthday_with_values(name: "Beyonce", current_age: 31)
  puts "Happy Birthday, #{name}"
  current_age += 1
  puts "You are now #{current_age} years old"
end

# NOTE: This is utilizing 'keyword arguments' in this scenario:
def happy_birthday_refactored(name:, current_age:)
  puts "Happy Birthday, #{name}"
  current_age += 1
  puts "You are now #{current_age} years old"
end

class Person
  attr_accessor :name, :age

  def initialize(name, age)
    @name = name
    @age = age
  end
end

class PersonRefactored
  attr_accessor :name, :age

  # NOTE: By making the small change to include keyword arguments, the user can specify
  # the related values of these two variables in any order they wish:
  def initialize(name:, age:)
    @name = name
    @age = age
  end

end

print_name_and_greeting("'sup", "Hillary")

happy_birthday("Beyonce", 31)

# Example of calling the refactored method above with keyword arguments in different orders:
happy_birthday_refactored(current_age: 31, name: "Carmelo Anthony")

# Example of using 'Mass Assignment' using the 'PersonRefactored' class above:
person_attributes = { name: "Sophie", age: 26}

# NOTE: We have to use the 'double splat' operator in order to use convert a hash into keyword arguments:
sophie = Person.new(**person_attributes)

puts "sophie: ", sophie

Error:

samuelbanya@Samuels-MBP ~/hub/Development/code/phase-3/phase-3-metaprogramming-keyword-arguments-and-mass-assignment $ ruby run.rb 
'sup, Hillary
Happy Birthday, Beyonce
You are now 32 years old
Happy Birthday, Carmelo Anthony
You are now 32 years old
Traceback (most recent call last):
        2: from run.rb:57:in `<main>'
        1: from run.rb:57:in `new'
run.rb:28:in `initialize': wrong number of arguments (given 1, expected 2) (ArgumentError)

Additional Context

No response

Suggested Changes

The use of the double splat operator does not appear to be the solution in this lesson.

Related StackOverflow Post:

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.