Strong Parameters

& other fun party conversations

Who?
What?

When?

Where?

How?

Why?

Who?

Chris Flack

Developer

🖕Hello🖕

Who?
What?

When?

Where?

How?

Why?

Why?

What?

What?

Because!

People do terrible things. 

Like this: 

class User < ActiveRecord::Base {
    :id     => :integer,
    :handle => :string,
    :admin  => :boolean
}


# DO NOT THIS
def create
  User.create(params[:user])
end

params = { user: { handle: "BadGuy", admin: true } }

Who?
What?

When?

Where?

How?

Why?

What?

class PeopleController < ActionController::Base
  # This will raise an ActiveModel::ForbiddenAttributes exception
  # because it's using mass assignment without an explicit permit
  # step.
  def create
    Person.create(params[:person])
  end

  # This will pass with flying colors as long as there's a person key
  # in the parameters, otherwise it'll raise a
  # ActionController::ParameterMissing exception, which will get
  # caught by ActionController::Base and turned into that 400 Bad
  # Request reply.
  def update
    person = current_account.people.find(params[:id])
    person.update_attributes!(person_params)
    redirect_to person
  end

  private
    # Using a private method to encapsulate the permissible parameters
    # is just a good pattern since you'll be able to reuse the same
    # permit list between create and update. Also, you can specialize
    # this method with per-user checking of permissible attributes.
    def person_params
      params.require(:person).permit(:name, :age)
    end
end

Who?
What?

When?

Where?

How?

Why?

When?

When?

pls stop

Who?
What?

When?

Where?

How?

Why?

Where?

STAHP

Everywhere

srsly guise

*

Who?
What?

When?

Where?

How?

Why?

How?

Require

This specifies a required parameter.

params = 
  { wrong_key: 
    { 
      key1: "thing_one",
      key2: "thing_two"
    }
  }

def object_params
  params.require(:object)
        .permit(:key1, :key2, :_destroy)
end
# ActionController::ParameterMissing: param is
# missing or the value is empty: objects

 {}

Just what it says on the tin.

Simplest Use Case

This is all you need most of the time.

params = 
  { object: 
    { key1: "thing_one",
      key2: "thing_two",
      key0: "not_permitted"
    }
  }

def object_params
  params.require(:object)
        .permit(:key1, :key2, :_destroy)
end
# Unpermitted parameter: key0

 {
    "key1" => "thing_one",
    "key2" => "thing_two"
 }

You'll get back: 

Arrays

We can permit any scalar value, so arrays are ok.

params = 
  {
     object: 
     {  
        key1: [ "thing_one", "thing_two" ]
     }
  }
def object_params
  params.require(:object).permit(key1: [])
end

We can redefine our method.

def object_params
  params.require(:object).permit(key1: [])
end

# Result

 {
     "key1" => ["thing_one", "thing_two"]
 }

Arrays

This breaks our current definition with a NoMethodError.

params = 
  {
    objects: 
      [
         { key1: "one_one", key2: "one_two" }, 
         { key1: "two_one", key2: "two_two" }
      ]
  }


# NoMethodError: undefined method 'permit' 
# for #Array:0x00007f2e59a70b50
def object_params
  params.require(:objects).map do |object|
    object.permit(:key1, :key2)
  end
end


OR


def object_params
  @params.permit(objects: [:key1, :key2])
end

It's getting weird...

Result

def object_params
  params.require(:objects).map do |object|
    object.permit(:key1, :key2)
  end
end






OR

def object_params
  params.permit(objects: [:key1, :key2])
end
  [
    {
      "key1" => "one_one",
      "key2" => "one_two"
    },
    {
      "key1" => "two_one",
      "key2" => "two_two"
    }
  ]

OR

  {
    "objects" => [
      {
         "key1" => "one_one",
         "key2" => "one_two"
      },
      {
         "key1" => "two_one",
         "key2" => "two_two"
      }
    ]
  }

Nested Objects

We can do that.

