Railsとデータ操作
モチベーション
- データのコントロールを規則的に実施したい。
- Rails使ってまでやることかと言われると、そこまでじゃないけど、自分で足回り書くのもめんどいし、楽しいじゃんというところ。
- 爆速で、データベースにデータをぶち込んで、データ操作したいなど。
ストラテジー
Rails特にrails consoleを使って、Rubyを書いてデータ操作する。ほぼActive Record。
作成するモデル・使い方
Webページを扱う。モデルは以下の内容で作成する。
| column1* | column2 | column3 | column4 |
|---|---|---|---|
| url | title | access_flag | content |
プロジェクトの作り方
# make dir
mkdir bookmark
cd $_
bundle init
# Install Rails
echo 'gem "rails"' >> Gemfile
# install rails
bundle install
# make rails project
bundle exec rails new --minimal .
Reference
scaffoldを使用した機能作り
| Name | Item |
|---|---|
| モデル名 | Page |
| カラム1* | url:string |
| カラム2 | title:string |
| カラム3 | access_flag:boolean |
| カラム4 | content:text |
rails generate scaffold 名前 [カラム名:型[:index]..] [オプション]
bundle exec rails g scaffold Page url:string title:string access_flag:boolean content:text
Rails 8系を想定している場合、scaffold時にurl:string:uniqと指定すればマイグレーションファイルに自動でユニーク制約が追加される。
実際のところ、scaffoldする必要もないかもしれない。モデルだけ作ればいいかもしれない。
ユニーク制約
migrateファイルにて
生成されるdb/migrate/yyyymmddhhmmss_create_pages.rbを編集する。
add_index :pages, :url, unique: true
をchangeメソッドの中に追加する。制約があれば、自由に追加する。
マイグレートする。
bundle exec rails db:migrate
Reference
https://qiita.com/devactken/items/df5422d33768a05969eb
データ取り込み
データの用意
データを用意する。今回は、複数のURLを使用する。
cat <<'EOF'> urls.txt
https://yumayx.github.io/
https://github.com/YumaYX
https://yumayx.github.io/YS1/
EOF
bundle exec rails consoleにて、追加していく。
File.foreach("urls.txt") do |line|
url = line.strip
next if url.blank?
page = Page.new(url: url)
unless page.save
puts "NG: #{url} / #{page.errors.full_messages}"
end
end
Page.all
rakeタスクにしてもいい。seedを使うのもどうかな。
データ操作
rails console
簡単な操作を行う場合。bundle exec rails consoleにて、例えばタイトルを全て大文字にする。
Page.find_each do |page|
next unless page.title.present?
page.update(title: page.title.upcase)
end
Reference
Active Record クエリインターフェイス - Railsガイド
タスクで扱う場合
少し複雑な操作、何度も繰り返す場合。URLにアクセスして、タイトルを取得する。
bundle exec rails generate task update_titles
vim lib/tasks/update_titles.rake
# lib/tasks/update_titles.rake
namespace :data do
desc "Update titles from URLs"
task update_titles: :environment do
require 'open-uri'
require 'nokogiri'
Page.find_each do |page|
next unless page.url.present?
begin
html = URI.open(page.url, read_timeout: 5)
doc = Nokogiri::HTML(html)
title = doc.at('title')&.text
if title.present?
page.update(title: title)
puts "Updated: #{page.url}"
else
puts "No title: #{page.url}"
end
rescue => e
puts "Error: #{page.url} (#{e.message})"
end
end
end
end
bundle exec rake data:update_titles
lib/*.rbを使う場合
cat <<'EOF'>lib/method.rb
module Y123
def self.hello
v = "hello"
puts v
v
end
end
# Y123.hello
EOF
cat <<'EOF'>lib/tasks/use_lib_method.rake
require Rails.root.join('lib/method')
namespace :jisaku do
desc "use lib/module method"
task use_lib_mod_method: :environment do
Y123.hello
end
end
EOF
bundle exec rails jisaku:use_lib_mod_method
#=> hello
保存時に加工したい場合(DBに保存される値を変える)
# example
class User < ApplicationRecord
before_save :normalize_name
private
def normalize_name
self.name = name.strip.capitalize
end
end
libのRubyスクリプトから操作する場合
lib/app.rb等のスクリプトを作成する。config/environment.rbをrequire_relativeすると、Modelが使用できるようになる。
require_relative '../config/environment'
page = Page.find_by(url: target_url) # など
# ...
Webフォームでも、コンソールでも更新する場合
※ferrum, nokogiriを使用しているので、以下は、Gemfileの更新が必要。
echo 'gem "ferrum"' >> Gemfile
echo 'gem "nokogiri"' >> Gemfile
bundle install
mkdir -p lib/page_tools
ファイル:lib/page_tools/metadata.rbを作る。
cat<<'EOF'>lib/page_tools/metadata.rb
require_relative '../../config/environment'
require "ferrum"
require "nokogiri"
module PageTools
module Metadata
def self.update(page)
#return unless page.access_flag # すでにアクセス済みならスキップ
browser = Ferrum::Browser.new(timeout: 30)
browser.goto(page.url)
browser.network.wait_for_idle
title = browser.title
html = browser.body
page.update!(
title: title,
content: html,
access_flag: true # 成功したらtrue
)
rescue
# 失敗時は何もしない(access_flagもfalseのまま)
puts "ERROR" * 40
nil
ensure
browser&.quit
end
end
end
EOF
Controllerの修正
vim app/controllers/pages_controller.rb
libを読み込むようにする。
require_relative "../../lib/page_tools/metadata"
createメソッドの中で、save後に以下のメソッド呼び出しを含める。
PageTools::Metadata.update(@page)
コンソールなどでの更新
bundle exec rails c
require_relative "lib/page_tools/metadata"
Page.where(access_flag: false).each do |page|
PageTools::Metadata.update(page)
end
データを見たい
アクセス方法
bundle exec rails server
# rails server -b 0.0.0.0
http://localhost:3000/pagesにアクセスする。ドメイン名は適宜変更する。
見た目(html構成)を変えたい
データを扱いたいだけなので、あんまり見たいということは無いが、デバッグ用に。他に、htmlやmarkdown生成には、役立つかもしれない。
この辺りを変えてあげればいいのかも。
app/views/pages/_form.html.erb
app/views/pages/_page.html.erb
app/views/pages/edit.html.erb
app/views/pages/index.html.erb
app/views/pages/new.html.erb
app/views/pages/show.html.erb
_始まりは、他のビューから呼ばれる部品。<%= render 'page' %>などで使用できる。
データが多い場合は表示を絞ったり、した方がいい。
出力・応用
タスクに登録したり、bundle exec rails consoleで、Rubyでできることは、なんでもできる。
Environments
$ ruby -v
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [x86_64-linux]
$
$ bundle exec rails -v
Rails 8.1.3
$