LoginSignup
10
3

More than 3 years have passed since last update.

ecto(Elixir) + ActiveAdmin(Ruby on Rails)で管理ツールを作ってみる(with Elixir)

Last updated at Posted at 2020-04-09

はじめに

  • 私はElixirを使ってプログラムを書くのが好きです
    • |> がお気に入りです
  • 仕事ではRuby on Railsを使うことが多く、中でもActiveAdminにたいへんお世話になっています
    • たったの数行を書くだけでCRUDを備えた管理画面ができあがります
  • この記事では以下のことを行います
    • Elixirを使って定期的に天気予報データを取得して、データベースに保存をします
    • ActiveAdminを使って、管理画面を作ります
  • Sapporo.beamOkazaKirin.beam という勉強会での成果です

スクリーンショット 2020-04-08 23.13.54.png

補足

  • もちろん、Elixirだけを使ってPhoenixで管理画面をつくることはできますし、Rubyだけで作ることもできます
  • それを言われると「はい、そうですね」とその通りであることを認めるのですが、とにかく私はElixirを使いたいのです
    • (好きなものに理由は必要ですか?)
    • (理由がアレコレあるものは本当に好きなものなのですか?)
  • 以前、Slack Workflowの申請内容を一覧(CSV)にする(with Elixir) という記事を書いて、Slackからある条件にあてはまるデータを取得して、CSVに出力するというプログラムを紹介しました
  • これを発展させて、ectoを使ってデータベースに保存し、ActiveAdminを使って一覧表示をする社内ツールをつくりました
  • このときのエッセンスだけを取り出して、 まとめております

作品

ecto_activeadmin

必要なもの

  • Elixir 1.9.4
  • Ruby 2.6.3
    • もっと新しいバージョンでもおそらく大丈夫です
  • PostgreSQL 12.1
    • この記事では、ユーザ名がpostgresでパスワードがpostgresのスーパーユーザアカウントがあることを前提にしています
    • もしあなたのマシンにこのアカウントが設定されていない場合は、psql postgresと入力して以下のコマンドを入力することで作成できます
    • これはEctoの記事から転載しています
postgres=# CREATE USER postgres;
postgres=# ALTER USER postgres PASSWORD 'postgres';
postgres=# ALTER USER postgres WITH SUPERUSER;
postgres=# \q

それでは作業ディレクトリを作って早速はじめましょう。

$ mkdir ecto_activeadmin
$ cd ecto_activeadmin

ectoを使ったproject(Elixir)

  • 大好きなElixirのプログラムを作ります
  • ectoは、データベースとElixirの間のデータマッピングと統合クエリのためのツールキットです
$ mix new weathers --sup
  • mix.exs を少し変更します
mix.exs
  defp deps do
    [
      {:ecto_sql, "~> 3.0"},
      {:postgrex, ">= 0.0.0"},
      {:lwwsx, "~> 0.1.1"}
    ]
  end
$ mix deps.get
  • Weathers.Repo をつくります
  • これを通して、データベースにインサートします
$ mix ecto.gen.repo -r Weathers.Repo
  • lib/weathers/application.exを変更して、Weathers.Repoをsupervision tree内のsupervisorとして登録をします
    • ルー大柴さんみたいになっていますが、こうとしか説明ができないです(私が理解できていないとも言う:man_tone3:)
    • Elixirが、堅牢だと言われるゆえんのところの話で奥が深いです
    • Don't think, feeeel.
lib/weathers/application.ex
  def start(_type, _args) do
    children = [
      Weathers.Repo
    ]
  • config/config.exsを変更します
config/config.exs
config :weathers, Weathers.Repo,
  database: "weathers_repo",
  username: "postgres",
  password: "postgres",
  hostname: "localhost"

config :weathers,
  ecto_repos: [Weathers.Repo]
  • ここまでできたら、データベースを作ります
$ mix ecto.create
  • 続いてテーブル作成のためのマイグレーションファイルの雛形をつくります
$ mix ecto.gen.migration create_weathers
  • マイグレーションファイルは以下の内容にします
priv/repo/migrations/20200408123754_create_weathers.exs
defmodule Weathers.Repo.Migrations.CreateWeathers do
  use Ecto.Migration

  def change do
    create table(:weathers) do
      add :city_number, :integer, null: false
      add :city, :string, null: false
      add :text, :text, null: false

      timestamps()
    end
  end
end
  • lib/weathers/weather.exを以下のように作ります
  • 感じてください
lib/weathers/weather.ex
defmodule Weathers.Weather do
  use Ecto.Schema
  import Ecto.Changeset

  schema "weathers" do
    field :city_number, :integer
    field :city, :string
    field :text, :string

    timestamps()
  end

  def changeset(weather, attrs) do
    weather
    |> cast(attrs, [:city_number, :city, :text])
    |> validate_required([:city_number, :city, :text])
  end
end
  • lib/weathers/worker.exを作ります
  • 1000 * 60 * 60 秒ごとにWeathers.Worker.handle_infoが呼び出されて、Weathers.Worker.runが実行されます
  • Weathers.Worker.runは任意の地点の天気予報データを取得してきて、データベースに保存をします
