Ruby on Rails Interview Questions and Answers — Controller — Part 4

Gokul
9 min readOct 31, 2023

--

  • What is the purpose of strong parameters, and how do you define them for nested attributes?
  • How can you customize the behavior of Rails’ automatic CSRF protection in controllers?
  • Explain the role of the ActionController::Live module and how it enables real-time features.
  • How do you implement file uploads and handle file storage in Rails controllers?
  • What is API versioning, and how can you implement it in Rails controllers for RESTful APIs?

16. What is the purpose of strong parameters, and how do you define them for nested attributes?

Strong parameters are a security feature in Ruby on Rails, a popular web application framework. They help protect your application from mass assignment vulnerabilities. Mass assignment occurs when a user can manipulate parameters to set or modify attributes they should not have access to. Strong parameters allow you to whitelist which parameters are allowed to be updated or created, preventing this security risk.

Controller Setup

In your controller, you will define a private method that specifies which parameters are allowed for mass assignment. The typical convention is to name this method model_name_params.

class UsersController < ApplicationController
def create
@user = User.new(user_params)
# ...
end

private

def user_params
params.require(:user).permit(:name, :email, addresses_attributes: [:street, :city, :zip])
end
end

In this example, user_params is a private method that specifies that the name and email attributes of the User model can be mass assigned. Additionally, it allows for nested attributes through addresses_attributes, specifying that the street, city, and zip attributes can be updated for the associated addresses.

Strong Parameters with Nested Attributes

If you have a model with nested attributes, like a User with associated Address records, you use the _attributes convention and specify the permitted attributes for the nested model within the permit method.

In this case, you should also have appropriate model associations set up, such as accepts_nested_attributes_for in your User model:

class User < ApplicationRecord
has_many :addresses
accepts_nested_attributes_for :addresses
end

With this setup, you can create or update a user and their associated addresses in one go by passing the parameters in the request, and strong parameters ensure that only the permitted attributes are assigned.

By using strong parameters, you can control which attributes can be mass-assigned and prevent unauthorized updates or creations of records. This is crucial for the security of your Ruby on Rails application.

17. How can you customize the behavior of Rails' automatic CSRF protection in controllers?

CSRF (Cross-Site Request Forgery) protection is enabled by default to help prevent attackers from executing malicious actions on behalf of a user. CSRF tokens are automatically generated and verified by Rails, but you can customize their behavior in your controllers if necessary.

Customizing CSRF Behavior

If you need to customize the behavior of CSRF protection, you can do so by overriding methods or modifying the settings in the ApplicationController. You can find these settings in the config/application.rb file.

For example, you can customize the behavior of the CSRF token generation by overriding the masked_authenticity_token method:

class ApplicationController < ActionController::Base
def masked_authenticity_token
your_custom_logic_to_generate_token
end
end

Additionally, you can customize the behavior of CSRF token verification by overriding the verified_request? method:

class ApplicationController < ActionController::Base
protected

def verified_request?
your_custom_verification_logic
end
end

Custom CSRF Token Name

By default, the CSRF token parameter is named authenticity_token. You can customize the name of the CSRF token parameter by modifying the config/application.rb:

config.action_controller.request_forgery_protection_token = 'custom_csrf_token_name'

This will change the name of the parameter that Rails uses to look for the CSRF token in incoming requests.

Custom CSRF Exception Handling

You can also customize how Rails handles CSRF exceptions by rescuing from ActionController::InvalidAuthenticityToken in your controller. This allows you to provide a custom response or redirect the user to a specific page when a CSRF exception occurs.

class ApplicationController < ActionController::Base
rescue_from ActionController::InvalidAuthenticityToken do
# Handle CSRF exception, e.g., render a custom error page
end
end

Disabling CSRF Protection

In some cases, you might need to disable CSRF protection for a specific controller or action. To do this, use the skip_before_action method in your controller. For example, to disable CSRF protection for the entire controller:

class MyController < ApplicationController
skip_before_action :verify_authenticity_token
# ...
end

Keep in mind that disabling CSRF protection should be done with caution and only in situations where you have a clear need to do so, such as with API endpoints.

By customizing these aspects of CSRF protection, you can adapt the behavior to suit your specific application’s requirements while maintaining the security measures needed to protect against CSRF attacks. However, be cautious when customizing CSRF protection, as it can introduce security vulnerabilities if not done carefully.

18. Explain the role of the ActionController::Live module and how it enables real-time features

The ActionController::Live module is designed to enable real-time features in your web applications. It allows you to create live, streaming responses to clients, enabling features such as live updates, chat applications, live notifications, and more. This module is particularly useful when you want to push updates from the server to the client in real-time, as opposed to the traditional request-response cycle.

Server-Sent Events (SSE) and Streaming

The primary use case of ActionController::Live is to provide server-sent events (SSE) and streaming capabilities. SSE is a standard for sending real-time updates from the server to the client over a single HTTP connection. It enables the server to push data to the client as soon as it becomes available, without the need for the client to repeatedly poll the server.

Usage in Controllers

To use ActionController::Live, you typically create a controller action that includes the module and uses the response.stream method to initiate streaming. You can then write data to the stream as it becomes available, and the data will be sent to the client in real-time.

Here’s a simple example of how you might use ActionController::Live to implement a basic SSE stream in a controller action:

class LiveUpdatesController < ApplicationController
include ActionController::Live

def stream
response.headers['Content-Type'] = 'text/event-stream'
sse = SSE.new(response.stream)

10.times do
sse.write({ message: 'This is a live update!' })
sleep 1
end

sse.close
ensure
response.stream.close
end
end

