Inferred Controller Parameters

Be careful when making a network request like this to a Rails controller with strong parameters: // const employee = { name: "Jake" }; const updateEmployee = (id: number, employee: Employee) => axios.put<Employee>(`/employees/${id}`, employee) def employee_params params.require(:employee).permit(:name) end Note that our payload is expected under key employee– but we aren’t sending it that way. When this happens, Rails infers what goes where: PUT https://server.dev/employees/1 { "employee" => { "name" => "Jake" } } Notice that name is in the employee object, even though I didn’t send such a payload. This works fine until you send a parameter that’s not on the Employee model, such as a nested attribute. Then you get this: ...

March 1, 2024

Assert About Response Body in RSpec Controller Tests

When testing a serialized Rails API with RSpec, a common mistake is to assert about the response body, only to find that it’s an empty string. The controller is controlling, the view is presenting. What’s missing? What’s missing is the view: RSpec views are stubbed by default. This is ideal for most controller tests. Just not those asserting about serialized data. Render those views with render_views: require "rails_helper" RSpec.describe WidgetsController, type: :controller do render_views describe "GET index" do it "has a widgets related heading" do get :index expect(response.body).to match /<h1>.*widgets/im end end end

February 23, 2024

Highlighted Routes Searching

On a non-trivial Rails app, the output from rake routes will be huge. Many people develop a reflex for searching through this output via something like: $ rake routes | grep "<string>" Which filters the output to matches on the string. I’d like to suggest a better option: use a command-line search tool that highlights the output! Here’s that same command with RipGrep: $ rake routes | rg "<string>" The results are the same (although much faster 😈), but even better is that that each match will be colorized. It’s bright red in my terminal. The colorizing makes the matches much easier to interpret.

August 24, 2021

fields_for exclude ID

Rails’ fields_for helper inserts a hidden ID field into your form. This is generally helpful, because you’ll want that information when you’re updating the record. But what if you don’t? This is the scenario I encountered today: a ‘read-only’ form nested inside a fields_for helper, with no inputs. It almost worked: unfortunately, due to the default ID-inserting behavior of fields_for, there was a hidden ID input. It was getting submitted with the rest of the form and breaking things. ...

August 12, 2021

Generate a Rails Secret Key

Have you ever wondered about those secret keys found in config/secrets.yml of your Rails app? The comments generated in that file describe the keys as ‘used for verifying the integrity of signed cookies.’ Great… but what if they become compromised? Or we need to change them? We can generate new ones. Rails provides rake secret for just this purpose. The source code is here. It’s pretty simple; the code simply requires SecureRandom and spits out a string. If you want to be really clever, you can pipe the string directly into your Vim buffer for the config file, with :.! rake secret. ...

January 12, 2016

Show Model and Grep with Pry-rails

This week Josh Branchaud wrote about show-models in Pry-rails: http://til.hashrocket.com/posts/62f61ee06f-show-rails-models-with-pry This method has two nice variants. show-model User shows you that model, useful in a large database. show-models also accepts --grep for searching: [1] pry(main)> show-models --grep published Post id: integer title: string published_at: datetime # 'published' is highlighted

January 8, 2016

Set a Default Scope

ActiveRecord provides default_scope, which you can use to control how records are returned. An example: # app/model/chapter.rb class Chapter < ActiveRecord::Base default_scope { order(:title) } end Now, instead of this (ordered by ID): Chapter.all.pluck(:title).take(5) => ["BALINESE CAUSE", "BUILDING RAT", "TAIL", "LILAC MERCURY", "PUMP SQUARE"] We get this: Chapter.all.pluck(:title).take(5) => ["AFGHANISTAN COAT", "ALCOHOL GONG", "ANTARCTICA BULL", "ASPARAGUS", "BALINESE CAUSE"] Beware, this decision can lead to inexplicable behavior down the road. Use with caution. One use case I’ve heard is soft deletes. We might not want soft-deleted records appearing in .all: ...

January 8, 2016

Rails restore_attributes

Today I found a useful method in ActiveRecord, restore_attributes. This method restores attributes on a dirty model. Use it when you are hacking in the Rails console and want to quickly return to a clean slate. Dirty the record: [1] pry(main)> p = Post.first Post Load (0.4ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."id" ASC LIMIT 1 => #<Post:0x007f8462b09eb8 id: 106, title: "Hello World!", created_at: Sun, 09 Feb 2014 17:15:01 CST -06:00, url_slug: "hello-world"> [2] pry(main)> p.url_slug = 'foobar' => "foobar" [3] pry(main)> p.created_at = Time.now => 2015-10-13 11:41:37 -0500 [4] pry(main)> p => #<Post:0x007f8462b09eb8 id: 106, title: "Hello World!", created_at: Tue, 13 Oct 2015 11:41:37 CDT -05:00, url_slug: "foobar"> And restore: ...

October 13, 2015

Rails destroy

The rails generate (g) command is useful for quickly creating the building blocks of a Rails app. Until you misspell or otherwise mess up a command: % rails g model blurg_post invoke active_record create db/migrate/20151006162244_create_blurg_posts.rb create app/models/blurg_post.rb invoke rspec create spec/models/blurg_post_spec.rb ‘Blurg post’? I meant ‘blog post’. Luckily, we can remove those auto-generated files as fast as we created them, with destroy (d). % rails d model blurg_post invoke active_record remove db/migrate/20151006162244_create_blurg_posts.rb remove app/models/blurg_post.rb invoke rspec remove spec/models/blurg_post_spec.rb http://edgeguides.rubyonrails.org/command_line.html#rails-destroy

October 6, 2015

Change Column Null

The not null constraint is a great way to ensure data integrity. If a Rails model validates_presence_of an attribute, that column should be not null in the database. Rails has a special migration method for setting this constraint. change_column_null :users, :mandatory_attribute, false You could also use the change_column method. The reason change_column_null is a better choice is that change_column requires you to state the type of the column; change_column_null does not. change_column(table_name, column_name, type, options) change_column_null(table_name, column_name, null, default = nil)

June 28, 2015

Don’t miss my next essay

Hear from me immediately when I post: no ads, unsubscribe anytime.