ようへいの日々精進XP

よかろうもん

2018 年 08 月 03 日 (金)

ジョギング

  • 香椎浜 x 2 周
  • なんか疲れが溜まっている感じで全然体が動かず...

日課

  • (腕立て x 50 + 腹筋 x 50) x 3

リモート勉強会

参加者全員がリモート参加の勉強会に参加した.

めっちゃ, 楽しかった.

今日のるびぃ ~ 先人達の知恵と教訓 (2) ~

irb に動作確認環境は以下の通り.

$ ruby --version
ruby 2.1.10p492 (2016-04-01 revision 54464) [x86_64-linux]
$ irb --version
irb 0.9.6(09/06/30)

今回は, こちらこちら を参考にさせて頂いた.

superclass と class について

irb(main):002:0> Class.ancestors
=> [Class, Module, Object, Kernel, BasicObject]
irb(main):001:0> class Cls1; end
=> nil
irb(main):002:0> Cls1.ancestors
=> [Cls1, Object, Kernel, BasicObject]
irb(main):003:0> Object.superclass
=> BasicObject
irb(main):005:0> BasicObject.superclass
=> nil
irb(main):006:0> Class.superclass
=> Module
irb(main):001:0> Object.class
=> Class
irb(main):002:0> BasicObject.class
=> Class
irb(main):003:0> Class.class
=> Class
irb(main):004:0> Module.class
=> Class
irb(main):005:0> Kernel.class
=> Module

引き続き, const_missing (1)

以下のコードを実行するとどのような結果となるか.

class Class
  def const_missing(id)
    puts "Class#const_missing"
  end
end

class Cls1
  def self.const_missing(id)
    puts "Cls1#const_missing"
  end
end

class Cls2 < Cls1
  def hoge
    puts "CONST_VAR: #{CONST_VAR}"
  end
  def self.const_missing(id)
    puts "Cls2#const_missing"
    id = 'hoge'
  end
end

Cls2.new.hoge

以下, 解答予想.

Cls2#const_missing
CONST_VAR: hoge

以下, irb にて確認.

irb(main):022:0> 
irb(main):023:0* Cls2.new.hoge
Cls2#const_missing
CONST_VAR: hoge
=> nil

引き続き, const_missing (2)

以下のコードを実行するとどのような結果となるか.

class Class
  def const_missing(id)
    puts "Class#const_missing"
  end
end

class Cls1
  def self.const_missing(id)
    puts "Cls1#const_missing"
    id = 'Cls1'
  end
end

class Cls2 < Cls1
  def hoge
    puts "CONST_VAR: #{CONST_VAR}"
  end
  def const_missing(id)
    puts "Cls2#const_missing"
    id = 'Cls2'
  end
end

Cls2.new.hoge

以下, 解答予想.

Cls1#const_missing
CONST_VAR: Cls1

以下, irb にて確認.

irb(main):024:0* Cls2.new.hoge
Cls1#const_missing
CONST_VAR: Cls1
=> nil

引き続き, const_missing (3)

以下のコードを実行するとどのような結果となるか.

class Class
  def const_missing(id)
    puts "Class#const_missing"
    id = 'Class'
  end
end

class Object
  def Object.const_missing(id)
    puts "Object#const_missing"
    id = 'Object'
  end
end

class Cls2
  def hoge
    puts "CONST_VAR: #{CONST_VAR}"
  end
  def const_missing(id)
    puts "Cls2#const_missing"
    id = 'Cls2'
  end
end

Cls2.new.hoge

以下, irb にて確認.

irb(main):024:0> 
irb(main):025:0* Cls2.new.hoge
Object#const_missing
CONST_VAR: Object
=> nil
irb(main):026:0> Cls2.ancestors
=> [Cls2, Object, Kernel, BasicObject]

undef

インスタンスメソッドを undef しても, クラスメソッドには影響しない.

irb(main):001:0> class Foo
irb(main):002:1>   def self.foo
irb(main):003:2>     puts 'Foo.foo'
irb(main):004:2>   end
irb(main):005:1>   def foo
irb(main):006:2>     puts 'Foo#foo'
irb(main):007:2>   end
irb(main):008:1>   
irb(main):009:1*   undef foo
irb(main):010:1> end
=> nil
irb(main):011:0> 
irb(main):012:0* Foo.foo
Foo.foo
=> nil
irb(main):013:0> Foo.new.foo
NoMethodError: undefined method `foo' for #<Foo:0x00562a77f6c258>
...

private なメソッドを継承した場合の挙動

irb(main):018:0> class Foo
irb(main):019:1>   private
irb(main):020:1>   def foo
irb(main):021:2>     puts 'Private Foo'
irb(main):022:2>   end
irb(main):023:1> end
=> :foo
irb(main):024:0> class Bar < Foo
irb(main):025:1> end
=> nil
irb(main):026:0> Bar.new.foo
NoMethodError: private method `foo' called for #<Bar:0x00562a77d4e4d0>

継承したインスタンスメソッドを直接呼び出した場合には, レシーバ付きで呼び出すことは出来ない. NoMethodError となる.

irb(main):001:0> class Foo
irb(main):002:1>   private
irb(main):003:1>   def foo
irb(main):004:2>     puts 'Private Foo'
irb(main):005:2>   end
irb(main):006:1> end
=> :foo
irb(main):007:0> 
irb(main):008:0* class Bar < Foo
irb(main):009:1>   def foo
irb(main):010:2>     super
irb(main):011:2>   end
irb(main):012:1> end
=> :foo
irb(main):013:0> 
irb(main):014:0* Bar.new.foo
Private Foo
=> nil

super を利用することで, 親クラスの private メソッドを呼び出すことが出来る.

フムフム.

2018 年 08 月 02 日 (木)

ジョギング

  • 香椎浜 x 2 周
  • 引き続き, なんかギクシャクしている感じ
  • 右足の太もも裏, お尻にかけて痛み

日課

  • お休み

JAWS-UG のあつまり

  • 当初の目的からは逸脱してしまったが, 色々と話せて楽しかった
  • そして, 藤崎さんが用意してくれたお店がとてもマグロの美味しいお店で, 次回は奥さんをつれて行こうと思う

今日のるびぃ ~ 先人達の知恵と教訓 (1) ~

irb に動作確認環境は以下の通り.

$ ruby --version
ruby 2.1.10p492 (2016-04-01 revision 54464) [x86_64-linux]
$ irb --version
irb 0.9.6(09/06/30)

今回は, こちら に記載されていた, 例題を解いてみる. また, こちらの記事についても参考にさせて頂いた.

問題 (1)

module M
  def method_missing(id, *args)
    puts "M#method_missing()"
  end
end

class A
  include M
  def method_missing(id, *args)
    puts "A#method_missing()"
  end
end

class B < A
  def method_x
    puts "#{self.class.name}:method_x"
  end
  class << self
    def method_missing(id, *args)
      puts "B`method_missing()"
    end
  end
end

obj = B.new
obj.method_x
obj.method_y

以下, 解答予想.

B:method_x
A#method_missing()

以下, irb にて確認.

irb(main):025:0* obj = B.new
=> #<B:0x0055cd982f1e38>
irb(main):026:0> obj.method_x
B:method_x
=> nil
irb(main):027:0> obj.method_y
A#method_missing()
=> nil

以下, 解説.

  • obj.method_x で素直に B#method_x が呼ばれる
  • obj.method_y は, 継承を辿って super クラス (class A) の method_missing が呼ばれる

問題 (2)

class Module
  def method_missing(id, *args)
    puts "Module#method_missing()"
  end
end

class Class
  def method_missing(id, *args)
    puts "Class#method_missing()"
  end
end

module M
  def method_missing(id, *args)
    puts "M#method_missing()"
  end
end

class A
  include M
  def method_missing(id, *args)
    puts "A#method_missing()"
  end
end

class B < A
  def self.method_x
    puts "#{self}.method_x"
  end
  def method_missing(id, *args)
    puts "B#method_missing()"
  end
end

B.method_x
B.method_y

以下, 解答予想.

B.method_x
Class#method_missing

以下, irb にて確認.

... 略 ...
irb(main):034:0> 
irb(main):035:0* B.method_x
B.method_x
=> nil
irb(main):036:0> B.method_y
Class#method_missing()
=> nil

以下, 解説.

  • B クラスの self.method_x で, クラスメソッド B.method_x が出力される
  • B クラスにはクラスメソッド method_y は未定義なので, Class クラスの method_missing が呼ばれて Class#method_missing() が出力される

問題 (3)

class Module
  def const_missing(id)
    puts "Module#const_missing()"
    id = 1
  end
end

class Class
  def const_missing(id)
    puts "Class#const_missing()"
    id = 2
  end
end

class Object
  def const_missing(id)
    puts "Object#const_missing()"
    id = 3
  end
end

class A
  def const_missing(id)
    puts "A#const_missing()"
    id = 4
  end
end

