“...I've been working since 2008 with Ruby / Ruby on Rails, love a bit of Elixir / Phoenix and learning Rust. I also poke through other people's code and make PRs for OpenSource Ruby projects that sometimes make it. Currently working for InPay who are based in Denmark...”

Rob Lacey
Senior Software Engineer, Copenhagen, Denmark

Ruby 2 - Time Intervals

Old blog posts I never published, except I have now #12

Say we wanted to schedule a job but wanted to limit duplicate jobs running within a specific time period. We could set a time for a job to run and ignore duplicate requests in our queue. It appears Ruby’s Time object makes this simple for us. We can round up (or floor) to the nearest, time interval. Nice.

2.2.2 :012 > Time.now.floor(15.minutes)
=> "2016-02-29T17:15:00.000+00:00" 
2.2.2 :013 > Time.now.round(15.minutes)
=> "2016-02-29T17:30:00.000+00:00"

Ruby 4.0.0-preview2

Ok, I’m doing it. Ruby 4.0.0 come at me brah

rl@macbookair personal % asdf plugin update ruby
Location of ruby plugin: /Users/rl/.asdf/plugins/ruby
Updating ruby to master
From https://github.com/asdf-vm/asdf-ruby
   1ce84e2..8f28d3a  master     -> master
   1ce84e2..8f28d3a  master     -> origin/master
Already on 'master'
Your branch is up to date with 'origin/master'.
rl@macbookair personal % asdf install ruby 4.0.0-preview2 
ruby-build: using openssl@3 from homebrew
==> Downloading ruby-4.0.0-preview2.tar.gz...
-> curl -q -fL -o ruby-4.0.0-preview2.tar.gz https://cache.ruby-lang.org/pub/ruby/4.0/ruby-4.0.0-preview2.tar.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 22.3M  100 22.3M    0     0  17.5M      0  0:00:01  0:00:01 --:--:-- 17.5M
==> Installing ruby-4.0.0-preview2...
ruby-build: using libyaml from homebrew
ruby-build: using gmp from homebrew
-> ./configure "--prefix=$HOME/.asdf/installs/ruby/4.0.0-preview2" --with-openssl-dir=/opt/homebrew/opt/openssl@3 --enable-shared --with-libyaml-dir=/opt/homebrew/opt/libyaml --with-gmp-dir=/opt/homebrew/opt/gmp --with-ext=openssl,psych,+ --enable-yjit

Got a MySQL Process that appears to be stuck

It’s not uncommon for Rails specs to kill our database in development. It appears that if a browser spec doesn’t close cleanly you can end up with processes just hanging and so the next spec run will just wait forever.

Here’s the process list. It’s sleeping on the job.

MariaDB [(none)]> SHOW PROCESSLIST;
+-----+-------+-----------------+------------+---------+------+----------+------------------+----------+
| Id  | User  | Host            | db         | Command | Time | State    | Info             | Progress |
+-----+-------+-----------------+------------+---------+------+----------+------------------+----------+
| 357 | painy | localhost:52732 | painy_test | Sleep   |  563 |          | NULL             |    0.000 |
| 358 | painy | localhost:52733 | NULL       | Sleep   |  565 |          | NULL             |    0.000 |
| 363 | painy | localhost:53036 | painy_test | Sleep   |  384 |          | NULL             |    0.000 |
| 364 | painy | localhost:53037 | NULL       | Sleep   |  436 |          | NULL             |    0.000 |
| 381 | robl  | localhost       | NULL       | Query   |    0 | starting | SHOW PROCESSLIST |    0.000 |
+-----+-------+-----------------+------------+---------+------+----------+------------------+----------+
5 rows in set (0.000 sec)

sql

Probably just best to kill these processes and get on with our lives.

MariaDB [(none)]> KILL 357;
Query OK, 0 rows affected (0.000 sec)

MariaDB [(none)]> KILL 363;
Query OK, 0 rows affected (0.000 sec)

sql

Rails Custom Error Messages

Old blog posts I never published, except I have now #11

