Entity Framework を導入するにあたり、まとまった情報が見つからなくて辛い問題
ようやく Entity Framework を導入することに
今までC#の開発では、所々の事情で iBatis というO/Rマッパーを利用していたのですが、ようやくEntity Framework を導入することで話がまとまってきました。
ということで色々と導入に向けて情報を集めている段階なのですが、導入に際して、まとまった情報がなく、苦労しています。
おすすめの本とか知りたい。。
C# って日本語情報が Web 上に少ないような
感覚的な話なので、それは違うよ! という反応もありそうですが、現在の標準とか、流行りとかが Web 上で感じ取りにくい気がします。観測範囲の問題かもしれません。
もっとどのような構成で開発をしているのかというのがわかるようになるといいなあと思ってます。
まあ、一番は英語をもっと勉強して、直接情報を取得しろよ!ってところなのでしょうけれども。。
Entity Framework 関連で参考にしているサイト
以下にあげさせてもらうサイトとかを確認させてもらいながらなんとか進めています。
Entity Framework カテゴリーの記事一覧 - インクリメンタルなカイハツにっき
今後自分が躓いたところを中心に、ブログに書いていきたいと思います。
今回はただの雑文でした。
しばらく更新していなかったので、なんか更新しなきゃ! となったので。
ActiveAdmin にCSV取込機能を追加する際に便利な gem active_admin_import
前提
ruby 2.1.10p492
Rails 4.2.8
active_admin_import
既存システムの修正依頼
前に他の担当者が作成した Rails アプリで、CSVインポートが上手くいかないという相談があり、アプリを確認したところ、管理サイト側が ActiveAdmin を利用していました。
そして、CSVインポート機能として採用されていたのは active_admin_importable という gem でした。
この gem なんですが、最新バージョンの更新日が2013年4月30日で、約4年更新がありません。
枯れているという意味ではいいのかもしれませんが、既存のソースを確認してみるとモンキーパッチを当てている箇所があったり、Shift-JIS の取込で苦労していそうな感じでした。
なので、別の gem を探してみたところ、今回利用した active_admin_import がいい感じでした。
調査時点での最新バージョンの更新日付は2017年4月26日です。
機能紹介
使い方はサンプル含め、以下のサイトに記載があります。
ざっと機能を確認すると、
- 置換(Replacements)
- エンコード処理(Encoding handling)
- CSVオプション設定(CSV options)
- ヘッダー自動付加機能(Ability to prepend CSV headers automatically)
- activerecord-import を利用した一括インポート(Bulk import (activerecord-import))
- コールバック(Callbacks)
- Zipファイル取込(Zip files)
と、なかなか便利な感じです。
使ってみる
gem 'active_admin_import' # ActiveAdmin用CSVインポート機能
上記を Gemfile に記載し、bundle install
します。
この gem は active_admin 用ですので、 app/admin/モデル名.rb
ファイルに追記していくことになります。
以下は User モデルへの追加例です。
# app/admin/user.rb ActiveAdmin.register User do active_admin_import validate: true, batch_transaction: true, template_object: ActiveAdminImport::Model.new( hint: "インポートするCSVファイルにヘッダー行は必要ありません。<br> 文字コードは CP932(Windows-31J) を想定しています。(Excelを元にしたCSVファイルを想定)<br> <br> 以下の順序で設定されているファイルを取り込みます:<br> 'ID', 'パスワード', '姓', '名', '所属'<br> <br> 取込に失敗した場合のエラーは5件分のみ表示しています。", csv_headers: ['user_id', 'password', 'last_name', 'first_name', 'dept'], force_encoding: :'CP932' )
上記のように記載するだけで、ヘッダー行なしのエクセルベースで作成したCSVファイルを取り込むことができるようになりました!
使ってみての利点
こちらの gem のいいところですが、
です。
日本語を扱う上で エンコードは常に問題になりますし、色々と考える必要に迫られます。しかし、特に英語圏で作成された gem はそのような課題にぶつからないのか、けっこう対応できていない gem は多いように感じます。
また、バリデーションに対応しており、バリデートエラーは画面にメッセージが表示されますので、利用者に優しいです。(active_admin_importable は取込がうまく言ったのかどうかがわかりにくかったんですよね。。)
さらに、トランザクション対応していますので、バリデートエラーが発生した場合はすべての取込をロールバックできます。(バリデートエラーが発生したレコード以外は取り込むように設定することもできます。)
日本語で検索するとactive_admin_importable を紹介しているサイトばかりで active_admin_import を紹介しているサイトがなかったので、今まで選択肢に上がってこなかったかもしれませんが、使ってみることをおすすめします。
sqale がサービス終了するので、Heroku に移行した
sqale からの移行推奨先は Heroku
公式にそのような案内がありました。
ただ、正直な話、レイテンシが発生するということ、ドル払いになってしまうことから最初は他のサービスに移行したいと考えていました。
移行先調査した
ということで、移行先を検討してみたんですが、国内サービスは(私の観測範囲では)見当たらなかったんですよね。
で、結局
・AWS
・GCP
・Azure
・Heroku
という形になってしまいました。
この内、
・AWS
はIaaSしかなさそう、
・GCP
はPaaSがあったが、rubyはベータ版?っぽい、
・Azure
はよくわからんかった。
となり、結局Herokuになってしまった。
もう少し調査に時間がかけられたら・移行に時間がかけられたら、というたらればはあるのですが、いかんせんあまり時間ばかりもかけていられなかったので。
Heroku に移行すると大半の人はコストアップになりそう
というのが、Heroku のデータベースに起因してきます。
デフォルトでは、DBの容量がたったの5MB。(ClearDB MySQLの場合)
Sqaleでは2GB使えていた(ハズ)なので、移行を検討している殆どの人が引っか
かるのではないかと。
で、これを1GBにしようとすると、9.99ドル/月が必要になります。
Heroku サーバ自体が、24時間無停止にしようとすると7ドル/月 かかるため、併せて16.99ドル/月 が最低料金になるかと。
Heroku くらいしか同じように使えるところがないからしょうがない・・・
月額が約2倍となってしまい、更に東京リージョンではないためにレイテンシが発生するという全然嬉しくない結果なのですが、他が見当たらないのでしょうがないです。
sqale のサービス終了、痛いです。
移行してみて
で、実際 Heroku に移行してどうなのか、ですが、レイテンシはもちろんありますが、それ以外の部分は結構使いやすいです。
一つつまずいたのが、crontab です。
今までは whenever という gem で crontab の設定を管理していたのですが、Heroku ではcrontab が使えないようです。
代わりに Heroku scheduler というものでバッチ系の処理を管理するみたいです。
Heroku Scheduler | Heroku Dev Center
そのあたりがわかっていなかったので、対応に時間がかかりました。
Rails5 でバッチ処理を利用する際に知らないとハマるかもしれないポイント!
前提
なににハマったのか?
Rails5 で新規アプリを作成していて、バッチ処理を作成しました。
処理は .\lib\tasks\
配下に格納し、 .\config\application.rb
に以下の設定を行いました。
class Application < Rails::Application ・・・ config.autoload_paths += Dir["#{config.root}/lib"] ・・・ end
これで lib ディレクトリ配下は自動的に読み込まれる設定になっていると思っていました。
Rails4 時代はこれでよかったんです。。。
さらに、開発環境では問題なくバッチ処理を呼び出すことができます。
これによって問題の解消がますます困難になりました。。。
Rails5 の本番環境では autoload が無効化された
A Guide for Upgrading Ruby on Rails — Ruby on Rails Guides
上記に記載されていますが、Rails5から本番環境では、autoload が無効になっています。
これは、、、知らないと絶対ハマる。
どのように問題に対応するか
上記のURLには解決策として、.\config\application.rb
に
class Application < Rails::Application ・・・ config.enable_dependency_loading = true ・・・ end
と記載することで、autoload するように変更することがあげられています。
しかし、autoload が無効化されたということは、今後使えなくなることが想定されます。
ですので、今回は eager_load を利用することにしました。 .\config\application.rb
に
class Application < Rails::Application ・・・ config.paths.add 'lib', eager_load: true ・・・ end
と指定し、lib
ディレクトリ配下を eager_load されるように指定することで問題を解消しました。
app
ディレクトリ配下の読み込みはこの eager_load で行われているようなので、これで問題ないはずです。
Rails のルーティングに使用する id を別の内容に変更する方法
通常、edit 等に使用する URI に設定される id を、別のキー等で置き換えたい場合があります。
$ be rake routes Prefix Verb URI Pattern password_resets POST /password_resets(.:format) password_resets#create new_password_reset GET /password_resets/new(.:format) password_resets#new edit_password_reset GET /password_resets/:id/edit(.:format) password_resets#edit password_reset PATCH /password_resets/:id(.:format) password_resets#update PUT /password_resets/:id(.:format) password_resets#update
この id を code に置き換えたい場合、.\config\routes.rb
に以下のようにparam: code
を設定することで、置き換えることができます。
resources :password_resets, param: :employee_code, only: [:new, :create, :edit, :update]
上記のようにすることで、ルーティング情報は以下のようになります。
$ be rake routes Prefix Verb URI Pattern Controller#Action password_resets POST /password_resets(.:format) password_resets#create new_password_reset GET /password_resets/new(.:format) password_resets#new edit_password_reset GET /password_resets/:code/edit(.:format) password_resets#edit password_reset PATCH /password_resets/:code(.:format) password_resets#update PUT /password_resets/:code(.:format) password_resets#update
id とは別のキーを主キーのような形で使う際に重宝します。
Perl でパスワードZipを求められた際の対応方法
前提
Perl は初めて触りました。
対応を求められたサーバの環境は古いです。
$ perl -v This is perl, v5.10.1 (*) built for x86_64-linux-thread-multi
QBK (急にボールが来たので)
他にヘルプできそうな人間がいなかったということで、触ったことのない Perl のソースコード修正を求められました。
内容は、Webフォームに添付されたデータをメールで送信するシステムの添付ファイルをパスワードZipで圧縮すること。
Perl 触ったこともないので、極力他に影響を与えたくない一心で対応しました。
調べてみると、Perl の標準モジュールに「Archive::Zip」というものがあるのですが、これはパスワードZipに対応していませんでした。。。
どうしようかなー、と悩んだのですが、Perl からコマンドが呼び出せるらしいので、コマンドでパスワードZipを作成し、それを添付することにしました。
ということで対応したコードが以下。
#----------------------------------------------------------- # パスワードzip生成 # 引数: ファイル名 # 備考: $cf{upldir} には、ファイルの保管場所ディレクトリパスが入っている #----------------------------------------------------------- sub zip { my $ZIP_CMD = "/usr/bin/zip"; my $ZIP_PASSWD = "************"; my ($origin_name) = @_; my $zip_name = "$origin_name.zip"; my $cmd = "$ZIP_CMD -jP $ZIP_PASSWD $cf{upldir}/$zip_name $cf{upldir}/$origin_name"; # パスワードzipの生成コマンド system($cmd); # コマンドの実行 }
で、これをメール送信処理中の添付ファイルをセットする前に
&zip($fname); # $fname には対象となるファイル名が格納されている
と呼び出して、生成したファイルを添付することで対応しました。
今回はパスワード自体をランダム化する必要が無いという要件だったので、ソースコード内にパスワードを埋め込んでいますが、あまりよろしくないという認識はあります。
また、system関数は便利な半面、セキュリティリスクになりそうなので、使い所を見極めて使う必要がありますね。
ActiveRecord に対する scope は条件に一致するレコードが存在しない場合 .all の結果を返却する
scope を利用していたところ、思わぬところでハマってしまいました。。 指定の条件で検索を行い、返却された結果が nil かどうかで条件分岐させようと思っていたのですが、scope を利用すると常に nil ではなく、.all の結果が返却されてきます。
scope :search_with_user, -> (user_id) { where('user_id = ?', user_id) }
よくよく調べて見たところ、 scope はメソッドチェーンを実現するために、nil を返すことはなく、.all を返すんだとか。
要は scope の利用方法に関する認識不足だったのですが、他の人も同じような勘違いをする可能性もあるかも、と思ったのでブログに残しておきます。