頭の体操[初級] 連続したフィールドの入力で、途中の未入力欄を検知 (Ruby編)
前回の記事の続きです。
入力の途中で空欄があったらエラーメッセージを表示する機能を実装します。
実装してみよう
サンプルとして、以下のようなメソッドを作成します。
- 引数にStringの配列を受け取る
- 配列の途中に空文字があり、その欄より後に空文字でない要素がある場合は、エラーメッセージを表示する。メッセージ中には何番目の要素がエラーかを表示する。
こんな動作イメージです
arr = ["佐藤", "鈴木", "高橋", "", "伊藤", "", "", "", "", ""] detect_empty_field arr # OUTPUT # 4番目のフィールドが空です。値を入力してください。
簡単のために、以下を前提とします。
この記事ではRubyで実装します。
実装は続きを読むをどうぞ
続きを読む頭の体操[初級] 連続したフィールドの入力で、途中の未入力欄を検知 (Java編)
以前に、仕事で以下のような挙動をする処理を書く必要がありました(正確には、後輩が実装して自分はレビュー担当)。連続して値を入れるような入力欄で、途中で入力が空になっている場所を検知します。
実装してみよう
サンプルとして、以下のようなメソッドを作成します。
- 引数にStringの配列を受け取る
- 配列の途中に空文字があり、その欄より後に空文字でない要素がある場合は、エラーメッセージを表示する。メッセージ中には何番目の要素がエラーかを表示する。
こんな動作イメージです
detectEmptyField02(new String[]{"佐藤", "鈴木", "高橋", "", "伊藤", "", "", "", "", ""}); // OUTPUT // 4番目のフィールドが空です。値を入力してください。
簡単のために、以下を前提とします。
- 引数で渡される配列はnullではない
- 配列中の各要素はnullでない
自分の勉強のためにも、JavaとRuby両方で実装してみます。この記事ではJavaで実装します。
実装は続きを読むをどうぞ
続きを読むRubyでクラス宣言直下に書かれたメソッドの実行時期
Railsのスコープ定義文を見ていたら、クラス宣言直下に書かれたメソッドはいつ実行されるのかという疑問がわいてきたので調べました。
Rubyはクラス宣言直下に任意の式が書ける
railsのコードでは、クラス宣言直下でscopeメソッドを呼び出しています。
class Book < ActiveRecord::Base scope :costly, -> { where("price > ?", 3000) } end
"初めてのRuby (Yugui 著)"には、
クラス定義の内には任意の式を書くことができます
と書いてあります。
クラス宣言直下に書かれたメソッドはいつ実行されるのか
クラス定義直下に書かれたメソッドがいつ実行されるのか、コードを書いて確かめます。
class CallUnderClassDefTest puts "from under class def 01" def method_01 puts "from method_01" end puts "from under class def 02" end puts "out of class 01" CallUnderClassDefTest.new.method_01 puts "out of class 02"
実行結果
from undfer class def 01
from under class def 02
out of class 01
from method_01
out of class 02
ファイルの先頭から処理が実行されているようです。 クラスの宣言が終わっていないのにメソッドが実行できるというのは不思議ですが、これもスクリプト言語の特徴なのでしょう。
Javaのstaticブロック
Javaはコンパイル言語なので同様の機能は無いのですが、staticブロック内には任意の式を書くことができます。使用用途としては、クラス変数の初期化などです。以下のコードで実行順序を確認してみます。
public class SomeClass { private static List<String> fruitList = new ArrayList<String>(); static { System.out.println("||static block start||"); fruitList.add("apple"); fruitList.add("banana"); fruitList.add("coconut"); System.out.println("||static block end||"); } public static List<String> getFruitList() { return fruitList; } } public class StaticBlockTest { public static void main(String[] args) { System.out.println("start main"); List<String> fruits = SomeClass.getFruitList(); System.out.println("==== fruits ===="); for (String fruit : fruits) { System.out.println(fruit); } System.out.println("================"); System.out.println("end main"); } }
実行結果
start main
||static block start||
||static block end||
==== fruits ====
apple
banana
coconut
================
end main
mainメソッドが呼ばれたあとにSomeClassのstaticブロックが実行されています。
Rubyを習い始めた時は、Rubyのクラス宣言直下に書かれた処理は、Javaのようにクラスが呼び出された際に実行されると思っていたので、今回確かめることができ、勉強になりました。
初めてのRuby |
"初めてのRuby" 本の感想とRubyの印象
Ruby on Railsを勉強する前に、"初めてのRuby"を読みました。本の感想とRubyの印象を書きます。
自身のプログラミング言語経験
本の内容が専門的なので、参考に自身のプログラム経験を書いておきます。
- 業務で主に使用している言語
- 業務で使ったことのある言語
- Perl、シェルスクリプト、javascript (バッチ書いたり、社内ツールを作るときに使います)
- 授業や研修で習った言語
本の感想
他の言語を知っている人向け
本の"はじめに"に書いてある通り、プログラム経験者向けです。特にオブジェクト指向の経験は必須で、"クラス" "インスタンス"などの用語が説明なしに出てきます。
オブジェクト指向でプログラミング経験があれば、自分の知っている言語へ用語を対応づけることで理解できると思います。
個人的には、関数型言語は未習得なので、関数型由来と思われる関数 (select等)は理解するのに時間がかかりました。
説明はあっさり、簡潔
他言語で経験のあるプログラマ向けなので、全体的に、Rubyでコレはこう書きます、といった書き方が多く、冗長な表現はありません。
ページ数が少ないので一気に読める
Rubyがどんな言語かさらっと知りたい場合にはおすすめです。ページ数が205ページと、プログラミング専門書の中では薄いほうなので、Rubyの概要を知るにはよい本です。
本の中でよくわからなかったこと
ブロック付き構文
javaだと関数をそのまま渡すことはできないのでブロック構文の理解に戸惑いました。 Rubyを勉強していくうちに、javascriptのコールバックと同じようなものだと理解しました。
シンボル
文字列とは違うのか、どのようなときに使うのかがいまいち分かりづらかった。
Rubyの印象
perlっぽい
一番強く思ったのがRubyはperlっぽいということです。 本の中でも、
と書かれています。 似ている点は以下
perlよりもよいと思った点は以下
かゆいとことに手が届いている
比較的最近できた言語というだけあって、他の言語を使用していてちょっと不便だなと感じるていたことが、Rubyでは簡単に行えるようになっています。
- 使い終わったファイルを自動的に閉じてくれる機能が標準でサポートされている
- 負の添字で配列を後方から参照できる
- GCを自動で行ってくれる
サーバサイドも書けるしバッチも書ける
普段はJavaでサーバサイドプログラムを書き、たまにperlでバッチを書いているのですが、Rubyならサーバサイドでもバッチ処理でも使えて習得にお得感があります。
個人的な感想
長らくプログラミング言語を新たに習得するということをしていなかったので、そもそも本書を理解できるか不安でしたが、半分以上は理解できたと思います。手続き型言語なので、順次・分岐・繰り返し の構文、オブジェクト指向なのでクラス・メソッド・各種変数があるのはどの言語でも同じだと認識しました。
ただ、JavaとRubyでは、equalsと==の意味が逆なので絶対間違えると思う。
本書を読んだ副作用として、思いがけずjavascriptの理解を深めることができました。クロージャという言葉を知れたのが大きいです。javascriptは系統立てて学んでいないので、本を読んで理解することは大切だと痛感しました。
初めてのRuby |
Rubyでの動的なクラスメソッドの追加方法
メソッドの実行でクラスメソッドが追加される
以下はrailsのscope定義文です。このように書くと、Book.costlyでレコードが取得できます。 メソッド呼び出しでメソッドが動的に追加されます。 メソッドの引数が新しいメソッドのメソッド名になるのです。不思議!Javaではこのようなことはできません。
class Book < ActiveRecord::Base scope :costly, -> { where("price > ?", 3000) } end
どのような方法でメソッドを追加できるのでしょうか。
singleton_class とdefine_methodでクラスメソッドを追加する
Rubyでは動的なメソッド追加は割と普通に行われるようで、define_methodで行うことができます。 クラスメソッドの追加は特異クラスへのメソッド追加として行うことができます。
class Sample def self.my_define_method(name) singleton_class.send(:define_method, name, -> { puts "This method name is '#{name}'" }) end # クラス内でクラスメソッドを追加 my_define_method :sample_method end # クラス外からクラスメソッドを追加 Sample.my_define_method :sample_method2 # 追加したクラスメソッドを実行 Sample.sample_method Sample.sample_method2
これを実行すると、以下のように表示され、メソッドの追加に成功したことが分かります
$ ruby sample_01.rb This method name is 'sample_method' This method name is 'sample_method2'
moduleをextendする方法
上記の方法で動的にクラスメソッドを追加することができました。 Ruby on Railsのscopeのコード*1を見ると、Moduleのコードを呼んでいるので、同じような実装を行ってみます。
module ParentModule def my_define_method(name) singleton_class.send(:define_method, name, -> { puts "This method name is '#{name}'" }) end end class Sample extend ParentModule my_define_method :sample_method end Sample.sample_method
実行します。
$ ruby sample_02.rb This method name is 'sample_method'
メソッドの追加ができました。Railsのコードに近い形になりました。
クラス定義内でモジュールをextendすることで、モジュールのメソッドを特異メソッド(クラスメソッド)として取り込むことができます。
うまく行かなかった方法たち
define_methodをsingleton_classをレシーバとして呼び出した場合
priveteなメソッドと呼ぶなと怒られます。
class Sample def self.my_define_method(name) # エラー。priveteメソッドは呼び出せない singleton_class.define_method name, -> { puts "This metho name is #{name}" } # 正しくは下記 # singleton_class.send(:define_method, name, -> { puts "This method name is '#{name}'" }) end end # クラスメソッドを追加 Sample.my_define_method :sample_method # 追加したクラスメソッドを実行 Sample.sample_method
$ ruby sample_01_bad.rb sample_01.rb:3:in `my_define_method': private method `define_method' called for #<Class:Sample> (NoMethodError)
singleton_class(オブジェクトの特異クラス)のdefine_methodはプライベートメソッドなので外部から呼び出せません。
ただし、sendメソッドを使用することでprivateメソッドを(強制的に)呼び出すことができます。
define_methodをインスタンスメソッドから実行した場合
インスタンスメソッドとして定義した場合は、そのインスタンスの特異メソッドとして定義され、クラスメソッドにはなりません。
class Sample def my_define_method(name) singleton_class.send(:define_method, name, -> { puts "This method name is '#{name}'" }) end end instance = Sample.new # インスタンスのメソッドを追加 instance.my_define_method :sample_method # インスタンスメソッドとして実行 instance.sample_method # エラー。クラスメソッドとしては定義されていない Sample.sample_method
$ ruby sample_01.rb This method name is 'sample_method' sample_01.rb:14:in `<main>': undefined method `sample_method' for Sample:Class (NoMethodError)
インスタンスの特異クラス(singleton_class)にメソッドを追加したため、インスタンスにメソッドが追加されました。
後記
define_methodの実装も気になりましたが、今回はこの辺で。
環境
- OS : Mac OS X バージョン 10.8.4
- Ruby バージョン 2.3.1
- Ruby on Rails バージョン 4.2.6
Ruby初心者 AcriveRecordのscope定義式の意味を理解する
Ruby on Railsの環境構築が終わったので、"パーフェクト Ruby on Rails"に載っていサンプルアプリを作っていきます。
プロジェクトの作成とモデルの作成
rails new プロジェクト名
で新しいプロジェクトを作成します。本のサンプルに従って、プロジェクト名は book_admin としました。
$ rails new book_admin
モデルの生成には rails g model コマンドを使用します。カラム名とデータ型の対は複数指定できます
rails g model モデル名 カラム名 : データ型 カラム名 : データ型 ...
$ ./bin/rails g model Book name:string published_on:date price:integer number_ofpage:integer
scope定義の文
scopeを定義することでwhere文に名前をつけて管理することができます。 値段(price)が3000以上のデータを抽出するスコープを定義します。
class Book < ActiveRecord::Base scope :costly, -> { where("price > ?", 3000) } end
Java エンジニア、悩む
本に従ってこのコードを書いてみたのですが、文の意味がわかりません。どのような要素で成り立っているのか、costlyの最初のコロン(:)はシンボルを表すらしいけれどシンボルとは何なのか、カンマは何なのか矢印(->)は何を意味するのか。
ひとつひとつ解読していく
クラス宣言
まずはクラス宣言の部分をみていきます
class Book < ActiveRecord::Base # 略 end
Bookクラスを宣言しています。 "< ActiveRecord::Base" はActiveRecord::Baseクラスの拡張していることを意味します。 ActiveRecord::BaseはActiveRecordモジュールのBaseクラスを表します。
スコープ定義文
scope :costly, -> {where("price > ?", 3000) }
はじめ、この文を見たときに、矢印(->)で文が区切られているのかと思って悩みました("scope :costly," と "{where(..)}" で分かれるのかと思った)
実際はscopeメソッドとその引数なので、対応関係は以下のようになります
役割 | 値 |
---|---|
メソッド名 | scope |
第1引数 | :costly |
第2引数 | -> { where("price > ?", 3000) } |
第1引数はシンボルです。シンボルについてまだよく理解できてはいませんが、以下の記事が役に立ちました。シンボルは"名前に対応した整数"だそうです。なので、引数に渡したシンボルで新たなメソッドを定義できるというわけです。(上記の例だとBook.costlyで第2引数に渡したブロックが実行される)
第2引数は手続きオブジェクトです。矢印とカッコ(-> {...})はlambda記法です。lambda { where("price > ?", 3000) } と書いても同じです。
whereメソッドとSQLインジェクション
where()はSQLのwhere条件を追加するためのメソッドです。"price > ?" の クエスチョンマークはプレースホルダーと呼ばれ、第2引数以降の値が割り当てられます。where("price > ?", 3000) と where("price > 3000") はほぼ同じ内容を表します(完全に同じではない)。
where("price > 3000") と書かずに、プレースホルダーを使ってwhere("price > ?", 3000) と書くのは、SQLインジェクションという攻撃を無効にするためです。今回、第2引数は数値でしたが、通常はユーザの任意の入力をSQL文として組み立てることになります。ユーザの入力をSQL文として組み立てるとき、開発者の意図しないSQL文が組み立てられることがあります(ユーザ入力にSQLのキーワードが含まれる場合)。プレースホルダーを使うことで、この脆弱性をなくすことができます。
SQLインジェクションに対する脆弱性を作らないようにするため、検索条件には必ずプレースホルダーを使わなければなりません。
パーフェクトRuby on Rails |
環境
- OS : Mac OS X バージョン 10.8.4
- Ruby バージョン 2.3.1
- Ruby on Rails バージョン 4.2.6
Railsのインストール
Rubyのバージョンアップ、bundlerのインストールも終わったのでRailsのインストールをします。
インストールするRailsのバージョン決定
参考にしている" パーフェクトRuby on Rails" では、Rubyのバージョン2.1.2とRailsバージョン4.1.1を利用していますがなるべく新しいバージョン使っておいた方が後が楽な気がするので参考書とは別バージョンで開発を進めます。
Ruby gems のページを見ると、最新の安定板が4.2.6、rc版が5.0.0.rc1となっています(2016年5月27日現在)。 バージョン5も気になるところですが、今回は4.2.6をインストールします。
All versions of rails | RubyGems.org | your community gem host
Railsのインストール
gemコマンドでインストールします。関連するライブラリが多いので13分かかりました。
$ gem install rails -v 4.2.6 Fetching: rack-1.6.4.gem (100%) Successfully installed rack-1.6.4 Fetching: concurrent-ruby-1.0.2.gem (100%) Successfully installed concurrent-ruby-1.0.2 # 中略 Installing ri documentation for mail-2.6.4 Parsing documentation for actionmailer-4.2.6 Installing ri documentation for actionmailer-4.2.6 Parsing documentation for rails-4.2.6 Installing ri documentation for rails-4.2.6 Done installing documentation for rack, concurrent-ruby, sprockets, thread_safe, tzinfo, i18n, activesupport, mini_portile2, nokogiri, loofah, rails-html-sanitizer, rails-deprecated_sanitizer, rails-dom-testing, rack-test, erubis, builder, actionview, actionpack, sprockets-rails, thor, railties, arel, activemodel, activerecord, globalid, activejob, mime-types-data, mime-types, mail, actionmailer, rails after 778 seconds 31 gems installed
パーフェクトRuby on Rails |
環境
関連記事
全出力
gem install rails -v 4.2.6 したときの全出力を記録のために残しておきます
続きを読む