はじめに
- 私はElixirを使ってプログラムを書くのが好きです
-
|>
がお気に入りです
-
- 仕事ではRuby on Railsを使うことが多く、中でもActiveAdminにたいへんお世話になっています
- たったの数行を書くだけでCRUDを備えた管理画面ができあがります
- この記事では以下のことを行います
- Elixirを使って定期的に天気予報データを取得して、データベースに保存をします
- ActiveAdminを使って、管理画面を作ります
- Sapporo.beamとOkazaKirin.beam という勉強会での成果です
補足
- もちろん、Elixirだけを使ってPhoenixで管理画面をつくることはできますし、Rubyだけで作ることもできます
- それを言われると「はい、そうですね」とその通りであることを認めるのですが、とにかく私はElixirを使いたいのです
- (好きなものに理由は必要ですか?)
- (理由がアレコレあるものは本当に好きなものなのですか?)
- 以前、Slack Workflowの申請内容を一覧(CSV)にする(with Elixir) という記事を書いて、Slackからある条件にあてはまるデータを取得して、CSVに出力するというプログラムを紹介しました
- これを発展させて、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)
$ mix new weathers --sup
-
mix.exs
を少し変更します
mix.exs
defp deps do
[
{:ecto_sql, "~> 3.0"},
{:postgrex, ">= 0.0.0"},
{:lwwsx, "~> 0.1.1"}
]
end
- lwwsx というのは、Livedoor様のお天気Webサービスから天気予報を取得するHexです
- 依存関係を解決します
$ mix deps.get
-
Weathers.Repo
をつくります - これを通して、データベースにインサートします
$ mix ecto.gen.repo -r Weathers.Repo
-
lib/weathers/application.ex
を変更して、Weathers.Repo
をsupervision tree内のsupervisorとして登録をします
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.
- 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
Yay!
まとめ
-
Elixirは美しく、書いていて楽しい言語です
- プログラミングってこんなにおもしろかったのだ! と改めて気づかせてくれます
-
Ruby on Rails以外で作ったデータベースのスキーマは、
rails db:schema:dump
でdb/schema.rb
に反映させます - ActiveAdminを使うと、たった数行書くだけで、CRUDを備えた管理画面を完成できます
- このプロジェクトは以下で動かしています
- しばらくはサンプルをTime4VPS上で動かしておきますのでご自由にお使いください
User: admin@example.com
Password: password