In this example, the stream action sets the response content type to 'text/event-stream' and then uses the SSE class to write data to the response stream at regular intervals. The client can connect to this action to receive real-time updates.

Client-Side Implementation

On the client side, you would typically use JavaScript to open a connection to the SSE stream and listen for incoming events.

const eventSource = new EventSource('/live_updates/stream');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log(data.message);
};

The client establishes a connection to the /live_updates/stream URL and listens for incoming messages. When the server sends an event using sse.write, it's received by the client's event listener and can be processed in real-time.

Use Cases

The ActionController::Live module is useful for implementing various real-time features, including live dashboards, chat applications, notifications, and any scenario where you need to push data from the server to the client without the need for continuous polling.

It’s important to note that while ActionController::Live is a powerful tool for enabling real-time features, it requires careful consideration of server resources and potential bottlenecks, as it maintains open connections for the duration of the stream. Proper server and connection management are essential for ensuring the stability and scalability of real-time features in your Rails application.

19. How do you implement file uploads and handle file storage in Rails controllers?

Implementing file uploads and handling file storage in Rails controllers involves a series of steps. You need to set up your controller to handle file uploads, create a form for file selection, and manage file storage. Here’s a step-by-step guide to help you achieve this:

Set Up Your Model

Create a model to represent the data associated with the uploaded file. This model can include attributes like file_name, file_size, and file_content_type. You can use the ActiveStorage framework in Rails, which provides built-in support for file attachments.

rails generate model Document title:string

Add File Attachment Support

In your model, use has_one_attached or has_many_attached to define associations with the uploaded files.

class Document < ApplicationRecord
has_one_attached :file
end

Set Up Your Controller

Create a controller action to handle file uploads. Typically, this action will render an HTML form for users to select and upload a file.

def new
@document = Document.new
end

Create a View for File Upload

In your view, create a form that includes a file input field. You can use the form_with helper in Rails to generate the form.

<%= form_with(model: @document, local: true) do |form| %>
<%= form.label :title %>
<%= form.text_field :title %>

<%= form.label :file %>
<%= form.file_field :file %>

<%= form.submit 'Upload' %>
<% end %>

Handle File Upload in Controller

In your controller, define the action to process the file upload. Use the create action to save the uploaded file to the model.

def create
@document = Document.new(document_params)
if @document.save
redirect_to @document
else
render :new
end
end

private

def document_params
params.require(:document).permit(:title, :file)
end

Configure ActiveStorage

ActiveStorage in Rails provides storage backends for file uploads. Configure your storage settings in config/environments/development.rb and config/environments/production.rb. For example, you can use the local storage for development:

config.active_storage.service = :local

For production, you might use cloud storage services like Amazon S3 or Google Cloud Storage.

Display Uploaded Files

Create a view to display or download the uploaded files. You can use the rails_blob_path helper to generate URLs for the attached files.

<%= link_to 'Download File', rails_blob_path(@document.file, disposition: 'attachment') %>

File Storage Management

Configure and manage your chosen storage service (e.g., Amazon S3) for production environments. You can set up backups, access controls, and other settings as needed.

Validations and Security

Implement validations to ensure that the uploaded files meet your requirements (e.g., file type, and size limits). You may also want to consider security measures like authentication and authorization to control access to uploaded files.

20. What is API versioning, and how can you implement it in Rails controllers for RESTful APIs?

API versioning is a technique in web development that allows you to manage changes and updates to your application’s API (Application Programming Interface) while ensuring backward compatibility with existing clients. It involves creating different versions of your API to accommodate changes in functionality, data structure, or behavior over time.

API versioning helps prevent breaking changes for existing consumers of your API while allowing you to introduce new features, updates, or improvements for newer clients.

You can implement API versioning for RESTful APIs using various strategies. Two common methods for API versioning in Rails controllers are URL-based versioning and Accept header-based versioning.

URL-Based Versioning

URL-based versioning involves including the version number in the URL of your API endpoints. This makes the version explicit and easy to identify in the request.

Configure your routes to include the version number in the URL. You can use the scope or namespace methods to organize your routes by version.

namespace :api do
namespace :v1 do
resources :posts
end
namespace :v2 do
resources :posts
end
end

Create separate controllers for each version of the API, such as Api::V1::PostsController and Api::V2::PostsController. These controllers can contain version-specific logic and behavior.

In your client requests, specify the desired version in the URL, for example, /api/v1/posts or /api/v2/posts.

Accept Header-Based Versioning

Accept header-based versioning involves specifying the desired API version in the Accept header of the HTTP request. This method keeps the URL free of version information and is often considered more RESTful.

Set up your routes without specifying versions in the URL. Use a single versionless namespace for your routes:

namespace :api do
resources :posts
end

Create controllers for each version, such as Api::PostsController. These controllers should house version-specific logic and features.

In your controllers, determine the requested API version by inspecting the Accept header in the request. Respond to the request accordingly, as shown in the previous response.

In the client’s request, specify the desired version in the Accept header, for example, Accept: application/vnd.myapp.v1+json or Accept: application/vnd.myapp.v2+json.

Implementing API versioning in Rails controllers ensures that your API can evolve over time without disrupting existing clients. It also provides a clear way to introduce new features and enhancements for different versions of your API while maintaining compatibility with earlier versions. The choice between URL-based and Accept header-based versioning depends on your project’s requirements and design preferences.

I appreciate you taking the time to read this. Please follow me on Medium and subscribe to receive access to exclusive content in order to keep in touch and continue the discussion. Happy Reading..!

--

--

Gokul

Consultant | Freelancer | Ruby on Rails | ReactJS