Ruby on Railsに組み込まれているモデル操作用(DB操作用)のライブラリといえばActiveRecordですが、これをRailsを使わずにRubyとActiveRecord単体の組み合わせで使ってみたいと思います。
phpMyAdminでダミーのデータベースを作成する。
早速ダミーのデータベースとテーブル作成でつまづいた。よく考えると、チュートリアルや本の写経でテーブル作成したことはあったが、自分で一から作るのは初めてでした。
なので、テーブル作成の時に以下のような初歩的っぽいエラーがたくさん出ました(T_T)
- エラー#1063:「id」にauto incrementをつけた状態では、型をINT系にしなければいけない。(最初VARCHARにしてた。) <参考>
- エラー#1293:テーブル内でデフォルト値がCURRENT_TIMESTAMPにできるのは1つだけらしい。createdとmodifed両方にCURRENT_TIMESTAMPを付けていたのを、createdのみにした。<参考>
作成したダミーのDBはこんな感じ。一応、あとからスクレイピングしたデータを挿入できるような構造にしてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
mysql> show tables; +------------------------------+ | Tables_in_learn_activerecord | +------------------------------+ | scraping_data | +------------------------------+ 1 row in set (0.00 sec) mysql> DESC scraping_data; +-------------+--------------+------+-----+-------------------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+--------------+------+-----+-------------------+----------------+ | id | int(10) | NO | PRI | NULL | auto_increment | | title | text | YES | | NULL | | | image_url | varchar(255) | YES | | NULL | | | article_url | varchar(255) | YES | | NULL | | | issue-date | datetime | YES | | NULL | | | created | timestamp | YES | | CURRENT_TIMESTAMP | | | modified | timestamp | YES | | NULL | | +-------------+--------------+------+-----+-------------------+----------------+ 7 rows in set (0.00 sec) mysql> select * from scraping_data; +----+-------------+-------------------+-------------------+---------------------+---------------------+----------+ | id | title | image_url | article_url | issue-date | created | modified | +----+-------------+-------------------+-------------------+---------------------+---------------------+----------+ | 1 | sample text | /images/img01.jpg | /article/sample01 | 2015-09-29 09:24:22 | 2015-10-01 14:20:20 | NULL | +----+-------------+-------------------+-------------------+---------------------+---------------------+----------+ 1 row in set (0.00 sec) |
ActiveRecordを使うための環境構築
まずは環境構築。ActiveRecordはRailsの中で使われていたりするが、Ruby単体でも使用することができます。その場合、gemを使ってActiveRecordをインストールする必要がある。それと、mysqlに接続するためのアダプタもgemインストールが必要とのこと。とりあえずActiveRecordを単体で使うにはというそのものズバリのサイトを参考にしました。以下のようにGemfileに’activerecord’と’mysql2’を追記して、
Gemfile
1 2 3 4 5 6 7 8 |
# A sample Gemfile source "https://rubygems.org" # gem "rails" gem 'nokogiri' gem 'activerecord' gem 'mysql2' |
bundleでインストールすると
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
MyMBA:scraping_traning meguroman$ rbenv exec bundle install Fetching gem metadata from https://rubygems.org/......... Fetching version metadata from https://rubygems.org/.. Resolving dependencies... Installing i18n 0.7.0 Installing json 1.8.3 with native extensions Installing minitest 5.8.1 Installing thread_safe 0.3.5 Installing tzinfo 1.2.2 Installing activesupport 4.2.4 Installing builder 3.2.2 Installing activemodel 4.2.4 Installing arel 6.0.3 Installing activerecord 4.2.4 Using mini_portile 0.6.2 Installing mysql2 0.4.1 with native extensions Gem::Ext::BuildError: ERROR: Failed to build gem native extension. /Users/meguroman/.rbenv/versions/2.1.4/bin/ruby extconf.rb checking for ruby/thread.h... yes checking for rb_thread_call_without_gvl() in ruby/thread.h... yes checking for rb_thread_blocking_region()... yes checking for rb_wait_for_single_fd()... yes checking for rb_hash_dup()... yes checking for rb_intern3()... yes checking for mysql_query() in -lmysqlclient... no ----- mysql client is missing. You may need to 'brew install mysql' or 'port install mysql', and try again. ----- *** extconf.rb failed *** Could not create Makefile due to some reason, probably lack of necessary libraries and/or headers. Check the mkmf.log file for more details. You may need configuration options. Provided configuration options: --with-opt-dir --without-opt-dir --with-opt-include --without-opt-include=${opt-dir}/include --with-opt-lib --without-opt-lib=${opt-dir}/lib --with-make-prog --without-make-prog --srcdir=. --curdir --ruby=/Users/meguroman/.rbenv/versions/2.1.4/bin/ruby --with-mysql-dir --without-mysql-dir --with-mysql-include --without-mysql-include=${mysql-dir}/include --with-mysql-lib --without-mysql-lib=${mysql-dir}/lib --with-mysql-config --without-mysql-config --with-mysql-dir --without-mysql-dir --with-mysql-include --without-mysql-include=${mysql-dir}/include --with-mysql-lib --without-mysql-lib=${mysql-dir}/lib --with-mysqlclientlib --without-mysqlclientlib extconf failed, exit code 1 Gem files will remain installed in /Users/meguroman/GoogleDrive/WebSite/localServer/Training/rooter_training/scraping_traning/vendor/bundle/ruby/2.1.0/gems/mysql2-0.4.1 for inspection. Results logged to /Users/meguroman/GoogleDrive/WebSite/localServer/Training/rooter_training/scraping_traning/vendor/bundle/ruby/2.1.0/extensions/x86_64-darwin-14/2.1.0-static/mysql2-0.4.1/gem_make.out An error occurred while installing mysql2 (0.4.1), and Bundler cannot continue. Make sure that `gem install mysql2 -v '0.4.1'` succeeds before bundling. |
mysql2のインストールのところで滝のようなエラーメッセージが表示されました。怖すぎる(T_T)
1 2 |
mysql client is missing. You may need to 'brew install mysql' or 'port install mysql', and try again. |
とのありがたい教えが含まれていたので、どうもmysqlが入って無いじゃんということのようです。そうか、今までMAMPのMySQLに頼りっぱなしで、自家製のMySQLはインストールすらしていませんでした。
良い機会ですし、MAMPを使ってインストールするのは結構たいへんそうなので、brewでローカルのMySQLをインストールしてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
MyMBA:scraping_traning meguroman$ brew install mysql ==> Downloading https://homebrew.bintray.com/bottles/mysql-5.6.26.yosemite.bottle.1.tar.gz ######################################################################## 100.0% ==> Pouring mysql-5.6.26.yosemite.bottle.1.tar.gz ==> /usr/local/Cellar/mysql/5.6.26/bin/mysql_install_db --verbose --user=meguroman --basedir=/usr/local/Cellar/mysql/5.6.26 --datadir=/us ==> Caveats A "/etc/my.cnf" from another install may interfere with a Homebrew-built server starting up correctly. To connect: mysql -uroot To have launchd start mysql at login: ln -sfv /usr/local/opt/mysql/*.plist ~/Library/LaunchAgents Then to load mysql now: launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist Or, if you don't want/need launchctl, you can just run: mysql.server start ==> Summary 🍺 /usr/local/Cellar/mysql/5.6.26: 9854 files, 338M |
これでMySQLが入りました。338MB!結構時間がかかったわけだ。。再度、bundle installを実行します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
MyMBA:scraping_traning meguroman$ rbenv exec bundle install Fetching gem metadata from https://rubygems.org/......... Fetching version metadata from https://rubygems.org/.. Resolving dependencies... Using i18n 0.7.0 Using json 1.8.3 Using minitest 5.8.1 Using thread_safe 0.3.5 Using tzinfo 1.2.2 Using activesupport 4.2.4 Using builder 3.2.2 Using activemodel 4.2.4 Using arel 6.0.3 Using activerecord 4.2.4 Using mini_portile 0.6.2 Installing mysql2 0.4.1 with native extensions Using nokogiri 1.6.6.2 Using bundler 1.10.6 Bundle complete! 3 Gemfile dependencies, 14 gems now installed. Bundled gems are installed into ./vendor/bundle. |
mysql2のインストールができました!
ちなみにローカルのMySQLの起動方法、ログイン方法は、blewでMySQLした際に表示されますが、以下の手順になります。手順ではmysql -urootで行けるはずなのですが、エラーが出てしまいました。が、sudoで実行してみるとログインすることができました。うーん。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
MyMBA:~ meguroman$ mysql.server start Starting MySQL .. SUCCESS! MyMBA:~ meguroman$ mysql -uroot ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/Applications/MAMP/tmp/mysql/mysql.sock' (2) MyMBA:~ meguroman$ sudo mysql -uroot Password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 1 Server version: 5.6.26 Homebrew Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> |
これでMAMPではなくローカルのMySQLを読みに行くようになったので、MAMPで作ったテスト用のDBとテーブルを、ローカルの新MySQLの方に移してきます。移行方法はDBをダンプ・リストアする方法もありますが、せっかくの勉強なので、手打ちで移行するのもよいかなと思いました。
phpMyAdminのGUIでテーブル作成しましたが、実はGUI操作しても内部で実行されたSQL文を確認することができます。phpMyAdminのこちらのページにアクセスして、「SQL履歴」というところを確認してください。
これでActiveRecordが使えるようになっているはずです。
ActiveRecordを簡単なサンプルで試す
ActiveRecordを使ったプログラミング方法は、先ほどのActiveRecordを単体で使うにはというサイトのサンプルを使って動かしてみます。いろいろトラブリましたが(後述します)最終的には以下のようにCRUD(登録・読み込み・更新・削除)の動作確認することができました。ActiveRecordの使い方はここを参考にしました。
(テーブルは超簡単な別のテーブルを作って、そちらで動作確認しています。)
テーブル構造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
mysql> DESC personal -> ; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | id | int(11) | YES | | NULL | | | name | varchar(20) | YES | | NULL | | +-------+-------------+------+-----+---------+-------+ 2 rows in set (0.01 sec) mysql> select * from personal; +------+---------+ | id | name | +------+---------+ | 1 | takeshi | | 2 | yoshio | +------+---------+ 2 rows in set (0.00 sec) |
ActiveRecordを使ったプログラム
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# -*- encoding: utf-8 -*- require "rubygems" require "active_record" # DB接続設定 ActiveRecord::Base.establish_connection( adapter: "mysql2", host: "localhost", username: "root", password: "", database: "test", ) # テーブルにアクセスするためのクラスを宣言 class User < ActiveRecord::Base self.table_name = 'personal' end # テーブル初期状態取得 print "====テーブル初期状態====\n" p User.all print "========================\n\n" # レコード挿入 user = User.new(id: 3, name: :masao) if user.save print "data insert done!!\n\n" else p "data insert error occured!!" end # レコード挿入後結果確認 print "====テーブル状態(id=3 ) 挿入後====\n" p User.all print "========================\n\n" # レコード更新 user = User.find_by_id(3) if user.update_attribute(:name, "MASAO") print "data update done!!\n\n" else p "data update error occured!!" end print "====テーブル状態更新(id=3のnameをmasao->MASAO ) 後====\n" p User.all print "========================\n\n" # レコード削除 if user.destroy print "data delete done!!\n\n" else p "data delete error occured!!" end # レコード削除後結果確認 print "====テーブル状態削除(id=3を削除 ) 後====\n" p User.all print "========================\n\n" |
実行結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
MyMBA:scraping_traning meguroman$ bundle exec ruby get_db_data.rb ====テーブル初期状態==== #<ActiveRecord::Relation [#<User id: 1, name: "takeshi">, #<User id: 2, name: "YOSHIO">]> ======================== data insert done!! ====テーブル状態(id=3 ) 挿入後==== #<ActiveRecord::Relation [#<User id: 1, name: "takeshi">, #<User id: 2, name: "YOSHIO">, #<User id: 3, name: "masao">]> ======================== data update done!! ====テーブル状態更新(id=3のnameをmasao->MASAO ) 後==== #<ActiveRecord::Relation [#<User id: 1, name: "takeshi">, #<User id: 2, name: "YOSHIO">, #<User id: 3, name: "MASAO">]> ======================== data delete done!! ====テーブル状態削除(id=3を削除 ) 後==== #<ActiveRecord::Relation [#<User id: 1, name: "takeshi">, #<User id: 2, name: "YOSHIO">]> ======================== |
単体でActiveRecord実行する時のトラブルシューティング
ActiveRecordを使ったコードを書き上げて、普通に動くかと思いきや、以下のようなエラーが出ることがあります。箇条書きしますので、参考になればと思います。
mysql2のバージョンが最新指定だと、GemのLoadErrorが出る
1 2 3 4 5 6 |
MyMBA:scraping_traning meguroman$ bundle exec ruby get_db_data.rb /Users/meguroman/GoogleDrive/WebSite/localServer/Training/rooter_training/scraping_traning/vendor/bundle/ruby/2.1.0/gems/activerecord-4.2.4/lib/active_record/connection_adapters/connection_specification.rb:177:in `rescue in spec': Specified 'mysql2' for database adapter, but the gem is not loaded. Add `gem 'mysql2'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord). (Gem::LoadError) from /Users/meguroman/GoogleDrive/WebSite/localServer/Training/rooter_training/scraping_traning/vendor/bundle/ruby/2.1.0/gems/activerecord-4.2.4/lib/active_record/connection_adapters/connection_specification.rb:174:in `spec' from /Users/meguroman/GoogleDrive/WebSite/localServer/Training/rooter_training/scraping_traning/vendor/bundle/ruby/2.1.0/gems/activerecord-4.2.4/lib/active_record/connection_handling.rb:50:in `establish_connection' from get_db_data.rb:6:in `<main>' |
原因はよくわからないですが、応急的な対応方法としてはmysql2アダプターのバージョンを最新版から0.3.20まで落とすという方法があります。以下のようにGemfileを書き換え、再度bundle installしてください。
1 2 3 4 5 6 7 8 |
# A sample Gemfile source "https://rubygems.org" # gem "rails" gem 'nokogiri' gem 'activerecord' gem 'mysql2', '~> 0.3.20' |
プログラムのdatabase指定が間違っている場合のエラー
1 2 3 4 |
yMBA:scraping_traning meguroman$ bundle exec ruby get_db_data.rb /Users/meguroman/GoogleDrive/WebSite/localServer/Training/rooter_training/scraping_traning/vendor/bundle/ruby/2.1.0/gems/activerecord-4.2.4/lib/active_record/connection_adapters/abstract_mysql_adapter.rb:305:in `query': Mysql2::Error: No database selected: SELECT `scraping_data`.* FROM `scraping_data` (ActiveRecord::StatementInvalid) from /Users/meguroman/GoogleDrive/WebSite/localServer/Training/rooter_training/scraping_traning/vendor/bundle/ruby/2.1.0/gems/activerecord-4.2.4/lib/active_record/connection_adapters/abstract_mysql_adapter.rb:305:in `block in execute' |
プログラムのTable指定が間違っている場合のエラー
1 2 3 4 |
MyMBA:scraping_traning meguroman$ bundle exec ruby get_db_data.rb /Users/meguroman/GoogleDrive/WebSite/localServer/Training/rooter_training/scraping_traning/vendor/bundle/ruby/2.1.0/gems/activerecord-4.2.4/lib/active_record/connection_adapters/abstract_mysql_adapter.rb:305:in `query': Mysql2::Error: Table 'test.scraping_data' doesn't exist: SELECT `scraping_data`.* FROM `scraping_data` (ActiveRecord::StatementInvalid) from /Users/meguroman/GoogleDrive/WebSite/localServer/Training/rooter_training/scraping_traning/vendor/bundle/ruby/2.1.0/gems/activerecord-4.2.4/lib/active_record/connection_adapters/abstract_mysql_adapter.rb:305:in `block in execute' |
ActiveRecordで指定するデータベースには、主キーが必要
主キーを入れないDBにActiveRecordで接続する場合、以下のようにNoMethodErrorのエラーが出ます。
1 2 3 4 5 6 7 8 9 10 11 |
yMBA:scraping_traning meguroman$ bundle exec ruby get_db_data.rb 1 #<User id: nil, name: "yoshio"> /Users/meguroman/GoogleDrive/WebSite/localServer/Training/rooter_training/scraping_traning/vendor/bundle/ruby/2.1.0/gems/activerecord-4.2.4/lib/active_record/sanitization.rb:59:in `block in expand_hash_conditions_for_aggregates': undefined method `to_sym' for nil:NilClass (NoMethodError) from /Users/meguroman/GoogleDrive/WebSite/localServer/Training/rooter_training/scraping_traning/vendor/bundle/ruby/2.1.0/gems/activerecord-4.2.4/lib/active_record/sanitization.rb:58:in `each' ...中略... rooter_training/scraping_traning/vendor/bundle/ruby/2.1.0/gems/activerecord-4.2.4/lib/active_record/persistence.rb:241:in `update_attribute' from get_db_data.rb:35:in `<main>' |
対応方法は主キーを追加することでした。SQLでテーブルに主キーを追加する方法はこちらです。