class B < A
  CNST_X = "123"
    
  def method01
    puts "CNST_X=#{CNST_X}"
    puts "CNST_Y=#{CNST_Y}"
  end

  def const_missing(id)
    puts "B#const_missing()"
    id = 5
  end
end

obj = B.new
obj.method01

以下, 解答予想.

CNST_X=123
Class#const_missing()
CNST_Y=2

以下, irb にて確認.

irb(main):043:0* obj = B.new
=> #<B:0x0055f9bd413860>
irb(main):044:0> obj.method01
CNST_X=123
Class#const_missing()
CNST_Y=2
=> nil

以下, 解説.

以下, も少し const_missing について.

class Class
  def const_missing(const)
    const = '0'
  end
end

class Cls1
  def const_missing(const)
    const = '1'
  end
end

class Cls2 < Cls1
  CONST_1 = '123'
  def method1
    puts CONST_1
    puts CONST_2
  end
end

Cls2.new.method1

以下, irb にて確認.

... 略 ...
irb(main):020:0> Cls2.ancestors
=> [Cls2, Cls1, Object, Kernel, BasicObject]
irb(main):021:0> Cls2.new.method1
123
0
=> nil

以下, Object クラスと Class クラスで const_missing を定義した場合.

class Object
  def self.const_missing(const)
    const = '-1'
  end
end

class Class
  def const_missing(const)
    const = '0'
  end
end

class Cls1
  def const_missing(const)
    const = '1'
  end
end

class Cls2 < Cls1
  CONST_1 = '123'
  def method1
    puts CONST_1
    puts CONST_2
  end
end

Cls2.new.method1

以下, irb にて確認.

... 略 ...
irb(main):025:0> Cls2.ancestors
=> [Cls2, Cls1, Object, Kernel, BasicObject]
irb(main):026:0> 
irb(main):027:0* Cls2.new.method1
123
-1
=> nil

以下, メモ.

  • const_missing はクラスメソッドで実装されている必要がある
  • Class クラスのインスタンスメソッドは他のクラスのクラスメソッドとして処理される

フムフム.

2018 年 08 月 01 日 (水)

ジョギング

  • 香椎浜 x 2 周
  • なんかギクシャクしている感じ
  • 右足の太もも裏, お尻にかけて痛み

日課

  • お休み

Fukuoka.rb

今日は, awspec に新しいリソースタイプを追加する作業を行った.

早速, マージして頂き, ありがたや, ありがたや.

帰宅時に大濠公園の花火大会とバッティングしたけど, 無難に香椎まで帰ってくることが出来た.

今日のるびぃ ~ Ruby 技術者認定試験合格教本 (Silver/Gold 対応) Ruby 公式資格教科書 基礎力確認試験 (2) オブジェクト指向 (2) ~

irb に動作確認環境は以下の通り.

$ ruby --version
ruby 2.1.10p492 (2016-04-01 revision 54464) [x86_64-linux]
$ irb --version
irb 0.9.6(09/06/30)

オブジェクト指向 (6)

Q17. 以下のコードを実行すると何が表示されるか.

module Mod1; end
module Mod2; end
class Cls1
  prepend Mod1
end
class Cls2 < Cls1
  prepend Mod2
end
p Cls2.ancestors

以下, 解答.

  1. [Mod2, Cls2, Mod1, Cls1, Object, Kernel, BasicObject]

以下, irb にて確認.

irb(main):001:0> module Mod1; end
=> nil
irb(main):002:0> module Mod2; end
=> nil
irb(main):003:0> class Cls1
irb(main):004:1>   prepend Mod1
irb(main):005:1> end
=> Cls1
irb(main):006:0> class Cls2 < Cls1
irb(main):007:1>   prepend Mod2
irb(main):008:1> end
=> Cls2
irb(main):009:0> p Cls2.ancestors
[Mod2, Cls2, Mod1, Cls1, Object, Kernel, BasicObject]
=> [Mod2, Cls2, Mod1, Cls1, Object, Kernel, BasicObject]

以下, 解説より抜粋.

  • prepend されたモジュールは, prepend した対象のクラスよりも先にメソッドの探索が行われる

オブジェクト指向 (7)

Q18. 以下の実行結果になるように, [ x ] に記述する適切なコードを全て選べ.

# コード 
class Example
  def hoge
    self.piyo
  end
  [ x ]
  def piyo
    puts 'piyo'
  end
end
Example.new.hoge

# 実行結果
piyo

以下, 解答.

  1. protected
  2. public
  3. 何も記述しない

以下, 解説より抜粋.

  • self.piyopiyo メソッドに self レシーバーをつけている為, piyo メソッドが private だとエラーとなる
  • private 以外の protected, public, 何も記述しない場合は self.piyo で呼び出すことが出来る

オブジェクト指向 (8)

Q19. 以下のコードを実行すると何が表示されるか.

class Object
  X = 'X'
  def self.const_missing a
    p "#{a}"
  end
end
Y

以下, 解答.

  1. "Y"

以下, irb にて確認.

irb(main):001:0> class Object
irb(main):002:1>   X = 'X'
irb(main):003:1>   def self.const_missing a
irb(main):004:2>     p "#{a}"
irb(main):005:2>   end
irb(main):006:1> end
=> :const_missing
irb(main):007:0> Y
"Y"
=> "Y"

以下, 解説より抜粋.

  • 定数が見つからない場合, Module#const_missing が実行される
  • Module#const_missing はオーバーライトが可能である
  • 設問コードには定数 Y は存在しておらず, Object クラスには const_missing が定義されている
  • トップレベルは Object クラスなので, Object クラスに定義された const_missing が実行され Y が表示される

オブジェクト指向 (9)

Q20. 以下の実行結果になるように, [ x ] に記述する適切なコードを選べ.

# コード
class C
  def hoge
    puts 'A'
  end
end

module M
  refine C do
    def hoge
      puts 'B'
    end
  end
end

c = C.new
c.hoge
[ x ] M
c.hoge

# 実行結果
A
B

以下, 解答.

  1. using

以下, irb にて確認.

irb(main):001:0> class C
irb(main):002:1>   def hoge
irb(main):003:2>     puts 'A'
irb(main):004:2>   end
irb(main):005:1> end
=> :hoge
irb(main):006:0> 
irb(main):007:0* module M
irb(main):008:1>   refine C do
irb(main):009:2*     def hoge
irb(main):010:3>       puts 'B'
irb(main):011:3>     end
irb(main):012:2>   end
irb(main):013:1> end
=> #<refinement:C@M>
irb(main):014:0> c = C.new
=> #<C:0x00562753ea6098>
irb(main):015:0> c.hoge; using M; c.hoge
A
B
=> nil

以下, 解説より抜粋.

  • refine で再定義したメソッドを有効にするには, using を利用する

フムフム.

2018 年 07 月 31 日 (火)

ジョギング

  • 香椎浜 x 2 周
  • なんかギクシャクしている感じ

日課

  • お休み

長田さんと

長田さんと, 奥さんと三人で博多で串カツをつつきながら一杯. クラウドの話や, AS400 の話等, 色々な話をさせて頂いて楽しい時間を過ごすことが出来た. 今度は人生の相談とかもできればと考えている.

頭痛とか肩こりとか

  • 朝からキツイ

今日のるびぃ ~ Ruby 技術者認定試験合格教本 (Silver/Gold 対応) Ruby 公式資格教科書 基礎力確認試験 (1) オブジェクト指向 (1) ~

irb に動作確認環境は以下の通り.

$ ruby --version
ruby 2.1.10p492 (2016-04-01 revision 54464) [x86_64-linux]
$ irb --version
irb 0.9.6(09/06/30)

オブジェクト指向 (1)

Q12. 以下の実行結果になるように, [ x ] に記述する適切なコードを選べ.

# コード
class Super
  def greet
    'Hello'
  end
end

class Sub < Super
  def greet
    [ x ] + 'World.'
  end
end

puts Sub.new.greet

# 実行結果
Hello World.

以下, 解答.

  1. super

以下, irb にて確認.

irb(main):001:0> class Super
irb(main):002:1>   def greet
irb(main):003:2>     'Hello'
irb(main):004:2>   end
irb(main):005:1> end
=> :greet
irb(main):006:0> 
irb(main):007:0* class Sub < Super
irb(main):008:1>   def greet
irb(main):009:2>     super + 'World.'
irb(main):010:2>   end
irb(main):011:1> end
=> :greet
irb(main):012:0> 
irb(main):013:0* puts Sub.new.greet
HelloWorld.
=> nil

以下, 解説より抜粋.

オブジェクト指向 (2)

Q13. 以下のコードを実行した時, 何が出力されるか.

module M
  def foo
    self.class
  end
end

class C
  include M
end

p C.new.foo

以下, 解答.

  1. C

以下, irb にて確認.

