超絶怒涛のゆるふわコードリーディング GraphQL gem篇
GraphQL gemをふんわり読んだ
ふんわり読んで見ました、とりあえずよく使われてそうなDSLをおったメモ
versionはv1.9.12 が最新リリースである時期のmasterブランチ読みました。
ゆるふわの極みです
とりあえず最初に公式のGetting Startedで出てくるやつ
module Types class PostType < Types::BaseObject field :id, ID, null: false field :title, String, null: false field :truncated_preview, String, null: false field :comments, [Types::CommentType], null: true, description: "This post's comments, or null if this post has comments disabled." end end
field is 何?
GraphQL::Schema::ObjectがextendしてるGraphQL::Schema::Member::HasFieldsのメソッド。
def field(*args, **kwargs, &block) field_defn = field_class.from_options(*args, owner: self, **kwargs, &block) add_field(field_defn) nil end
field_class is 何?
def field_class(new_field_class = nil) if new_field_class @field_class = new_field_class elsif @field_class @field_class elsif self.is_a?(Class) superclass.respond_to?(:field_class) ? superclass.field_class : GraphQL::Schema::Field else ancestor = ancestors[1..-1].find { |a| a.respond_to?(:field_class) && a.field_class } ancestor ? ancestor.field_class : GraphQL::Schema::Field end end
引数はわたしてないので一個目の分岐は飛ばして、@field_classも(多分素のままだと)設定されてないのでGraphQL::Schema::Fieldか何がしかのクラスが返されているっぽい GraphQL::Schema::Field.from_optionsはオプションを色々整えた後newしてインスタンス返してくれるメソッド。
def self.from_options(name = nil, type = nil, desc = nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block)
引数の数すごい
最終的に下のコードはGraphQL::Schema::Fieldかサブクラスかなにかのインスタンス作ってると言えそう。
field_defn = field_class.from_options(*args, owner: self, **kwargs, &block)
add_field is 何
つぎはこの行
add_field(field_defn)
def add_field(field_defn) if CONFLICT_FIELD_NAMES.include?(field_defn.original_name) && field_defn.original_name == field_defn.resolver_method warn "#{self.graphql_name}'s `field :#{field_defn.original_name}` conflicts with a built-in method, use `resolver_method:` to pick a different resolver method for this field (for example, `resolver_method: :resolve_#{field_defn.original_name}` and `def resolve_#{field_defn.original_name}`)" end own_fields[field_defn.name] = field_defn nil end
:context, :object, :method, :class 書くと警告されるっぽい。 中身はハッシュで名前をキーにしてfield_class.from_optionsでつくったインスタンスをそのまま突っ込んでる。
んでこのハッシュは下記の2つのメソッドで使ってるっぽい
def fields # Local overrides take precedence over inherited fields all_fields = {} ancestors.reverse_each do |ancestor| if ancestor.respond_to?(:own_fields) all_fields.merge!(ancestor.own_fields) end end all_fields end def get_field(field_name) if (f = own_fields[field_name]) f else for ancestor in ancestors if ancestor.respond_to?(:own_fields) && f = ancestor.own_fields[field_name] return f end end nil end end
多分実行してるときに使ってるコード
public_sendしてますね。引数も渡せそう。
オブジェクトがもともとメソッドもってればなんでもフィールドにできる感じなのかな?
def resolve_field_method(obj, ruby_kwargs, ctx) if obj.object.is_a?(Hash) inner_object = obj.object if inner_object.key?(@method_sym) inner_object[@method_sym] else inner_object[@method_str] end elsif obj.object.respond_to?(@method_sym) if ruby_kwargs.any? obj.object.public_send(@method_sym, **ruby_kwargs) else obj.object.public_send(@method_sym) end else raise <<-ERR Failed to implement #{@owner.graphql_name}.#{@name}, tried: - `#{obj.class}##{@resolver_method}`, which did not exist - `#{obj.object.class}##{@method_sym}`, which did not exist - Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash To implement this field, define one of the methods above (and check for typos) ERR end end
前半の分岐がよくわからんけど
@method_symはこんな感じでinitializeされるときに入れてる
method_name = method || hash_key || @underscored_name @method_sym = method_name.to_sym
まとめ
ちょっと読んだ後公式のドキュメントみて使い方知らないとわけわからんことに気づいたのでここで終わりです。
OSSに貢献してる人これ無償で読んで理解して書いてるってすごすぎませんか??????
エンジニアとして働いて
エンジニアとして働き初めて半年がたった。まだまだわからないこと、初めて知ることばかりであり、周りの方の助けを借りながらなんとかやっている。
なんとなく書きたくなったのでエンジニアとして働き初めて感じたこととかをまとめようと思う。
エンジニアとして働くことは幸せなこと
エンジニアは幸せな仕事だと思う。自分がやっていることと仕事を簡単に結びつけやすい職業だ。仕事を人生にしやすいと感じる。
自分が趣味やら素振りやらで培った技術がすぐに仕事に活かせるし、そうでなくても原理原則をしれば仕事のツールとして絶対に効いてくる。
自分は現在教育分野での開発をしているのだけど、教育とITの相性もいいし教育とエンジニアの相性もとてもいいと思う。学んで試してを繰り返している人がエンジニアは多いように思うし、教育も自走できる人間を育てるのが一番の目指すところだと思う。自分ができなかったことができるようになっていくのも、知らなかったことを知るのも楽しい。
僕は大学までノホホンとしていて自分から学ぶことなんてゲーム以外でしなかったが、プログラミングを学び始めてからは知ることも試すことも全て自分から学びに行っている実感がある。誰に言われるでもなく自分が知りたいことを自分から学びに行くことがこんなに楽しいことだとは知らなかった。ましてやそれが実際に収入に結びつくなんて最高だ。
自分が頑張った分だけ良くなっていくという希望はこの仕事を通して初めて感じた。
転職した理由は身近で仕事を楽しそうに話す人がエンジニアだったという理由だったが間違いではなかったと思う。
いわゆる技術と同等に大事だと思うこと
ITエンジニアとしての技術力それ自体ももちろん大切だけど、半年働いてみてこれも重要だなぁと思うこともできた。
チームの雰囲気を良くしていく力
チームの効率を上げたりだとかも重要かもしれないけど、それ以上に雰囲気を和ますというか適度にチャットしたりおしゃべりできるような空気を作る力というのはものすごく大きい思う。心理的安全性というやつだろうか。いつも笑ってるぐらいがちょうどいいと思う。
他業種に対する敬意
売上を出しているのはコードだけではない。営業の人や方針をきめた経営者やユーザーの声を最初に聞いてくれる人、日々プロダクトとユーザーについて真摯に向き合っている人等大勢の人が総合して売上をだしている。
自分も前職のときは自分の組織が使っているシステムを必死に使っていた。他業種の大変さとかを理解するのは人間がわかりあうのは難しいので難しいが、少なくとも敬意をもって考えを聞く姿勢は常に持っているのが大事だと感じた。みんなそれぞれの考えを持って働いているのだ。
人に頼ること
わからないことをきちんとわからないということも大切だし、人間が一人でできることには限界があることを認めることも重要だと思う。
自分が余り知らない機能のコードがあったとして、自分でじっくりそのコードを読むこともいいが、その機能に詳しそうな人に伺うという行為をちゃんとすることも大事だと思う。得意でない事を得意な人に聞いている人というのは素晴らしい人だと思う。
これから
どうなるか知らんけど楽しく働いて行きたい
「試して理解 Linuxのしくみ」を読んだ
[試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識
- 作者: 武内覚
- 出版社/メーカー: 技術評論社
- 発売日: 2018/02/23
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
読んだ理由
せっかく自宅の環境をUbuntuにしたので
Linuxの基本的な知識知りたいなぁと思ったのと、A Tour of Goとかruby/rubyの並行テストとか見て並行処理の意味が全くわからんちんだったので、プロセスを詳しく知るために購入した。
全体的な感想
試して理解ということでサンプルコードがたくさん載っていた。最初はGitHubに全部上がってるの知らずにポチポチC言語写経しながら読んでいた。
実際にハードウェアにアクセスするのがカーネルで、普段僕が書いてるアプリケーションはあくまでプロセスとしてシステムコールのラッパー関数を呼び出しているだけ、という事からして初めて知ったので、全編に渡って新しい発見の連続だった。
あと著者の方の環境がメモリ32Gで人権を感じた。
要約感想的なもの
3章
- fork()関数もこの本で初めて知った。webサーバーがなんで同時に複数のリクエストをさばけるのか少しわかりかけてきた。
(この記事の意味がようやく少しわかった。)
4章 プロセススケジューラー
論理CPU一個が同時に二つプロセス動かすことはない。プロセスはコンテキストスイッチでそのプロセス関係なく処理が一旦止まることがありうる。
単位時間あたりの処理量と結果的な処理にかかる時間はトレードオフ
5章 メモリ管理
メモリアドレスを直接扱うのはプロセスにはまだ早すぎるので仮想アドレスを使う。
ページテーブルはルーティングテーブルみたいなもの。コンピュータというのはどこも二分探索にするための工夫が多い気がする。
デマンドページング 仮想メモリ確保 != 物理メモリ確保
copy on write fork()時にいきなり新しい物理メモリを確保するのではない。 書き込むときに子プロセス用のメモリを確保して書き込む。
ヒュージページ 葉を増やすと枝も増える。仮想メモリと物理メモリの関係も増える。単位を大きくして防ぐ。 ubuntu 19.04ではトランスペアレントヒュージページのデフォルトはmadvise()だった。
6章 記憶階層
メモリとキャッシュメモリは違うもの
キャッシュメモリに局所性(実際に使うところ)をいかにおさめるか
メモリにキャッシュしたものをダーティにして実際に変更してダーティを解除
ページテーブルもCPUにあるTLBで
Linuxはファイルを可能な限りページキャッシュしようとするって大規模サービス実践技術入門にもあった気がする。実際有無を比べると早さが違う
write backは自分の環境でも五秒に一回だった。想像の何百倍も間隔長かった。ダーティのまま中断は普通に起こりそう。
7章 ファイルシステム
ファイルシステムでもcopy on write 書き換えるときはコピーする
8章 ストレージデバイス
ファイルは連続あるいは近く
連続する領域へのアクセスは一回で
終わりに
サンプルコードや実際に手を動かすところが多かったのも良かったけど、解説において過程を飛ばさないで一つ一つ全部図解してくれていたことがありがたかった。
どうやって並行処理の整合性保ってるのかはあんまりわかってない。 子プロセスで書き込みしたらcopy on writeで別の物理アドレスになってしまうのでは???
次は「なるほどUNIXプロセス Rubyで学ぶUnixの基礎」を買ったのでまた知識の上塗りをしていきたい。
rails consoleに簡単にオプションを足せるようになるgemを作った
作ったgemはこれです
使い方
config/application.rb
に下のようにオプションをセットします。
Rails::Application
の上に書くようにしてください。
Bundler.require(*Rails.groups) D4C::Console.add_option 'hello' do puts "hello" end module YourRailsApplication class Application < Rails::Application
これでrails console
に渡せるオプションを追加できました。実際にオプションが呼ばれると渡したブロックがcall
されます。
rails c --hello Running via Spring preloader in process 7922 hoge Loading development environment (Rails 5.2.3) irb(main):001:0>
なんで作ったか
rails console
を叩くときに、「デフォルトで実行されたくないけど実行したいときはさっと実行したい」処理があったからです。
職場でact_as_tenant
というgemを使っています。dbのレコードに制限をかけるgemです。
act_as_tenant
の都合上、rails consoleからdbのレコードを叩くには
ActsAsTenant.without_tenant do end
として作業しなくてはなりませんでした。
とはいえRailtie
のconsole
メソッドでせっかくかけたセキュリティをデフォルトで外してしまうことはしたくありません。
--sandbox
みたいにオプションで選択できると良いと考え、このgemを作りました。 1
実装の話
そもそもrails console --sandboxをした時の流れ
bin/rails console
自体の具体的な流れはRailsガイドにとっても詳しく書いてあるので読みましょう。
では--sandbox
はどうやって処理されているのでしょうか。
(注 Railsのコードを載せていますが色々省いています。)
Rails::Command::ConsoleCommand
クラスは下のようになっています。
class_option :sandbox, aliases: "-s", type: :boolean, default: false, desc: "Rollback database modifications on exit." def perform Rails::Console.start(Rails.application, options) end
受け取れるコマンドラインのオプションをclass_option
で追加しています。
Rails::Command::ConsoleCommand
はThorというコマンドラインツールの作成を助けるgemを継承しています。class_option
はそのgemのメソッドです。
Rails::Application
クラス内では--sandbox
が入力されたかを保存できるようにしてあります。
attr_accessor :assets, :sandbox
Rails::Console
クラスでは、コマンドラインから渡されたオプションの有無をアプリケーションのインスタンスに渡しています。
def initialize(app, options = {}) @app = app @options = options app.sandbox = sandbox? end def sandbox? options[:sandbox] end
これによりif app.sandbox
で条件分岐できるようになります。
実際にD4Cで行った拡張
module Rails Application.prepend D4C::PrependOptionAccessor Console.prepend D4C::PrependOptionSetter Command::ConsoleCommand.prepend D4C::PrependClassOption end
D4CのmoduleはD4C::Console.add_option
に渡されたオプションをRailsのクラスが扱えるようにするものです。 それぞれprepend
してmoduleの中でsuper
することで処理を足しています。
D4C::Console
の中身はオプションを構造体にして保存するクラスのインスタンスです。
Gemの名前
ジョジョの奇妙な冒険のスタンド名です。
最初はbaby_faceって名前でした、コンソールなので。
ディ・モールトベネなgemの名前だと思いながら作成して、いざ公開しようと思ったら同じ名前のgemが既にあって泣きました。皆さんはこうならないようにちゃんとrubygemsで名前を検索してから作りましょう。
最終的にたやすくオプションを足せるってところからD4Cにしました。
最後にこれが一番書きたかったんですけど、D4Cってまさに「Ruby」だと思うんですよね。
クラスメソッドをPryのコマンドにできるGemをリリースしました
作ったGemはこれです
使い方
まず.pryrc
内で下のようにクラスをセットします。
PrySingular.make_commands HogeJob, FactoryBot
これでPryの中で設定したクラスの特異メソッドをコマンドとして使うことができます。
pry(main)> perform_now
引数を渡すこともできます(あくまでコマンドなので引数との間はスペースが必要)
pry(main)> create :user
コマンドにしたいメソッドを直接指定することもできます。
PrySingular.make_commands FactoryBot, only: [:build, :attributes_for]
コマンドにしたくないメソッドを指定することもできます。
PrySingular.make_commands FactoryBot, except: :create
コマンドなのでObject
を汚してメソッドを生やすことはありません。
[1] pry(main)> self => main [2] pry(main)> self.respond_to?(:create) => false
また、内部ではsingleton_methods(true)を使っているので、class
とかrubyのコアな部分まではコマンドを設定しません。
作った理由
FactoryBotのcreate
とかActsAsTenantのwithout_tenant
とかをいちいちrails cの中で書くのめんどい〜みたいな話が周りで起こり、.pryrcの話がにわかに盛り上がりました。そのとき職場の人がPry::ClassCommand
でゴニョゴニョといっていたのを聞いたのが始まりです。
最初はFactoryBotとPryを連携させたいな〜と思って作っていたのですが、書いていくうちに「これクラス設定してもらって全部できるようにしたほうが便利だな」と思って実装してみました。
実装
コアな部分はここです
def import_class_command(klass, options) singular_methods = adapt_options_singleton_methods(klass, options) set_pry_command do singular_methods.each do |klass_method| command "#{klass_method}", "#{klass}.#{klass_method}" do extend PrySingular::Slop klass.class_eval <<-EOS #{parse_singular_method_command(Readline::HISTORY.to_a.last)} EOS end end end end def set_pry_command(&block) commands = Pry::CommandSet.new &block Pry.config.commands.import(commands) end
module PrySingular module Slop def parse_singular_method_command(items) method, args = items.split(" ", 2) method + ' ' + args.gsub(' ', '') end end end
command
はStringオブジェクトをメソッドの名前、メソッドの説明の順に引数に取って、コマンドが呼ばれたらブロックの中身を実行してくれるPryのメソッドです。
仕組みとしてはコマンドが呼ばれたらReadline::HISTORY
の最新のものを一度メソッドと引数に分けてから引数の空白を削除し、対象のクラスをレシーバーにclass_eval
して直接実行しています。
pry(main)> create :user
と呼ばれたら最終的に
FactoryBot.class_eval <<-EOS create :user EOS
が実行されるといった具合です。 なのでコマンドの実装の中身は全て、クラスにReadlineから受け取った文字列をズルっとわたしているだけです。
Gemを作ってみて
Gem作成は初めてでしたが、RubyGemsのおかげで簡単に公開することができました。
PryのDSLの柔軟性にも助けられましたし、オプションの設定方法もRailsのパクリです。 巨人の肩は高い 。
反省点としてはリリースしてはだめじゃんを繰り返して既にversionが0.1.7なことですね…
まだまだテストもカバレッジ足りないしリファクタもできると思うし、そもそもまだバグもあるかもしれないので、よいGemにできるようやっていきです。
よかったら使ってみてください、バグとかこれがあったら面白いとかあればissueやコメントで教えてくれたら泣いて喜びます。
2019/07/21 追記
インターフェースをmake_commandsにしたり内部のコードいろいろかえました
*1:ブログに乗せることも快く了承いただき本当にありがとうございます
個人開発用マシンをThinkPad X1 Carbon with Ubuntuにしました
経緯
個人の開発マシンはプログラミング始めた去年の6月にMBP2014の13インチをヤフオクで落札して以来ずっとそれを使っていたのですが、5月末くらいにコーヒーを盛大にこぼしてトラックパッドが使用不能に…。
最新のMBPの13インチか迷ったけどLinux使ってみたかったのでThinkPad x1 CarbonにUbuntu入れて使うことにしました。
ThinkPad x1 Carbonにした理由は
- Linuxを入れている報告が多数あること
- ThinkPadかっこいいという勝手なイメージ
- コーヒーとか多少こぼしても大丈夫な堅牢性
(この動画最高ThinkPad - 史上最強の証明テスト - YouTube)
です。いろいろいれたら17万くらいになっちゃいました。
スペック
- Coffee Lake Core i7-8550U
- 16GB RAM
- 14.0型WQHD液晶
- M.2 SSD180 (ぶっちゃけ200もいらないので減らした)
- 英字キーボード
こうみるとそんなに盛ってないですね
ふぁーすと・いんぷれっしょん
カーボンの天板の高級感と手触りは◎。キーボードも噂に違わぬ押しやすさです。 そしてなんといっても軽量です。前回のMBP2014が13インチが1.57 kg、職場のMBP2017の15インチが1.83kg。比較してThinkPad X1 Carbon G6は1.13kg。持つとわかりますが本当に軽いです。あと端子がまとも
スピーカーが背面にあるのだけ悲しい
Ubuntu入れてみて
ThinkPadにLinuxってポピュラーな使い方だし大丈夫だろ〜と思ってたのですが結構苦労しました…
トラックパッドとトラックポイント(赤ポチ)が反応せず、ググりまくって色々試してもどこか治るとどこか動かなくなる状態で途方にくれていました。
結局18.04から19.04にアップデートしたら治ったので良くわからない。
18.04でも使いたいって人がいたらこれをいれるのがいいかと思います。
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1791427/comments/94
スリープ状態でもバッテリーがガンガン消費される問題もありました。BIOSでLinux用の設定に変えれば解決しますが。
現在もbluetoothの音質がかなり悪く、なんとかならんかと試行錯誤しています。
Macとの違い
そんなにないです。⌘キーって便利だったんだなぁというくらい。
tweak tool入れてaltをctrにするといい感じ。現状はかなり快適です。
Dockerいれるのに19.04だと公式通りにやってもうまく行かなくてちょっとハマりましたが、Dockerそのものは明らかにMacより軽いです。キビキビ。
ワークスペースやウインドウの管理なんかも楽ですしスムーズです。
とりあえず入れて正常に動くことを確認したもの
chrome入れれば環境はほとんど同期したも同然だし、シェルもfish使ってるのでチョチョっとプラグイン入れるだけですみました。 今はlinuxbrewもあるのでこれからも困ることはそんなにないかと思います。
まとめ
やっぱり黒一色っていいですね。秋葉に黒い人が多いのもしょうがない。
「Linuxのしくみ」とかこれを使って実践しながら読んでいきたいと思います。
後いざThinkPadで競プロや!ってABCでたらB以降さっぱりできなくて泣いた。
SQL実践入門を読みました
SQL実践入門──高速でわかりやすいクエリの書き方 (WEB+DB PRESS plus)
- 作者: ミック
- 出版社/メーカー: 技術評論社
- 発売日: 2015/04/11
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (7件) を見る
SQLアンチパターンや失敗から学ぶRDBの正しい歩き方を読んでいて、ウインドウ関数や結合などまだまだ浅い理解だなーと思って買いました。
以下、気になったことと感想を書きます。
where句で条件分岐するのは素人、プロはSELECT句で条件分岐する。
どういうことかというと 例えば 名前、性別、部署を持つテーブルがあったとして
社員名簿テーブル
名前 | 性別 | 部署 |
---|---|---|
太郎 | 男 | 営業 |
花子 | 女 | 営業 |
男性と女性人数を部署ごとに出したいとき whereを使うと
SELECT 部署, COUNT(*) FROM 社員名簿 WHERE 性別= '男' GROUP BY 部署; SELECT 部署, COUNT(*) FROM 社員名簿 WHERE 性別= '女' GROUP BY 部署;
という感じでUNION使っても二回テーブルスキャンしなくてはいけないところを
SELECT 部署, SUM( CASE WHEN sex = '男' THEN 1 ELSE 0 END), SUM( CASE WHEN sex = '女' THEN 1 ELSE 0 END) FROM 社員名簿 WHERE 性別= '女' GROUP BY 部署;
というように書くことができます CASE式はWHENに当てはまればそこで評価を終了するので先程のWHERE句での分岐より処理も早いです。
Joinの際に駆動表はなぜ小さくするのか
ネステッドループで結合する際に、駆動表はフルテーブルスキャンが行われますが内部表はインデックスが使われます。
どうせフルテーブルスキャンするなら小さいほうが良いし、大きなテーブルスキャンするならインデックス使ったほうが良いので、駆動表は小さいほうがいいんですね。
ハッシュで結合するのは結合するテーブルが同じくらい大きいサイズのときにも有効ですが、メモリを大量に消費するので注意ともありました。
ぐるぐる系とガツン系
ぐるぐる系というのはいわゆる繰り返し処理(ループ)のことです。
一行ずつレコードを引っ張ってきて簡単な処理する、という行為のことですね。
対してガツン系はCASEやウインドウ関数等を駆使して処理を一回で記述してしまうことです。
シンプルでトランザクションの制御が容易だが、チューニングしにくくパフォーマンスもでにくいぐるぐる系と、チューニングしやすくパフォーマンスもでやすいが、トランザクションの制御やオプティマイザに委ねる部分が大きいガツン系か、そのトレードオフを認識する必要があるとのことです。
もともとSQLはループをできるだけ排除したい、という思想の元で生まれたそうで、手続き型言語になれている人ほど処理を全部自分で書いてループさせがちなので、ガツン系のメリットをしっかり知っておくのが重要です。
インデックスが使えないとき、役にたたない時
「失敗から学ぶRDBの正しい歩き方」のありましたが、
IS NULL
列に直接演算
これらで検索するときはインデックスが使えません。
なぜかというと、インデックスの中に存在する値はあくまでそのセルの中にある値であって、演算後の値でも、NULLでもないからです。
選択率が高い(取り出すレコードがテーブル全体に対して割合が高い)ときも要注意です。
感想
SQLの当たり前に使われている用語に対して説明、考え方が乗っている良書でした。
ウインドウ関数や結合についてはなんとなくで使っていることが多かったので、クロス結合やパーティショニングのカットのイメージから説明してくれる本書はありがたかったです。
また全体を通してトレードオフというものを意識して書かれてあるとも感じました。
それぞれのアルゴリズムや手法にメリットデメリットが書かれていて、それを把握した上で活用するのが大事だということが繰り返し述べられていた印象です。