Posts in Rails (7 found)
tekin.co.uk 1 months ago

Overriding Rails’ default validation error message format

This is a followup to my recent lightning talk and writeup about i18n in Rails and how it can be useful even when we’re not translating our applications. In the talk and writeup I describe how we can change the Active Record error message format from the default of and drop the attribute prefix, giving us more flexibility in how we phrase our error messages. The downside to this is that it results in the default Rails error messages being output as incomplete sentences. It’s since occurred to me that it’s possible to avoid this by replicating the default validation error messages in our app’s locale file with the attribute prefix as part of the message , rather than the format! So we end up with a locale file that looks something like this: With that in place, we’re free to define bespoke validation errors for specific attributes and models without the attribute prefix, whilst also preserving the default error messages as a fallback.

0 views
tekin.co.uk 1 months ago

10 Things You Might Not Know About Rails i18n

This is mostly a transcript of my recent lightning talk on i18n in Rails , go watch that if you prefer your content in video form. Otherwise read on to find out why I believe understanding i18n in Rails can be useful to you, a Rails developer, even if you’re not translating your applications into other languages. I’m going to share ten things I think are interesting in i18n, and explain how we can take advantage of them in our single language app code. But first a brief primer on i18n… i18n is shorthand for Internationalisation When we’re talking about i18n, it’s useful to understand the difference between Internationalisation and Localisation : The take away here is that although i18n is primarily a framework for adapting applications to support other languages, the core idea of separating content from applications code has useful properties that we can take advantage of it to improve our Rails applications, even if they only support a single language. Rails ships with the i18n gem , and there are two main components: Rails aliases these helpers in views and helper modules to the convenient shorthands of and respectively. So the most basic usage of i18n would look something like this: Primer over! On to the ten things… Even if you’re not localising your app you’re almost certainly still using i18n. That’s because it’s baked into the very fabric of the framework. For example Active Record uses i18n under the hood to generate error messages for its validations. It does so using a YAML file that ship with Active Model : Whenever you see something like “Title cannot be blank”, this is the i18n plumbing that was used to construct it. Rails i18n makes it easy to override these default errors messages. Just replicate the same key structure in your application’s locale file with your chosen phrasing: Rails will check your application’s locale file first, falling back to the defaults only if the message isn’t defined there. As well as overriding this global phrasing, it’s also possible to override the error messages for a given attribute name: Or you can get even more granular and override specific attributes on a given model. For example if we have the following model and validation: It’s possible to set a bespoke error message for this specific model and attribute: As well as overriding individual validation messages globally and for specific attributes, it’s also possible to change the actual format of error messages. By default, errors are constructed with the familiar format of . This is defined in the same YAML that ships with Active Model alongside the default error messages: This format works as a reasonable default to ship with the framework as it gives us mostly coherent error messages out of the box without us having to do anything, but it lacks fidelity and can result in some clunky and unfriendly sounding messages. For example, take this validation: If we go with the default format we end up with . Which is fine, but we can definitely do better. My personal preference on the apps I work with is to remove the attribute prefix entirely: This frees me from having to make my error messages work with attribute’s name upfront, allowing me to write more user-friendly and coherent error messages: Now changing the default format does come at a cost: without the attribute name prefix, the default messages that ship with Rails no longer form complete sentences. This forces me to write bespoke error messages for each validation I add to my app: Personally I’m happy to pay this cost for the benefit of improving the user experience. So whilst you may not want to do this on an existing app with thousands of error messages to backfill, it might be something you consider next time you run and are starting from a clean slate. Update : There’s actually a fairly neat way to avoid this cost, check out my follow-up post on how . As well using i18n to manage and override the error messages for the built-in validations, you can also use it to manage the messages for your custom validations. Let’s look at a custom model validation: Here the error message is inlined as part of the validation code. Instead of doing this, we can pass in a symbol identifying the error, and define the corresponding message in the locale file using the same key structure as the built-in validations: This results in more concise and compact model code, but also equally as nice is that it removes a presentational concern — the specific phrasing of the message — our the business logic. It’s also possible to interpolate both the attribute name and the value of the attribute directly into the error message, which is useful for custom validations that are used across multiple attributes and/or models: Another place where Rails makes use of i18n under the hood is the form helpers: specifically, the label helper methods. By default the label helper methods humanize the attribute name to arrive at the label text: One way to override the label text is to pass in a string to the helper call like so: I think a better way is to define label overrides in the application’s locale file: Not only does this keep the view code more concise and less noisy, but it has the added advantage of defining this label override in one place: now any other forms that display the same label will automatically get the overridden text without you having to manually copy the literal string to every template. And as with form errors, label overrides can be defined per-model as well as globally: i18n has sophisticated support for pluralisation that we can leverage to simplify our app code. As an example, we can display the stock availability of a product by calling out to i18n like so: And then define the phrasing based on the number of items in stock in our locale file: Much neater than a bunch of conditional logic in a helper method! As well as being available in Rails controllers and views, i18n is also integrated into Action Mailer, allowing you to organise content for mailer templates in local files. Mailer get one extra feature of controllers: the ability to specify email subject lines in locale files: Again, this enables us to move another presentational concern out of our application code and put it somewhere more appropriate. There is also a meta benefit to defining all these bits of content (mailer subject lines, form labels, validation error messages) in an application’s locale file: it puts them all in one centralised place, making them easier to change, check for consistency and generally manage. It also makes the content more accessible to non-developers such as designers and product folks. Right at the top I mentioned the method that is used to output localised times and dates. The way this works is by defining our own time and date formats in our locale file using the same format specification as Ruby’s : Here we’ve defined a short date format that will be familiar to my UK-based audience: day/month/year. To render a date using this format we simply pass in the format identifier to (or its alias): or with the shorthand: Now my American readers may be confused by this short format, as across the pond the preferred way to display dates is actually month/day/year. So whilst you may not need to localise your entire application, you might want to consider localising your date rendering, especially if you have customers both sides of the Atlantic. To do this you would define a US region specific English locale file: Then update your controller code to automatically switch locale so the user gets dates formatted appropriately for them. There are many ways to do actually do the locale switching, but at a high-level, you identify the user’s specific locale and then make sure your controller actions are executed with that locale. Here’s an example of doing that by sniffing the user’s locale from their browser headers: (The exact mechanics of the method is left as a separate exercise, but you could do worse than use the http_accept_language library) Little known fact: the i18n library supports locale files defined using Ruby! Now why would you do this, other than a burning hatred for YAML? Well one reason is that Ruby-based locale files allows you to make use of procs to dynamically generate content. Below is an excerpt from a Ruby-based locale file from one of my applications. It uses a proc to format dates as academic years, changing the output depending on which side of September the date is: Now this could just as easily be achieved using a standard Rails helper method. But for me I like the consistency and clarity of using a single mechanism ( ) for formatting dates across my application code, rather than with a mishmash of calls to bespoke helpers, , , etc. This is the part of the lightning talk where I make a meta point about what I’ve covered. I won’t write that up here, but if you’re interested go watch the video from minute 9. Internationalisation — the process of abstracting content and other locale-specific things away from application code itself Localisation — the process of adapting software to support different languages and regions (maybe using i18n) locale files where textual content is organised and stored per-language (normally as YAML) two helper methods: for outputting text content from the locale files for localising dates and times

