VPN advice for schools (& probably most other businesses): when getting your firewall configured, make sure they set it up so it works with native VPN clients.
People shouldn’t need to install third party software (& kernel extensions!!!) just to connect to your network.
Raspberry Pi 4 is an impressive bit of kit! Quad core CPU, Gigabit ethernet, dual 4k displays & 4 USB-A ports (2 USB3).
Biggest disappointment is the “USB-C” power port. I thought our Pi was DOA but it turns out it just needed a lower quality cable due to a design flaw 🤦♂️
I think I’m pretty savvy when it comes to online scams and phishing but it turns out I’m not so savvy in the real world 😔
A “fencing contractor” came to our door and said he’d been hired by our back neighbour to replace the (very dilapidated) wooden fence. He spent a while talking about site access and where to put the skip bin and whether it was okay to cut back one of our trees on the fenceline to make sure it didn’t impinge on the new fence.
Eventually I brought up the topic of payment and after “consulting with our neighbour” he came back and told me she’d agreed to pay the bulk of it as she was getting her (much longer) side fence done at the same time. Our half of the shared portion was only $500. That seemed like a bargain to me (I’m capable of physical labour but I really don’t enjoy it) so I readily agreed!
He “started working” (pulling some palings off the fence by hand) but said he couldn’t stay long as he had to provide a quote in another suburb. He told me that his colleagues would be back that arvo to complete the demolition. At this point he finally asked for a 50% deposit and I agreed.
I should have been more suspicious when he asked for cash instead of a bank transfer but I just assumed he was a dodgy contractor trying to stay below the tax man’s radar. I almost never pay cash for anything these days so I explained I’d have to go out and withdraw some.
I was actually in the middle of filling in a doorway in our house (again, I’m capable of physical labour, I just don’t enjoy it) so I went back to work and completely forgot about him or the cash. When he came to the door a little later I felt guilty for forgetting him. “Thankfully” I remembered I had some birthay cash stashed away so I fished it out and forked over $200.
That was the last I saw of “Eddy” or my $200. I later found out he scammed a lot more money out of our back neighbour.
With the benefit of hindsight there were lots of things I should have picked up on but he was very brazen, he added some convincing little details to his stories and he was very patient. It didn’t even occur to me at the time that he was anything other than a local lad trying to earn a (mostly) honest buck while also doing a solid for our elderly neighbour.
Despite the loss (and the embarassment!) there’s lots to be grateful for:
And that’s not even mentioning the fact that, despite living in a “poor area” of Sydney, we are safe, comfortable and own our own home (some of it anyway… we still owe the bank a good chunk 😆). That probably puts us in the top 5 or 10% of the world’s wealthiest people!
My laptop fan started screaming… checked Activity Monitor and Safari CPU usage was at 122%
I closed the Twitter tab and within a few seconds the fans started to ease off.
First time I’ve logged in to Twitter for a while (wanted to ping someone), doesn’t make me miss it!
The latest Ruby Weekly newsletter linked to a helpful blog post about magic comments (or pragmas) in Ruby by Mehdi Farsi. It’s a short read and I appreciated getting some more info on a topic I’ve never thought too much about. A couple of his examples made me a little suspicious though, so I did some more digging.
When specifying encoding, he demonstrates that the first magic comment is applied and any other encoding comments are ignored. That makes intuitive sense to me: you can’t change encoding part way through a file (at least I really hope you can’t, encodings cause enough confusion as it is!!!).
For his next two tests, Mehdi simply gave the magic comment two different settings at the top of the file and observed that the setting in the second one takes effect. He then concluded that only the last one is processed while the others are skipped.
Another possible conclusion is that both were processed and the different settings were in effect for different parts of the file. The way to test that would be to use a bigger example and turn the settings on and off multiple times:
# indentation-precedence-test.rb
# warn_indent: true
puts "\n*** Indentation Warnings: true ***"
def bad_indentation_1
"#{__method__} (line #{__LINE__})"
end
p bad_indentation_1
# warn_indent: false
puts "\n*** Indentation Warnings: false ***"
def bad_indentation_2
"#{__method__} (line #{__LINE__})"
end
p bad_indentation_2
# warn_indent: true
puts "\n*** Indentation Warnings: true ***"
def bad_indentation_3
"#{__method__} (line #{__LINE__})"
end
p bad_indentation_3
# warn_indent: false
puts "\n*** Indentation Warnings: false ***"
def bad_indentation_4
"#{__method__} (line #{__LINE__})"
end
p bad_indentation_4
In this test we try to turn the indentation warnings on and off (and on and off again). When I run it with ruby indentation-precedence-test.rb
I get the following output:
indentation-precedece-test.rb:6: warning: mismatched indentations at 'end' with 'def' at 4
indentation-precedece-test.rb:24: warning: mismatched indentations at 'end' with 'def' at 22
*** Indentation Warnings: true ***
"bad_indentation_1 (line 5)"
*** Indentation Warnings: false ***
"bad_indentation_2 (line 14)"
*** Indentation Warnings: true ***
"bad_indentation_3 (line 23)"
*** Indentation Warnings: false ***
"bad_indentation_4 (line 32)"
Despite having four badly indented methods we only received two warnings; one for the first method (lines 4-6) and one for the third method (lines 22-24). These two methods were (badly) defined after turning warnings on. Conversely, the other two methods don’t cause warnings. They were (badly) defined after turning warnings off. This confirms that all of the warn_indent
settings in the magic comments are applied, simply changing the state from that point in the file onwards.
But what about in other files when you require them? Does the setting carry over?
# indentation-test-requiring.rb
puts "\n*** Main file, Indentation Warnings: default ***"
def bad_indentation_1
"#{__method__} (line #{__LINE__})"
end
p bad_indentation_1
# warn_indent: true
puts "\n*** Main file, Indentation Warnings: true ***"
def bad_indentation_2
"#{__method__} (line #{__LINE__})"
end
p bad_indentation_2
require_relative 'indentation-test-inherit.rb'
# warn_indent: false
puts "\n*** Indentation Warnings: false ***"
def bad_indentation_4
"#{__method__} (line #{__LINE__})"
end
p bad_indentation_4
require_relative 'indentation-test-override.rb'
puts "\n*** Main file, Indentation Warnings: did they change??? ***"
def bad_indentation_6
"#{__method__} (line #{__LINE__})"
end
p bad_indentation_6
# indentation-test-inherit.rb
puts "\n*** New file, Indentation Warnings: Do they inherit??? ***"
def bad_indentation_3
"#{__method__} (line #{__LINE__})"
end
p bad_indentation_3
# indentation-test-override.rb
# warn_indent: true
puts "\n*** Another new file, Indentation Warnings: true ***"
def bad_indentation_5
"#{__method__} (line #{__LINE__})"
end
p bad_indentation_5
In this example we only get two warnings, one for method 2 in the main file and one for method 5 in the “override” file (that we explicitly turned them back on for).
This shows us a few things:
But do the same rules hold for frozen string literals?
Let’s start by confirming the default state for frozen string literals is off:
# frozens-test.rb
string_1 = 'frozen? 1'
p "string_1.frozen?: #{string_1.frozen?}" # => "string_1.frozen?: false"
Now let’s turn it on for the next one:
# frozens-test.rb
string_1 = 'frozen? 1'
p "string_1.frozen?: #{string_1.frozen?}" # => "string_1.frozen?: false"
# frozen_string_literal: true
string_2 = 'frozen? 2'
p "string_2.frozen?: #{string_2.frozen?}" # => "string_2.frozen?: false"
Wait, what!? It’s not frozen! Unlike the indentations warning, it seems this particular magic comment has to come at the top of the file. But in Mehdi’s test he had another comment before it. Let’s test if all comments and whitespace are okay:
# frozens-test-2.rb
# frozen_string_literal: true
# Random comment after turning string literals on...
#... and lots of white space before we turn them back off
# frozen_string_literal: false
# Different comment types are fine...
=begin
If you want to read
a block-commented Haiku
this will have to do
=end
# Other pragmas (magic comments) are fine too
# # warn_indent: true
# We can still turn them on...
# frozen_string_literal: true
string_3 = 'frozen? 3'
p "string_3.frozen?: #{string_3.frozen?}" # => "string_3.frozen?: true"
It turns out comments are fine, white space is fine, and other pragmas (magic comments) are fine.
So maybe it just needs to be before any strings?
# frozens-test-3.rb
2 + 2
# frozen_string_literal: true
string_4 = 'frozen? 4'
p "string_4.frozen?: #{string_4.frozen?}" # => "string_4.frozen?: false"
Nope! Adding 2 numbers together (or even defining an empty method), prevents it from being used. In my testing, the only vaguely code-like things that seem to be allowed before this magic comment are pointless references to existing objects:
# frozens-test-4.rb
# nil is okay:
nil
# Mentioning special variables (like the last exception) seems to be okay
$!
# Referencing the args array is okay too, even if it's not empty
$*
# Referencing the file path is also fine
__FILE__
# frozen_string_literal: true
string_5 = 'frozen? 5'
p "string_5.frozen?: #{string_5.frozen?}" # => "string_5.frozen?: true"
And just when you thought I’d plumbed the boring depths of this, I found one last exception to the rule:
# frozens-test-5.rb
# It doesn't like special variables on consecutive lines!!!
$!
$*
# frozen_string_literal: true
string_6 = 'frozen? 6'
p "string_6.frozen?: #{string_6.frozen?}" # => "string_6.frozen?: false"
So in conclusion:
# frozen_string_literal
has to come before any (useful) codeKiba is really nice for doing ETL work in Ruby! It strikes a great balance between providing all the tools you need to be productive without forcing you down any particular path or to learn huge amounts of syntax! Very flexible and powerful!
According to its terms of use, Netflix can’t be used in a classroom, it’s only for personal use. However, I recently learned there are certain Netflix documentaries that Netflix grants special permissions to allow educational streamings (e.g. Take Your Pills).
That sounds fantastic but it doesn’t seem like Netflix have actually thought about the intended audience for this as the user experience to identify these documentaries is awful.
If you’re a teacher, here are the steps to find out which documentaries are available:
On a brand new laptop with no distractions and an uncontested 100Mbps Internet connection (three things I wouldn’t count on teachers in most schools having) I was able to check three documentaries in just under a minute, not including the time to save the URL if it was one of the small percentage that are permitted for educational use (nor the time to navigate to the next page when you finish one).
All up that’s going to be 40+ painful minutes of clicking and navigating for a list of about 25 education permitted documentaries (extrapolating from a fairly small sample).
A far better experience would be if the list view contained a little icon of some sort (a mortaboard hat icon perhaps) indicating which documentaries permit education screenings.
Standard HTML links (that allow opening links in a new tab and seeing which links have already been visited) would also be a huge improvement.
I contacted Netflix PR (the part of the organisation that hosts the “Only on Netflix” portion of the site) over a week ago and asked them for more information about the titles available under this service and whether they were aware of the usability and accessibility issues but I didn’t receive even an automated reply. I will update this post if they get back to me.
I used the kiba gem for an ETL process today. My initial impressions are very positive! It lets you take a data source and build a “pipeline” to process the data using a declarative syntax. I think I’ll be using it a fair bit more!
An anesthesiologist gives a great little (< 20 min) talk on how failure research applies to web apps.
All about resilience in systems and the inevitable pressures that are constantly pushing the operating point closer to the failure boundary.
In my recent security travels, I found an article by the author of bcrypt-ruby
on How To Safely Store A Password.
It was last updated in 2011 but I think it holds up well.
While I was thinking about security I decided to double check that I’m practicing what I preach.
When building a Rails app I don’t want to roll my own security (too easy to miss things) but I want something that’s simpler and lighter weight than Devise. There are lots of good options in this space but I usually reach for Clearance… I respect the Thoughtbot team’s approach to development, they actively maintain Clearance for use in their own projects and they were responsive when I flagged an issue using Clearance with the upcoming Rails 6 release.
Anyway, I kind of knew that Clearance used BCrypt and salting, but when I looked in my database schema I couldn’t find a column for storing the random salt. After doing a little digging, I discovered that bcrypt-ruby, quite cleverly, securely generates the salt for you and then appends it to the end of the now salted and hashed password. All you need to do is store the result in a single field.
Not only does this prevent well-meaning but clueless developers (i.e. me) from doing silly things with the salt (in a mis-guided attempt at “making it more secure”), but it also simplifies the interface of the library.
On a related note, bcrypt-ruby
cleverly overrides the ==
method to let you compare a plaintext password (e.g. that someone just provided at log in) with the stored hash/salt combo to see if it’s a match. Jesus Castello has written a helpful article that just happens to explain this exact mechanism.
All in all, it’s an excellent example of making it easy for the user (in this case application developers) to do the right thing.
I discovered today that the 20 character randomly generated password I thought I’d been using for an online banking service was actually being constrained to 6 characters by the bank. That got me doing some calculations…
With other restrictions they put in place I think the total possible number of passwords available is roughly 62 * 62 * 61 * 61 * 61 * 10 or 8.7 billion.
In the best case* scenario (passwords salted and then hashed using bcrypt with enough rounds to slow down attackers without making login times too slow) I think it would probably take a budget password cracking rig about 28 hours (on average) to crack each password if someone ever got hold of the data.
That’s probably slow enough to be a deterrent for many systems but I doubt that would stop an attacker who is going after bank log-ins!
Using the same calculations, and leaving the other password restrictions in place, increasing the password length to seven increases average cracking time to 1,697 hours (70 days per password) while eight characters increases it to 103,538 hours (almost 12 years) on that budget rig.
Even better would be to increase it to a minimum of 12 characters (with no maximum length) to make it 163 million years per 12 character password. That way, even if an attacker could somehow go 1 million times quicker than the budget rig, it would still take 163 years per password, more than enough time for the bank to detect a breach has occured and notify their customers to change their passwords.
* I’m hoping the best case scenario is true. One would assume that security staff at one of Australia’s biggest banks HATE the fact that their customer’s passwords are so short but can’t do anything about it yet for legacy reasons. The worst case scenario is that this is indicative of systemic problems at the bank and there are several other poor security practices in place… no salting, a fast hashing algorithm (or worse, no password hashing), etc. If that’s the case, every single password would likely be compromised within hours of a breach occuring.
Interesting thought experiment in youtu.be/1VsSXRPEB…
He makes a reasonable argument that, in any given application, the data is probably harder to replace than the source code… yet we spend a lot more time learning about writing code than how to manage data well
Tonight I finished Ruby Under a Microscope by Pat Shaughnessy 📚
Some sections were pretty tough but the illustrations definitely helped!
The GC chapter was great, especially after listening to www.se-radio.net/2019/07/e…
The JRuby and Rubinius chapters were really helpful. I’d love to see another chapter on TruffleRuby though - github.com/oracle/tr…
I’m upgrading from a 2010 MacBook Pro to a 2019 MacBook Air… and yet so far the thing I’ve been most floored by is the rope handle of the carry bag… it’s actually made out of circular knit paper! 🤯
thoughtbot.com/blog/git-… is a very clever alternative to aliasing bundle exec
Wifi safety comes up again and again in schools… it’s interesting to read about the origin of some of the myths
I recently finished Bad Debts by Peter Temple 📚🎧
It was a bit gritty for my taste but the writing was excellent and the book was narrated well.
Event Sourcing Made Simple was pretty persuasive about the benefits of decoupling the model from the events in an app that cause the model to be changed.
Piotr Solnica then reinforced those ideas on Remote Ruby
I love the idea of No Backlog. If an idea has merit but misses a cycle, someone can track it individually and re-raise it later.
I‘ve found backlogs to be burdensome… plus it’s all too tempting to pity-pick a project just because its been waiting on the backlog a long time!
Today I learned that Ruby modules are actually implemented as classes under the hood.
When you include
a module (which is kind of a class in disguise), it actually gets dynamically inserted into the inheritance tree.
Consider the example of a school. We might have a Person class to model every person in the school with some common attributes:
class Person
attr_accessor :given_name
attr_accessor :surname
end
Then we might have subclasses for students, staff and parents to model attributes specific to each group. Focusing on the student, our program might look like this:
class Person
attr_accessor :given_name
attr_accessor :surname
end
class Student < Person
attr_accessor :year_level
end
To add attributes to staff and students that don’t apply to parents we could define a module and mix it in. Let’s say we want to be able to add students (e.g. Harry and Ron) and staff (e.g. Minerva McGonagall) to a house (e.g. Gryffindor). Our program now looks more like this:
class Person
attr_accessor :given_name
attr_accessor :surname
end
module House
attr_accessor :name
attr_accessor :colour
end
class Student < Person
include House
attr_accessor :year_level
end
Previously Student inherited directly from Person but now the House module (or, more accurately, a copy of it) gets inserted as the new parent class of Student. Under the hood the inheritance hierarchy now looks more like Student < House < Person
.
That’s all well and good but Ruby doesn’t support multiple inheritance; each class can only have one parent. So how does including multiple modules work? It turns out, they each get dynamically inserted into the inheritance tree, one after another.
Stretching our example a little further, if we had some attributes common to parents and students, we might use another module. Perhaps we want to record the household so we can model that Ron, Ginny and Mr & Mrs Weasley are all part of the “Weasley” household (I’m stretching this example quite far, hopefully it doesn’t snap and take out an eye). Our student class might now look like this:
class Person
attr_accessor :given_name
attr_accessor :surname
end
module House
attr_accessor :house_name
attr_accessor :house_colour
end
module Household
attr_accessor :household_name
attr_accessor :address
end
class Student < Person
include House
include Household
attr_accessor :year_level
end
Under the hood, Ruby first inserts House as the new parent (superclass) of Student but then on the very next line it inserts Household as the parent of Student. The inheritance chain is now Student < Household < House < Person
.
There’s always several things worth reading in the Ruby Weekly newsletter
So far this week I’ve enjoyed reading about the regexp-examples gem (and Tom Lord’s associated blog post)
🎉 Free ebook with hard cover purchase
😐 Fine print says 45 days “free access”
😞 They cancelled that program… now it’s 30 days
🤦♂️ The “30 days free” link takes you to a 10 day free trial
“I am altering the deal. Pray I don’t alter it any further.” – Big Publishers
📚 🎧 Finished listening to 9 Perfect Strangers - www.goodreads.com/book/show…
First time I’ve read Liane Moriarty; thoroughly enjoyed it! Some laugh out loud moments but in the end she made me care about every single (flawed!) character!
This evening my son and I switched to Codenvy.com for his school project (he is building a quiz app).
It’s a bit more complicated to get going than repl.it and came with much older software versions but it seems to have a much higher ceiling and less weird constraints.
It feels similar to where Cloud9 used to be… before Amazon bought it and “AWSed” (complicated) it.