Rails.configuration.exceptions_app.call({'REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/404', 'rack.input' => ''})[2].body

Face

Old blog posts I never published, except I have now #10

O$$$$$$ZZO$NNDMMMMNDDDDMNNNNDNZZ$$7II$OOZOD88DO8NO8I?DD?$$$$7777III??????+++77I?
8ZZZZZZZZD$NDNMMMMDDNDMMMMND8OZ$$$$$ZO8D8OO8NNNNNDD$ID8?$$$$7777IIIDNDN$???7$7I?
DD8888OOODDMNNMMMNDDNDMMNNDD88OZ888DNDN88O8NNNNNNNNDZ8D7$$$7$Z8DDDDMMNN$II77II??
DOOOOOODDNDNDDMMMNNNDMNND8OOZ$O88NMMMMN8OZ8DDNMMMMMMNDD$Z$$DD88D88NMMNN7I??$$7?I
NNNNNMNNNNNN8OMMNDONNMMD8O7$ZZZ88MMMMND8OOO88DNMMMMMMMNZZ$$8DDD8NNMMMMNZI7?I7II?
8ODDNDOZZZZZZZMMNDNNMN8OZ$7ZZ$ZDDMNN8$7I$$$OO8DNMMMMMMM8OZZDDDDDDDMNNNNZ7?$IO7?I
ZOZONDD$NDDD8DMMNNNMNM88$I7$$ONNNDO$???I7$$ZOO8DDNMNMMMMDZ$8DDDDMDNNNNMOII7ZZ$7$
O8ZONN8$DODD8ND8O$7$7$77I?77Z8D8ZI??++?II$$ZOO888DNMMMMMM8Z8DDDDMMMNMMM8Z777Z7?$
OZZONNZZN8D8$8DMMMMNNZZ?III$ONNO+~:~=+??I$$ZZO8888DNMMMMMMZDDNNDMNMMNMMO77$??I87
DNNN8DDD88DDDDONNNND8Z=??7$$8ND$~::~=+?I7$$$ZZO88DDDNMMMMMMDDNDNMMMMMMMO$Z77III$
D8$DDD88DNNDDDZNNN8DZ+~+I7ZZNDZ?:,,:~+?I$$$7$ZO888DDNMMMMMMNDDNNMMMMMMMDO7IIII?I
ZOZDDZDDO8OOOO$NNNDZ7~=IIZ88D8+~,,,:~=+?7ZZ7$ZO8888DNMMMMMMMNNDNNMMMMMMDIIII????
$$ZD888D$OZZDD8NNN87=~+7IODNNO=,,,~=??=I$ZZ7$ZO88DDDNNMMMMMMNMMMMMMMMMMDII7?????
O88888OO8DDD8ZZMNO7~=+I$II8MDO:,=I7$$ZZZOOOOOO8NNMNMNMMMMMMMMMMMMMMMMMMNII??????
$8888ZO8OZ$7D88MM7===II$I$8MN8=+$$ZO8D8OZ$ZO8DDND8O88DMMMMMMMMMMMMMMMMMM7I????I?
87O888D$$$DDDZ$M8++~+I$$$78ND8+=7$DNZ8OZI~I8DDDONMDNNDNMMMMMMNMMMMMMMMMM??++++++
8+=7$ZZZZ$$$$77$?=~==IZ$8ZDMDOI?=+7OO8Z?~,+ZNDND8DDDDDDMMMMMMNNMMMMMMMDNII??????
7=+..+??????IIII====+?Z$88NNDZ+~,:~?I7:,,.~$DDDDD888DDDMMMMMMMNMMMMMMMMMIII?????
I77I7Z88DDDDDD8I=:=~7O$8ONMM8+:,,:=?+,,...,?8NNDDD88D8DMMMMMMMNMMMMMMMMMIIII????
NNNNNMMMMM8NNZZ7++I+?O$ZDDMM8:,,,~==~=:,...+$OD888888DDMMMMMMMMNMMMMMMMMIIIIII??
8DMNMIDMN8NM$Z8?=?==IOZ$ONDMN,...,:~===,.,:?$Z87NND88DDMMMMMMMMNMMMMMMMMIII?I???
8888DNMMMMMDOO7?=?+I7OD7Z88NN. ...,:~==,=Z$77DDD88888DDMMMMMMMN8ODNMMMMMI???????
O8O888888D8DIII?++?7$ZDI$Z8NN. ....,:~:::~+7.~+7ZOONDDNMMMMMMMND$DDN87IIIIII????
OOO88888D8DOZ$$7I=+?7$OIZZDON....,.,,~=+?77.:?7ZZO8MNNMMMMMMMMMNNNMNNZDNDMMMMMNN
OOOOOO888DDONZZI$~++IZZZZ$88N:$7+:.:=?I77$.:?$O8DZO8DDMMMMMMMMMNNDNDNNNMMMMMMMMM
O88888OZZ88DD7ZI$?==+$7Z$ZZ$OM$$..~:I7$O8 .+7$I~+88888MMMMMMMMMMN8DDNMMMMDNNNMMM
MMNMMMNDD888O$I+I=:+=7OZZZZZ7DM8Z..=$:===..:?~.:?Z88NODDDMMMMMMMND888DMNMNDMMMMM
NMNNDD8DD8NO$ZI??:+?I$$OZZ8OOZNMD,. .7D$...:~,,+?$I?Z888ONMMMMMMMDD8DOOZ$NNDNMMM
$DOZD$DZDDO$7$I=:I77O8Z88888OONMMN.........,:,=++=~=?Z8D8MMMMMMMMNDD88OOZZZNMDNM
8ZO88D8N88O77Z::+7ODDOO8$ZDD8DNMMND. ......,,~===++=?7$$8DNMMMMMMD8888OOOZZZZDND
O88DMONONO7.~=~I==78ND8Z$Z8O8DNMMMN,..   . .:~+==?+??+=?$88MMMMMMNOOOZZOOOZZZZ8N
D$8DZNONZ.....77~+?78DD8$$ZOO8NMMMNN.     .,:+==??I??=+I7ZOMMMMMNN8ZZZZ$$ZZZ$ZZZ
Z888DDZ.....=O=$I??IZDDDZ$8$8ONMMMMN...  ..,:===+I?++??7$ZNMMMMNNDOZZ$$$$Z$ZZ$Z$
M8D$OI:==?I?+I7:7?IIIDD8DOO$ZDMMMMNN8..   ..~=++???+?I$$$ZOMMMMNNDOZZ$$$$Z$$$$$Z
DD$+=~.......::~:?77$8ZDNDD7ZODMMMNND.   ..~=+++??++I7$$$ZMMMMNMN88Z$$7$7$$Z$7$Z
MZ+?+... .....~::.7$$O$8NMNZOZNMMMMD8~ ...:~=+??++?I7$$$ZDNMMMMNNDOZ$$777$$$777$
7=$O:..........~::.7ZOD$DNMND8DNMMMN8?....~=+???+?II77$$8NMMMMMNNDO$$7777$7777$$
~78O:...........=~:,7O8ZZNNNMDDNMMMN7....:~=+?++?777I7$O8MMMMMMMNNO$$777I$7I77$$
?DDI....~....~...+=~?ZO8$ZNNNMNNNMMO:...,=+++++I77777$ZO8MMMNNMMMNO$Z$777$I7777$
NNOI.....?.....=,,?+:IODZ$DD8MMNNMN+....:==+++I7777$$ZOZOMMMMMNMMNOZZ77$7III77I7
MNNO.....,....,.,=,I?+OZOOO888MMMN?....,~=+==I7777$Z$ZZZODNMMMMNMDD$Z$$Z77$77I77
MNN7....... ..,:~+==7?ZZDZONDDMMN7. ..,:==+??I7$7$ZO$ZZZONMMMMNNMNNZZZZ$7$777777
MM8,...........,,=+I77I$8O8NDDNMZ, ....~=++?777$$ZZ$$ZZDNNMMNNNNMMNZOZO$$$7777II
O8Z..............::=I$Z$Z8DNDDNN=.....:=+=~=7$$ZZ$$$ODZOD8MMNNNNMMMOZOZ$Z$$777I7
8ND...........::::::~=I$OONDDNDI. ...,~====+I$OOOZZ$Z8OZ8NMDNNMNNNNDO8OOZ$$7777I
NM8..   .......,~+===~~+$8NNNN8=....,~==+++?7ZOZZ$ZNOZZDONMMNNNNNDNNO8D8O$$$777I
DDD...........,..:=??+==+78NNN,....,:~===+?I$ZOZOZ$ZOZZ88DNMMNMMMNMOODDZ$OZ$$777
M88..........,:~:,,~?I??I?ZNN=. ..,:~=+=++I7ZOZZZZZZZZZZ8DNMMNNMMMDOO8$Z$8OZZ$77
MN8,..........,::~:,:+77I7ZD$, ...:~~=+++I7$Z8OZZZDOOZOZ88DNMMDMMNOOO8I777OOOZ$7
?NM8............,~=~~~+?77$O=. ..,~~==+??7$ZON8OOOONZZZOO8DNNMNMMNOO8DN8$NOMNOO$
MMMD..  ... .....,~===,,I$$I. ...:~=+++?I$$Z88DOOONMDDOON8DNNMNDMM888MMMZ88DMMMO

