Is there any function for get an array value from string in ruby except eval?

I have a string. It will be "1" or "['1','2','3','4']". By using eval method i can get the result 1 or [1,2,3,4] but i need the result should be array [1] or [1,2,3,4].

I have a string. It will be "1" or "['1','2','3','4']". By using eval method i can get the result 1 or [1,2,3,4] but i need the result should be array [1] or [1,2,3,4].

params[:city_id] = eval(params[:city_id]) #params will be "1" or "['1','2','3']"

scope :city, -> (params) { params[:city_id].present? ? where(city_id: (params[:city_id].is_a?(String) ? eval(params[:city_id]) : params[:city_id])) : all }

Here i don't want eval.

scope :city, -> (params) { params[:city_id].present? ? where(city_id: params[:city_id]) : all }

params[:city_id] #should be array values e.g [1] or [1,2,3,4] instead of string


Functional Programming: What is it? What it like? Does it work?

Functional Programming: What is it? What it like? Does it work?

What Is Functional Programming? In this talk Russ Olsen will strip away the cloud of mystery to uncover the simple — and wonderful — truth about functional programming

What Is Functional Programming?

Functional programming has finally escaped from academia. These days developers are building real systems in functional programming languages like Clojure, Scala, Elixir and F#. Functional techniques are also seeping into more traditional languages like Java and Ruby. Unfortunately somewhere along the way functional programming has also developed a reputation for being deep and mysterious: Good programs achieve the Zen-like state of being functional which somehow involves immutability, higher order functions and being referentially transparent.

In this talk Russ Olsen will strip away the cloud of mystery to uncover the simple — and wonderful — truth about functional programming: It can make your programming life easier by letting you do simple things simply while also providing you with the sharp tools you need to tackle more complex problems.

Speaker: Russ Olsen - Author of Getting Clojure and Eloquent Ruby, VP at Cognitect

5 Reasons to Use Protocol Buffers Instead of JSON

5 Reasons to Use Protocol Buffers Instead of JSON

Where browsers and JavaScript are not consuming the data directly – particularly in the case of internal services – it’s my opinion that structured formats, such as Google’s Protocol Buffers, are a better choice than JSON for encoding data.

Service-Oriented Architecture has a well-deserved reputation amongst Ruby and Rails developers as a solid approach to easing painful growth by extracting concerns from large applications. These new, smaller services typically still use Rails or Sinatra, and use JSON to communicate over HTTP. Though JSON has many obvious advantages as a data interchange format – it is human readable, well understood, and typically performs well – it also has its issues.

Where browsers and JavaScript are not consuming the data directly – particularly in the case of internal services – it’s my opinion that structured formats, such as Google’s Protocol Buffers, are a better choice than JSON for encoding data. If you’ve never seen Protocol Buffers before, you can check out some more information here, but don’t worry – I’ll give you a brief introduction to using them in Ruby before listing the reasons why you should consider choosing Protocol Buffers over JSON for your next service.
This is image title

A Brief Introduction to Protocol Buffers

First of all, what are Protocol Buffers? The docs say:

  • “Protocol Buffers are a way of encoding structured data in an efficient yet extensible format.”

Google developed Protocol Buffers for use in their internal services. It is a binary encoding format that allows you to specify a schema for your data using a specification language, like so:

message Person {
  required int32 id = 1;
  required string name = 2;
  optional string email = 3;
}

You can package messages within namespaces or declare them at the top level as above. The snippet defines the schema for a Person data type that has three fields: id, name, and email. In addition to naming a field, you can provide a type that will determine how the data is encoded and sent over the wire – above we see an int32 type and a string type. Keywords for validation and structure are also provided (required and optional above), and fields are numbered, which aids in backward compatibility, which I’ll cover in more detail below.

The Protocol Buffers specification is implemented in various languages: Java, C, Go, etc. are all supported, and most modern languages have an implementation if you look around. Ruby is no exception and there are a few different Gems that can be used to encode and decode data using Protocol Buffers. What this means is that one spec can be used to transfer data between systems regardless of their implementation language.