irb(main):001:0> module M
irb(main):002:1>   def foo
irb(main):003:2>     self.class
irb(main):004:2>   end
irb(main):005:1> end
=> :foo
irb(main):006:0> 
irb(main):007:0* class C
irb(main):008:1>   include M
irb(main):009:1> end
=> C
irb(main):010:0> 
irb(main):011:0* p C.new.foo
C
=> C

以下, 解説より抜粋.

  • include は, モジュールをクラスに mix-in する
  • C.new.foofoo メソッドを呼び出すと, foo メソッド内の selfC.new で生成されたオブジェクト自身を返すので, self.classC を表す

オブジェクト指向 (3)

Q14. 以下のコードを実行するとどうなるか.

module Mod
  def foo
    puts 'Mod'
  end
end

class Cls1
  include Mod
  def foo
    puts 'Cls1'
    super
  end
end

class Cls2 < Cls1
  def foo
    puts 'Cls2'
    super
  end
end

Cls2.new.foo

以下, 解答.

  1. Cls2 Cls1 Mod

以下, irb にて確認.

... 略
irb(main):022:0* Cls2.new.foo
Cls2
Cls1
Mod
=> nil

以下, 解説より抜粋.

  • メソッドは, 自クラス→インクルードしているモジュール→スーパークラススーパークラスのインクルードしているモジュールの順番に検索される
  • 設問において, Cls2 クラスのオブジェクトで foo を呼び出すと, Cls2 クラスの foo が実行され Cls2 が出力される
  • Cls2#foo の super で Cls1#foo を呼び出し, Cls1 を出力される
  • 更に, Cls1#foo の super で Mod#foo を呼び出して, Mod を出力する

以下, メソッド探索色々.

# メソッド探索色々 (1)
module Mod
  def foo
    puts 'Mod'
  end
end

class Cls1
  include Mod
  def foo
    puts 'Cls1'
  end
end

Cls1.new.foo #=> 'Cls1'

# 以下, irb にて確認.
... 略 ...
irb(main):013:0> 
irb(main):014:0* Cls1.new.foo
Cls1
=> nil

引き続き, 探索.

# メソッド探索色々 (2)
module Mod
  def foo
    puts 'Mod'
  end
end

class Cls1
  include Mod
  def foo
    puts 'Cls1'
    super
  end
end

Cls1.new.foo

# 以下, irb にて確認.
... 略 ...
irb(main):014:0> 
irb(main):015:0* Cls1.new.foo
Cls1
Mod
=> nil

さらに探索.

module Mod
  def foo
    puts 'Mod'
  end
end

class Cls1
  def foo
    puts 'Cls1'
  end
end

class Cls2 < Cls1
  include Mod
  def foo
    puts 'Cls2'
    super
  end
end

Cls2.new.foo

# 以下, irb にて確認.
... 略 ...
irb(main):020:0> Cls2.new.foo
Cls2
Mod
=> nil
irb(main):021:0> Cls2.ancestors
=> [Cls2, Mod, Cls1, Object, Kernel, BasicObject]

さらに探索.

module Mod
  def foo
    puts 'Mod'
  end
end

class Cls1
  include Mod
  def foo
    puts 'Cls1'
    super
  end
end

class Cls2 < Cls1
  include Mod
  def foo
    puts 'Cls2'
    super
  end
end

Cls2.new.foo

# 以下, irb にて確認.
... 略 ...
irb(main):022:0> Cls2.ancestors
=> [Cls2, Cls1, Mod, Object, Kernel, BasicObject]
irb(main):023:0> Cls2.new.foo
Cls2
Cls1
Mod
=> nil

上記, 面白い例. include すると super クラスの間に挿入されるはずなので,

Cls2
Mod
Cls1

と出力されると予想したけど...子クラスで include したモジュールが無視されているように見える.

オブジェクト指向 (4)

Q15. 以下のコードを実行するとどうなるか.

class Cls1
  def Cls1.foo
    puts 'Cls1'
  end
end

class Cls2 < Cls1
  def foo
    puts 'Cls2'
  end
end

Cls2.foo

以下, 解答.

  1. Cls1 と表示される

以下, irb にて確認.

... 略 ...
irb(main):013:0* Cls2.foo
Cls1
=> nil

以下, 解説より抜粋.

  • Cls2.foo はクラスメソッド foo を呼び出す
  • Cls2#fooインスタンスメソッドの為, Cls1 のクラスメソッド foo が実行される

オブジェクト指向 (5)

Q16. 以下のコードを実行するとどうなるか.

module M1; end
module M2; end
class Cls1
  include M1
end
class Cls2 < Cls1
  p self.ancestors
  include M2
end

以下, 解答.

  1. [Cls2, Cls1, M1, Object, Kernel, BasicObject]

以下, irb にて確認.

irb(main):001:0> module M1; end
=> nil
irb(main):002:0> module M2; end
=> nil
irb(main):003:0> class Cls1
irb(main):004:1>   include M1
irb(main):005:1> end
=> Cls1
irb(main):006:0> class Cls2 < Cls1
irb(main):007:1>   p self.ancestors
irb(main):008:1>   include M2
irb(main):009:1> end
[Cls2, Cls1, M1, Object, Kernel, BasicObject]
=> Cls2

以下, 解説より抜粋.

  • ancestors は, クラス, モジュールの優先度で配列に格納して返す
  • Cls2 クラスの include M2 は ancestors 実行後の為, ancestors の対象外となる

フムフム.

Elasticsearch でフィールド同士の差分で検索したい (例えば, 開始から終了の経過時間で検索したいとか)

tl;dr

分かりづらいタイトルですいませぬ.

以下のようなデータを Elasticsearch に突っ込んでいたとして, startend の差分 (経過時間) を算出して, その結果から N 秒以上とか, N 秒以上, N 秒以下を検索条件として利用する方法を検討したのでメモっておく.

PUT /sample1/sample/1
{
  "title" : "test1",
  "start": "2018-07-30T12:00:00+09:00",
  "end": "2018-07-30T12:03:00+09:00",
}

PUT /sample1/sample/2
{
  "title" : "test2",
  "start": "2018-07-30T12:10:00+09:00",
  "end": "2018-07-30T12:15:00+09:00",
}

PUT /sample1/sample/3
{
  "title" : "test3",
  "start": "2018-07-30T13:10:00+09:00",
  "end": "2018-07-30T13:45:00+09:00",
}

PUT /sample1/sample/4
{
  "title" : "test4",
  "start": "2018-07-30T14:10:00+09:00",
  "end": "2018-07-30T15:10:00+09:00",
}

PUT /sample1/sample/5
{
  "title" : "test5",
  "start": "2018-07-30T14:10:00+09:00",
  "end": "2018-07-30T14:11:00+09:00",
}

尚, 検証に利用した Elasticsearch 環境は以下の通りで, Kibana のバージョンは 6.2.4 を利用している.

{
  "name": "UTIlghF",
  "cluster_name": "docker-cluster",
  "cluster_uuid": "FtmlW5cTRdidMnQZQ9sOuA",
  "version": {
    "number": "6.2.2",
    "build_hash": "10b1edd",
    "build_date": "2018-02-16T19:01:30.685723Z",
    "build_snapshot": false,
    "lucene_version": "7.2.1",
    "minimum_wire_compatibility_version": "5.6.0",
    "minimum_index_compatibility_version": "5.0.0"
  },
  "tagline": "You Know, for Search"
}

また, 全ての Elasticsearch クエリは Kibana の Dev Tools コンソールから実行することを前提としている. Dev Tools マジ便利.

確認

ドキュメントを確認

登録しているドキュメントを確認する.

GET sample1/_search

以下のように登録されている.

{
  "took": 6,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 5,
    "max_score": 1,
    "hits": [
      {
        "_index": "sample1",
        "_type": "sample",
        "_id": "5",
        "_score": 1,
        "_source": {
          "title": "test4",
          "start": "2018-07-30T14:10:00+09:00",
          "end": "2018-07-30T14:11:00+09:00"
        }
      },
      {
        "_index": "sample1",
        "_type": "sample",
        "_id": "2",
        "_score": 1,
        "_source": {
          "title": "test2",
          "start": "2018-07-30T12:10:00+09:00",
          "end": "2018-07-30T12:15:00+09:00"
        }
      },
      {
        "_index": "sample1",
        "_type": "sample",
        "_id": "4",
        "_score": 1,
        "_source": {
          "title": "test4",
          "start": "2018-07-30T14:10:00+09:00",
          "end": "2018-07-30T15:10:00+09:00"
        }
      },
      {
        "_index": "sample1",
        "_type": "sample",
        "_id": "1",
        "_score": 1,
        "_source": {
          "title": "test1",
          "start": "2018-07-30T12:00:00+09:00",
          "end": "2018-07-30T12:03:00+09:00"
        }
      },
      {
        "_index": "sample1",
        "_type": "sample",
        "_id": "3",
        "_score": 1,
        "_source": {
          "title": "test3",
          "start": "2018-07-30T13:10:00+09:00",
          "end": "2018-07-30T13:45:00+09:00"
        }
      }
    ]
  }
}