lib/weathers/worker.ex
defmodule Weathers.Worker do
  use GenServer

  def start_link(initial_val) do
    GenServer.start_link(__MODULE__, initial_val, name: __MODULE__)
  end

  def init(initial_val) do
    Process.send_after(__MODULE__, :tick, 1000)
    {:ok, initial_val}
  end

  def handle_info(:tick, state) do
    spawn(Weathers.Worker, :run, [])
    Process.send_after(__MODULE__, :tick, 1000 * 60 * 60)

    {:noreply, state}
  end

  def run do
    {city_number, city} = Lwwsx.cities() |> Enum.random()
    {:ok, text} = Lwwsx.current_text(city_number)

    Weathers.Repo.insert(
      Weathers.Weather.changeset(%Weathers.Weather{}, %{city_number: city_number, city: city, text: text})
    )
  end
end
  • またルー大柴さんみたいなことをいいますが、lib/weathers/application.exを変更して、Weathers.Workerをsupervision tree内のsupervisorとして登録をします
lib/weathers/application.ex
  def start(_type, _args) do
    children = [
      Weathers.Repo,
      Weathers.Worker
    ]
  • ok
$ iex -S mix
  • このまま放っておけば、1時間に一回天気予報データを取得するようになります
  • どうでしょうか!?
  • 美しいですよね!

ActiveAdminを使ったプロジェクト(Ruby on Rails)

  • 管理画面を作っていきます
$ pwd
/hoge/ecto_activeadmin/weathers
$ cd ..
$ mkdir web
$ cd web
$ bundle init
  • Gemfileを編集します
Gemfile
gem "rails"
  • bundle installしてRuby on Railsのプロジェクトを作ります
$ bundle install --path vendor/bundle
$ bundle exec rails new . -d postgresql
  • データベースに接続できるようにconfig/database.ymlの設定を変更します
config/database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: postgres
  password: postgres

development:
  <<: *default
  database: weathers_repo

test:
  <<: *default
  database: web_test

production:
  <<: *default
  database: web_production
  username: web
  password: <%= ENV['WEB_DATABASE_PASSWORD'] %>
  • まず、Elixirを使って作ったプロジェクトのほうでできたデータベースのスキーマをダンプします
$ bundle exec rails db:schema:dump
  • そうすると、db/schema.rbが以下のように作られるはずです
db/schema.rb
ActiveRecord::Schema.define(version: 2020_04_08_123754) do

  # These are extensions that must be enabled in order to support this database
  enable_extension "plpgsql"

  create_table "weathers", force: :cascade do |t|
    t.integer "city_number", null: false
    t.string "city", limit: 255, null: false
    t.text "text", null: false
    t.datetime "inserted_at", precision: 0, null: false
    t.datetime "updated_at", precision: 0, null: false
  end

end
  • すばらしい!
  • 続いて、Gemfileを変更してActiveAdminを使えるようにします
Gemfile
gem 'activeadmin'
gem 'devise'
gem 'cancancan'
gem 'draper'
gem 'pundit'

ちょっと一息

  • deviceのライセンス表記をみますと、以下のようになっています
MIT License. Copyright 2020 Rafael França, Leaonardo Tegon, Carlos Antônio da Silva. Copyright 2009-2019 Plataformatec.
We are the company behind Elixir
We offer Elixir consulting and development services for companies using Elixir
  • と書いてあります
  • Elixirとの強い縁を感じます

  • それでは、開発に戻りましょう

  • bundle installしてから、以下のようにActiveAdminのセットアップ、データベースのマイグレーション、シードデータの投入をします

$ bundle install
$ bundle exec rails g active_admin:install
$ bundle exec rails db:migrate
$ bundle exec rails db:seed
  • ここまではいろいろ手順はありますが、一行もコードは書いていません
  • なんでも準備には時間と労力がかかるのです
  • そして、ここからいよいよRuby on Railsプロジェクトにコードを追加していきます
app/models/weather.rb
# app/models/weather.rb
class Weather < ApplicationRecord; end
app/admin/weathers.rb
# app/admin/weathers.rb
ActiveAdmin.register Weather do
  permit_params :city_number, :city, :text, :inserted_at
end
  • 以上です
  • たったこれだけ、この2ファイルだけを作ればよいのです
  • この記事のハイライトです
  • さて、Railsを起動してみましょう
$ bundle exec rails s 
  • ブラウザで、 http://localhost:3000/admin/ にアクセスしてください
  • ログイン画面が表示されますので以下の内容を入力してください
User: admin@example.com
Password: password

スクリーンショット 2020-04-08 23.13.54.png

Yay!

まとめ

  • Elixirは美しく、書いていて楽しい言語です
    • プログラミングってこんなにおもしろかったのだ! と改めて気づかせてくれます
  • Ruby on Rails以外で作ったデータベースのスキーマは、rails db:schema:dumpdb/schema.rbに反映させます
  • ActiveAdminを使うと、たった数行書くだけで、CRUDを備えた管理画面を完成できます
  • このプロジェクトは以下で動かしています
  • しばらくはサンプルをTime4VPS上で動かしておきますのでご自由にお使いください

User: admin@example.com
Password: password
10
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
3