For example, installing the ruby-protocol-buffers Ruby Gem installs a binary called ruby-protoc that can be used in combination with the main Protocol Buffers library (brew install protobuf on OSX) to automatically generate stub class files that are used to encode and decode your data for you. Running the binary against the proto file above yields the following Ruby class:

#!/usr/bin/env ruby
# Generated by the protocol buffer compiler. DO NOT EDIT!
require 'protocol_buffers'

# forward declarations
class Person < ::ProtocolBuffers::Message; end

class Person < ::ProtocolBuffers::Message
  set_fully_qualified_name "Person"

  required :int32, :id, 1
  required :string, :name, 2
  optional :string, :email, 3
end

As you can see, by providing a schema, we now automatically get a class that can be used to encode and decode messages into Protocol Buffer format (inspect the code of the ProtocolBuffers::Message base class in the Gem for more details). Now that we’ve seen a bit of an overview, let’s dive in to the specifics a bit more as I try to convince you to consider taking a look at Protocol Buffers – here are five reasons to start.

Reason #1: Schemas Are Awesome

There is a certain painful irony to the fact that we carefully craft our data models inside our databases, maintain layers of code to keep these data models in check, and then allow all of that forethought to fly out the window when we want to send that data over the wire to another service. All too often we rely on inconsistent code at the boundaries between our systems that don’t enforce the structural components of our data that are so important. Encoding the semantics of your business objects once, inprotoformat, is enough to help ensure that the signal doesn’t get lost between applications, and that the boundaries you create enforce your business rules.

Reason #2: Backward Compatibility For Free

Numbered fields in proto definitions obviate the need for version checks which is one of the explicitly stated motivations for the design and implementation of Protocol Buffers. As the developer documentation states, the protocol was designed in part to avoid “ugly code” like this for checking protocol versions:

if (version == 3) {
  ...
} else if (version > 4) {
  if (version == 5) {
    ...
  }
  ...
}

With numbered fields, you never have to change the behavior of code going forward to maintain backward compatibility with older versions. As the documentation states, once Protocol Buffers were introduced:

  • “New fields could be easily introduced, and intermediate servers that didn’t need to inspect the data could simply parse it and pass through the data without needing to know about all the fields.”

Having deployed multiple JSON services that have suffered from problems relating to evolving schemas and backward compatibility, I am now a big believer in how numbered fields can prevent errors and make rolling out new features and services simpler.

Reason #3: Less Boilerplate Code

In addition to explicit version checks and the lack of backward compatibility, JSON endpoints in HTTP based services typically rely on hand-written ad-hoc boilerplate code to handle the encoding and decoding of Ruby objects to and from JSON. Parser and Presenter classes often contain hidden business logic and expose the fragile nature of hand parsing each new data type when a stub class as generated by Protocol Buffers (that you generally never have to touch) can provide much of the same functionality without all of the headaches. As your schema evolves so too will your proto generated classes (once you regenerate them, admittedly), leaving more room for you to focus on the challenges of keeping your application going and building your product.

Reason #4: Validations and Extensibility

The required, optional, and repeated keywords in Protocol Buffers definitions are extremely powerful. They allow you to encode, at the schema level, the shape of your data structure, and the implementation details of how classes work in each language are handled for you. The Ruby protocol_buffers library will raise exceptions, for example, if you try to encode an object instance which does not have the required fields filled in. You can also always change a field from being required to being optional or vice-versa by simply rolling to a new numbered field for that value. Having this kind of flexibility encoded into the semantics of the serialization format is incredibly powerful

Since you can also embed proto definitions inside others, you can also have generic Request and Response structures which allow for the transport of other data structures over the wire, creating an opportunity for truly flexible and safe data transfer between services. Database systems like Riak use protocol buffers to great effect – I recommend checking out their interface for some inspiration.

Reason #5: Easy Language Interoperability