マッピングを確認

インデックスのマッピングを確認する.

GET sample1/_mapping

今回は特にテンプレートは定義していないが, 以下のようなマッピングとなっている.

{
  "sample1": {
    "mappings": {
      "sample": {
        "properties": {
          "end": {
            "type": "date"
          },
          "start": {
            "type": "date"
          },
          "title": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    }
  }
}

startend はそれぞれ Date データタイプで登録されているのでありがたい.

どうしたか (Script Query を利用する)

要件 (やりたいこと)

  • 各ドキュメントで経過時間(startend の差分) を取得する
  • 経過時間 N 秒以上, N 秒以下という検索条件でドキュメントを検索出来るようにしたい

Script Query を利用した.

Script Query を利用した.

www.elastic.co

以下のような Script Query を考えてみた. end から start の差分が 300 以上のドキュメントを検索するクエリとなる.

GET /sample1/_search
{
    "query": {
        "bool" : {
            "must" : [
                {
                    "script" : {
                        "script" : {
                            "lang": "painless",
                            "source": "(doc['end'].date.getMillis()/1000 - doc['start'].date.getMillis()/1000) > 300"
                        }
                    }
                }
            ]
        }
    }
}

doc['end'].date.getMillis()/1000 及び doc['start'].date.getMillis()/1000 は, 以下のような挙動となる.

  1. doc['end']doc['start'] でドキュメントのフィールドにアクセスしている
  2. .date.getMillis() でフィールドの値 (Date Type) を epoch タイムに変換して, さらに /1000 で 1000 で割って秒に変換
  3. end から start を差し引いた時間 (経過時間) を比較する (> 300)

検索してみると, 以下のような結果が返ってくる.

{
  "took": 8,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 1,
    "hits": [
      {
        "_index": "sample1",
        "_type": "sample",
        "_id": "4",
        "_score": 1,
        "_source": {
          "title": "test4",
          "start": "2018-07-30T14:10:00+09:00",
          "end": "2018-07-30T15:10:00+09:00"
        }
      },
      {
        "_index": "sample1",
        "_type": "sample",
        "_id": "3",
        "_score": 1,
        "_source": {
          "title": "test3",
          "start": "2018-07-30T13:10:00+09:00",
          "end": "2018-07-30T13:45:00+09:00"
        }
      }
    ]
  }
}

いい感じ.

例えば, title: test3 の場合には 2018-07-30T13:45:00+09:00 から 2018-07-30T13:10:00+09:00 を差し引いた時間は 35 分 (1500 秒) となり, 300 秒よりも長い為, 対象のドキュメントが検索結果として出力されている.

Script Query

前後してしまうが, 簡単に Script Query について触れる. 内容はドキュメントからの抜粋となる為, オフィシャルな情報が欲しい場合にはドキュメントを読みましょう. Elasticsearch のドキュメントは英語ということを除けば (汗) 細かく書かれていると思うし, 日本語で質問が投げられる disscuss.elastic.co というサイトもあるので, 何かあればこのサイトを利用すれば良いんだなって, 今更ながらに思った次第.

で, Script Query とは

www.elastic.co

Elasticsearch には, 以前のバージョンから検索クエリにスクリプトを埋め込むことが出来ていた. 以前のバージョンでは, Groovy という JVM 上で動作する言語が利用されていたが, 自分は実際に使ったことがないので, 使い勝手等については言及出来ない. ちなみに, elasticsearch scriptググると Groovy による実装についての記事がちらほら検索結果の上位に現れてくるので, 注意が必要だと思う.

で, 今回は検証に使っているのは Elasticsearch 6.2.2 となるので, Groovy ではなく, Painless Script Language というスクリプト言語を利用して記述することになる. 尚, ドキュメントによると, Painless Script Language がデフォルトで利用可能で, lang プラグインを有効にすることで, 以下のような言語を利用することが可能とのこと.

  • expression
  • mustache
  • java

尚, これらの言語での実装は柔軟性に欠けるものの, 特定のタスクにおいては, 高いパフォーマンスを発揮するらしい.

Painless Script Language

Painless Script Language について, ドキュメントを確認したところ, Painless Script Language について言及されているのは, Elasticsearch 5.0 からである為, Painless Script Language がサポートされたのは Elasticsearch 5.0 からだと推測される.

www.elastic.co

Painless Scripting Language については, 以下のような特徴がある. (上記のドキュメントをざっくり意訳して抜粋)

  • 高いパフォーマンス
  • 安全性 (Fine-grained whitelist with method call/field granularity がよく理解出来なかった)
  • 変数とパラメータは明示的又は動的な型が利用可能
  • Groovy スタイルのスクリプト言語機能
  • Elasticsearch に最適化

実装にあたっては, 以下の example を参考にしながら実装すると良さそう.

www.elastic.co

再掲, Script Query とは

改めて...

Script Query とは Elasticsearch のクエリに Painless Script Language というスクリプト言語で書いたスクリプトを埋め込んだもので, 以下のようなクエリとなる (以下のクエリはドキュメントより引用).

GET /_search
{
    "query": {
        "bool" : {
            "must" : {
                "script" : {
                    "script" : {
                        "source": "doc['num1'].value > 1",
                        "lang": "painless"
                     }
                }
            }
        }
    }
}

ドキュメントの値へのアクセスは doc という名前のマップを利用する. また, パラメータを渡す場合には, 以下のように params キーを利用する. 尚, params キーには複数のパラメータを渡すことも出来る.

GET /_search
{
    "query": {
        "bool" : {
            "must" : {
                "script" : {
                    "script" : {
                        "source" : "doc['num1'].value > params.param1",
                        "lang"   : "painless",
                        "params" : {
                            "param1" : 5
                        }
                    }
                }
            }
        }
    }
}

Script Query を利用することで, 今回のようなフィールド間の計算等のより柔軟なクエリを利用することが出来ると思う.

ちなみに

Range Aggregation

Aggregation と Script を組み合わせることで, N 秒以上は N 件, N 秒以上, N 秒以下は N 件のような集計も可能となる.

GET /sample1/_search
{
    "aggs" : {
        "time_ranges" : {
            "range" : {
                "script" : {
                    "lang": "painless",
                    "source": "doc[\"end\"].date.getMillis()/1000 - doc[\"start\"].date.getMillis()/1000"
                },
                "ranges" : [
                    { "from" : 0, "to" : 100 },
                    { "from" : 101, "to" : 500 },
                    { "from" : 501, "to" : 7200 }
                ]
            }
        }
    }
}

以下のように出力される.

{
... 略 ...
  "aggregations": {
    "time_ranges": {
      "buckets": [
        {
          "key": "0.0-100.0",
          "from": 0,
          "to": 100,
          "doc_count": 1
        },
        {
          "key": "101.0-500.0",
          "from": 101,
          "to": 500,
          "doc_count": 2
        },
        {
          "key": "501.0-7200.0",
          "from": 501,
          "to": 7200,
          "doc_count": 2
        }
      ]
    }
  }
}

フムフム.

以上

かなりざっくりとだけど, Script Query や Script を触れてみた. Script を多用した場合の検索パフォーマンス等, 気になるところはあるが, 用法用量を守って Script をうまく使うことで, Elasticsearch をより便利に利用出来るようになると思う.

Elasticsearch 楽しい.

2018 年 07 月 30 日 (月)

ジョギング

  • 香椎浜 x 2 周
  • 引き続き, 左足太もも前あたりに突っ張るような痛みは少しやわらいだ感
  • 全体的に体がバッキバキ

日課

  • (腕立て x 50 + 腹筋 x 50) x 3

台風一過

ちょっと涼しい感じがした.

頭痛とか肩こりとか

  • 朝からキツイ

今日のるびぃ ~ Ruby 技術者認定試験合格教本 (Silver/Gold 対応) Ruby 公式資格教科書 模擬試験 (20) 標準添付ライブラリ (3) ~

irb に動作確認環境は以下の通り.

$ ruby --version
ruby 2.1.10p492 (2016-04-01 revision 54464) [x86_64-linux]
$ irb --version
irb 0.9.6(09/06/30)

標準添付ライブラリ (12)

Q36. DateTime クラスのオブジェクトに 1 を加算するとどうなるか.

以下, 解答.

  1. 一日後の時刻を表す

以下, irb にて確認.

# 一日後の時刻を表す
irb(main):001:0> require 'date'
=> true
irb(main):002:0> d1 = DateTime.now
=> #<DateTime: 2018-07-15T18:37:23+09:00 ((2458315j,34643s,965591997n),+32400s,2299161j)>
# 一日後
irb(main):003:0> d1 + 1
=> #<DateTime: 2018-07-16T18:37:23+09:00 ((2458316j,34643s,965591997n),+32400s,2299161j)>
# 一ヶ月後
irb(main):004:0> d1 >> 1
=> #<DateTime: 2018-08-15T18:37:23+09:00 ((2458346j,34643s,965591997n),+32400s,2299161j)>
# 一ヶ月前
irb(main):005:0> d1 << 1
=> #<DateTime: 2018-06-15T18:37:23+09:00 ((2458285j,34643s,965591997n),+32400s,2299161j)>
# 一日前
irb(main):006:0> d1 - 1
=> #<DateTime: 2018-07-14T18:37:23+09:00 ((2458314j,34643s,965591997n),+32400s,2299161j)>

以下, 解説より抜粋.

  • DateTime クラスのオブジェクトに 1 を加算すると, 1 日後の時刻を示す
  • Date クラスでも同様に 1 日後の時刻を示す
  • Time クラスの場合には 1 秒後の時刻を示す

以下, 諸々確認.

irb(main):009:0> d2 = Date.new(2018, 07, 15)
=> #<Date: 2018-07-15 ((2458315j,0s,0n),+0s,2299161j)>
irb(main):010:0> d2 + 1
=> #<Date: 2018-07-16 ((2458316j,0s,0n),+0s,2299161j)>
irb(main):011:0> d2 >> 1
=> #<Date: 2018-08-15 ((2458346j,0s,0n),+0s,2299161j)>
irb(main):012:0> d2 << 1
=> #<Date: 2018-06-15 ((2458285j,0s,0n),+0s,2299161j)>
irb(main):013:0> d2 - 1
=> #<Date: 2018-07-14 ((2458314j,0s,0n),+0s,2299161j)>
irb(main):014:0> t1 = Time.now
=> 2018-07-15 18:42:01 +0900
irb(main):015:0> t1 + 1
=> 2018-07-15 18:42:02 +0900
irb(main):016:0> t1 - 1
=> 2018-07-15 18:42:00 +0900

標準添付ライブラリ (13)

Q39. rdoc コメントのマークアップとして適切な記述を全て選べ.

以下, 解答.

  1. *word* で太文字
  2. * で番号無しリスト

以下, Rdoc のサンプル.

class Foo
  CONST = [:const1, :const2, :const3] # :nodoc:

  # Foo オブジェクトの作成
  # === 引数
  # * +arg+ - ただの引数
  # === memo
  # * *memo* - 太文字 (日本語やスペースは無視される)
  # * _memo_ - 斜体 (日本語やスペースは無視される)
  # * +memo+ - 等幅フォント (日本語やスペースは無視される)
  # * <b>メモ</b> - 太文字 (日本語が利用可能)
  # * <em>メモ</em> - 斜体 (日本語が利用可能)
  # * <tt>メモ</tt> - 等幅フォント (日本語が利用可能)
  def initialize(arg)
    @arg0 = arg
  end
...

標準添付ライブラリ (14)

Q41. 以下のコードを実行すると何が表示されるか.

d1 = Time.new
d2 = Time.new
p (d2 - d1).class

以下, 解答.

  1. Float

以下, irb にて確認.

irb(main):001:0> d1 = Time.new
=> 2018-07-15 23:42:33 +0900
irb(main):002:0> d2 = Time.new
=> 2018-07-15 23:42:33 +0900
irb(main):003:0> p (d2 - d1).class
Float
=> Float

以下, 解説より抜粋.

  • Time クラス同士の演算結果は Float 型になる
  • DateTime クラス同士の演算結果は Rational 型になる
irb(main):005:0> require 'date'
=> true
irb(main):006:0> d1 = DateTime.now
=> #<DateTime: 2018-07-15T23:43:54+09:00 ((2458315j,53034s,566531787n),+32400s,2299161j)>
irb(main):007:0> d2 = DateTime.now
=> #<DateTime: 2018-07-15T23:44:00+09:00 ((2458315j,53040s,755208242n),+32400s,2299161j)>
irb(main):008:0> (d2 - d1).class
=> Rational

標準添付ライブラリ (15)

Q48. 以下の実行結果になるように, [ x ] に記述する適切なコードを全て選べ.

# コード
require 'json'

h = {'a' => 1, 'b' => 2}
puts [ x ]

# 実行結果
{"a": 1, "b": 2}

以下, 解答.

  1. h.to_json
  2. JSON.dump(h)

以下, irb にて確認.

irb(main):009:0> require 'json'
=> true
irb(main):010:0> 
irb(main):011:0* h = {'a' => 1, 'b' => 2}
=> {"a"=>1, "b"=>2}
irb(main):012:0> puts h.to_json
{"a":1,"b":2}
=> nil
irb(main):013:0> puts JSON.dump(h)
{"a":1,"b":2}
=> nil
irb(main):015:0> puts JSON.generate(h)
{"a":1,"b":2}

以下, 解説より抜粋.

  • json ライブラリは Hash クラスに JSON 文字列を生成する to_json メソッドを追加する
  • JSON.dump メソッドでも同様の JSON 文字列を生成する
  • JSON.dump は与えられたオブジェクトを引数として JSON.#generate を呼び出している

フムフム.

2018 年 07 月 29 日 (日)

ジョギング

  • 香椎浜 x 2 周
  • 引き続き, 左足太もも前あたりに突っ張るような痛み...嫌な痛みなのでちょっと休むことを検討

日課

  • (腕立て x 50 + 腹筋 x 50) x 3

ゴロゴロ

台風が近づいているということで, 終日ゴロゴロ. 朝, 5 時過ぎに目が覚めたので適度に眠気に襲われて寝て起きてを繰り返していた感じ.

今日のるびぃ ~ Ruby 技術者認定試験合格教本 (Silver/Gold 対応) Ruby 公式資格教科書 模擬試験 (19) 標準添付ライブラリ (2) ~

irb に動作確認環境は以下の通り.

$ ruby --version
ruby 2.1.10p492 (2016-04-01 revision 54464) [x86_64-linux]
$ irb --version
irb 0.9.6(09/06/30)

標準添付ライブラリ (6)

Q2. 次のコードを実行するとどうなるか. 該当するものを全て選べ.

a = 1.0 + 1
a = a + (1 / 2r)
a = a + (1 + 2i)

以下, 解答.

  1. 1 行目で a の値が Float インスタンスとなる
  2. 3 行目で a の値が Complex インスタンスとなる

以下, irb にて確認.

irb(main):001:0> a = 1.0 + 1
=> 2.0
irb(main):002:0> a.class
=> Float
irb(main):003:0> a = a + (1 / 2r)
=> 2.5
irb(main):004:0> a.class
=> Float
irb(main):005:0> a = a + (1 + 2i)
=> (3.5+2i)
irb(main):006:0> a.class
=> Complex

以下, 解説より抜粋.

irb(main):008:0> (1 + 1).class
=> Fixnum
irb(main):009:0> (1 + 1.0).class
=> Float
irb(main):010:0> (1 + 1r).class
=> Rational
irb(main):011:0> (1 + 1i).class
=> Complex
irb(main):012:0> (1.0 + 1r).class
=> Float
irb(main):013:0> (1.0 + 1i).class
=> Complex
irb(main):014:0> (1r + 1i).class
=> Complex

標準添付ライブラリ (7)

Q16. 以下のコードを実行すると何が表示されるか.

require 'date'

d1 = DateTime.new
d2 = DateTime.new
p (d2 - d1).class

以下, 解答.

  1. Rational

以下, irb にて確認.

irb(main):001:0> require 'date'
=> true
irb(main):002:0> 
irb(main):003:0* d1 = DateTime.new
=> #<DateTime: -4712-01-01T00:00:00+00:00 ((0j,0s,0n),+0s,2299161j)>
irb(main):004:0> d2 = DateTime.new
=> #<DateTime: -4712-01-01T00:00:00+00:00 ((0j,0s,0n),+0s,2299161j)>
irb(main):005:0> p (d2 - d1).class
Rational
=> Rational

以下, 解説より抜粋.

ちなみに, Time クラス同士の演算結果は Float となる.

irb(main):007:0> t1 = Time.new
=> 2018-07-15 16:45:21 +0900
irb(main):008:0> t2 = Time.new
=> 2018-07-15 16:45:27 +0900
irb(main):009:0> p (t2 - t1).class
Float
=> Float

標準添付ライブラリ (9)

Q21. 以下のコードを実行すると何が表示されるか.

require 'yaml'
dir = <<EOY
file1:
  name: app.rb
  data: ruby
file2:
  - aaaa.rb
  - ruby
EOY
p YAML.load(dir)

以下, 解答.

  1. {"file1" => {"name" => "app.rb", "data" => "ruby"}}

以下, irb にて確認.

irb(main):001:0> require 'yaml'
=> true
irb(main):002:0> dir = <<EOY
irb(main):003:0" file1:
irb(main):004:0"   name: app.rb
irb(main):005:0"   data: ruby
irb(main):006:0" EOY
=> "file1:\n  name: app.rb\n  data: ruby\n"
irb(main):007:0> p YAML.load(dir)
{"file1"=>{"name"=>"app.rb", "data"=>"ruby"}}
=> {"file1"=>{"name"=>"app.rb", "data"=>"ruby"}}

以下, 解説より抜粋.

  • 設問の YAML 形式のデータはハッシュとして読み込まれる

以下, 配列を含む場合.a

irb(main):001:0> require 'yaml'
=> true
irb(main):002:0> dir = <<EOY
irb(main):003:0" file1:
irb(main):004:0"   name: app.rb
irb(main):005:0"   data: ruby
irb(main):006:0" file2:
irb(main):007:0"   - aaaa.rb
irb(main):008:0"   - ruby
irb(main):009:0" EOY
=> "file1:\n  name: app.rb\n  data: ruby\nfile2:\n  - aaaa.rb\n  - ruby\n"
irb(main):010:0> p YAML.load(dir)
{"file1"=>{"name"=>"app.rb", "data"=>"ruby"}, "file2"=>["aaaa.rb", "ruby"]}
=> {"file1"=>{"name"=>"app.rb", "data"=>"ruby"}, "file2"=>["aaaa.rb", "ruby"]}

標準添付ライブラリ (10)

Q24. stringio ライブラリの説明として適切な記述を全てを選べ.

以下, 解答.

  1. 文字列を IO オブジェクトと同じように扱える

以下, 解説より抜粋.

  • stringio ライブラリは, 文字列に IO オブジェクトと同じインターフェースを持たせる StringIO クラスを含む
  • StringIO クラスは IO クラスを継承したクラスでは無い

標準添付ライブラリ (11)

Q30. 以下のコードを実行するとどうなるか.

array = [1, 2, 3].freeze
array += [4, 5]
p array

以下, 解答.

  1. [1, 2, 3, 4, 5] と表示される

以下, irb にて確認.

irb(main):011:0> array = [1, 2, 3].freeze
=> [1, 2, 3]
irb(main):012:0> array += [4, 5]
=> [1, 2, 3, 4, 5]
irb(main):013:0> p array
[1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]

以下, 解説より抜粋.

  • freeze はオブジェクトの内容変更を禁止するが, 参照変数自身は変更可能
  • 設問では変数 array を破壊的に変更 (concat, uniq! 等)することは出来ないが, 変数 array が別の変数を参照するように変更することは出来る

以下, ドキュメントより引用.

irb(main):001:0> a1 = 'foo'.freeze
=> "foo"
irb(main):002:0> a1 = 'bar'
=> "bar"
irb(main):003:0> a2 = 'foo'.freeze
=> "foo"
irb(main):004:0> a2.replace('bar')
RuntimeError: can't modify frozen String

凍結されるのはオブジェクトであり、変数ではありません。代入などで変数の指す オブジェクトが変化してしまうことは freeze では防げません。 freeze が防ぐのは、 `破壊的な操作' と呼ばれるもの一般です。

フムフム.

改めて exercism.io の始めたのでメモ

tl;dr

exercism.io

exercism.io は出題されたお題のコード書いてサブミットすると世界中のメンターが寄ってたかってレビューしてくれる Web サイト. 以前にお試しでアカウントを作っておいたんだけど, しばらく時間が経って久しぶりにログインしてみたら, バージョンアップしてコマンドラインツールの使い方とか変わってしまっていたので, 使い方をざっくりとメモっておく.

コマンドラインツール

exercism.io はコマンドラインツールが提供されており, そのコマンドラインツールを利用してお題をローカルに取得して, コードを書き, ユニットテストで確認を行い, コマンドラインツールを利用してサブミット (解答の提出) するという流れ. ということで, 以下は, コマンドラインツールを取得して, お題を取得出来るようになるまで.

環境は, Ubuntu 16.04 を利用する. また, 既に exercism.io にログインした状態で, 何らかの言語のトラックにジョインしていることを前提としている.

$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.4 LTS"

コマンドラインツールを取得. 最新版のコマンドラインツールはこちらで確認する.

mkdir ~/bin/
wget https://github.com/exercism/cli/releases/download/v3.0.6/exercism-linux-64bit.tgz
tar zxvf exercism-linux-64bit.tgz

展開したら, カレントディレクトリに exercism コマンドが展開されている.

$ exercism version
exercism version 3.0.6

トークンを利用して初期設定を行う. トークンは exercism.io にログインした状態で settings メニューページから取得することが出来る. 以下のように configure サブコマンドのオプション引数にトークンを指定してコマンドを実行する.

$ exercism configure --token=xxxxxxxxx-xxxx-xxxx-xxx-xxxxxxxxxxxxxx

実行すると, 以下のように出力される.

You have configured the Exercism command-line client:

Config dir:                       /home/${USER}/.config/exercism
Token:         (-t, --token)      xxxxxxxxx-xxxx-xxxx-xxx-xxxxxxxxxxxxxx
Workspace:     (-w, --workspace)  /home/${USER}/exercism
API Base URL:  (-a, --api)        https://api.exercism.io/v1

あとは, download サブコマンドを利用してお題をダウンロードする. 例えば, Ruby トラックのお題である Hello World をダウンロードした場合は以下のような出力になる.

$ exercism download --exercise=hello-world --track=ruby

Downloaded to
/home/${USER}/exercism/ruby/hello-world
$ ls ~/exercism/ruby/hello-world/
GETTING_STARTED.md  README.md hello_world_test.rb

hello_world_test.rb は以下のような内容になっており, minitest を利用してテストを走らせられるようになっている.

... 略 ...
class HelloWorldTest < Minitest::Test
  def test_say_hi
    # skip
    assert_equal "Hello, World!", HelloWorld.hello
  end
end
... 略 ...

未実装の状態でテストを走らせると以下のようにエラーとなる.

$ ruby hello_world_test.rb 

Error:
hello_world_test.rb:4:in `require_relative' cannot load such file -- /home/${USER}/exercism/ruby/hello-world/hello_world

*****************************************************
You got an error, which is exactly as it should be.
This is the first step in the Test-Driven Development
(TDD) process.

The most important part of the error is

   cannot load such file

It's looking for a file named hello_world.rb that doesn't
exist yet.

To fix the error, create an empty file named hello_world.rb
in the same directory as the hello_world_test.rb file.

Then run the test again.

For more guidance as you work on this exercise, see
GETTING_STARTED.md.
*****************************************************

ということで, これで世界と戦う準備が出来た. テストが通るにコードを書いて, 以下のように submit サブコマンドで exercism.io にプッシュするとメンター達によるレビューが行われる.  

Hello World

せっかくなので

せっかくなので, Ruby と Go で Hello World を解いてみる.

Ruby

Ruby のバージョンは以下の通り.

$ ruby --version
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]