params = 
  {
    object: 
      {
        key1: "one",
        key2: "two",
        nested_object_attributes: 
          {
            nested_attr_one: "nested_one",
            nested_attr_two: "nested_two"
          }
      }
  }
def object_params
  params.require(:object)
    .permit(:key1, :key2, 
            nested_object_params: [ :nested_attr_one, :nested_attr_two, :_destroy ])
end

Nested Objects

Things can get messier.

params = 
  {
    objects: 
      [{
          key1: "one_one",
          key2: "one_two",
          nested_object_attributes: 
            [{
                nested_attr_one: "nested_one",
                nested_attr_two: "nested_two"
             },
             {
                nested_attr_one: "nested_one2",
                nested_attr_two: "nested_two2"
            }]
       },
       {
          key1: "two_one",
          key2: "two_two",
          nested_object_attributes:
            [{
                nested_attr_one: "nested_one3",
                nested_attr_two: "nested_two3"
            }]
      }]
  }
def object_params
  @params.require(:objects).map do |object|
    object.permit(:key1, :key2,
                  nested_object_attributes: [:nested_attr_one, :nested_attr_two, :_destroy])
  end
end
[
    [0] {
                            "key1" => "one",
                            "key2" => "two",
        "nested_object_attributes" => [
            [0] {
                "nested_attr_one" => "nested_one",
                "nested_attr_two" => "nested_two"
            },
            [1] {
                "nested_attr_one" => "nested_one2",
                "nested_attr_two" => "nested_two2"
            }
        ]
    },
    [1] {
                            "key1" => "two_one",
                            "key2" => "two_two",
        "nested_object_attributes" => [
            [0] {
                "nested_attr_one" => "nested_one3",
                "nested_attr_two" => "nested_two3"
            }
        ]
    }
]

Edge Cases

A whole lot messier...

params = 
  {
    objects: 
      [{
        key1: "one_one",
        key2: "one_two",
        nested_objects: 
          [{
            nested_attr_one: "nested_one",
            nested_attr_two: "nested_two"
          },
          {
            nested_attr_one: "other_nested_one",
            nested_attr_two: "other_nested_two"
          }]
      },
      {
        key1: "two_one",
        key2: "two_two",
        nested_objects:
          [{
            nested_attr_one: "lone_nested_one",
            nested_attr_two: "lone_nested_two"
          }]
      }]
  }

buckle up

def object_params
  @params.require(:objects).map do |object|
    nested_attributes = object.extract!(:nested_objects)

    object.permit(:key1, :key2)
          .merge(nested_object_attributes: nested_object_params(nested_attributes))
  end
end

def nested_object_params(nested_attributes)
  attributes.require(:nested_objects).map do |nested_object|
    nested_object.permit(:nested_attr_one, :nested_attr_two, :_destroy)
  end
end

I promise this works, but I'm not showing my work here.

Enjoy this kitten instead.

Edge Cases

You think this is funny?

params = 
  {
    objects: 
      [{
        key1: "one",
        key2: "two",
        nested_objects: 
          []
      },
      {
        key1: "two_one",
        key2: "two_two",
        nested_objects:
          [{
            nested_attr_one: "lone_nested_one",
            nested_attr_two: "lone_nested_two"
          }]
      },
      {
        key1: "three_one",
        key2: "three_two",
        nested_objects: nil
      }]
  }

Who?
What?

When?

Where?

How?

Why?

Why?

just...

why?

def object_params
  @params.require(:objects).map do |object|
    nested_attributes = object.extract!(:nested_objects)

    object.permit(:key1, :key2)
          .merge(nested_object_attributes: nested_object_params(nested_attributes))
  end
end

def nested_object_params(nested_attributes)
  (attributes || []).map do|nested_object| 
    nested_object.permit(:nested_attr_one, :nested_attr_two)
  end
end

*

DO NOT THIS

def object_params
  @params.require(:objects).permit!
end

Strong Parameters

By ccflack

Strong Parameters

  • 1,279