Because Protocol Buffers are implemented in a variety of languages, they make interoperability between polyglot applications in your architecture that much simpler. If you’re introducing a new service with one in Java or Go, or even communicating with a backend written in Node, or Clojure, or Scala, you simply have to hand the proto file to the code generator written in the target language and you have some nice guarantees about the safety and interoperability between those architectures. The finer points of platform specific data types should be handled for you in the target language implementation, and you can get back to focusing on the hard parts of your problem instead of matching up fields and data types in your ad hoc JSON encoding and decoding schemes.

When Is JSON A Better Fit?

There do remain times when JSON is a better fit than something like Protocol Buffers, including situations where:

  • You need or want data to be human readable
  • Data from the service is directly consumed by a web browser
  • Your server side application is written in JavaScript
  • You aren’t prepared to tie the data model to a schema
  • You don’t have the bandwidth to add another tool to your arsenal
  • The operational burden of running a different kind of network service is too great

And probably lots more. In the end, as always, it’s very important to keep tradeoffs in mind and blindly choosing one technology over another won’t get you anywhere.

Conclusion

Protocol Buffers offer several compelling advantages over JSON for sending data over the wire between internal services. While not a wholesale replacement for JSON, especially for services which are directly consumed by a web browser, Protocol Buffers offers very real advantages not only in the ways outlined above, but also typically in terms of speed of encoding and decoding, size of the data on the wire, and more.

What are some services you could extract from your monolithic application now? Would you choose JSON or Protocol Buffers if you had to do it today? We’d love to hear more about your experiences with either protocol in the comments below – let’s get discussing!

Validate a Credit Card Number with Javascript, Ruby, and C

Validate a Credit Card Number with Javascript, Ruby, and C

This post is primarily a comparison between a lower level language vs a higher level language. If you would like to see how I implemented the credit card checker, check out my code in C, Ruby, or Javascript

Credit card companies are responsible for a high volume of highly sensitive global network traffic per minute with no margin for error. These companies need to ensure they are not wasting resources processing unnecessary requests. When a credit card is run, the processor has to look up the account to ensure it exists, then query the balance to ensure the amount requested is available. While an individual transaction is cheap and small, the scales involved are enormous.

When it comes to programming, each language that I have encountered comes with its unique quirks and virtues. I wanted to compare the difference in syntax of several common languages by writing a credit card checker. The goal of this application is to accept an input of a credit card number and then to identify if a credit card number is syntactically valid. This post is primarily a comparison between a lower level language vs a higher level language. If you would like to see how I implemented the credit card checker, check out my code in C, Ruby, or Javascript here.

This is image title

Credit Card Payment Method

Most of us have encountered this screen when trying to make a payment for an online purchase. Usually at the front end, Javascript would handle the validation to check if the credit card is a valid card before a call is sent to the servers. The process of validation checking is based on a checksum algorithm created by Hans Peter Luhn. Here’s a simple break down on Luhn’s algorithm.

Luhn’s checksum algorithm

Multiply every other digit by 2, starting with the number’s second-to-last digit, and then add those products’ digits together.

Add the sum to the sum of the digits that weren’t multiplied by 2.

If the total’s last digit is 0 then the number is valid!

Take for example the following American Express number, 378734493671000. Starting from the second-to-last-digit, multiply the last number by 2.

72 + 72 + 42 + 92 + 62 + 12 + 0*2

The result:

14 + 14 + 8 + 18 +12 + 2 + 0

Adding the product digits:

1 + 4 + 1 + 4 + 8 + 1 + 8 +1 + 2 + 2 + 0 = 32

Finally add the digits that were not multiplied to the sum

32 + 3 + 8 + 3 + 4 + 3 + 7 + 0 + 0 = 60

The checksum 60 ends with the number 0, therefore it a syntactically sound credit card number

Identifying Credit Card Types

Aside from the checksum, credit card number numbers also identify the type of credit card company. Visa cards start with the number 4. MasterCards start with the number 51, 52, 53, 54, or 55. American Express starts with the number 34 or 37.

The Big Picture

The solution can be broken down into two parts:

  1. Check if the card number is valid.
  2. Identify the type of credit card.

Let’s take a look at the syntax for C and walkthrough the code.