まずはお題のダウンロード.

$ exercism download --exercise=hello-world --track=ruby

Downloaded to
/home/${USER}/exercism/ruby/hello-world

とりあえず, テスト.

$ ruby hello_world_test.rb 

Error:
hello_world_test.rb:4:in `require_relative' cannot load such file -- /home/${USER}/exercism/ruby/hello-world/hello_world

そうですな. hello_world.rb がないっすな.

$ ruby hello_world_test.rb 
Run options: --seed 30127

# Running:

E

Finished in 0.000665s, 1504.2003 runs/s, 0.0000 assertions/s.

  1) Error:
HelloWorldTest#test_say_hi:
NameError: uninitialized constant HelloWorldTest::HelloWorld
Did you mean?  HelloWorldTest
    hello_world_test.rb:18:in `test_say_hi'

1 runs, 0 assertions, 0 failures, 1 errors, 0 skips

ですです, HelloWorld クラスが実装されてないっすな. 以下のように HelloWorld クラスを実装してみた.

class HelloWorld
end

もう一度テスト.

$ ruby hello_world_test.rb 
Run options: --seed 37451

# Running:

E

Finished in 0.000980s, 1020.1990 runs/s, 0.0000 assertions/s.

  1) Error:
HelloWorldTest#test_say_hi:
NoMethodError: undefined method `hello' for HelloWorld:Class
    hello_world_test.rb:18:in `test_say_hi'

