Skip to content

Commit

Permalink
[New Concept]: Symbols (#1560)
Browse files Browse the repository at this point in the history
* Add symbols

* Add design.md

* Fixes

* Fix prerequisites

* Fix test file

* Update exercises/concept/port-palermo/.docs/introduction.md

Co-authored-by: Victor Goff <[email protected]>

* Update concepts/symbols/introduction.md

Co-authored-by: Victor Goff <[email protected]>

* Update concepts/symbols/about.md

Co-authored-by: Victor Goff <[email protected]>

* Update concepts/symbols/about.md

Co-authored-by: Victor Goff <[email protected]>

* Update concepts/symbols/about.md

Co-authored-by: Victor Goff <[email protected]>

* Update concepts/symbols/about.md

Co-authored-by: Victor Goff <[email protected]>

* Update concepts/symbols/about.md

Co-authored-by: Victor Goff <[email protected]>

* formulation improvements

* Update blurb

---------

Co-authored-by: Victor Goff <[email protected]>
  • Loading branch information
meatball133 and kotp authored Aug 9, 2023
1 parent 3312f62 commit 4d26aaf
Show file tree
Hide file tree
Showing 13 changed files with 408 additions and 0 deletions.
5 changes: 5 additions & 0 deletions concepts/symbols/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"blurb": "Symbols are very important in Ruby. They are used as \"identity\". They are immutable, guaranteed to be a single item no matter where it is used or referenced. They are used internally as variable, method, constants, and keyword identifiers. They are also useful, and often used, as keys in a `Hash`.",
"authors": ["meatball133"],
"contributors": ["kotp"]
}
80 changes: 80 additions & 0 deletions concepts/symbols/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# About

[Symbols][symbols] are named identifiers that can be used to refer to a value.
Symbols are created through a symbol literal, which is by prefixing a name with a `:` character, e.g. `:foo`.
They also allow for being written with quotes, e.g. `:"foo"`, which allows, for example, spaces in the name.

```ruby
:foo # => :foo
:"foo boo" # => :"foo boo"
```

Symbols are used in many places in the language, including as keys in hashes, to represent method names and variable names.

## Identifier

What makes symbols different from strings is that they are identifiers, and do not represent data or text.
This means that two symbols with the same name are always the same object.

```ruby
"foo".object_id # => 60
"foo".object_id # => 80
:foo.object_id # => 1086748
:foo.object_id # => 1086748
```

## Modifying Symbols

Symbols are immutable, which means that they cannot be modified.
This means that when you "modify" a symbol, you are actually creating a new symbol.
There are a few methods that can be used to manipulate symbols, they all return new symbols.
All methods can be found in the [Symbol API][symbols-api].

```ruby
:foo.upcase # => :FOO

:foo.object_id # => 1086748
:foo.upcase.object_id # => 60
```

The benefit of symbols being immutable is that they are more memory efficient than strings, but also safer to use as identifiers.

## Conversion

Symbols can be converted to strings and vice versa.
This can be useful when you want to modify a symbol, or when you want to use a symbol as a string.
To present a symbol as a string, you can use the `String#to_sym` method, and to do the opposite, you can use the `Symbol#to_s` method.
Due to symbols having a limited set of methods, it can be useful to convert a symbol to a string to use string methods on it, if a new symbol is needed.

```ruby
:foo.to_s # => "foo"
"foo".to_sym # => :foo
```

## Getting names in scopes

In Ruby you can get names in scopes by using various methods to get names of constants, methods, and variables.
These methods returns arrays of symbols.

Some methods are `Module#constants`, `Module#instance_methods`, `Module#class_variables`, `global_variables` and `local_variables`.

```ruby
module Foo
BAR = 1
def self.baz; end
end

a = 1

Foo.constants # => [:BAR]
Foo.instance_methods # => [:baz]

local_variables # => [:a]
```

All methods can be found in the [Kernel API][kernal-api] and [Module API][module-api].

[symbols]: https://www.rubyguides.com/2018/02/ruby-symbols/
[symbols-api]: https://rubyapi.org/o/symbol
[kernal-api]: https://rubyapi.org/o/kernel
[module-api]: https://rubyapi.org/o/module
55 changes: 55 additions & 0 deletions concepts/symbols/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# About

[Symbols][symbols] are named identifiers that can be used to refer to a value.
Symbols are created through a symbol literal, which is by prefixing a name with a `:` character, e.g. `:foo`.
They also allow for being written with quotes, e.g. `:"foo"`, which allows, for example, spaces in the name.

```ruby
:foo # => :foo
:"foo boo" # => :"foo boo"
```