0 views
Jameel Ur Rahman 8 months ago

The story of OnlineOTP

A few months ago I faced an annoying problem. I wanted to redeem my Cathay Pacific Miles but I was unable to log into my account. The SMS OTP never arrived. My account was tied to my Sri Lankan number, which I’ve had for many years, yet I never received their OTP. After wasting an inordinate amount of time, first with their chat support, then with the call support, I was told this was a “known problem”. When I browsed through /r/SriLanka I immediately noticed this was a recurring problem that has gone back for more than a year on a number of services. I really wanted to scratch this itch. I knew from past experience that jumping to building was not the best solution, but at the same time I wanted to ride the momentum of this idea. Two weeks later I had an MVP ready to go. Powered by Ruby on Rails, Tailwind, Render, Twilio, Resend, Tally, Hopes and Wishes. The Security guy in me was crying as I built this product. The Pragmatist in me was satisfied that I had a use case when the product flow was broken. The Entrepreneur in me watched me scratch this itch knowing the pitfall I was knowingly putting myself into, after all I hadn’t validated this product yet. With this I went live! ... in Beta . My hope was that the survey would get me my initial customers and help me validate this product. I was all too willing to keep this product live for a year in case I got a single Beta user. 1 month later I had 5 survey submissions I gave out 4 beta codes And got 0 signups that redeemed a code Fun Fact: Someone created an account on my site before I could 😅 After I went live in Beta, itch scratching satisfied, the Entrepreneur in me finally got a hold of the steering wheel and went to work. I talked to a number of people and one very helpful interested customer who reached out to me on LinkedIn. When trying to list every real world situation where someone might need OnlineOTP, or less gloriously "SMS to email" I came up with a surprising number of usecases. Expats who lose access to home-country services Professionals who must verify accounts across multiple countries Travelers who need OTP reliably and without roaming fees People in countries with unreliable carriers People with privacy or security concerns I went on the hunt. I stalked through forums trying to find users who face this problem and to pick up how they solved this problem for themselves. I had some success And some failures Overall I came to the overwhelming conclusion that I had a problem, but not really a solution that would reliably work. Here’s a snippet from a report I wrote to my coach. The majority of people want to receive OTP from financial institutions like banks. Banks do not like virtual numbers as it somewhat defeats the purpose of a multi factor authentication. Which means as a product calling itself OnlineOTP, I can not guarantee service quality as banks may not send their OTPs to VOIP numbers even though they accept it during registration. #strike1 The TAM is quite small and will get smaller as The people who want the solution seem to either be travellers who feel this momentary pain and then they are back home As banks move towards the industry standard of two factor auth via apps, passkeys or authenticator apps this will reduce in value. People don’t want to let others get access to OTPs. especially since it’s coming from banks. Trust factor issue. #strike2 When I initially started the project I got an “OK” from Twilio for my usecase, but before I went past Beta I wanted to be doubly sure and this time there was a lot of push back and a polite no, that this is against their acceptable use policy. Researching other providers I found that almost all of them have terms that imply they won’t be happy with reselling phone numbers or using it to receive OTPs. #strike3 I think I’m fairly sure a real problem exists but I don’t think the solution I’ve come up with is the right solution. At the moment there doesn’t seem to technically be a way to provide SMS to Email without becoming a Telecom Provider myself (MVNO specifically), which is not practical. A bit disapointing. That said, I regret nothing. It’s been fun going through this process even though it's resulting in me shutting down a product a month after launch. I’m just glad no one redeemed a beta code, as I would be honour bound to support the product for at least a year then. With this blog post, I'm closing up OnlineOTP. Excited to see what 2026 holds. Happy New Year! Cool Logo… Check Shareworthy Landing page… Check SEO… Check Focusing on the problem…Check Functioning buy a VoIP number and then get SMS to Email… Check Handling edge cases when buying a number… Check [Entrepreneur: What why?] Live dashboard showing SMS as you receive it… Check [Entrepreneur: seriously?] [Security guy: mate you're receiving OTPs… you should be self destructing it instead.] Mandatory FAQ explaining caveats with this product… Check I'm a UK expat living in Malaysia who needs a UK phone number that can receive SMS while in Malaysia. I'm a Certified Public Accountant based in the Philippines with clients in Singapore and Hong Kong. I am unable to reliably receive SMS OTP to process payments while sitting in Philippines I'm a Virtual Assistant who manages their client's accounts remotely and needs OTP access to complete tasks. I'm a Freelancer who needs a local number in multiple countries to access region-specific apps. I'm a business owner who manages accounts in multiple regions and needs OTPs from each region forwarded to one inbox. I'm a businessman who wants to receive OTPs on my Canadian phone number without having to pay Roaming Charges while I travel. And I travel frequently. I'm a back packer on a tour around the world who uses a temporary number but still needs to reliably access OTPs from their local bank. I'm a digital nomad who cycles through countries every few months and can't maintain SMS reliability. I'm a cruise passenger relying on ship WiFi and unable to receive SMS at sea. (Or Flight). I'm a traveler who temporarily uses a local SIM card but still needs OTPs from my home-country number. I'm a Sri Lankan who has a local Sri Lankan number who does not reliably receive SMS from Cathay Pacific on my local phone number. I'm someone living in a rural area where cellular coverage is weak, but email over WiFi works. People accessing platforms that require local numbers I'm an online seller/buyer who needs verification codes from marketplaces that only text local numbers. (If I remember correctly, Carousell in Singapore had that issue when I tried to buy something from it when I visited SG) I'm someone who wants an international virtual number for privacy but needs guaranteed SMS delivery. I'm someone who frequently relocates and prefers a stable, long-term virtual number. I'm a business founder who doesn't want to expose their personal number to dozens of SaaS platforms.