1 runs, 0 assertions, 0 failures, 1 errors, 0 skips

ちょっとだけ変わった. hello メソッドが実装されていないですな. 以下のように HelloWorld.hello を実装してみる.

class HelloWorld
  class << self
    def hello
      'Hello, World!'
    end
  end
end

これでどうだ.

$ ruby hello_world_test.rb 
Run options: --seed 36305

# Running:

.

Finished in 0.000644s, 1553.4201 runs/s, 1553.4201 assertions/s.

1 runs, 1 assertions, 0 failures, 0 errors, 0 skips

LGTM.

そして, この解答を submit サブコマンドを利用して提出すると, 以下のように出力される.

$ exercism submit ~/exercism/ruby/hello-world/hello_world.rb 


    Your solution has been submitted successfully.
    You can complete the exercise and unlock the next core exercise at:

    https://exercism.io/my/solutions/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

ちなみに, 他のソリューションについても考えてみる. 例えば, 以下のようにモジュールに定義したメソッドを extend してみる.

module HelloWorldModule
  def hello
    'Hello, World!'
  end
end

class HelloWorld
  extend HelloWorldModule
end

テストを走らせてみると...

$ ruby hello_world_test.rb 
Run options: --seed 8451

# Running:

.

Finished in 0.001956s, 511.2582 runs/s, 511.2582 assertions/s.

