Upload
kazuki-minamitani
View
861
Download
5
Embed Size (px)
Citation preview
趣味でやるSmalltalk Webアプリ開発
2014/12/25 南谷千城
片手間の開発
• 勢いのあるうちに
• 短期間でやりたい
ガッと取り掛かって、パッと終わらせたい
もたもたしていると
• 忙しい、飽きる
• 中断、放置
• 忘れる
• 上手く終わらせてもメンテは必要
Webアプリのリプレース
• 2013年• 趣味のRSSアグリゲータ• Grails 2.2
– Groovy– Spring– Hibernate– GORM– GSP– H2 (in-memory)– Rome– Twitter4j
何かと問題はある
• Herokuにタダ乗りするための割り切った設計
–一枚岩
– DB揮発
• 実際はVPSで運用
• TwitterからIPでブロック
– タイムラインにアクセスし過ぎた?
メンテするにも
• 忘れてる
– Grails
• コマンド
• Springが差し込んでくれる部分の約束事
• GORM
• HQL
• フレームワークのアップデートについて行けない
– フルスタックなフレームワークを採用したら一蓮托生• Grails 2.2 -> 2.3 -> 2.4 -> 3.x
Grails以外のフレームワーク
• モデル部分だけでもGroovyの資産を使いたかったが...– GORMにSpringがついてくる
• 候補– Ratpack
• Sinatra like
– Gaelyk• GAE
– Vert.x• Node.js like
じゃあSmalltalkで
• Smalltalkの定番
–大きい、独特
• Seaside (+ GLORP)
–向いてない
• Amber Smalltalk
–開発停滞
• Iliad
–なんとなく敬遠
• AIDA
欲しいもの
• 薄い
• 小さい
• メタメタしない
• 忘れても大丈夫–見ればわかる
–読めばわかる
「バカでも乗れるくらい操縦が簡単で、バカでも扱えるマニュアルつき」なヤツ
趣味なので、観念して自分でやる
Pharo3.0
Zinc Http PostgresV3
SoupTeapot
XML Parser/XPath
Kobati
SoupStock FeedStore
App
Zinc SSO
PharoはWeb開発にもそこそこ使える
• ターンアラウンドは速い– 普通のSmalltalk環境– GUI周りはバグ多め
• 標準ライブラリに同梱– HTTP
• Zinc
– 正規表現• Regex
– 暗号化• Zodiac (SqueakSSL)
– VisualWorks NC...
マルチバイト環境用のパッチは必要
• マルチバイト文字列の操作が遅い
1. String >> at:put:
2. primitive error
3. WideString >> at:put:
• パッチをあてる
– 100倍程度の速度改善• キリがない
String >> convertFromWithConverter: converter
| readStream c |readStream := self readStream.^ WideString new: self size streamContents: [ :writeStream|
converter ifNil: [^ self].[readStream atEnd] whileFalse: [
c := converter nextFromStream: readStream.c
ifNotNil: [writeStream nextPut: c] ifNil: [^ writeStream contents]]].
Teapot
Pharo3.0
Zinc Http PostgresV3
SoupTeapot
XML Parser/XPath
Kobati
SoupStock FeedStore
App
Zinc SSO
Teapothttp://smalltalkhub.com/#!/~zeroflag/Teapot
• マイクロWebフレームワーク– Sinatra(Ruby)、Flask(Python)、Ratpack(Groovy)
• HTTP method
– GET、POST、PUT、DELETE、etc...
• URL pattern
– ルーティング
• Action
– レスポンスの組み立て
| teapot | teapot := Teapot configure: { #port -> 8080. }. teapot
GET: '/cat/<who>/<name>‘ -> [:req | (req at: #who) , ' は ' , (req at: #name) , ' である']; start.
Soup
Pharo3.0
Zinc Http PostgresV3
SoupTeapot
XML Parser/XPath
Kobati
SoupStock FeedStore
App
Zinc SSO
Souphttp://smalltalkhub.com/#!/~PharoExtras/Soup
• HTMLをDOMで扱うためのライブラリ
–どちらかと言えば、スクレイピング向け
– Beautiful Soup(Python)、JSoup(Java)
• .htmlをそのままテンプレートとして使用
– Thymeleaf(Java)、Mixer2(Java)、Kwartz(Ruby)
–見えている通り
–デザイナとの連携容易
SoupStock
Pharo3.0
Zinc Http PostgresV3
SoupTeapot
XML Parser/XPath
Kobati
SoupStock FeedStore
App
Zinc SSO
SoupStockhttp://smalltalkhub.com/#!/~kaminami/SoupStock
| server |SsServer stopAll.server := SsServer configure: {
(#port -> 8080).(#debugMode -> true).(#templateRoot -> './stock/template/').(#staticRoot -> './stock/static/').
}.
server registerStaticFileServer. "GET: '/static/*' -> [ :req | handle static file request ]. "server GET: '/example01' -> [ :handler | handler simpleReplace ] handler: #SsExample.server GET: '/example02/<a>/<b>' -> [ :handler :a :b | handler replaceA: a andB: b ] handler: #SsExample. server GET: '/example03' -> [ :handler | handler replaceList ] handler: #SsExample.
server start.^ server
• Teapot + Soup + Fuel
– htmlの断片をストック、切り貼り
SoupStockhttp://smalltalkhub.com/#!/~kaminami/SoupStock
SsExample>>simpleReplace| soup |soup := self soupAt: 'example01/simpleReplace'.
(soup findTagById: 'title') replaceContents: thisContext printString.(soup findTagById: 'willBeReplaced') replaceContents: 'Replaced!!!'.
^ soup
<!-- ./stock/template/example01/simpleReplace.html --><html><head>
<title id="title">title</title><link href="/static/css/sample.css" rel="stylesheet" type="text/css" />
</head><body>
<span id="willBeReplaced">willBeReplaced</span></body>
</html>
Kobati
Pharo3.0
Zinc Http PostgresV3
SoupTeapot
XML Parser/XPath
Kobati
SoupStock FeedStore
App
Zinc SSO
Kobatihttp://smalltalkhub.com/#!/~kaminami/Kobati
• ORマッパー
– SQL文とオブジェクトのマッピング
– SQL! SQL! SQL!
–モデルクラスはプレーンなままで良い
• Mutatorメソッドだけは必要
• MyBatis (Java, .NET)
• PostgreSQL専用
– PostgresV3
<mapper id="feedstore-mapper">
<select id="selectEntriesByTag" selector="selectEntriesByTag:limit:" resultMap="entryWithFeedMap" arguments="tag, limit">SELECT
e.id AS entry_id, e.title AS entry_title, e.link AS entry_link, e.summary AS entry_summary, e.issued AS entry_issued, f.id AS feed_id, f.name AS feed_name, f.feed_url AS feed_url, f.site_url AS site_url, t.id AS tag_id, t.tag AS tag_tag
FROM Entry AS e
LEFT OUTER JOIN Entry_Feed AS e2f
ON e.id = e2f.entry_idLEFT OUTER JOIN
Feed AS fON f.id = e2f.feed_id
LEFT OUTER JOIN Tag_Feed AS t2f
ON f.id = t2f.feed_idLEFT OUTER JOIN
Tag AS tON t.id = t2f.tag_id
WHERE(LOWER(t.tag) IN (#{tag}))
ORDER BYe.issued DESC
LIMIT #{limit}</select>....
<resultMap id="feedMap" type="FsFeed"><id property="id" column="feed_id" /><result property="name" column="feed_name" /><result property="feedUrl" column="feed_url" /><result property="siteUrl" column="site_url" /><collection property="tags" type="Set" ofType="FsTag" >
<id property="id" column="tag_id" /><result property="tag" column="tag_tag" />
</collection></resultMap>
<resultMap id="entryWithFeedMap" type="FsEntry"><id property="id" column="entry_id" /><result property="title" column="entry_title" /><result property="link" column="entry_link" /><result property="summary" column="entry_summary" /><result property="issued" column="entry_issued" /><association property="feed" resultMap="feedMap" />
</resultMap>
selectEntriesByTag: tagStr limit: limit^ FsFeedStore dbAccessor execute:
[:session || mapper |mapper := session getMapper: 'feedstore-mapper'.mapper selectEntriesByTag: tagStr limit: limit]
FeedStore
Pharo3.0
Zinc Http PostgresV3
SoupTeapot
XML Parser/XPath
Kobati
SoupStock FeedStore
App
Zinc SSO
FeedStorehttp://smalltalkhub.com/#!/~kaminami/FeedStore
• フィードの取得、管理
• RSS1.0, 2.0、Atom1.0に対応
• Zinc-ZnEasyでフィードをGET
• XPathで適当にパース
• Kobatiで保存
Pharo3.0
Zinc Http PostgresV3
SoupTeapot
XML Parser/XPath
Kobati
SoupStock FeedStore
App
Zinc SSO
• 登録済みアプリからの投稿だけなら容易
• Zinc-SSO
– Zincのアドオン
–シングルサインオン対応
TwitteraccessTokenDic := Dictionary new
at: 'oauth_token' put: 'YOUR-AUTH-TOKEN';at: 'oauth_token_secret' put: 'YOUR-AUTH-TOKEN-SECRET';yourself.
accessToken := ZnOAuth1Token newFromDictionary: accessTokenDic.
data := ZnOAuth1ConsumerData newForTwitterconsumer: 'YOUR-CONSUMER'; consumerSecret: 'YOUR-CONSUMER-SECRET'; yourself.
service := ZnOAuth1Service new providerAccount: data ; yourself.
userAccess := ZnOAuth1TwitterUserAccess new oauth1Service: service; accessToken: accessToken;yourself.
userAccess statusesUpdate: 'yah!!'.
それなりに使える。はず。
Pharo3.0
Zinc Http PostgresV3
SoupTeapot
XML Parser/XPath
Kobati
SoupStock FeedStore
App
Zinc SSO
おわりに
• ビジネスには怖いが、趣味ならOKな分野
– Pharo(Squeak)にはそんなものばかり
• 教育向けにはかなり良い
• 絶賛開発中
– SmalltalkHub
• http://smalltalkhub.com/#!/~kaminami