Card Length Validation

To check if the card number is valid, there is a preliminary check that we can check for before calculating the checksum. We know that a credit card number can only be either be 13, 15, or 16. We can do that with a simple while or for loop.

The user’s card number is stored in the variable card number and on each iteration of the length of the number the last digit will be removed and counted. The count of the length of the number will then be checked with if it is either 13, 15, or 16 digits.

Javascript and Ruby both have higher order functions that simplify the process of determining the length of the variable. Essentially, under the hood of the method or function length a similar process is being utilized.

Checksum Validation

After passing the first test, the next step would be to see if the checksum is valid. Again we, will take a look at the syntax in C.

In this example, the array number is declared and the card numbers are enumerated through and each digit is saved in the array number. The digits that are stored in the array using this method is reversed from the original card number because of the operation of removing the last digit first and storing it in the first index.

Take for example the credit card number is 4012 8888 8888 1881. Using the modulo and divide by 10 method to store the array the resulting array would be [1,8,8,1,8,8,8,8,8,8,8,8,2,1,0,4].

In Javascript, if using a higher order function to convert a number, the number needs to be converted to a string first before using the higher function to convert to an array.

It can be noted that this concept of converting to a specific data type is also similar when using Ruby methods to convert to an array. Ruby and Javascript are similar in that they both require the data to be a certain type and often require coercing the data into a usable type before a higher order function can be used to operate on the data type. You can notice that in my example of Javascript (above)and Ruby (below) that the integers is converted into a string and created into an array and then mapped back into integers.

The checksum for C was cleaner to implement for this reason that there were less type conversions needed to manipulate the data. The array used simple for loops and conditional statements to validate the number.

In the C code above, a new array was created to clone the number array and starting from the second-to-last value the value was multiplied by 2.

The bulk of the validation occurs in this nested if statement. First the length of the card number is determined. Then the arrays digits are added up and checked if the checksum is valid in this one line.

sumdigit = (number[i] % 10) + (number[i]/10 % 10);

The type of card is validated by checking the first and second index of the array. In this example, Visa cards start with the number 4.

cardarray[12] == 4 && accumulator % 10 == 0

Implementing the validator in ruby and javascript by using higher order functions made it fairly verbose. It would definitely be possible to use the same algorithm for the C program in Javascript and Ruby. However, I wanted to utilize the higher order functions of the language.

To split the array in C, the card number was cloned and multiplied using two for loops. In Javascript, I found that I could use the filter function to separate the initial array into two arrays of every other digits and then a simple map over the first array to double the digits.

The array that has been multiplied by 2 is then summed and added into the array that has not being multiplied using the reduce method. If the checksum passes, then the first two digits of the card array is sliced to check what type of card. The implementation of the card type check is similar to the C syntax. By using a conditional statement the digits of the card array is then evaluated for each type.

Concluding Thoughts

This post is more of a self reflection on the differences in programming in a lower level language and a higher level language. In attempting to create a credit card checker, I have found that the lower level language syntax to be more concise in getting to the solution, whereas using the higher level language requires data type conversions to use the higher order functions.

I’m still learning more about code every day. I would love to hear from you, if you have any tips or suggestions.

Thank you !

Build a CRUD Web App with Ruby on Rails

Build a CRUD Web App with Ruby on Rails

If you are fairly new to Ruby on Rails (or forgot how to use it) and need a quick guide to creating a CRUD (create, read, update, destroy) web application, then you’ve come to the right place!

If you are fairly new to Ruby on Rails (or forgot how to use it) and need a quick guide to creating a CRUD (create, read, update, destroy) web application, then you’ve come to the right place!

Below I have written some simple steps to creating a very simple application, linked to some documentation or a tutorial for each step, and included an example of what you might write for each step.

Here’s a nice, simple tutorial that gives some more context and explanation and walks you through these steps. This guide is also fairly easy to follow, much longer, and more detailed.

  1. Create a new app
rails new pets-app

  1. Launch a server and go to http://localhost:3000 in your browser to view your app
rails s

  1. Create models