1 runs, 1 assertions, 0 failures, 0 errors, 0 skips

一応, LGTM. こちらのソリューションも submit しておく.

Go

Go のバージョンは以下の通り.

$ go version
go version go1.6.2 linux/amd64

こちらもお題の取得.

$ exercism download --exercise=hello-world --track=go

Downloaded to
/home/${USER}/exercism/go/hello-world

取得されたファイルは以下の通り.

$ ls ~/exercism/go/hello-world/
README.md  hello_test.go  hello_world.go

とりあえず, テスト.

$ cd ~/exercism/go/hello-world/
$ go test
--- FAIL: TestHelloWorld (0.00s)
        hello_test.go:13: HelloWorld() = , want Hello, World!
FAIL
exit status 1
FAIL    _/home/kappa/exercism/go/hello-world    0.003s

Hello, World! が返ってきていないようなので, 上記のように FAIL するので hello_world.go を以下のように実装する.

... 略 ..
func HelloWorld() string {
    return "Hello, World!"
}

もう一度, テストを走らせる.

$ go test
PASS
ok      _/home/kappa/exercism/go/hello-world    0.002s

LGTM.

こんなに簡単でいいんだろうかと思いつつも submit する.

$ exercism submit ~/exercism/go/hello-world/hello_world.go 


    Your solution has been submitted successfully.
    You can complete the exercise and unlock the next core exercise at:

    https://exercism.io/my/solutions/yyyyyyyyyyyyyyyyyyyyyyyyyy

よし, 次の問題を解くぞー...

ということで

exercism.io の使い方 (問題の取得, 提出方法) を簡単にまとめてみた. 世界中の強者メンターから教えを請いながら少しでもプログラミングのスキルを上げていきたいと思っている.

2018 年 07 月 28 日 (土)

ジョギング

  • 香椎浜 x 2 周
  • なんか, 両足が筋肉痛気味だったのでゆっくりと
  • 左足太もも前あたりに突っ張るような痛み

日課

  • (腕立て x 50 + 腹筋 x 50) x 3

8 時間耐久カラオケ

奥さんと 8 時間耐久カラオケを実施した. ほぼ 8 時間歌いっぱなしで喉がへんな感じになってしまった. 奥さんは平気で歌っていたので, 鍛え方が違うなーと思った次第. 総じて楽しかった. 猛暑でつらい休日の過ごし方はコレでで決まりかもしれない.

今日のるびぃ ~ Ruby 技術者認定試験合格教本 (Silver/Gold 対応) Ruby 公式資格教科書 模擬試験 (18) 標準添付ライブラリ (1) ~

irb に動作確認環境は以下の通り.

$ ruby --version
ruby 2.1.10p492 (2016-04-01 revision 54464) [x86_64-linux]
$ irb --version
irb 0.9.6(09/06/30)

標準添付ライブラリ (1) 基礎力確認問題より

Q26. 以下のコードを実行したらどうなるか. テストされるクラスファイル foo.rb は存在していると仮定.

require 'test/unit'
require 'foo'

class TC_Foo < Test::Unit::TestCase
  def foo_test
    # テストコード...
  end
end

以下, 解答.

  1. foo_test が規則に則っていないので, テストは実行されない

以下, 解説より抜粋.

  • Test::Unit のテストメソッド名は test_ で始める

以下, 正しいコード.

require 'test/unit'
require 'foo'

class TC_Foo < Test::Unit::TestCase
  def test_foo
    # テストコード...
  end
end

標準添付ライブラリ (2) 基礎力確認問題より

Q27. socket ライブラリにあるクラスを全て選べ.

以下, 解答.

  1. UDPSocket
  2. BasicSocket
  3. TCPSocket

以下, 解説より抜粋.

  • socket ライブラリに, BasicServer クラスや UDPServer クラスは存在しない
  • ちなみに, TCPSerever は存在する (TCP/IP ストリーム型接続のサーバ側のソケットのクラス)

尚, BasicSocket を親として, 以下のような親子関係になっている.

# Ruby 2.5.1 で確認 (activesupport が Ruby 2.2.2 以上を要求する為)
irb(main):001:0> require 'active_support/core_ext/class/subclasses'
=> true
irb(main):002:0> require 'socket'
=> true
irb(main):003:0> 
irb(main):004:0> p BasicSocket.subclasses
[Socket, IPSocket, UNIXSocket]
=> [Socket, IPSocket, UNIXSocket]
irb(main):005:0> p UNIXSocket.subclasses
[UNIXServer]
=> [UNIXServer]
irb(main):006:0> p IPSocket.subclasses
[TCPSocket, UDPSocket]
=> [TCPSocket, UDPSocket]
# TCPServer は TCPSocket の子クラスとなる
irb(main):007:0> p TCPSocket.subclasses
[TCPServer]
=> [TCPServer]

標準添付ライブラリ (3) 基礎力確認問題より

Q28. 以下の実行結果になるように [ x ] に記述する適切なコードを選べ.

# コード
require 'date'

date = Date.new(2000, 10, 10)
puts date [ x ] 1

# 実行結果
2000-11-10

以下, 解答.

以下, irb にて確認.

irb(main):001:0> require 'date'
=> true
irb(main):002:0> date = Date.new(2000, 10, 10)
=> #<Date: 2000-10-10 ((2451828j,0s,0n),+0s,2299161j)>
irb(main):003:0> puts date >> 1
2000-11-10
=> nil

以下, 解説より抜粋.

  • date + 1 とすると, date の 1 日後の日付を返す
  • date - 1 とすると, 1 日前の日付を返す
  • date << 1 は 1 ヶ月前, date >> 1 は 1 ヶ月後を返す

以下, irb にて確認.

irb(main):003:0> puts date >> 1
2000-11-10
=> nil
irb(main):004:0> puts date + 1
2000-10-11
=> nil
irb(main):005:0> puts date - 1
2000-10-09
=> nil
irb(main):006:0> puts date << 1
2000-09-10
=> nil
irb(main):007:0> puts date >> 1
2000-11-10
=> nil

標準添付ライブラリ (4) 基礎力確認問題より

Q29. 以下の記述で適切なものを全て選べ

以下, 解答.

  1. StringIO は IO と同じインターフェースを持つ
  2. Float オブジェクトと Rational オブジェクトの演算結果は Float オブジェクトとなる
  3. DateTime オブジェクトに 1 加算すると, 翌日のデータとなる

以下, irb で確認出来るものは irb で確認.

# StringIO は IO と同じインターフェースを持つ
irb(main):010:0> sio = StringIO.new("hoge", 'r+')
=> #<StringIO:0x005622d72857a8>
irb(main):011:0> sio.read
=> "hoge"
irb(main):012:0> sio.rewind
=> 0
irb(main):013:0> sio.read(0)
=> ""
irb(main):014:0> sio.read(1)
=> "h"
irb(main):015:0> sio.read(2)
=> "og"
irb(main):016:0> sio.rewind
=> 0
irb(main):017:0> sio.read(1)
=> "h"
irb(main):018:0> sio.write("OGE")
=> 3
irb(main):019:0> sio.rewind
=> 0
irb(main):020:0> sio.read
=> "hOGE"

# Float オブジェクトと Rational オブジェクトの演算結果は Float オブジェクトとなる
irb(main):021:0> (1.0 + 1r).class
=> Float
irb(main):022:0> (1 + 1r).class
=> Rational
irb(main):023:0> (1i + 1r).class
=> Complex
irb(main):024:0> (1 + 1i).class
=> Complex

# DateTime オブジェクトに 1 加算すると, 翌日のデータとなる
irb(main):001:0> require 'date'
=> true
irb(main):002:0> date.class
=> DateTime
irb(main):004:0> date = DateTime.now()
=> #<DateTime: 2018-07-15T09:31:32+09:00 ((2458315j,1892s,381051256n),+32400s,2299161j)>
irb(main):005:0> date + 1
=> #<DateTime: 2018-07-16T09:31:32+09:00 ((2458316j,1892s,381051256n),+32400s,2299161j)>

以下, 解説より抜粋.

  • rdoc において * は番号無しのリストを生成する
  • Thread クラスには run は無い
    • インスタンスメソッドには run は存在する
      • ドキュメントより引用: 停止状態(stop)のスレッドを再開させます。 Thread#wakeup と異なりすぐにスレッドの切り替えを行います。
    • 新しいスレッドを生成するのは new, start, fork なので注意する

以下, Thread クラスのメソッドを確認した図.

