徒然なるままに

個人の備忘録を中心としたブログです

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
bundle install で、installing libv8 に異常に時間がかかる(五分くらい?)


libv8は、gemパッケージである'therubyracer'が依存するライブラリであり、
therubyracerを使えばRuby上でJavascriptを動かすエンジンが簡単に実装されるとのこと
Ubuntu(13.04)ではlibv8のインストールに時間がかかるからか
デフォルトでコメントアウト
# gem 'therubyracer'
されているのだが、

rails server を使うときに必要となるので、コメントアウトを外してインストールしなければならない。
だが、とにかくlibv8のインストールに時間がかかる。

この問題の原因、解決方法について以下のブログに丁寧にまとめてあった。

therubyracer 0.11.0 問題まとめ - 自称すーじー。

ここの解決方法としてlibv8の古いバージョンは使用できなくなっているようなので、
therubyracerのバージョンを前のものにしてインストールするという方法をとることにする。

gem 'therubyracer', '0.10.2'

しかし現在のtherubyracerの最新版は0.12.0なので、結構古いものになってしまう。。
気になるようじゃ五分程度なら待ってもいいかな
そもそも0.12.0になってるのに、0.11.0の問題が解決していないってのはどういうことやら・・・
スポンサーサイト
(※追記 2013/09/15)
この記事は最低限の覚え書きとあるように、
現時点ではrvmを使ってrubyからインストールする↓の方法を推奨。
また後でrbenvの方がいいとか言いだしそうですが、今はrvm使ってます。
http://kamiyasu2.blog.fc2.com/blog-entry-41.html

Ruby on Railsの環境を構築する際に最低限インストールするのは
ruby本体、Rails、sqlite3の三つ。
このうちrubyは既にインストールされているものとする。

sqlite3本体のインストールが必要となるほか
rails,sqlite3のgem(RubyGems)パッケージをインストールする。
ちなみに、RubyGemsは、Ruby言語用のパッケージ管理システムであり
Debian用のパッケージ管理システムであるaptのようなものと言えば分かりやすいだろうか。

railsのインストール
sudo apt-get install ruby-dev
sudo gem install rails -v 3.2.1 # -v はversionの指定 vオプションを指定しない場合は最新版(現在は4.0)がインストールされる


sqlite3のインストール
sudo apt-get install sqlite3 libsqlite3-dev
sudo gem install sqlite3


なお、gemではrailsの複数のversionを使用できるようだ。
その場合は、railsプロジェクトの作成に使うrails newコマンドに
以下のようにversionの指定を加えてやる必要がある。
rails _3.2.1_ new hoge


さらに、rvmやrbenvというRubyのバージョン管理システムを使えば
Railsのバージョンだけでなく、Rubyのバージョンも複数インストールことができるらしい。
今は取りあえずrails使えればいいやということで、そこまでは触れず最低限の環境構築の覚書きでした。

クラスのインスタンスメソッドは何も指定がない場合、全てpublicメソッドになる。
例外的にinitializeメソッドだけは暗黙的にprivateメソッドになるが、
その他のインスタンスメソッドは可視性(public,private,protected)を指定することができる。

一方、クラスのインスタンス変数の可視性(public,private,protected)は指定できず、暗黙的に全てprivateになる。
これはオブジェクトのカプセル化概念を強固にするためのRubyの仕様である。

publicメソッドは、クラス定義内、クラス定義外(クラスのオブジェクトを生成して使用する側)のどちらからでも呼び出せる。
privateメソッドは、クラス定義内でのみ呼び出すことができ、呼び出すときはprivate_method(任意の引数個数)という関数のような形で呼び出さなければならない。self.private_method()とは書くことができない。
protectedメソッドは、そのクラスの定義内、またはそのクラスの全てのサブクラスの定義内からのみ呼び出すことができる。こちらもprotected_method()という関数のような形式で呼び出さなければならない。



インスタンスメソッドの可視性の指定の方法は二通りある。
1、インスタンスメソッド定義の前に何もつけない(public)、protected、privateを宣言(*注 正確な表現ではない。詳細は後述)する方法。
2、インスタンスメソッドの定義の後、protected、privateにインスタンスメソッド名のシンボルを渡す方法。



1、インスタンスメソッド定義の前に何もつけない(public)、protected、privateを宣言(*注 正確な表現ではない。詳細は後述)する方法

