This video contains 6 individual talks

SF Bay Area Ruby Meetup - January 2025
+6
See all speakers
See all 6 speakers

Sections in this recording



Summarized using AI

SF Bay Area Ruby Meetup - January 2025

Irina Nazarova, Jason Swett, Bart Agapinan, Cameron Dutro, Alex Rodionov, and Claudio Andrei • January 16, 2025 • San Francisco, CA • Talk

SF Bay Area Ruby Meetup - January 2025

This video features the first meetup of the SF Ruby community in 2025, highlighting the continued importance of Ruby in the tech industry. The meetup emphasizes Ruby's role in powering startups across the Bay Area while fostering collaboration and community among Ruby developers.

Key Points Discussed:

  • Welcome and Updates: The event opens with greetings from Irina Nazarova, who shares updates about recent Ruby releases including Ruby 3.4 and their improvements in performance and memory efficiency.
  • Hosting and Sponsorship: Productboard is acknowledged as the venue host and sponsor for this meetup, highlighting their commitment to the Ruby community and their hiring efforts.
  • Talk Topics:
    • Easier Tests: Jason Swett discusses strategies for writing more understandable tests, arguing that clear specifications should guide test creation rather than verification approaches. He emphasizes reducing complexity in tests to improve maintainability.
    • Passkeys in Rails: Bart Agapinan presents on how to implement passkeys for better security in Rails applications, detailing their advantages over traditional password methods and the importance of user experience.
    • WASM in Ruby: Cameron Dutro shares insights into how WebAssembly (WASM) can enhance Ruby applications, focusing on compiling C libraries to WASM for improved performance in web environments.
    • Using Bazel in Ruby Projects: Alex Rodionov discusses Bazel, a build tool ideal for multi-language projects. He demonstrates how to implement Bazel in Ruby applications, focusing on its efficiency in handling dependencies and build processes.
  • Open Mic and Community Engagement: The meeting concludes with an open mic session where attendees share announcements, ideas, and questions, thereby strengthening community ties.

Conclusions and Takeaways:

  • The meetup underlines the vibrant Ruby community in the Bay Area, showcasing a platform for knowledge exchange among experienced programmers, newcomers, and startup founders.
  • Key subjects such as the evolution of testing practices, modern security techniques, the power of WebAssembly, and build tools highlight the diverse technical landscape Ruby developers navigate today.
  • Engaging meetups like this foster professional growth, collaboration, and the continuous evolution of skills within the community.
    Overall, the SF Bay Area Ruby Meetup serves as a reminder of Ruby’s powerful role while reinforcing community connections and encouraging shared learning.

SF Bay Area Ruby Meetup - January 2025
Irina Nazarova, Jason Swett, Bart Agapinan, Cameron Dutro, Alex Rodionov, and Claudio Andrei • San Francisco, CA • Talk

Date: January 16, 2025
Published: January 24, 2025
Announced: unknown

​Hey, Bay Area builders and tinkerers!

While Ruby may not dominate the headlines, it's the silent force behind the Bay Area's most successful startups. From GitHub and Stripe to Chime and Stackblitz, Ruby powers teams that go HUGE with less.

​Join us as we re-kindle the SF Ruby community's flame! It is a unique place to meet authors of your favorite open-source tools (or members of the Ruby core team), incredibly experienced engineers and new converts, as well as new startup founders building and experimenting with the power of Ruby and Rails. It's a chance to meet every month and make new Ruby friends.

Our meetups explore practical and explorational stories about Rails at scale, performance and maintainability, deployments, frontend, AI-powered features, and so much more—all through Ruby's uniquely productive lens.

For the first event of the year, Productboard is our gracious host!

Our special 🌟 speaker is Jason Swett, coming from Michigan!

Agenda:
5 pm Doors open, pizza
5.30 pm intro words, Irina and Productboard
5.40 Jason Swett (codewithjason.com) on How to make your tests easier to understand
6.20 Bart Agapinan (ID.me) on How to add passkeys to a Rails app (and why)
6.40 break
7.00 open mic
7.10 Cameron Dutro (GitHub) on WASM in Ruby
7.30 Alex Rodionov (Toptal) on using Bazel in Ruby projects
9 pm doors close

Submit a talk proposal here:

​​https://forms.gle/C9HQzaT5jvwA5xwc7


Catch you on the flip side! A massive shoutout to Productboard for hosting and sponsoring the event!

Brought to you by Evil Martians.

https://lu.ma/goujxu3a

SF Bay Area Ruby Meetup - January 2025

