[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
  • hogeメソッド内で定義されたvarは、fugaメソッドでも参照されている
  • インスタンスであるi_2varを参照できない
  • クラスメソッドであるpiyoはvarを参照できない



クラス変数

@@で宣言される。スコープはクラス、サブクラス、メソッド、別インスタンスでも共有される、グローバル変数である。


コード

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


  • hogeメソッド内で定義されたvarは、fugaメソッドでも参照されている
  • インスタンスであるi_2varを参照できている
  • クラスメソッドであるpiyoはvarを参照できている

参考文献


[Ruby on Rails] 数えるメソッドの違い length, size, count

初めに


Railsでは要素の数を数えるメソッドとして、lengthsizecountがある。今回はこれらのメソッドの違いについて、まとめていく


結論


メソッド名 キャッシュ参照 キャッシュ保存 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 %>
....


上記のコードだとタスクの作成ができ、エラーメッセージも問題なく表示されているのがわかる

成功時 Image from Gyazo

失敗時 Image from Gyazo


検証


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



結果

Image from Gyazo

リダイレクトされているため、errors.full_messagesを参照することができないことが分かる

参考