If you execute IRB with the command line flag –noprompt you will enter in to an IRB session with none of the extra characters showing up on the left side of the terminal. This is great if you want to experiment with code and then use the mouse to copy & paste from it. Like, for example, in a code blog like the one you’re reading…
(Thanks to 6ftdan for this cool tip.)
Showing posts with label ruby. Show all posts
Showing posts with label ruby. Show all posts
Monday, February 22, 2016
Monday, February 8, 2016
Converting unix timestamps in Ruby
Unix timestamps are common to see in other languages, and also common to see in API responses. Converting a unix timestamp to DateTime in Ruby is dead simple:
Let’s say your unix timestamp includes milliseconds:
Notice how both DateTime strings are the same? DateTime converted to string drops any millisecond data, but keep it as a DateTime object and you can run
unix_timestamp = Time.now.to_i
# => 1453667949
DateTime.strptime(unix_timestamp.to_s, "%s").to_s
# => "2016-01-24T20:39:09+00:00"
Let’s say your unix timestamp includes milliseconds:
unix_timestamp = (Time.now.to_f * 1000).to_i
# => 1453667949151
DateTime.strptime(unix_timestamp.to_s, "%Q").to_s
# => "2016-01-24T20:39:09+00:00"
DateTime.strptime will allow you convert just about any formatted time string, see the docs for examples.Notice how both DateTime strings are the same? DateTime converted to string drops any millisecond data, but keep it as a DateTime object and you can run
strftime and display it in any format you like:
DateTime.strptime(unix_timestamp.to_s, "%Q").strftime('%D %T:%L')
# => "01/24/16 20:39:09:151"
Monday, January 25, 2016
Arbitrary SQL ordering
I ran across an issue the other day where I had 4 records from the database that were being displayed in order of creation date, but I wanted them displayed in an arbitrary order - not alphabetical by name or title, or chronological, or sorted by id.
Before, I probably would have used Ruby to loop through the records and build an array by comparing ids. Now, though, I know better than to use Ruby for this type of thing, because it’s faster and better for memory to do it in SQL if possible (which I learned the hard way).
Turns out, querying using SQL looks a lot like what I would have written in Ruby anyhow:
Pretty cool! Thanks to cpjolicoeur on Github for this one.
Before, I probably would have used Ruby to loop through the records and build an array by comparing ids. Now, though, I know better than to use Ruby for this type of thing, because it’s faster and better for memory to do it in SQL if possible (which I learned the hard way).
Turns out, querying using SQL looks a lot like what I would have written in Ruby anyhow:
SELECT * FROM foo ORDER BY
CASE
WHEN id=67 THEN 1
WHEN id=23 THEN 2
WHEN id=1362 THEN 3
WHEN id=24 THEN 4
END
Pretty cool! Thanks to cpjolicoeur on Github for this one.
Monday, January 18, 2016
Migrating data in Rails using Rails migrations
Put this one in the “should’ve been obvious” column…
For an embarassingly long while, when I had a feature ready for deploy that required production data be modified for a whole bunch of records – for example, downcasing all
Not only is this bad code since it doesn’t keep a record of what happened, it’s also tedious because it’s an extra step that must be taken by whoever deploys the code.
Luckily there’s a better way - just put the updating login in a migration!
Example:
This example isn’t great because 1. there’s almost certainly a way to do this with pure SQL without instantiating every user record, and 2. it’s not reversible. But since I’m lazy and since it illustrates the point of the post, I’m leaving it.
For an embarassingly long while, when I had a feature ready for deploy that required production data be modified for a whole bunch of records – for example, downcasing all
name attributes – I would write a one-off rake task to update those records, then delete that task.Not only is this bad code since it doesn’t keep a record of what happened, it’s also tedious because it’s an extra step that must be taken by whoever deploys the code.
Luckily there’s a better way - just put the updating login in a migration!
Example:
class DowncaseName < ActiveRecord::Migration
def up
User.where.not(name: nil).find_each do |user|
user.update!(name: user.name.downcase)
end
end
end
This example isn’t great because 1. there’s almost certainly a way to do this with pure SQL without instantiating every user record, and 2. it’s not reversible. But since I’m lazy and since it illustrates the point of the post, I’m leaving it.
Monday, January 11, 2016
No return value in rails console
A quick tip when working in rails console. If you're testing commands to run and don't want the console to output the return value, just append a semicolon to your ruby code.
OK, so that's a contrived example, but this really comes in handy
User.all
# => [#<user:0x007fd72a408630> {:id => 1, :name => "Mr. T", ...}, tons and tons of other records]
User.all;
# => nothing!
OK, so that's a contrived example, but this really comes in handy
- when creating records, or
- if you need to update several records in one shot and don't want to see the return values, or
- POSTing to an external API and avoiding all the cruft that comes in the response
Labels:
command line,
console,
rails,
ruby,
tricks
Monday, January 4, 2016
Classy Ruby enumerating
Given two users:
Say you want to get an array of arrays, with each individual array containing two elements: the first and last name, and the id. You could do something like this, which is perfectly valid but also gross to read:
However, it’s much more readable to use tuple syntax:
User.create(first_name: "Joe", last_name: "Louis")
#=> User id: 1, first_name: "Joe", last_name: "Louis"
User.create(first_name: "Muhammed", last_name: "Ali")
#=> User id: 2, first_name: "Muhammed", last_name: "Ali"
Say you want to get an array of arrays, with each individual array containing two elements: the first and last name, and the id. You could do something like this, which is perfectly valid but also gross to read:
User.pluck(:id, :first_name, :last_name).map do |user|
id = user[0]
first = user[1]
last = user[2]
["#{first} #{last}", id]
end
However, it’s much more readable to use tuple syntax:
User.pluck(:id, :first_name, :last_name).map do |(id, first, last)|
["#{first} #{last}", id]
end
#=> [
["Joe Louis", 1],
["Muhammed Ali", 2]
]
Monday, December 21, 2015
Funky Ruby Class Method Syntax
I’ve run across this syntax a few different times but never grasped how it worked, and I guess never cared enough to look it up. Turns out it’s dead simple.
This:
is equivalent to this:
Thanks to Yehuda Katz for finally clearing that up for me.
This:
class Person
def self.species
"Homo Sapien"
end
end
is equivalent to this:
class Person
class << self
def species
"Homo Sapien"
end
end
end
Thanks to Yehuda Katz for finally clearing that up for me.
Monday, December 14, 2015
Literally the coolest way to build an array
One of Ruby’s literal syntax shorthands,
Caveat: This syntax was added in Ruby 2.0, so don’t go trying it in any previous version.
%i essentially loops over each word, converts to a symbol and adds it to an array. For example:
%i(dog cat mouse)
# => [:dog, :cat, :mouse]
Caveat: This syntax was added in Ruby 2.0, so don’t go trying it in any previous version.
Monday, December 7, 2015
Familiarize quickly in new code with this trick
Here’s a quick and nifty little trick that comes in extremely handy in a lot of situations, but particularly when dealing with an unfamiliar codebase, such as at a new job or working with a previously unused external gem.
In the rails console, enter:
and it will show all the class and instance methods, as well as associations and a bunch of other cool stuff for that model.
I’ve begun using this in place of these two:
Not only because it’s simpler with less typing but it else ends up being much easier to read.
In the rails console, enter:
ls Model
and it will show all the class and instance methods, as well as associations and a bunch of other cool stuff for that model.
I’ve begun using this in place of these two:
Model.methods
Model.new.methods
Not only because it’s simpler with less typing but it else ends up being much easier to read.
Monday, November 23, 2015
count vs size on ActiveRecord select queries
I stumbled on this bit of weirdness when using the SQL query
Out of curiosity, I did some digging. According the the ActiveRecord docs,
User.select(:id, :name, :email).count. It threw a PG::UndefinedFunction error. Using User.select(:id, :name, :email).size worked instead.Out of curiosity, I did some digging. According the the ActiveRecord docs,
not all valid select expressions are valid count expressions. TheI also stumbled on an issue on rails Github issues page about this, and a very helpful comment from rafaelfranca explained that I can also use
specifics differ between databases. In invalid cases, an error from
the database is thrown.
count(:all).
Monday, November 9, 2015
Intro to SSH for relative newbies
I have an app running on an Amazon EC2 instance, and it requires an SSH key to access it. Previously, I had to access it by navigating to the folder containing my private key pem file, then running
Let’s break this down a bit.
This lets me simply run
Here’s where setting up the
Once I save, I can now use my computer’s built in SSH manager to access my server by running the command
It will read my
–
Big ups to Nerderati for the helpful explanation on SSH config.
ssh -p 2222 -i private_key.pem -A ubuntu@www.example.com.Let’s break this down a bit.
-p 2222- the port that my server runs on-i private_key.pem- use an Identity File (SSH key) and specify the name-A- use ForwardAgent to allow my public keys to pass through to the AWS server (important if I want to be able to access Github or some other service while SSH-ed into the server)ubuntu@www.example.com- username (ubuntu) and server HostName (www.example.com)
~/.bash_aliases
alias my-server-ssh="cd ~ && ssh -p 2222 -i private_key.pem -A ubuntu@www.example.com"
This lets me simply run
my-server-ssh from whatever directory I’m in on the terminal and automatically login to my server. However, once I have a couple more servers to juggle, it starts to get troublesome keeping track of these things. It’s also problematic if I want to use a tool like Capistrano for deployment.Here’s where setting up the
~/.ssh/config file comes in handy. I opened this file in my text editor, and entered this info:
Host my-server
HostName www.example.com
Port 2222
User ubuntu
IdentityFile "~/.ssh/private_key.pem"
ForwardAgent yes
Once I save, I can now use my computer’s built in SSH manager to access my server by running the command
ssh my-server. Then, in a tool like Capistrano, I can plug this line into my config file:
server 'my-server', roles: %w{app web}
It will read my
~/.ssh/config file, find the configuration for host my-server and SSH in.–
Big ups to Nerderati for the helpful explanation on SSH config.
Monday, November 2, 2015
Object overload! Careful of instantiating objects in Ruby
Wowzers, I just tackled a major memory issue in an app I’ve been developing, and it was nearly all due to rampant over-instantiation of ActiveRecord objects.
Here’s the TL;DR: Use SQL queries whenever possible to grab info from the database, rather than using Ruby code.
Now for the longer version. First, I knew I had a memory issue because my server was crashing and it was showing up the logs saying that memory could not be allocated. But I had to find out what the issue was first.
Oink to the rescue. It’s a handy little Ruby gem that will log how much memory and how many ActiveRecord objects are instantiated for each request.
I installed Oink, ran through a few requests that seemed kinda slow, and lo and behold, I found the problem: One of my requests was generating 1500 objects!
Here’s what was happening. I was looping through an array of IDs, and for each one I was calling
Don’t laugh. I have a baby at home, I was sleep deprived, I don’t know why I did it this way. But yes, this is instantiating all User objects in an account, and it’s doing it every single time it iterates through the loop. Hence, 1500 objects instantiated per request.
This was easily solved by a simple
That took me down to only one instance of each User object, which is still not fantastic. So, I ended up using one of my favorite ARel methods,
That’s the ticket! From 1500 User objects to 0, this should significantly help with speed and memory usage. I ended up combing through the rest of the app and finding a few other spots where object instantiation was a bit out of control, though not nearly as bad as that one.
It became a kind of game - and in the end, I’d say I won.
Thanks to Engine Yard for the great tips. Lots more in their linked post.
Here’s the TL;DR: Use SQL queries whenever possible to grab info from the database, rather than using Ruby code.
Now for the longer version. First, I knew I had a memory issue because my server was crashing and it was showing up the logs saying that memory could not be allocated. But I had to find out what the issue was first.
Oink to the rescue. It’s a handy little Ruby gem that will log how much memory and how many ActiveRecord objects are instantiated for each request.
I installed Oink, ran through a few requests that seemed kinda slow, and lo and behold, I found the problem: One of my requests was generating 1500 objects!
Here’s what was happening. I was looping through an array of IDs, and for each one I was calling
account.users.order(:id).select{ |user| user.id == id }.first
Don’t laugh. I have a baby at home, I was sleep deprived, I don’t know why I did it this way. But yes, this is instantiating all User objects in an account, and it’s doing it every single time it iterates through the loop. Hence, 1500 objects instantiated per request.
This was easily solved by a simple
find_by_id with the id from the array. That took me down to only one instance of each User object, which is still not fantastic. So, I ended up using one of my favorite ARel methods,
pluck, to get just the attributes of the users that I need without actually creating the User objects.That’s the ticket! From 1500 User objects to 0, this should significantly help with speed and memory usage. I ended up combing through the rest of the app and finding a few other spots where object instantiation was a bit out of control, though not nearly as bad as that one.
It became a kind of game - and in the end, I’d say I won.
Thanks to Engine Yard for the great tips. Lots more in their linked post.
Monday, October 12, 2015
URL Encoding and Silly Safari
I have a web app that also acts as an API for an iOS, and I ran into a tricky situation. One of my models on the web app takes a URL as input, and it validates it using the validates_url gem. However, some URLs when visited from within the iOS app would give a Webkit error and show as invalid.
After some head scratching, it turned out that those URLs had encoding issues that were handled fine by all modern browsers except Safari, the default browser on iOS. For example, something with curly brackets like "http://www.example.com?ID={12345}" would work in most browser but not in Safari/iOS.
To get around this, in addition to using
But wait, there’s more!
It turns out that on subsequent saves, the URL would be escaped again, and it wouldn’t recognize the %7B and %7D as encoding, it would just see the % sign as an invalid character needing escape. So, on the second save, the same URL would be transformed again into "http://www.example.com?ID=%7B12345%7D". Notice the “25” inserted in there twice?
The fix ended up being something that reads very strange and seems unnecessary, but that works perfectly:
So, if a URL is already escaped, it unescapes it first, then re-escapes it. If the URL is not escaped, then unescaping it does nothing, then it gets escaped as it should.
If there’s a better way to do this, I haven’t found it, and am open to suggestions.
After some head scratching, it turned out that those URLs had encoding issues that were handled fine by all modern browsers except Safari, the default browser on iOS. For example, something with curly brackets like "http://www.example.com?ID={12345}" would work in most browser but not in Safari/iOS.
To get around this, in addition to using
validates_url, I had to escape each URL during validation using URI.escape(url). The above URL would then become "http://www.example.com?ID={12345}". (Notice that the left curly bracket changed to %7B, the right turned to %7D - check out this HTML URL Encoding Reference for details)But wait, there’s more!
It turns out that on subsequent saves, the URL would be escaped again, and it wouldn’t recognize the %7B and %7D as encoding, it would just see the % sign as an invalid character needing escape. So, on the second save, the same URL would be transformed again into "http://www.example.com?ID=%7B12345%7D". Notice the “25” inserted in there twice?
The fix ended up being something that reads very strange and seems unnecessary, but that works perfectly:
URI.escape(URI.unescape(url))
So, if a URL is already escaped, it unescapes it first, then re-escapes it. If the URL is not escaped, then unescaping it does nothing, then it gets escaped as it should.
If there’s a better way to do this, I haven’t found it, and am open to suggestions.
Tuesday, October 6, 2015
Refactoring using guard clauses
This post is almost purely about code style, but it’s an approach to coding and refactoring that I just found and really enjoy for its simplicity and readability.
The secret sauce to this approach is called a guard clause.
Say I have a method that I’m writing where I want to return one thing is a condition is true, another thing if it’s false. The way I used to write the code is exactly how I wrote that sentence:
It may help to have a more complex example, perhaps using nested conditionals:
If all of that isn’t convincing enough, check out the quasi-official Ruby Style Guide, because it also recommends using guard clauses when possible.
The secret sauce to this approach is called a guard clause.
Say I have a method that I’m writing where I want to return one thing is a condition is true, another thing if it’s false. The way I used to write the code is exactly how I wrote that sentence:
def do_something_conditionally(fact)
if fact == true
fact
else
false
end
end
Pretty standard, right? Now check out the same example with a guard clause:def do_something_conditionally(fact)
return false unless fact == true
fact
end
We saved a couple lines of code, which is nice (although I could’ve saved more turning the conditional into ternary (fact == true ? fact : false)). However, the big gain from doing it this way is that it’s easier to read. Disagree?It may help to have a more complex example, perhaps using nested conditionals:
def account_type
if has_problems?
type = "problems"
if is_past_due?
type = "past due"
if is_weeks_past_due?
type = "delinquent"
end
end
else
type = "normal"
end
type
end
…and now with guard clauses:def account_type
return "delinquent" if is_weeks_past_due?
return "past due" if is_past_due?
return "problems" if has_problems?
"normal"
end
We’ve saved more lines of code, this time something we could not have easily change to ternary, and it’s harder to argue that this isn’t more readable now.If all of that isn’t convincing enough, check out the quasi-official Ruby Style Guide, because it also recommends using guard clauses when possible.
Monday, September 28, 2015
Asynchronous processing pitfalls and ActiveRecord locking
My app is humming along, but there are a few sections that have to do a lot of heavy lifting, whether that be image processing, updating several different related objects, or looping through all of a has_many association to manipulate every record.
These types of things are perfect places to push some logic to a background process. In other words, make it asynchronous. So, if a user clicks a button to update several objects, unless the user needs to see those updates completed immediately, then a tool like Sidekiq can help run some expensive logic as a background process and allow the user to continue surfing the site while that process runs.
There are plenty of tutorials about how to set this up, so that’s not what I want to talk about here.
One of the potential pitfalls of processing things asynchronously, particularly if the processing involves updating database records, is multiple records being updated at the same time. For example, one user updates a Project in a background process, and another user updates the same Project somewhere else in the app that doesn’t use a background process. We want to last update to be the one that sticks, but if both updates are happening at the same time, things could get hairy.
This is the dreaded “race conditions” issue, and luckily there’s a pretty simple fix.
On ActiveRecord - and I believe this will work with any SQL database but definitely at least on Postgres and MySQL - I can lock a specific row while editing, then unlock it afterwards, so that each edit takes place in its own little bubble.
This is really important when dealing with numbers, money, and balances. Let me share a parable to explain:
There is an Account with 75.
A split second later, another user in that account makes a transaction for $90.
What should happen is the second user’s transaction should be rejected for lack of funds.
What actually happens without locking is that for each transaction, the first step is that the server runs
Is your head spinning yet?
When we lock the account per transaction, we avoid this issue by only allowing the account to be edited on transaction at a time.
It’s a cool concept but also can be tricky to wrap your head around. Here’s a blog post I borrowed heavily from for my post. Or check out the Rails API docs.
These types of things are perfect places to push some logic to a background process. In other words, make it asynchronous. So, if a user clicks a button to update several objects, unless the user needs to see those updates completed immediately, then a tool like Sidekiq can help run some expensive logic as a background process and allow the user to continue surfing the site while that process runs.
There are plenty of tutorials about how to set this up, so that’s not what I want to talk about here.
One of the potential pitfalls of processing things asynchronously, particularly if the processing involves updating database records, is multiple records being updated at the same time. For example, one user updates a Project in a background process, and another user updates the same Project somewhere else in the app that doesn’t use a background process. We want to last update to be the one that sticks, but if both updates are happening at the same time, things could get hairy.
This is the dreaded “race conditions” issue, and luckily there’s a pretty simple fix.
On ActiveRecord - and I believe this will work with any SQL database but definitely at least on Postgres and MySQL - I can lock a specific row while editing, then unlock it afterwards, so that each edit takes place in its own little bubble.
Account.transaction do
acc = Account.lock.find(id)
acc.balance -= 25
acc.save!
end
This finds an account and locks it, then updates the balance, saves, and unlocks.This is really important when dealing with numbers, money, and balances. Let me share a parable to explain:
There is an Account with 75.
A split second later, another user in that account makes a transaction for $90.
What should happen is the second user’s transaction should be rejected for lack of funds.
What actually happens without locking is that for each transaction, the first step is that the server runs
account = Account.find(id) to retrieve the account, and since this is happening before either transaction has finished, the balance on the account in both cases is 25 and the account is saved. But on the second user’s transaction, the account object that was pulled from the database still shows a balance of 90 charge from the balance and saves it, thereby allowing the transaction to go through and setting the saved account balance as $10.Is your head spinning yet?
When we lock the account per transaction, we avoid this issue by only allowing the account to be edited on transaction at a time.
It’s a cool concept but also can be tricky to wrap your head around. Here’s a blog post I borrowed heavily from for my post. Or check out the Rails API docs.
Labels:
activerecord,
asynchronous,
rails,
ruby,
sidekiq,
sql
Friday, September 11, 2015
Turbocharged custom Rails / ActiveRecord validations
My code just got a lot less janky when dealing with start and end times. Before, when I wanted to validate that a user-submitted end time came after a start time, I was doing something like this:
Converting the values to epoch time and comparing isn’t terrible, but I just found a cool way to extract this out to a custom validator to make it simpler and more readable:
(Thanks to botandrose and the calagator project for this cool piece of code.)
validate :start_time_is_before_end_time
def start_time_is_before_end_time
unless start_time.to_i < end_time.to_i
errors.add(:end_time, 'must come after start time')
end
end
Converting the values to epoch time and comparing isn’t terrible, but I just found a cool way to extract this out to a custom validator to make it simpler and more readable:
# app/models/model.rb
require "active_model/sequential_validator"
validates :start_time, :end_time, sequential: true
# lib/active_model/sequential_validator.rb
class SequentialValidator < ActiveModel::Validator
def validate record
values = options[:attributes].map do |attribute|
record.send(attribute)
end.compact
if values.sort != values
record.errors.add options[:attributes].last, "cannot be before #{options[:attributes].first}"
end
end
end
True, the actual validator code is not as easy to read I don’t think, but it takes some of the burden out of the model, and it also allows reusing the sequential validation in other models.(Thanks to botandrose and the calagator project for this cool piece of code.)
Tuesday, September 8, 2015
More readable for ActiveRecord statements using Placeholder Conditions
Here’s another readability hack for Ruby ActiveRecord code. It’s called “Placeholder Conditions.”
This:
works the same as this
The second is more terse, but if you are inserting a lot of values into the WHERE statement, then the first is nice for readability.
Source: Ruby on Rails Guides
This:
where("start_time < :time", time: Time.now)
works the same as this
where("start_time < ?", Time.now)
The second is more terse, but if you are inserting a lot of values into the WHERE statement, then the first is nice for readability.
Source: Ruby on Rails Guides
Friday, September 4, 2015
Indifferent Access in Ruby Hashes (with ActiveSupport)
I kept seeing this “Hash with indifferent access” in open source projects, but never knew quite what it was. Turns out it’s something I use in Rails all of the time without knowing, particularly in dealing with params. It allows me to access values in a hash using either string or symbolized keys.
For example:
Source: apidock and RoR api
For example:
params['name'] == params[:name]
# => true
However, a normal Hash in Ruby does not allow this:hash = { 'name' => 'Fred' }
hash['name']
# => 'Fred'
hash[:name]
# => nil
hash['name'] == hash[:name]
# => false
We can fix this by making this into a hash with indifferent access:new_hash = hash.with_indifferent_access
new_hash['name'] == new_hash[:name]
# => true
The hash can also be defined/initialized with indifferent access, either by calling that method on the initial hash assignment, or by creating it using ActiveSupport (which is what the with_indifferent_access method does under the hood anyway):hash = ActiveSupport::HashWithIndifferentAccess.new(name: 'Fred')
hash['name'] == hash[:name]
# => true
Note: When working in straight Ruby, this will not work as it’s using ActiveSupport. One would likely need to run gem install active_support and require it properly.Source: apidock and RoR api
Tuesday, September 1, 2015
More readable value change testing in rspec
Some simple but effective
I think this is fairly straightforward, but essentially it just lets you test for value changes. This is basically just a clearer way of writing expectations and would replace something like this:
Check the rspec docs for more details.
rspec syntax that I just learned:
expect{Object.method}.to change{Object.count}.from(0).to(1)
I think this is fairly straightforward, but essentially it just lets you test for value changes. This is basically just a clearer way of writing expectations and would replace something like this:
expect(Object.count).to eq(0)
Object.method
expect(Object.count).to eq(1)
Check the rspec docs for more details.
Friday, August 28, 2015
Quicker and Easier headers with HTTParty
You may remember last month I showed how to retrieve just the headers in an HTTP request using the HTTParty ruby gem.
At that time, there were a couple of extra options that needed to specified in the case of redirects, otherwise on each subsequent redirect, the HEAD request would be converted to a normal GET request. This wasn’t ideal because the reason I was doing HEAD requests in the first place was to save the time it would take to retrieve the entire page and instead just find out whether the request was successful. It also didn’t make much common sense.
So I changed the gem. It now follows redirects and maintains the HEAD request across all redirects.
This is one of the reasons I really enjoy working in code. There’s this great tool that works extremely well in most cases, but there’s something slightly off about one small part of it. Rather than making a support request to some big conglomeration that may or may not read it and may or may not fix the issue, I can just jump in, make the change, and hopefully help improve the functionality for myself and, in the process, for countless others.
At that time, there were a couple of extra options that needed to specified in the case of redirects, otherwise on each subsequent redirect, the HEAD request would be converted to a normal GET request. This wasn’t ideal because the reason I was doing HEAD requests in the first place was to save the time it would take to retrieve the entire page and instead just find out whether the request was successful. It also didn’t make much common sense.
So I changed the gem. It now follows redirects and maintains the HEAD request across all redirects.
This is one of the reasons I really enjoy working in code. There’s this great tool that works extremely well in most cases, but there’s something slightly off about one small part of it. Rather than making a support request to some big conglomeration that may or may not read it and may or may not fix the issue, I can just jump in, make the change, and hopefully help improve the functionality for myself and, in the process, for countless others.
Subscribe to:
Posts (Atom)