irb(main):001:0> t = Thread.new{}
=> #<Thread:0x0055b0d90c5dc0 run>
irb(main):002:0> t.methods
=> [:pending_interrupt?, :raise, :join, :value, :kill, :terminate, :exit, :run, :wakeup, :[], :[]=, :key?, :keys, :priority, :priority=, :status, :thread_variable_get, :thread_variable_set, :thread_variables, :thread_variable?, :alive?, :stop?, :abort_on_exception, :abort_on_exception=, :safe_level, :group, :backtrace, :backtrace_locations, :inspect, :set_trace_func, :add_trace_func, :nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :to_s, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :extend, :display, :method, :public_method, :singleton_method, :define_singleton_method, :object_id, :to_enum, :enum_for, :==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__]
irb(main):007:0> t = Thread.new{}
=> #<Thread:0x0055b0d9089848 run>
irb(main):008:0> t = Thread.start{}
=> #<Thread:0x0055b0d907ca80 run>
irb(main):009:0> t = Thread.fork{}
=> #<Thread:0x0055b0d906b230 run>
irb(main):010:0> t = Thread.run{}
NoMethodError: undefined method `run' for Thread:Class

標準添付ライブラリ (5) 基礎力確認問題より

Q30. 以下の記述で適切なものを全て選べ

以下のコードは HTTP ダウンロードしたデータを表示するコードである. [ x ] に記述する適切なコードを選べ.

require 'open-uri'

[ x ]('https://docs.ruby-lang.org/ja/2.1.0/doc/index.html') do |f|
  print f.read
end

以下, 解答.

  1. open

以下, irb にて確認

# ページ全体がダンプされないように, 読み込みサイズを指定した (f.read(100))
irb(main):001:0> require 'open-uri'
=> true
irb(main):002:0> 
irb(main):003:0* open('https://docs.ruby-lang.org/ja/2.1.0/doc/index.html') do |f|
irb(main):004:1*   print f.read(100)
irb(main):005:1> end
<!DOCTYPE html>
<html lang="ja-JP">
<head>
  <meta http-equiv="Content-Type" content="text/html; cha=> nil

以下, 解説より抜粋.

  • open-uri は Kernel モジュールの open (Kernel.#open) を再定義して, HTTP で指定したファイルを取得する

フムフム.

Ruby 技術者認定試験 Gold に `やっと` 合格して福岡県民の義務を果たせた

やっと 合格

毎年の目標に掲げ続けていた, Ruby Association Certified Ruby Programmer Gold version 2.1 に合格出来たので振り返りたいと思う.

やっと Gold が取れて福岡県民の義務を果たせました。

モチベーション

なぜ, 受験したのか. それは, Ruby 推しの街, 福岡に住むエンジニアとしての義務だと思っているから...

speakerdeck.com

業務上で Ruby コードを書くことはないが, ちょくちょく Ruby 製のツールをいじる機会があるので, これらのツールがどのような仕組みで動いているのか, その基盤となっている Ruby についてちゃんと理解するべきという思いから 2016 年の 8 月に Silver を受験して合格し, Gold 合格を目指して勉強を重ねてきた. (ちゃんと勉強に取り掛かったのは 2017 年末)

実は三浪

試験センターのお姉さんに顔を覚えられたかな...

実は今回の試験で 4 回目の受験. 試験センターのお姉さんにも顔を覚えられてしまったのではというくらい. 過去 3 回の試験をちょっとだけ振り返ると以下のような感じ.

一回目

  • 2017 年 12 月末に受験
  • 合格教本の問題集の答えだけ覚えて受験した感じで, ちょっと甘く見ていた
  • 62 点

二回目

  • 2018 年 1 月末に受験
  • 一回目の反省を踏まえて, 合格教本とメタプログラミング Ruby もちゃんと読み直し, IT トレメの問題を解くようにした
  • でも, 不合格の 70 点

三回目

  • 2018 年 7 月 20 日, 記念すべき fukuoka.rb 100 回目に花を添えようと思って受験
  • 二回目以降, だいぶん間が空いてしまっていて, 試験の感覚が鈍っていた
  • 最後の見直しで直した部分が全部間違っていたと記憶している (自信を持って答えらえるレベルに達していないと実感)
  • 72 点

そして今回

  • クラス継承の理解を根本的に間違っていると三回目の不合格に気づいたので, 慌ててその理解の矯正をした
  • 三回目のモチベーションを維持しつつ, 最後のチャレンジということで自分にプレッシャー

1 行でまとめると

  • 記憶に頼るだけでは合格は難しい!!!

どんな勉強をしたか

教材, 目的, 取り組み

教材 目的 利用 (学習) 頻度
合格教本 出題範囲の把握等 3 回程通読
公式リファレンス 不明な点をググる前に確認 都度利用
メタプログラミング Ruby 第2版 メタプログラミングの理解を深める 3 回程通読, 気分転換にパラパラと
チェリー本 合格教本, 公式リファレンスで理解出来ない部分を補う 都度利用
合格教本の各種問題 (基礎力確認, 模擬) 問題に慣れる, 答えの説明が出来るように 隙間時間や毎朝の起き抜けに
IT トレメ 問題に慣れる, 答えの説明が出来るように 隙間時間や毎朝の起き抜けに
RubyExamination 理解度の確認 3 回程
  • 仕事で Ruby を触っているわけではないので, 一日 10 分でも Ruby に触る時間を設けた
  • 参考資料に書かれているコードを徹底的に写経, コードを少し変えて自作の問題を作る
  • そのうちに答えを覚えてしまうので, その後は, なぜ, その答えになるのかを説明出来るようになるまで理解を深める
  • 公式のドキュメントの細かい部分まで読んでおく (全ての項目は無理なので, 気になった項目だけでも)

オブジェクト指向, 組み込みライブラリ, 例外処理

特に以下の出題範囲について勉強に多くの時間を割いた.

オブジェクト指向では, クラスの継承, メソッドや定数の探索経路, メソッドの可視性等を, 組み込みライブラリでは Enumerable や正規表現, 文法では例外処理のイメージが全然掴めていなかったので, 合格教本や IT トレメの設問を写経したり, 自分でコードを考えたりして挙動の把握に努めた.

書籍教材

gihyo.jp

公式の問題集で, 出題範囲毎の解説と基礎力問題, 模擬問題が掲載されている. 以後は「合格教本」と呼ぶ.

www.oreilly.co.jp

Gold 受験するなら必読と呼ばれている良書. メンターのビルからあなた (読者) に OJT を通してメタプログラミングを教えるような流れになっていて, 完全に内容を理解出来なくても楽しく読み進めることが出来る. 自分はどちらかと言うと, 他の教材で学習する息抜きに読み進めた感じ.

gihyo.jp

チェリー本. 合格教本や公式リファレンスを読んでいて, なんと無く理解出来ていない部分もをこのチェリー本を解りやすい解説を読むことで, 自分の中でその部分を言語化することが出来た.

サイト教材

オブジェクト指向スクリプト言語 Ruby リファレンスマニュアル (Ruby 2.1.0)

ググる前に公式のリファレンスを読む癖を付けた.

jibun.atmarkit.co.jp

IT トレメ.

REx - Ruby Examination

合格教本に掲載されている問題や IT トレメに掲載されている問題と比較すると格段に難しくて, 本物の試験問題よりも難しいんじゃないかと思うくらいの問題が出題されるので, 一問, 一問, リファレンスや書籍を参考にしながら解く感じで進めた.

その他, 「Ruby Gold」や「Ruby 認定試験」でググってヒットする過去に受験された方々の受験期や勉強メモ等も参考にさせて頂いた.

以上

変化

勉強していく過程で, 自分自身にいくつか変化があった. 一つは, 他人が書いた Ruby のコードが少し読めるようになったこと. もう一つはググる前に公式のドキュメントを読むようになったこと (すごく当たり前のようなことだけど).

これからが大事

合格出来て本当に嬉しいし, 本当に苦労したけど, Ruby を体系的に学ぶことが出来て受験して本当に良かったと考えている. 今後もこれを活かせるようにがんばっていきたいと思う.

ありがとう, fukuoka.rb

福岡の Ruby コミュニティ fukuoka.rb が 100 回目を迎えるとのことで, そこに花を添えようと思い (余計無いお世話だけど), 三回目を受験したけど不合格で, 俺って本当に馬鹿なんだろうなあと凹みつつも, これが最後のチャレンジでなんとか合格することが出来た次第. fukuoka.rb が 100 回目を迎えていなければ, もしかすると三回目は受けていなかったかもしれないので fukuoka.rb には本当に感謝している.

ありがとう, 奥さん

落ちても「良い笑いのネタになったね」と励ましてくれた奥さん, 「あんた, Ruby の本質を解ってるの?」とダメ出ししてくれた奥さん, 家族団らんの時間を勉強に割いても笑って許してくれた奥さん, 本当にありがとう.