0 views
André Arko 11 months ago

Rails on SQLite: exciting new ways to cause outages

This post was originally given as a talk for Friendly.rb . The slides are also available. Between Litestack and the Rails 8 trifecta of Solid Cable, Solid Cache, and Solid Queue, it’s easier than ever to spin up a Rails app that doesn’t need a database service, or a redis service, or a file storage service. It’s great to simplify things, but even after 20 years of deploying Rails apps I was still caught out by some of the ways things are different. Based on what happened when I built a new side project in Rails on SQLite, we’ll cover what’s different, what’s new, and several ways that you can knock your site offline or even destroy your entire production database. As we go, we’ll also talk about the advantages of using SQLite, and how those differences can help you. So who am I, how did I learn these things, and why should you listen to me? I’m André Arko, better known on the internet as @indirect. A long time ago, I helped create Bundler , and I’ve been the OSS team lead for RubyGems and Bundler for more than a decade at this point. I work at Spinel Cooperative , a collective of Ruby open source maintainers building rv , the Ruby language manager that can install Ruby in one second flat. We offer retainers for unlimited access to core team experts from Bundler, Rails, Hotwire, and more, who can answer your questions and solve your problems.

0 views
DHH 11 months ago

Thrice charmed at Rails World

The first Rails World in Amsterdam was a roaring success back in 2023. Tickets sold out in 45 minutes, the atmosphere was electric, and The Rails Foundation set a new standard for conference execution in the Ruby community. So when we decided to return to the Dutch Capital for the third edition of the conference this year, the expectations were towering

0 views
ptrchm 2 years ago

How to Use UUIDv7 in Rails for Primary Keys

Using UUIDs for primary keys offers many benefits, but there are some downsides to consider. The most widely-used UUIDv4 is fully random, which is ideal for minimizing the risk of collision. However, random IDs as primary keys do not index and sort efficiently, leading to index bloat and performance issues. There is a new standard that addresses this problem by including a UNIX timestamp 1 in the initial bits: UUIDv7 2 . As of this writing, selecting in Rails defaults to using UUIDv4 for primary keys, with the generation deferred to Postgres.

0 views