コードを分かりやすく説明するために、protected、privateを「宣言」すると書いたが
これは正しくない。実際のところprotected、privateはObjectクラスのprivateメソッドであり、
後に続く複数のインスタンスメソッドの定義を引数にとるものになっている。


class MyClass
# 事前に何も指定がない場合はpublicメソッド
def public_method
puts "public_method"
end

def X # この@xアクセッサもパブリックメソッド
@x # xはnilである
end

# ここに任意の数のpublicメソッドを定義できる

protected # このprotected宣言(実際はprotectedはObjectのインスタンスメソッド)はprivate宣言まで有効
def protected_method
puts "protected_method"
end

# ここに任意の数のprotectedメソッドを定義できる

private # このprivate宣言(実際はprivateはObjectのインスタンスメソッド)はクラス定義の最後まで有効
def private_method
puts "private_method"
end

#ここに任意の数のprivateメソッドを定義できる

end

c = MyClass.new
c.public_method #=> "public_method"と表示
c.protected_method #=> エラー
c.private_method #=> エラー


2、インスタンスメソッドの定義の後にprotected、privateにインスタンスメソッド名のシンボルを渡す方法。

1で述べたようにprotected、privateの両者はObjectのprivateメソッドであり、これらはメソッド名のシンボルを複数引数に取ることができる。


class MyClass

def public_method
puts "public_method"
end

def X #この@xアクセッサもパブリックメソッド
@x
end

def protected_method
puts "protected_method"
end
protected :protected_method

def private_method
puts "private_method"
end
private :private_method

end




このようにprivate、protectedされたメソッドでもRubyのメタプログラミング機能によりアクセスすることができる。
メタとは「~を越えた」という意味をもつ言葉で、プログラミングを越えた強力な機能であるから使用には注意を要する。
通常は、この機能を使わないようにプログラムを設計するのが正しい。

sendメソッドは任意のオブジェクトのインスタンスメソッド名のシンボルを渡すことで、そのインスタンスメソッドにアクセスすることができる。
instance_evalはブロック内で評価を行うことでインスタンスメソッドにアクセスできる。instance_evalはカプセル化されたインスタンス変数にもアクセスすることができる。

c = MyClass.new
c.send :private_method
c.instance_eval {public_method}
c.instance_eval {@x}

Rubyにおける継承はJavaにおける継承と異なる細かな点がいくつもあったのでまとめる必要があった。
この記事はO'ReillyのRuby本を参考にしている。

まず、基本型。
「Point」をスーパークラスとするサブクラス「Point3D」を宣言する方法は以下の通り。

#super class
class Point
#ここはPointクラスの実装(implementation)
#メソッド(インスタンスメソッド・クラスメソッド),
#変数(インスタンス変数・クラス変数・クラスインスタンス変数),
#定数
#などが入る
end

class Point3D < Point
#ここはPoint3Dクラスの実装(implementation)
end


これからPointで宣言された以下の六つの要素がどのように、Point3Dに継承されるのか(あるいは継承されないのか)を一つずつ見ていく。
1、インスタンスメソッド
2、クラスメソッド
3、インスタンス変数
4、クラス変数
5、クラスインスタンス変数
6、定数



1、インスタンスメソッド

インスタンスメソッドは「全て」継承する。
ここで「全て」と言っているのはinitializeメソッドも含めるということを意味する。
Javaではコンストラクタは継承されず、サブクラスで定義し直す必要があったが、
Rubyではinitializeメソッドを例外とせず、通常のインスタンスメソッドと同じように扱い、そのまま継承される。

class Point
def initialize(x,y)
@x,@y = x,y
end
def to_s
"(#@x,#@y)"
end
end

class Point3D < Point
#何も定義しない
end

p = Point.new(1,1)
q = Point3D.new(2,2)
puts p #=> (1,1)を表示
puts q #=> (2,2)を表示


なお、継承された全てのインスタンスメソッドは上書き(override)できる。
上書きするインスタンスメソッドについて、サブクラスの定義の中でsuperメソッドを使うと、スーパークラスの同名メソッドを呼び出すことができる。
これはinitializeをオーバーライドする時にはほぼ必須のテクニックである。

class Point
def initialize(x,y)
@x,@y = x,y
end
def to_s
"(#@x,#@y)"
end
end

class Point3D < Point
def initialize(x,y,z)
super(x,y)
@z = z
end
def to_s
"(#@x,#@y,#@z)"
end
end

p = Point.new(1,1)
q = Point3D.new(2,2,2)
puts p #=> (1,1)を表示
puts q #=> (2,2,2)を表示