Symbols are used in many places in the language, including as keys in hashes, to represent method names and variable names.

## Identifier

What makes symbols different from strings is that they are identifiers, and do not represent data or text.
This means that two symbols with the same name are always the same object.

```ruby
"foo".object_id # => 60
"foo".object_id # => 80
:foo.object_id # => 1086748
:foo.object_id # => 1086748
```

## Modifying Symbols

Symbols are immutable, which means that they cannot be modified.
This means that when you "modify" a symbol, you are actually creating a new symbol.
There are a few methods that can be used to manipulate symbols, they all return new symbols.
All methods can be found in the [Symbol API][symbols-api].

```ruby
:foo.upcase # => :FOO

:foo.object_id # => 1086748
:foo.upcase.object_id # => 60
```

The benefit of symbols being immutable is that they are more memory efficient than strings, but also safer to use as identifiers.

## Conversion

Symbols can be converted to strings and vice versa.
This can be useful when you want to modify a symbol, or when you want to use a symbol as a string.
To present a symbol as a string, you can use the `String#to_sym` method, and to do the opposite, you can use the `Symbol#to_s` method.
Due to symbols having a limited set of methods, it can be useful to convert a symbol to a string to use string methods on it, if a new symbol is needed.

```ruby
:foo.to_s # => "foo"
"foo".to_sym # => :foo
```