Plenty of *not right* going on

Old blog posts I never published, except I have now #9

I, [2015-01-22T07:15:59.225481 #30041]  INFO -- :   Rendered api/characters/_character.json.jbuilder (0.0ms)
I, [2015-01-22T07:15:59.225700 #30041]  INFO -- :   Rendered api/characters/_character.json.jbuilder (0.0ms)
I, [2015-01-22T07:15:59.225951 #30041]  INFO -- :   Rendered api/characters/_character.json.jbuilder (0.1ms)
I, [2015-01-22T07:16:00.561068 #30041]  INFO -- :   Rendered api/characters/_character.json.jbuilder (1307.7ms)
I, [2015-01-22T07:16:00.561527 #30041]  INFO -- :   Rendered api/characters/_character.json.jbuilder (0.1ms)
I, [2015-01-22T07:16:00.561772 #30041]  INFO -- :   Rendered api/characters/_character.json.jbuilder (0.1ms)
I, [2015-01-22T07:16:00.561993 #30041]  INFO -- :   Rendered api/characters/_character.json.jbuilder (0.1ms)
I, [2015-01-22T07:16:00.562210 #30041]  INFO -- :   Rendered api/characters/_character.json.jbuilder (0.0ms)
I, [2015-01-22T07:16:00.562425 #30041]  INFO -- :   Rendered api/characters/_character.json.jbuilder (0.1ms)

Blocking Chinese Bots from your Shopify store

After realising that Bot traffic originating from China takes up significantly more hits to a store than normal traffic, we’re taken steps to try and block traffic to get analytics back in good order. Here are the CloudFlare rules we’ve gone with.

Block CN + Other Regions

Blocks all traffic that Cloudflare sees as coming from China according to GeoIP.

(ip.geoip.country in {"CN" "RU" "KP" "SY"})

Block China Unicom ASNs

AS 4134 is the “ChinaNet Backbone” (China Telecom).

ip.src.asnum in {4134}

Block China Unicom ASNs

These are several ASNs owned by China Unicom. For example, AS 4837 is a well-known Unicom backbone. Cybercrime Information Center. AS 133118 and other ASNs (136958, 134543, 135061) are also associated with Unicom.

ip.src.asnum in {4837, 133118, 134543, 135061, 136958}

Block China Mobile / CMCC ASNs

AS 9808 is commonly associated with China Mobile.

ip.src.asnum in {9808}

Challenge Suspicious / Low-Reputation Requests

Uses Cloudflare’s threat scoring + bot detection to challenge likely bot traffic. Good for reducing automated “dead” sessions.

cf.threat_score > 20 and cf.client.bot

Fingers crossed this give us some respite.

Image

Paginated Array

Old blog posts I never published, except I have now #8

Came across a situation whereby we expected

Artist.where(:a => 'b').paginate(:page => 1)
class PaginatedArray < Array

  attr_reader :current_page, :per_page, :total_entries

  def initialize(values = [], options = {})
    @current_page = options[:page] || 1
    @per_page = options[:per_page] || 20
    @total_entries = values.size

    start = (@current_page - 1) * @per_page
    finish = start + (@per_page - 1)

    super(values[start, finish])
  end

end
def paginate(options = {}) PaginatedArray.new(self, options) end
Artist.where(:a => 'b').paginate(:page => 1)
Artist.where(:a => 'b').to_a.paginate(:page => 1)

MATOTU

Old blog posts I never published, except I have now #7

I wonder if this even still works…maybe.

module Braindeaf
  module Matotu
    require "java"

    # Swing
    java_import javax.swing.JFrame
    java_import javax.swing.JPanel
    java_import javax.swing.JTextField
    # AWT
    java_import java.awt.GridBagLayout
    java_import java.awt.GraphicsEnvironment

    class ShippingGUI < JFrame

      def initialize
        super('ShippingGUI')
        self.init
      end

      def init
        puts header
        @frame = JFrame.new
        @backpanel  = JPanel.new(GridBagLayout.new)
        @input = JTextField.new
        @backpanel.add(@input)
        @frame.add(@backpanel)

        @frame.setLocationRelativeTo nil
        @frame.undecorated = true
        @frame.default_close_operation = JFrame::EXIT_ON_CLOSE
        GraphicsEnvironment.local_graphics_environment.default_screen_device.full_screen_window = @frame
        @frame.setVisible true
      end

      def header
        header = <<-STR
  _____ ______       ________  _________    ________  _________    ___  ___
  |\   _ \  _   \    |\   __  \|\___   ___\ |\   __  \|\___   ___\ |\  \|\  \
  \ \  \\\__\ \  \   \ \  \|\  \|___ \  \_| \ \  \|\  \|___ \  \_| \ \  \\\  \
   \ \  \\|__| \  \   \ \   __  \   \ \  \   \ \  \\\  \   \ \  \   \ \  \\\  \
    \ \  \    \ \  \ __\ \  \ \  \ __\ \  \ __\ \  \\\  \ __\ \  \ __\ \  \\\  \ ___
     \ \__\    \ \__\\__\ \__\ \__\\__\ \__\\__\ \_______\\__\ \__\\__\ \_______\\__\
      \|__|     \|__\|__|\|__|\|__\|__|\|__\|__|\|_______\|__|\|__\|__|\|_______\|__|
        STR
      end
    end
  end
end
Braindeaf::Matotu::ShippingGUI.new

Keep your method to yourself with define_singleton_method

So define_singleton_method is quite cool.

Angel = Class.new do
  def dreaming_of_an
    whatever
  end
end

This is going to fail, whatever is not defined.

Angel.new.dreaming_of_an
(irb):3:in `dreaming_of_an': undefined local variable or method `whatever' for #<Angel:0x0000000107045b98> (NameError)

I can define whatever outside of the object itself.

Angel.new.tap { def whatever; 'aaaaaangel--iiii'; end }.dreaming_of_an
=> "aaaaaangel--iiii"

But what if I define this method directly on the instance

Angel.new.tap { def _1.whatever; 'aaaaaangel'; end }.dreaming_of_an
=> "aaaaaangel"

This seems cleaner

Angel.new.tap { _1.define_singleton_method(:whatever) { 'aaaaaangel' } }.dreaming_of_an
=> "aaaaaangel"

For bonus points, the method lookup fails for _2 because the second argument is nil and it defaults to the global method

irb(main):012> Angel.new.tap { def _1.whatever; 'aaaaaangel'; end }.dreaming_of_an
=> "aaaaaangel"
irb(main):013> Angel.new.tap { def _2.whatever; 'aaaaaangel'; end }.dreaming_of_an
=> "aaaaaangel--iiii"

However, we did manage to assign a singleton method on nil…which is fun.

nil.whatever
=> "aaaaaangel"