We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Developing and maintaining a platform with Rails and Lotus

00:00

Formal Metadata

Title
Developing and maintaining a platform with Rails and Lotus
Title of Series
Part Number
21
Number of Parts
89
Author
License
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Identifiers
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
This talk illustrates the development techniques, Ruby patterns and best practices we adopt at DNSimple to develop new features and ensure long-term maintainability of our codebase. It also features how we used Lotus to develop the new API as a standalone Rack app mounted under the Rails router. Two years ago we started a major redesign of our REST API with the goal to decouple it from our main Rails application and expose all the main features via API. It was not a trivial task, but still feasible due to the guidelines we adopted in the last 6 years to structure our Rails application.
81
Software testingMultiplication signSoftware frameworkObservational studyState of matterArithmetic meanLevel (video gaming)Computing platformComputer animation
Game theoryService (economics)MereologyDifferent (Kate Ryan album)Formal languageCartesian coordinate systemShared memoryDecision theoryDomain nameSoftware developerCodeRevision controlConnectivity (graph theory)Multiplication signGame controllerClient (computing)Software frameworkSinc functionGroup actionVariety (linguistics)Public key certificateNormal (geometry)Endliche ModelltheorieDirect numerical simulationSingle sign-onRootStandard deviationSet (mathematics)MathematicsFocus (optics)Representational state transferAdditionBitMessage passingOrder (biology)TwitterCore dumpRight anglePresentation of a groupTheoryImage registrationDemoscenePhysical systemRow (database)Domain nameExecution unitMonster groupData structurePurchasingLevel (video gaming)Software development kitComputer animation
Cartesian coordinate systemCASE <Informatik>Row (database)CodeEndliche ModelltheoriePoint (geometry)1 (number)File formatModule (mathematics)INTEGRALNumberObject (grammar)Software maintenanceSystem callLibrary (computing)Interactive televisionIntegerImplementationExtension (kinesiology)Fiber (mathematics)Query languageConnectivity (graph theory)String (computer science)Software testingArithmetic meanSingle-precision floating-point formatRevision controlDefault (computer science)Game controllerSensitivity analysisMessage passingTerm (mathematics)DemosceneRepresentational state transferDirect numerical simulationProduct (business)Independence (probability theory)InformationWordHypermedia2 (number)Dependent and independent variablesAdditionElectronic mailing listMultiplication signMereologyIntegrated development environmentParameter (computer programming)Adaptive behaviorResultantKey (cryptography)Plug-in (computing)Bounded variationDirection (geometry)Projective planeWebsiteBitoutputOrder (biology)Formal languageSet (mathematics)Validity (statistics)Element (mathematics)Normal (geometry)Computer animation
AreaFamilyInjektivitätService (economics)Game controllerCartesian coordinate systemNormal (geometry)Domain nameCodeRow (database)View (database)Endliche ModelltheorieData storage deviceCore dumpAbstractionMereologyConnectivity (graph theory)DatabaseBitInformationLine (geometry)Term (mathematics)Suite (music)ResultantoutputException handlingMessage passingError messageFunction (mathematics)CASE <Informatik>Single-precision floating-point formatExecution unitAuthenticationParsingForm (programming)LogicIntegrated development environmentLevel (video gaming)Validity (statistics)Data structureImplementationParameter (computer programming)Dependent and independent variablesINTEGRALGroup actionWeb pageTask (computing)Interactive televisionLibrary (computing)Rule of inferenceTime zonePhysical systemSoftware testingSeries (mathematics)Position operatorPrice indexServer (computing)DivisorObject (grammar)Attribute grammarState of matterSystem callTheory of relativityRight angleDirected graphArithmetic meanTouch typingAdditionComputer animation
Right angleConnectivity (graph theory)Context awarenessMedical imagingGame controllerGoodness of fitEncapsulation (object-oriented programming)View (database)Core dumpCartesian coordinate systemAxiom of choiceData structureLatent heatSoftware testingInheritance (object-oriented programming)Set (mathematics)Point (geometry)Library (computing)Source codePhysical lawHuman migrationLevel (video gaming)Focus (optics)Term (mathematics)Data storage deviceComplex (psychology)Endliche ModelltheorieLogicService (economics)InformationNumberEntire functionKey (cryptography)Integrated development environmentSoftware developerSoftware architectureMultiplication signDomain nameCoefficient of determinationAbstractionProjective planeData miningBenchmarkSoftware frameworkParallel portQuicksortSemiconductor memoryImplementationOntologyDifferent (Kate Ryan album)Line (geometry)Product (business)Sound effectFrame problemObject (grammar)MereologyFile formatBit rateCodeDependent and independent variables2 (number)Direction (geometry)DemosceneMathematicsRow (database)Form (programming)Adventure gameComputing platformSingle-precision floating-point formatTask (computing)DataflowGroup actionOrder (biology)Independent set (graph theory)Router (computing)Pattern languageBitSystem callLaceDatabaseComputer animation
Multiplication signMereologyPhysical systemFunctional (mathematics)Presentation of a groupCartesian coordinate systemRevision controlGame controllerRule of inferenceSoftware developerPay televisionProgramming languageInformation privacySerial portInformationVideo gameInclusion mapModule (mathematics)Representational state transferForm (programming)Different (Kate Ryan album)Formal languageLink (knot theory)Group actionImplementationCodeSheaf (mathematics)GravitationSocial classMessage passingSoftware frameworkRight angleObject (grammar)DatabaseService (economics)Parameter (computer programming)Order (biology)Traffic reportingLibrary (computing)Domain nameInheritance (object-oriented programming)AnalogyCodecCASE <Informatik>Process (computing)Price indexLogicINTEGRALData structureDebuggerSoftware bugImage resolutionSpeech synthesisValidity (statistics)Directory serviceSystem callDependent and independent variablesSoftware architectureSymbol tableBitProgrammer (hardware)Data miningSingle-precision floating-point formatRoutingComputer animationXML
Computer animation
Transcript: English(auto-generated)
Thank you very much for coming, all of you. I know it's the last talk. Actually, it's the first talk after the lunch on the last day. So I'm pretty surprised that there's still
people going to talk. So thank you very much for being here today. It's really, really an amazing opportunity for me to be here because it's RailsConf and because I work for a company that somehow was born at one of the Rails conferences. So it's really, really super exciting for me to be here.
Today I'm gonna talk about developing and maintaining a platform with Rails and Hanami. As you can see, the title is slightly different than what you have published on the program because by the time I proposed the talk and the talk was accepted, the framework changed the name. So that's why you can see like Rails and Hanami today.
So sometimes I will mention Lotus, but actually Lotus is Hanami today. So keep in mind that's the new name. Now yesterday, I was struggling. I was trying to find a way to break the ice because normally I'm really, really super nervous when I am on stage. And so I was talking with my coworkers and I was like, what can I do?
And they were saying to me like, we have candies. I was like, yeah, but I cannot throw candies on the stage. And I was like, why not? So I wanna start the talk when I make sure that all of you are focused and getting my attention by just playing a new game, which is throw and catch the candies.
So hopefully I will not hit anybody. Here we go, you can take some of the candies. Here we go. And we have more candies over there, in the front here, here, and over there.
Okay. We have more after, don't worry, don't worry. Just stay focused, that's the goal. You have to stay focused. You have to catch the candy when I'm throwing them. Okay, great, let's start. So my name is Simone. I work for DNSimple. My nickname is Webos, pretty much everywhere, GitHub, Twitter.
And I work for DNSimple, which is a company who provides domain name registration, DNS hosting, and SSI certificates. Now, you might have or not heard about us, but probably you heard about some of our amazing customers. So we provide service to Ruby gems. So if you install any of the gems,
you use our infrastructure. We provide service for RaceConf, and many other services, including Travis CI, for example. Now, before I start getting to the core of my talk, I have a very important public service announcement. This is not my birthday today.
Now, 99% of you won't get this joke, but I know at least two people over there will get that, so you can ask explanation after that. But it was really important for my safety to say that it's not my birthday today. Okay, let's get started. Today, stop laughing.
So today I wanna tell you a story, and this is a story about a feature we have been working on for at least a couple of years at DNSimple. So what I wanna do today is I wanna share the decision that we made during the development of this feature, and I wanna share with you some of the behind-the-scenes that facilitated the adoption of the decisions
that we made. Now, the story is about a new component to our system, which is the new version of the API, API version two. Now, it's not gonna be another talk about building the API with Race or any other framework. So if you came here with the idea of not hearing
about building API, that's the right talk. But, of course, I had to spoke about some experience, and our experience would be close enough to the idea of the API. Now, we have been providing API in DNSimple since the very beginning, since 2010. That's a really core component of our service. And our customers uses our API for a variety of reasons.
For example, registering domains, or provisioning new DNS records, for example, through Chef, or purchasing SSI certificates. Now, the way people interacted with our infrastructure back in 2013 and 2014 was, I'd say, a traditional way. So we have the DNSimple application,
and we have a normal standard set of traditional API. But we wanted to do something different. We wanted to extend, to provide, to shift the focus from this standard set of API towards an idea of a platform, a toolkit, for building things around DNSimple.
And so in 2016, actually in 2014, we started re-engineering our API, and so as of today, we still have the standard set of API and application, but we also started offering OAuth, we started offering webbooks, and official clients in different languages.
Now, interesting part is that besides the clients that are the official clients that are now developing in different languages, in addition to Ruby, pretty much the rest of the things you can see here are designed and built with Ruby, okay? So now that I shared a little bit more about the background, what I wanna show you today
is essentially three different messages. The first one I wanna share with you today, the DNSimple approach with Rails. I wanna present you the Anami framework, which is the framework that we used in order to build this new part of DNSimple,
and I wanna show you how you can use Anami or any other framework to interact with Rails. This is actually possible. So let's get started with the first part of the presentation, the DNSimple approach to Rails. Now, our approach is a little bit different than the traditional Rails way, I'd say. And the reason is because we have a pretty large application
and so it's not really uncommon, as soon as your applications start growing beyond the standard Rails blog application, to face huge need of having a lot of different needs within the same application, like talking with external services and so on.
So if your application started growing, you will likely hit the same kind of troubles that we did. And so one of the key principle for us was to be able to write code that we can maintain, okay? We have been in business for six years and we hope we'll be in business for even more years. So the idea is that we need to have code that can be maintained for a long time.
So you cannot really change, you cannot really have full control of your code unless you write your own code, right? I still remember the days when the transition between Rails 2 and 3 make a difference between the way that JSON was serialized.
So before we had the root, the model that was the root of the JSON and then suddenly from Rails 2 to Rails 3, everything changed. But you cannot really go back and change, like in one month, the API that all your customers have been using, right? So what you wanna do is that you wanna be in control of your code. And for us, Rails is just another component that we wanna be in control of.
So what we do essentially is that every dependency that we use in dnsimple is wrapped behind a custom API. So this is a very super simple example here. In some part of our code, we need to deal with phone validation. So we use a library which is called phony that perform validation and normalization of phone numbers.
And rather than going and calling phony directly, what we did is that we wrap the phony library behind some custom code. This is a dnsimple phone module. Now the very interesting thing about that is that at some point, this is the final code that you see, at some point, we had to introduce some whitelisted numbers.
And the reason is because phony was not catching some particular countries, was not able to understand those country phone number formats. And so what we had to do is that we had to introduce a whitelist. And that was super simple, super easy to be done because we just introduced a whitelist within the module and we didn't have to go
and change all the code, all the previous code I was using the library directly. So this is a very super simple example of how you can build your, you can improve the maintenance of your code just by writing a little bit of extra code rather than going directly with a gem. So some of the advantages of this approach
is that site features or extensions like the whitelist can be introduced without really changing the code base, incompatibilities can be addressed in a single place, and if you need to replace the underlying library, you can do that in a single place rather than going and changing all the single method calls. And another really important component and advantage
is that testing doesn't require extensive stopping. Now I don't know what's your code, but our code was using, still uses a large number of stops. And this is not really extremely maintainable in the long run, so this approach is much more flexible.
And in fact, here's an example of another component we have, another library we have inside our code where we can avoid using stopping. So here we have a resolver, which is one library that deal with our custom DNS record called DLS, and that performs the resolution.
We have adapters behind the scenes, we have another adapter that essentially discards all the requests, we have a test adapter that is what we use in testing, so rather than stopping, we can flip to this adapter and we can feed the adapter with the response that we expect in the test. And then we have the Go production adapter
that will rely on our Go language implementation of DLS record, and this is turned on by default in production. And this is an example of how we use that in the test environment, we can simply switch to the test mode, and we can feed the adapter with the kind of response that we expect, and then we can test that,
rather than, and we have the full control of our code, we don't need to use mock, because we don't need to mock the original libraries, that's a really huge advantage. Of course, another component that we have to deal with that is ActiveRecord, and in that particular case, we have special guidelines for ActiveRecord,
so here's a few ones, for example, methods defined in ActiveRecord base, like find, where, and so on, cannot be used outside the model itself. Instead, the models must expose a custom API to perform operation, and we call them the finders, so we have specific objects that are called finders. Callbacks are not allowed anywhere
except for that integrity, and the query methods in ActiveRecord cannot be used outside the models or the finders, so any code outside the model, sorry, any code in the application only interact with those finders, and finally, scopes can be invoked directly, but they are used just to compose queries
within the finders. Here's an example of a finder. We have, for example, an implementation, a custom implementation for the find. I don't know how many of you had the problem that the default find version uses the out increment key of the database, but sometimes you need a string. You need to search for a string, so you don't really want to override the default find
because other libraries or plugin might rely on that specific behavior, so this is an example of a custom finder, and then this is an example of a different finder that use a variation of an input. For example, we want the TLD to be not case sensitive during the query. Another example are default scopes.
Another example are default orders. You don't really want to set default orders in the model because in other places, you will have to de-override the defaults, and then another example here is when you want to have queries that instead of returning active record relations, returns, for example, an array of elements.
Now, all those kind of methods, and then we have the last one, which is a specific implementation of the not found where we raise a custom message rather than the default one in active record because the default one in active record includes information about the query, so for example, the where parameters. Now, all this code allows us to be independent
from the underlying implementation of active record and improves our maintenance. Of course, these guidelines are the result of years of experiments and discussions. They are not really in stone, and they keep changing and evolving within the team based on the experience we have,
and that's why I also want to take a few seconds to say thank you to all the team members of DnSimple for contributing to these guidelines and for contributing to the quality of the project. Oh, wow, we have another candy time, so more candy here.
Was I expecting that? Ah, over there. Here we go. Candy here, there, candies, and who wants missing candy? Oh, yeah. Here we go. Actually, instead of presenting, I could go and play baseball. Okay, awesome. Let's move on.
Next step is what I call the MVC+. It's essentially a different approach that we have in DnSimple to structure our application. Traditionally, if we don't consider the view part, traditionally, your application have two layers, you know, the model and the controller, but in DnSimple, we essentially have four layers, right?
So we have the controllers, we have the models, and in the middle, we have commands and services. I'm gonna explain a little bit what are the needs for these extra layers. Now, this is a request. Okay, the user is performing an HTTP request, and the request can either be an API request or a normal request for our browser, right?
So the request is sent to the controller. The controller handled the request, and it can be a race controller or it can't. I mean, we don't really have to, but let's pretend it is for now. Now, the controller handles the incoming request, incoming request validates the input, parses the output, parses the parameter, stops my form request,
authenticate the request, but what's important is that the controller has no business logic at all. This is an example of a controller, so you can see this is a typical race controller. We have the new action, we have the create action, and as you can see, it's really, really small
and tiny compared to the normal kind of controller you probably have seen in our application, and just for you to know, this is the real code from the domain create controller, so it's essentially, domain is one of the core components in our application, so you can imagine that creating a domain is one of the most complex parts
of our application. Still, as you can see, the controller is extremely readable, extremely short in terms of line of code. What it does is it delegates to a command passing the parameters that we expect, gets some data out, and then if the response is successful, then renders the page,
otherwise renders another different page where we ask the user to provide more information. That's it, that's all about the controller. Then the next layer is the command. Now, commands encapsulate the user business logic for us, so we have specific operation that the user can perform, and in fact, other companies, other libraries
have a similar notion, and they call them operations. Now, we don't call them operations because of a historical problem, but yes, so that's essentially commands. You can think them about operations. Now, command can only delegate to lower level. This is more or less the same rule of controllers.
You have never seen a controller in Rails calling another controller. That's the same for the command. In our application, a command can only go down beyond in the path. It cannot call another command. This is an example of a command, the same command that you saw before. As you can see, there are some includes that creates the underlying structure of the command,
and then you have some preliminary first level business logic validation. Here's where we have the first level of business logic validation. Then, we have permission, we have roles. After the domain successfully create using a service, I will talk about service later on, after the domain is successfully created using the service,
we perform some extra operation. For example, we track the activity of the creation of the domain. We send out notification about the creation of the domain. All those kind of things that you are probably used to see as callbacks in a model, but I can tell you that as soon as you have dozens of this kind of activities within the model,
it will be a huge nightmare to test them and to maintain them in your application. The commands never raise errors. Okay, what they do is that they have to return a result. The result can be either successful or unsuccessful,
and if the result is unsuccessful, has an error message. Even more importantly, the error message has to be a user-friendly error message, not a kind of exception message. So in case we have an exception there, we rescue that, and then we try to provide a meaningful error message.
Then, we have services. Now, the services are the core of our business logic. Each service is essentially a public, contains public methods that represent the business unit, the single unit of operation that can be performed in our application.
So example of services for us are the domain service, the two-factor authentication service. An example of public methods within those services are two-factor authentication dot enable, dot disable, dot authenticate, and so on. So you can think about every single method is essentially a small unit, a single action, a single API that our application
can expose to the external environment. Now, another interesting thing is that services are, I'd say, almost functional, in the sense that they don't store any state. Services don't have any state, and this is really important for us because it's really a readable implementation.
Services get an input and then transform the input and return back the output. No internal state. This is an example of a service. So as you can see, the creation of the service uses dependency injection to get some of the other services that the service is interacting with, and this is really helpful when we have to isolate
each service in the test suite. And then we have several different public methods. We have the create, we have the delete, the lock, and this is one of the examples, so the creation. The creation, what it does is that it interacts with the models, takes some attributes, and then creates the object,
and then interact with other services. That's the other thing. That's where the interaction happens. A service can interact with other services, and after that, the service is expected to return a result. And of course, services can raise exceptions, and they are, those exceptions are expected
to be catched in the commands. This is another example of the service. This is a zone service that was used by the domain service. And as you can see, a method can call other methods. So for example, here we have the creation of the zone that uses the creation of the system record
and the refresh of the zone, because as I said, you can expect every single method to be a single task, a single operation, and so that specific operation can be reused elsewhere in the application itself. But it's really important to code that specific component in one single way so that we can test the code
in isolation. And then finally, we have the models. Again, in our case, models are still active record models but they are a little bit different, so they are essentially an abstraction for the storage engine, and they contain only methods that are useful to persist information to the database.
Models should have no business logic. A model should not write to other models and then, or trigger other model actions. And then we don't use callbacks, as I said, except for that integrity. This is an example of a model. As you can see, for example, take the move to account method. Now, the move to account method, as you can imagine,
is shifting a domain from one account to another. It's a pretty huge task within our application because it involves a lot of business logic behind. We have to change the billing information, we have to increase the number of domains on that particular account, but as you can see, there's no code here that deals with that.
Everything happens in the service. Here, we only deal with the persistence of the information within the database. And that prevents a huge model like the domain one to become like thousands and thousands of lines, including every sort of kind of operation,
including, I don't know, calls to external HTTP services, for example, which don't really belong in a model. So at this point, you might be wondering why. Why we should have all these level of complexity? And in fact, if you are dealing with smaller application, you probably don't need all this level of complexity,
but still, you might want to have something in the middle between the models and the controllers that stores the logic of your application. Now, if we just consider the commands, the services, and the models, you can actually understand that you essentially coded the entire business logic of your application
into an environment which is completely separate from the context of an HTTP request, right? And even separate from the context of a LACE controller. And we actually took that one step further. So the core is codified and easily maintainable in an independent set of libraries, because after all, our models
are still ActiveRecord models, right? So we don't have control over those libraries, because they rely on ActiveRecord. So ActiveRecord can make changes behind the scenes that will affect our applications. Our code, the code we rely on, the business logic is essentially coded within the commands and the services. So that's the reason why we have
this kind of abstraction. And so that allows us to start shifting the focus and introduce different kinds of controllers. So for example, the controllers can be erased, but then you can also introduce, I don't know, Hanami, or you can introduce any other framework, right? At that point, once you have
that specific isolation of your code, you can easily introduce something, more things in the front, and you don't have to refactor, you don't have to change how the code of your application work. So this is essentially a super simple, simplified example of our infrastructure for the DNSimple application.
As you can see, we have RACE on the front, we have another framework, which I will talk in a few seconds, Hanami. Then we have the commands, we have the services, and we have the models. And you can see the flow of a specific request coming from the controllers, going through a specific command, a single command that will delegate to one or more services,
and then each service will interact with the models in order to accomplish the task. So next step, Hanami. Okay, I mentioned Hanami a couple of time during this talk, and I wanna talk a little bit about Hanami and why we use Hanami
and what is Hanami, first of all. So Hanami, as I say, was formerly called Lotus, and it's an emerging Ruby framework that was created by a friend of mine, Luca. Luca designed this framework in a way that encouraged developers to use good development patterns, okay? And you can see that if you check at the,
not only if you use Hanami, but even if you check how the source code in Hanami was implemented. Now, Hanami is not 1.0 yet, but it's really stable and it's really mature. We have been using that in production at DNSimple for almost one year so far. Hanami is not just a simple single task framework.
It's actually a full framework. It is made of different components. So you have the application, you have the router, you have the actions, you have the views, you have the models, you have the migration, the helpers, the mailers, the assets, and the common lab, right? So it's pretty much similar to RACE in terms of focus, in terms of components.
And the good thing is that you can just use some of them. You don't have to use all of them. And in fact, in the DNSimple API, we use the router and we use the actions. Some of the key features of Hanami that I love are modular architecture,
which means you can use the components you want. You can use all of them or just a few of them, even in an existing application. A design based on composition that encourages you to use object-oriented encapsulation. So good development patterns. And even more importantly, test-friendly approach.
Given the good design behind the structure of Lotus, you can really test specific Lotus component in a super easy way. So the question, one question is, why DNSimple adopted Hanami? And rather than going through different frameworks
or just staying with RACE, for example. So RACE provides, as you probably know, a way to format the same response in different ways. You know, you can return from a controller, you can return a JSON response, you can return an HTML response or whatever. Now the problem is that this approach is not really maintainable for large
and complex application. Because you might have, for example, changes in the view that will affect the API, right? So you don't really wanna do that for really large application. So the choice at this point is even, is either to create new controllers, so clone your controllers and have those controllers just to deal with the API,
or go with an alternative framework. And because RACE is a massive, huge set of libraries, and because we wanted to have control of our code, we decided to not create more RACE controllers for that. So the normal choice at this point is Sinatra, as you probably guessed. Sinatra is a nice framework,
but I have a kind of hate-love relationship with it, because it's super nice, especially for prototyping, but as soon as your application starts growing, and you have 30 different routes, or 40 or 50, and then you have helpers, then you have formatters, and so on, suddenly Sinatra, your Sinatra application will be really hard to maintain,
and really hard to test. So at that point, Luca was starting, actually a few months before we started dealing with that, Luca started his adventure in building a new framework called Lotus, and I knew Luca since a while, I was working with him in previous projects,
so I decided to give Hanami a try. We originally started with Sinatra, so we implemented three, four API methods in Sinatra, and then in less than one day, I changed, well actually in a few hours, I switched, and I implemented all those methods in Hanami, and I had them running in parallel for a while.
I was running tests, I was running benchmarks, and that was extremely stable, and the way we could test, and to be through, to be, actually, I didn't even test the Sinatra controllers, the Sinatra implementation, because it was so hard, and at that point, I was just prototyping the new API.
Whereas when I switched to Lotus, the testing was so easy that, along with the library, along with implementation, I was able to write a test. So at that point, we realized that Hanami was working really well for us, and that's why we decided to stick with that, and essentially build the whole API v2 and that part of the platform with Hanami.
Okay, this. I think, okay, here we go. I see more and more people sleeping, so it's time for candies. Here we go, there. Am I missing someone?
Here we go. All right, in the front? Oh, here we go. Awesome. Okay, last part of the presentation, how we use Hanami in parallel with Vegas. Now everybody's looking for candies, and nobody taking attention to my presentation. Awesome, well done, good job. Okay, so the architecture that I presented before
helped us to reach this goal, because as you saw, we split apart the controller part with the rest of our infrastructure, so we didn't really have to completely rewrite our code to deal with Hanami. So Hanami is essentially a rack application mounted on the RACE controller. We still use the main, the primary RACE controller
to orchestrate the Hanami application. As you can see at the top, there is the RACE routing file. We have a scope for the API version two. Actually, we have a scope for the API, and we have a particular scope for the API version two. And then we have the router, the routes for Hanami. So Hanami has its own router,
and that's where we define the rules for routing a request within the Hanami framework. I don't know if you can see that. You can see that? So here we have in parallel an example of the same controller implemented with RACE and implemented with Hanami. So on the right side, you have the Hanami implementation,
which deals with the API. And on the left side, you have the RACE implementation, which is essentially our front-end application. As you can see, the code is pretty much the same. And so because of the different layers that we have in our application,
and because of our controller being so small, it was really easy for us to start implementing the API with Hanami. As you can see, we call the command. We have custom objects in order to deal with the parameters. We have custom serializers, as I told you before, because we love to be in control of our code.
So we don't use any Hanami-specific or RACE-specific library for dealing, for example, with parameters or validation or the serialization of the response. We wanna be in control of that specific part of the code. And this is an example of how a controller, an Hanami controller, is structured. And that's actually really interesting,
the part where, in Hanami, every single action is a class. Sorry, it's an object, right? So we don't have a single controller like in RACE where every single method is a different action and you have to deal with public method that are interpreted as actions and private methods and so on.
So in Hanami, every single action is a class and the class has to respond to a method called call. And this is where the logic of the application happens. And that's pretty interesting because if you have a specific action that has to deal with some non-default operation,
you can just create a method or you can just override the standard implementation of the action and that will be. In this case, you can see we have before filters, so we do validation of the subscription, whether the account is in good standing before performing any kind of operation. And here we have two actions.
One is to enable the WISP privacy and the other one is to disable the WISP privacy. And Hanami also has a nice DSL to configure the controllers. So this is the example. You can include modules. Modules are essentially just Ruby code that you can reuse. And for example, here we have shared information,
shared methods across all the different controls that we use for validations. Okay, so wrapping up my presentation, my message today for you is first of all, experiment. This is one of the key principle in the end symbol. And we like to experiment new ideas to try new framework to experiment new approaches.
And you should do the same. Take the time every once in a while to do some research and development because you might be surprised how many new technologies and interesting emerging technologies you can find out of there. The second tip I have for you is get influenced by other communities.
So I'm here today in a race conference talking about race and other frameworks. And the talk after mine will be about a completely different language. So you have to be influenced by other communities. And it can be other frameworks or it can be communities built around other services, services you use.
For example, we had an amazing experience when we started dealing and writing code that was interacting with GitHub or even Slack, for example. By the way, speaking of Slack, I have a very super simple, super quick announcement. For all of you that are the Unsimple customers, we just released a Slack implementation, a Slack add-on,
so you can now manage all your domain through Slack and that will interact with the DNSimple account. That was actually a screenshot I took a few days ago. And as you can see, it's DNSimple.slack. But today we got the news that the Slack integration was approved in the Slack directory so you can enable that directly from Slack as of today.
The other tip I have for you is, this is a really important tip, get influenced by other programming languages. So in the last month, in the last four months, I personally worked in four different languages in parallel to build all the DNSimple API clients.
So I work in Ruby, in Go, in Elixir, and a little bit of PHP. And so that was really wide-opening for me because interacting with other languages, working with other languages, made me a better Ruby programmer. I was able to deal with assumption, I had to deal with information
that was coming from our API, from our application that were based on Ruby assumption, and I had to face with the problem of dealing with that from other different languages. So every once in a while, be sure to take a look at different languages because they will be, even if you won't use those languages in your primary daily activity,
they will make you, in that particular case, a better Ruby programmer. This is a credit for the picture at the beginning of my presentation, and that's it for today. So thank you very much for being here. If you have any questions, we need to ask you. Thank you.