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.rbrequire_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
$