2、クラスメソッド

クラスメソッドは、インスタンスメソッドと同じように継承できる。
上書き(override)もできるし、サブクラスの定義の中でsuperメソッドを使うと、スーパークラスの同名クラスメソッドにアクセスできる点も同様。

しかし、注意しておきたいことがある。
一般にクラスメソッドは「ある一つのクラスに固有のメソッド」と考えるのが自然である。
つまりPointのクラスメソッドsumを呼び出したいなら、Point.sumを使うべきで、
内容が全く同じでも、継承されたPoint3D.sumを使うべきではない(使えたとしても)。
このように、レシーバを明示してクラスメソッドを呼び出すときには、継承されたクラスメソッドには頼らない方が良い。
(ここでいうレシーバというのはPointであり、レシーバを明示したクラスメソッドの呼び出しというのはPoint.sum)
クラスメソッドは、メソッドを定義したクラス(Point.sum)を介して呼び出すべきである。

ではレシーバを明示しないでクラスメソッドを呼び出すときというのはどんな時であろうか。
レシーバを明示しないでクラスメソッドを呼び出すことが可能なのか。
とも思われるかもしれないが、そのような場合があるのだ。
それは、クラスメソッド本体中において、「他の」クラスメソッドを呼び出す場合である。例を見てみよう。

class Point
#単純にクラス名を表示するクラスメソッド
def Point.puts_class
puts self.to_s #selfは省略可。省略された場合も、暗黙のうちにselfから呼び出される。
end
end

class Point3D < Point
end

puts Point.puts_class # => Pointを表示
puts Point3D.puts_class # => Point3Dを表示


ここでPoint.puts_classをPoint3Dは継承し、クラスメソッドPoint3d.puts_classを持つ。
スーパークラスのクラスメソッドPoint.puts_classを呼び出したときのselfは「Point」であり
継承されたクラスメソッドPoint3D.puts_classを呼び出したときのselfは「Point3D」となる。
つまりselfはレシーバによって動的に形を変えるのだ。
継承されるクラスメソッドが、サブクラスで利用される(サブクラスをレシーバとして呼び出す)ことが想定される場合は
このようにselfを使って動的に動作が変わるものであることが大半だろう。

だが例外としてObject.newは、ほとんどのオブジェクトに継承され、そのクラスを介して呼び出される。



3、インスタンス変数

Rubyでは、インスタンス変数は継承されない。
Rubyでは、インスタンス変数は継承されない。
Rubyでは、インスタンス変数は継承されない。
Rubyでは、インスタンス変数は継承されない。
Rubyでは、インスタンス変数は継承されない。
Rubyでは、インスタンス変数は継承されない。
Rubyでは、インスタンス変数は継承されない。
Rubyでは、インスタンス変数は継承されない。

これは私も含めたJavaプログラマへの啓蒙である。
では詳しく説明しよう。

以下のコードではPoint3DはPointのインスタンス変数@x,@yを継承し、
独自インスタンス変数@zを持っているかのように見える。
しかし、それは正しい捉え方ではない。

class Point 
def initialize(x,y)
@x,@y = x,y
end

def to_s
"(#@x,#@y)"
end
end

class Point3D < Point
def initialize(x,y,z)
super(x,y)
@z = z
end

def to_s
"(#@x,#@y,#z)"
end
end


Rubyにおける変数は、代入した時点で初めて変数が作られる。
つまり代入がおこらなければ、変数は存在しないのだ。
Point3D.new(1,1,1)を呼び出すと、Point3Dのインスタンスメソッドinitializeが起動され、
内部でsuper(1,1)を呼び出す。するとPointのインスタンスメソッドinitializeが起動され、
@x,@y = 1,1の代入が行われる。
その後、Point3Dのインスタンスメソッドに戻り@z = 1の代入が行われる。
このようにして、インスタンス変数は作られている。

Point3Dが継承した(実際はさらに上書きした)のはinitializeメソッドだけであり、
initializeのsuperを通して、インスタンス変数@x,@yが作られている。
このことで、あたかもインスタンス変数が継承されたかのように見えるが
実際のインスタンス変数は継承のメカニズムとは全く無関係なのである。