[symbols]: https://www.rubyguides.com/2018/02/ruby-symbols/
[symbols-api]: https://rubyapi.org/o/symbol
10 changes: 10 additions & 0 deletions concepts/symbols/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"url": "https://www.rubyguides.com/2018/02/ruby-symbols/",
"description": "Ruby Guides: Ruby Symbols"
},
{
"url": "https://rubyapi.org/symbol/",
"description": "Ruby API: Symbol"
}
]
17 changes: 17 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,17 @@
"conditionals"
]
},
{
"slug": "port-palermo",
"name": "Port Of Palermo",
"uuid": "d852aa08-f712-4c83-b19a-9adef4318b5e",
"concepts": [
"symbols"
],
"prerequisites": [
"conditionals"
]
},
{
"slug": "bird-count",
"name": "Bird Count",
Expand All @@ -118,6 +129,7 @@
"prerequisites": [
"instance-variables",
"booleans",
"symbols",
"conditionals"
]
},
Expand Down Expand Up @@ -1639,6 +1651,11 @@
"slug": "strings",
"name": "Strings"
},
{
"uuid": "4b0f3718-cc06-4aa6-8b0f-7db3bbe6af5d",
"slug": "symbols",
"name": "Symbols"
},
{
"uuid": "0189a402-ed46-47ee-9f5b-cfa5f557720d",
"slug": "enumeration",
Expand Down
24 changes: 24 additions & 0 deletions exercises/concept/port-palermo/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Hints

## 1. Create the ports identifier

- You need to define a [constant][constants] that should contain the symbol value specified in the instructions.

## 2. Get identifiers for other ports

- A string can be [slice][slice] to get parts of it.
- `Strings` have a [method][upcase] to upcase the letters.
- `Strings` have a [method][to_sym] to convert a string to a symbol.

## 3. Determine which terminal a ship should dock at

- Symbol has a [method][to_s] to convert a symbol to a string.
- A string can be [slice][slice] to get parts of it.
- Use conditional [`if`][doc-if], to check if the ship is carrying `"OIL"` or `"GAS"`.

[doc-if]: https://ruby-doc.org/core/syntax/control_expressions_rdoc.html#label-if+Expression
[constants]: https://www.rubyguides.com/2017/07/ruby-constants/
[upcase]: https://ruby-doc.org/core/String.html#method-i-upcase
[slice]: https://ruby-doc.org/core/String.html#class-String-label-String+Slices
[to_s]: https://rubyapi.org/symbol#method-i-to_s
[to_sym]: https://rubyapi.org/string#method-i-to_sym
51 changes: 51 additions & 0 deletions exercises/concept/port-palermo/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Instructions

The port of Palermo is a busy harbor, with a lot of ships coming and going.
It has for a long time had a manual system for keeping track of the ships that are currently in the harbor.
This system is very error prone, and the harbor chief has decided to replace it with a computerized system.

The harbor chief has hired you to implement the new system.
The system has to handle identifiers for ships, but also for destinations.

## 1. Create the ports identifier

The first thing you need to do is to create the identifier for the port of Palermo.
The identifier are the first four letters of the name of the port, in uppercase.

Define the `Port.Identifier` constant to be a symbol with the value `:PALE`.

```ruby
Port::Identifier
# => :PALE
```

## 2. Get identifiers for other ports

The program will also have to handle other ports, so you need to create identifiers for them as well.
The port would like the system to be automated and since ports uses different naming conventions, the identifiers should be generated from the name of the port.
The identifier are the first four letters of the name of the port, in uppercase.

Define the `Port.get_identifier` method to take a string as an argument, and returns the identifier as a symbol for that port.

```ruby
Port.get_identifier("Hamburg")
# => :HAMB
```

## 3. Determine which terminal a ship should dock at

The port has two terminals, and the system needs to determine which terminal a ship should dock at.
The terminal is determined by the identifier of the ship.

The identifier is built of 2 parts, the first part is made of 3 uppercased letters which says which cargo the ship is carrying, and then 3 numbers which is the unique identifier of the ship.

If the identifier of the ship is carrying: **OIL** or **GAS** the ship should dock at terminal **A**.
Else the ship should dock at terminal **B**.

Define the `Port.get_terminal` method to take a symbol as an argument which is the identifier of the ship.
The method should return the terminal as a symbol.

```ruby
Port.get_terminal(:OIL123)
# => :A
```
55 changes: 55 additions & 0 deletions exercises/concept/port-palermo/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# About

[Symbols][symbols] are named identifiers that can be used to refer to a value.
Symbols are created through a symbol literal, which is by prefixing a name with a `:` character, e.g. `:foo`.
They also allow for being written with quotes, e.g. `:"foo"`, which allows, for example, spaces in the name.

```ruby
:foo # => :foo
:"foo boo" # => :"foo boo"
```

Symbols are used in many places in the language, including as keys in hashes, to represent method names and variable names.

## Identifier

What makes symbols different from strings is that they are identifiers, and do not represent data or text.
This means that two symbols with the same name are always the same object.

```ruby
"foo".object_id # => 60
"foo".object_id # => 80
:foo.object_id # => 1086748
:foo.object_id # => 1086748
```

## Modifying Symbols

Symbols are immutable, which means that they cannot be modified.
This means that when you "modify" a symbol, you are actually creating a new symbol.
There are a few methods that can be used to manipulate symbols, they all return new symbols.
All methods can be found in the [Symbol API][symbols-api].

```ruby
:foo.upcase # => :FOO

:foo.object_id # => 1086748
:foo.upcase.object_id # => 60
```

The benefit of symbols being immutable is that they are more memory efficient than strings, but also safer to use as identifiers.

## Conversion

Symbols can be converted to strings and vice versa.
This can be useful when you want to modify a symbol, or when you want to use a symbol as a string.
To present a symbol as a string, you can use the `String#to_sym` method, and to do the opposite, you can use the `Symbol#to_s` method.
Due to symbols having a limited set of methods, it can be useful to convert a symbol to a string to use string methods on it, if a new symbol is needed.

```ruby
:foo.to_s # => "foo"
"foo".to_sym # => :foo
```

[symbols]: https://www.rubyguides.com/2018/02/ruby-symbols/
[symbols-api]: https://rubyapi.org/o/symbol
18 changes: 18 additions & 0 deletions exercises/concept/port-palermo/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"authors": [
"meatball133"
],
"files": {
"solution": [
"port_palermo.rb"
],
"test": [
"port_palermo_test.rb"
],
"exemplar": [
".meta/exemplar.rb"
]
},
"icon": "strings-package",
"blurb": "Learn about the symbols while helping out with modernize the ports of Palermo computer system."
}
34 changes: 34 additions & 0 deletions exercises/concept/port-palermo/.meta/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Design

## Goal

The goal of this exercise is to teach the student the basics of the Concept of Symbols in Ruby.

## Learning objectives

- Know of the existence of the `Symbol` object.
- Know how to create a symbol.
- Know of some basic symbol methods (mostly conversion methods).

## Out of scope

- Hashes.

## Concepts

- `Symbols`

## Prerequisites

- `Conditionals`

## Representer

This exercise does not require any specific representation logic to be added to the [representer][representer].

## Analyzer

This exercise does not require any specific logic to be added to the [analyzer][analyzer].

[analyzer]: https://github.com/exercism/ruby-analyzer
[representer]: https://github.com/exercism/ruby-representer
12 changes: 12 additions & 0 deletions exercises/concept/port-palermo/.meta/exemplar.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module Port
Identifier = :PALE

def self.get_identifier(city)
return city[0..3].upcase.to_sym
end

def self.get_terminal(ship_identifier)
cargo = ship_identifier.to_s[0..2]
return cargo == "OIL" || cargo == "GAS" ? :A : :B
end
end
Loading

0 comments on commit 4d26aaf

Please sign in to comment.