Category TILs
Thread.new.join
command = Thread.new do
# do something
end
command.join
command = Thread.new do
# do something
end
command.join
The join
method is used to wait for the thread to complete before continuing with the rest of the program. This ensures that any work performed by the thread has finished before moving on to the next task.
Devise paranoid
Problem
When trying to reset password with not exists email => “Email not found”
Cause
Normal action of devise but not good if someone want to know your user email
Solution
Use devise paranoid mode
# It will change confirmation, password recovery and other workflows
# to behave the same regardless if the e-mail provided was right or wrong.
# Does not affect registerable.
config.paranoid = true
Confident Rails ENV
Problem
Sometimes after deployment my app did not work because of missing ENV
Cause
Currently my code look like
vault_token = ENV['VAULT_TOKEN']
and when I forgot to add VAULT_TOKEN
to .env file it simple return nil
so my app failed
Solution
Thanks to Confident Ruby and rubocop, I totally move to fetch
vault_token = ENV.fetch('VAULT_TOKEN')
# => in `fetch': key not found: "VAULT_TOKEN" (KeyError)
fetch
also have options for default value, for block…
FYI: https://apidock.com/ruby/Hash/fetch
Rails 6.1 form_with update
Problem
My form submit process as WidgetsController#create as HTML
My expectation: WidgetsController#create as JS
Cause
Due to this PR
Rails 6.0: form_with
would generate a remote form by default.
Rails 6.1: form_with
would generate a non-remote form by default.
Solution
Update config config/environments/*.rb
config.action_view.form_with_generates_remote_forms = true
Spread object support for safari 10
Problem
I got error message “Unexpected token ‘…’. Expected a property name.” when using Safari 10.
Cause
Due to Document Spread in object literals only support Safari version 11.1 and later.
Solution
Using this plugin to parse spread to Object assign
{
"plugins": ["@babel/plugin-proposal-object-rest-spread"]
}
In
z = { x, ...y };
Out
z = Object.assign({ x }, y);
GraphQL Invalid IAP credentials: empty token
Tech stacks:
- Google app engine with Identity-Aware Proxy turned on
- GraphQL with Apollo Server Express
Problem
- Cannot enable playground in production
- Appolo Docs
const { ApolloServer } = require('apollo-server');
const { typeDefs, resolvers } = require('./schema');
const server = new ApolloServer({
typeDefs,
resolvers,
introspection: true,
playground: true,
});
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
Cause
- Request POST
/graphql
return 401 errors
Digging in that request we got "credentials": "omit"
Solution
...
const server = new ApolloServer({
typeDefs,
resolvers,
introspection: true,
playground: {
settings: {
'request.credentials': 'include'
}
},
});
...
const { ApolloServer } = require('apollo-server');
const { typeDefs, resolvers } = require('./schema');
const server = new ApolloServer({
typeDefs,
resolvers,
introspection: true,
playground: true,
});
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
/graphql
return 401 errors Digging in that request we got
"credentials": "omit"
...
const server = new ApolloServer({
typeDefs,
resolvers,
introspection: true,
playground: {
settings: {
'request.credentials': 'include'
}
},
});
...
You dont need Momentjs
Problem
The format I need: 2020-04-20T16:20:00+09:00
Some code smell I wrote :-ss
const standardizeDate = (date) => {
date.setHours(date.getHours() + 9);
return date.toISOString().slice(0, -5) + "+09:00";
}
This one looks much better:
<script src="https://unpkg.com/dayjs@1.8.21/dayjs.min.js"></script>
const standardizeDate = (date) => dayjs(date).format('YYYY-MM-DDTHH:mm:ssZ');
Performance concerns
Shout out to You-Dont-Need-Momentjs and also dayjs
Using Puppeteer in Google Cloud Functions
Setup
package.js
{
"name": "sample-http",
"version": "0.0.1",
"dependencies": {
"puppeteer": "^1.9.0"
}
}
Problem
const browser = await puppeteer.launch();
// Error: Failed to launch chrome!
Solution
const browser = await puppeteer.launch({
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
]
});
Reference to puppeteerl
Better Rails where like query
Problem
user_name = "I'm Tony"
User.where("name like '%#{user_name}%'")
# => SELECT `users`.* FROM `users` WHERE (name like '%I'm Tony%')
=> ActiveRecord::StatementInvalid: Mysql2::Error: You have an error in your SQL syntax;
Better solution
User.where("name like ?", "%#{user_name}%")
# => SELECT `users`.* FROM `users` WHERE (name like '%I\'m Tony%')
Set basic auth for specific environment
For all environment
Set authentication in ApplicationController
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
http_basic_authenticate_with name: 'username', password: 'password'
end
For specific environment
Set authentication in environment.rb
, using rack middleware
# config/environments/staging.rb
# also can set for other environment (development, clone, preview...)
Rails.application.configure do
config.middleware.use Rack::Auth::Basic, "Protected Environment" do |username, password|
username == "username" && password == "password"
end
end
Rails cache fetch
Problem
def generate_job_id
cached_value = Rails.cache.fetch("cache_key", expires_in: 24.hours) do
job_ids = Job.all.map(&:id) # Trillion jobs
return [] if job_ids.blank?
job_ids
end
cached_value
end
def generate_job_id
cached_value = Rails.cache.fetch("cache_key", expires_in: 24.hours) do
job_ids = Job.all.map(&:id) # Trillion jobs
return [] if job_ids.blank?
job_ids
end
cached_value
end
=> Missing cache when job_ids blank, cached_value is nil
def generate_job_id
cached_value = Rails.cache.fetch("cache_key", expires_in: 24.hours) do
job_ids = Job.all.map(&:id) # Trillion jobs
break [] if job_ids.blank?
job_ids
end
cached_value
end
=> Missing cache when job_ids blank, cached_value is []
def generate_job_id
cached_value = Rails.cache.fetch("cache_key", expires_in: 24.hours) do
job_ids = Job.all.map(&:id) # Trillion jobs
job_ids.blank? ? [] : job_ids
end
cached_value
end
=> Problem solved, “cache_key”
Reference to https://github.com/rails/rails/blob/a08a069acdbea8a74282f753a2498d0d7b0c3137/activesupport/lib/active_support/cache.rb#L31 Exactly function
#https://github.com/rails/rails/blob/master/activesupport/lib/active_support/cache.rb#L737
def save_block_result_to_cache(name, **options)
result = instrument(:generate, name, options) do
yield(name)
end
write(name, result, options) unless result.nil? && options[:skip_nil]
result
end
The line write
is not executed =>
Ruby Array uniq
List post
post_a (topic_name: "Ruby", priority: 10)
post_b (topic_name: "PHP", priority: 9)
post_c (topic_name: "Ruby", priority: 8)
post_a (topic_name: "Ruby", priority: 10)
post_b (topic_name: "PHP", priority: 9)
post_c (topic_name: "Ruby", priority: 8)
Expect each topic has only 1 post, order by priority
current_posts
# => [["Ruby", post_a], ["PHP", post_b], ["Ruby", post_c]]
to_h
current_posts.to_h
# => {"Ruby" => post_c, "PHP" => post_b}
=> post_c
with lower priority is above post_b
=> ERROR
uniq
current_posts.uniq(&:first).to_h
# => {"Ruby" => post_a, "PHP" => post_b}
=> as expected
Rails Size & Count
Mỗi khi exec query, Active Record sẽ tạo ra biến @loaded = true cho Relation đó.
@posts = Post.all
@posts.loaded? #=>true
Hàm size
và count
def size
loaded? ? @records.length : count(:all)
end
def count(column_name = nil, options = {})
# [...]
calculate(:count, column_name, options)
end
count
luôn luôn sinh ra query, còn size
sẽ check relation và query nếu cần.
Rõ ràng là cần cân nhắc dùng count
để tránh dư thừa query.
Ruby Set
Ruby Set - a list of unique items.
Two special attributes:
- Fast lookup times (with include?)
- Unique values
Benchmark #include?
# Ruby 2.5.0
set include: 8381985.2 i/s
array include: 703305.5 i/s - 11.92x slower
Credit to rubyguides
HTML target _blank and blank
<a target="_blank|_self|_parent|_top|framename">
_blank
always opens url in new windowblank
meansframename
, opens url in a window namedblank
if exists, otherwise open new window and name itblank
- you possible to set any name for
target
eg:<a target="new_window">
Happy 420
#happy420
#happybirthday
Bootstrap 4 grid tier & reordering
Grid option
From v4.0.0-alpha.6, the xs
tier no longer requires a breakpoint abbreviation
Reordering
From v4.0.0-beta, use .order-
classes for controlling the visual order of your content
PC view
| Column A | Column B |
Mobile view
| Column B |
| Column A |
Boolean type in Rails & Mysql
MySQL BOOLEAN data type, which is the synonym of TINYINT(1), value range [-128..127]
Confused situation
id (integer) | name (string) | status (boolean) |
---|---|---|
1 | Aoi | 1 |
2 | Maria | 10 |
# Rails 4
Idol.find(2).status
=> false
# Rails 5
Idol.find(2).status
=> true
Explanation
Rails 4 boolean.rb
def cast_value(value)
if value == ''
nil
elsif TRUE_VALUES.include?(value)
true
else
# [...]
false
end
end
Rails 5 boolean.rb
def cast_value(value)
if value == ""
nil
else
!FALSE_VALUES.include?(value)
end
end
Be careful when use find_by
City.find_by(name: "Maria") || City.find_by(name: "Aoi")
City.find_by(name: "Maria") || City.find_by(name: "Aoi")
Liệu có giống với:
City.find_by(name: ["Maria", "Aoi"])
Trông thì có vẻ giống đó, thử check SQL câu dưới xem
Idol.find_by(name: ["Maria", "Aoi"])
# => SELECT `idols`.* FROM `idols` WHERE `idols`.`name` IN ('Maria', 'Aoi') LIMIT 1
Trông vẫn có vẻ ổn, nhưng câu này lại trả ra Aoi
thay vì Maria
như mình mong đợi.
Vấn đề là hàm find_by(arg, *args)
được định nghĩa như sau:
Finds the first record matching the specified conditions. There is no implied ordering so if order matters, you should specify it yourself. If no record is found, returns nil.
# File activerecord/lib/active_record/relation/finder_methods.rb, line 80
def find_by(arg, *args)
where(arg, *args).take
rescue ::RangeError
nil
end
Vì no implied ordering
, nên khi gặp Aoi
với id nhỏ hơn sẽ trả ra kết quả luôn thay vì tìm Maria
trước theo như argument mà ta truyền vào.
Để có được kết quả như mong đợi mình phải dùng where và thêm order:
Idol.where(name: ["Maria", "Aoi"]).order("FIELD(name, ('Maria, Aoi'))").take
# => SELECT `idols`.* FROM `idols` WHERE `idols`.`name` IN ('Maria', 'Aoi') ORDER BY FIELD(name, ('Maria, Aoi')) LIMIT 1
Dùng 2 account github trên cùng 1 thiết bị
Config lại file ~/.ssh/config
# Account của công ty
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa
# Account cá nhân
Host github-me
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa_me
Khi Clone thì chú ý url, sửa git@github.com
thành git@github-me
git clone git@github-me:account_name/repo_name.git
Check lại remote:
git remote -v
origin git@github-me:account_name/repo_name.git (fetch)
origin git@github-me:account_name/repo_name.git (push)
Config author
- Add config system
# /etc/gitconfig [user] email = account@company.com name = company
- Add config local
# your_personal_repo/.git/config [user] email = account@me.com name = me