rails g model owner first_name last_name age:integer email
rails g model pet name species owner_id:integer owner:belongs_to

  1. Set up associations
class Owner < ApplicationRecord
  has_many :pets
end

  1. Add validations
class Owner < ApplicationRecord
  has_many :pets
  validates :name, presence: true
end

  1. Run migrations
rails db:migrate

  1. Check your schema by looking at the db/schema.rb file

  2. Seed your database

Owner.create!(first_name:"Dan", last_name:"Weirdo", age: 18, email:"[email protected]")
Pet.create!(name:"Snowball", species:"dog", owner_id:1)

  1. Test associations
Owner.first.pets
Pet.first.owner

  1. Test validations
owner = Owner.new(first_name: "")
owner.valid?
owner.errors.messages

  1. Create controllers and views
rails g controller owners index show new create edit update destroy
rails g controller pets index show new create edit update destroy

  1. Create routes using resources or manual routes
resources :pets
resources :owners

  1. Make sure your routes are working correctly by running your server

(rails s) and looking at each path that you created (i.e. /pets)

  1. Add/edit controller actions

Hint: scroll to the bottom for an example of standard controller actions

  1. Add/edit view files

Hint: “find on page” “new.html”, “index.html”, etc. to see examples

Another hint: the form_for helper

That’s it! Make sure you test your code as you go, and commit often.

Ruby 2.7 — Pattern Matching 

Ruby 2.7 — Pattern Matching 

Shall we grab a sneak peak at what Pattern Matching looks like in Ruby 2.7? The code’s merged, nightly build is on the way, and I’m a rather impatient writer who wants to check out the new presents.

Ruby 2.7 — Pattern Matching — First Impressions

Shall we grab a sneak peak at what Pattern Matching looks like in Ruby 2.7? The code’s merged, nightly build is on the way, and I’m a rather impatient writer who wants to check out the new presents.

This is my first pass over pattern matching, and there will be more detailed articles coming out as I have more time to experiment with the features.

To be clear: This article will meander as it’s a first impression. I’ll be running more tests on it tomorrow, but wanted to write something on it tonight to see how well first impressions matched up.

The Short Version

This is going to be a long article, and the short version isn’t going to cover even a small portion of it. Each major section will have a single style it covers.

The test file covers a good portion of this if you want an at-a-glance read

Literal Matches

Much like a regular case statement, you can perform literal matches:

case 0
in 0 then true
else false
end
# => true

In these cases it would probably be best to use when instead.

The difference is that if there’s no match it’s going to raise a <a href="https://github.com/ruby/ruby/blob/9738f96fcfe50b2a605e350bdd40bd7a85665f54/test/ruby/test_pattern_matching.rb#L83" target="_blank">NoMatchingPattern</a> error rather than returning nil like case / when statements would.

Multiple Matchers

In typical case statements, a comma would be used, but with some of the new syntax this would break some of the destructuring that will be mentioned later:

case 0
in 0 | 1
  true
end
# => true

In the case of captured variables below, they can’t be mixed:

case 0
in a | 0
end
# Error: illegal variable in alternative pattern

Captured Variables

One of the more interesting additions is captured variables:

case true
in a
  a
end
# => true

In Guard Clauses

These can also be used in suffix conditionals or rather guard type clauses in what’s reminiscent of Haskell:

case 0
in a if a == 0
  true
end
# => true

In Assignment Mappings

They can even be assigned with a Hash-like syntax:

case 0
in 0 => a
  a == 0
end

With Placeholders

Most interesting in this section is that they appear to treat underscores as a special placeholder:

case 0
in _ | _a
  true
end

Though this could also be seen as a literal variable assignment, it’s an interesting riff on Scala’s pattern matching wildcards.

Destructuring

Interestingly you can shadow the variables as well while destructuring an array:

case [0, 1]
in a, a
  a == 1
end
# => true

It appears that the last assignment will be a in this case, and that commas are now used for destructuring rather than for denoting a separate pattern to match against as is the case for when.

Deconstruct

In the top of the file there’s a class defined:

