Ruby
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Methods and Blocks
-
Section 4: Object-Oriented Ruby
-
Section 5: Metaprogramming
-
Section 6: Working with Data and Files
-
Section 7: Ruby Frameworks Overview
-
Section 8: Ecosystem and Testing
-
Section 9: Practical Projects
-
Section 10: Interview Practice
-
Section 11: Data Structures and Algorithms in Ruby
-
Section 12: More Practice Exercises
-
Section 13: Enumerable and Functional Style
-
Section 14: More OOP Practice
-
Section 15: More Testing
-
Section 16: Enumerable and Comparable Modules In Depth
-
Section 17: Ruby Standard Library: Core Utilities
-
Section 18: Ruby Standard Library: Data and Security
-
Section 19: Ruby Standard Library: CLI and Text
-
Section 20: Ruby Networking
-
Section 21: Ruby on Rails Deep Dive
-
Section 22: Ruby Metaprogramming Deep Dive
-
Section 23: Ruby Design Patterns
-
Section 24: Ruby Concurrency
-
Section 25: Ruby Testing Deep Dive
-
Section 26: Ruby Gems and Packaging
-
Section 27: Ruby Performance
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Rails API Development
-
Section 31: Rails Authentication and Authorization
-
Section 32: Rails Testing Deep Dive
-
Section 33: Rails Performance
-
Section 34: Rails Deployment
-
Section 35: Sinatra and Lightweight Ruby Web Apps
-
Section 36: More Ruby Language Deep Dive
-
Section 37: Ruby 3.x Modern Features
-
Section 38: More Data Structures in Ruby
-
Section 39: More Practical Projects
-
Section 40: Ruby Ecosystem Tools
-
Section 41: More Practice and Review
-
Section 42: Final Practice and Mastery
-
Section 43: Ruby Interview Deep Dive
-
Section 44: Ruby Background Processing Deep Dive
-
Section 45: Ruby GraphQL
-
Section 46: Ruby Object Model Deep Dive
-
Section 47: Ruby Hanami Framework Overview
-
Section 48: Ruby gRPC and Protocol Buffers
-
Section 49: Ruby Data Processing
-
Section 50: Ruby Search Integration
-
Section 51: Ruby File Upload and Media
-
Section 52: Ruby Email and Notifications
-
Section 53: Ruby Admin Panels
-
Section 54: Ruby Feature Flags and Experimentation
-
Section 55: Ruby Monitoring and Observability
-
Section 56: Ruby Docker and Deployment Deep Dive
-
Section 57: Ruby Security Deep Dive
-
Section 58: More Advanced Metaprogramming
-
Section 59: More Final Projects
219: Building an Admin Panel with Avo
I’ve lost count of how many hours I've wasted in my career building the same CRUD interfaces over and over again. You know the drill: a controller for the admin, a set of views that look like they're from 2005, and a mountain of permission logic. When I first stumbled onto Avo, I was skeptical—usually, these "admin generators" either get in your way or create a mess of hidden magic. But let's dive in and see how it actually feels to build one out for a real project.
For this session, I'm using a bookstore app I've been tinkering with. I've already got Book, Author, and Order models. Right now, if I want to change a book's price or mark an order as shipped, I have to go into the Rails console or write a custom admin page. Let's fix that.
Getting the scaffolding up
First, I'll throw gem 'avo' into my Gemfile and run bundle install. Then, the installer: rails avo:install. I’m not going to bore you with the installation logs, but the important thing here is that Avo creates its own namespace. It doesn't hijack your ApplicationController, which I appreciate. It lives in its own world.
I'll fire up the server and head to /avo. It's a blank slate. Completely empty. I've got the dashboard, but no data. This is where the "declarative" part comes in. Instead of writing a controller, I'm going to generate a resource for my books.
rails generate avo:resource Book
Now, I'll check the generated file in app/avo/resources/book_resource.rb. By default, Avo tries to be helpful and adds every single column from my database table to the index and the form. When I refresh the page, it works, but it's ugly. I've got created_at, updated_at, and internal database IDs cluttering up my view. That's not how a professional admin panel should look.
Taming the Book Model
I want to curate this. I only want to see the title, the author, and the price on the main list. Let's go into the BookResource and start hacking at the fields block. I'll comment out the noise and explicitly define what I want.
class BookResource < AvoResource
self.model = Book
fields :title, :author, :price
end
I refresh. Much better. But wait—the price is just a raw decimal. It looks like a math textbook, not a store. I remember that Avo lets you specify the field type to get better UI components. I'll change :price to a more explicit definition.
field :price, as: :currency, currency: 'USD'
There it is. A nice, formatted currency string. It's these little quality-of-life tweaks that make an admin panel actually usable for a non-technical client. If you give a client a raw decimal field, they'll eventually ask you why the "cents are missing" or why there's no dollar sign.
Finding the needle in the haystack
As my bookstore grows, I'm going to have thousands of books. Scrolling through a paginated list to find books that are out of stock is a nightmare. I want a quick way to filter these. I'll try adding a scope.
I already have a scope in my Book model called .out_of_stock. I'm wondering if Avo can just... pick that up? Let's try adding it to the resource file.
class BookResource < AvoResource
self.model = Book
scopes :out_of_stock
fields :title, :author, :price
end
I hit save and refresh. A "Scopes" dropdown appears at the top of the table. I click "Out of Stock," and boom—the list filters instantly. I didn't have to write a single line of SQL or a custom controller action. This is where the productivity gain actually happens. I'm spending my time defining what I want to see, not how to fetch it from the database.
Cleaning up the editing experience
One last thing. When I go to edit a book, I noticed the author field is just a text box. But Book belongs to Author. It should be a dropdown, right? Actually, it is a dropdown by default because of the association, but it's a giant, scrolling list of every author in the system. That's a usability disaster.
I'll try to make the author field searchable. Avo has a searchable option for associations. Let's see if this does what I think it does:
field :author, as: :belongs_to, searchable: true
Now, instead of a static HTML select menu, I've got a searchable combo box. I can type "Tolkien" and it filters the authors on the fly. This is exactly the kind of polish that usually takes an entire afternoon to implement with a JS library, but here it's just one line of Ruby.
📋 Practical Task
Implementing a Low-Stock Alert View in Avo
You are managing an inventory for a hobby shop. You have a Product model with a stock_count (integer) and a name (string).
- Create a scope in your
Productmodel calledlow_stockthat returns products wherestock_countis less than 10. - Generate an Avo resource for
Product. - Configure the
ProductResourceso that:- The
low_stockscope is available in the UI. - The
stock_countfield is displayed as a number. - The
namefield is the primary field displayed in the index.
- The
- Verify that clicking the "Low Stock" scope correctly filters your product list.
There are no comments for now.