Pointは@x,@yの二つのインスタンス変数しか持たないので、これらは明確だが
内部の実装が分からないクラスを拡張したサブクラスにおいて、インスタンス変数を新たに定義するときは要注意だ。
例えば、Pointが内部のinitializeで@statusを宣言していて、この@statusを元にメソッドの動作が変わるとしよう。
そこで、このPointを継承して拡張しようとするユーザがこの@statusというインスタンス変数があることを知らずに、
自分で独自に使う機能をつけるために@statusという変数を使おうとした場合、initializeメソッドでsuperの後に@statusへの代入文を書くと、
祖先のインスタンス変数を上書きしてしまう。
その場合、祖先から継承したメソッドの動作が変わってしまうということが起こりうる。

このようなことからRubyのサブクラス化はスーパークラスの実相がよくわかっている場合を除いて危険だと考える理由の一つになっている。


4、クラス変数

クラス変数は継承される。
具体的にはクラスとそのすべてのサブクラスによって「共有」されるという形を取る。
これはつまり、サブクラスでクラス変数を変更すると、
スーパークラスの同名クラス変数も変更されてしまう。

class PointA
@@value = 1
def self.value; @@value; end
end
puts PointA.value #=>1を表示
class PointB < PointA; @@value = 2; end #サブクラスが共有クラス変数を変更
puts PointA.value #=>2を表示
class PointC < PointB; @@value = 3; end
puts PointB.value #=>3を表示
end



5、クラスインスタンス変数

クラスインスタンス変数は継承されない。
クラスインスタンス変数は、インスタンス変数と紛らわしいが以下の@a,@bのように宣言する。
class Point
@a = 0
@b = 0
def initialize(x,y)
@x,@y = x,y
end
end


@x,@yがPointクラスのインスタンス(例:Point.new(1,1))のインスタンス変数だとすると
@a,@bはClassクラスのインスタンス(Point)のインスタンス変数である。
Pointをスーパークラスとするクラスが継承するのはPointクラスの実装、いわば設計図であり、
Classクラスの(設計図から作られた)一つのインスタンス(Point)がどのような変数を持っていても何も関係しないのだ。

つまり、そもそもクラスインスタンス変数は継承のメカニズムとは無関係である。


6、定数

定数は継承する。
定数もインスタンスメソッドと同様に、継承され、オーバーライドできる。
が、定数はインスタンスメソッドのように動的ルックアップが行われない。
(動的ルックアップというのは、同名の関数が定義されている場合、一番近くで定義されたものを動的に採用するというもの)
優先されるのは、定数が使われている場所(メソッド)と同じスコープである。
その後、継承階層で探索が行われるが、これは抽象クラスにおける抽象メソッドの呼び出しのようなものになる。

# -*- coding: utf-8 -*-
class Point
def initialize(x,y)
@x = x
@y = y
end

def to_s; "(#@x,#@y)"; end

ORIGIN = Point.new(0,0) #selfを省略

#テストメソッド(内部でgreetingメソッドとORIGIN定数を参照する)
def test
"#{greeting}-#{ORIGIN}"
end
def greeting; "Hello"; end
end

class Point3D < Point
def initialize(x,y,z)
super(x,y)
@z = z
end

def to_s; "(#@x,#@y,#@z)"; end
def greeting; "GoodAfternoon"; end #greetingをオーバーライド
end

puts Point::ORIGIN #=> (0,0)を表示
puts Point3D::ORIGIN #=> (0,0)を表示 *POINT3D::ORIGINはPointクラスのインスタンスである。

Point3D::ORIGIN = Point3D.new(0,0,0)
puts Point::ORIGIN #=> (0,0)を表示 定数は共有されない
puts Point3D::ORIGIN #=> (0,0,0)を表示 *POINT3D::ORIGINはPoint3Dクラスのインスタンスである。

p = Point.new(1,1)
q = Point3D.new(1,1,1)
puts p.test #=> Hello-(0,0)を表示
puts q.test #=> GoodAfternoon-(0,0)を表示



testメソッドの定義はPointクラスにあり、内部でインスタンスメソッドgreetingと定数ORIGINを参照する。
p.testではgreetingもORIGINも、testメソッドと同じスコープから探し出す。
q.testでは動的ルックアップによりPoint3Dのgreetingを採用し、GoodAfternoonを返すが、
定数は、その定数を使用しているメソッド(ここではtest)と同じ場所(Pointクラス)から優先して探し出す。
そこで採用されるのがPoint::ORIGINというわけだ。



以上が、Rubyの継承メカニズムにおける六つの要素の挙動である。

kamiyasu

Author:kamiyasu

QR

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。