[JavaScript] export import きほんのき
初めに
これまでJavaScriptのモジュールにおける、import
export
export default
をなんとなく使用してきたため、今回は復習の意味も込めて簡潔にまとめておく。
export
モジュール化をして他のプログラムから使いたい変数や関数・オブジェクトなどをエクスポートする文法である。名前付きエクスポートと、デフォルトエクスポートの2種類の方法がある。
1. 名前付きエクスポート
module.js export const name = 'taro'; export const fruits = ['apple', 'banana', 'grape']; export const user = {name: '太郎', age: 30}; export function greet() { console.log('hello') };
上記は文字列(name)・配列(fruits)・オブジェクト(user)・関数など複数の値に対してexportを使って記述したモジュールであり、他のプログラムから読み込むだけで、これらの配列や関数などを利用することができるようになる
const name = 'taro'; const fruits = ['apple', 'banana', 'grape']; const user = {name: '太郎', age: 30}; export {name, fruits, user};
最初に宣言をして、後でexportすることも可能
2. デフォルトエクスポート
export default function() { console.log('hello') };
exportには「default」を付与してモジュール化する方法もある。これは1つのモジュールに対して1つだけしか作成できない。また関数名をつけない。import側では任意の名前で取得することができる。
(余談) 集約エクスポート
異なるモジュールから、インポートし、まとめてエクスポートすることもできる。
export { default as name fruits } from 'module.js';
module.jsから、name,fruitsをインポートし、defaultでまとめてexportしている
import
あるモジュールでエクスポートされた、関数やオブジェクトを別モジュール内にインポートするために使う文法。exportにて記述した方法によって、importの記述方法も変わる。
1.名前付きの場合
モジュールからエクスポートをインポートする
import {name, fruits, greet} from './module.js';
importに続けて{ }の中に、export側で定義した変数名、関数名を記述して、モジュール化したファイルのパスを指定する
モジュールのコンテンツすべてをインポートする
import * as myModule from './module.js'; ... myModule.greet();
モジュールにスコープを加えることによって、呼び出すときは、
myModule
という名前空間を用いる。
2.defaultの場合
import hoge from './module.js'; hoge();
defaultを付与している場合は、{ }は付けずに任意の名称をimportに続けて設定することができる。
参考
[Ruby on Rails]Active Job、Sidekiqで非同期に
初めに
筆者は未経験からweb系エンジニアを目指しており、技術面接の対策として、Rails開発者が採用面接で聞かれる想定Q&A 53問(翻訳)を参考にしている。
問題の中で、聞いたことがあっても手元で動かしたことがない機能があり、今回はその1つのActive Jobについて、実際にコードを書きながら、理解を深めていく。
前提
Active Job
ジョブの処理をバックグラウンドで、非同期で実行できる機能のことをいう。主に重たい処理やリアルタイム性を伴わない処理に用いられるとのこと。
Ex.
- メールの送信
- 画像の処理
- データを集計してCSVに落とす等
Job
コンピューターがする仕事の単位。タスクのようなもの
Queue(キュー)
最も基本的なデータ構造の一つで、要素を入ってきた順に一列に並べ、先に入れた要素から順に取り出すという規則で出し入れを行う、タスクの入れ物のようなもの。
順番を待つ人の行列と同じ仕組みであるため「待ち行列」とも訳される。
アダプタ
Active Jobにはキューイングバックエンドに接続するためのアダプタが用意されている。
- async•••デフォルト。プロセスを再起動した時に、登録したジョブがなくなる
- Sidekiq•••アダプタの1つ。Redisを必要としており、Redisはコンピューターから直接アクセスできるメモリ上で動作するため、高速処理が可能
実装
今回はTaskモデルを作成しアダプタはSidekiqにて、実装していく。
1.ジョブクラスの作成
bin/rails g job task
Running via Spring preloader in process 36345
invoke test_unit
create test/jobs/task_job_test.rb
create app/jobs/task_job.rb
2. app/jobs/task_job.rbにて、jobを追加
class TaskJob < ApplicationJob queue_as :default def perform(title) Task.create!(title: title) end end
perform
•••非同期処理時に呼ばれるメソッドであり、ジョブで実行したい処理を実装する
3.キューイングバックエンドの設定
- gem sidekiq の追加。bundle install
gem 'sidekiq'
config/application.rb
でアダプタをSidekiqに指定
... module ActiveJobTodo class Application < Rails::Application config.load_defaults 6.0 config.active_job.queue_adapter = :sidekiq # 追加 end end
4.Redis環境の構築
- dockerを使用して、redis環境をpullする
% docker pull redis Using default tag: latest latest: Pulling from library/redis 1fe172e4850f: Pull complete 6fbcd347bf99: Pull complete ...
- redis環境を立ち上げる
% docker run -p 6379:6379 redis ... 1:M 25 Apr 2022 03:46:25.214 # Server initialized 1:M 25 Apr 2022 03:46:25.214 * Ready to accept connections
5.ジョブをキューへ追加する(コンソール)
irb(main):001:0> TaskJob.perform_later(title: "ブログを作成") Enqueued TaskJob (Job ID: b1095411-52eb-4c2e-bcf7-5dd135aff3cd) to Sidekiq(default) with arguments: {:title=>"ブログを作成"} ...
perform_later
•••バックエンドキューにジョブを追加する。
6.Sidekiqを起動して、ジョブを実行させる
% bundle exec sidekiq m, `$b .ss, $$: .,d$ `$$P,d$P' .,md$P"' ,$$$$$b/md$$$P^' .d$$$$$$/$$$P' $$^' `"/$$$' ____ _ _ _ _ $: ,$$: / ___|(_) __| | ___| | _(_) __ _ `b :$$ \___ \| |/ _` |/ _ \ |/ / |/ _` | $$: ___) | | (_| | __/ <| | (_| | $$ |____/|_|\__,_|\___|_|\_\_|\__, | .d$$ |_| ...
7.ジョブが実行されたことが確認できる
irb(main):002:0> Task.last Task Load (0.3ms) SELECT "tasks".* FROM "tasks" ORDER BY "tasks"."id" DESC LIMIT ? [["LIMIT", 1]] => #<Task id: 1, title: "{:title=>\"ブログを作成\"}", body: nil, created_at: "2022-04-25 04:13:42", updated_at: "2022-04-25 04:13:42">
参考
[Ruby on Rails] ActiveSupport::Concernとは
初めに
今回はActiveSupport::Concernについて触れていく。モジュールを通常のmix-inするよりも、依存関係を気にせずに書けるぐらいの認識だったので、より理解を深めるためにも、公式のコードから掘り下げていく。
結論
- include時にクラスメソッドとしても読み込む必要がある際に、簡易的に書くことができる
- 依存関係をよしなに解決してくれる
前提
include
インスタンスメソッドとして、モジュールを読み込む
extend
クラスメソッドとして、モジュールを読み込む。また、特定のインスタンスにモジュールを読み込ませたいときにも使用可能
本題
公式で取り上げられているコードを深堀し、ActiveSupport::Concernの理解を深める。
モジュールを一般的にmix-inすると以下のようになるとある。
module M def self.included(base) base.extend ClassMethods base.class_eval do scope :disabled, -> { where(disabled: true) } end end module ClassMethods ... end end
メソッド
included
•••モジュールがincludeされたときに呼び出されるメソッドclass_eval
•••クラスにインスタンスメソッドやクラスメソッドを追加することができるメソッド
(base)
に入るのはincludeしたクラス(モジュール)が入っており、extend
メソッドにより、このモジュールをinclude
した際には、インスタンスメソッドとしてだけではなく、クラスメソッドとしても使用できるといった内容である。
ActiveSupport::Concernを使うことで、上記のモジュールは以下のように書くことができるとある。
require "active_support/concern" module M extend ActiveSupport::Concern included do scope :disabled, -> { where(disabled: true) } end class_methods do ... end end
self.included(base)
内でクラスメソッドとして読み込む必要があったものが、extend ActiveSupport::Concern
によって簡素化されたclass_methods do
のブロック内で記述したものがクラスメソッドとして読み込まれるようになった。
また依存するモジュールがある際は、従来以下のように複雑に書かなければならなかった
module Foo def self.included(base) base.class_eval do def self.method_injected_by_foo ... end end end end module Bar def self.included(base) base.method_injected_by_foo end end class Host include Foo include Bar end
- Hostクラスでは、Barモジュールのみを使用したいが、依存関係にあるFooも読み込まないといけないため、include Fooの記載がある。
ActiveSupport::Concernを読み込むことで、よしなに依存関係を解決してくれる
require "active_support/concern" module Foo extend ActiveSupport::Concern included do def self.method_injected_by_foo ... end end end module Bar extend ActiveSupport::Concern include Foo included do self.method_injected_by_foo end end class Host include Bar # It works, now Bar takes care of its dependencies end
extend ActiveSupport::Concernをそれぞれのモジュール内で、読み込ませ、Bar内でFooをincludeすることで最終的なHostクラスでは、Barのみの記述ですんていることがわかる。
参考
[Ruby] ローカル変数 インスタンス変数 クラス変数
初めに
Rubyの変数宣言で、代表的なローカル変数、インスタンス変数、クラス変数についてまとめていく
※補足
本記事のメソッドとはクラスメソッド、インスタンスメソッドを含めたものを指します
ローカル変数
先頭が小文字、_
で宣言される。スコープは、変数が宣言されたところから、ブロック、メソッド、またはクラス・モジュール定義の終りまでであり、それ以外で参照することができない。
コード
class Variable def hoge var = 0 var += 1 puts var end def fuga puts var end def self.piyo puts var end end
コンソール
[1] pry(main)> i = Variable.new [2] pry(main)> i.hoge 1 => nil [3] pry(main)> i.fuga NameError: undefined local variable or method `var' for [5] pry(main)> Variable.piyo NameError: undefined local variable or method `var' for Variable:Class from ...
- hogeメソッド内で定義された
var
は、別のfugaメソッドでは参照できないことがわかる- クラスメソッドであるpiyoも
var
参照されていないことがわかる
インスタンス変数
先頭に@
で宣言される。スコープは、クラスのinitialize
メソッドとインスタンスメソッド内でのみ参照、定義することができる。また、インスタンスごとに独立した値を持つ。
コード
class Variable def hoge @var = 0 @var += 1 puts @var end def fuga puts @var end def self.piyo puts @var end end
コンソール
irb(main):001:0> i = Variable.new => #<Variable:0x00007fb71c670e50> irb(main):002:0> i.hoge 1 => nil irb(main):003:0> i.fuga 1 => nil irb(main):004:0> i_2 = Variable.new => #<Variable:0x00007fb71c682010> irb(main):005:0> i_2.fuga => nil irb(main):006:0> Variable.piyo => nil
クラス変数
@@
で宣言される。スコープはクラス、サブクラス、メソッド、別インスタンスでも共有される、グローバル変数である。
コード
class Variable def hoge @@var = 0 @@var += 1 puts @@var end def fuga puts @@var end def self.piyo puts @@var end end
コンソール
irb(main):001:0> i = Variable.new => #<Variable:0x00007fb71b49bd88> irb(main):002:0> i.hoge 1 => nil irb(main):003:0> i.fuga 1 => nil irb(main):004:0> i_2 = Variable.new => #<Variable:0x00007fb71b4a1530> irb(main):005:0> i_2.fuga 1 => nil irb(main):006:0> Variable.piyo 1 => nil
参考文献
[Ruby on Rails] 数えるメソッドの違い length, size, count
初めに
Railsでは要素の数を数えるメソッドとして、length
、size
、count
がある。今回はこれらのメソッドの違いについて、まとめていく
結論
メソッド名 | キャッシュ参照 | キャッシュ保存 | COUNT関数 |
---|---|---|---|
length | ○ | ○ | × |
size | ○ | × | ○ |
count | × | × | ○ |
前提
SQLキャッシュ
クエリが返す結果をキャッシュする機能。同じクエリが発生した際には、DBへのクエリを実行せずに、キャッシュの値を返す。
準備
name, emailを持つUserモデルを用意し、seeds.rbファイルで10件のデータを用意する。console上でそれぞれのメソッドを呼び出し、検証していく
db/seeds.rb
10.times do |i| User.create(name: "name_#{i}", email: "example-#{i}@hoge.com") end
コンソール下
% bin/rails c irb(main):001:0> users = User.all
検証
length
irb(main):002:0> users.length User Load (0.5ms) SELECT "users".* FROM "users" => 10 irb(main):003:0> users.length => 10
- SQLはusersテーブルを指定し取得している
- User Loadの記述からキャッシュに保存されていることがわかる
- キャッシュを参照しているため、2回目はSQLを発行せず、キャッシュの値を返している。
メソッド名 | キャッシュ参照 | キャッシュ保存 | COUNT関数 |
---|---|---|---|
length | ○ | ○ | × |
size
irb(main):002:0> users.size (0.3ms) SELECT COUNT(*) FROM "users" => 10 irb(main):003:0> users.size (0.3ms) SELECT COUNT(*) FROM "users" => 10 irb(main):004:0> users.length User Load (0.5ms) SELECT "users".* FROM "users" => 10 irb(main):005:0> users.size => 10
- SQLはCOUNT関数にて取得している
- sizeメソッドでは、User loadの記述がなくキャッシュに保存されないことがわかる
- キャッシュを参照しているため、lengthの後に呼び出すと、SQLを発行せずキャッシュの値を返している
メソッド名 | キャッシュ参照 | キャッシュ保存 | COUNT関数 |
---|---|---|---|
size | ○ | × | ○ |
count
irb(main):002:0> users.count (0.2ms) SELECT COUNT(*) FROM "users" => 10 irb(main):003:0> users.count (0.3ms) SELECT COUNT(*) FROM "users" => 10 irb(main):004:0> users.length User Load (0.4ms) SELECT "users".* FROM "users" => 10 irb(main):005:0> users.count (0.4ms) SELECT COUNT(*) FROM "users" => 10
- SQLはCOUNT関数にて取得している
- countメソッドでは、User loadの記述がなくキャッシュに保存されないことがわかる
- キャッシュを参照しないため、lengthの後に呼び出しても、SQLを発行していることがわかる
メソッド名 | キャッシュ参照 | キャッシュ保存 | COUNT関数 |
---|---|---|---|
count | × | × | ○ |
参考
[Ruby on Rails] redirect_to と render の挙動の違い
初めに
なぜインスタンスの保存に成功するとredirect_to
で、失敗するとrender
なのか。ぼんやりとは分かるが、自信を持って言語化できなかったので、実際にコードを書き掘り下げていく
結論
- 保存に成功したときに
render
をしてしまうと、インスタンス変数が参照できず(controllerを経由しないため)例外が発生する - 保存に失敗した後に
redirect_to
をしてしまうと、errorsが参照できなくなってしまう
前提
redirect_to
ブラウザ側に対し、別のURLへ再度アクセスするように指示をする。つまりredirect_toの後は、
routes → controller(model) → view の順に処理が実行される
render
ブラウザ側に返すviewファイルを指定する。この場合は特にcontrollerなど経由せず、そのままviewファイルを参照する
準備
今回はscaffoldを活用して、簡易的なTodoアプリを作成する。modelsにバリデーションとして、titleには必ず値が入るようにし、bodyには、20文字以内とする
models/task.rb
class Task < ApplicationRecord validates :title, presence: true validates :body, length: { maximum: 20 } end
controllers/tasks_controller.rb
... def create @task = Task.new(task_params) if @task.save redirect_to tasks_path else render :new, status: :bad_request end end ...
views/tasks/new.html.rb
... <%= form_with(model: task, local: true) do |form| %> <% if task.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(task.errors.count, 'error') %></h2> <ul> <% task.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> </div> <% end %> ....
上記のコードだとタスクの作成ができ、エラーメッセージも問題なく表示されているのがわかる
成功時
失敗時
検証
1. 保存に成功したときに、render :index
にする
def create @task = Task.new(task_params) if @task.save render :index ... end
結果
ログを見ると、
ActionView::Template::Error (undefined method `each' for nil:NilClass): ... app/views/tasks/index.html.erb:15 app/controllers/tasks_controller.rb:22:in `create'
レシーバーである@tasksがTaskのコレクションを取得しておらず、nilとなっているため発生するエラーと分かる
2. 保存に失敗したときに、redirect_to new_task_path
とする
def create if @task.save ... else redirect_to new_task_path end end
結果
リダイレクトされているため、errors.full_messagesを参照することができないことが分かる