Haskellメモ: Salesforceでクエリするまで
ここのgistに至るまでのメモ
haskell-salesforce.hs · GitHub
データ定義。ログインに必要なデータ型を定義。
LoginResultとしてセッションIDとかもひとまとめにしておく
data LoginRequest = LoginRequest{ username :: String, password :: String, endpoint :: String, apiVersion :: String } deriving Show data LoginResult = LoginResult{ sessionId :: String, userId :: String, serverUrl :: String, metadataServerUrl :: String, instanceUrl :: String, resApiVersion :: String } deriving Show
クエリリザルトとレコードの型定義。
fromJSONはちゃんと型定義が必要なので、もしライブラリ化するとなるとAccountとかのデータ型は随時ライブラリ利用者側で作ってもらう必要がありそう
data QueryResult = QueryResult{ done :: Bool, totalSize :: Int, records :: [Account] } deriving Show instance FromJSON QueryResult where parseJSON (Object v) = QueryResult <$> v .: "done" <*> v .: "totalSize" <*> v .: "records" parseJSON _ = mzero data Account = Account{ sfid :: String, name :: String } deriving Show instance FromJSON Account where parseJSON (Object v) = Account <$> (v .: "Id") <*> (v .: "Name")
上の書き方だと上の属性から順に定義していくっぽい。JSONの属性名とデータ型の属性名は合わせなくてもOK。
上の属性から順に定義するのがダルかったり、色々手を加えたい場合はdoでいろいろやる
{-# LANGUAGE RecordWildCards #-} instance FromJSON QueryResult where parseJSON = withObject "QueryResult" $ \v -> do records <- v .: "records" totalSize <- v .: "totalSize" done <- v .: "done" return QueryResult{..}
RecordWhileCardsは{..}
っていう記法でrecords=records, totalSize=totalSize, ...
みたいにワイルドカード展開してくれる神機能
Generic使うともっと楽でこれだけでOKっぽい
{-# LANGUAGE DeriveGeneric #-} import GHC.Generics instance FromJSON QueryResult where
Aesonの使い方はここが詳しい→ https://artyom.me/aeson
main部分。IO型は特殊な感じ。副作用あれこれ
main :: IO () main = do
コマンドライン引数はgetArgsで
args <- getArgs
環境変数はgetEnvで
username <- getEnv "SALESFORCE_USERNAME"
- http://hackage.haskell.org/package/base-4.12.0.0/docs/System-Environment.html#v:getEnv
- String -> IO String
なんかデバッグしたい場合はSystem.IO.printで
System.IO.print loginResult
- http://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#v:print
- Show a => a -> IO ()
IOを返すけど戻り値を無視すると print XXX >> fでprint XXX >>= _ -> と等価だった(気がする)
なので連続して実行するのと変わらないって感じ
文字列replaceはそんな関数用意されいないっぽいので自前で
replace :: String -> String -> String -> String replace x y src = inner src where inner [] = [] inner str@(s:ss) | L.isPrefixOf x str = y ++ inner (L.drop (L.length x) str) | otherwise = s:inner ss
isPrefixOfで合致したらその文字列を削る L.drop (L.length)
してその文字列をくっつけて y ++
それを再帰的に実行すればOK
HTTPリクエスト、正直よくわからないけどこんな感じでできた
import Network.HTTP.Conduit import Network.URI import Network.URI.Encode as URI initReq <- parseRequest $ "https://" ++ endpoint ++ "/services/Soap/u/" ++ apiVersion manager <- newManager tlsManagerSettings response <- httpLbs req manager
XMLパースはもっとよくわからない。
let Document _ _ root _ = xmlParse "" $ (BL8.unpack (responseBody response)) cont = CElem root noPos result = xtract id "/soapenv:Envelope/soapenv:Body/loginResponse/result" cont !! 0 sessionId = tagTextContent $ xtract id "/result/sessionId" result !! 0
Data.ByteString.Char8
とか Data.ByteString.Lazy.Char8
とか Data.Text
とかあんまよくわかってない…
とりあえずpackとかunpackとかで変換かけれるっぽい
- http://hackage.haskell.org/package/bytestring-0.10.8.2/docs/Data-ByteString-Char8.html#v:unpack
- http://hackage.haskell.org/package/bytestring-0.10.8.2/docs/Data-ByteString.html#v:unpack
- http://hackage.haskell.org/package/text-1.2.3.1/docs/Data-Text-Lazy.html#v:unpack
あとOverloadedStringsを使うと文字列リテラルでTextやByteStringを定義できるようになって便利。IsStringインスタンスが定義されていれば何でもいける…?
{-# LANGUAGE OverloadedStrings #-}
NamedFieldPuns使うとPerson{name=name} みたいな冗長な記述を排除できて便利
{-# LANGUAGE NamedFieldPuns #-} return LoginResult{sessionId, userId, ...}
URIエンコードするときはNetwork.URI.Encode.encodeで
let queryUrl = instanceUrl lr ++ "/services/data/v" ++ resApiVersion lr ++ "/query/?q=" ++ URI.encode query
Data.Aeson.decodeはMaybe型を返す
let result = JSON.decode body :: Maybe QueryResult
- http://hackage.haskell.org/package/aeson-1.4.2.0/docs/Data-Aeson.html#v:decode
- FromJSON a => ByteString -> Maybe a
正規表現 (他にもライブラリあるっぽいしパーサコンビネータであれこれできるっぽいけどregex-posixを利用)
import Text.Regex.Posix matches = serverUrl =~ ("^(https://[^/]*)/.*" :: String) :: [[String]]
2018-05-10
記事紹介
Adding Automated Speech Recognition for Phone Calls to Ruby on Rails Applications
- TwilioとRailsの連携について
- User(Phone) => Twilio => Rails => Twillio => Phone という流れ
- Rails => Twilioの連携はTwiMLで行う
- twilio-rubyのgemを使うと簡単にTwiMLを生成可能
Rails API and Facebook login (featuring Doorkeeper and OAuth 2 authorization)
- モバイルからのfacebook認証をdoorkeeperを使って実装する
- モバイルがfacebookからaccess_tokenを取得 => doopkeeperにassertionとしてaccess_tokenを投げて認証する、という流れ
- OAuthのassertion grantを使っている(このgrant、初めて知った…)
Writing Less Error-Prone Code
- Timecopはblockを使うとbefore/afterでやるよりはミスが少ない(Dir.chdirとかFile.openでも使われてる)
- 簡潔さより読みやすさ
A Simple Way to Decrease Complexity of Routes in Rails
- routes.rbを分割して管理しやすくする方法
- config.paths['config/routes.rb'] = Dir[Rails.root.join('config/routes/*.rb')]
2018-06-05
記事紹介
Dynamically setting default_url_options in Capybara
- Capybaraでdefault_url_options[:host]や[:port]を書き換える話
- xxx_pathを使うのであんまり問題になったこと無いな…
Good Things Come to Those Who Await
- async/awaitの話
- asyncのfunctionはPromiseを返すの知らなかった…
- asyncなfunctionの外側ではthen, catchが必要
2018-06-01
RubyKaigi 2日目。今日も超絶楽しかった
My way with Ruby
- 圧倒的アウトプット・アクティビティだった…
Faster Apps, No Memory Thrash: Get Your Memory Config Right
Guild Prototype
- Rubyでパラレル処理する機構の話
- GuildはGVLの挙動を変える変更(という理解)。マルチスレッドでもマルチコアを活かせるようになる。
- 今まで: Process -> Thread -> Fiber
- これから:Process -> Guild -> Thread -> Fiber
- Guildというのはプロジェクト名なので実際の名前は検討中らしい
- Actorモデル(スレッドとか意識せずにモデルレベルでメッセージパッシングしていく感じ)
- GuildはGVLの挙動を変える変更(という理解)。マルチスレッドでもマルチコアを活かせるようになる。
- 所感
- マルチコアを使い切りたいというのは数値計算をマルチコアでスケールさせたいという感じの要件から来てるのかなーと思った。webのコンテキストだと、現状でもコンカレントに動いてIOバウンドな処理に対しては実用上問題ないレベルでスケールする気がするし、pumaとかはマルチプロセス・マルチスレッドで動くのでサーバーリソースは今のRubyの仕様でも十分使い切れる気がするので、Guildが入ったからと言ってwebのパフォーマンスが改善させるとかそういう話ではないと思う。
- GVLのロックをかけずにセキュアにマルチコアでスレッドを動かす感じだと思うが(そのためにmutable/imutableなオブジェクトの扱いや共有するか否かのあれこれが必要)、GCの扱いが結構変わりそうだしRubyに入るのはもう少し後になりそうだなーと思った
Ruby Programming with Type Checking
- steepの話
- sorbetとは違うアプローチで、外部ファイルに型情報を定義して、実ファイル内にアノテーションを書く、というスタイルでの型チェック。sorbetは実ファイルにsignature(=コード。コメントではない)を書くスタイル
- 型情報定義面倒そうだなーと思ったけどscaffold的な型定義ジェネレータなコマンドがある(全部、型がanyになるけど…)
RNode with code positions
- ASTのノードの構造体であるRNodeにコードポジション(行とか列とか)を付与したので色々できるようになったよ、という話
- NoMethodErrorでこういう表記ができるようになる(barが無い場合)。ASTのノードにコードポジション付与して、ASCIIでグラフィカルに良い感じに表示ってpower-assertっぽいなーと思った
foo.bar ^^^^
Type Profiler: An analysis to guess type signatures
- 型チェックのアプローチの話
- 1番目: メソッド内で対象の引数に対してAメソッドが呼ばれていたら、対象の引数はAメソッドを持つ引数だ!という感じで引数を推測する方式
- 2番目: メソッド内で a = b + 1という式があったら、B#+(NUM) 戻り値A、となるようなメソッドを持つクラスを探してBとAの型を推定する方式
- 3番目: IntelliJがRubyTypeInferenceというのを進めている。テスト回してメソッドに与えられた引数のクラスとかを見て、メソッドの引数の型を推測する方式。
- TypeDB
- 型定義情報を定義するDB。型チェックはいろんなアプローチがあるが、TypeDB経由で3rd partyの型チェッカを動かせるようにできるように、という構想。例えばTypeDBを使ってsteepの型情報ファイルを生成したり、IDEに型情報をフィードバックして精度上げたり
2018-05-31
今日はRubyKaigi@仙台でした。聞いた内容をざっくり
KeyNote
- 名は体を表す(名前重要)
- 概念を理解していないと名前を付けられない => 響きました…。もっと理解し、考えます。
- Ruby is DeadとかRails is Deadとか言われるのあれなのでもっとコミュニティに貢献していこうと思いました
Reducing Memory Usage in Ruby
- AST, YARVあたりとてもわかりやすかった!
- ブログの記事の内容の発表って感じでした
- allocation_tracerのgemが便利そうだった
- 昔のIssueに同じ内容のがあったってのが面白かったw
TypeChecker
- RubyでTypeチェッカー実装した話
- https://sorbet.run/
- まだ非OSS
- stripeではすでに使われている様子
- 個人的には型を明示的に書くの好きじゃないので型推論のアプローチが好きなんだが実装がとても気になる。OSSになったらぜひコードを読みたいし、ちょっとしたプロジェクトに入れ込みたい。
- パフォーマンスも良さそう
Rubocop
- 最初はregular expressionでやってたらしい
- そっからRipper使って、Ripper辛くなって今はparser使っている
Parser based syntax highlight
1日目まとめ
- 懇親会とかも含めてやっぱりもっとリアルのコミュニティに出て発信するなりコミュニケーションしていかないと損だなーと思った。
- ブログとかそういうオンラインでのアウトプットも必要だけど、やっぱりリアルも大事
- RipperとかParserとかのAST解析してそっから云々するライブラリの仕組み、例えばrubocopとかiroとかの仕組みが全然わからなかったのでもうちょっと勉強した方がもっと面白く聞けたと思う。
- あと英語大事…。リスニング全然できなくてslideを本気で読むマシンになっていた
2018-05-29
記事紹介
TestProf: a good doctor for slow Ruby tests
TestProf II: Factory therapy for your Ruby tests
Ruby 2.4 adds Comparable#clamp method
- clampの紹介。最大、最小を超える場合はその範囲に寄せてくれるらしい。便利っぽい
Running Chrome Headless, Selenium and Capybara inside GitLab CI and Docker
- chrome headlessがdockerで動かないときの対処法について
- sandboxのやつです
kevinrood/json_key_transformer_middleware
alekseyl/niceql
- SQLフォーマッタ
- 超便利そう
Conditional execution with DSL in Ruby
2018-05-28
記事紹介
Actionable Tips to Improve Web Performance with Rails
- Rails(というかHTTPレベルも含めた)パフォーマンス・チューニング話
- resource hintingは使ったこと無いのでちょっと使ってみようかなー
Fetch vs. Axios.js for making http requests
- fetch()とaxiosの違い
- 結構fetchの挙動アレっぽいのでaxios使ったほうがよさげ。json()とか確かにめんどいと思った