12/11(水) に目黒のリブセンスさんの勉強会スペースにて「個人開発がやりたくなるLT」を開催しました。
当日は出席率も高く40人近くの人が来てくれて、LT も個人開発に関するいろんな知見(オフレコ含む)を共有できたと思います!
懇親会もかなり盛り上がって、コミュ障な僕でも楽しく話せたのがすごく嬉しかったです。発表者&参加者の皆さんには本当に感謝です。
この記事では LT会の参加レポートと運営者としての思いを書いて行きたいと思います!
今回の勉強会のコンセプトは、
でした。
特に勉強会での心理的安全性を大切にして、個人開発者が自分がやっていることを気軽に相談・共有できる知り合いができる場を作りたいと思い勉強会を開催しました。
個人開発の目的は作者さんによって様々だと思いますが、特に収益化や専業を目指している開発者(僕含む)にとっての課題は「個人開発に適した登り方が体系化・言語化されていないこと」だと感じています。
サービス開発の「集客・収益化」のためには開発だけでなく、企画やデザイン、営業、分析、ディレクション等々様々な「専門性」が必要になります。
また、個人開発者の強みやプロダクトのターゲットによって「スタートアップ的なベストプラクティス」を生かしやすい部分と、異なる手法をとったほうが良いことがあると感じています。
僕はWebエンジニアとしては食べていけていますが、プログラミング以外についてはやはりプロレベルではなく、上記のような要素をどうやりくりしていくかを常に四苦八苦しています。
この解決策の一つの可能性として勉強会をメンバーと一緒に企画して行きました。
今回の勉強会での発表スライドです。(ごめんなさい、今回は発表したスライドがあるものだけに限定させてください)
技術同人誌の『個人開発がやりたくなる本』の企画・編集などをメインで担当した @yuzutas0 さんの発表スライドです。
書籍でも個人開発でも同じように、マーケットサイズを把握して顧客への提供価値を整理し、需要の仮説をたて、アジャイルで書籍を組み立てていく点など、個人開発でもそのまま活かせる知識が満載でした!
『最新巻.jp』を運営している @bakunyo さんの発表スライドです。
個人開発におけるデータを高く的に分析して、いろんな角度から個人開発でモチベーションを保つために必要な振り返りをしています。特に1ヶ月間の時間の使い方をTogglでとって集計しているところは流石です!!
テレビでも紹介された『My感謝日記』などのアプリを作られている @kara_mage さんの発表スライドです。
30代のエンジニアなら誰しも感じる(?)「何者にもなれないやも」という漠然とした不安感みたいなことを言語化してくれた素晴らしいスライドでした。年齢やキャリアがすごく近かったのでめちゃくちゃわかる!!ってなりました(笑)
イベントの開催日当日にイベントの存在を気づいてくれて唯一空いていた LT 枠に応募、当日『個人開発がやりたくなる本』
を買って読んでくれてさらに発表資料まで作ってくれた @moyashidaisuke さんの発表スライドです。
モチベーションのキープに「もくもく会」や「個人開発者が参加するSlack」に参加したり、MVPを小さく作り始める、時間のコントロールに「習慣化」を活用するなど役立つ Tips を共有してもらえました!
余談ですが、個人開発者が参加する Slack は『運営者ギルド』がすごくおすすめです。活発に使われてたり、参加者の皆さんがめちゃくちゃ雰囲気良かったりします!
自分の発表スライドです!過去15年で4回くらい同じコンセプトのプロダクトを作りながら、試行錯誤を繰り返してきた「しくじり談」です(笑)
このスライドで言いたかったことは改めてブログ記事で言語化していきたいなぁと願望は持ってますww
余談ですが、GMOペパボの創業者でCampfireやBASEなどにも関わられてきた家入一馬さんのインタビュー記事「家入一真の履歴書|ひきこもり、起業、上場、大赤字。天国と地獄を経験して分かった「失敗」の本質」はめちゃくちゃ良いのでおすすめです!
一番嬉しかったのは懇親会がすごく盛り上がったことと、Twitter で良い知見が共有できたことと、懇親会が話しやすかったと言ってもらえたことです。
企画をした時のコンセプト・課題の設定は悪くなかったと思いますし、解決策のトライとしては良い結果を得ることができたと感じています!
これは参加してくれた全ての人のおかげだと思いますし、勉強会・懇親会のスペースや懇親会費用を負担していただけた リブセンス さん、LTや運営をお願いできた @tchikuba 、 @sugaishun らのおかげだと思っています!
エンジニアのカルチャーに対する理解が圧倒的な リブセンス さん、最高です!!!
「個人開発がやりたくなるLT」の2回目をぜひやりたいと思っています!もし、次回参加してもいいよという方がいたら、良かったら connpass のグループ に参加してもらえると嬉しいです!!
あと5-10分くらいで LT やってもいいよと言ってもらえる個人開発者の方がいたら @zyunnosuke までお気軽に DM ください。
僕の考えですが、個人開発の課題の一つが専業として食べていくまでの「登り方のベストプラクティス」が体系化・言語化されていない点だと思っています。
なので、これからいくつかの記事を通して、3年半取り組んできた「個人開発に対する考え」について少しでもその言語化にトライしてみたいと思っています。
個人開発は「現代の自営業」だと、 yarukinai.fm でスガイさんが言ってたけど本当に良い言葉だなぁと思っています。
(八百屋さんのような自営業のように、もっとカジュアルにエンジニアで「個人開発」を専業にする人が増えていくと良いなという意図です)
今回は個人開発が現代の自営業としてなぜ「おすすめ」かというのを資本主義や日本の市場という視点で書いてみたいと思います。
フルタイムでスタートアップで働きながら、平日の朝晩と週末はひたすら(3年ちょっと)個人サービスを作ってるWebエンジニアです。
サービス名は諸般の事情であかせませんが、月50万PVで収益で個人開発だけでも食べていけるくらいの収益が出せてたりします。
もしサービス内容とか詳しく聞いてみたい人いたら、12/11(水)に目黒の勉強会で LT しますので遊びに来てください。
株式会社は根本的には「株主」のものであり、経営者からみてエンジニアは最終的にはコストでしかないと実体験として感じました。
企業は利益を追求し始めた時に、そのコストを適正化しようとします。そうすると「エンジニアの給与は市場から見てバランスの良い額」に収束してしまうのかなと思っています。
別の視点として、高い技術力が競争力になるサービスと技術力以外が競争力になっている分野・企業があります。
技術力以外が競争力になっている分野は、どうしても「エンジニア」に高いコストをかけることは難しいと感じています。
GAFA のような企業は技術力が競争力の源泉になり、世界中の有能なエンジニアにアピールして世界中の才能を集めています。また一部の輝いているスタートアップは「将来の市場からの利益の皮算用」を示すことで、例外的にエンジニアに対して高い給与を払えていると感じています。
業界全体で見ると「エンジニア」の待遇は少しずつよくはなっていますが、技術のコモディティ化とともに、ずっとキープできるわけではないと思います。
景気の状況やコモディティ化とともに、少しずつ収束に向かっていくことは避けられないと思っています。
(もちろんその人にしか出せない希少な価値や、その人がいないと潰れる状況を経営陣に認められれば、例外的な給与をもらえる人もいると思います)
自分のコミットしている事業へのオーナーシップが大きければ大きいほど、事業からのリターンを得やすくなると思っています。
別の考え方として、少ない資本しかない人が、期待するお金を手にするためには「ギャンブル」、つまりリスクを取っていくことが有効な手段になります。
その意味で、オーナーシップを持てる個人開発には全員が必ず成功するわけではないが、自分へのリターンを得やすい形だと思っています。
言い換えると、未来の継続的な収益に対してベットするのが、収益化を狙った個人サービスの開発だと思っています。
一生の中で限られた「時間」を自分の事業に対してベットして、結果に責任を持つ事は企業で働き続けるとしても視座を上げられるよい経験だと思っています。
また副業からの収入を得ることで、副次的に本業でもリスクを取りやすくなると感じています。
例えば、PMFしていないスタートアップはリスクが大きいが、大きく成長する可能性があり、SOが多く頂ける傾向があったりします。(あくまで傾向ですが、頑張って交渉していきたい!)
副業での収入的なサポートがあれば、心理的にそういったギャンブルにトライしやすくなると感じています。
少し悲しい話として、日本は若い年齢層の人口が減っており、これから市場全体が縮小傾向に向かうことは避けられません。
市場が縮小すると、ほとんどの企業がマイナス成長になってしまうのではと思っています。
株主に「成長」をコミットしているということは「企業の利益を増やす」「市場を独占して未来の利益を確約する」ということです。
市場が縮小していく中で、それを実現するには「競合との競争」をするか、「合併して価格競争を避け、資本効率をよくするか」の二択になると思ってます。
昨今ニュースを賑わしているような合併や買収はこれから、大なり小なり加速していくのではと思っています。
話を個人開発に戻すと、企業の規模が大きくなっていく中で「規模の大きな企業」の形態が手を出しづらい領域はたくさんあると思ってます。
例えば売り上げ数十万から数千万円前半の市場であれば、企業が参入するメリットが小さいので、放置されがち。
個人で自分のリソースを使う部分ではこのくらいの市場なら参入しやすく、競合が少ない状態にあるのでトライできることはある。
イノベーションは少しリスクをとるところから始まるという話があります。個人開発はリスクを取りやすいので、そこが差別化であり、競争力の源泉になると思っています。(リスクは犯罪という意味では全くないです。法律の中で少しだけ人に役立つものを作れるかだと思っています)
ネットを前提としたソフトウェアの強みは「グローバル」のお客さんを相手に価値を届けられることだと思っています。
強みはお客さんが「1人」でも「1万人」でもかけた開発コストに対して価値を届けることはほとんど同じということはすごい特徴だと思っています。
同じサービスを作るなら、グローバルを相手にできる「ソフトウェア」は個人開発にとってもすごく良いマーケット。
ターゲットを絞って、シンプルな「価値」を顧客に提供することが大切だと思います。1億人より60億人。
Web 企業が沢山ある以上、資金をかければそれ以上のリターンが上がりやす市場だし、個人開発が狙いやすいマーケットは沢山眠っていると思います。あとは個人開発者にとっての「登り方のベストプラクティス」がうまく共有されていくと良いなぁと思ってます。
それは僕だけでは難しいですが、これから少しずつでも自分の試行錯誤の経験談、自分なりの考えを言語化して投稿していきたいと思います。
主語が大きかったり、対象を正しく定義していなかったりするので、不快に感じたら教えてください。直します。
]]>rbenv, rvmの両方に対応しました。オリジナルのcapコマンドの作り方も書きました!
今回の環境では次のような環境を想定しています。
* DB: PostgreSQL |
デプロイ先のDBの接続用ユーザーとデータベースを作成するために、次のコマンドをstaging環境とproduction環境で実行します。
こちらは、PostgreSQLを使う場合の手順です。
# postgreユーザーになる |
Gemfile
に次のGemを追加して、bundle install
を実行。
group :deployment do |
次のコマンドでCapistranoの設定ファイルdeploy.rb
らを生成。
bundle exec cap install |
Capfile
を次のように変更します。
# Load DSL and set up stages |
まず、共通のデプロイ情報をdeploy.rb
に記入していきます。
lock '3.9.1' |
次に環境ごとに異なる設定をconfig/deploy/staging.rb(production.rb)
に記述していきます。
set :branch, 'master' |
サーバ側のcronの設定をコードで簡単に管理できるwheneverは、『Wheneverは導入が超簡単なcrontab管理ライブラリGemです![Rails4.1]』を参考に設定!
本番環境へのデプロイは次のように実施します。
bundle exec cap production deploy:initial |
デプロイに必要なディレクトリを生成して、デプロイを実行。
# productionへのデプロイ |
capコマンドの一覧を表示したい場合は次のコマンドを実行します。
bundle exec cap -T |
set :branck, 'マスタ'
の部分を次のように書き直し。
set :branch, `feature/xxx` |
namespace :devops do |
driver = Selenium::WebDriver.for :chrome |
Chromeを開くなら:chrome
、Firefoxを開くなら:firefox
、Internet Exploreを開くなら:ie
です。
driver.get 'http://www.yahoo.co.jp' |
WebDriver
はロードが完了するのを待たないので必要に応じて待ち時間を設定してください。
find_element
:最初に見つかった要素を返すfind_elements
:見つかった要素すべてを返す# IDが一致する要素を返す |
driver.find_element(:id, 'some_id').text |
driver.find_element(:id, 'some_id').attribute('class') |
# ボタンやリンクをクリックする |
キーボードでenter
を実行する場合はこちら。
driver.find_element(:id, '#q_name').native.send_keys(:return) |
# チェックボックス/ラジオボタンを選択する |
# セレクトタグの取得 |
# 1件以上あれば要素が存在すると判定 |
driver.execute_script('return window.location.pathname') |
特定の要素が表示されるまで10秒を上限にwait(待ち時間を設定)ができます。
wait = Selenium::WebDriver::Wait.new(timeout: 10) |
find_elementのタイムアウト時間はimplicit_wait
で設定できます。
driver.manage.timeouts.implicit_wait = 10 |
Capybaraの場合はcssが表示されるまで待ってくれますが、Seleniumの場合は表示されるまでは待ってくれないので注意して使ってください。
window_titles = driver.window_handles.map do |w| |
a = driver.switch_to.alert |
スクリーンショットの取得方法です。private APIのため動作保証はありませんのでご注意ください。
driver.save_screenshot('path/to/filename.png') |
職場のテストエンジニアさんに参考になる意見をたくさんいただきました。ありがとうございます!
]]>...
」を付ける縦横比が正方形でない画像から正方形の画像を切り出す手順です。HTML側は次のようになります。
<div class="square-box"> |
CSS側は次のようになります。
.square-box { |
...
」を付ける複数行の文字列を一定のサイズで区切ってそれ以降は「…」を付ける方法です。まずHTMLは次のようになります。
|
CSS(SCSS)側は次のようになります。
// Variables |
レスポンシブデザインで左右の要素がある場合に一方を固定サイズで指定して、もう一方の要素のサイズをデバイスに合わせて可変にしたい場合は次にように記述します。
.left { |
フォームのinputタグの中のPlaceholderの文字色を変えたい場合は次のように記述します。
::-webkit-input-placeholder { /* Chrome/Opera/Safari */ |
text-transform
CSSでテキストの大文字、小文字等を制御するのがtext-transform
です。
オプション | 説明 |
---|---|
none | 記述したとおりに表示 |
capitalize | 単語の先頭文字を大文字で表示 |
lowercase | すべてを小文字で表示 |
uppercase | すべてを大文字で表示 |
出力は次の用になります。
# 指定なし(none)の場合 |
Appleのスマートフォンサイトのような横スクロールのナビゲーションバーのサンプルです。
<nav class="nav-container"> |
.nav-container { |
上記とほぼ同じですが、折り返しがあるとうまくいかないパターンがあったので念の為メモ。
<div class="text-container"> |
.text-container { |
PostgreSQLについて丁寧な解説がされているスライドです。PostgreSQLの実行計画を理解するのにすごく参考になりました!
SQLのCREATE INDEX
でIndexを作成できます。
-- レコードがユニークではないインデックスの場合 |
EXPLAIN
DBはSQLクエリを解析して、最も効率のよい問い合わせ計画(実行計画)を作成し、エグゼキュータがデータを取得します。
SQLにEXPLAIN
を付けるとDBの作成した実行計画を取得できます。
EXPLAIN SELECT |
DBのパフォーマンスを上げるためには、データの性質によって最適な実行計画を立てるようにインデックスで手助けすることが必要です。
EXPLAIN ANALYZE
実際の実行結果等の取得はEXPLAIN ANALYZE
を使います。
EXPLAIN ANALYZE SELECT |
実行時間や、検索した行数、ループの回数を取得できます。
EXPLAINコマンドで出てくる項目の概要です。
オプション | 説明 | 既定値 |
---|---|---|
seq_page_cost | シーケンシャル読み込み1回 | 1.00 |
random_page_cost | ランダム読み込み1回 | 4.00 |
cpu_tuple_cost | 行の処理1回 | 0.01 |
cpu_index_tuple_cost | インデックスの処理1回 | 0.005 |
cpu_operator_cost | 計算1回 | 0.0025 |
work_mem
の大きさをもとに最適なプランを推定する分類 | 演算子 | 概要 |
---|---|---|
テーブルスキャン | Seq scan | インデックスを使用せず、テーブルを最初から最後までシーケンシャルにアクセス。候補行が多い場合に有効 |
Index scan | インデックスを使用してスキャン。候補行が少ない場合に有効。ランダムアクセス | |
Bitmap scan | ビットマップを使用してスキャン。インデックスを使ってBitMapを作りORやAND演算に利用する | |
Index only scan | 問い合わせがインデックスに含まれるカラムのみでのスキャン。テーブルにアクセスしないので高速だが、Visibility Mapが有効でないとだめ | |
Tid scan | 検索結果がタプルID(ctid)のスキャン。ctid(行の物理的位置)を条件に設定した場合に有効 | |
その他スキャン | Function scan | 関数がデータを収集した結果をスキャン |
テーブルの結合 | Nested Loop | Nested Loop結合を行う(後述) |
Merge Join | ソート・マージ結合を行う | |
Hash Join | ハッシュ結合を行う | |
検索結果に対し作用 | Group | GROUP BY |
limit | LIMIT 、OFFSET | |
Uniq | DISTINCT | |
Aggregate | COUNT 、SUM 、MAX … | |
Group Aggregate | 集合関数にGROUP BY を適用してより大きな結果セットを得る | |
Result | 非テーブルの問い合わせ | |
Append | UNION | |
SetOp | INTERSECT (積)、EXCEPT (和) | |
そのほかの処理補助 | Sort | 明示的なORDER BY 、暗黙的なSort-Merge Join など |
プランナーの中身の動作がわかりやすくまとまっています。
GROUP BY
やDISTINCT
NULL
値を含まないので注意PostgreSQL 9.6対応の200ページ以上の電子書籍的なドキュメント。説明も丁寧でわかりやすいです。
PostgreSQL_Internals_1_for_PostgreSQL96_ja_20170211-1.pdf
Immutable.jsはimmutableなデータ構造を扱うJavaScriptのライブラリです。
const { Map } = require('immutable') |
ReactにImmutable.jsを導入することで変更ロジックをモデルの中にまとめ、保守性を向上できます。
<PostEditor |
Immutable.jsのない通常のReact Component:
class PostEditor extends React.Component { |
this.props.onChange
のメソッドでstateの更新を行います。
Immutable.jsを含んだコード:
import { Record, List } from 'immutable'; |
新しいオブジェクトを生成して、それを返り値にします。ロジックがImmutable.jsのコード内に集約されるのでテストが見通しが良くなります。
Immutable.jsでよく使う型のチートシートです。
Record
型はJSのObjectにフィールドとそのデフォルト値がついたしくみです。
// 新規オブジェクトの生成 |
Map
型はkeyとvalueのセットの構造です。
const map1 = Map({a: 'hoge', b: 'fuga'}); |
List
型の基本的な使い方は次のとおりです。
// List 生成 |
日本語対応のChromeの入ったDockerfile
は次のように記述します。
FROM ubuntu:latest |
docker-compose.yml
は次のように記述します。
version: '3' |
あとはdocker-compose build
でイメージを作成します。
puppeteer
をインストールします。
yarn init |
package.json
に以下を追加します。
"scripts": { |
ブラウザを操作するpuppeteer.js
はこちら。
const puppeteer = require('puppeteer'); |
作成した結果を試してみます。
# bashを起動 |
これでブラウザの操作結果のスクリーンショットを取得できます。
GitHubとDocker Hubにファイルを公開しました。
フォームのselect
タグを選択する方法です。
await page.evaluate(() => { |
タイムアウトを長くしたい場合は、page.goto
のオプションでtimeout
を指定すると良さそうです。
await page.goto('https://example.com/', { timeout: 3000000 }); |
スクリーンのサイズは次の設定で指定できます。できるだけメモリ消費を抑えるために小さめで設定した方が良さそうです。
page.setViewport({ width: 320, height: 640 }); |
スマートフォンやタブレットのブラウザをエミュレートすることも比較的容易です。次のように記述します。
const puppeteer = require('puppeteer'); |
Dockerがデフォルトで準備する一時ファイル領域は64MBで、一般的な装飾のサイトではこの容量を超えてしまい、Page crash
することがありました。docker run
コマンドの場合はオプション--shm-size=256m
、docker-compose
ファイルの場合はshm_size: 256
を指定すると良さそうです。
Chromeのアウトプットを標準出力煮出す場合はこちら。
const browser = await Puppeteer.launch({ dumpio: true }); |
Array
)の基本的なメソッドを知らずに詰まることがあったので、よく使うものを整理してみました!push
push()
で配列に要素を追加できます。
var sports = ['soccer', 'baseball']; |
forEach
forEach()
は配列の要素を順番に処理していきます。
var result = []; |
map
map()
を使うと各要素に対して、特定の処理を実施できます。返り値は処理を実行した結果の配列です。
var array = ['a', 'b', 'c', 'd']; |
配列の中の特定の要素だけ、別の要素に置き換えるのは次のように記述します。
var arr = [1, 2, 3] |
filter
filter
は配列で特定の条件を満たす要素を選別する(フィルタリング)を行うメソッドです。
var array = [0, 1, 2, 3]; |
find
find
は配列内の要素から条件を満たす最初の要素を取り出すメソッドです。
[12, 5, 8, 130, 44].find(el => el >= 15); //=> 130 |
findIndex
findIndex
は配列から条件を満たす要素のインデックスを取得するメソッドです。対象の要素がみつからない場合は-1
を返します。
[4, 6, 8, 12].findIndex(e => e === 1); //=> -1 |
reduce
reduce
は配列の隣り合う要素に対して(左から右へ)同時に関数を適用して単一の値を返すメソッドです。
var array = [1, 2, 3, 4]; |
unshift
unshift()
メソッドは、配所の最初に要素を追加するメソッドです。
返り値は、新しい配列の長さです。
var arr = [1, 2]; |
join
join()
メソッドは配列の要素を結合して文字列を返すメソッドです。
var fruits = ["Banana", "Orange", "Apple", "Mango"]; |
some
some()
メソッドは配列のどれかが条件を満たせばtrue
を返すメソッドです。
function isBiggerThan10(element, index, array) { |
concat
concat()
メソッドは2つ以上の配列を結合させて新しい配列を生成します。
var arr1 = ['a', 'b', 'c']; |
slice
slice()
メソッドは配列から一部を切り出すことができます。
var a = ['zero', 'one', 'two', 'three']; |
1つ目の引数がスタート位置で、2つ目の引数が終了位置です。終了位置の要素は含まないのを注意してください。
配列の要素の重複を排除してユニークにするには次のように記述します。
array = [1, 2, 3, 1] |
Arrayの要素の中で特定の要素を除去(削除)するには次のように記述します。
var inArray = ['one', 'two', 'three']; |
配列からかどうかを判定するのは次のコードです。
var array = []; |
Arrayのメソッドではないですが、2次元配列 => 1次元配列のように配列をフラットにするコードです。
var array = [ |
null
やundefined
を削除する配列からnull
やundefined
をfilter
メソッドを使って削除する手順です。
var arr = [1,2,,3,,3,null,,0,,undefined,4,,4,,5,,6,'',false,,]; |
1
からN
までの配列を生成するのは次の方法です。
Array.from({length: 5}, (v, k) => k+1); //=> [1,2,3,4,5] |
配列の最後の要素を取得する方法です。1つ目はシンプルなgetterメソッドを作る方法です。
if (!Array.prototype.last){ |
Getterメソッドなどを作らずにslice()
メソッドで対応する場合はこちら。
[1, 2, 3].slice(-1)[0] //=> 3 |
break
var arr = [1, 2, 3, 4, 5]; |
Array内に特定の要素が含まれているかどうかを判定するにはinclude()
メソッドを使います。
var a = [1, 2, 3]; |
rancherの管理画面や立ち上げたDockerのインスタンスなどを管理する「rancher server」とユーザーが設定したアプリケーションが起動する「rancher agent」によって構成されます。
Rancherで使われている単語や定義を簡単に説明します。
用語 | 説明 |
---|---|
Service | 用途別のコンテナをグループ化。web, application, dbなど |
Stack | Serviceのグループ化。一連のアプリケーションを関連付け。ひとつのサービスやシステムなど |
Environment | 環境。Production, Staging, Developmentなどの使い分け |
「Vultr」にiPXEブートでRancherOSを起動します。
#!ipxe |
「[初回ログインパスワード]」にはパスワードを記入します。
インスタンスが立ち上がったら、sshでサーバにログインします。パスワードはさきほどiPXEブートのスクリプトに記述した物を使います。
ssh rancher@[サーバのIPアドレス] |
ログインしたらcloud-config.yml
を作成します。
#cloud-config |
次のコマンドでRancherOSをインストールします。
sudo ros install -c cloud-config.yml -d /dev/vda |
次にrancher serverをインストールします。
mkdir ~/data |
dockerのプロセスが立ち上がったら、ブラウザからhttp://[サーバのIPアドレス]:8080/
にアクセスします。
rancher serverのメニューのADMIN > Access Control
からGitHubログインでのアクセス制限を行います。
まずはGitHubの「Profile > Developer settings > OAuth Application > Register a new application」を選択します。
Client ID、Client Secretを使ってGitHubログインを行うように設定します。
Rancher severのメニューの「Infrastracture > Hosts > Add Host」でホストを追加します。Vultrは初期メニューには出てこないので「Manage available machine drivers」からVultrを追加します。
入力項目は次のとおりです。
項目 | 説明 |
---|---|
Name | 任意の名前を入力 |
apiKey | Vultrのコンソール > Account > APIかはら発行できます |
endPoint(URL?) | https://api.vultr.com/ |
osId | 初期設定のまま、RancherOSが起動 |
planId | 202 (2GBメモリプラン)などを選択。https://api.vultr.com/v1/plans/list に一覧 |
priavateNetWorking | 「オン」Private Networkに入れるかどうか |
pxeScript | そのまま |
regionId | 25 (Tokyo)などを選択。https://api.vultr.com/v1/regions/list に一覧 |
sshKeyId | 本来はSSH keyのID。私が試したときにはうまくうごいてくれなかった |
入力してしばらく待つとHostが起動してくれます。
あとは「Stacks > User > Add Stack」からお望みのアプリケーションのdocker-composeをstackから登録するだけです。今回はTrelloクローンのwekanを立ち上げてみます。
version: '2' |
Dockerコンテナ内でshellコマンドを実行したり、Rakeタスク等を実行するための手順です。
Rancher OS内でコマンドを実行する場合はdockerベースでコマンド実行するのが良さそうです。
# git |
Rancher CLIコマンドを追加します。まず以下を~/.zshrc
か~/.bash_profile
に登録します。API KeyなどはRancherの管理画面から取得できます。
alias rancher="docker run -ti --rm -v $(pwd):/rancher rancher/cli" |
rancherコマンドは「Rancher CLI commands」で確認できます。
Rancherのスタックでcronで一定期間毎に実行するにはまず、Rancher管理画面のCATALOGから「Rancher Container Cron」を追加します。
そしてstackのdocker-compose.yml
に次の内容を追記します。
version: '2' |
rancher-comose.yml
は次のように記述します。
version: '2' |
これでrancher up -d
でデプロイすると定期的にスクリプトを実行できます。(動いていない間は停止します)
cronの設定はわかりやすい設定の説明です。
Tipsですが、cronを1分、6分、11分のように少し切りの悪いタイミングで動かす場合は次のように記述します。
01-59/5 * * * * command |
「Scaleway」でサーバを借りてRancher Agentをインストールするまでの手順です。
ScalewayのDockerイメージは少し古いDockerが入っているので、Dockerをバージョンアップしたうえで、Rancher Agentを導入しましょう。
sudo apt-get autoremove --purge docker-engine |
docker.conf
に次の内容を記述します。
[Service] |
変更を反映します。
# 変更の反映 |
ssh rancher@IPアドレス
でログインReact.jsが扱うUIのstate(状態)を管理するためのフレームワークです。React.jsで複雑なアプリケーションを作るとstateの変更箇所が複数に分散(componetが複数箇所で保持)して、管理が困難になるのを解決するためです。
Action
Action
は「何をする」という情報をもつオブジェクトですAction
はstore.dispatch()
で store を変更する Reducer へ送られますtype
プロパティを必ず持ちますconst ADD_TODO = 'ADD_TODO' |
ActionCreator
ActionCreator
はAction
を生成するメソッドです。
function addTodo(text) { |
dispatch
するときはActionCreator
で作成したaction
を渡します。
dispatch(addTodo(text)) |
もしくはdispatch
までを行うActionCreator
を準備する方法もあります。
const boundAddTodo = (text) => dispatch(addTodo(text)); |
reduxのbindActionCreators()
を使う方法もあるようです。
import { Component } from 'react' |
同期・非同期の ActionCreator をつくるTipsを別記事に書きました。
Store
Store
はアプリケーションの状態(state)を保持する場所です。Store
の役割は次のとおりです。
getState()
メソッドでstateにアクセスを許可するdispatch(action)
メソッドでstateを更新するsubscribe(listener)
メソッドでリスナーを登録できるtodo
のサンプルのstateは次のようになります。
{ |
Store
には次のルールがあります。
Store
は1つのみとし、State
は単独のオブジェクトとしてStoreに保持する必要がありますstate
を直接変更することはせず、Store
へdispatchすることでしかstate
は変更できませんReducer
Reducer
はAction
とstate
から「新しいstate
」を返すメソッドですAction
のtype
プロパティに応じて処理を書く必要がありますstate
」はstore
に保持されますstate
にはUIの内容を入れないようにするのが推奨されますfunction todos(state = [], action) { |
Reducer
には次のルールがあります。
Reducer
は現在のstate
とaction
を受けて新しいstate
を返すだけの純粋なメソッドとしますProviderの目的は次の2つです。
connect
を使えるようにすることReduxのStoreがReactにアクセスするための関数。
// index.js |
// App.js |
macOSの場合はChrome WebDriverをインストールします。
brew install chromedriver |
プロジェクト直下のGemfile
に以下を追加して、bundle install
を実行。
group :test do |
spec/supports/capybara.rb
を追加して以下を記入。
require 'capybara/rspec' |
rails_helper.rb
にspec/supports/capybara.rb
を読み込む設定を追加。
require 'supports/capybara' |
テストは次のように記述します。
require 'rails_helper' |
これだけでテストの際にヘッドレスのChromeが立ち上がってテストを実施できます。
# Click button by id, text, title or alt of img tag |
# Set text to text tag by id, name or label text |
# Find element by css |
# Check existance of the element by css |
# Scope with xpath |
# Waiting & try to check the element for 30 seconds |
英語版のCapybaraチートシートは「Ruby Capybara with selenium Cheat Sheat」です。
RSpec以外でのCapybaraの利用手順です。
require 'capybara' |
$ ruby cabybara_sample.rb |
<img src=/img/seo-google-web-master-web-starter-guide.png alt=検索エンジン最適化スターターガイド />
Googleの公式ドキュメントによって、WebサービスでSEOのために守るべきルールを正しく把握できます。
「Googleのモバイルユーザビリティレポート」でチェックできますので、ぜひ試してみてください。
URL構造(ルーティング)は、検索エンジンがクローリングしやすくなるための重要な要素のひとつです。
ナビゲーションはユーザーがどのように詳細ページにたどり着くかを常に意識してサイトを構築することが大切です。
ソーシャルメディアの特性を理解して、ユーザーが興味をもつコンテンツに絞って紹介するようにしたり、関連するコミュニティへ参加していくことで自然な被リンクの獲得を目指しましょう。
schema.orgで公表されている構造でマークアップすることで、Googleの検索結果にリッチスニペットを表示できる可能性があります。
詳細は「最新のSEO事情!schema.orgで構造化マークアップせよ! - Qiita」がわかりやすいので御覧ください。
title
タグはGoogleでの検索結果にメインで表示されます。
description
タグは検索結果のtitleの下部に表示されます。
h1, h2, h3, h4, h5, h6
はHTML内での重要性を表すために使います。目次を作るようにコンテンツの何が主要なのかを考えて、コンテンツのポイントとなる部分に見出しタグを設置するようにしましょう。
「画像のalt属性」は画像が表示されない際にユーザーに代替情報を知らせることと、検索エンジンに画像に関する情報を知らせることの2つの意味があります。alt属性もアンカーテキストなどと同じく、簡潔で説明的なテキストを心がけましょう。
アンカーテキストとは「リンクが設定されたテキスト」のことです。アンカーテキストでは次のことに気を付けましょう。
ページネーションを設定した際はrel属性でnext
とprev
を指定する必要があります。
コメント欄や掲示板などユーザーが気軽に書き込める場所は、時としてスパムの標的となります。スパムサイトへのリンクがページ内に存在すると、「Googleがそのサイト自体の評価を下げてしまう可能性があります」。これを防ぐためにユーザーが自由に操作できるリンクにはrel属性の値にno follow
をセットするようにしましょう。
allow
メソッドでスタブ(テストの際、そのモジュールの代わりに用いる代用品)を構築できます。
RSpec.describe "Specifying a return value" do |
allow_any_instance_of
を使うと生成されたインスタンスすべてに対して、スタブを設定できます。
RSpec.describe "allow_any_instance_of" do |
エラーを通知するような場合のRSpecは次のように記述します。
RSpec.describe "calling a missing method" do |
Railsでログのテストを行いたい場合は次のように記述します。
it 'writes some message in log' do |
FactoryGrilで同じ値にならないように連番をつけていくのはsequence
です。
sequence(:email) { |n| "person#{n}@example.com" } |
aggregate_failures
:テストをまとめて検証RSpec 3.3からの新機能aggregate_failures
を使うと
を実現できます。
require 'spec_helper' |
これを全体に適用する場合は、spec/spec_helper.rb
に次のように記述します。
RSpec.configure do |config| |
end_with
end_with
は文字列・配列の後ろ側が期待どおりかをチェックするためのマッチャー(matcher)です。
expect("this string").to end_with "string" |
RSpecの実行後に、時間の掛かったSpecをn件表示する設定です。プロジェクト直下の.rspec
に以下を追記します。
--profile 任意の数 |
RSpecで警告が発生する場合はエラーにする設定。spec/rails_helper.rb
に以下を追記。
RSpec.configure do |config| |
it
とspecify
の使い分け人間の読みやすい自然言語的な使い分けを心がける。
it
=> itを主語として使うと自然な場合に使うspecify
=> 明示する、明記するなどの意味。itを主語似できないような場合に使うEveryday Rails - RSpecによるRailsテスト入門
RSpecの書き方をサンプルアプリケーションを例に順番に学ぶことができる電子書籍です。RSpecを書き始めた人が最初に読むのに最適な日本語ドキュメントです。
可読性が高く、無駄のない効率的なテストを作っていくのに適したRSpecのスタイルガイドです。RSpecを書いていく中で身についたノウハウが詰まっているので初心者〜中級者向けにオススメです。
npmによるJavaScriptのライブラリをyarnで管理できます。これにより、npmで管理されたライブラリを気軽に利用できます。
bin/yarn install
でnpmのライブラリをインストールできます。
--webpack
オプションを付けると、webpackを使ってJSをコンパイルできます。
新規プロジェクトの場合は次のように--webpack
をつけると有効になります。
rails new プロジェクト名 --webpack |
既存プロジェクトに導入する場合はGemfile
に次のコードを追加してください。
em 'webpacker' |
そして次のコマンドを実行します。
bin/rails webpacker:install |
RailsのJavaScript実装のjquery-ujs
がjQueryではなく、pure JavaScriptになり「rails-ujs」となるそうです。
SystemTestCase
を使うことで、インテグレーションテストを実装しやすくなりました。
require 'application_system_test_case' |
標準で作成されるApplicationSystemTestCase
でCapybaraのドライバやブラウザ、画面解像度を設定できます。
require test_helper |
config/secrets.ymlを次のコマンドで暗号化します。
bin/rails secrets:setup |
実行するとconfig/secrets.yml.key
(鍵)とconfig/secrets.yml.enc
(暗号化されたファイル)が生成されます。config/secrets.yml.key
は本番環境の環境変数RAILS_MASTER_KEY
に設定します。
Action Mailerにパラメータを渡せるので、DRYに書きやすくなりました。
class InvitationsMailer < ApplicationMailer |
routes.rb
に次のようなHelperを書くことができるようになりました。
direct :commentable do |model| |
routes.rb
に次のようなルーティングを記述できるようになりました。
resource :basket |
すると次のようにform
側を記述できます。
<%= form_for @basket do |form| %> |
<%= form_with url: posts_path do |form| %> |
生成されるHTMLは次のようになります。
<form action=/posts method=post data-remote=true> |
<%= form_with model: Post.first do |form| %> |
生成されるHTMLは次のようになります。
<form action=/posts/1 method=post data-remote=true> |
Datetime型だけでなく、Time型もタイムゾーンを考慮した実装に変わりました。
# DatetimeとTime型の両方でTimezoneをつける |
rails
バージョンを5.1にアップデートHashWithIndifferentAccess
は速度の問題があるため、ActiveSupport::HashWithIndifferentAccess
に移行config/secrets.yml
を使っている場合は、キーをシンボルで読み込むようにするArray
=> 配列List
=> リスト構造Vector
=> 追加削除、検索の速度が一定な万能なデータ構造Map
=> キーと値のセットSet
=> 集合Range
=> 範囲Array
Array
は一般的なプログラミング言語の配列です。
val arr = Array[Int](1, 2, 3, 4, 5) // 型を明確に指定した定義 |
List
List
は一度作成したら中身を変更できない、immutable
な構造をしています。
val list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9) |
List
の特徴は次のとおりです。
List
は先頭へのアクセスは早いNil
Nil
は空のListを表すオブジェクトです。
val a1 = Nil //=> List() |
::
::
(コンス)はすでにあるList
に要素をつけるメソッドです。
val a1 = 1::Nil //=> List(1) |
++
++
はList
どうしを連結できます。
val a1 = List(1, 2) ++ List(3, 4) //=> List(1, 2, 3, 4) |
mkString
mkString
メソッドはListを結合して文字列にします。
List(1, 2, 3, 4).mkString //=> 1234 |
map
map
メソッドは各要素を加工して新しいList
を返します。
List(1, 2, 3, 4).map(x => x * 2) //=> List(2, 4, 6, 8) |
filter
filter
メソッドは条件にあった要素を抽出した新しいList
を返します。
List(1, 2, 3, 4).filter(x => x % 2 == 0) //=> List(2, 4) |
find
find
メソッドは条件にあった最初の要素を返します。
List(1, 2, 3, 4).find(x => x % 2 == 0) //=> Some(2) |
count
count
メソッドは条件にマッチする要素の件数を返します。
List(1, 2, 3, 4).count(x => x % 2 == 0) //=> 2 |
Vector
は要素の追加・削除、ランダムなアクセスで一定の速度で処理を行えるimmutable
なデータ構造です。
// データの追加 |
Map
はキーとバリューのペアのデータ構造です。
val urls = Map( |
通常のMap
はimmutable
なデータ構造です。
val m = Map("hoge" -> 1, "fuga" -> 2, "pugi" -> 3) |
mutable
なデータ構造のMap
も利用できます。
import scala.collection.mutable |
Set
は値の集合を表すデータ構造です。Set
も通常はimmutable
なデータ構造です。
val s = Set(1, 2, 3, 4) |
immutable
なデータ構造のSet
も利用できます。
import scala.collection.mutable |
Range
は範囲を表すオブジェクトでto
やuntil
を使って生成できます。
1 to 5 //=> Range 1 to 5 |
Hello World
の流れや型の定義、Scala特有の機能を紹介します。HomebrewでScalaをインストール。
brew install scala sbt |
HelloWorld.scala
を作成。
object HelloWorld { |
build.sbt
を作成して以下を記入。
scalaVersion := "2.12.2" |
次のコマンドでコンパイルして実行。
sbt |
ScalaのREPLの呼び出しはsbt console
で呼び出せます。
sbt console |
REPLの終了は:quit
or :q
。
// コメントA |
var 変数:型 = 値
の形式。型は型推論で省略が可能。
var id:Int = 1 |
var
は再代入可能な変数(mutable)、val
は再代入ができない変数(immutable)を示す。
var y = 1 |
val 変数:型 = 値
の形式。
val NUM:Int = 1 |
型 | Javaでの型 | 説明 | 初期値 |
---|---|---|---|
Boolean | boolean | true 、false の真偽値 | false |
Char | char | 1文字(2byte) | \0 |
String | String | 文字列 | null |
Int | int | 32bitの整数 | 0 |
Long | long | 64bitの整数 | 0 |
Float | float | 32bitの浮動小数 | 0.0 |
Double | double | 64bitの浮動小数 | 0.0 |
s"..."
とすると$xxx
の変数を展開して文字列置換してくれます。
val name = "hoge" |
複数のデータを格納できるコンテナ型で、違う型も格納できる。22個が格納できるオブジェクトの上限。
val t = (1, "hoge", 2.2) |
Unit
は戻り値のない型。void
と同じもの。
// if |
// 通常の書き方 |
return
は省略可能で、省略時は最後の文が評価されます
def hoge(): String = { |
この場合の{}
は{}式
です。メソッド構文に特別に{}
が含まれているわけではないです。
クラスのフィールドはval
はgetter
のみ提供、var
はgetter
とsetter
を提供。
class Dog(_name: String, _age: Int) { // コンストラクタ |
継承はextends
、メソッドオーバーライドする際はoverride
修飾子を追加。
class GoldenRetriever(_name: String, _age: Int) extends Dog(_name: String, _age: Int) { |
そのほかにもprivate
, protected
修飾子などがあります。
try
、catch
は値を返すが、finally
は値を返さないので注意。
try { |
【Bootstrap製】90ページ超えの無料HTML5/CSS3テンプレート素材 Titan
無料のテーマが整理されてまとまっています。特に90ページ以上の大量のサンプルHTMLは、一部パーツを探して使うのに便利そうです!
Bootswatch: Free themes for Bootstrap
15種類のBootstrap3対応のテーマがまとまっている。着実にテーマが増えていったり、クオリティが改善し続けているのがすばらしい!
フラットデザインの参考にも!Bootstrap3対応のかっこよすぎる無料テーマまとめ
LIGさん渾身の記事。クオリティが高めのテーマばかり、きれいにまとまっています。
Bootstrap利用、高品質&クリエイティブな無料HTMLテンプレート素材まとめ
Photoshop VIPが提供しているだけあってクオリティが中〜上のテーマが集まっています。すばらしい!
Flatstrap by Littlesparkvt.com
Bootstrap3に対応したフラットデザインのテーマ。かなりきれい!
TODC Bootstrap
Bootstrap3に対応したGoogleスタイルのテーマ。
AdminLTE
Bootstrap3対応の管理画面のテーマ。ハイセンス!
Bootflat
Flat UI KITというオープンソースをベースにしたBootstrap3のテーマ。
Bootstrap Material
マテリアルデザイン風のBootstrapテーマ!
Free Bootstrap Templates & Themes
Bootsrap3のテンプレートがメインでマトメらているサイト。うまく使いこなせばかなり省力化できそう!
WrapBootstrap - Bootstrap Themes & Templates
約$5-20の格安の料金でかなりクオリティが高い商品が集まっています。力を入れたサイトを作る場合はだいたいここで買っちゃっています。
Bootstrap CDN
BootstrapのCDNサイト。サイトにアップロードしなくても使えるので、いろいろ応用がききそう!
@RequestMapping
や、@BindingResult
、オブジェクトのModel
などを紹介します。@RequestMapping
Java Spring MVCのControllerの処理対象となるURLを@RequestMapping
アノテーションのvalue
オプションで指定します。(value
は最初の/
は省略できます)
// value で処理対象のパスを指定 |
GETやPOSTなどのmethod
オプションで指定します。
// methodでmethodを指定 |
GET POST, PUT, PATCH, DELETE, HEAD, OPTIONS, TRACE
を指定可能です。
@GetMapping
@RequestMapping
のGETリクエスト用のアノテーションが@GetMapping
です。記述の省略と可読性の向上が目的です。
"books") ( |
@PostMapping
@RequestMapping
のPOSTリクエスト用のアノテーションが@PostMapping
です。記述の省略と可読性の向上が目的です。
"books/create") ( |
@PathVaribable
@PathVaribable
は/books/1
のようにREST形式のURLのパラメータ1
を受け取るのに使います。value属性は省略でき、省略した場合は引数名をパラメータ名と解釈します。
"/books/{id}") ( |
@RequestParam
@RequestParam
は?order=price
のようにリクエストパラメータorder
を受け取るのに使います。value属性は省略でき、省略した場合は引数名をパラメータ名と解釈します。
"/books") ( |
ModelMap
コントローラからビューに値を渡すのに、メソッドの仮引数にModelMap
を指定する方法があります。
"/books") ( |
Model
、ModelMap
、ModelAndView
の簡単な説明です。
オプション | 説明 |
---|---|
Model | Model はインターフェースでaddAttribute メソッドなどをもつ |
ModelMap | ModelMap はMAPインターフェースの実装。Mapメソッドをもと |
ModelAndView | ModelMap とviewオブジェクトのコンテナ |
@ModelAttribute
@ModelAttribute
をメソッドにつけるとRequestMapping
のアクションを実行する前にそのメソッドが呼び出されます。
"create") (value = |
@ModelAttribute
はアクションの引数に付与することもできます。その場合は自動的に同名のフィールドにマッピングされ、リクエストスコープにも設定されます。
@BindingResult
@BindingResult
はメソッド引数として直前のフォームオブジェクトのバリデーション結果を格納します。@BindingResult
はメソッドの引数の並び順をバリデーション対象の直後にすることが必須なので注意してください。
"create", params = "confirm") (value = |
RedirectAttributes
RedirectAttributes
はリダイレクト先にオブジェクトを送るのに使います。
"create") (value = |
RedirectAttributes
のaddFlashAttribute
とaddAttribute
メソッドを紹介します。
メソッド | 説明 |
---|---|
addFlashAttribute | sessionでオブジェクトを送付するため、リダイレクト時に一度だけ有効なデータの受け渡し方になります |
addAttribute | URLパラメータでオブジェクトを送付するため、その後何度でも有効な文字列となります |
build.gradle
に次の内容を追加してgradle build
を実行。
buildscript { |
PostgreSQLのインストールは「macOS SierraへのPostgreSQLインストール」を参照ください。
DBとユーザーを作成します。
createuser -s mybatis_test -P |
SQLを実行してテストテーブルを作成します。
CREATE TABLE test_table ( |
以下を実現するコードを紹介します。
├── build.gradle |
DBとの接続情報を記述したmybatis-config.xml
は次のとおりです。
|
sample_mapper.xml
へのSQLの定義は次のように行います。パラメータは#{xxx}
で定義します。
|
MyBatisのMapperを使うことで直感的で読みやすく、SQLと紐付けを行うことができます。
import java.util.List; |
SQLの結果をマッピングするJavaオブジェクトTestTable.java
を定義します。
public class TestTable { |
パラメータを渡して、対応するデータを取得するJavaのコードは次のとおりです。
public class MybatisSample { |
TestTable [id=1, values=hoge] |
SQLへの引数の受け渡しと実行結果をJavaオブジェクトにセットできていることがわかります。
SQL内に<if></if>
タグでSQLのwhere構文を値を動的に変更できます。
<?xml version="1.0" encoding="UTF-8" ?> |
SQL内に<trim></trim>
タグを使うことでUpdate SQL文を動的に置き換えることができます。
<?xml version="1.0" encoding="UTF-8" ?> |
DBのSequenceからidを取得してからinsert文を実行する場合はsample_mapper.xml
を次のように設定します。
ample_mapper.xml |
@Select
などのアノテーションを使うとSQLを直接コードに埋めて実行ができます。Testなどでダミーデータを登録するのに便利です。
Mapper
側にメソッドを定義します。
public interface TestTableMapper { |
Mapperの呼び出し側は次のようにSQLを呼び出します。
public class MybatisSample { |
実行結果は次のとおりで、件数を取得できているのがわかります。
3 |
_int
のように_
を最初につけるインスタンス | 概要 | スコープ |
---|---|---|
SqlSessionFactoryBuilder | mybatis-config.xml の設定ファイルを読み込む | メソッド内だけで使い捨て |
SqlSessionFactory | SqlSession を生成するメソッド。一度作ったら使いまわす | アプリケーション内で使いまわす |
SqlSession | SQL実行で使う。1連のSQLを実行したらcloseする | リクエスト間でだけ使いまわす |
実行するSQLをログに出力する場合は「Logback Home」をbuild.gradle
に追加すればDEBUGレベルで出力できます。
compile 'ch.qos.logback:logback-classic:1.1.3' |
ログの出力結果は次のようになります。
07:36:11.111 [main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [org.postgresql.jdbc4.Jdbc4Connection@5f9d02cb] |
テーブルのスネークケースとキャメルケースの自動マッピングを行う場合はmybatis-config.xml
に以下を追加します。
<configuration> |
insert, update, delete
が実行されるとキャッシュがクリア>
、<
はCDATAで囲む>
、<
を使う場合はCDATA
で対象のSQLを囲むことで使えます。
<![CDATA[ |