class C
  class << self
    attr_accessor :keys
  end
  def initialize(obj)
    @obj = obj
  end
  def deconstruct
    @obj
  end
  def deconstruct_keys(keys)
    C.keys = keys
    @obj
  end
end

It appears to indicate a way to define how an object should be destructured by pattern matching:

[[0, 1], C.new([0, 1])].all? do |i|
  case i
  in 0, 1
    true
  end
end

including accounting for unbalanced matches:

[[0], C.new([0])].all? do |i|
  case i
  in 0, 1
  else
    true
  end
end

I don’t understand how deconstruct_keys is working, but it feels really odd to me from first glance.

With Splats

It also will capture multiple arguments like destructuring currently works on Ruby arrays:

case []
in *a
  a == []
end
case [0, 1, 2]
in *a, 1, 2
  a == [0]
end

Meaning that * could also be used as a bit of a wildcard:

case 0
in *
  true
end

On Hashes

These appear to work a lot like keyword arguments:

case {a: 0}
in a: 0
  true
end

That also means that kwsplats could be used here:

case {}
in **a
  a == {}
end

…and with the keywords bit above, that means that classes could define their own destructuring using keywords, but I’m not clear on that.

I’m really hoping that this also uses === on each param, but the tests don’t indicate this. I’ll be experimenting with this later.

Triple Equals

Like our current case statements, it appears that the new method of pattern matching uses <a href="https://github.com/ruby/ruby/blob/9738f96fcfe50b2a605e350bdd40bd7a85665f54/test/ruby/test_pattern_matching.rb#L255" target="_blank">===</a> for comparisons:

case 'abc'
in /a/
  true
end
case 0
in -> i { i == 0 }
  true
end

Combined with Destructuring

One thing I enjoyed in Qo was the ability to match against another array using === to write queries. It looks like the new syntax will compare each element and give us something very similar:

case [0, 1, 2, 3, 4, 5]
in [0..1, 0...2, 0.., 0..., (...5), (..5)]
  true
end

It’s amusing that they’ve included beginningless and endless ranges in the example, as that could be very useful later on. I certainly hope these work with hashes, as I really really really want this to work:

case object
in method_a: 0..10, method_b: String
  true
end

…because if we can define our own deconstructors just imagine the possibilities there. The class attr for keys is odd though, not sure what to think of that one.

Pin Operator

I’m going to have to come back to this one as I do not understand it. I assume it means not to assign and to “pin” a variable so it doesn’t get overwritten:

a = /a/
case 'abc'
in ^a
  true
end
case [0, 0]
in a, ^a
  a == 0
end

I believe this can be used to capture one element in a variable and also reference it later like a back-reference of sorts.

Thoughts?

This hasn’t built with Nightly yet, so I intend to make a second pass on this after it clears to verify some behavior I have suspicions about. Mostly I want to see about the hash matchers, as there’s some amazing potential there.

Ruby 2.7 — Pattern Matching — Destructuring on Point

Now that pattern matching has hit Ruby Nightly as an experimental feature, let’s take a look into some potential usecases for it starting with Destructuring.

This article will be a bit more structured.

Testing Warning!

Be sure to remember that variable assignment destructuring assigns local variables. This will mess with your testing unless you do it in methods instead of in a direct Pry or IRB session. We’ll dig into this more later in the article.

If something doesn’t match, it’s going to raise an error, so be sure to use else to handle default cases.

On Point!

One of the interesting things I’d noted was the ability to destructure from an object. Let’s say we have a Point that has an x and y coordinate:

Point = Struct.new(:x, :y) do
  def deconstruct
    self.to_a
  end
  
  def deconstruct_keys(keys)
    self.to_h
  end
end

We’ll use this as our base example for now.

Array Destructuring

Destructuring is a means of extracting values from an object in Ruby. You may be familiar with the left-hand style from assignment:

x, y = Point.new(0, 1).to_a
x # => 0
y # => 1
*coords = Point.new(2, 3).to_a
coords # => [2, 3] 

These are all valid in in expressions in a pattern matching context. That includes splatting values.