00:00:04.319 um everyone welcome welcome back to SF Ruby um
00:00:10.440 hey yeah uh Happy New Year uh and super super grateful to uh see all of you here
00:00:18.439 today and to kind of like begin this year together officially um and uh we have a pretty
00:00:27.720 cool um plan for today uh I'll begin with a
00:00:34.680 few a few updates okay uh I I will say the setup
00:00:42.120 today is a little bit tricky but we will figure it out
00:00:48.120 so the the biggest news is Jason SWAT arrived U to San Francisco and of course
00:00:54.800 he couldn't just come and not speak ATA Ruby yay
00:01:02.079 um so we'll hear from Jason in in a sec uh first is this
00:01:09.840 ah okay um uh I want to share something of course we had before between the last
00:01:16.640 Meetup and this one there were releases of rail Sid Ruby 3.4 and I I just saw
00:01:23.600 something I wanted to share yesterday um this is Maxim's uh post
00:01:29.680 about about uh uh Ruby 3.4 with white jet being not only faster more
00:01:37.079 productive but also less more memory efficient I think this is a cool new
00:01:43.159 update with the Y team is also now I think the team is now also optimizing for memory so I think it's it's pretty
00:01:50.560 cool um uh the other update is yeah we have this super cool page at SF Ruby oh
00:01:57.759 at um Ruby videos dode Ruby
00:02:03.560 video.de um and this is where you will find all the recordings including
00:02:10.039 including the recording of this one and we're also uploading the recording let's say every
00:02:16.599 time now um also there's this update um
00:02:22.360 there's this uh small rails shop gusta um and they reached out proposing
00:02:30.640 to sponsor our Meetup so I'm collecting
00:02:36.680 ideas what should we do yeah and one of the ideas for example one of the ideas
00:02:43.920 yeah just collecting ideas we could do for example we could do a a sruby scholarship for somebody uh so I'm just
00:02:51.319 thinking about something more meaningful because I think you all have a lot of merch already
00:02:57.519 um assuming um what way do they want to sponsor this in a way of giving us
00:03:07.879 money I mean yeah just just let's collect ideas as a as a commune
00:03:14.440 because swag what yeah um let's collect ideas um talk
00:03:22.840 to me after in the break or after this and uh also uh you can sh me an email or um
00:03:30.879 reach out on those social media um that's that um the next meet up
00:03:38.959 I just you didn't see it right no okay uh
00:03:44.720 what's your favorite venue for this Meetup product board is you can not no
00:03:52.319 no not product board except product Board second favorite second favorite
00:04:15.160 employer okay which one Center Center oh yeah but
00:04:22.079 yeah I I still want I still I I still I'm still looking forward to the hoko one you know the Salesforce yeah but we
00:04:28.919 we we're not the there yet yet yet um
00:04:34.080 yeah it's going to be at GitHub um yeah uh they they have a Prett
00:04:40.039 cool venue and uh we just we actually had uh notice
00:04:46.199 that the date is February 11 um two days ago if you went there to Luma SAU it was
00:04:53.240 February 13 kind of had to change a little bit um so that's going to be
00:04:58.280 February 11 uh please sign up on Lumar this is the only
00:05:03.680 way I can reach you um for free um
00:05:10.240 so yep hope hope to see you there and of course as as as always there is a link
00:05:15.400 to submit a cfp so please submit your talk proposals if I'm not responding to
00:05:21.000 your proposal reach out using this
00:05:26.720 email uh and yeah let's begin so um uh
00:05:33.080 now um clao from who's leading uh engineering at product board we'll tell
00:05:39.639 you a bit about where we are uh and uh we're super grateful for product to prod product board for hosting us for the
00:05:46.520 second time this year so let's give them a shout
00:05:54.600 out hey everyone so I'm gloud you I'm leading the San Francisco office
00:06:00.080 engineering and um how many know about prog board before
00:06:07.000 today okay it's a good it's a good amount so I to be honest I didn't know
00:06:12.840 much before I joined because what we do is actually we're trying to help
00:06:18.400 companies build better products right for their own customers that means we want to want to help them understand
00:06:24.639 their own customers and then take that information and act on it whether they're they're starting planning on and
00:06:30.000 thinking how Road mapping and putting everything in in motion right so this is
00:06:35.160 what we do we've been around for uh I think a company officially started 10 years ago but we launched around eight
00:06:42.080 years ago and uh we launched on ruon Rails um we've been uh it's still the
00:06:48.840 biggest part of our our code base I think and we got to be successful on
00:06:54.639 Rubi on Rails right so uh that's why we're hosting we're contining to hire
00:07:01.160 people not only Ruby on Rails but or Ruby but we're we're we're continuing
00:07:06.680 hiring right now we have open roll for our growth teams um and uh feel free you
00:07:14.160 know this Market I just read that meta is going to lay off on a bunch of people
00:07:19.680 so if you have friends if you're interested with hiring we have a great team here we have another great team in
00:07:26.160 Prague so we we travel between the the two offices is um with another perk so
00:07:32.199 feel free to say hi to us um say hi to me say hi to people that have Pro board
00:07:37.680 you met Jana and Sabrina at the entrance both downstairs and upstairs go and say
00:07:43.919 hi to them there are recruiters and uh yeah have fun today thank you thank you yeah cool
00:07:54.520 again yeah I think it's it's a pretty amazing front board just an amazing case of
00:08:00.120 growing on Rails being successful on Rails uh folks there there are few chairs there um please don't be shy to
00:08:08.360 pick a chair do we need more what do you think we might we might need more
00:08:15.759 chairs uh I think Marlin will help us with that yeah thank you um so uh with
00:08:22.039 that uh we get we have I know you're all cannot wait for Jason to talk to you
00:08:27.800 about uh writing B tests more tests that that are easier to
00:08:34.200 understand this is the criteria this is yeah this is let's talk about this after that we have Bart with pasis at uh how
00:08:43.800 to um like an intro to pass keys at ra applications in real applications and
00:08:49.040 then after the break we have camon uh who came up with something interesting with rubian wson h and then Alex um Alex
00:08:59.040 with puz which might might be something new for all of us to to learn today so
00:09:04.480 thank you so much uh again we have an open mic if you're here for the first time today you're not familiar with this
00:09:11.040 it's it's when it's when we all um kind of share short announcements or
00:09:17.040 questions or anything and so this is my call for you to mentally prepare and uh
00:09:24.760 prepare and share something during this Open Mic session um now uh Jason uh come
00:09:31.480 over and let's get Let's help you get set up uh Jason I don't know you want to
00:09:38.680 speak up or you want to try I can just speak up that's fine
00:09:44.440 okay but you you need to join Zoom this is the tricky part
00:09:50.720 um so you need to join zoom and you need to connect to
00:09:56.640 Wi-Fi folks sorry uh 30 second break uh
00:10:02.360 let board gu BB guest BB guest product board small letters
00:10:12.000 2020 just
00:10:56.040 turn turn
00:11:09.160 Shar theid like
00:11:15.399 this and then should just do entire screen no just do this do this
00:11:20.519 okay then okay do you want do you want the that' be
00:11:26.399 nice thank you
00:11:38.360 okay maybe not oh sorry
00:11:44.519 okay it doesn't work okay let I'll just do it manually
00:11:52.720 yeah set up so um folks how many of you U listens to one of the Jason's
00:12:04.600 podcast quite a lot so um Jason is U hosting a c City Ruby conference
00:12:12.320 um raise your hand if you're speaking at C City
00:12:17.639 Ruby this year it's going to be in May April when is it 11th and 12th 11
00:12:27.399 in um and Jon is the author of professional rails testing
00:12:32.880 book could you hold that up for me thank the first time I'm seeing
00:12:38.959 a physical form highly recommend it and also
00:12:45.560 the um what else what else so many things uh snail mail um yeah I have a
00:12:51.160 snail mail newsletter and I brought a bunch of copies to give out um it's I
00:12:56.279 I'll be talking about that during the talk but it's called nonsense
00:13:01.800 monthly so with that please welcome
00:13:16.040 Jason okay talk is about how to write tests that are
00:13:21.399 understandable um what is the biggest problem with most of the test Suites
00:13:27.160 that we work with let's have a little audience participation
00:13:34.399 Cameron yeah flaky flaky okay slow they
00:13:39.720 do exist but flaky they do exist but they're flaky okay slow uhhuh don't make sense that's a big
00:13:48.880 one yeah Okay so we've heard they're annoying is that what you
00:13:55.600 said yeah so there a problem speed flakiness lack of coverage over
00:14:02.560 mocking um I contend that the biggest problem with most of the test Suites
00:14:08.279 that I see is that can't tell what the is going
00:14:18.160 on okay so we're going to talk about that first a little bit about me I guess Arena already introduced me I didn't
00:14:24.720 know she was going to do that um author of professional rails testing host of the code with Jason podcast and I work
00:14:31.839 as a consultant and I'll talk more about that later um but I do Consulting on
00:14:37.440 topics including what I'm going to talk about today so why are our tests so hard
00:14:44.839 to understand does anybody have any idea why this is why does this
00:14:52.560 happen SK skill issue okay
00:14:59.399 um I think in order to answer that question we need to go a little bit deeper and ask ask ourselves some more
00:15:07.120 fundamental questions like what is application code and what are
00:15:14.639 tests might seem like a funny question to ask like what are tests we all know
00:15:19.680 what tests are but can you put it into words what is a test can anybody say
00:15:25.759 what a test is yeah you be testing something like that okay that's one kind
00:15:31.199 of test ABY testing yes go ahead definition of
00:15:38.160 functionality definition of functionality okay and yeah specification specifications all right I
00:15:44.759 wasn't expecting the correct answer so soon um but yeah a test the the way I
00:15:52.120 word it is a test is an executable specification took me many years from
00:15:58.600 when I started testing to when I realized that a test is an executable specification and I started putting it
00:16:05.800 in those terms um okay so that's what a test is and we can talk more about that but let's also talk about what is
00:16:13.000 application code so we have the test code and we have the application code what is
00:16:21.600 code the executable implementation of the specification the executable
00:16:27.639 implementation of the specification that's pretty
00:16:38.920 good oh a process that humans want to repeat over and over again by Machine
00:16:44.680 yeah can you say yours again me yeah yeah yeah an executable implementation of
00:16:53.519 specification an executable implementation of the specification
00:16:59.959 yeah that's interesting and Brad what did you say just a human process or a process
00:17:07.120 that humans want to have a machine repeat over and over yeah a process that humans want to repeat by machine over
00:17:15.039 and over yeah so okay so the conclusion that I arrived at is that code is
00:17:23.400 Machinery um application code is Machinery that creates a product
00:17:29.520 and so I mean product like in a really broad sense of the word like if an endpoint returns an HTTP HTTP response
00:17:38.559 for example that is the product that the code generates um and not to get too like
00:17:46.360 wacky but I realized after I thought about it that like it's not just an analogy like code literally is physical
00:17:55.640 machinery and it literally creates a physical product like code exists on a dis like
00:18:05.480 that's physical so the code even though we see it on the screen it is a physical
00:18:11.600 entity but we don't need to talk about that part um so application code is
00:18:16.640 Machinery that creates a product and tests are executable specifications for that machinery and you could flip that
00:18:24.080 the other way also and say tests are executable specifications and what was your definition
00:18:34.799 again for which one for for code for the application code executable implementation of the specification yes
00:18:42.440 yeah so I wasn't expecting such a thought-provoking answer so now I'm G to go have to like sit in my hotel room and
00:18:49.440 uh and ponder that um I have a talk that I've been
00:18:55.039 been working on the idea that you can write code without tests but you can't
00:19:02.120 write codes without code without specification and I actually checked
00:19:07.200 with the guy Creator rspec and confirmed stpc stands for Ruby
00:19:14.840 specification yeah all right so brief aside uh I I'll repeat some of that in case anybody didn't hear so you talked
00:19:21.760 with Stephen R Baker the creator of arpec and he actually told me the same
00:19:27.240 thing because I talked to him the other day and he said rspec stands for Ruby specification and that makes so much
00:19:34.159 sense he also told me that he doesn't use rspec the creator of rspec doesn't use rspec because it was never intended
00:19:41.760 to be used so we're all doing it
00:19:49.720 wrong um
00:19:56.159 right um so I would like to attempt to
00:20:01.240 persuade you if you're not already of this state of mind that in a system that
00:20:09.360 has tests and application code the tests are the main thing and the application
00:20:16.400 code is secondary um because the application code is subordinate to the
00:20:21.760 test it answers to the tests the tests only answer to your arbitrary desires
00:20:29.080 um which is not the way that most people think about it it's not the way that I thought about it for most of my
00:20:34.480 programming career um but after I worked with one of my Consulting clients for a
00:20:39.960 while for example he's like you know I used to think of the application code as
00:20:45.679 being the main thing and the test as being something secondary but now I think of the the tests as the main
00:20:54.440 thing um and I want to hopefully you buy this
00:20:59.919 statement also um if your tests are then your application code is
00:21:05.159 too uh I think that is necessarily true I'll also say I people usually find
00:21:13.120 this statement extremely controversial or so self-evident that it's not even worth stating that you can't have good
00:21:20.720 code without tests um does anybody not buy
00:21:26.840 that everybody buys that sorry everyone everyone
00:21:35.679 okay I work with people who will have a problem with that yeah yeah I and I
00:21:42.360 actually have interesting conversations there's it's interesting how some people
00:21:48.600 approach development in a completely different way that I ever could imagine
00:21:53.799 for example using like a dropping a the buger and like
00:21:59.640 looking around and developing your code as you are in the okay and then taking what you so sense you are kind
00:22:07.279 of but not right yeah I mean all code gets
00:22:15.000 tested one way or another yeah um okay so everybody here no nobody raised their
00:22:22.120 hand that they don't buy that statement I don't believe everybody yeah I just
00:22:28.039 feel it's and T signal mat so if I'm something my feedb is my
00:22:35.799 it's test so if you want to drive velocity unfortunately I've been I've
00:22:41.840 not WR a test because I'm like I don't know if this is going to be a strong signal of our customers and not spendo
00:22:47.919 much time on T but I actually don't like that so that's that's where I come from interesting I I've never been in a test
00:22:55.400 where maintaining it was worth can you please leave
00:23:03.480 um okay so for for any silent denters in the audience and in case you dear viewer
00:23:11.720 um don't buy my statement I have a rock solid um uh thing to to share with
00:23:21.080 you okay so my rock solid premise number one is you can't have good code without
00:23:27.640 periodic non-trivial refactoring I think that's pretty hard to argue against and
00:23:34.320 my second premise is that you can't do non-trivial refactoring without
00:23:40.320 tests and so if you buy both of those premises then what necessarily logically
00:23:47.919 follows is that you can't have good code without tests so if you disagree with me you're
00:23:55.000 just wrong
00:24:01.000 okay so if we want to have understandable tests there's a lot that goes into that but again first we need
00:24:08.679 to go down to the very fundamentals and we need to understand what tests are and
00:24:15.440 what they're for um so we've discussed it a little bit already but what is a
00:24:23.240 test spefication yes that's yes
00:24:29.000 um and what are tests for what's the purpose of
00:24:34.039 tests proof proof wait was that you who said that
00:24:41.480 the test okay that was you
00:24:48.559 okay okay okay does anybody have any additional answers documentation
00:24:56.600 documentation foot gun vention okay defect
00:25:03.440 prevention yeah protecting against regressions is maybe yeah shipping B production yeah
00:25:10.559 okay okay so here's here's what I came up with which I think is consistent with
00:25:15.840 all that um okay so I think that a test has two
00:25:26.399 purposes one is to describe the behavior of a system so you said
00:25:31.440 documentation I think this is basically a different way of saying the same thing um so it's like there's two audiences
00:25:38.640 for a test one is the human reader and the other is the system so it a test
00:25:44.679 exists to describe the behavior of a system and to enforce the described
00:25:54.279 behavior um and Bob Martin said this really interesting thing tests are about
00:25:59.760 specification not verification and this is a really subtle
00:26:06.600 but also really important distinction in fact the first sentence
00:26:13.799 of the first chapter of my book talks about the difference between specification and verification and I say
00:26:20.399 that if you get nothing else out of the book that distinction between specification and verification is the
00:26:26.440 one thing that I hope you get out of it unfortunately it's a really hard thing to get
00:26:32.960 across um um and we'll talk a bit more about this
00:26:41.360 concept as we go via VIA some examples um what makes I'm I'm gonna go back for
00:26:49.080 a second what makes a test understandable I'm going to pause for a
00:26:54.960 second because I feel like we've been going a little too fast for a second okay I'm caught up um what makes a test
00:27:06.760 understandable yeah a clear mapping to the implementation a clear mapping to
00:27:13.159 the implementation what do you mean by that it's not like convoluted you can just look like oh this specification the
00:27:20.200 first name is to be uppercase so you should be able to look at the same file
00:27:25.559 and see it's right there and find you should be able to find this ification very easily next to the impation and I
00:27:32.000 have to dig around for it yeah um this is hard to articulate it's like
00:27:38.360 the the test should be a good explanation of the of the behavior that
00:27:44.240 it describes but it's hard to talk about it without being kind of circular yeah I think it's just the two
00:27:51.240 need to be very you need to look at both and see them ideally in the same close to each
00:27:57.840 other so you can intu it what's going on yes you intu the implementation and
00:28:04.000 specification so okay I'm going to go to my my answers to this question does anybody have anything to add before we
00:28:10.320 go to the next slide yeah feto I thing need to fists uh are more understandable
00:28:16.440 when they're more concrete and simple yeah okay so feto said something really
00:28:21.519 important tests are easier to understand when they're concrete okay and I'm going to say
00:28:27.919 something now about specification versus verification I use an an analogy in the
00:28:33.399 book that I don't completely love but it's the least worst analogy I've come up with so far let's say you're visiting
00:28:38.919 a squirt gun Factory um and you want to make assertions about the design of
00:28:46.159 the Machinery that manufactures the squirt guns one thing you can do is you
00:28:51.240 can have a verification mindset and you can go to the factory line and pick up a
00:28:56.480 finished squirt gun and ask does this squirt gun work and you can test that
00:29:02.080 squirt gun and and say does this particular squirt gun that I'm holding in my hand
00:29:07.320 work or you can do the same exact thing grab a squirt gun but
00:29:13.880 ask is the squirt gun manufacturing Machinery working as
00:29:20.679 specified and even though you're using the same object in the second scenario
00:29:26.240 you're only using the squirt gun as a proxy to make assertions about the
00:29:32.480 Machinery so the difference is about your your target that you have in mind
00:29:37.840 when you're performing the tests this is why it's like a subtle difficult thing to to get across um but it's again the
00:29:45.320 Target that you have in mind
00:29:50.519 um okay so I think what makes a test understandable is first of all if you're
00:29:55.919 expressing it in terms of specification rather than verification it's easier to understand
00:30:01.919 if it's meaningful and what I mean by meaningful is if it targets ends rather
00:30:09.320 than means you guys have probably seen tests
00:30:15.240 that assert for the presence of active record
00:30:20.480 associations yeah useless thank you um why is it useless because it's a
00:30:27.960 configuration check I can just look at the model and see if there already it's it's like I believe rail should work I
00:30:36.640 don't need to test that I'm using rails correctly then you know I don't even
00:30:41.679 think that's a necessary part of the argument um because it has nothing to do with believing that rails should work um
00:30:49.240 but it's a common argument so I think it's important to address address that one it's not about believing that rails
00:30:54.679 should work if rails were to not work what would happen happen the behavior that that
00:31:01.039 Association enables would fail so you don't even need to believe real is going
00:31:06.480 to work or not because again if it doesn't work then your higher level
00:31:12.120 Behavior will will fail to work and if you have a higher level test then your test will
00:31:17.559 fail um so when you have tests for associations your testing the means to
00:31:22.880 an end not the end itself and it's redundant
00:31:28.440 because if you're if you have a test for the behavior that that Association
00:31:33.919 enables then again if that Association were to break then your higher level
00:31:39.240 test would fail so it's better to Target inss rather than means um and ins tend
00:31:46.799 to be uh at a higher level of abstraction and easier to understand
00:31:52.880 because it's like you test an association for example and it's like okay but like what does that mean that this Association like what does that get
00:31:59.120 me you don't know um but if you're testing the in feature then it's like oh
00:32:04.840 okay this is uh uh we need to be able to create a new customer record or something like
00:32:12.360 that I think tests are easier to understand when they're at the appropriate level of
00:32:19.039 abstraction and almost nobody ever does
00:32:24.159 this um when you open up most tests you're just presented with a giant wall
00:32:32.120 of details and the writer of the test is
00:32:37.799 leaving it up to the reader to look at all the details and think about all the
00:32:43.760 details and do the job of figuring out from the details what's the higher level
00:32:51.360 meaning and that is is wasteful because maybe you saved some
00:32:58.039 time in writing the test but now when people read it it's going to incur a cost every time
00:33:04.720 somebody has to read that test um and so it'll be much cheaper if you take the
00:33:11.159 work to add a layer of abstraction over your test so that you don't have to see
00:33:17.240 a whole bunch of details and figure out the meaning instead you can just see the meaning
00:33:22.840 directly um and then you don't have to think as much
00:33:27.960 the fourth thing here is cohesion uh what do you guys think I mean by cohesion in
00:33:36.200 tests similar Styles similar styles that could
00:33:41.960 apply what else what is cohesion
00:33:48.200 anyway holding together the way I Define cohesion is
00:33:55.120 grouping like with like if you look at a file and everything in that file is
00:34:01.360 about the same thing um then it has cohesion I think Ken Beck said uh
00:34:08.079 cohesion is when the same things in the file are tightly coupled with one
00:34:13.200 another in in a way that is appropriate for them to be tightly coupled he worded
00:34:18.480 it much more eloquently than that but it was that was the idea
00:34:24.560 okay how am I doing on time
00:34:30.320 um verification so here's an example of like a verification mindset versus a
00:34:35.960 specification mindset in the vast majority of the test Suites I look at the tests are expressed
00:34:43.000 in this first way it does X it does y correctly it Returns the right
00:34:49.520 z um it makes me extremely sad when I look at a test and it has the word
00:34:55.639 correctly in it why do you think that makes me
00:35:01.119 sad any ideas
00:35:08.480 yeah that's a good point I didn't even think of that in case anybody missed it what Cameron said was shouldn't the
00:35:15.160 tests always be asserting correct Behavior it's kind of Superfluous to say
00:35:20.640 that it's correct yeah that's the question what is
00:35:26.079 correct exactly so 10 minutes 10 minutes okay I didn't
00:35:32.320 mean to talk for so long um um um correctly sorry what did you say it asks
00:35:38.800 the question what does correctly yeah when you look at a test and it says it does such and such
00:35:44.880 correctly it's like okay but what does correctly mean so don't put the word correctly in your test instead say what
00:35:52.040 correctly means what's so funny fedo that I've done
00:35:59.119 terms of right so what I think is a better format for test is like under scenario a it does this under scenario B
00:36:07.960 it does this um and the other good effect of that is like sometimes when
00:36:13.800 you look at a test and it's like it does this it's like okay but like what's the
00:36:19.839 significance of that but if you say in this scenario it does this and you show an alternate scenario or like the
00:36:26.200 inverse scenario where it does this other thing it's like oh okay so like when this is the case it does this but
00:36:31.440 in this other situation it does this you can piece together the meaning more easily uh when it's like that okay we
00:36:39.880 already talked about some of the stuff I do want to share my definition of
00:36:45.680 abstraction because I think abstraction is a really important idea to me it's
00:36:50.920 the act of replacing lower level ideas with higher level ideas in order to make
00:36:57.000 a particular aspect of the world easier to understand thank
00:37:02.200 you I based on our uh short time together I value your opinion so I appreciate your thumbs
00:37:09.640 up um and by the way there's abstractions outside of programming I think we can
00:37:15.680 think about those and learn from those but we'll have to skip that for now cohesion is grouping like with
00:37:22.359 like okay so my talk is almost done here are kind of the things that I want to
00:37:27.920 leave you with um in order to write tests that are easy
00:37:35.359 to understand again I think it's it's really helpful to go down to the
00:37:40.520 fundamentals and give yourself a firm foundation of what are these basic
00:37:45.720 things what is a test what is application code what's the purpose of a
00:37:51.160 test and I challenge you to put these things as concisely as you possibly can
00:37:58.520 and commit the definitions to memory because I think you know there's so many
00:38:03.560 things in programming like words we throw around all the time test abstraction cohesion blah blah blah and
00:38:10.280 if we can't uh recite the definition of these ideas off the top of our head uh
00:38:17.720 then I I think it could be said that we don't we don't understand them as well as we could so I think it's a really
00:38:24.359 good idea to to do that exercise and these four things that make tests
00:38:30.640 easier to understand specification rather than verification meaningfulness remember
00:38:37.920 targeting ins not means abstraction replacing lower level details with higher level ideas in order
00:38:46.000 to make things easier to understand and cohesion um grouping lake with
00:38:52.079 Lake okay we already mentioned cinity Ruby but I want to tell you about it
00:38:57.880 it's either the 10th or 11th or the 11th and 12th I someday I'll uh I'll get it
00:39:04.000 right but it's on the website sincity ruby.com the dates on the website are correct uh 100 attendees maximum it's a
00:39:12.560 very small conference it's it's very in Intimate we spend two days together in
00:39:18.000 Las Vegas um and we have some good speakers again Arena and fto are speaking there
00:39:25.240 Chris Oliver from from go rails if you if you know Chris um and I'm not going
00:39:31.119 to read the whole list to you but but there's the rest of the speakers um
00:39:36.960 brief word about my Consulting uh to give a a quick taste one of the things that I often do is I'll work with a team
00:39:44.880 and we will have maybe a weekly or even daily session where we talk about this kind of stuff and I I challenge the team
00:39:54.079 with questions like what is the point of testing what is abstraction all these
00:39:59.680 kinds of things testing oop um project planning just like all
00:40:05.760 kinds of stuff and and we we think about these things and how to be more
00:40:11.280 efficient and productive and stuff like that again my uh monthly snail mail
00:40:20.200 programming newsletter can be found at nonsens monthly.com and I'm going to pass out some copies of this letter
00:40:26.040 tonight this is my book professional rails testing I brought a whole bunch of
00:40:32.760 copies with me but they're in my hotel room um are we going to like a drink
00:40:41.359 place a bar or something like that after maybe I'll go back and get them and give them to you yeah and here's all that
00:40:48.960 information in one place and that concludes my talk all
00:40:55.720 right uh thanks Arena
00:41:01.920 um have a clicker but I'll just I'll just use the key um yeah so tonight I'm
00:41:09.760 going to talk about um ask key authentication in Ruby on Rails I'll talk about very briefly what paskis are
00:41:17.839 um why or why why or why not you might want to use them um and then how and 20
00:41:25.560 minutes is not enough time to implement keys so I might be going through the code very quickly um I'll share the
00:41:31.480 slides and Sample code um so just heads
00:41:36.520 up um my name is Bart agapan I'm a senior software engineer at IDM um I'll
00:41:42.200 start we are Ruby on Rails shop and we are hiring so this QR code is for our
00:41:48.280 jobs board if anyone's looking um uh I've been a Ruby on Rails
00:41:54.040 developer since 2007 although really only ser seriously since 2015 um when I started doing it
00:42:01.040 professionally um so I love Ruby love Ru um this is my first uh SF Ruby Meetup
00:42:07.200 talk so thanks um and a quick disclaimer this is
00:42:15.480 a security and pasis talk I am not a security and pasis expert just an
00:42:21.359 Enthusiast so uh before you put any of this into production check with your security team
00:42:28.559 um so this is what I want to talk about in the brief amount of time what are pass Keys what are the pros and cons and
00:42:34.440 then finally how to add pass keys and I'll have a little um a demo at the
00:42:40.079 end so what are pass Keys um also known as web auen or web authentication it's a
00:42:47.040 uh worldwide Web Consortium standard for Secure Public key authentication for web
00:42:53.599 apps web web uh websites web apps um they claim to be more secure than
00:43:00.559 passwords and onetime passwords uh because they can't be fished um they are
00:43:06.440 they use strong cryptography um and they can be used for several
00:43:12.760 uh uh ways to keep people out of of accounts but uh getting into your
00:43:18.240 account it's can be used for two Factor mult uh two-factor authentication it can
00:43:23.599 be used for passwordless login and it can even be used for username list Lin B basically nearly fully Anonymous logins
00:43:31.839 but I'm not going to really talk too much about that um and then compared to existing
00:43:37.440 security methods these are some popular ones you may have seen uh username and password to factor off using uh
00:43:45.280 authenticators SMS email even phone calls my bank does that um and then
00:43:51.200 magic links and then just kind of comparing to pass Keys um T is often
00:43:57.920 fishable so somebody could call you up and and have a code sent to you and have
00:44:03.359 it read over the phone um many reasons that pass keys are
00:44:08.680 better um and I'm not I won't go through all this so list but um just take my word
00:44:15.680 for it um so pass Keys deal with
00:44:20.920 uh public key cryptography the private key never leaves your device there's a little asteris there that there are
00:44:27.839 cases where it does but um in a hopefully safe way it requires uh an
00:44:35.079 authorization gesture is what the standard calls it where user has to opt into using it um you can't write a pass
00:44:42.599 key on a post it and stick it on your monitor so it's already going to be more uh secure for some
00:44:49.200 people so obviously sounds like you should use them right
00:44:55.079 uh unfortunately there's been a lot of press recently about how pass Keys uh
00:45:01.359 aren't all that great so it depends um pass Keys it means you need to make a choice between usability and security
00:45:10.440 talk to your local staff engineer um so here are some recent
00:45:17.119 articles you may have seen these are just in the past few months um and of course dhh himself
00:45:24.680 wrote a blog post about how uh pass Keys maybe aren't aren't the best
00:45:29.920 so M Ruby Meetup Ruby on Rails Meetup talking about pass Keys hope this works
00:45:36.440 out um so actually I don't know how important it is to talk about this but
00:45:42.520 there are two types of pass keys of authenticators there's a platform authenticator and a roaming
00:45:47.839 authenticator platform authenticator is something like one password or your Apple echain uh roaming authenticators
00:45:55.040 like a UB key um your users are probably not going to
00:46:00.160 know the difference or that there these are even both pass Keys um and so that
00:46:05.640 can add some confusion that that you need to know when you're implementing this the um make sure that you're not
00:46:12.160 going to lock your users out or that um people will be able to use it when they expect to be able to use it uh I won't
00:46:19.079 be able to go into too much detail on that but all those articles that I just posted um go into that in some detail so
00:46:25.559 I'll share links to those if you're if you're interested um because things like this
00:46:30.720 can happen you can lose your UB key uh you can try to you can set up a pass key
00:46:35.880 on One OS and then try to use it on another and you may not be able to use it depending on what kind of key you set
00:46:41.480 it up with um you might delete your pass key um or through no fault of your own
00:46:48.160 maybe it just disappears somehow that happens to some people um and if that happens you may not be able to access
00:46:56.040 your account you need a fallback and so uh that's where the convenience versus
00:47:02.160 security trade-offs have to be made um so these are my non-expert
00:47:08.839 recommendations uh if you want to use pass keys for MFA you should probably have at least two forms of MFA either
00:47:16.319 two paskis or paskis and a authenticator or another um
00:47:21.599 two-factor um setup um or you're going to need to have a customer service
00:47:27.640 uh rep who can go and reset this stuff so that can be a cost um for
00:47:33.359 passwordless login you probably want to also have a password in case it doesn't work and then for username is
00:47:39.680 login like I said I wasn't going to talk about this but there may be are cases where it's okay to use it you don't care
00:47:46.000 about the the data so much or you have another way of doing account
00:47:51.520 recovery um so when you're deciding whether to use pass Keys just make sure you balance your user friendliness
00:47:58.599 because these are pretty user friendly if if used correctly um with security um
00:48:04.599 because making it more user SEC making it more user friendly can actually make your security go all the security uh
00:48:12.800 gains you made by using Pas keys can go away to make it user friendly and and recoverable and I'm not going to read
00:48:18.960 this whole list but um there's lots of things to consider despite that I'm still going to
00:48:25.480 tell you how to add p these derails um and maybe actually before I show you
00:48:30.640 that I'll just do the demo because I think this will make more sense when you
00:48:36.119 uh let you see how it works so I have written a small demo app
00:48:44.520 yeah all right yeah you're sure yeah um so the way this works is it's
00:48:52.760 just a signin and a you know a user settings page so I've written I have one account on
00:49:00.839 here and uh you'll notice I just have a username here not a password uh form so
00:49:07.040 this is a little different but what that oh shoot of course hold
00:49:14.319 on gotta run the
00:49:23.880 server yes this is actual running code right here here all right let's try this
00:49:37.520 again and of course I'm running it on a non-standard
00:49:45.040 port
00:49:53.079 nope okay all right never mind
00:49:58.799 my the server is
00:50:04.119 crashing I developed I ran it in Dev containers but now I'm not running Dev what did the demo do okay the demo well
00:50:10.680 I'll walk through the I'll keep going through here so the demo basically let you add pass keys to
00:50:17.119 your account um it showed you the UI for adding pass keys and then um logged in
00:50:22.960 with pass keys with passwordless login maybe I'll be able to get it working at the end um so I have a sample
00:50:31.799 app uh this is the the URL you can go check it out I'll also have a QR code at the end um this works with any recent
00:50:39.119 version of rails um this this Dem this uh setup example will be with JavaScript
00:50:45.640 using import Maps uh stimulus and uh in this case it was just the like rails
00:50:52.640 generate authentication setup um um so it works with
00:50:59.040 that um so it has two main dependencies a gem called webn and uh a JavaScript package called
00:51:07.599 webo and Json and so this is how you add them and then you'll need to add a pass
00:51:14.960 key model and connect it to your user um so a user can have many pass Keys um
00:51:24.359 and then this last piece is adding a pass key ID to this to your user model
00:51:29.839 that's actually optional there's other ways to do it but all of the um online examples that I found use that and it
00:51:35.400 makes just makes it a little easier um so this is the flow when you're creating a pass key for a user
00:51:42.640 you create on your server you create a challenge you send that challenge to the
00:51:47.680 browser excuse me the browser uh makes a API
00:51:53.559 call the JavaScript makes a call to browser to say click your authenticator or click
00:52:01.160 the button to uh create a pass key the authenticator will create a key pair at
00:52:06.799 that point and it'll be tied to the domain of the site that's um setting up the pass key and it will take the
00:52:14.559 challenge that the server sends and it will sign it using its private key and send it back to the server and the
00:52:21.200 server just verifies that the public key that is sent with this Challenge and the signed
00:52:27.520 um uh challenge match and if they do then they say okay good this is this
00:52:33.240 this authentication this authenticator matches uh or the the authenticator has
00:52:39.359 the private key that I think it does and then it saves that pass key to the user
00:52:45.440 um that's a kind of a simplified way of what it's doing but um generally generally speaking that's how it works
00:52:51.200 um so in order to do that you have to add a pass Keys controller um this is the code to do
00:52:57.160 that and then some routes you need to have uh a challenge route and then
00:53:02.520 there's the normal uh create and Destroy um so I'm not going to read
00:53:07.599 through everything here but the important stuff is um you have a form
00:53:12.839 that posts to your challenge URL um there's some stimulus controllers here
00:53:18.040 that are going to um kind of uh take the
00:53:23.640 data that comes back from that and process it a little bit more um and then send it to a third URL and
00:53:30.480 I'll show you the stimulus code for that um so this form just has a nickname for the authenticators that's just so the
00:53:37.720 user knows am I you know they can give a name like oh I'm using my UB key or I'm using one password they know which one
00:53:44.640 uh has been set up in case they ever want to delete it um this is the challenge code in the
00:53:52.799 uh the controller so this um again you don't need to look and everything but this calls the web aen uh Library it
00:54:01.200 generates a user ID that's unique and it generates options that are going to be sent to the the JavaScript front end um
00:54:10.319 and that's how uh that's how the authenticator knows um who this is for
00:54:16.400 what user this is for and what uh what domain um and you can see that it
00:54:23.079 excludes uh it has this exclude Clause there which basically says um don't
00:54:29.000 include any pass keys that are already associated with the user uh and then it stores it in the session uh so that it
00:54:35.760 can compare it later um this is the JavaScript this is a snippet of the JavaScript where it
00:54:42.680 creates it calls that web auen Json create that call is what tells the browser to ask for the user to you know
00:54:50.680 tap tap their key and then it posts that to the Callback URL which is also on the
00:54:57.400 which is the create endpoint on the pass key controller um sending the payload that the authenticator sent to
00:55:04.440 it um and then finally so this is the this is the endpoint that collects it it takes that um that payload it's feeds it
00:55:12.599 to the web offend credential from create method um and then it Compares what it
00:55:19.720 got with uh what's in the session so um it tries to pull
00:55:27.039 uh it creates a new Pass key on the user with the data that came back um and then
00:55:32.880 it verifies that that uh or this Pass Key credential verify is where it checks
00:55:38.760 that uh the challenge that it's sent matches the data that came back uh from
00:55:44.400 the authenticator and then it saves it um and then to log in that was it for
00:55:52.000 for that once it saves the pass key that pass key is now associated with that user in your in your system um to log in it's a very similar uh
00:56:01.000 process you create an you create a challenge in this case it's for authentication the authenticator already
00:56:07.640 has uh the same uh private key that's tied to the
00:56:13.559 domain that you're trying to authenticate to uh and sends back basically signs that challenge and sends
00:56:19.960 it back and then the your system will verify that the challenge matches one of the stored pass keys and because it can
00:56:26.680 be any of those pass keys that were sent before so in this case uh you probably already have a session controller if you
00:56:33.559 been using device um or if you use the the rails authentication creates a session
00:56:38.920 controller you need to add these two endpoints Challenge and
00:56:44.000 verify um in your url you add a uh a button to and not I'm kind of glossing
00:56:51.760 over the UI changes there there's a lot you might have to do to actually add the you know uh sign in with Pass key that
00:56:58.599 collection of the username first um whether you're going to do password list or whether you're going to do MFA or
00:57:06.240 even usern nameless um those are all kind of UI decisions this uh this
00:57:11.960 example is for um this could be used for either uh TFA or
00:57:18.240 passwordless um so this is just a button that's going to submit to that's going to post to
00:57:24.680 this challenge URL um it's going to send an email address that was previously
00:57:30.200 entered um and then there's going to be a login uh stimulus controller that's
00:57:36.000 going to intercept the data and then send it to the verify
00:57:41.079 URL um so this is what the challenge looks like you just assuming that you've typed in an email address already it's
00:57:47.520 going to load that User it's going to create a web aend credential
00:57:53.440 um it's going to say and this is a choice for you but in this case this
00:57:58.520 says any of the users pass keys are good for um logging in here you can if you
00:58:03.680 wanted to like restrict it to a UB key or restrict it to a certain Pass Key you
00:58:10.760 could do that um again you store the pass key
00:58:17.200 challenge into your sessions you can compare it later um and then we'll
00:58:23.680 scroll up uh this is later on in the same method
00:58:28.960 um you yes so you store the challenge in the session and then you're going to
00:58:34.200 send back those um that challenge to the to the front end this login controller is going to
00:58:41.920 intercept that um it's going to call that web Json in web off and Json before
00:58:47.760 it called create in this case it's calling get and get just means tell the user that I want them
00:58:53.680 to tap their key um and then it will send that data as Json to the Callback
00:59:00.720 endpoint that's the verify endpoint uh verify
00:59:06.000 action this will create a credential from the parameters that were sent by the authenticator it's going to load the
00:59:12.200 user from the session and then it's going to check all of the pass keys that the user has do any of them match the ID
00:59:19.559 of this uh credential that was just sent back and then um this verify call
00:59:26.599 uh is is where it checks if they match and if it does then it will update um
00:59:33.640 and log the user in um and what's not shown here uh I
00:59:39.079 guess you can kind of see it on the bottom any of these steps fail it just raises an exception so um your exception
00:59:45.000 handling is how you handle failed authentications um and at the end you also have to make sure you clear your
00:59:51.599 session um I tried the demo already I don't think it's going to work if I try
00:59:58.039 again because I'm missing uh actually you know what I'm just gonna try one
01:00:03.760 more time I'm going to try it with my Dev
01:00:13.680 container let's try this one more
01:00:23.559 time I close it no
01:00:34.039 down I'm going to start this up in a known
01:00:42.559 good
01:00:51.559 environment all right
01:00:58.000 okay hopefully this works
01:01:06.079 s uh Dev containers for the win um so I'm going to sign in with a user that
01:01:12.520 only has a username and password and not any um no pass Keys associated with so
01:01:19.160 when there's no pass keys this UI shows a password field I have a very secure password
01:01:26.680 that Chrome is going to tell me is not secure so this is my user I have no pass Keys associated with it and I'm going to
01:01:33.880 just add one I'm gonna add one password hopefully it's set
01:01:40.240 up and so yeah so you can see it I've
01:01:46.559 got um an offer to save a pass key so I'm just going to do
01:01:53.520 it it's saved and then you can see here now I have a I spelled one password
01:02:00.079 wrong but um so that was the nickname that I gave to this one and um I just
01:02:06.079 added this as a just a as an example but this is the the type that the
01:02:12.640 authenticator returned it says this is a platform authenticator and then I'll add a UB
01:02:18.240 key just for fun this in and then in order to do this and this
01:02:25.359 is one of the things it's a little confusing one password you have to click this and then you get another one and I
01:02:32.119 think here you can just hit the key doesn't say you can but that's how it
01:02:37.440 works so you can see this is a crossplatform pass key um so it's a
01:02:43.480 little different but now now my account has pass keys so I can log in with
01:02:48.920 it one password's nice and helpful here and in this case now it says sign in
01:02:54.160 with Pass key doesn't have the password anymore and now um that pass uh web auen get was
01:03:00.200 called and now I can just click the sign in button and I'm I in and that works
01:03:05.520 both with uh one password and with a physical
01:03:12.640 key yeah and that's
01:03:22.400 it do you have a question yeah have one password addon browser addon on
01:03:29.920 your operating system on the browser like would this not work is that two is
01:03:34.960 the private public key signing process something that's native to Chrome so
01:03:40.599 Chrome has a built-in Pass Key um implementation I believe uh Apple has
01:03:47.440 one built in so Safari can do it um I'm not sure I think Android has one
01:03:54.200 but I'm not sure about like fire Fox if there's a if there's a native one um but
01:03:59.240 there are lots of um like one password is not bit Ward and we'll do it I believe and um some
01:04:04.760 other
01:04:10.200 password yeah this is one of the problems with pass Keys is like not everybody knows
01:04:15.400 what's going to work and what browser it's going to work on and um it's it's not standardized yet so it may get
01:04:21.720 better question One browser sometimes the site we'll say I have pass Keys
01:04:27.640 and'll use Touch ID which is great and then other times it throws up QR code which is like no yeah what what
01:04:35.240 technically is going on in the implementation of the server that's doing that why why is there that difference the same so I think that's
01:04:41.799 not a server thing I think that's a browser thing um and in some cases like
01:04:48.440 you saw one password wants to be the authenticator but it allows you to fall back to using another one
01:04:55.960 sometimes I've had the apple one will jump in front and I can't use one password because it got there first so
01:05:01.799 it seems like they don't play nicely and that's part of what the problem
01:05:07.720 is can does handle this
01:05:13.720 natively uh I don't know it does use it oh okay
01:05:20.599 yes I thought yeah so none of this code was this didn't use any like special
01:05:27.520 software didn't use devis um it just used that webo and Gem but yeah if rodol
01:05:32.559 has their own implementation then this does not this demo does not apply but the the way it works should should be
01:05:39.119 the same yeah I think you mentioned you could use pass keys for anonymous sign
01:05:46.000 in but in the in the demo you were looking at by user ID how do you find the right Pass Key when you're doing
01:05:53.640 Anonymous do these pass Keys have IDE yeah so every Pass Key has a unique ID
01:05:59.240 um so that that would be how every user Pass Key then if you were doing usern
01:06:05.319 nameless login then the pass key would be basically sending you the user ID along with it's um the the answer to the
01:06:14.680 challenge so okay Co yeah I think that was it so this is a a
01:06:22.559 link to the demo app that I just was running um there's also these slides are linked
01:06:28.720 from that GitHub page um yeah thank you so
01:06:39.680 much uh repeat speaker at Ruby which is the main credential also um a co-
01:06:46.640 container of new component um which apparently sucks
01:06:51.680 I and second yes nice thank you the
01:06:57.119 employee of GitHub that's that's what we guess from from you company for sure for
01:07:05.039 sure well so I was goingon to do a really quick like um open mic thing but
01:07:10.079 two people mentioned react and so now I feel like I shouldn't say this but I think it's funny this is a very
01:07:15.200 important website that I found it says should I use react for that and it just says
01:07:21.680 no we have our answer
01:07:26.920 all right so yeah let's see let me make sure I have my like speaker notes and
01:07:33.279 whatnot need do like the full or the I I guess that might not be possible
01:07:38.920 with the zoom huh that's fine we'll just make it work okay have speaker notes for the me
01:07:45.640 up I mean I just want to be able to see the next slide it's fine um okay so I'm gonna be talking today about um using WM
01:07:53.520 A wasm compiled module from Ruby and I'll explain why you might want to do
01:07:58.839 that or maybe not um the first thing though that I want to say is that I know that product
01:08:04.720 board has offices in Prague right and I wondered if people from Prague are called programmers and they do
01:08:11.240 programming oh thank prog grammar is that a thing I feel like that might be a thing no okay
01:08:18.960 well um also my code that I'm going to present has no tests and I think that that's probably Jason I think probably
01:08:26.719 thinks that it's by definition and so you might not want to listen to this I don't know it's up to
01:08:33.759 you all right just a spike exactly yes um I'm
01:08:38.960 Cameron D work for GitHub um here's my Social Links and um our goal today is going to
01:08:45.040 be to compile a C library into a wasm module and then we're going to call those C functions from Ruby and
01:08:52.799 hopefully at the end we'll have learned how WM works this is the whole reason that I am giving this talk is I learned
01:08:57.880 a bunch about how WM works and I wanted to share that with somebody else and so that's why this talk
01:09:03.480 exists so what C library are we going to be using so onigmo who knows what onigmo
01:09:09.679 is nobody knows okay that's that's fine somebody raise it no onigo is
01:09:17.560 Ruby's regular expression engine it's actually a separate project you can find it on GitHub under K takata's username
01:09:23.960 it's used by other projects too I think I want to say python uses it as well um
01:09:29.080 and it's built into Ruby so most of the time you wouldn't actually I mean all the time you would just use Ruby's regx engine you wouldn't have to reach for
01:09:34.960 this we're using it because it is a non-trivial code base right so it does
01:09:40.120 something cool and useful um I mean everybody uses reg X's kind of whether they want to or not and it's because of
01:09:46.040 that it's like a decent wasm showcase so I talked about why I'm I'm
01:09:51.279 giving this talk um I learned some things about WM and how to use it from Ruby why did that happen um well it
01:09:57.199 happened because in 2022 or so I started working on Garnet JS and I talked about that at this Meetup before um there's a
01:10:04.560 URL for it it's really it's a implementation of the Ruby virtual machine in typescript the idea was that
01:10:10.040 it could be run in the browser and it's so it's RI implementation turns out that's really hard so I haven't actually
01:10:16.679 contributed to it for a little bit um but back in the day I needed to bolt on
01:10:21.719 a regular expression engine because Ruby has one and so garnet needed one and I
01:10:27.080 didn't want to write one because I'm just not smart enough to write a reg X engine and so I also didn't want to use
01:10:33.560 JavaScript which also has a regx engine because it's not 100% compatible Ruby so if I were to run you know the Ruby test
01:10:40.400 Suite it would fail likely on some of those tests it's not the same thing but I learned what's yes that's
01:10:47.239 right there were tests in that one so that was not terrible code as it turns out um but I could compile enigmo to a
01:10:54.480 waza module and then use that in the browser and that's why this whole sort of talk came about it's because I
01:11:00.400 originally wrote the code that you're about to see in typescript and then ported it to R for this talk to sort of showcase how it works it works very
01:11:06.520 similarly in both in both languages so so what is WM and we've heard about it I think a couple times at
01:11:12.640 this Meetup um it stands for web assembly WM it's a 32bit binary format so it's
01:11:19.960 not 64-bit which is super interesting at least I thought it was it's all processors these days are 64-bit
01:11:25.920 um it's executed by a virtual machine just like Ruby code the cool thing about it though is that it's embedded in the browser or Le it can be uh most browsers
01:11:33.360 support this now it's stack based like Ruby is um it focus on this linear
01:11:38.440 memory model so there's just a giant chunk of memory that's like all contiguous and you can read and write from that unlike what an operating
01:11:44.199 system would do in most cases which is to like fragment memory all over the place um and it also even though it's a
01:11:49.719 virtual machine it has pretty decent performance so that's why there are whole websites built in
01:11:55.960 with or with WM so if you're familiar with figma figma's actually built in C++ compiled to WM then runs in your
01:12:02.719 browser there's also a bunch of different like implementations and runtimes of wasm wasmer is a popular one
01:12:09.000 the one that I'm going to talk about today is called wasm time it has bindings to Ruby um WM 3 WME tiny WM is
01:12:14.960 an embedded version a lot of different implementations
01:12:20.360 yeah why would you use WM well it sort of offers the same benefits that Java
01:12:26.159 was supposed to offer right um so it compile it once you can run it anywhere it's got applications sort of you know
01:12:33.280 everywhere you can imagine desktop mobile embedded systems via tiny WM other things like that uh and of course
01:12:39.120 web browsers that's one of the big sort of reasons that I'm excited about why I'm you run code written in other
01:12:44.679 languages in your browser and that's I think we're just talking about stack blit stack blits does this a lot it's my understanding very cool so many
01:12:52.120 languages compile to them C and C++ sort of being two of maybe the the most um I
01:12:57.719 don't to say exciting two of the most sort of common ones that you might compile to to WM um so like people have
01:13:03.440 gotten postgress running in their browsers they've gotten FFM Peg running in their browser right all because it's
01:13:08.480 all written C can compile it to WM run your browser um but also go and rust and
01:13:13.520 Zig and even some of the net languages compiled to WM um Lua and even
01:13:19.400 typescript via this other project called assembly script yep and as put him on there
01:13:25.960 because you know poor Java really tried to make the whole compile once for everywhere thing happen so I thought
01:13:31.199 Duke deserved a little bit of a shout out there why not ffi why don't we just use
01:13:36.400 ffi to talk to C libraries well you can ffi stands for a foreign function interface and the idea is that it's uh a
01:13:43.239 lot of your the sort of the gems you know and love in the Ruby ecosystem rap there the rap C library is in ffi I make
01:13:49.239 calls to these C functions um and this is mostly enabled because Ruby itself is a c program so it can run
01:13:55.760 um you know code as well like link against code and c and and call those functions but in order to make this work
01:14:01.239 you need a c compiler if You' ever had to like install gems for rails app and it installs Noco giri and it's like building gem native extensions right
01:14:07.760 like it's having to compile lib XML behind the scenes for you and to do that it needs to have a seat compiler it
01:14:13.760 needs to have um you know that that code needs to be um you know written to work on your architecture and most most of
01:14:19.760 the time it works sometimes it doesn't issues with this um at least I have in the past like um it's potentially slow
01:14:28.320 and notoriously error prone sometimes you compiler errors make no
01:14:34.280 sense I think somebody joined and is talking Tanya LV
01:14:44.880 hello it's all good um and also the have this issue where like B you could provide you know like
01:14:52.239 pre-compiled binaries for um lib XML and for and that's certainly been done before um uh but that also means you
01:14:59.320 have to maintain that then so like every time that um flavor Jones releases a new version of XML or of um of noiri he has
01:15:06.400 to make sure that he also provides these binaries for each platform that somebody might be using Darwin Mac OS right um
01:15:13.320 Linux Windows all of those and you might not have access to a machine that can do that maybe you have a CI system that can
01:15:18.760 do that but those cost money right so the idea being that like having a binary per architecture and per OS can be
01:15:24.320 really hard and difficult to maintain right WM binaries on the other hand are
01:15:29.679 portable you compile them once you can pass them around they'll run on any system as long as you have a WM virtual
01:15:34.840 machine to run them on right so it's the same idea of java Java has a virtual machine installed on Mac or on Windows
01:15:40.960 you just download that and install it and then you can run your Java code same thing with WM um one of the big
01:15:46.120 differences of course being that WM can run again in your browser so let's look at uh this is this
01:15:51.880 is directly taken from the onigo Repository examples folder so it's a
01:15:57.239 little bit of C code and the only reason we're looking at C code is to understand the shape of how this Library works so
01:16:03.080 we have a regular expression object on N regx it's a pointer I mean it's a Memory address we have a pattern here don't
01:16:09.280 worry too much about all of the very like complicated like you know syntax of C here but the idea is we have a pattern
01:16:15.480 we have a string that we want to match with that pattern we call this onig new function pass in some parameters to it
01:16:23.159 um and that because we're passing a lot of the stuff by reference this is initializing that regular expression and compiling this this pattern that you
01:16:29.719 we're passing in and then to do a match we create a new region um we have some um pointers to
01:16:36.840 the the string in here and we pass those into this function called onig search and that returns to us an INT and that
01:16:42.920 int can be less than zero if it's less than zero that means an error condition if it's greater than equal to zero that means a normal
01:16:49.520 exit and so we can say okay what it also gives me the index at which the match occurred so I can print f printf means
01:16:56.320 like puts like puts this to the screen matched at position R right now and we can also Loop through all of the um the
01:17:03.840 region information that was captured here so there might be multiple matches right there's one sort of beginning match for the whole thing for the whole
01:17:10.040 match and there's different regions within that if there are capture groups which there are in this case uh and you can Loop through those with this numb
01:17:16.199 regs this region object now if that if it didn't work then we could also print
01:17:21.239 out an error message and then at the end because this is C we have to clean up after ourselves free the region and free
01:17:27.520 the regular expression okay so the general shape of this is we have a pattern the string we
01:17:33.480 call onig new get a region with onig new region new and then onig search and then we're
01:17:38.960 done so if we were to imagine a ruby API for this let's actually maybe say we do
01:17:45.000 write a test for this um we're going to do the same thing
01:17:50.159 right we have an a reg X object onigo onig regs regex sorry do compile we
01:17:56.400 match against a string and then we assert that what it returned to us is the correct information we got captures
01:18:01.639 and begin and end and match things like that so the idea would be at the end of this talk we could run this test and it
01:18:07.159 would pass so which functions do we need from onigmo we need onig new and actually
01:18:14.560 we're going to use onig new Deluxe because why not um onig region new a bunch of them and then also at the
01:18:19.960 bottom Malik and free and we'll talk about Malik and free in a minute those are lib C functions we need to use we
01:18:26.760 also need a couple of constants so onigo is really nice about like giving us all this cool encoding support so um we're
01:18:33.920 just going to use utf16 because that's what JavaScript used that's what this is ported from um we also need onig as key
01:18:41.080 or encoding ask key which is for the error messages that it returns to us um and then we're also going to do this case fold flag not important what that
01:18:47.040 is and syntax Ruby is just the flavor of enigma's uh the flavor of of of regex
01:18:52.520 we're going to use with it is the Ruby flavor so how do we compile onigmo to WM well
01:18:59.320 it's it's not that hard I I'll I have to say I really struggled with this because I'm not a c programmer I have not used
01:19:04.679 the C tool chain that much um but trying to to get this to work I there's a bunch of sort of prior art like cobbled
01:19:10.280 together and figure this out um so first thing we need to do is install the SDK a couple of other tools um and then we're
01:19:16.520 going to use clang or cang to compile using Target wazin 32 wazzy um wazzy by
01:19:21.800 the way stands for the web assembly system interface and it's the thing that let let you talk to IO so you can read files talk to the to standard in
01:19:28.600 standard out using wazzy we're going to link so after we we compiled a bunch of object files we'll
01:19:34.440 link them together with w and LD and then we can trim off some of the extra stuff using a couple of strip
01:19:40.840 tools we can do all this in a Docker container for portability okay so here's the clang
01:19:47.480 command and it's there's just some highlights here don't need to commit this whole thing to memory um this is
01:19:53.239 the build Target I mentioned before so instead of saying Target equals like x86 64 Linux or something you're saying WM
01:19:59.120 32 wazzy this is the CIS route this is where we find libc the optimization
01:20:05.360 level we're saying I want to build against libc this thing should have libc built into like it needs these these functions to to operate the lipy
01:20:12.080 provides bun of include paths so you know we're providing it a path to the current directory a path to the WM
01:20:18.480 directory which is where um actually I'm not sure why that's in there I'll have to think about that later um encoding directories in this
01:20:25.920 directory which is the where all the encoding support is here's all the C files we want to compile so once we run this code this this this command rather
01:20:32.800 we should get a bunch of object files. o files which correspond to each of the C files and they contain object code right
01:20:38.920 like we compiled these things into object code they're not executable yet or even a library they're just object
01:20:44.360 files and then we run aswm LD and this is super important so this is the
01:20:49.520 linking step and what we're saying is we want to be able to access these exports in our Ruby code so we need to tell the
01:20:57.080 Linker to export them so we can use them we also want to we don't have a main function this is just a
01:21:02.719 library um we're going to Output a file called enigma. WM it's the the linked
01:21:08.239 compiled final version and here's our search path again so we can link against lip C here we are linking against liy and finally we're saying like we going
01:21:14.120 to link all these object files together so just star. o these files okay so once
01:21:20.000 that's that's done oh and then we run our our strip command so just take out some of the extra stuff we don't need in there
01:21:25.520 so by the end of this process we've got onmo atwm and we can see that if we run WM object jump we can see all of the
01:21:31.960 exports that are in this thing so we've got our memory which is the thing we're going to read and write write to and
01:21:37.600 write to and read from um we have these constants that we needed like the UTF encoding one and the syntax Ruby one we
01:21:44.760 have Malik and free and onig free and all this cool I cut it off because there's too much here but all the functions that we talked about on the
01:21:50.159 previous slide those are now exported we can use them okay
01:21:55.199 so now we get to actually write some Ruby code and to do that we're going to bring in the WM time Gem and wasm time
01:22:01.719 is a runtime for basm that has Ruby bindings so it's essentially a um a
01:22:06.960 native extension that um is written in Rust and the nice thing about Russ it's a lot
01:22:12.920 easier to compile than C at least in my experience and it also you know includes pre-compiled binaries for a bunch of
01:22:18.600 platforms including my arm Mac OS machine and it allows us to call WM fun
01:22:24.520 functions from Ruby and we can also call Ruby functions from WM so it's it's it's kind of the the the key that sort of
01:22:30.040 unlocks all of the the WM goodness that we're going to do in a minute so let's start writing some Ruby
01:22:35.679 code we have our we require won time we've got our module enigmo and to sort of instantiate our WM
01:22:42.560 module we've got an instance class method here it's confusing an instance class method it's a class method called
01:22:49.480 instance anyway and in this thing we're going to make a new instance of the time
01:22:55.560 engine get the path to our module um the store thing not important what that is and then we're going to say instance.
01:23:01.760 new here and pass in our module the third argument by the way here is to pass Imports so you can pass like Ruby
01:23:07.639 functions that then can be called from code essentially those can also be passed in this this array at the end
01:23:13.440 here um and then that's what's we're going to return from this we memorize it and that's the start of our of our code
01:23:19.920 we're just going to be like essentially just like making this have this little function that gives us a thing that now we can use to to make calls to these
01:23:26.800 functions we're also going to grab these exports so WM time has this nice interface we can say like do export and
01:23:33.560 then in this case because it's a global constant there's a two Global method doget we're getting a reference to this
01:23:39.440 thing um we also um expose an instance or a a method for our memory object that
01:23:46.120 we're going to use to read to and write from or read from and right to um we'll see how that works in a
01:23:52.880 minute um Okay so now we're ready to do the first step of our process which is the the new function so onic new Deluxe
01:24:00.000 so to do this we're going to add a method called onig new Deluxe and we're
01:24:05.159 grabbing uh using our exports again grabbing that function this time instead of saying uh to Global we're saying two
01:24:10.560 Funk this is a function we want to call and then all we're doing is taking the arguments we're ped and we're just forwarding them on to to to to the
01:24:17.360 onigod new Delux function so it's a really simple way of calling it now there's a lot of sort of things we're not doing yet we haven't actually like
01:24:23.520 determined what we have to actually pass in here all we're doing is giving it like an entry point like you call this thing the C library will do something
01:24:29.400 and hand you something back and we also need to do this for malakin free because we also need to be
01:24:34.800 able to allocate and deallocate memory and so let's just add a little handy function that lets us declare these
01:24:40.679 these these functions more easily so now we can say exports Funk oning new Deluxe malikan
01:24:48.679 free so let's take a look at the C declaration this is in the header file for onigmo
01:24:54.360 this is the onig new Deluxe function you can see it takes five parameters so it also um references this
01:25:01.000 thing called an onig uar that's just a type def or an alias or an unsigned Char
01:25:06.400 so nothing fancy going on there um onig compile info is another struct and this
01:25:12.920 thing has a bunch of information about like how it should compile your regular expression um and then another thing
01:25:18.000 oning error info so if there an error happens this um struct will get populated with the error information and
01:25:24.760 we also need to pass one of these things in and so we can see that it has three Fields all of which are also pretty pretty standard or I would say we know
01:25:31.000 what they are okay so let's try to implement this so module Enigma we're going to do
01:25:36.239 compile info first we have our class here and we'll say size 24 that's because that struct we looked at a
01:25:42.520 minute ago has 24 bytes of information in it because remember WM is a 32-bit
01:25:48.000 format so we can let's just bring up our definition of that struct here we can just count these fields and find out how
01:25:53.679 much how bytes they take so an INT is four bytes um owning and codings are um
01:26:00.280 four byes those are just addresses or pointers to data structures right so those are all pointers are also just ins
01:26:05.840 really and so that's four bytes there and all the way down the line until we add those together and we get 24 bytes
01:26:12.880 whoops okay so we know that compile info struct is 24 bytes so let's create a new um instance
01:26:21.000 of this struct so we'll expose a method called create here on pile info and it's expecting you to pass it a ruby hash and
01:26:28.080 then in this guy we're going to say okay WM give me some memory give me 24 bytes of memory because that's how much I need
01:26:35.080 to represent the struct in memory and what Mal is going to do is it returns to me an address and I'm storing that in
01:26:41.800 this start Adder local variable and then because in C um a
01:26:47.000 struct's fields are packed as they're defined so we can just run down the list here and pull out all of these elements
01:26:54.520 of our of our hash right and stick those into an array and they're now in order
01:27:00.000 and then I can write those to memory at the starting address so when I called
01:27:05.119 Malo it gave me a a contiguous block of 24 bytes and that block of 24 bytes has
01:27:10.880 nothing in it it's it's probably garbage it could be zeros I'm not sure we're going to write over that with all of the
01:27:18.159 fields we were given and we're doing this in order to maintain struct order right we're using the pack function rub
01:27:23.960 pack function and we're saying lar just means take all like l means um a 32-bit unsigned integer so it's saying take all
01:27:31.159 of the items in this array and pack them into the 32-bit binary format and then we're going to write that so returns a
01:27:36.600 string and then we write that into memory at that address uh that start address that we got from
01:27:41.719 Malik okay and then we're going to return a new instance of compile info and all that we really need to represent
01:27:47.880 that is that address and so we say new here which invokes our Constructor our initialized function and we're just
01:27:53.520 storing that address in the the class so that's the whole that's all you
01:27:59.080 have to really do for this right we've got like a a bunch of fields we WR them to Memory return the address and we're good to go okay so how do we use this
01:28:05.440 thing in owning new Deluxe well we can just grab our we can define a nice
01:28:11.000 little extra function make compile info and what this is going to do is just give us a bunch of like sort of canned
01:28:16.600 options you could obviously change these or have these passed in somehow instead of doing it um you know like like just
01:28:22.880 can like this um but this is kind of how I started because it was easier I have no idea what num of elements means I've
01:28:28.960 looked through the code I cannot figure out what that means everywhere in the examples they just pass the number five
01:28:34.440 and so that's what I'm doing too no idea what that means um so yeah so that's the beginning
01:28:40.119 of our of our our regx class so we're gonna create some compiling so that's all it does so far next thing we can do and I'm going
01:28:47.040 to handwave over these structs these are just you could sort of follow the same pattern we did before look at the definition in the C code Define your
01:28:53.239 Ruby class pack the fields the right way so um first we create a pointer to a pointer to a reg X I don't know why we
01:28:59.000 have d double pointer there but that's in the code follow that that Paradigm we have an error info and then we also want to create our pattern and we have a
01:29:05.480 utf-16 string class that like takes your Ruby string converts it to utf16 writes
01:29:11.080 those bytes out to memory and gives you back the the address just like we're doing with compile info okay and now we can actually
01:29:18.159 finally call the new function um so we're saying owning new Deluxe we're passing in just like we saw in the
01:29:24.360 function signature we showed the C function signature we're passing in the regx pointer the pattern start and end addresses um the compile info address
01:29:31.639 and the error info address so hopefully what if everything has been hooked up correctly this will give us back either
01:29:37.719 an error code well I guess will give us back an error code or zero for normal exit so we can check to see okay did
01:29:43.960 this error code actually return correctly if it did then we can return a new instance of our reg apps thing and free some stuff in the process or we can
01:29:50.679 use this error code to string function get back at error code as a string and and then return that or raise a runtime error for
01:29:57.360 that okay I know we're going very fast but I don't have that much time I'm so sorry okay and then for onic search was
01:30:03.440 the last one we'll look at so this one also we have our C function here and this we're going to sort of we don't need to really do too much here we just
01:30:09.000 need to Define them here oning search oning region new and um to remind ourselves of what this looks like we'll
01:30:14.800 just paste it down there so in our regx function we'll for regx class we'll Define another function called search
01:30:20.920 this takes a string this is the string we want to match on there's a start and end position so we can determine like we
01:30:26.320 can say like I want to match only within this substring of this string um so we create our utf16 string
01:30:32.159 pointer um we grab a new region um and then we call search and we can just pass in our regx our start
01:30:39.159 address end address um and then we're GNA there's the the next two parameters are the um the the range in the string
01:30:47.600 so like if I want to start at character three as opposed to character Zero that's how I would do this you pass in those positions why am I multiplying two
01:30:56.199 here any
01:31:03.440 ideas okay that's because this is a utf16 string utf16 strings are always
01:31:09.080 two bytes so the start address is going to be start position which is a logical position times two which is the number
01:31:15.119 of bytes the bite offset within the string to start searching same thing for end pause um
01:31:21.320 and then that's essentially all we have to do for for search um there's obviously a little bit more to handle that exit code so we'll say Okay um in
01:31:29.360 this case we're looking for and to notice this negative2 variable this negative2 value rather comes from the C
01:31:35.080 code I had to read through that to find this um so if the error code is less than negative2 that means an error occurred and so we'll do the same thing
01:31:41.440 we did before error code to string raise that as an exception um if there's an onic mismatch in other word I think
01:31:47.880 that's negative 1 if negative one occurs that means there was no match found so we can just return n otherwise there was
01:31:53.920 a match found and so we can use our there's another struct called match data that also on Enigma defines um and
01:32:01.560 that's I haven't shown the code for that but essentially we just pass in our string and our region and it gives us back like this nice Ruby array of
01:32:09.840 matches and then we have to clean up after ourselves okay so let's see if it works okay great we make a new regx and
01:32:17.560 you can see that the instance variable reg apps here is just a number it's just a it's just an address into wazin memory
01:32:22.600 it's not actually a a ruby class or anything other than a number okay and we
01:32:28.000 going to say read. search and that gives us back this very nice match data object with our captures in it so um because
01:32:35.000 ABC matched the a right there it matched characters four comma five it's the range of characters that matched um so
01:32:41.080 yes it works okay I don't remember what this is oh yes our test now I I wanted to make
01:32:48.400 sure that I you know included a test here because of Jason's talk um and I decided to rename it a little
01:32:54.920 bit
01:33:00.040 oh so I don't I don't I don't know that seems better than the previous version I think yeah I think so all right so we
01:33:06.400 compile we're gonna do the same thing we showed on the previous slide we're just GNA match here and we assert that all of
01:33:11.840 these return the right values I'm sorry we check the specification that uh this particular
01:33:18.480 example uh adheres to the specification yes thank you yeah thank you um I I
01:33:23.840 thought so I didn't think it was you know at all but I I could be wrong all right that's it thanks for
01:33:38.119 listening so that was a huge worldwind the code is also on GitHub uh if you want to read through it in at more
01:33:44.400 Leisure um there's two projects here Enigma WM is the C library that has like
01:33:50.199 the clang command in it and whatnot and then odingo Ruby is the bindings to to that to make it work in
01:33:56.480 R any questions before I step down do we have time for
01:34:01.560 that so you got it Ruby runs in the browser no problem um with Garnet you mean oh boy
01:34:09.679 no no it doesn't um opal is amazing though check out opal it's another implementation that does run the
01:34:16.520 browser
01:34:22.360 yes um no I mean so Garnet's a really fun project but I
01:34:28.480 kind of hit a wall on it I didn't know how to make any more progress and so I kind of gave up on it um it was more of a learning tool anyway if I'm being
01:34:35.159 honest right um so so you know it's some point it'd be cool to pick it back up again but I
01:34:42.080 mean at this point I think we'll just let it let it let it live on GitHub and
01:34:47.520 you know die die of a dignified death
01:35:07.600 you the the JavaScript code if you're interested
01:35:13.880 yeah all right thanks everybody this this is Alex's first
01:35:19.320 presentation at of Ruby um and he works at Dr and contribut to open
01:35:26.159 source yeah tell tell us about yeah I have a slide so thanks um so apparently
01:35:33.520 there is a lot common with Cameron's talk in mine I just realized that first of all we use the same slides H the same
01:35:40.840 template but I changed colors but if you look at it's actually quite the same
01:35:45.920 yeah um i' also talk about things that compile and basically the thing you use
01:35:51.159 to compile um and
01:35:57.159 uh I there was something else but I kind of forgot let me just start um so we're
01:36:02.280 gonna talk about basil I'm very happy to be here and have the opportunity to talk about my name is
01:36:09.600 Alex I work as a quality architect at topt I also do a lot of Open Source work
01:36:15.040 um I have uh written a small Cleo manager called Mac if for Mac OS it's
01:36:20.119 very popular maybe some people here use it maybe not um I also uh maintain
01:36:26.480 selenium so you probably guys all know the selenium project to some extent I
01:36:32.320 I'm responsible for everything related to Ruby there and basil we use basil
01:36:37.400 there as well uh apart from that I have started a project called alumnium doai
01:36:43.320 it's an open source project for uh AA power test automation you're welcome to
01:36:49.400 check it out but it's in Python it's kind of not relevant uh but for the
01:36:54.639 purpose of this talk what matters is I'm also a member of the basil rule special interest group where I maintain
01:37:02.400 everything uh like the support for Ruby language within the basil ecosystem and that's what we're going to
01:37:08.440 talk about today so um does anyone here knows what basil
01:37:13.719 is okay how do you know Cameron if I may ask comp used it
01:37:25.520 large yeah okay I remember the third thing you mentioned that figma uses is written in C++ and compiled so figma
01:37:33.080 uses basil um a lot of different companies stripe uses basil and many
01:37:40.199 many different uh big tech companies uh basil is just a build and test tool that's kind of it so we have rake we
01:37:47.719 have make we have a lot of different tools for building basil is just one of them but um it was was created at Google
01:37:55.639 and it was created at Google you can imagine that it has a very specific um
01:38:02.119 requirements in terms of how it works B Google has monor repo and uh basil was
01:38:08.040 created for monor repo specifically and then at some point it was open sourced um and uh now it's I
01:38:16.320 think it's an Apache 2 project um so it's a build and test tool created at
01:38:21.719 Google designed for monor repos by the way uh does anyone uh here not know what
01:38:27.159 monor repo is so our monor repo is just like when you put all your code in a single G
01:38:35.360 repository or maybe a different version control um all your languages all pretty
01:38:41.239 much everything and then it creates a set of challenges with tooling and basically is
01:38:47.000 a solution to many of these challenges um so it's for monor repos
01:38:52.360 it's polyglot meaning it supports all different languages in your repository you can have Java you can have python
01:38:59.320 Ruby obviously um you can have rust um and it all works with basil and because
01:39:07.400 it was created at Google it is extremely efficient it is extremely scalable because it can support huge monor repos
01:39:14.320 like magga repositories with millions of source code files and uh it kind of
01:39:20.400 created um a set of design choices and trade-offs that they've done uh while
01:39:28.159 developing Google and one of them is that it has to be hermatic and by
01:39:33.599 hermatic I mean that when you use rake or make you use it through your operating system right uh like you
01:39:40.880 install make on your operating system and then you use this binary but uh in basil everything is managed through
01:39:47.199 Basel so like if you want to use if you want to compile something with basil it will actually download compiler put it
01:39:53.000 into sandbox and uh it won't really pollute your operating system it will
01:39:58.679 use that um and the last point we'll get back to this remote caching and remote
01:40:04.560 execution I kind of show you through the demo why it is important to mention okay
01:40:11.000 so basil is a build tool so what is uh build in basil um and if there is one thing that
01:40:17.400 I want you to uh take from this talk is that build in basil is just take a bunch
01:40:23.199 of of inputs run some action over them and produce output so inputs are files
01:40:29.040 maybe you have some uh Ruby source code like add RB remove RB and you have a gem
01:40:35.560 binary these are the inputs and then you run action with this input so maybe you run this G binary and you give it a
01:40:42.960 build command and you give all the source code files and it produces uh a gem file that's your
01:40:49.760 output and then uh what you action produce as an output can be input for
01:40:55.320 other actions so maybe you build several gems and then you want to package them in your Docker image so you run Docker
01:41:02.360 build over this inputs and that creates a tar file which is a Docker uh Docker
01:41:09.520 container image um and that's kind of it so you have a bunch of inputs you run
01:41:14.679 some actions that produces new files that can be inputs to other actions and so on and so on and I mentioned that
01:41:22.119 basil is also a test tool and test is kind of very similar to that you have
01:41:28.000 inputs your source code your test files your maybe the rspec binary and then you
01:41:34.000 run rpec and it gives you some kind of test result that uh and an exit code of
01:41:40.599 course um and that's pretty much it and I guess you can see the pattern that uh
01:41:47.000 all your build targets all your test is essentially becoming a huge graph within your uh source code so some files are
01:41:55.760 inputs for some actions and some files are inputs for some other actions and they kind of all wrap up together in one
01:42:03.159 huge graph and basil creates this graph builds it for you keeps it in memory
01:42:08.560 caches it and then operates over it it's a graph and it's very efficient in a
01:42:14.480 sense that um if for example you work on your source code and you change the test
01:42:19.760 file and you tell Basil hey build an has everything for me it knows that okay it
01:42:26.119 doesn't have to run gem build because you didn't touch these files uh you only
01:42:31.159 change a test so you have to rerun rpec and you don't need to run Docker
01:42:36.400 build or gem build uh because it's not relevant um I also mention it's polyglot
01:42:43.599 and because inputs and outputs are just files it can be anything right so maybe
01:42:48.800 you have some static assets that are not relevant for testing and building the gems but they are important for
01:42:55.679 packaging the docker image or maybe you have front end that you want to go
01:43:01.480 through some JavaScript tool chain and maybe Minify and again if you run your
01:43:08.199 specs it doesn't matter it's not an input uh but it is important when you
01:43:14.239 package everything inside the docker build and uh it's polyglot and when I
01:43:19.960 say it's polyot it's truly polyglot so it has built-in support for Java and python these are I guess because Google
01:43:27.520 uses those extensively um and whatever programming
01:43:32.560 language you have in mind name it there is a a support for for it there is no JZ typescript go rust rust has native
01:43:40.599 support for compiling to basm by the way um there is pack there is special rules
01:43:46.440 for Docker open container initiative Helm and Ruby so let's talk about Ruby
01:43:51.719 that's kind of where we're here um so Ruby is just a module for Basil
01:43:57.320 that supports provides native and decent support uh for for the programming
01:44:02.800 language so as I mentioned um basil will install all the
01:44:07.920 dependencies for you so it would the rules Ruby would install Ruby it would install dependencies through bundler and
01:44:14.199 then provide some kind of a conventional common interface that you would use to
01:44:20.040 uh build gems uh run tests run Rubik script and we'll show I'll show all of
01:44:25.280 that in a second but basically you can think of it as just a module within the
01:44:31.000 ecosystem all right demo let's try to do that and um is that big enough folks in
01:44:39.119 the back can you see that okay a little bigger is that
01:44:45.280 better yeah fine okay um if it's not good enough just let me know um okay so
01:44:52.280 for the demo I have a ruby gem um it's fairly I guess uh straightforward so we
01:44:58.719 have some gems back it's calculator I didn't came up with anything better than that um there is some files like
01:45:06.960 calculator there two classes to add and subtract and it's uh very simple just
01:45:12.920 you know basic operations uh basic math operations
01:45:18.719 um there are some specs for them like calculator aded works correctly corly
01:45:23.800 and subtract works correctly so we can run this it works um and and then we
01:45:33.000 have a Jem file with our spec and rubocop so probably I have some rubocop we can run it yeah fairly easy right
01:45:41.159 nothing nothing fancy um and yeah by the way I use Ruby 3.3 H maybe that matters maybe not uh
01:45:49.440 but yeah it's try I'm kind of trying to give it simple so um let me start by adding basil to this
01:45:56.199 and kind of show you what it does and then I hope we get to a point why you
01:46:01.639 might want to do that like why why what's why does it matter so um we start
01:46:07.639 by saying that there is a special file called module basil it's basically a way for module for Basil to say okay this is
01:46:14.199 a basil workspace this is a root and there you declare um what kind of
01:46:19.840 languages do you need so here we have we say we need rules Ruby uh and it will install the uh the module and then we
01:46:27.760 say um install please install Ruby for me um so just read it what's in Ruby
01:46:34.159 version file uh install it and then fetch some dependencies uh uh through
01:46:40.800 bundler so we need gem file we give it gem file gem file log the gem speack and
01:46:46.119 the version RB that's because the gem spec requires version RB so we have to
01:46:51.480 explicitly say these are inputs okay um and then we have uh build file and build
01:46:58.679 file is just uh where you actually Define the targets what you want to
01:47:03.880 build what you want to test so uh if I tell basil now to build and I use triple
01:47:11.520 dot this means recursively build everything it's not going to do anything you can see that it created some folders
01:47:17.920 these are Sim links for convenience uh but it hasn't done anything
01:47:23.440 because there are no test targets and even though we said we need Ruby we we we don't have any Targets that require
01:47:29.840 Ruby um so nothing has happened so let's try to do that let's try to add some
01:47:36.239 Target um and for example what we're going to do is um and uh GitHub copilot
01:47:44.119 is smart but not smart enough um the language uh by the way the language is
01:47:50.400 called starock it's uh looks like python it's not really python but it has things
01:47:56.679 like cycles and U loops and uh functions
01:48:01.840 so let's say uh we're going to build a gem okay so let's call it
01:48:07.040 calculator and it needs some inputs inputs are source code files so we're just going to take everything inside the
01:48:13.320 liap folder so this is I guess a regular glob expression um no dependencies we also
01:48:20.880 need to say where is our gems pack um
01:48:27.040 and that's that should be it so let's try to build it and let's see what
01:48:33.560 happens it worked yes awesome so you can see that it uh build a gem and it
01:48:40.719 actually prints the output from gem command and it put it somewhere in basil
01:48:46.280 Bean folder so um Let me refresh that yeah you can
01:48:53.199 see it over here and maybe we can unpack it and see what's inside so there is
01:49:00.719 this well these are our source code files right nothing else this is everything that we declared as an input
01:49:08.280 um so let's let's remove that um what happens if I do it again nothing
01:49:15.480 it says it you see it does not attempt to build again because nothing has changed in the inputs we said that for
01:49:23.320 these files do this and as long as nothing has changed in these files it's not going to do anything because what's
01:49:30.400 the point right the result is going to stay the same um okay so building gems is nice uh
01:49:38.199 what about rubocop um let's try to add rubocop so rubocop is a test in a sense
01:49:47.239 like you run it it returns an exit code uh one if it failed um an exit Code Zero
01:49:55.119 if it passed so let's just run rubocop over the same source code uh maybe let's
01:50:02.320 add specs yeah I think it kind of makes sense maybe let's add gem file I think
01:50:08.280 rocop runs over gem file and gems pack as well so these are all SRC file these
01:50:14.880 are all inputs for ruoko um and in order to tell basil to
01:50:21.880 actually run rub op because it's maintained by bundler we need to tell to
01:50:27.480 use a a rubocop uh binary that is available through bundler and it's it's kind of an
01:50:34.880 LS here so what what is here is the same thing that we have used here so can be
01:50:42.360 anything but uh the main thing is um we need to tell which binary we run and we
01:50:50.480 need to tell that we need the whole fondler dependencies to do that uh so if we run
01:50:59.800 now let's to basil test triple dot again run all the tests that are available you
01:51:06.520 can see that it actually installs bundler and installs a bunch of gems and then it runs the test and it
01:51:13.639 fails um and it failed because there are some legitimate
01:51:20.079 offenses uh but I yeah I think fail because I did not use rubocop yaml as an
01:51:27.320 input so let me just add it um here there is this you might be wondering why
01:51:33.880 it's not part of the src's field um it's a it's a kind of decision that basil has
01:51:41.719 made at some point that src's are just uh language specific files like Ruby
01:51:47.360 files in our case and data is everything that is not so but essentially it's the
01:51:53.400 same thing it's just a convention so we run the test okay cool they're passing
01:51:58.719 now and you can see there is no output uh by default if it's passing
01:52:05.079 there is no output but we can treat that and yeah you can see there is this
01:52:10.760 regular ubac okay cool it works you might be
01:52:16.679 wondering why what's the point to do that like you can still run bnda of CH
01:52:21.960 build so let's try to make something a bit more reasonable and fun let's try to
01:52:28.639 add some specs um so to add specs I'm going to create a build file inside the spec
01:52:35.480 directory that's where we are uh hold where we host or our tests and let's add
01:52:43.920 the same RB test and let's define a test for our first spec
01:52:49.599 file so uh we're going to call it a add and um we're going to include our spec
01:52:58.639 file oh sorry that's adpack RB and similar to how we use droc cope here we
01:53:06.040 need uh these two Fields um Main arpack and bundle that I
01:53:13.440 think that's it so let me try to run all the tests inside the spec
01:53:20.920 folder um you see that it R spe add and it failed and if we look at it it failed
01:53:28.440 because it actually cannot load calculator and it makes sense because for this test
01:53:36.880 all we have is just the test itself so the way basil works is that it will take
01:53:42.960 all the inputs put it in a separate folder in a sandbox environment isolated from your operating
01:53:50.239 system and then run our spec there because of that there is no calculator there so we have to declare somehow
01:53:56.760 dependency over it and um in basil you can't refer to files
01:54:04.480 from your parent folder because that would break the isolation and that would
01:54:09.639 potentially allow you to escape uh from the sandbox uh at all so the way you
01:54:16.280 would do that is by declaring proper dependencies inside this uh inside the
01:54:22.520 Le folder so let me let me just quickly show you how you would do that so again
01:54:28.199 we create a build file um this time let me just copy this time we say it's U it's not a
01:54:36.599 test it's just a ruby library and what we're going to do is have um we're going
01:54:44.079 to declare a library for each source code files you can join them but for the
01:54:49.480 purpose of this uh presentation we um we want to have them
01:54:55.880 separated okay um sorry typ yeah looks
01:55:02.520 good um yeah I think it looks good yeah um
01:55:08.440 and then we expose it the calculator RB and we need to do the same for all the
01:55:13.920 files inside sub directory so let's do that let's add one for
01:55:20.880 add RB um let's do the same for
01:55:26.840 subtract and let's do the same for version even though we don't care about it that much so um and
01:55:36.280 now if we switch back to our tests we can say that it actually depends
01:55:42.440 on calculator. ADD um calculator um
01:55:49.719 there is no such thing calculator do subract and the calculator file that is in the original
01:55:58.040 folder and try to run the test again so now basil we think that I need to put
01:56:05.000 these files to the same folder too and it fails because there is another
01:56:12.360 Concept in basil called visibility rules and we'll get back to it I just wanted to show you that it fails by default you
01:56:20.320 can't refer to things that are not uh publicly declared which
01:56:26.360 kind of might make sense in when you work in a huge code base um so let's just fix that real quick we're just
01:56:32.719 going to say that everything is public inside this folder and everything is
01:56:37.800 public here and rerun the tests and finally they're passing okay um cool
01:56:47.040 again why would you want to do that makes no sense just run our spec um so
01:56:52.480 let's add another test for subtract and we're going to run the
01:56:58.719 subtract spec actually we don't need subtract for adding test right it makes
01:57:05.280 it is not used there likewise we don't need add uh RB in in our subtracts spec so
01:57:12.239 let's try to run this two again um uh sorry
01:57:20.280 typo Okay so it run two tests one thing you can notice is that it it run two things not
01:57:28.360 one this is because by default every test is run in its own process in its
01:57:33.400 own directory with its own set of inputs so if you if you have a lot of tests um basil
01:57:41.520 out of the box would allow you to paralyze them and scale them as much as you want like use all the cores um but
01:57:49.599 another thing that you might notice if you run the test is that it says it's cached so what does it
01:57:55.040 mean it means that basil took all the inputs uh calculated them and cached
01:58:02.239 them run tests and as long as you don't change the inputs it's not going to rerun the tests because nothing is going
01:58:09.079 to change so if we go to let's say add B and uh dog add some changes and run all
01:58:18.639 the tests you can see that subtract Act is cached it's not being runed in the only
01:58:25.560 rerun tests that matter um and that is one of the most
01:58:32.480 important things uh with basil is that you can work on your source code as long
01:58:38.360 as you build everything properly you can work on your source code and you can run
01:58:43.440 all the tests and it will only run things that makes sense that matter for this
01:58:48.880 particular uh based on your on your changes and
01:58:54.520 um another thing that I that is very important is that uh because you can't
01:59:01.760 just refer to uh classes and constant that non-exist in your uh test you can
01:59:08.000 uh catch uh you can kind of set the boundaries within your application so
01:59:13.599 like I know many people use packwork these days which provides you a way to isolate uh part of your sour code and
01:59:21.800 particular folder and not allowed to use it so think of it as um basil provides
01:59:28.400 this out of the box it's kind of steroids because if for some reason I say well subtract now depends on ADD and
01:59:35.599 I try to run the test the test will fail because there is no such
01:59:41.079 file and I can fix that of course I can say that actually
01:59:46.880 subtract depends on ADD and now my test is passing uh but if you compare it for
01:59:53.360 example with packwork what you can do easily with basil is you can ship
01:59:59.199 everything to production and not worry about things breaking because you refer to constants that non-exist but you can
02:00:05.840 also run all your tests and catch uh violations that uh catch the like the
02:00:12.800 code using some other code it's not supposed to be using okay and
02:00:18.719 um um yeah let's let's revert that didn't make much sense anyway um but
02:00:26.400 yeah so that's that's that's the main uh thing with testing so you cache
02:00:32.400 everything and then you keep track of uh changes so for the demo uh couple other
02:00:40.920 things I wanted to show is that once you start adding these build files and you kind of takes a bit of work but ENT it's
02:00:48.960 it's it's not very complicated but then when you operate on large code base and you have multiple teams working with the
02:00:55.639 code you can start using the same build and test tool as a way to notify when
02:01:01.800 you for example want to change something so like if we uh we can add
02:01:07.079 deprecations and they will be printed we can uh we can
02:01:13.119 declare uh actually the whole source code as um let's do it private and then
02:01:20.400 we will see the same errors during the test execution as you've seen before um so yeah think of it as
02:01:29.360 a packwork plus a test Runner plus everything uh like a build tool and um
02:01:37.760 that is that that works out of the box and and with a lot of extra uh nice things uh I wanted to
02:01:46.520 mention we I mentioned the remote caching and remote execution um and I
02:01:52.159 want to kind of uh explain why I think it's very important so you've seen how it things
02:02:00.239 are being cached in your machine so you run test once and then you can rerun them as many times as you want it's not
02:02:05.480 going to do anything but because everything is input and file um you can take this cache and you
02:02:13.280 can put it in in the cloud and out of the box basil for example can put your
02:02:18.480 local cach to Google cloud storage and then some other developer in your team
02:02:23.760 would clone the repository run the same basil test and as long as their inputs are the same it's not going to again
02:02:30.400 it's just going to download the cache and uh you're not going to he's not going to run these
02:02:36.360 tests um likewise take it further everything is an input and file why not
02:02:42.760 we just put it like take the inputs put them to remote machine run the actions
02:02:49.280 there that will produce some other files and then we download these files and that's called remote build execution
02:02:55.920 again supported natively by basil uh there are open source servers you can run in your infrastructure there are
02:03:02.599 paid services that provide that but um Dev containers and a lot of
02:03:10.040 tools exist for remote execution and basil kind of have it out of the box with a big difference that you don't
02:03:17.079 really containerize anything you keep working in your local computer you just change the code in your ID and then
02:03:23.679 basil would package that applaud run something intensive there scale you can
02:03:29.119 run the test maybe with a 100 course parallelism that all happens inside the
02:03:34.360 cluster and then you get the results back um an example for that selenium I mentioned uh is massive monor repo so we
02:03:42.440 have Java we have rust trby C++ C all of that works through
02:03:49.520 basil and um it was RCI was taking like several hours to run everything and then
02:03:57.239 once we adopted basil once we uh started using the remote cach in remote
02:04:02.599 execution now you open the pr and you get it runs all the tasks and it gives
02:04:07.679 you like five minutes it runs everything that you actually care about because you changed these files and the rest is just
02:04:15.119 cach and it's uh it's so powerful uh concept having the inputs cashing them
02:04:23.159 and producing the outputs um so I think that was that that could be compellent
02:04:29.440 right so we have a lot of uh paral parallel tests and different solutions for that um and basil have this out of
02:04:36.480 the box um yeah so I think at this point
02:04:44.119 um uh that's mostly it I just wanted to say thanks uh some links for Basil the
02:04:51.760 is a GitHub repository for the rules Ruby we have a basil slack with Ruby channel so if you have any questions or
02:04:58.000 you would like to give it a shot uh you can go there and ask questions I'm there
02:05:04.400 um and then there is my email if you if you're not a fan of slack just shoot me
02:05:09.760 a message thank you very much I hope I managed to convince you that there is other build tools except for out there
02:05:25.559 question um about the adoption I think that's what people think about uh so you
02:05:31.159 mentioned selenium case when you guys were know adopting and um how much effort you take
02:05:40.920 um I would say it wasn't two Engineers I think
02:05:46.400 working on that and selenium being an open source project you never have full-time people working on it
02:05:52.360 but we have Stuart who is uh one of
02:05:58.520 the uh core contributors of Basil so he knows it very well but I would say it
02:06:05.159 took us maybe Year and that that's that's kind of
02:06:11.119 painful but again selenium being very specific we have a lot of languages uh and we actually written the
02:06:17.960 rues Ruby because nothing like that existed uh first lenum project so the
02:06:24.800 adoption is complicated um and I would only suggest to do that if you have a
02:06:30.040 monor repa with multiple languages then it's worth it otherwise no just just use
02:06:35.119 rake there's no
02:06:41.400 point for like a typical cicd pipeline say you want like build a Docker image
02:06:47.920 and then push it up somewhere how do how does basil fit in like the last last step to get it get your image live or
02:06:55.079 something like that so what we did is um you use rules oci that's a thing that
02:07:03.520 takes your inputs and uh builds a tar at a Docker image then that is pushed to
02:07:10.480 repository like a regular Docker uh registry but on top of that it's used as
02:07:16.199 an input for Helm package so uh in a release pipeline you just run uh
02:07:22.800 something like basil run deploy that will build the image that will then trigger the helm and uh at that point it
02:07:30.800 will finish
Explore all talks recorded at SF Bay Area Ruby Meetup
+44