It may not be the fastest programming language, nor is it the most popular language when it comes to the numbers game, but how can you not fall in love with:
10.times { print "Hello!" }
(instead of something like for(var i=0; i<10; i++){ console.log("Hello!"); }
)
Do things with sets like:
["a", "b", "c"] & ["b", "c", "d"] # gives you ["b", "c"]
[1,2,3] + [4,5] # gives you [1,2,3,4,5]
["a", "b", "c"] - ["b", "c", "d"] # gives you ["a"]
Or (with a little (opinions differ on this one ;)) help of Rails):
10.days.ago
That offers you exactly what you would think it would return: the date of 10 days ago.
And no ;
’s, only brackets when absolutely needed, everything is an object…
Yes, it’s actually a language a human might understand, and still: it is pretty powerful, powering some of the most popular sites on the web, like AirBnB, Shopify, Basecamp (they’re the creators of Ruby on Rails), Github, Kickstarter, Twitch, Strava and many more (most of the full stack projects I did & do are using ruby).
Enjoyed this? Follow me on Mastodon or add the RSS, euh ATOM feed to your feed reader.
A lot of automation is about doing less manual labour. People who automate enjoy doing less. This desire lead to new programming languages, advanced IDE-tooling, but recently we've gotten a new type of assistance: AI. Microsoft using Github Copilot, Amazon with CodeWhisperer, and more will follow offering yet another Code predictor using 'open source' models.
But is it the right approach?
I mainly write in a language that was developed for developer happiness: ruby. It wasn't designed for optimal performance, but allows code to be readable and easy to write (when you have a certain proficiency in Ruby that is). After having turned CodePilot on and off for a year, I'm really not impressed. It has saved me typing strings that I might have otherwise copied from an earlier test, but with the disadvantage that the resulting text…
When you're writing server to server communication locally, and these servers are running behind some proxy like puma-dev that enables SSL connections, you may face issues with OpenSSL (or variant) not trusting the connections. That is good, you want OpenSSL not to trust just any random certificate. But how to make it pass?
Your web browser probably allows you to visit a page on a server and even approve the certificate. You should then export the chain as .pem (Firefox makes this pretty easy: click on the lock, view security, view certificate, go to the "CA"-tab for that certificate and export the chain.
This PEM chain is just a plain text file, containing the public certificates of your local 'CA' (created by Puma-Dev)
Copy the text using an editor of choice.
And now open up: /opt/homebrew/etc/ca-certificates/cert.pem
and add it.
Also make sure that everything uses this cert.pem by symlinking it to this file, so for example with ruby run
$ ruby -ropenssl -...
In a code review that I did today, I left a final note to the author of some new piece of code:
Sorry for nitpicking on code naming. The approaches you take are good, but naming is hard (but important!):
> There are only two hard things in Computer Science: cache invalidation and naming things.
(see here a list of variations on these two hard problem 'jokes' in Computer Science)
I enjoy the ruby programming language because you can get a long way by just assuming things. A collection implements each
, every object has nil?, in rails, I can get the relationship of a record and use scopes to filter the relationships as defined in the class of that related record. If it ands with a questionmark, it returns a boolean(y). Anyway.
So while they might say: "never assume things", and sure, a lot of things are chaos, but ideally not our code base :) I prefer to work from assuming t…
It is good practice to leave your database in a consistent state. There are different ways to do this. Foreign key constraints, indexes, typing of columns, are all strategies to keep your database in a consistent state. Transactions are another way providing you a tool to keep the database consistent: if one of the inserts or updates fail, your database will rollback to the state before the first in the series of inserts and updates within that transaction.
Some languages make it really simple to create a transaction. In Ruby on Rails it is simply opening a block:
User.transaction do
## ... all db operations are now in a transaction
end
But be cautious; transactions don't come for free: they lock the table or row, which is bad for performance. It can, by design, stop other processes from updating the same rows. And all this gets worse when transactions take longer, when for example they contain request to remote resources.
Hence, my approach to transact…
I'm fond of data-URI's (MDN Link). 12 years ago I reappropriated a tool that stored a webpage with its related resources in a Microsoft specific format and rewrote it into something that would store it in normal HTML where the related resources were encoded in data URI's. Recently the topic came up again at a project I was working in, where microservices are still a thing. And while discussing it with colleagues it seemed as if knowledge about this quite useful URI-scheme wasn't on top of everyone else's mind. Instead, the original idea was, we could upload the resource to S3, pass the link, download the resource from S3 at the receiving end, and then have some policy that takes care of deleting it… nah…
This is the most simple data-URI:
data:,Hello%2C%20World%21
You [can open it in your browser](dat…
Are you sure?
Decorators decorate your class with new set of functionality.
What are your decorators doing? Adding a few rendering specific methods to a class to help with rendering? Perhaps you should consider Presenters. But better: how will it scale, can it be grouped, will it really add the simplification. Be wary of too quick branching off functionality to decorators. Most cases I've seen them were overly architectured, and they didn't bring much value.
One might consider using Concerns or mixins as an alternative. The disadvantage here is that your main object gets more public methods, but I consider it as a feature after having experienced too much potentially reusable functionality grouped arbitrarily away in other presenter / decorator classes.
I'm quite familiar with writing ruby code. I've even expressed my love for the language long time ago. But some constructs still bug me. One of them is unless…
I've seen variants of this code:
temperature = 36.7
unless temperature > 37.2 || temperature 35.5 && temperature < 37.2
puts "Healthy temperature. Temperature is within the normal range."
end
or perhaps even nicer:
if (35.5...37.2).include?
puts "Healthy temperature. Temperature is within the normal range."
end
There are a few hard problems in computing. Correctly handling time, naming, preventing off by one errors… sorting text may not be one of them but recently we ran into a discussion where I couldn't make up my mind anymore. Hence, this post's topic: sorting text.
How do you sort the following words:
If you'd ask ruby I'd get:
%w[cheese Ape Drums dent Beer].sort
Results in:
Which in my useless and ramshackle programmer's brain translates to, well why not, it is sorted right?
But then we moved the data into a database which was correctly set up with a proper locale for 'collation', a term that I've seen but never meant anything to me until this problem. Collation is:
> the assembly of written information into a standard order.
(thanks Wikipedia - Collation)
Databas…
So I wrote a few short articles on when to use FormObjects and Jobs and ServiceObjects. The question is of course "it depends", but the leading principle I have is keep it simple. That being said, for inspiration, some suggestion for different layers to manage the application complexity from Vladimir Dementyev's talk on Railsconf:
Whenever your model gets too heavy?
The easiest way to clean up your classes might be to create smaller, more concise methods. The next easiest way of tiding up your models is moving stuff to modules (whether they are 'Concerns' or not). Modules can then be included in the final classes. It will lead to a crowded list of methods exposed on these classes, for which alternative solutions exist (Presenters, Decorators), but if you shield off private methods nicely and have a consistent way of naming things, I wouldn't be too concerned about that. Note that having many modules used in only a single class might be a code smell: perhaps you're trying to do too much with that single class.
When you're using Rails, you can make use of Concerns. They offer a few advantages over traditional modules, so use it whenever you're bothering recreating the same behaviour using plain old ruby Modules. I prefer consistency, so if you've adopted Concerns, use con…
When necessary.
It depends. By default I would advise against them; not creating Form objects to receive and validate data that could be validated by the Model directly. Even when you have a few nested attributes that belong to the main model modified, I would advise against Form objects. Keep It Simple.
But… sometimes you have more complex forms that don't fit the database-mirroring ActiveRecord model as nicely.
Always.
When you are able to do stuff async (not blocking the web-request), make it async. It will also reduce the need for a category of Service-objects. Worker or Job objects can often be called inline if desired.
Sidenote: I personally prefer the "Job" object name, a Job that needs to be performed. Worker is a name that was popularised by Sidekiq, but Sidekiq moved to Jobs as well.
I've got some opinions about certain ways of setting up more advanced (mostly Rails) applications. These might be short 'posts' which I might return to later. Let's see how it goes :)
Never.
There is of course never an absolute answer to stuff but if you are running it in a background job anyway have you considered directly writing it in a Worker or Job-object? Note that you can always run jobs async when needed.
My main objection against service objects is that all too often they are ill defined as a category. So while having fat controllers or fat models may be a bad thing, just creating a bunch of somewhat arbitrary 'Services' is not making the code more manageable.
When considering adding a 'services' directory to your app, try to think of what class of problems you want to tackle. And when in doubt, just keep messing around with the somewhat fatter models & controllers.
Dit artikel van murblog van Maarten Brouwers (murb) is in licentie gegeven volgens een Creative Commons Naamsvermelding 3.0 Nederland licentie .