Direct Value

We can destructure to match directly against values:

case Point.new(0, 1)
in 0, 1 then Point.new(0, 2)
end
=> #<struct Point x=0, y=2>

These items all respond to === as we’ll see later in this article.

Triple Equals Destructuring

Anything that responds to === is perfectly fair game here:

case Point.new(0, 1)
in 0.., 1.. then Point.new(0, 2)
end

Direct Variable

If we wanted to just move north, we can use pattern matching to pull out our x and y values by position:

case Point.new(0, 1)
in x, y then Point.new(x, y + 1)
end
=> #<struct Point x=0, y=2>

So it looks like the then keyword is still valid here. Good to know.

Now something interesting is also happening here. It’s assigning local variables, meaning after that statement this works:

[x, y]
=> [0, 1]

This works with any of the variable assignment styles, and caught me a bit by surprise though it does make sense.

Triple Equals Destructuring into Variables

How about if we have some ranges?

case Point.new(0, 1)
in 0..5 => x, 0..5 => y
  Point.new(x, y + 1)
end
#<struct Point x=0, y=2>

What’s important to note here is that the format is:

value or matcher => destructured variable

These will respond to anything implementing === , which is what makes case statements so powerful in Ruby. Read this for more information on ===

Keyword Destructuring

What about keywords?

What we don’t necessarily get in Ruby is the ability to natively destructure on keywords, but with pattern matching we can if and only if deconstruct_keys is defined and returns a hash-like object like above:

def deconstruct_keys(keys)
  self.to_h
end

I’m not sure what keys are doing here, I’ll have to take a TracePoint at this to try and find out what’s going on later. If you have ideas let me know!
Considering Structs kind-of already do some of this, that’s an interesting technicality but not one we’ll worry about for now.

Keywords are not Variable Assignments

The thing to be careful of here is that the keys are used for destructuring, but not assignment:

case Point.new(0, 1)
in x: 0, y: 1..5 then Point.new(x, y + 1)
end
NameError: undefined local variable or method `x' for main:Object

Arrows are still used for Assignment

So that doesn’t work. We have to use => here to bind them to a local variable:

case Point.new(0, 1)
in x: 0 => x, y: 1..5 => y then Point.new(x, y + 1)
end
=> #<struct Point x=0, y=2>

This means we get full access to === here as well, which can be very useful.

Emulating Qo — Preview

Now I’d written a pattern matching library a while back, and I kinda want to see how it stacks up

This is a preview of some of the next article.

Let’s start with a Person:

Person = Struct.new(:name, :age) do
  def deconstruct
    self.to_a
  end
  
  def deconstruct_keys(keys)
    self.to_h
  end
end

We’ll also be using the Any gem for a wildcard

Name is Longer than 3 Characters

The Qo way:

name_longer_than_three = -> person { person.name.size > 3 }

people_with_truncated_names = people.map(&Qo.match { |m|
  m.when(name_longer_than_three) { |person|
    Person.new(person.name[0..2], person.age)
  }
  m.else
})

The Pattern Matching way:

person = Person.new('Edward', 20)
case person
in name: -> n { n.size > 3 } => name, age: Any => age
  Person.new(name[0..1], age)
else
  person
end
=> #<struct Person name="Ed", age=20>

Wrap Up

This was my first chance to play with some of pattern matching in Ruby, and I’m rather fond of it so far. There are some definite gotchas and a lot of syntax to take in, but there’s definitely a lot of power there.

There are also some very odd things like keys I don’t understand, and what happens if you try and do anything fancy in a pattern match like this:

case Point.new(0, 1)
in x: :even?.to_proc => x then Point.new(0, 0)
end
endSyntaxError: unexpected '.', expecting `then' or ';' or '\n'
  in x: :even?.to_proc => x then Point.new(0...

I believe this is likely a bug in the parser, but as this is experimental that’s to be expected.

My next few dives into pattern matching will likely follow trying to emulate various features I’d used Qo

Thanks for reading ❤

If you liked this post, share it with all of your programming buddies!