Rails Note and Cheatsheet
Catastrophic Consequence of Prepared Statement in Rails < 5
- Rails has prepared statement limit to 1000 per connection and cache in the memory, which could cause memory leak issue.
- When we run migration remove/adding new column, it could cause the prepared statement cache to be invalid and cause fail in execution. Fix: retry. Best practice: should restart server when run migration.
- Actually we can set prepared statement to false in database configuration
- From Rails 5, should be a fixed and improvement on prepared statement
- Reference:
https://medium.com/carwow-product-engineering/activerecord-error-cached-plan-must-not-change-result-type-aca72cdef504
https://medium.com/@devinburnette/be-prepared-7768d1a111e1
Rails 5 Routes: Nest resources and shallow option
Whether you shallow on action new
, create,
index
or not, it is still not shallow. For example:
resources :users do
resources :payments, only: %i(create), shallow: true
endresources :users do
resources: :payment, only: %i(create), shallow: true
end
will produce the same routes:
user_payments POST /users/:user_id/payments(.:format) payments#create
present? vs any? vs exist?
- In Rails 4,
present?
retrieves all records.any?
counts record.exists?
retrieves one record. Always useexists?
though. any?
andexists?
in Rails 5 is the same thing.
Don’t confuse with
file?
orpresent?
vsexists?
in rails paper clip.file?
orpresent?
check if there is paper clip object whileexists?
performs TCP connection to cloud storage to check if file exists. In this case,file?
orpresent
performance much better but useexists?
if only you want to check file exist in the cloud storage.
Postgresql Time Zone
- Always use timezone across database server as UTC and don’t dare to switch time zone to anywhere else even even you change database location. With this golden rule, you don’t have to care if you use
timestamp
ortimestamptz.
- No matter how many applications you access to database server, no matter how you config time zone at application server, always persist timestamp in UTC.
How to start conversation. First what is your timezone? Is it in UTC? Otherwise, conversation is terminated.
includes
and eager_load
include
will make multiple queries but with some condition where
on included resources, includes
will make single query. However we can force includes
to use single query by adding references
. On the other hand, eager_load
will always make single query with or without some condition where
on included resources. Thus, why do Rails have single query and multiple queries strategy? Using single query when small data set OR with some condition where
. Using multiple queries when large data set AND without condition where
. On the order hand, preload
is not smart as includes
, it will use multiple queries strategy to load data, while includes
will swap to use single query left outer join
if there are condition where
or order
.
Two kind of strategies to store session in Rails
- Cookie store: store all data in the cookie
- Everything else (memory store and database store): Store a pointer to data in the cookie
The user of `:inverse_of`
- Memory optimization, no necessary query to database
- Creating child nest attributes with the validation of presence of its parent
- Auto creating join model in
has_many :through
Request caching > object memoization
It is a wake up call ever to understand the what is actually request caching differs from object memoization. Request caching works like caching the result of request in the memory and and then if there is the same request, it won’t query database call again. It will retrieve result from memory and it needs to expire sometimes.
Unlike object memoization, in Rails object life cycle along with request, so per request Rails can instantiate many objects in the memory and those objects life cycle will end after a request ends too. Similarly, object can be memoized in the memory too. During a request, if object that make database call is already instantiated, it won’t be instantiated again, because it is memoized in the memory. Because of object life cycle is short, it is normally not to be expired.
Even request caching and object memoization is technique in different context, but they generally use the same data structure such as hash to store key and value in the memory.
Rails Funny and Weird Learning
With save, if something went wrong, it quietly cancels saving. Unlike, bang save (save!) cancels saving loudly with error exception. To me, Save! save me life.
Two types of debug: application level and service level (nginx, database…etc.). Thus, don’t just debug your application. Occasionally debug your system. Maybe it is memory leaking…You won’t know!
Rails App Optimization
Pagination, Caching, Background jobs
Rails has a great love to RESTful web service
Why REST?
- give API for free
- simplicity and convenience
- turn code into resource
- HTTP verbs
PUT vs PATCH
- use PUT to replace resource
- use PATCH to update resource
- Rails uses PATCH rather than PUT
HTTP status codes
- 1xx : information
- 2xx: success
- 3xx: redirect
- 4xx: client error
- 5xx: server error
API in Rails
- use namespace to keep controllers organized (module API….)
- write API integration test with ActionDispatch::IntegrationTest
- status: 200 == status: ok
- Try CURL
- Try JBuilder gem
- Try http_accept_languae gem
- We should use empty session because REST API is stateless:
- protect_from_forgery with: :null_session
API versioning
- URL versioning: namespace on route
- test with assert_generate (ActionDispatch::IntegrationTest)
- module on controller
- Try versionist gem
- 2 ways to version API:
- Add version to URL
- Add version to request header
API Authentication
- Basic Auth: Basic + base64 encoding (not encrypted)
- use authenticate_with_http_basic method
- custom realm
- http token: it is more secure and flexible than basic auth because it can be expired or generated base on client user.
- In the header: token = abc123…789xyz
- digest authentication: work like basic auth but the user name and password are encrypted during communication.
- Oauth (Open Authorization): is better because it works same as user login/out. Oauth with Doorkeeper or Oauth2. Allow provider to give access to users without exchange of credentials. User does not have problem when provider changes password. Oauth has client_id, token_secret, access_token. Try omni_auth gem: use at client application. Try oauth2 gem
Oauth Token
Token is the key that mobile app uses to request resource from resource server. There are two types of token, access token and refresh token.
How token get generate?
- A client (third party, mobile app) requests a token.
- An issuer issues a token.
- A resource server consumes a token (has a trust relationship with the issuer).
What is Token?
Tokens represent specific scopes and durations of access, granted by the resource owner , and enforced by the resource server
API Best Practice
We should better handle unauthorized request by not halting the request. Instead we should return the boolean check and create method to handle the unauthorized request gracefully. For example, 401 is unauthorized code.
Service Object in Rails
A service object’s job is to hold the code for a particular bit of business logic. ~ DAVE COPELAND, from Anatomy of a Rails Service Objects.