※この記事は、Speee Advent Calendar19日目の記事です。
昨日の記事はこちらtech.speee.jp
こんにちは!
デジタルトランスフォーメーション(DX)事業本部エンジニアの岡田です。
Speeeでは、クリアコードの須藤さんにOSS活動をサポートしてもらっています。
今回、OSS活動はほぼ未経験の自分が、どのように挑戦したかを書いてみました。
最初はチャットでの相談から入り、現在もサポプロを使って勉強しています。
OSS 活動に挑戦した背景
自分は、プログラミングを社会人から始めて、特に体系だった知識をもたないまま、業務で必要なスキルを都度都度勉強してきました。
結果、WEB開発という文脈であれば、フロントエンドも、サーバーサイドも、クラウド周りもある程度は一人で実装できるようになりました。
一方で、まだまだ技術の話で分かってないことが多いなと感じており、特にライブラリ周りはただ使わせてもらうだけで、どう作られてるのかを詳しく知りたかったので、勉強したい!という気軽な気持ちで挑戦しました。
OSS Gateに参加
まず、Speeeと須藤さんで開催されている「OSS Gateミートアップ for Red Data Tools」というイベントに参加しました。 speee.connpass.com
Red Data Toolsについて、詳しくはこちら!
下のチャットで、わからないことを日本語で質問しながら作業できるので、安心でした!
Red Datasets にデータセット追加
須藤さんより、まずは簡単なものとして、Red Datasets にデータを追加することを提案頂いたので、まずはこちらに取り掛かりました。
こちらから自分の好きなIssueを探し、他のPRを参考にしながら、実装を行います。
こちらは、須藤さんに環境構築方法や、わからないことをチャットから質問して、あっさり終わらせることができました!
Red Arrow の開発ドキュメントを追加
次に、Rubyで作られたGemを実際に修正してみたかったので、須藤さんに相談したところ、Red Arrowへの機能追加を提案いただきました!
で、早速やろうとしたのですが、Macでうまく動かずつまづきました...
須藤さんに相談しながら、上手く修正できたので、次行われる方のために、ドキュメント追加を行いました。
Red Arrow の slice が、hashを受け取るようにする
環境構築ができたので、早速 Red Arrow への機能追加に取り掛かりました。
既に、table.slice(1..8)
というように、rangeを渡した時は、その値でIDが絞り込まれるように実装されていました。
今回は、table.slice(count: 1..8)
のように、カラムを指定して、絞り込みをする機能を追加するのがゴールとなります。
ハマったこと(学んだこと)
Rangeの最後の値を確認する方法がわからない
{ count: 8.. }
としたときに、最後の値がendlessになるかをテストしたかったです。
Range#last
を使って比較しようとしましたが、cannot get the last element of endless range
というエラーが起きてしまいました。
最終的に、Range#end
を使うと問題なくできました。
こちらが endの実装で、
static VALUE range_end(VALUE range) { return RANGE_END(range); }
こちらがlastの実装(引数がない時はendと同じ挙動だけど、引数渡すと挙動が変わる)
static VALUE range_last(int argc, VALUE *argv, VALUE range) { VALUE b, e; if (NIL_P(RANGE_END(range))) { rb_raise(rb_eRangeError, "cannot get the last element of endless range"); } if (argc == 0) return RANGE_END(range); b = RANGE_BEG(range); e = RANGE_END(range); if (RB_INTEGER_TYPE_P(b) && RB_INTEGER_TYPE_P(e) && RB_LIKELY(rb_method_basic_definition_p(rb_cRange, idEach))) { return rb_int_range_last(argc, argv, range); } return rb_ary_last(argc, argv, rb_Array(range)); }
という二つの差を理解していなかったのが原因でした。
Rubyの実装を見る機会が今まで特になかったので、とても勉強になりました。
複数条件での絞り込み方法がわからない
table.slice(count: ..8, name: 'okadak')
というように複数で絞り込みを可能にしたかったです。
Arrow::Table
は簡単にいうと、表形式のCSVのデータが入ってるイメージに近くて、今回はCSVのheaderの値を指定して、その列の値でフィルタリングしたいという感じです。
須藤さんに教えてもらったところ、こんな感じに動けばいいことが分かりました。
slicers = [] conditions = [] slicer = Arrow::Slicer.new(table) conditions << (slicer[:count] >= 8) conditions << (slicer[:name] == 'okadak') slicers << conditions.inject(:&) # 省略: slicersを用いて、filterする
このメソッドの中身に関しては、まず、slicer[:count] >= 8
これを実行すると、「[]
」メソッドが実行されます。
def [](column_name) column = @table[column_name] return nil if column.nil? ColumnCondition.new(column) end
これが実行されると、Arrow::Table
の「[]
」メソッドを実行します。
def [](selector) case selector ... else find_column(selector) end end def find_column(name_or_index) case name_or_index when String, Symbol name = name_or_index.to_s index = schema.get_field_index(name) return nil if index == -1 Column.new(self, index) .... end
Arrow::Column
が以下のように初期化され、
def initialize(container, index) @container = container # Arrow::Table が入る @index = index # 何列目の値を指定してるか @field = @container.schema[@index] @data = @container.get_column_data(@index) end
この値を使って、Arrow::Slicer::ColumnCondition
が初期化されます。
なので、slicer[:count]
は、Arrow::Slicer::ColumnCondition.new(Arrow::Column.new)
を返すようになります。
よって、Arrow::Slicer::ColumnCondition.new >= 8
となるので、Arrow::Slicer::ColumnCondition
の 「>=
」メソッドが引数「8」で実行され、Arrow::Slicer::ColumnCondition::GreaterEqualCondition
が返ります。
def >=(value) GreaterEqualCondition.new(@column, value) end
このArrow::Slicer::ColumnCondition::GreaterEqualCondition
は、Arrow::Slicer::Condition
を継承していて、conditions.inject(:&)
のように実行すると、「&
」メソッドが実行され、Arrow::Slicer::Condition::AndCondition
が返されます。
def &(condition) AndCondition.new(self, condition) end class LogicalCondition < Condition def initialize(condition1, condition2) @condition1 = condition1 @condition2 = condition2 end def evaluate function.execute([@condition1.evaluate, @condition2.evaluate]).value end end class AndCondition < LogicalCondition private def function Function.find("and") end end
このArrow::Slicer::Condition::AndCondition
は、Arrow::Slicer::LogicalCondition
を継承しており、LogicalCondition
は複数のArrow::Slicer::Condition
を保持できるようになっているので、このArrow::Slicer::Condition
を使って、複数条件の絞り込みが可能になりました。
(Filter側の処理は今回修正していないので、説明を省略します。)
最終的には、こちらのようなコードになりました。
「slicer[:count] >= 8
」みたいなコードを特に意識せずに使えるのは、誰かが裏で綺麗に設計してくれていたからなんだなと、すごく勉強になりました!
Part2へ...
今はこちらの実装をしてます!
まだ終わってないので、次回も続きを書きたいと思います!
終わりに
このようにクリアコードの須藤さんに教えてもらいながら、OSS活動をしはじめています!
現在はサポプロを使ってやっているわけですが、C++ の書き方や、コードの意図など、分からないことを丁寧に説明してくださるのでとても勉強になっています。
また、OSS活動を始めてから、Rubyのライブラリやドキュメントを読む力が上がっている感覚があり、困った時の解決速度が実際に上がりました。
何より、初めて知ることが多くてとても楽しいです!プログラミングについて、まだまだ勉強できることがたくさんあるなと感じています。
今後もエンジニアとしてのスキルをもっと上げていきながら、OSSコミュニティへ貢献できたらと思います!
Speeeでは一緒にサービス開発を推進してくれる仲間を大募集しています!もしSpeeeに興味を持っていただいた方は以下で社内メンバーのカジュアル面談を公開しているので、お気軽にご連絡ください! tech.speee.jp
Speeeでは様々なポジションで募集中なので、「どんなポジションがあるの?」と気になってくれてた方は、こちらチェックしてみてください!もちろんオープンポジション的に上記に限らず積極採用中です!!!
読んでいただきありがとうございました!