Quantcast
Channel: JavaScriptの記事一覧|TechRacho by BPS株式会社
Viewing all 164 articles
Browse latest View live

週刊Railsウォッチ(20170728)bootsnapがRailsで正式採用、Ruby Prizeの推薦開始、PostgreSQL配列の重複を除去ほか

$
0
0

こんにちは、hachi8833です。南極の氷が増えているのか減っているのか、だんだんよくわからなくなってきました。

7月最後のRailsウォッチ、いってみましょう。

Rails 5.1.3rc2と5.0.5rc2がリリース(Rails公式ニュースより)


rubyonrails.orgより


つっつきボイス: 「バグフィックスが中心だろうし、正式版来たらアップグレードするかな」「先週rc1リリースから早くもrc2です」「そういえばRubyコミュニティには、rcいくつまでをどういうタイミングで出すみたいな目安ってあるんだろうか」

Ruby Prize 2017の推薦開始


www.ruby.or.jpより


つっつきボイス: 「Ruby Prize 2017は毎年Ruby World Conferenceの場で発表されていますね」「Ruby Prizeはソフトウェアや企業ではなく、個人を対象にした賞であるところが特徴」「(今さら要項を見て)ほんとだ」


「社内では毎年話してる気がするんだけど、こういう個人向けの賞が長年運用されているところがRuby界隈のコミュニティのいいところだなーと思ってます」「確かに他の言語ではこういうのあまり見ないかも」「contributeする個人に陽のあたる場所を提供する場というのは本当に大事」「何も見ずに言うけど、昨年の受賞者にたしかOpenSSL bindingをひたすらアップデートした学生さんがいたんじゃなかったかな: こういう地味な作業って普通はあまり表に出てこない」

「OpenSSLのアップデートってバグか何か?」「いやいや、元のAPIがガラッと変わってbindingがほぼ書き直しを迫られたということ」


賞が欲しくてcontributeする人はあまりいないと思いますが、そういう人たちに報いる場があることで張り合いが出るのはいいですね。

TechRachoを1年近くやってて痛感していますが、ポジティブフィードバックってめったに当事者には伝わりません。いいと思ってくれる人は何も発しないことがほとんどなので。

というわけで、皆さまもがんがんRuby Prizeに推薦を送ってあげてください

↑もうちょっと小さくしたいんだけどなこれ。

Rails: 今週の改修(Rails公式ニュースより)

新機能: bootsnap gemがRails標準に

以前のRailsウォッチでご紹介したy-yagiさんの予想どおりになりました。

# Gemfile
+# for railties app_generator_test
+gem "bootsnap", ">= 1.1.0", require: false

つっつきボイス: 「おー、Railsに組み込むのかと思ったらgemのまま取り込まれたのか」「bootsnapってどうしてもbootstrapに見えてしまってw」

改修: rails dbconsoleコマンドでの環境指定で-eが必須になった

+    Previously:
+        $ bin/rails dbconsole production
+    Now:
+        $ bin/rails dbconsole -e production

つっつきボイス: 「たしかにこういうのはオプション付きで指定するほうがいいかも」

修正: カスタムのテーブルエイリアス名でのwhereの挙動

kamipoさんの修正です。SELECT句でカスタムのテーブルエイリアス名を使えるように修正されました。

# activerecord/lib/active_record/relation/query_methods.rb
        if select_values.any?
           arel.project(*arel_columns(select_values.uniq))
         else
-          arel.project(@klass.arel_table[Arel.star])
+          arel.project(table[Arel.star])
         end
       end

つっつきボイス: 「(ドラフトを眺めて)custom table alias nameって、『カスタムテーブルのエイリアス名』じゃなくて『カスタムのテーブルエイリアス名』じゃないかな?」「おっと失礼しました: 英語って助詞がないからこういう区切りってわかってる人じゃないとパースしづらいですね」「まあそれは日本語もそうですけどね」


「情報処理安全確保支援士合格者御一行様歓迎列車到着遅延見込」や「東京大学理学部地質学鉱物学教室施設定例保守点検実施中止経過報告」みたいに漢字だらけだと、外国人でなくてもつらそうです。メソッドチェーンでこんなのやったらMR通らなさそう。

参考: Wikipedia-ja 般若心経

修正: エンコードエラーのメッセージに表示される無効なUTF-8文字をscrubするようにした

無効な文字のせいでCIがコケることがあったそうです。

# actionpack/lib/action_dispatch/request/utils.rb
          unless params.valid_encoding?
             # Raise Rack::Utils::InvalidParameterError for consistency with Rack.
             # ActionDispatch::Request#GET will re-raise as a BadRequest error.
-            raise Rack::Utils::InvalidParameterError, "Non UTF-8 value: #{params}"
+            raise Rack::Utils::InvalidParameterError, "Invalid encoding for parameter: #{params.scrub}"
           end
         end
       end

つっつきボイス: 「こういう修正は地味にありがたいかも」

self が不正なバイト列を含む場合に別の文字列に置き換えた新しい文字列を返します。
不正なバイト列を置き換える文字列を指定します。省略した場合は self の文字エンコーディングが Encoding::UTF_16BE, Encoding::UTF_16LE, Encoding::UTF_32BE, Encoding::UTF_32LE, Encoding::UTF_8 のいずれか の場合は “\uFFFD” を表す文字で、それ以外の場合は “?” で置き換えられます。ブロックが指定された場合は不正なバイト列はブロックの戻り値で置き換えられます。
Ruby リファレンスマニュアル: instance method String#scrubより

つっつきボイス: 「ところで、invalidは「不正な」と訳されることが多いんですが↑、私はvalid/invalidは『有効な』『無効な』って訳すようにしてます」「言われてみれば、技術用語のinvalidって『不正送金』みたいな違法なニュアンスは全然ないはずだから、『有効/無効』の方が的確だなー」「実は某社の翻訳スタイルガイドにそういう指示があったのでしたw」

改修: rails yarn:installによるリビルドをproductionだけに限定

開発中にyarn installするたびにネイティブパッケージがリビルドされて時間がかかっていたのが修正されました。

# railties/lib/rails/tasks/yarn.rake
namespace :yarn do
   desc "Install all JavaScript dependencies as specified via Yarn"
   task :install do
-    system("./bin/yarn install --no-progress")
+    system("./bin/yarn install --no-progress --production")
   end
 end

yarn install呼び出しのたびにネイティブパッケージがリビルドされます(yarnpkg/yarn#932)。私たちのところでカスタムフォントをビルドするためにdevDependenciesでnodeのネイティブパッケージを使ったところ、それまで2秒で済んだデプロイがリビルドのせいで30秒以上かかるようになってしまいました。Railsではassets:precompileの前にyarn:installが使われますが、このパッチをあてるとdev dependencyが無視されるようになります。bin/yarnを実行すれば従来どおりすべてがインストールされます。
とりあえず問題を回避したい場合: bin/yarnを以下のように更新してください。

# ...
  begin
    no_dev = %w[production staging].include?(ENV['RAILS_ENV'])
    exec "yarnpkg #{ARGV.join(' ')} #{'--prod' if no_dev}"

# 29851より

RailsアプリにSalesforce APIを統合する(RubyFlowより)


www.railscarma.comより

restforceというgemを使ってSalesforceのAPIを使えるようにする記事です。


つっつきボイス: 「ぱっとタイトルだけ見たとき、Salesforceが自社APIをRailsアプリ化したのかと思っちゃいました」「んなこたーないw」「まあ、よくある記事ではある」

ClojureのinterposeをRubyで実装したった(RubyFlowより)


bcobb.netより

ClojureのinterposeをRubyでも使いたくて実装したのだそうです。

# bcobb.netより

{one: 1, two: 2, three: 3}.interpose(:sep).to_a
# [[:one, 1], :sep, [:two, 2], :sep, [:three, 3]]

StringIO.new("line one\nline two\nline 3\n").interpose("|").to_a
# ["line one\n", "|", "line two\n", "|", "line 3\n"]

つっつきボイス: 「Clojureのこのinterposeって、配列要素の間に挿入するってことか」「Rubyならもっとスマートに書く方法がありそうな気がして仕方がない」「これが便利なものならActiveSupportあたりにありそうなものだけど、見当たらないということは…w」

interpose:
{他動-1} : 〜を間に入れる[置く]
{他動-2} : 〔他の人が話をしているときに言葉・質問・意義などを〕差し挟む


Clojureって、ついClosureと打ち間違えてしまいます(打ち間違えました)。

HerokuでDBサーバーの負荷を80%軽減した


schneems.comより

$ heroku pg:outliers
total_exec_time  | prop_exec_time |   ncalls    |   sync_io_time   |                                                                                       query
------------------+----------------+-------------+------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3790:50:52.62102 | 80.2%          | 100,727,265 | 727:08:40.969477 | SELECT  ? AS one FROM "repos" WHERE LOWER("repos"."name") = LOWER($1) AND ("repos"."id" != $2) AND "repos"."user_name" = $3 LIMIT $4
493:04:18.903353 | 10.4%          | 101,625,003 | 52:09:48.599802  | SELECT COUNT(*) FROM "issues" WHERE "issues"."repo_id" = $1 AND "issues"."state" = $2

つっつきボイス: 「うん、新しい話は何もないw」「そっかなーと思ったらやっぱりそうでしたか」「記事の問題じゃなくて、そもそもパフォーマンスチューニングってそういうものです: 地道な作業の積み重ねなので」

Autoprefixer CSS online: Autoprefixerの更新がつらい人向けのサービス

社内Slackからです。


autoprefixer.github.ioより


つっつきボイス: 「vendor prefixとかイヤやーw: Railsならcompass使えばいいんではないかと」「vendor prefixってホントその場しのぎの技術ですねw」


compass-style.orgより

Screencast: Codemy.netの「Rails APIシリーズ」(RubyFlowより)

Railscastsが開拓して以来(英語圏では)過熱が続くscreencast界にまた名乗りを上げてきたサイトです。思ったより動画がたくさんあります。APIサーバーに絞っているようですが、ひととおりカバーしている様子。
最初の動画が半年前の公開なのでかなり新しい方ではあります。


つっつきボイス: 「サムネイル画像が同じすぎてわかりにくいのー」「(ひとつ開いてみて)あー、transcript(文字起こし)が全然ないwこれはアカンなー: 動画の垂れ流しサイト観るのはよっぽど暇なときぐらいしか考えられないし」「5分の動画でも5分かけて見る気にはなれないですねー」「フィードバックしときます」

そういえば日本ではscreencastってさっぱり見かけませんね。別にいいんですが。

そこからCode Schoolのよさについての話題になりました。


つっつきボイス: 「Code Schoolの動画部分って見ます?」「見ないww: Code Schoolは動画より本編のできがずっといいので」「あのクォリティで本編作るのはそうそうできることじゃない、いやほんとに」


www.codeschool.comより

追記

たった今(日本時間で14時)Codemy.netに登録して「文字起こし、ほすいー」とチャットで伝えたところ、こんな時間なのに即レスがありました。

フィードバックありがとう! 実は今のサイトで文字起こしを置けないとかいろいろ制約があって困っていたので、サイト引越しの準備しているところ

というわけで今後に期待したいと思います。クレーム口調でない、改善につながるフィードバックを送ると、どこでも本当に喜んでもらえますね。

ベンチマーク結果をRubyでかっこよくグラフ化


codeship.comより

gruff gemを使っています。


codeship.comより


つっつきボイス: 「PythonにはCIのベンチマーク結果を継続的に掲載している公式のspeed.python.orgがあるんですが、Rubyにもそういう継続的なベンチサイトってありましたっけ?」「どうだったかなー、MRIやJRubyやmrubyといった実装ごとにベンチを比較するサイトならあったような気がするんだけど」

ざっとぐぐった限りではそれらしいものは見当たらず、そこからgraphという言葉についての話題になりました。


つっつきボイス: 「この記事でgraphという言葉を使っているのがすごく珍しいと思う」「あ、確かに: 技術英語サイトでは普通こういうのってchartって書きますよね」「graphっていうとグラフ理論の方を指す感じ」「そうそう、有向グラフとか」「もしかするとアメリカ英語とイギリス英語の違い?」

と思ってその場で各種辞書を繰ってみたのですが、意外にも辞書・シソーラスともに米英の違いがなく、「graphの類義語がchart」とのことでした。

つっつきボイス: 「chartとgraphってどう使い分けるんだろうw 謎」「謎」


日本語で言うとさしずめ「図」と「画像」ぐらいの違いなのかなと、何となく想像してしまいました。

Officetxt gem: 英語圏のフリーライター向けコマンドツール集(RubyFlowより)


github.com/officetxtより

これ自体は★は少ないですが、ツールの中でWordをMarkdownに変換するword-to-markdownというgem(★800個近く)が気になりました。日本語とおるでしょうか。

LibreOfficeが必要だそうです。

gem install word-to-markdown
file = WordToMarkdown.new("/path/to/document.docx")
=> <WordToMarkdown path="/path/to/document.docx">

file.to_s
=> "# Test\n\n This is a test"

file.document.tree
=> <Nokogiri Document>

つっつきボイス: 「単独のテキストツールじゃなくていろんなツール(gem)を一発でインストールするのか」「docxはxmlだからできそうダナ」


www.libreoffice.orgより

Deviseでユーザーを無効にする(RubyFlowより)


blog.kodius.ioより

短い記事です。

# https://blog.kodius.io/2017/07/26/how-to-deactivate-user-rails-with-devise/ より
class AddDeactivatedToUsers < ActiveRecord::Migration
  def change
    add_column :users, :deactivated, :bool
  end
end

# user.rb
def destroy
  update_attributes(deactivated: true) unless deactivated
end

def active_for_authentication?
  super && !deactivated
end

つっつきボイス:lockableでやればよさそうだけど、どうしてもカスタマイズしないといけない事情があったのかな」「こうやってフラグ立てるのってあんまりよくなさそうですね」「必ずしもそうとは限らない: 認証システムをカスタマイズ一切なしで導入できる状況って現実にはほとんどないんですよ」「うーむ」

参考: Devise Wikiシリーズ総もくじ: アカウント削除後のUserデータを保存する(論理削除)

補足

Stack Overflowには「Deviseのactive_for_authenticationでできた」「認証(authentication)ではなく承認(authorization)でやるべき」などの情報もありました。

プレゼン「TC39、ECMAScript、JavaScriptの未来」

中国の深セン経済特区で今年6月に開催されたTFC: Tencent Frontend Conferenceのプレゼンです。


tfc.alloyteam.comより

frontendが中国語で前端って書かれてるところにほだされて、つい拾ってしまいました。ECMAScriptsの最新プロポーザルが多数紹介されています。

# tfc.alloyteam.comより
[1, 2].indexOf(2) !== -1 // true
[1, 2].indexOf(3) !== -1 // false
[1, 2].includes(2) // true
[1, 2].includes(3) // false

つっつきボイス: 「TC39って何だろ」「(ググって)あー、ECMAの分科会か」「マジェスティックトゥエルブみたいw」

dnsimple.com: ドメイン管理アプリ


dnsimple.comより

require "dnsimple"
client = Dnsimple::Client.new(access_token: "abc123")

# Define an account id
account_id = 1010

# List your domains
puts client.domains.list_domains(account_id).data

# Create a domain
response = client.domains.create_domain(account_id, name: "example.com")

# Get a domain
response = client.domains.domain(account_id, "example.com")
puts response.data

同社のブログによるとHanamiで構築されているそうです。


つっつきボイス: 「こういうサービスってインフラエンジニア的にはどうでしょう?」「いろいろ機能はあるみたいだけど、AWSのRoute53使えばええやんw」

PostgreSQL

PostgreSQLのペネトレーションテストガイド

# medium.com/@cryptocracker99 より
postgres=# CREATE TABLE pentestlab (t TEXT);
postgres=# INSERT INTO pentestlab(t) VALUES('nc -lvvp 2346 -e /bin/bash');
postgres=# SELECT * FROM pentestlab;
postgres=# COPY pentestlab(t) TO '/tmp/pentestlab';


medium.com/@cryptocracker99より


つっつきボイス: 「ペネトレーションテストっていわゆる侵入テストですよね」「侵入テスト以外に、どこまでやったらサーバーが死ぬかを試すという意味のペネトレーションテストもあります」「これはどっちかな: nc打ってるし、まさに侵入かけようとしてる」

PostgreSQLの配列から重複を取り除く

-- https://medium.com/the-falconry より
SELECT
  song_id,
  UNNEST(STRING_TO_ARRAY(STRING_AGG(v.genre_list, ', '), ', ')) AS genre
FROM versions v
GROUP BY song_id
  1. 分野ごとに集約
  2. UNNESTで配列のネストを解除(↑上のコード)
  3. DISTINCTが使えるようになる
  4. 仕上げ

つっつきボイス: 「Rubyなら#flattenして#uniqするところをPostgreSQLでやってるのかー」

PostgreSQLのインデックス肥大化を探る

短い記事ですがよさげです。

# pgeoghegan.blogspot.jpより
 level | l_item | blkno | btpo_flags | type | live_items | dead_items | avg_item_size | page_size | free_size | distinct_real_item_keys | highkey | distinct_block_pointers
-------+--------+-------+------------+------+------------+------------+---------------+-----------+-----------+-------------------------+---------+-------------------------
     2 |      1 |   290 |          2 | r    |         10 |          0 |            15 |      8192 |      7956 |                      10 |         |                      10
     1 |      1 |     3 |          0 | i    |        285 |          0 |            15 |      8192 |      2456 |                     284 |  103945 |                     284
     1 |      2 |   289 |          0 | i    |        285 |          0 |            15 |      8192 |      2456 |                     284 |  207889 |                     284
     1 |      3 |   575 |          0 | i    |        285 |          0 |            15 |      8192 |      2456 |                     284 |  311833 |                     284
     1 |      4 |   860 |          0 | i    |        285 |          0 |            15 |      8192 |      2456 |                     284 |  415777 |                     284
     1 |      5 |  1145 |          0 | i    |        285 |          0 |            15 |      8192 |      2456 |                     284 |  519721 |                     284
     1 |      6 |  1430 |          0 | i    |        285 |          0 |            15 |      8192 |      2456 |                     284 |  623665 |                     284
     1 |      7 |  1715 |          0 | i    |        285 |          0 |            15 |      8192 |      2456 |                     284 |  727609 |                     284
     1 |      8 |  2000 |          0 | i    |        285 |          0 |            15 |      8192 |      2456 |                     284 |  831553 |                     284
     1 |      9 |  2285 |          0 | i    |        285 |          0 |            15 |      8192 |      2456 |                     284 |  935497 |                     284
     1 |     10 |  2570 |          0 | i    |        177 |          0 |            15 |      8192 |      4616 |                     177 |         |                     177
     0 |      1 |     1 |          1 | l    |        367 |          0 |            16 |      8192 |       808 |                     366 |     367 |                       6
     0 |      2 |     2 |          1 | l    |        367 |          0 |            16 |      8192 |       808 |                     366 |     733 |                       6
     0 |      3 |     4 |          1 | l    |        367 |          0 |            16 |      8192 |       808 |                     366 |    1099 |                       6
...
     0 |   2730 |  2741 |          1 | l    |        367 |          0 |            16 |      8192 |       808 |                     366 |  999181 |                       6
     0 |   2731 |  2742 |          1 | l    |        367 |          0 |            16 |      8192 |       808 |                     366 |  999547 |                       6
     0 |   2732 |  2743 |          1 | l    |        367 |          0 |            16 |      8192 |       808 |                     366 |  999913 |                       6
     0 |   2733 |  2744 |          1 | l    |         88 |          0 |            16 |      8192 |      6388 |                      88 |         |                       2
(2744 rows)

つっつきボイス: 「MySQLでは辛みも含めていろいろ経験積めたんで、そろそろPostgreSQLのインデックス周りの深いところもやってみるか」

CSS記事

margin corruption問題の対処方法


bitsofco.deより

良記事です。クイズも付いています。

「margin corruption」は日本語に定訳がない感じで、「marginが親要素に効いてしまう」「画像の下に謎の余白ができる」など完全にバラついているようです。

参考

CSS Variablesはこう扱え

madebymike.com.auより

CSS Variablesはカスタムプロパティとも呼ばれる、CSSのかなり新しい仕様です。

/* madebymike.com.au より*/

/* これは変数宣言 */
.thing {
  --my-var: red;
}
/* これはプロパティ宣言 */
.thing {
  background: var(--my-var);
}

つっつきボイス: 「(CanIUseを開きながら)IE対応なし、と↓」

http://caniuse.com/css-variables/embedより

オピニオン: Rustは「自動運転のC++」だ


www.rust-lang.orgより

短いのですぐ読み終わると思います。

実地のRustコードが見たかったので、この間のRailsウォッチで取り上げたdanielpclark/faster_pathから引用しようかなと思っていたところ「んー、このコードだとあんまりRustっぽくないっすねー」というツッコミがBPSアプリチームの方から聞こえてきたので、おすすめいただいたRustコードに差し替えました。

# https://github.com/BurntSushi/ripgrep/blob/master/src/worker.rs より
...
impl Worker {
    /// Execute the worker with the given printer and work item.
    ///
    /// A work item can either be stdin or a file path.
    pub fn run<W: WriteColor>(
        &mut self,
        printer: &mut Printer<W>,
        work: Work,
    ) -> u64 {
        let result = match work {
            Work::Stdin => {
                let stdin = io::stdin();
                let stdin = stdin.lock();
                self.search(printer, Path::new("<stdin>"), stdin)
            }
            Work::DirEntry(dent) => {
                let mut path = dent.path();
                let file = match File::open(path) {
                    Ok(file) => file,
                    Err(err) => {
                        if !self.opts.no_messages {
                            eprintln!("{}: {}", path.display(), err);
                        }
                        return 0;
                    }
                };
                if let Some(p) = strip_prefix("./", path) {
                    path = p;
                }
                if self.opts.mmap {
                    self.search_mmap(printer, path, &file)
                } else {
                    self.search(printer, path, file)
                }
            }
        };
        match result {
            Ok(count) => {
                count
            }
            Err(err) => {
                if !self.opts.no_messages {
                    eprintln!("{}", err);
                }
                0
            }
        }
    }
...

つっつきボイス: 「BurntSushiってなんちゅう名前w」「黒焦げw」「C++の有力な代替候補が不在の時期が長かったけど、Rustの隆盛で勢いづいてきたかな」


自動運転機能がなかった時代は人間が運転する以外の選択肢はありえませんでしたが、自動運転が普及して二世代も過ぎれば「えー!昔は人間が自動車を運転してたの!?それって危なくね?」となること請け合いですね。

参考: Wikipedia-ja: お猿の電車

書籍: XUnit Test Patterns


xunitpatterns.comより

morimorihogeさんが以下の図を見つけてくれたときのサイトを辿って見つけました。「XUnit Test Patterns」の第二版で使われる予定の図だったようですが、第二版はまだ刊行されていないようです。


xunitpatterns.comより

Go言語ではパッケージレベルの変数や関数内部での初期化を使わないこと

短いですが良記事です。関数に内部状態を埋め込むべきでない、と言われて反省しました。

// http://peter.bourgon.org/blog/2017/06/09/theory-of-modern-go.htmlより
func NewObject(n int) (*Object, error) {
    row := dbconn.QueryRow("SELECT ... FROM ... WHERE ...")
    var id string
    if err := row.Scan(&id); err != nil {
        logger.Log("during row scan: %v", err)
        id = "default"
    }
    resource, err := pool.Request(n)
    if err != nil {
        return nil, err
    }
    return &Object{
        id:  id,
        res: resource,
    }, nil
}

つっつきボイス: 「文法がっちり固まってるはずのGo言語にもmodernとかあるのかww」「本家のコードでは、関数が返すエラーをokっていう変数で受ける慣習があるんですが、あれだけはイヤですw」

ITエンジニアが健康を保つための5つのコツ: Rubyroid Labsの場合



blog.rubyroidlabs.comより

Rubyroid Labsの方から直々にお知らせいただいた記事です。

  • パソコンのモニタを見すぎない工夫をする
  • フィジカルトレーニングを続ける
  • タバコを控える
  • ストレスをためない工夫をする
  • 部屋にホコリをためない

つっつきボイス: 「面白すww」「病気の名前とか筋肉の部位みたいな名前の英語って実はほとんどわからなくってw: 腹筋って英語で何て言うんだっけというレベル」

AI同士が攻撃と防御に分かれて対決するコンテスト: セキュリティ研究の一環


www.technologyreview.comより

つっつき後に見つけたMITテクノロジーレビューの記事です。kaggle.comによると、「対象を特定しない敵対的攻撃」「対象を特定する敵対的攻撃」「敵対的攻撃からの防御」部門で募集したAIたちが対決するコンテストを今年12月のNIPS 2017期間中に開催するそうです。

「対象を特定しない攻撃」というと軍事用語のゲリラ戦(遊撃戦)を連想してしまいました。

皆さまもどうかAIから敵認定されませんように。

ツイートより

番外

Microsoftのニューラル翻訳サイト



translator.microsoft.com/neuralより

右と左で違う訳が出てくるのですが、もしかすると一方がニューラル翻訳でもう一方が旧来の機械翻訳ではなかろうかと根拠もなく思ったりしました。それだけです。

Windowsのmspaint.exeが終了


www.bbc.comより

仮面ライダーシリーズ、次は「ビルド」


今週は以上です。

バックナンバー(2017年度)

今週の主なニュースソース

ソースの表記されていない項目は独自ルート(TwitterやRSSなど)です。

Ruby 公式ニュース

Rails公式ニュース

RubyFlow

160928_1638_XvIP4h


Vue.jsサンプルコード(11)ラベルをクリックして表示開閉とラベル変更を行う

$
0
0

11. ラベルをクリックして表示開閉とラベル変更を行う

  • 「もっと見る」ラベルをクリックするとコンテンツが表示されてラベルが「閉じる」に変更されます。
  • 「閉じる」ラベルをクリックするとコンテンツが閉じて「もっと見る」ラベルに戻ります。

サンプル


なお、「CONTENTS」というプレースホルダはplaceholder.comで表示しています(以前はplaceholder.itというドメインでしたが移転したようです)。フレキシブルなプレースホルダを簡単に利用できるので何かと便利です。

https://via.placeholder.com/256?text=CONTENTS


placeholder.comより

補足: const vmについて

これまでのサンプルコードではnew Vue()を変数宣言なしで、またはvar vm =に代入して使っているものがありましたが、今回はconst vm =を使っています。

const vm = new Vue({
    el: "#app",
    data: {a: false},
    computed: {
      label: function() { return this.a ? "閉じる" : "もっと見る" },
    },
  });

const
この宣言は、グローバルか、宣言された関数内ローカルな定数を作ります。定数は初期化が必須です。つまり、宣言された同じ文の中で値を指定しなくてはいけません (あとで変更できません)。
const 宣言は、値への読み取り専用の参照を作ります。その値が不変ということではなく、その変数識別子が再代入できないというだけです。たとえば、定数がオブジェクトの場合、オブジェクト自体は変更可能です。
MDN: constより

このサンプルコードは行きがかり上const宣言をトップレベルで行っている(この場合vmはグローバルになる)のと、宣言したvmをその後参照していないので、直接の意味はありませんが、サンプルコードをコピペして使ったときの影響を抑えるため、今後はconstletを使うようにしようと思います。

constletはブラウザによっては動かない可能性もあります。


週刊Railsウォッチ(20170804)Rails 5.1.3と5.0.5が正式リリース、GitHubでローカライズ基盤サービス、正規表現で迷路を解くほか

$
0
0

こんにちは、hachi8833です。4年使ったMacbook Proのバッテリーがそろそろ天に召されそうです。

8月最初のウォッチ、行ってみましょう。

お知らせ: 来週は週刊Railsウォッチをお休みいたします。次回は8/18(金曜日)の予定です。

Rails 5.0.5と5.1.3が立て続けに正式リリース(Rails公式ニュースより)

月曜に先に5.0.5がリリースされ、今朝がた5.1.3もリリースされました。とりあえず5.1.3で記念写真↓。

Changelog

Rails: 今週の改修(Rails公式ニュースより)

修正: クエリに巨大な数値がある場合に結果が正しくなかった問題を修正

PRキリ番ゲットはkamipoさんでした。

# activerecord/test/cases/finder_test.rb より
+  def test_exists_with_large_number
+    assert_equal true, Topic.where(id: [1, 9223372036854775808]).exists?
+    assert_equal true, Topic.where(id: 1..9223372036854775808).exists?
+    assert_equal true, Topic.where(id: 1).or(Topic.where(id: 9223372036854775808)).exists?
+    assert_equal true, Topic.where.not(id: 9223372036854775808).exists?
+  end

つっつきボイス: 「DBMSの限界値をこんなふうに超えるケースは扱ったことなかったけど、挙動は確かにバグ」「idをすごい勢いで消費するシステムとかだと踏むかも: そういうシステムではid生成そのものがボトルネックになるので自前でid発番することが多そう」
「TwitterのSnowFlakeを思い出したでござる」
「id生成がボトルネックというのは?」「id生成はアトミックだから必ずロックがかかるでしょ」

そこからサーバー間の同期の話題になりました。

「システムによっては、複数サーバー間でデータ生成や順序処理を一意にするなどの目的で、サーバーごとに原子時計持たせることがある」「おー、サーバー間の同期や通信のコストを下げられるのか」

おまけ

2014年に製造された世界初の原子腕時計は、60万円投資すると10個限定でもらえるそうですが、あっというまにはけたそうです。手作り感溢れてます。

リファクタリング: Arelでbindのparamsを扱えるよう修正

かなり大規模にリファクタリングしてます。

# activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
       def case_sensitive_comparison(table, attribute, column, value) # :nodoc:
-        table[attribute].eq(value)
+        table[attribute].eq(Arel::Nodes::BindParam.new(value))
       end

       def case_insensitive_comparison(table, attribute, column, value) # :nodoc:
         if can_perform_case_insensitive_comparison_for?(column)
-          table[attribute].lower.eq(table.lower(value))
+          table[attribute].lower.eq(table.lower(Arel::Nodes::BindParam.new(value)))
         else
-          table[attribute].eq(value)
+          table[attribute].eq(Arel::Nodes::BindParam.new(value))
         end
       end

つっつきボイス: 「変更量ハンパない」「テスト通ればOKとはいっても、コアにこれだけ手を入れるってスゴイナー」
「ASTってやっぱり抽象構文木(abstruct syntax tree)か」「parserとかprepared statementのSQL生成部分とかの設計し直しまでやってるのかな」「記事数多いので次いきましょう」

ActiveSupport::Durationの除算関連の変更

「除算の余り」と「除算」の2つのコミットをまとめました。moduloといえば数学用語の「剰余とか法」ですね。

# activesupport/CHANGELOG.md より
# 変更前
5.minutes % 2.minutes #=> 60
# 変更後
5.minutes % 2.minutes #=> 1 minute

テストコードを見ると、分母と分子の単位(単位なしを含む)に応じて余りの単位をよしなに設定してくれるようです。

# activesupport/test/core_ext/duration_test.rb より抜粋
+  def test_modulo
+    assert_equal 1.minute, 5.minutes % 120
+    assert_equal 1.minute, 5.minutes % 2.minutes
+    assert_equal 1.minute, 5.minutes % 120.seconds
+    assert_equal 5.minutes, 5.minutes % 1.hour
+    assert_equal 1.day, 36.days % 604800
+    assert_equal 1.day, 36.days % 7.days
+    assert_equal 800.seconds, 8000 % 1.hour
+    assert_equal 1.month, 13.months % 1.year
   end

# activesupport/test/core_ext/duration_test.rb より
+  def test_division
+    assert_equal 1.hour, 1.day / 24
+    assert_instance_of ActiveSupport::Duration, 1.day / 24
+
+    assert_equal 24, 86400 / 1.hour
+    assert_kind_of Integer, 86400 / 1.hour
+
+    assert_equal 24, 1.day / 1.hour
+    assert_kind_of Integer, 1.day / 1.hour
+  end

つっつきボイス: 「(1つ目のa54e13を見ながら)ええー、分で分を割ったら単位がキャンセルされると思ってたけど、余りって単位を付けていいものなの?」「数学科出身のkazzさん、いかがでしょうw」「数学だと単位ってそもそもほとんど俎上に載らないんですよ」「むしろ物理学の方が単位にうるさいかも」「でも物理だと余りを出さないですね」「代わりに精度重視: 除算は最後に回すとか」
「単位付きの余りは少しぎょっとするけどこれはこれで便利そうだし、原理上そんなにおかしいものでもなさそうですね」

Durationって、ActiveSupportに限らず考えないといけないことが多くて大変ー」

Ruby-trunkより

バグ: 継承されたメソッドでRefinementのスコープが無視される

module Test
  refine String do
    def sleep; end
  end
end

puts "start"
"".sleep 3
puts "end"

つっつきボイス: 「RailsだとRefinementってまず見かけないけどねw」「Refinementって志高いし実装もすごく大変だったらしいのに、まだそれほど使われてない気がしました」

activerecord-import: ActiveRecordでデータを一括読込するgem(Random Rubyより)


revs.runtime-revolution.comより

★2000に迫る勢いです。

# https://github.com/zdennis/activerecord-import/wiki/Examples#import-using-columns-and-arrays より
columns = [ :title, :author ]
values = [ ['Book1', 'FooManChu'], ['Book2', 'Bob Jones'] ]

# Importing without model validations
Book.import columns, values, :validate => false

# Import with model validations
Book.import columns, values, :validate => true

# when not specified :validate defaults to true
Book.import columns, values

つっつきボイス:以前のウォッチでも少し話したけど、activerecord-importは昔からある定番gemですね: 案件でも使ったことあるし、CSVインポート10000件とかは余裕」「要するにvalidationかけてからINSERT複文でINSERTを実行するヤツ」

「いい点はRailsのvalidationがかかるので割りと自然な使い方ができる」
「悪い点があるとすれば、Rubyコードである以上カリカリに速くはならないことか: 案件でも速度を出すために結局生SQL書いたこともあったし」

jb gemでRailsのJSONを高速にしたった(RubyFlowより)


medium.com/companydevより

gemは★500に迫る勢いです。関係ありませんが、最近medium.comの技術ブログを見かけることが妙に増えました。


つっつきボイス: 「jbって知らなかったけど、amatsudaさんのgemか」「新しいgemですね」
「うん、Rails標準のJBuilderよりこっちの方が自然に書けそう: JBuilderのインターフェースはあまり直感的でないところがあって、書き足していくうちにだんだん居心地が悪くなってきたり」
「jbでよくないのは名前ぐらいかなw」「こんなに短いとねー」

ちょうど対照的なコードサンプルがあったので貼ってみました。

# jbの場合(https://github.com/amatsuda/jb)
json = {
  content: format_content(@message.content),
  created_at: @message.created_at,
  updated_at: @message.updated_at,

  author: {
    name: @message.creator.name.familiar,
    email_address: @message.creator.email_address_with_name,
    url: url_for(@message.creator, format: :json)
  }
}

if current_user.admin?
  json[:visitors] = calculate_visitors(@message)
end

json
# JBuilderの場合(https://github.com/rails/jbuilder)
json.content format_content(@message.content)
json.(@message, :created_at, :updated_at)

json.author do
  json.name @message.creator.name.familiar
  json.email_address @message.creator.email_address_with_name
  json.url url_for(@message.creator, format: :json)
end

if current_user.admin?
  json.visitors calculate_visitors(@message)
end

大文字でJBといえば:

Errbit: AirBrake API準拠のエラーログ収集gem


github.com/errbit/errbitより

BPSでも標準的に使われています。


つっつきボイス: 「Errbitシラナカッター」「シラナカッター」「マジでw TechRachoにもErrbitの記事ありますよ↓」「その頃ワタイらBPSにおりませんでした」
「タイトルにもあるようにAirBrakeのAPIと互換性あって、AirBrakeのgemをそのまま使うし:
置き場所がAirBrakeじゃないだけ」
「Rails以外にも使えるし、↑上の図でもわかるようにエラーをとりあえずグループ分けしてくれるところとかありがたい: 細かくは他の方法で見たりもするけど」
「結果をSlackに流しておくとさらに便利ですヨ~」
「以前はAirBrakeが定番だったけど、有料化したとたんにみんなすごい勢いでalternative探してErrbitが浮上してきた」

参考

Trailblazer: Rails MVCのビジネスロジックを分離するgemスイート


trailblazer.toより

JSON HALを調べていてTrailblazerにたどり着きました。Trailblazerについては昨年のRailsウォッチでごく簡単に触れたことがあり、★も多いのですが、日本語での情報をほとんど見かけないので、今回少しだけ一同で眺めてみました。

# http://trailblazer.to/gems/operation/2.0/index.html より
class Song::Create < Trailblazer::Operation
  extend Contract::DSL

  contract do
    property :title
    validates :title, presence: true
  end

  step     Model( Song, :new )
  step     :assign_current_user!
  step     Contract::Build()
  step     Contract::Validate( )
  failure  :log_error!
  step     Contract::Persist(  )

  def log_error!(options)
    # ..
  end

  def assign_current_user!(options)
    options["model"].created_by =
      options["current_user"]
  end
end

つっつきボイス: 「へーなるほど、コントローラの制御ロジックをstepOperationに切り出したり↑」「Contractでステートを記述したり↓」「SalesForceみたいに、大きなビジネスロジックを扱うアプリを開発するための基盤という感じ」「日本のSIer的な開発スタイルに向いているのかも」「昨今はどっちかというとマイクロサービスアーキテクチャ指向なんで、今後流行るのかなあ、どうなんだろう」

# app/concepts/song/contract/create.rb
module Song::Contract
  class Create < Reform::Form
    property :title
    property :length

    validates :title,  length: 2..33
    validates :length, numericality: true
  end
end

参考: Railsにおけるサービス層の導入と感触

trailblazer n. 先駆者, 草分け.

オピニオン: gemを追加する前にもうちょっと考えよう

短いながらも良記事です。


つっつきボイス: 「『車輪の再発明を避けようとしてgemだらけになる』ことのデメリットについて触れてるのがいい」「あまりにトリビアな機能を追加するgemとか『それ、書いた方が早くね?』って思うこと多いし」「たとえばどんなgemでしょうか?」「cts-as-taggable-onとかw」

compartmentalization: {名} : 区画化、区分化、区分け、棲み分け

米国Kite社がオープンソースツールに広告を仕込んで非難轟々


theoutline.comより

先の「We need better compartmentalization」の記事中のリンクで知りました。

仕込まれたとされるのはATOMエディタのプラグインであるMinimapautocomplete-pythonだそうです。

CIとCD(Continuous Deployment)とCD(Continuous Delivery)の違い(RubyFlowより)

semaphoreci.comより


つっつきボイス: 「この図↑が( ・∀・)イイ!!」「Continuous DeliveryとContinuous Deploymentの違いがひと目でわかりますね」
「CDとCDって、略語が完全一致しているのが向こうの人にとっても残念だろうな」「まあよくあることだし」

OWASPチートシートでRailsのセキュリティ問題を防止(Random Rubyより)

記事は普通な感じですが、OWASPの方が話題になりました。


つっつきボイス:OWASPは強制力こそ持たないけど、セキュリティ関係のガイドラインとかチェックシート的などを提案・啓蒙している団体ですね」「大手企業が実施するセキュリティチェックの品質監査ガイドラインなんかでもOWASPを参照していることがあったりするので、覚えておくとよいです」
「少なくともIPAよりは信頼されてるw」


https://www.owasp.orgより

Elixirを学んだらRubyが上達したお(Ruby Weeklyより)

Elixirの特徴に触れることでRubyのコーディングに好影響があったという割りと短い記事です。

# https://www.amberbit.com/blog/2017/7/27/how-learning-elixir-made-me-better-ruby-developer/ より
class Counter
  attr_reader :value

  def initialize(payload)
    @value = payload.to_i
  end

  def increment
    Counter.new(@value + 1)
  end
end

SOLIDの「D」: 依存関係逆転法則(Ruby Weeklyより)

SOLIDの連載シリーズです。他の記事も読み応えありそうです。


つっつきボイス: 「この図も、独特だけど見せ方が面白い」「六角形ぇェ」「SOLIDってDDD(ドメイン駆動開発)関連の記事で見たことがあったような」

RubyをJavaScriptっぽくする(Ruby Weeklyより)


kevinkuchta.comより

これでもRubyです:

  var first = 3;
  var second = 4;

  var sum = function(a, b) {
    a + b;
  }

  console.log("Sum = ", sum(first, second));

つっつきボイス: 「(´д`)イヤァ-ン!!ww」「これはひどずw」「しかも古いJavaScriptでw」

Railsで時間を変えてテストする方法(RubyFlowより)


つっつきボイス: 「タイトルでtimecop使ってるの丸わかりw」「テストで時間を変えるのってタイムトラベルって呼ぶんですね」「タイムトラベルらしく、timecopは時間を移動する他に元の時間に戻ったりもしますよ」

Passenger 5.1.7リリース(RubyFlowより)

前回のメジャーアップデートでのChangelogが多すぎて追いきれませんでした。

Passenger Libraryというサポートサイトがやたら充実しています。

Cooper Pressの技術情報ニュースレター


cooperpress.comより

英語圏の技術情報サイトが過剰気味な中、Cooper Pressのニュースレターは編集が効いていて選別が割りとよい感じです。いくつかはWeb上で直接読めるものもあり、ニュースレターのみのものも最新版ならWeb上で読めます。RailsウォッチでもRuby Weeklyをよく参照しています。

みなドメインが違うので最初気づきませんでしたが、レイアウトが共通なので同じところで発行されていることがわかります。

Cocoon: jQueryで動的なフォームのネストをやりやすくするgem

Driftingruby.comのNested Forms with Cocoonで見つけました。★2400です。


つっつきボイス: 「管理画面みたいにUIの制約が少ない案件だとちょっと便利だったりするかな」「案件で添付ファイル機能とか要求されなければいけるかも」

オピニオン: あらゆる言語を学べ(Random Rubyより)

真ん中あたりで「早いうちにC言語をやっとけ」「アセンブラもやっとけ」と書いてあったのが目に止まりました。

  1. 自分にレッテルを貼るのをやめる(私はRails開発者です、など)
  2. メタなことをやる
  3. 他の言語の元となる言語(archetypal languages)をやる
  4. ひたすら書く


blog.bradfieldcs.comより


つっつきボイス: 「ひと目でbrainf*ckとわかる↑」「視覚的な知名度ハンパないw」「Array programmingってなんだろ?」「つ ↓」

Rubyで機械学習: k-meanクラスタリング(RubyFlowより)


www.practicalai.ioより

The Last API Wrapper: Web APIのラッパー作成用gem(RubyFlowより)

★はまだ100個に到達していません。


つっつきボイス:grapeというものが前からあるよん↓」


github.com/ruby-grape/grapeより

Southeast Ruby Conference: 米国ナッシュヴィルで10月に開催(RubyFlowより)

テネシー州ナッシュヴィルでの開催というのがちょっと珍しく感じたので取り上げてみました。

GitLocalize: GitHubベースのローカライズ基盤サービス

https://github.com/marketplace/gitlocalizeより

つい先ほど知りました。GitHub自ら始めたサービスかと思いきや、よく見るとmarketplaceなのでサードパーティでした。
まだ詳しくみていませんが、GitHubと統一的に使えるらしい点がインパクトありそうです。



docs.gitlocalize.com/about.htmlより

もしかしたらですが、ローカライズ業界に変化が訪れる予感がうっすらとしています。

Nginxのセキュリティ対策ベスト25

2010年と新しくはありませんが、そこそこまとまってるように見えたので。


つっつきボイス: 「早々に『SELinuxをオンにしろ』とか時代を感じる」「量は多いけど、ふつううううううううううううのことしか書いてないな、さすがにw」

そこから、Apacheの違いやSELinuxについて話題になりました。

「Apacheはhttpd.confでまとめて扱えるから楽といえばとても楽: その代わりそれがセキュリティ上の弱点にもなる」
「Nginxは一か所にまとまってないし、自力で設定する気になれないような設定ファイルもある」
「SELinuxは確かに固いけど、固すぎて不自由さがハンパない」

遅いと思っていたWEBRickが…

各所でバズっている記事です。

gosu: Ruby向け2Dゲーム開発ライブラリ(Awesome Rubyより)

CSS(Frontend Weeklyより)

CSS-Tricks: Shadow DOMで遊んでみたお

<iframe>タグの進化版ともいえるShadow DOMでTwitterのツイートを埋め込んでみつつ、Shadow DOMを解説しています。

CSSは90年代後半以来複雑になったか

npmjs.comでマルウェアが大量に発見される

これも各所で話題になっています。

Chrome 60 が正式リリース

7月末にChrome 60がアップデートされました。私も本記事を書きながらアップデートしたら思ったより時間がかかってしまい、ハマりました。

BPS社内では新機能のAudit Panelが話題になりました。

私はV8の正規表現の強化(Unicodeプロパティの扱いとか)が今後予定されている方に個人的に盛り上がってしまいました。

/\p{Number}/u.test('①');      // true
/\p{Alphabetic}/u.test('雪');  // true

正規表現で迷路解いたりしてみた

こういう遊び好きです。

Go言語ベースやってますが、この記事で使っているdlclark/regexp2というパッケージは実は.NET Frameworkの正規表現ライブラリをGoに移植したもので、Goの標準正規表現ライブラリよりずっと強力であり、実は私もこっそり愛用してたりします。

// https://github.com/dlclark/regexp2 より
if m, _ := re.FindStringMatch(`Something to match`); m != nil {
    // the whole match is always group 0
    fmt.Printf("Group 0: %v\n", m.String())

    // you can get all the groups too
    gps := m.Groups()

    // a group can be captured multiple times, so each cap is separately addressable
    fmt.Printf("Group 1, first capture", gps[1].Captures[0].String())
    fmt.Printf("Group 1, second capture", gps[1].Captures[1].String())
}

番外

JSONで取れるデータあらいざらい(GitHub Trendingより)

最初opendatajson/football.jsonにしようかと思いましたが、こっちの方が断然すごかったので。

音楽、ゲーム、映画、アニメ、金融、詐欺チェック、チャック・ノリスねた、Tumblr、飛行履歴、天気など、よくぞこれだけ集めたと思います。

TypeScriptの型システムはチューリング完全

// 止まらない再帰
type Foo<T extends "true", B> = { "true": Foo<T, Foo<T, B>> }[T];
let f: Foo<"true", {}> = null!;

GitHub Issueでこれだけボタンが押されているのを自分は初めて見ました。

SNSはボキャブラリーの発達に好影響?ロシアの研究

最初に見たのは日本語記事「SNSばかりやっているとバカになる? ロシアの研究で意外な発見」でしたが、途中から有料のうえ、元記事の英語なら全文読めました。

香港のAIチャットが党を批判してサービス停止

各所で話題になっていますが、ソースは今のところこれ以外に見当たりません。QQ wheatgrass was banned, we talk about the chat robot “crime” Historyは、それを受けた評論記事です。


今週は以上です。

バックナンバー(2017年度)

今週の主なニュースソース

ソースの表記されていない項目は独自ルート(TwitterやRSSなど)です。

Ruby 公式ニュース

Rails公式ニュース

Ruby Weekly

Awesome Ruby

Random Ruby

Ruby on Rails Security Project

RubyFlow

160928_1638_XvIP4h

Hacker News

160928_1654_q6srdR

Github Trending

160928_1701_Q9dJIU

Vue.jsサンプルコード(12)特定の単語を入力したらボタンを無効にする

$
0
0

12. 特定の単語を入力したらボタンを無効にする

  • フィールドに「WIP」という単語が含まれている場合に「Merge」ボタンを無効にします。
  • 「WIP」が他の英単語の一部の場合は無視されます。
  • 「WIP」が日本語などの2バイト文字に接している場合は単語とみなされません。
  • 小文字の「wip」でも「Merge」ボタンは無効になります。

サンプルコード


ポイントはreturn /\bWIP\b/i.test(this.a)の行です。正規表現でiを指定しているので大文字小文字は区別されません。

 const vm = new Vue({
    el: "#app",
    data: {a: "WIP: 親譲りの無鉄砲で小供の時から損ばかりしている"},
    computed: {
      b: function() {
        return /\bWIP\b/i.test(this.a)  👈
      },
    },
  })

aでHTMLのinputクラスのv-model属性を参照しています。

<input class="form-control" v-model="a" />

補足: ES6のメソッド定義の省略形

ECMAScript 2015(ES6)より、上のコードのb: function()部分を以下のようにb()と略記することもできます(Vue.jsに限りません)。ブラウザの互換性にご注意ください。

b() {
  return /\bWIP\b/i.test(this.a)
},


Rails: render_async gemでレンダリングを高速化(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

Rails: render_async gemでレンダリングを高速化(翻訳)

Railsのコントローラにコードを追加するときに、さまざまな問題が発生することがあります。コントローラのアクションは非常に長くなることがあり、処理も複雑になりがちです。他の問題として、データ量の増加にともなってページの読み込みが重くなることもよくあります。コントローラのアクションにコードを追加した結果、一部のアクションで実行に失敗するとレンダリングが行われず、ユーザーに悪印象を残してしまうこともあります。

私たちのSemaphoreでもこの種の問題に何度か遭遇しました。通常は、コントローラのアクションをより小さなアクションに分割し、素のJavaScriptで非同期レンダリングすることで解決しています。

しばらくやっているうちに、これをrender_asyncというRailsページ高速化gemに切り出せることに気づきました。このgemは、RailsサーバーへのAjax呼び出しを行ってコンテンツを非同期にHTMLに読み込みます。

問題1: 時間とともに重くなる

新しいコードを足すと、Railsコントローラのアクションは肥大化します。気をつけておかないと、コードの量やデータ量の増加にともなってページの読み込みがだんだん重くなります。

問題2: アクションをブロックするコードへの対処

コントローラに新しいコードを追加する際、ビュー全体のレンダリングのためにコントローラのアクション内で追加のデータも読み込まないといけなくなることがあります。

ここで、アクションのレンダリングがブロックされてしまうコードの実例を見てみましょう。

movies_controller.rbのshowアクションで、データベースから映画を1件フェッチし、それに対応するレーティングも外部のIMDBから読み込みたいとします。

class MoviesController < ApplicationController
  def show
    @movie = Movies.find_by_id(params[:id])

    @movie_rating = IMDB.movie_rating(@movie)
  end
end

find_by_idで映画を読み込む部分は普通のコードです。ここでは自分たちが管理するデータベースから映画を1件検索しようとしています。

しかし映画のレーティングの読み込み部分では、外部IMDBへの追加リクエストに対して回答を受け取れることを当てにしているため、この外部サービスが不能になったりダウンしたりしたときに問題が生じます。こうなるとMoviesController#showがダウンし、本来の映画の読み込みまでできなくなってしまいます。

解決方法

render_async gemを使うことで、この2つの問題を解決できます。このgemは、Railsページのレンダリング完了後にコンテンツを非同期で読み込みます。

ページにHTMLを追加する既存のJavaScriptコードの代わりにrender_asyncを使うのはなぜでしょうか。その理由は、JavaScriptでの退屈なフェッチやリプレースをrender_asyncが代行してくれるからです。

render_asyncの動作

特定の映画と外部サービスから取得したレーティングの詳細を表示するapp/views/movies/show.html.erbというファイルがあるとします。

render_asyncを使う前のコードは次のとおりです。

# app/views/movies/show.html.erb

<h1>Information about <%= @movie.title %></h1>

<p><%= @movie.description %></p>

<p>Movie rating on IMDB: <%= @movie_rating %></p>

render_asyncを使えば、次のように書けます。

# app/views/movies/show.html.erb

<h1>Information about <%= @movie.title %></h1>

<p><%= @movie.description %></p>

<%= render_async movie_rating_path(@movie.id) %>

show.html.erbの読み込み後に、render_asyncによって映画のレーティングのセクションが読み込まれます。このページではmovie_rating_pathへのAjaxリクエストがjQueryで作成され、AjaxのレスポンスがページのHTMLでレンダリングされます。

render_asyncは特定のパスへのリクエストを作成するので、config/routes.rbにそのパスを追加しておく必要があります。

# config/routes.rb

get :movie_rating, :controller => :movies

ルーティングで指定したアクションをコントローラにも追加する必要があります。ここではmovies_controller.rbにmovie_ratingアクションを追加します。

# app/controllers/movies_controller.rb

def movie_rating
  @movie_rating = IMDB.movie_ratings(@movie)

  render :partial => "movie_ratings"
end

このmovie_ratingではパーシャルをレンダリングするので、対応するパーシャルも作成する必要があります。

# app/views/movies/_movie_rating.html.erb

<p>Movie rating on IMDB: <%= @movie_rating %></p>

もっとも肝心なのは、アプリケーションレイアウトにcontent_forタグを追加しておくことです。その理由は、render_asyncがAjaxレスポンスを処理するコードをそこに埋め込むためです。

# app/views/layouts/application.html.erb

<%= content_for :render_async %>

外部のIMDBサービスがダウンまたは応答停止した場合は、映画のレーティング情報以外のコンテンツがページに読み込まれるので、ページの他の部分は外部サービスの影響を受けなくなります。

まとめ

今回の例では、Railsページのレンダリングをrender_asyncで高速化するために以下を行いました。

  1. MoviesControllershowアクションをシンプルにしてテストや読み込みをやりやすくした。
  2. 映画レーティングのマークアップをパーシャルに移動した(Railsではおすすめのパターン)。
  3. 外部サービスが落ちてもshowアクションがブロックされないようにした。

render_asyncの追加機能や改良案がありましたら、render_asyncまでプルリクかissueをお送りください。

ご意見やご感想がありましたら、元記事のコメント欄にどうぞ。この記事がお役に立ちましたらぜひ共有をお願いします。


お知らせ: Semaphoreではスピードこそすべてであり、CIシステムを高速かつ快適にするのが私たちのミッションです。顧客と多くのやり取りや経験を重ね、あらゆるテストスイートを自動並列化して数分で完了できる新しいCI「Semaphore Boosters」を構築しました。詳しくはこちらをご覧ください。

関連記事

Vue.jsサンプルコード(13)「承認」チェックボックスをオンにしないと「送信」ボタンを押せないようにする

$
0
0

13. 「承認」チェックボックスをオンにしないと「送信」ボタンを押せないようにする

  • 「承認」チェックボックスがオフだと「送信]ボタンが半透明になり、押しても効きません。
  • 「承認」チェックボックスをオンにすると「送信」ボタンが押せる状態になります。
  • 画面を再読み込みすると最初の状態に戻ります。

サンプルコード


ポイント: htmlのボタンの属性の:disabled="!a"で、aの論理値を反転しています。

        <div class="btn btn-primary" :disabled="!a">
          送信
        </div>

「承認」チェックボックスがオフの状態だと、「送信」ボタンの属性はdisabled="true"ではなくdisabled="disabled"になるのでご注意ください。


CSS/JS: 画像を不規則な形に切り取ってテープで貼り付けるテクニック(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

注: 英語記事執筆の時点からブラウザのバージョンが進んでいるため、本記事内の細かなCodePen埋め込み表示は元記事のCodePen表示と若干異なっている可能性があります。また、ブラウザによってもCodePen表示が若干異なる可能性があります。

CSS/JS: 画像を不規則な形に切り取ってテープで貼り付けるテクニック(翻訳)

この間、Vasilis van Gemert氏のサイト「Atlas of Makers」のことがふと頭をよぎりました。とても面白く風変わりなこのサイトに惹かれて調べてみたところ、ここはまさに学ぶ価値のあるサイトであると確信できました。このサイトには数年におよび記事やよもやま話がたくさん詰まっていますが、サイト制作に使われている機能が実にクールなのです。このサイトの技法にはCSS Grid、カスタムプロパティ、blendモードや、SVGまで動員されていますが、それにもかかわらずなぜかそれほど知られていません。

このサイトでは四角でない不定形の画像の作成にSVGを使っており、まるでページに蛍光テープやスコッチテープで貼り付けたかのように表示されています(スコッチテープエフェクト)。本記事では、ブラウザだけで作業できるシンプルな手法でこの技法を再現する方法について解説します。それでは始めましょう。

訳注: 以下、原文のscotch tapeは単に「テープ」と表記します。

1. 画像のポリゴン頂点座標の取得

まずは題材選びから。こちらのsnow leopard(ユキヒョウ)画像を例として使うことにします。

ふわふわのsnow leopardが好奇の目でこちらを見ている画像です

続いて、このsnow leopardの輪郭に合わせてざっくりポリゴンで囲みます。今回はClippy(作: Bennett Feely)というサービスを使いました。CSSのclip-pathは現時点ではクロスブラウザではないため、ここでは使いません(使いたい方はMicrosoft Edgeのフォーラムで投票してください: ログイン不要です)。Clippyはとても素晴らしいサービスで、うんざりするほどのボタンやらオプションやらを装備した画像レタッチソフトウェアを使わずに、ポリゴンの頂点のデータだけをきわめて高速に取り出せます。

次は画像のカスタムURLとカスタム寸法を設定します。Clippyではviewportのサイズに沿って寸法を制限できますが、ここでは実際の画像のサイズはまったく重要ではありません(特に、最終出力は%値だけで指定されるため)。この画像にはアスペクト比2:3だけを指定することにします。

ClippyでサイズとURLを指定する

Clippyでカスタム寸法とURLを指定する場所は上のスクリーンショットに印をつけてあります。カスタム寸法の縦横比は2:3になるようにする必要があります。幅は540 = 2*270、高さは810 = 3*270とします。

作業を説明しやすくするため、Show outside clip-pathオプションをオンにしておきます。

アニメーションGIF: Show outside clip-pathをオンにしておくと後で役に立ちます。猫の周りをポリゴンの頂点でおおまかに囲むときに、ポリゴンの外側が部分表示で見えるようになります。

続いて、カスタムポリゴンをクリッピングパスで使えるように、すべての頂点を選択し、パスをきちんと閉じてから頂点の位置を適当に調整します。


ネコの輪郭をごく大まかに外側からなぞるようにカスタムポリゴンの点を選択すると、CSSのclip-pathコードが生成されます。

2. ブラウザのdeveloper consoleでの作業

このポイント(%値)のリストを選択し、ブラウザのdeveloper consoleを開いて以下のJavaScriptコードを貼り付け、文字列部分にポイントリストを貼り付けます。

// JS(見やすさのため適当に改行を挿入しています)
let coords = '69% 89%, 84% 89%, 91% 54%,
              79% 22%, 56% 14%, 45% 16%,
              28% 0, 8% 0, 8% 10%,
              33% 33%, 33% 70%,
              47% 100%, 73% 100%';

次のコードをコンソールに貼り付けて、%記号をすべて取り除き、文字列を分割します。

// JS
coords = coords.replace(/%/g, '').split(', ').map(c => c.split(' '));

続いて画像の寸法を指定します。

// JS
let dims = [736, 1103];

これで、画像の寸法の座標を拡大縮小できるようになりました。得られた値は次のJavaScriptコードで四捨五入します。画像がかなり大きいので、ネコの輪郭をなぞるポリゴン座標の小数点部分は不要です。

// JS
coords = coords.map(c => c.map((c, i) => Math.round(.01*dims[i]*c)));

後は以下のコードを実行すればおしまいです。

// JS
`[${coords.map(c => `[${c.join(', ')}]`).join(', ')}]`;

得られた値をブラウザのdeveloper consoleからコピーしてフォームに貼り付けます。

ブラウザのdeveloper toolsでの作業

3. SVG画像の生成

今度はPugでSVG画像を生成しましょう。ここでは2.で取得した頂点座標の配列をそのまま使っています。

// pug
- var coords = [[508, 982], [618, 982], [670, 596], [581, 243], [412, 154], [331, 176], [206, 0], [59, 0], [59, 110], [243, 364], [243, 772], [346, 1103], [537, 1103]];
- var w = 736, h = 1103;

svg(viewBox=[0, 0, w, h].join(' '))
  clipPath#cp
    polygon(points=coords.join(' '))
  image(xlink:href='snow_derpard.jpg'
        width=w height=h
        clip-path='url(#cp)')

上のコードを実行すると、次のように不規則な形の画像を得られます。

See the Pen irregular shaped image by Ana Tudor (@thebabydino) on CodePen.

4. 不規則な画像にテープエフェクトを追加する

いよいよページの画像にテープを貼り付けます。テープを生成するために、先ほどと同じ座標の配列を使います。テープの長さを座標から読み出すコードを繰り返すために、まずループを書きます。

// pug
-// (上と同じなので省略)
- var n = coords.length;

svg(viewBox=[0, 0, w, h].join(' '))
  -// (上と同じなので省略)
  - for(var i = 0; i < n; i++) {

  - }

続いてこのループ内で、頂点から次の頂点にテープを貼るかどうかを乱数で決めます。

// pug
- for(var i = 0; i < n; i++) {
  - if(Math.random() > .5) {
    path(d=`M${coords[i]} ${coords[(i + 1)%n]}`)
  - }
- }

strokenoneになっているので、これだけではテープは表示されません(Codepen)。

次のようにhsl()にランダムなhue値を与えてstrokeを太くすると、テープが表示されるようになります。

// scss
stroke: hsl(random(360), 90%, 60%);
stroke-width: 5%;
mix-blend-mode: multiply

さらにmix-blend-mode: multiplyを指定することで、よりテープらしく見えるようにしています。

See the Pen irregular shaped image with tape #1 by Ana Tudor (@thebabydino) on CodePen.

5. クロスブラウザ化

だいぶよくなってきましたが、実はまだ問題がいくつか残されています。

最初にして最大の問題は、クロスブラウザになっていないということです。mix-blend-modeはまだEdgeで動きません(まだの方はぜひ投票をお願いします!)。Edgeでほぼ同等の効果を得るために、Edgeの場合のみstrokeを半透明にすることにします。

当初私が思いついた手法は、RGBコンポーネントでサポートされない非整数をcalc()値で整数に変換するというものでしたが、この方法は現時点ではEdgeでしかサポートされていません。今使っているのは、残念ながらrgb()値ではなくhsl()値です。今回は幸いScssを使っているので、これでRGBコンポーネントを抽出できます。

// scss
$c: hsl(random(360), 90%, 60%);
stroke: $c;
stroke: rgba(calc(#{red($c)} - .5), green($c), blue($c), .5)

最下部の行はEdgeには適用されますが、calc()の動作によってChromeとFirefoxでは無効になります(下図左がChrome、右がFirefox)。

2つ目のstrokeの指定がChrome(左)とFirefox(右)で無効になっている

ただし、今後他のブラウザがEdgeと同じ動作になればこの手は使えなくなります。

今後も使える方法としては、@supportsがよいでしょう(codepen)。

path {
  $c: hsl(random(360), 90%, 60%);
  stroke: rgba($c, .5);

  @supports (mix-blend-mode: multiply) { stroke: $c }
}

6. テープの端を重ね貼り表示する

次の問題は、テープの端をもう少し伸ばしてテープらしく表示する方法です。幸い、この問題はstroke-line-capsquareに設定するだけで簡単に修正できます。これを指定すれば、簡単にテープの両端をテープ幅の半分だけ長くできます。

See the Pen irregular shaped image with tape #3 by Ana Tudor (@thebabydino) on CodePen.

7. テープのはみ出しが切り落とされないようにする

最後の問題は、テープがSVG画像の端で切り落とされてしまうことです。SVGのoverflowプロパティをvisibleに設定したとしても、SVGの内容が切り落とされてしまったり、画像の次に来る要素で隠されてしまったりする可能性が残ります。

これについては、imageの周りのviewBoxスペースを増やせばどうにかできそうです。テープで隠れないようにするのに十分なスペース増加分を、ここではpと呼ぶことにします。

-// 同上
- var w1 = w + 2*p, h1 = h + 2*p;

svg(viewBox=[-p, -p, w1, h1].join(' '))
  -// 同上

このpの値をどうやって決めるかが問題です。

理論編

値を決めるときには、今使っているstroke-widthの値が%になっている点に注意しておく必要があります。SVGでは、stroke-widthなどの%値はSVG領域の対角線の長さから算出されます。今回の場合、SVG領域は幅w、高さhの四角形(rectangle)です。この四角形に対角線を引くと、ご存知ピタゴラスの定理によって以下の黄色い三角形の斜辺を計算できることがわかります。

SVGの四角形に斜辺を引くと、直角の隣にある2辺がSVG viewBoxの幅と高さになる

したがって斜辺の長さは以下のPugコードで得られます。

// pug
- var d = Math.sqrt(w*w + h*h);

ここからは、stroke-widthを対角線の長さの5%で計算することにします。これは対角線d.05をかけたものと同等です。

// pug
- var f = .05, sw = f*d;

ここで得た値は、%値(5%)からユーザー設定の単位(.05*d)に変換されていることにご注目ください。こうしておくと、viewBoxの寸法を増やしたときに対角線の長さも増え、対角線の5%の長さも得られるので便利です。

strokeを描画すると、あらゆるpathについて幅の半分はパスの線の外側に、半分はパスの線の内側にそれぞれはみ出ます。今やりたいのは、viewBoxのスペースをstroke-widthの半分の長さより大きく取ることです。前述のstroke-linecapによって、パスの両端からstroke-widthの半分だけはみ出す点にも注意しておく必要があります。


`stroke`はパスの線の両側面に半分ずつはみ出し、`square-linecap`によって`stroke-width`の半分だけ端点からはみ出す。

実践編

ここからは実践編として、クリッピング用ポリゴンの頂点の1つが元画像のちょうど端にかかっている状況を考えてみましょう。こうした点がいくつあっても方法は同じですが、話を簡単にするために、ここでは1つの点についてだけ考えることにします。

以下のようにポリゴンの端点Eが元画像の最上部の境界にかかっているとします。さらに、切り出したSVGでも同様であるとします。

元画像の最上部の境界にある点を示す画像。

今知りたいのは、作成したstrokestroke-linecapsquareが設定されている場合に、画像最上部の境界からどのぐらいはみ出すかです。はみ出す長さは境界との角度によって変わるので、はみ出し部分が切り落とされてしまわないよう、境界のさらに上に確保する最大の余白を知りたいのです。

この点をより深く理解するために、次のインタラクティブなデモを用意しました。デモを開き、スライダを左右に動かしてはみ出した部分を回転させると、stroke(およびsquare-linecap)の隅が境界の外側でどのぐらいはみ出すかを実感できます。

See the Pen rotation at the top edge by Ana Tudor (@thebabydino) on CodePen.

strokestroke-linecapの外側の隅が描く軌道をたどるとわかるように、画像の境界の外側に必要な余白は、境界上の端点Eと、strokeおよびstroke-linecapによってできた隅(図ではAまたはB、位置は角度によって異なる)との間の線分が境界と垂直になるときに最大になり、必要な余白の長さはこの線分AE(またはBE)の長さに等しくなります。

テープの端はstroke-widthの半分の長さだけ端点Eからはみ出します。このはみ出しは、余弦PBと通常の方向EPで同じ長さになります。したがって、三角形EBPは2辺の長さがそれぞれstroke-widthの半分に等しい直角二等辺三角形となります。線分EBの長さは、この直角二等辺三角形の斜辺の長さになります。

パスの端点Eと、strokeおよびstroke-linecapではみ出した隅(AまたはB)との間の線分の長さは、stroke-widthの半分の長さを2辺とする直角二等辺三角形の斜辺と等しくなることを示す

ふたたびピタゴラスの定理に登場いただくと、Pugコードは次のようになります。

// pug
- var hw = .5*sw;
- var p = Math.sqrt(hw*hw + hw*hw) = hw*Math.sqrt(2);

これまでの結果をすべて合わせると、次のPugコードになります。

// pug
/* 座標と初期サイズは同上 */
- var f = .05, d = Math.sqrt(w*w + h*h);
- var sw = f*d, hw = .5*sw;
- var p = +(hw*Math.sqrt(2)).toFixed(2);
- var w1 = w + 2*p, h1 = h + 2*p;

svg(viewBox=[-p, -p, w1, h1].join(' ')
    style=`--sw: ${+sw.toFixed(2)}px`)
  /* 同上 */

後はCSS側で、stroke-widthの値を与えてテープのはみ出しを調整するだけです。

// scss
stroke-width: var(--sw);

この--swは、stroke-widthを設定してcalc(var(--sw)*1px)を算出するのに使うため、単位のない値は使えない点にご注意ください。理論的にはこれでも動くはずですが、現実にはFirefoxとEdgeでstroke-*calc()値がまだサポートされていません。

CodePen上の最終的な表示は、次のようになります。

See the Pen irregular shaped image with tape #4 by Ana Tudor (@thebabydino) on CodePen.

関連記事

新版: やばい臭いのするCSSコード(翻訳)

Webデザイナーがこっそり教える4つのテクニック

CSSでの句読点ぶら下げ: hanging-punctuationプロパティ

週刊Railsウォッチ(20170908)Rails 5.1.4と5.0.6リリース、コード書換え支援gem「synvert」、遅いテストを分析するTestProfほか

$
0
0

こんにちは、hachi8833です。ジェットコースターみたいに上下する気候はこたえますね。

6日に発生した太陽表面での大規模爆発の影響が本日15:00から夜半にかけて地球規模で生じるそうで、高緯度を中心にオーロラ発生が見込まれるほか、通信機器やGPSに障害が出るかもしれないとのことです。この時期に大きなリリースや祝賀ミサイル発射などをご検討の方は十分ご注意ください。


https://www.nict.go.jp/press/2017/09/07-1.htmlより

臨時ニュース: Rails 5.1.4と5.0.6が正式リリース

本日出たてほやほや。バグ修正が中心です。

いつもの記念写真です。

RubyKaigi 2017@Hiroshima開催迫る

今年のRubyKaigiももう再来週ですね。9/18(月)〜9/20(水)、広島の国際会議場です。

RubyKaigiは、ビジネス志向寄りのRuby World Conferenceと対照的にギーク色が強く、Rubyエンジニアの祭典という側面が前面に出ています。

年号付きの大規模なRubyKaigiは、会場のご当地名物をシンボルに採り入れています。昨年は京都の風景でしたが、今年はやはり広島・厳島神社の鳥居が使われています。

私も昨年のRubyKaigi 2016@京都や今年の大江戸Ruby会議に参加してみましたが、いずれもいい年こいてギンギンに意識が高まってしまったのが思い出されます。

Ruby界隈の特徴として、うかつな質問とかしたら後ろからバッサリ斬られそうなコワイ人がとんと見当たらないのがとてもありがたい点です。社交辞令ではなく、ユーモアやジョークを真面目に愛するいい人たちばかりだったという思いです。今回気になる点があるとすれば、今年の会場ではどのぐらいスムーズにWiFiにつなげるかぐらいでしょうか。

皆さまも今年こそ思い切って参加してみてはいかがでしょう。得られるものは想像以上に大きいと思います。
弊社からはTechRachoでおなじみmorimorihogeと、不肖私めも参加いたします。

Ruby Kaigi 2016に全日参加しました!(hachi8833)

追記

Rails公式

Rails公式の更新情報がここ数週間怒涛のように増えていて、遠い昔の夏休みの宿題を思い出してしまいました。

RailsのSDocを更新

group :doc do
-  gem "sdoc", "> 1.0.0.rc1", "< 2.0"
+  gem "sdoc", github: "robin850/sdoc", branch: "upgrade"
   gem "redcarpet", "~> 3.2.3", platforms: :ruby
   gem "w3c_validators"
   gem "kindlerb", "~> 1.2.0"

コードの変更量は少ないですが、SDocのテーマ変更やSEOタグ追加などいろいろ変わるようです。

SDocドキュメントってどこに行ったら見られるんだろうかと一瞬考えてしまいましたが、Railsディレクトリでsdocを実行して生成するのでした。すっかり忘れていましたが、ドキュメントの生成はかなり時間がかかるので、.gemrcに以下を書いてスキップしている人がほとんどかと思います。

# ~/.gemrc
install: --no-rdoc --no-ri
update:  --no-rdoc --no-ri

今回の変更はまだmasterブランチにしかないので、今日の5.1.4にはまだ含まれていません。以下は更新前のSDoc動画です。そのうちhttp://api.rubyonrails.org/で使えるようになるのでしょうか。

ActiveStorageのドキュメント更新

# activestorage/README.md
...
-Image files can further more be transformed using on-demand variants for quality, aspect ratio, size, or any other
-MiniMagick supported transformation.
+Image files can furthermore be transformed using on-demand variants for quality, aspect ratio, size, or any other [MiniMagick](https://github.com/minimagick/minimagick) supported transformation.
...

つっつきボイス: 「最初はからっぽだったActiveStorageドキュメントも着々と整備されてますね」「エライエライ」

「ところで、Railsガイドにはもう反映されてるかしら」「うむ、ないでござる」「じゃEdgeGuidesはどうだろう: こっちに先に掲載されるのが普通なので」


edgeguides.rubyonrails.orgより

「こっちにもまだないか」「EdgeGuidesなんてのがあったのね: シラナカッター」

Rails: 先々週の改修

Hash#deep_mergeの改良

あまり見かけない1ファイルのみの変更です。

# activesupport/lib/active_support/core_ext/hash/deep_merge.rb
  # Same as +deep_merge+, but modifies +self+.
   def deep_merge!(other_hash, &block)
-    other_hash.each_pair do |current_key, other_value|
-      this_value = self[current_key]
-
-      self[current_key] = if this_value.is_a?(Hash) && other_value.is_a?(Hash)
-        this_value.deep_merge(other_value, &block)
+    merge!(other_hash) do |key, this_val, other_val|
+      if this_val.is_a?(Hash) && other_val.is_a?(Hash)
+        this_val.deep_merge(other_val, &block)
+      elsif block_given?
+        block.call(key, this_val, other_val)
       else
-        if block_given? && key?(current_key)
-          block.call(current_key, this_value, other_value)
-        else
-          other_value
-        end
+        other_val
       end
     end
-
-    self
    end
  end

つっつきボイス: 「↑再帰やってるかなと思ったけどやってなさげ」「deepの付くコピーやマージはRuby言語側で直接サポートしていないこともあって実装もいろいろ、かつ改良が絶えない感じですね。」

「deepなんちゃらって、やっぱり言語でやらない方がいいんでしょうか?」「ワイはそう思う: deepなんちゃらは一般化しづらいし、言語で責任取りきれないっしょ」

Eager loadingで一発目のレスポンスタイムを短縮

次のPRとも関連しているようです。

# actionmailer/lib/action_mailer/railtie.rb
+    initializer "action_mailer.eager_load_actions" do
+      ActiveSupport.on_load(:after_initialize) do
+        ActionMailer::Base.descendants.each(&:action_methods) if config.eager_load
+      end
+    end
+
     config.after_initialize do |app|
       options = app.config.action_mailer

:action_controllerでのパラメータ設定読み込みを繰り返さないようにした

# actionpack/lib/action_controller/railtie.rb
-      ActiveSupport.on_load(:action_controller) do
+      ActiveSupport.on_load(:action_controller, run_once: true) do
         ActionController::Parameters.permit_all_parameters = options.delete(:permit_all_parameters) { false }
         if app.config.action_controller[:always_permitted_parameters]
           ActionController::Parameters.always_permitted_parameters =
# activesupport/lib/active_support/lazy_load_hooks.rb
       base.class_eval do
         @load_hooks = Hash.new { |h, k| h[k] = [] }
         @loaded     = Hash.new { |h, k| h[k] = [] }
+        @run_once   = Hash.new { |h, k| h[k] = [] }
       end
     end

...

+    private
+
+      def with_execution_control(name, block, once)
+        unless @run_once[name].include?(block)
+          @run_once[name] << block if once
+
+          yield
+        end
+      end
+
+      def execute_hook(base, options, block)
+        with_execution_control(name, block, options[:run_once]) do

つっつきボイス: 「この2つのコミット、関連してそうですね」「実装上はたぶん独立してるけど、目的は関連してるかも」
「1つめのif config.eager_loadで、設定ファイルからでもコントロールできるようにしてるのか」

uniquenessバリデーションで:scopeが効かないことがあったのを修正

# activerecord/test/cases/validations/uniqueness_validation_test.rb
+  def test_validate_uniqueness_with_scope_invalid_syntax
+    error = assert_raises(ArgumentError) do
+      Reply.validates_uniqueness_of(:content, scope: { parent_id: false })
+    end
+    assert_match(/Pass a symbol or an array of symbols instead/, error.to_s)
+  end

つっつきボイス: 「おおー、uniquenessでは今まで:scope使えてなかったのか」「改修部分よりテストの方がわかりやすいかと思ってテストを引用してみました」


ソースのactive_record/validations/uniqueness.rbを見ると、scopeのドキュメントは前からあったようです。

Ruby trunkより

String#valid_encoding?の副作用

content = "\xE5".dup.force_encoding(Encoding::ASCII_8BIT)

content.encode(Encoding::UTF_8, Encoding::UTF_8, invalid: :replace, replace: '?')
=> "?"

content.valid_encoding?

content.encode(Encoding::UTF_8, Encoding::UTF_8, invalid: :replace, replace: '?')
=> "\xE5"

つっつきボイス:content.valid_encoding?って何じゃらほいと思ったら、これを実行したら結果が変わってたってことか: これはイヤなバグだ」「破壊的でないはずのメソッドが破壊的に振る舞ってたのか(怖」

「ところでEncoding::ASCII_8BITってエンコードひどいw: UTF-8とすら互換性なくなりますね」

ハッシュが空なのにHash#compact!nilを返す

irb(main):001:0> {}.compact!
=> nil

# For Comparison
irb(main):002:0> { foo: nil }.compact!
=> {}
irb(main):003:0> {}.compact
=> {}
irb(main):004:0> { foo: nil }.compact
=> {}

つっつきボイス: 「Rubyだと{x: 3}.compact!{x: 3}じゃなくてnil返してる? 一瞬意味わからんと思ったけど、配列だと[3].compact!nilになるから、それと揃えたってことか」
!系メソッドの戻り値ってあてにしない方がよさげですなー」
「どうやら、挙動はこのままが正しくて、ドキュメントの方が違ってたってことみたいですね↓」

compact! → hsh
Removes all nil values from the hash. Returns the hash.

Rubyだと、他に返すものがないからとりあえずnilを返す、みたいな破壊的メソッドがそこそこある気がしました。
nilを埋め草的に使うのはあんまりよくなさそうな気がします。

Rails

JOINすべきかどうか、それが問題だ(Ruby Weeklyより)

良記事です。


つっつきボイス: 「このタイトルも、記事の小見出しもシェークスピアのハムレットをもじりまくってるんですよw: 誰がうまいこと言えと」「何人わかるんかしらそれ: ドラクエで説明してくれー」「イギリス人かと思ったけど著者名ラテン系だし」

「N+1を回避するために、INNER JOINなのかOUTER JOINなのか迷うのに、さらにRailsで eager_load とか #includes とか色々あって、どれにしたらいいのか考えるのがつらいでござるよ」「この記事、まさにそのあたりを衝いてるみたい: 翻訳したろ」

Rails: N+1クエリを「バッチング」で解決するBatchLoader gem(翻訳)


ついでながら、actの本来の意味は「演技する」や「(劇の)場面」のことだったりします。サブタイトルに「An act of #includes」とあるのもまさにそれです。第一幕、第二幕みたいなのをact I、act IIなどと表したりします。

英語だと「演技する」「演じる」「上演する」「演奏する」がplayやperformanceやactという基本的過ぎる言葉で表されることがとっても多いので、こういう言葉が短い見出しで使われていると意味を取り違えないよう緊張してしまいます。

⭐synvert: Rubyのコードをいろいろ変換するリファクタリングgem⭐(Ruby Weeklyより)


xinminlabs.github.io/synvertより

あまり追えていませんが、かなりよさげなコード変換ツールです。

gem install synvert  # これでインストールできる
synvert --sync       #最初に一回だけ実行: スニペットを取ってくる
synvert -l           #現在のスニペットのリストを表示↓

default
check_syntax
factory_girl
fix_deprecations
use_new_syntax
use_short_syntax
rails
convert_dynamic_finders
convert_mailers_2_3_to_3_0
convert_models_2_3_to_3_0
convert_rails_env
convert_rails_logger
convert_rails_root
convert_routes_2_3_to_3_0
convert_views_2_3_to_3_0
redirect_with_flash
strong_parameters
upgrade_2_3_to_3_0
upgrade_3_0_to_3_1
upgrade_3_1_to_3_2
upgrade_3_2_to_4_0
upgrade_4_0_to_4_1
upgrade_4_1_to_4_2
upgrade_4_2_to_5_0
upgrade_5_0_to_5_1
rspec
be_close_to_be_within
block_to_expect
boolean_matcher
collection_matcher
custom_matcher_new_syntax
explicit_spec_type
its_to_it
message_expectation
method_stub
negative_error_expectation
new_config_options
new_hook_scope
one_liner_expectation
pending_to_skip
remove_monkey_patches
should_to_expect
stub_and_mock_to_double
use_new_syntax
ruby
block_to_yield
fast_syntax
gsub_to_tr
iconv_to_encode
keys_each_to_each_key
map_and_flatten_to_flat_map
merge_to_square_brackets
new_hash_syntax
new_lambda_syntax
new_safe_navigation_operator
parallel_assignment_to_sequential_assignment
remove_debug_code
use_symbol_to_proc
shoulda
fix_deprecations
use_matcher_syntax
use_new_syntax
will_paginate
use_new_syntax

備え付けのスニペットもいろいろあり、upgrade_5_0_to_5_1のようなアップグレード向けの書き換えや、new_hash_syntaxのような非推奨文法を推奨文法に書き換えられるスニペットだけでも便利そうです。

convert_models_2_3_to_3_0のようなスニペットは、Rails 2系から3系へのアップグレードのような考えるだけでつらくなる作業を助けてくれそうです。DSLでスニペットを書くこともできます。

railsdiffと合わせて使えば、Railsのアップグレードがとってもはかどりそうな予感。


つっつきボイス: 「こやつはrubocopチャンと喧嘩するやつ?」「どっちかというと書き換えがメインなんで、住み分けできそうですね」


期待を込めて、今週の⭐を進呈いたします。おめでとうございます。

ドメインイベントでRailsの複雑なドメインを分割する(Ruby Weeklyより)


blog.carbonfive.comより


つっつきボイス: 「この場合のドメインは設計上の話でござるか」「ドメインって言葉、文脈でいろいろ変わってくるのでめんどいですね」
「Passengerとルーティングを例に取ってる: このぐらいだったらまとめて扱ってもいい気はするけど、分離してイベントでゴニョゴニョする方が幸せになれるっていう意図なのかな」「ビジネスロジックを分離するみたいな、理想だけど実践するのは大変なやつに見えますね: この間のウォッチ↓で扱ったTrailbrazerをちょっとだけ連想しました」

週刊Railsウォッチ(20170804)Rails 5.1.3と5.0.5が正式リリース、GitHubでローカライズ基盤サービス、正規表現で迷路を解くほか


trailblazer.toより

Rails + ActionCableで即応性の高いアプリを作る(RubyFlowより)


www.icicletech.comより


つっつきボイス: 「原文でReal Timeって言ってるけど、ガチでリアルタイム処理研究している人からツッコミ入りそうな気がしましたw: そのあたりよくわかってませんが」「記事は実用的でよさげ」

TestProf: Rubyのテストが遅くなる原因を診断するツール(Ruby Weeklyより)


evilmartians.comより

テストのどこで時間がかかっているのかをプロファイリングしてくれます。CIに組み込んでくれとツールが私の耳元でささやいているような気がしました。
記事もツールの紹介だけではなく、自分でプロファイルするうえでも役に立つことが書かれており、Tips to improve speed of your test suiteという記事を参考にリストアップしています。


つっつきボイス: 「このツールよさそう: アプリの改修が進むとテストも増えて時間かかるので、こういう問題は切実」「遅いテストは、開発者の意欲をザクザク削ってしまうのもデメリットですね」「そうなんでござるよ: デプロイのたびにCIで何十分も待たされるとか、耐え難い」

関数は小さい方がよいとは限らない(Awesome Rubyより)


edium.com/@copyconstructより

Rubyなどの言語ではコードをDRYに書くことが推奨されていますが、ルールを盲目的に守ればいいというものではないという主張です。ちょっと長いですが、良記事だと思います。


つっつきボイス: 「DRYに書けばいいってもんじゃない、と言ってますね」「確かに: メソッドを細かく割りすぎると今度は読みにくいんですよ、マジで」
「関数やメソッドを細かく分割しすぎると、名前つけるだけで大変だし、切れっ端みたいなコードがあちこちで再利用されると副作用が変なところに出てきたりする、とも言ってます」「まったくじゃわいw」


ところで、「considered Harmful」は最近流行りの煽り見出しのようですね。↓こういう記事の見出しなら内容に合ってると思いますが、何度も見かけるとちょっと鼻についてしまいます。

Railsの`CurrentAttributes`は有害である(翻訳)

コード品質ツールチェックの効果についてのアンケート(Awesome Rubyより)

さっと読める記事です。


rubyblog.proより


つっつきボイス: 「ツールで品質が良くなりましたか?というアンケート↑、2割近くが『なってない』って答えてるw」
「↓これも切実だなー: ツールにどこをチェックして欲しい?の回答みると、complexity(複雑さ)とかデザパタとかOOP準拠とかコードの臭いとかが上位」「ツールが苦手なところばっかりですねw: スタイルみたいな型にはめるチェックはだいぶできるようになりましたが」
「もちろんツールがないと困るし、コードレビューでスペースインデントみたいなどうでもいいところで時間取られたくないでござるよ」「まったく」


rubyblog.proより

FabricationとFactoryGirl、どっちがいいの?(Ruby Weeklyより)


ksylvest.comより

ざっくりベンチ取ってるだけの記事なので、すぐ読めます。結果はここには書きません。

factoryとfixtureを両方使いたいお(RubyFlowより)


evilmartians.comより


つっつきボイス: 「一つ前の記事と見出しがよく似てますw」
「あー、factoryとfixtureか: 自分の理解ではfixtureは↓こんなyamlとかの固定テストデータ、factoryは動的に生成されるテストデータ」「fixtureの本来の意味は『簡単に取り外せない(ホテルなんかの)備品』ですね」「まさしく、fixtureは備品のイメージだわ: 無理してどっちかに統一することはないでござるよ」

rubyonrails:
  id: 1
  name: Ruby on Rails
  url: http://www.rubyonrails.org

google:
  id: 2
  ;name: Google
  url: http://www.google.com

「記事の結論↓: factoryとfixtureは適材適所で使い分けよ、って」

Our conclusion is obvious: do not oppose factories and fixtures, bring them all and in the darkness bind them to rule your test suites mightily.


ところで、年がバレるのも構わずにIT業界の英語でやたら使われるのが、RPGの遠いご先祖とも言える「ロード・オブ・ザ・リング」の冒頭のナレーション↓のもじりです。上のは無理やり過ぎてだいぶ壊れてますが。

Three Rings for the Elven-kings under the sky,
Seven for the Dwarf-lords in their halls of stone,
Nine for Mortal Men doomed to die,
One for the Dark Lord on his dark throne
In the Land of Mordor where the Shadows lie.

One Ring to rule them all, One Ring to find them,
One Ring to bring them all and in the darkness bind them
In the Land of Mordor where the Shadows lie.
https://en.wikipedia.org/wiki/One_Ringより

「Oneなんとか、Oneなんとか、Oneなんとか」が出てきたら、「ああアレね」と生暖かい目で華麗にスルーしてあげてください。ついでながら、Martian Chroniclesというブログサイト名にも年を感じてしまいました

Railsのポリモーフィック関連付けを学ぶ(Ruby Weeklyより)


semaphoreci.comより

目新しい内容ではなさそうですが、シンプルなのですぐ読めそうです。

Visual Studio CodeでRailsしてみたった


medium.com/@PaulWritesCodeより

言われてみればVS Studioでもできそうですね。やってる人をまだ見たことがありません。

Rubyで混乱しやすい機能6つ(RubyFlowより)

著者はどことなくヴェトナム人っぽい名前ですね。rsyncを6歳児にもわかるように説明するという記事なども面白そうです。

  1. []メソッド
  2. %演算子
  3. Integer#zero?メソッド
  4. $[数字]グローバル変数
  5. 万能すぎてつらいTime.parse
  6. delegatorの挙動がRubyドキュメント↓と違う

Equality — At the Object level, == returns true only if obj and other are the same object. – Ruby documentation.


つっつきボイス: 「↓6.はちょっと言い過ぎな気がするかも: equalityとは何かみたいなのは文脈にもよるんだし、obj idが違うから同じではいけないというものでもないんじゃなかろうか」

# https://hqc.io/posts/six-confusing-features-in-ruby より
# 6.のコード
class Foo < Delegator
  def initialize(the_obj)
    @the_obj = the_obj
  end

  def __getobj__
    @the_obj
  end
end

foo = Foo.new(1)
foo.inspect # => 1
foo == 1 # => true

「そういえばRubyのトリプルイコール===case文の条件以外では使わないこと、というのがRubyスタイルガイドにありました」「そうそう」「RubyとJavaScriptで===の挙動が全然違うのってつらいですね、あれw」

Rubyの===演算子についてまとめてみた

Gemを書くときのチェックリスト(RubyFlowより)


gemcheck.evilmartians.ioより

ブラウザ上でそのままチェックリストをオンオフできます。
こういう形式の記事、結構便利かもです。

RubyでSlackコマンドAPIを書く

Railsでの画像アップロードをShrine.rbとDropzone.jsで実現(RubyFlowより)


codyeatworld.comより

4回シリーズの記事です。


つっつきボイス: 「Amazon S3に置くのが前提みたいです: ところでRailsの定番アップローダーgemって何でしたっけ」「carrierwave

JavaScript

deeplearn.jsをGoogleが発表

元記事: Harness the Power of Machine Learning in Your Browser with Deeplearn.js
公式サイト: https://pair-code.github.io/deeplearnjs/


research.googleblog.comより

機械学習も着々とパッケージ化されてきてますね。実は日本語記事も出ています。

Upterm: Electronベースのターミナルソフトウェア

★16000超えの快挙です。


つっつきボイス: 「自分のMacbook Proに入れてみました: ほれほれ」「おおお、凄いでないのコレ!: htopの後に自動で画面キャプチャまでしてくれた」
.bashrcとかまったく読み込まれなかったので、ほとんど素っ裸のシェルですが、それでもこれだけ使えるので、とにかく見せ方がうまいですね」

Android

flexbox-android


github.com/google/flexbox-layoutより

こちらもGoogle公式のライブラリです。こちらも★9000超えです。


つっつきボイス: 「これイイナー: Androidじゃなくても使いたい」

その他

homebrew-cask-upgrade: caskのバージョンチェックとアップグレードツール

個人的にとても便利だったので。合言葉はbrew cu
むしろ、これなしでbrew cask使いたくないくらいです。

プログラマーが健康と引き換えに失ったもの(RubyFlowより)


hackernoon.comより

急な激しい運動は身体を痛める元になるので、皆さまもご注意。

bettercap: 侵入テスト向けの中間者攻撃ツール(Ruby Weeklyより)

いわゆる『人に向けてはいけない』ツールです。

番外

これ欲しい

神経線維が光ファイバーになる日は来るか

あれは何だったのか


今週は以上です。

バックナンバー(2017年度)

今週の主なニュースソース

ソースの表記されていない項目は独自ルート(TwitterやRSSなど)です。

Rails公式ニュース

Ruby Weekly

Awesome Ruby

RubyFlow

160928_1638_XvIP4h


JavaScriptスタイルガイド 1〜8: 型、参照、オブジェクト、配列、関数ほか (翻訳)

$
0
0

  • 1〜8: 型、参照、オブジェクト、配列、関数ほか — 本記事
  • 9〜14: クラス、モジュール、イテレータ、プロパティ、変数、ホイスティング
  • 15〜26: 比較演算子、ブロック、制御文、型変換、命名規則ほか

概要

AirbnbによるJavaScriptスタイルガイドです。
MITライセンスに基いて翻訳・公開いたします。


github.com/airbnbより

凡例

原文にはありませんが、利便性のため項目ごとに目安となる分類を【】で示しました。

  • 【必須】【禁止】:従わないと技術的な悪影響が生じる
  • 【推奨】【非推奨】:技術上の理由から強く推奨される、または推奨されない
  • 【選択】:採用してもしなくてもよいスタイル
  • 【スタイル】:読みやすさのためのスタイル統一指示
  • 【知識】:指示に該当しない基礎知識

なお、Translationに日本語を含む各国の既存訳へのリンク一覧があります。

JavaScriptスタイルガイド 1〜8: 型、参照、オブジェクト、配列、関数ほか (翻訳)

メモ: 本ガイドではBabelの利用を前提とします。また、babel-preset-airbnbあるいは同等のライブラリが必要です。他に、airbnb-browser-shimsまたは同等のライブラリでshims/polyfillsをアプリにインストールしていることも前提とします。

1. 型(type)

1.1 【知識】プリミティブ型: プリミティブ型にアクセスすると、その値を直接操作する。

  • string
  • number
  • boolean
  • null
  • undefined
const foo = 1;
let bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9

1.2 【知識】 複合型: 複合型にアクセスすると、(値そのものではなく)値への参照で操作する。

  • object
  • array
  • function
const foo = [1, 2];
const bar = foo;

bar[0] = 9;

console.log(foo[0], bar[0]); // => 9, 9

2. 参照(reference)

2.1 【推奨】参照は常に constを使い、varは避けること。

理由: constを使うことで、参照への再代入を防止できる。参照への再代入はバグを誘発し、コードを読みづらくする。

// Bad: 定数をvarで宣言している
var a = 1;
var b = 2;

// Good: 定数をconstで宣言している
const a = 1;
const b = 2;

2.2 【必須】参照の再代入にはvarではなくletを使うこと。

理由: letはブロックスコープであり、varなどのような関数スコープではない。

// Bad
var count = 1;
if (true) {
  count += 1;
}

// Good: letを使うべき
let count = 1;
if (true) {
  count += 1;
}

2.3 【知識】letconstはどちらもブロックスコープである点に注意すること。

// constとletはどちらもそれらが宣言されたブロック内でのみ有効
{
  let a = 1;
  const b = 1;
}
console.log(a); // ReferenceError
console.log(b); // ReferenceError

3. オブジェクト(object)

3.1 【推奨】オブジェクトの作成にはリテラル文法を使うこと(newを使わないこと)。

// Bad
const item = new Object();

// Good
const item = {};

3.2 【推奨】オブジェクト作成時にプロパティ名を動的に与える場合は、computedプロパティ名(訳注: ECMAScript 6の機能)を使うこと。

理由: computedプロパティ名を使うと、オブジェクトの全プロパティを1箇所で定義できる。


function getKey(k) { return `a key named ${k}`; } // Bad const obj = { id: 5, name: 'San Francisco', }; obj[getKey('enabled')] = true; // Good const obj = { id: 5, name: 'San Francisco', [getKey('enabled')]: true, };

3.3 【推奨】オブジェクトメソッドのショートハンド表記(訳注: ES6の機能)を使うこと。

// Bad
const atom = {
  value: 1,

  addValue: function (value) {
    return atom.value + value;
  },
};

// Good: 「addValue(value)」ショートハンド形式にする
const atom = {
  value: 1,

  addValue(value) {
    return atom.value + value;
  },
};

3.4 【推奨】プロパティ値のショートハンドを使うこと。

理由: 短くて書きやすく、内容を端的に表せる。

const lukeSkywalker = 'Luke Skywalker';

// Bad
const obj = {
  lukeSkywalker: lukeSkywalker,
};

// Good
const obj = {
  lukeSkywalker,
};

3.5 【スタイル】ショートハンドプロパティは他のプロパティと混ぜず、オブジェクト宣言の冒頭にまとめる。

理由: ショートハンド形式のプロパティであることをわかりやすくするため。

const anakinSkywalker = 'Anakin Skywalker';
const lukeSkywalker = 'Luke Skywalker';

// Bad
const obj = {
  episodeOne: 1,
  twoJediWalkIntoACantina: 2,
  lukeSkywalker,
  episodeThree: 3,
  mayTheFourth: 4,
  anakinSkywalker,
};

// Good
const obj = {
  lukeSkywalker,
  anakinSkywalker,
  episodeOne: 1,
  twoJediWalkIntoACantina: 2,
  episodeThree: 3,
  mayTheFourth: 4,
};

3.6 【推奨】プロパティを引用符で囲む場合は、識別子として無効な名前だけを引用符で囲むこと。

理由: 主観的にはこの方が一般に読みやすいと考えられる。さらにシンタックスハイライトにもよい影響があり、さまざまなJSエンジンで最適化が効きやすくなる。

// Bad
const bad = {
  'foo': 3,
  'bar': 4,
  'data-blah': 5,
};

// Good: 'data-blah'だけ引用符で囲む
const good = {
  foo: 3,
  bar: 4,
  'data-blah': 5,
};

3.7 【禁止】Object.prototypeのメソッド(hasOwnPropertypropertyIsEnumerableisPrototypeOfなど)を(.prototypeを介さずに)直接呼んではならない。

理由: これらのメソッドは、そのオブジェクトのプロパティによって隠蔽される可能性がある。{ hasOwnProperty: false }の場合もあれば、オブジェクトが実際にはnullオブジェクト(Object.create(null))のこともある。

// Bad
console.log(object.hasOwnProperty(key));

// Good
console.log(Object.prototype.hasOwnProperty.call(object, key));

// ベスト
const has = Object.prototype.hasOwnProperty; // 探索はモジュールスコープ内で1度キャッシュされる。
/* 以下でもよい */
import has from 'has';
// ...
console.log(has.call(object, key));

3.8 【禁止】オブジェクトの「浅いコピー(shallow-copy)」ではObject.assign を使わないこと。
【推奨】spread演算子(訳注: ドット演算子とも呼ばれる)...の利用が望ましい。
【推奨】特定のプロパティが省略されるオブジェクトを取得する場合は...演算子で受けること。

// 非常に悪い
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 }); // `original`が変更されてしまう! ಠ_ಠ
delete copy.a; // これも`original`が消される

// Bad
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 });
// copy => { a: 1, b: 2, c: 3 }

// Good
const original = { a: 1, b: 2 };
const copy = { ...original, c: 3 };
// copy => { a: 1, b: 2, c: 3 }

const { a, ...noA } = copy;
// noA => { b: 2, c: 3 }

4. 配列(array)

4.1 【推奨】配列はリテラル文法で作成すること。

// Bad
const items = new Array();

// Good
const items = [];

4.2 【推奨】配列の項目はArray#push で作成すること。配列に項目を直接代入してはならない。

const someStack = [];

// Bad
someStack[someStack.length] = 'abracadabra';

// Good
someStack.push('abracadabra');

4.3 【必須】配列のコピーには配列のspread演算子...を使うこと。

// Bad
const len = items.length;
const itemsCopy = [];
let i;

for (i = 0; i < len; i += 1) {
  itemsCopy[i] = items[i];
}

// Good
const itemsCopy = [...items];

4.4 【必須】配列に近いオブジェクトを配列に変換する場合は、Array.from を使うこと。

const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);

4.5 【推奨】配列メソッドのコールバックではreturn文を使うこと。ただし、関数の内容が1文だけで、かつ副作用のない式を返す場合は8.2に基いてreturnを省略してもよい。

// Good
[1, 2, 3].map((x) => {
  const y = x + 1;
  return x * y;
});

// Good
[1, 2, 3].map(x => x + 1);

// Bad
const flat = {};
[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
  const flatten = memo.concat(item);
  flat[index] = flatten;
});

// Good
const flat = {};
[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
  const flatten = memo.concat(item);
  flat[index] = flatten;
  return flatten;
});

// Bad
inbox.filter((msg) => {
  const { subject, author } = msg;
  if (subject === 'Mockingbird') {
    return author === 'Harper Lee';
  } else {
    return false;
  }
});

// Good
inbox.filter((msg) => {
  const { subject, author } = msg;
  if (subject === 'Mockingbird') {
    return author === 'Harper Lee';
  }

  return false;
});

4.6 【スタイル】複数行に渡る配列では、開き角かっこ[の直後と閉じ角かっこ]の直前を改行すること。

// Bad
const arr = [
[0, 1], [2, 3], [4, 5],
];

const objectInArray = [{
id: 1,
}, {
id: 2,
}];

const numberInArray = [
1, 2,
];

// Good
const arr = [[0, 1], [2, 3], [4, 5]];

const objectInArray = [
{
  id: 1,
},
{
  id: 2,
},
];

const numberInArray = [
1,
2,
];

5. 分割代入(Destructuring)

5.1 【推奨】1つのオブジェクトで複数のプロパティにアクセスする場合は「オブジェクトの分割代入」を使うこと。

理由: プロパティの一時的な参照が作成されずに済むため。

// Bad
function getFullName(user) {
  const firstName = user.firstName;
  const lastName = user.lastName;

  return `${firstName} ${lastName}`;
}

// Good
function getFullName(user) {
  const { firstName, lastName } = user;
  return `${firstName} ${lastName}`;
}

// ベスト
function getFullName({ firstName, lastName }) {
  return `${firstName} ${lastName}`;
}

5.2 【推奨】「配列の分割代入」を使うこと。

const arr = [1, 2, 3, 4];

// Bad
const first = arr[0];
const second = arr[1];

// Good
const [first, second] = arr;

5.3 【推奨】複数の値を返す場合は「配列の分割代入」ではなく「オブジェクトの分割代入」を使うこと。

理由: 今後プロパティを追加したり順序を変更したりしても呼び出し側に影響を与えずに済むため。

// Bad
function processInput(input) {
  // この後えらいことになる
  return [left, right, top, bottom];
}

// 呼び出し元は戻り値の順序を常に気にしなければならない
const [left, __, top] = processInput(input);

// Good
function processInput(input) {
  // この後うれしいことが起きる
  return { left, right, top, bottom };
}

// 呼び出し側は欲しいデータだけを使えばよい
const { left, top } = processInput(input);

6. 文字列(string)

6.1 【スタイル】文字列は常に一重引用符' 'で囲むこと。

// Bad
const name = "Capt. Janeway";

// Bad - テンプレートリテラルは式展開か改行を含む場合に使うべき
const name = `Capt. Janeway`;

// Good
const name = 'Capt. Janeway';

6.2 【スタイル】100文字を超えるような複数行にわたる長い文字列を文字列結合などで分割しないこと。

理由: 分割された文字列は扱いづらく、コードの検索性も落ちる。

// Bad
const errorMessage = 'このウルトラスーパー長いエラーメッセージがスローされたのは\
バットマンのせい。バットマンがそんなことをする理由をいくら考えたところでさっぱり\
先に進まない。';

// Bad
const errorMessage = 'このウルトラスーパー長いエラーメッセージがスローされたのは' +
  'バットマンのせい。バットマンがそんなことをする理由をいくら考えたところでさっぱり' +
  '先に進まない。';

// Good
const errorMessage = 'このウルトラスーパー長いエラーメッセージがスローされたのはバットマンのせい。バットマンがそんなことをする理由をいくら考えたところでさっぱり先に進まない。';

6.3 【推奨】文字列をプログラムで組み立てる場合は+の文字列結合ではなく、テンプレート文字列を使うこと。

理由: テンプレート文字列は読みやすく、改行や文字列の式展開(interpolation)を正しく使える簡潔な文法であるため。

// Bad
function sayHi(name) {
  return 'How are you, ' + name + '?';
}

// Bad
function sayHi(name) {
  return ['How are you, ', name, '?'].join();
}

// Bad
function sayHi(name) {
  return `How are you, ${ name }?`;
}

// Good: ${name}にはスペースを含めないこと
function sayHi(name) {
  return `How are you, ${name}?`;
}

6.4 【禁止】eval()は決して文字列に対して使用してはならない。eval()は無数の脆弱性を引き起こす。

6.5 【推奨】文字列内の文字を不必要に\でエスケープしないこと。

理由: バックスラッシュ\は可読性を損なうため、本当に必要な場合にのみ使うこと。

// Bad
const foo = '\'this\' \i\s \"quoted\"';

// Good
const foo = '\'this\' is "quoted"';
const foo = `my name is '${name}'`;

7. 関数

7.1 【禁止】関数宣言(function declaration)は使わず、代わりに名前付き関数式(named function expression)を使うこと。

理由: 関数宣言はホイスティング(hoisting)されるため、ファイル内でその関数宣言の定義より上の位置から簡単に(あまりに簡単に)参照できてしまうため、可読性もメンテナンス性も損なわれる。ある関数定義が、ファイルの他の部分をすべて理解しなければならないほど巨大で複雑になっているのであれば、そろそろ関数を独自のモジュールに切り出してもよいだろう。その際、式には必ず名前をつけること。無名関数を使うとエラーのコールスタックで問題を特定するのが困難になるかもしれない(議論 )。

// Bad: 関数宣言
function foo() {
  // ...
}

// Bad: 無名関数
const foo = function () {
  // ...
};

// Good: 名前付き関数式
const foo = function bar() {
  // ...
};

7.2 【推奨】即時関数式(IIFE: immediately-invoked function expression)は丸かっこ( )で囲むこと。

理由: 即時関数式は単一のユニットであり、即時関数式とその呼出の丸かっこ()の両方を丸かっこ()で囲むことで明確に表現できるようになる。ただし、あらゆる場所でモジュールが使われているような世界では、即時関数式が必要になることはほぼまったくありえない。

// 即時関数式(IIFE)
(function () {
  console.log('インターネットへようこそ。フォローしてね。');
}());

7.3 【禁止】関数でないブロック(ifwhileなど)の中では決して関数宣言を行ってはならない。代わりに関数を変数に代入すること。ブラウザではそのようなコードでも動いてしまうが、コードはまったく違う形で解釈され、痛い目を見ることになる。

7.4 【知識】注: ECMA-262ではblockを文(statement)のリストと定義している。そして関数宣言は文ではないECMA-262の注記 を参照)。

// Bad
if (currentUser) {
  function test() {
    console.log('Nope.');
  }
}

// Good
let test;
if (currentUser) {
  test = () => {
    console.log('Yup.');
  };
}

7.5【禁止】 パラメータに決してargumentsという名前をつけてはならない。もし使うと、あらゆる関数スコープで与えられるargumentsオブジェクトが隠蔽されてしまう。

// Bad
function foo(name, options, arguments) {
  // ...
}

// Good
function foo(name, options, args) {
  // ...
}

7.6 【禁止】argumentsは決して使ってはならない。代わりに...記法を使うこと。

理由: ...を使うことで、どの引数を取得したいかが明確になる。さらに...記法の引数は本物の配列である。argumentsのような配列もどきではない。

// Bad
function concatenateAll() {
  const args = Array.prototype.slice.call(arguments);
  return args.join('');
}

// Good
function concatenateAll(...args) {
  return args.join('');
}

7.7 【禁止】引数を関数で変更してはならない。代わりにデフォルトパラメータ文法を使うこと。

// 非常に悪い
function handleThings(opts) {
  // 関数でこのように引数を変更するべきではない!
  // さらなる副作用: optsがfalsyの場合、必要なものがそのオブジェクトに
  // 運よく設定されるかもしれないが、微妙なバグの原因になるかもしれない
  opts = opts || {};
  // ...
}

// これでも悪い
function handleThings(opts) {
  if (opts === void 0) {
    opts = {};
  }
  // ...
}

// Good
function handleThings(opts = {}) {
  // ...
}

7.8 【推奨】デフォルトパラメータでは副作用の発生を避けること

理由: 動作の理解で混乱を招く。

var b = 1;
// Bad
function count(a = b++) {
  console.log(a);
}
count();  // 1
count();  // 2
count(3); // 3
count();  // 3

7.9 【必須】デフォルトパラメータは常に末尾に置くこと。

// Bad
function handleThings(opts = {}, name) {
  // ...
}

// Good
function handleThings(name, opts = {}) {
  // ...
}

7.10 【禁止】関数を決して関数コンストラクタで作成してはならない。

理由: 関数コンストラクタを使うと文字列がeval()と似た方法で評価され、脆弱性が発生する。

// Bad
var add = new Function('a', 'b', 'return a + b');

// still bad
var subtract = Function('a', 'b', 'return a - b');

7.11 【スタイル】関数シグネチャにはスペースを置くこと。

理由: スタイル統一のため。名前の追加や削除の際にこのスペースを増やしたり減らしたりすべきではない。

// Bad
const f = function(){};
const g = function (){};
const h = function() {};

// Good
const x = function () {};
const y = function a() {};

7.12 【禁止】受け取ったパラメータを変更してはならない。

理由: パラメータとして渡されたオブジェクトを改変すると、呼び出し元で意図に反して変数の副作用が発生する可能性がある。

// Bad
function f1(obj) {
  obj.key = 1;
}

// Good
function f2(obj) {
  const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
}

7.13 【禁止】パラメータに決して再代入してはならない。

理由: パラメータに再代入すると、特にargumentsオブジェクトにアクセスするする場合に思わぬ挙動が発生する可能性がある。さらに、特にV8エンジンで最適化に問題が発生する可能性もある。

// Bad
function f1(a) {
  a = 1;
  // ...
}

function f2(a) {
  if (!a) { a = 1; }
  // ...
}

// Good
function f3(a) {
  const b = a || 1;
  // ...
}

function f4(a = 1) {
  // ...
}

7.14 【推奨】variadic関数の呼び出しより、spread演算子...が望ましい。

理由: ...の方が簡潔であり、コンテキストを与える必要がない。applyを使うと(意味のない)newが簡単にはできなくなる。

// Bad
const x = [1, 2, 3, 4, 5];
console.log.apply(console, x);

// Good
const x = [1, 2, 3, 4, 5];
console.log(...x);

// Bad
new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]));

// Good
new Date(...[2016, 8, 5]);

7.15 【スタイル】シグネチャや呼び出しが複数行に渡る関数は、本スタイルガイドの他でも使われている方法でインデントすること。各項目は単独で1行ずつ記述し、最終項目末尾のカンマは省略しないこと。

// Bad
function foo(bar,
             baz,
             quux) {
  // ...
}

// Good
function foo(
  bar,
  baz,
  quux,
) {
  // ...
}

// Bad
console.log(foo,
  bar,
  baz);

// Good
console.log(
  foo,
  bar,
  baz,
);

8. アロー関数(arrow function)

8.1 【推奨】関数式を無名関数として渡さなければならない場合は、アロー関数記法を使うこと。

理由: アロー関数を使うと、thisのコンテキストで実行される関数が与えられる。これは多くの場合において適切であり、文法も簡潔になる。

使わない場合の理由: 関数がかなり複雑な場合は、ロジックを独自の関数宣言に切り出すことを検討すること。

// Bad
[1, 2, 3].map(function (x) {
  const y = x + 1;
  return x * y;
});

// Good
[1, 2, 3].map((x) => {
  const y = x + 1;
  return x * y;
});

8.2 【スタイル】関数の内容が1つのを副作用なしで返す単一の文の場合、波かっこ {}returnを省略するか、波かっこ {}returnを明示的に書くか、どちらかにする。

理由: シンタックスシュガーであり、複数の関数を連鎖したときに読みやすくなる。

// Bad
[1, 2, 3].map(number => {
  const nextNumber = number + 1;
  `A string containing the ${nextNumber}.`;
});

// Good
[1, 2, 3].map(number => `A string containing the ${number}.`);

// Good
[1, 2, 3].map((number) => {
  const nextNumber = number + 1;
  return `A string containing the ${nextNumber}.`;
});

// Good
[1, 2, 3].map((number, index) => ({
  [index]: number,
}));

// 副作用ありの場合のreturnの省略
function foo(callback) {
  const val = callback();
  if (val === true) {
    // コールバックがtrueの場合に何かする
  }
}

let bool = false;

// Bad
foo(() => bool = true);

// Good
foo(() => {
  bool = true;
});

8.3 【スタイル】式が複数行に渡る場合、丸かっこ( )で囲んで読みやすくする。

理由: 関数の開始と終了がわかりやすくなる。

// Bad
['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call(
    httpMagicObjectWithAVeryLongName,
    httpMethod,
  )
);

// Good
['get', 'post', 'put'].map(httpMethod => (
  Object.prototype.hasOwnProperty.call(
    httpMagicObjectWithAVeryLongName,
    httpMethod,
  )
));

8.4 【スタイル】引数を1つだけ取り、波かっこ{ }を使わない関数では、引数の丸かっこ( )を省略するか、読みやすさと一貫性のために引数を常に丸かっこ( )で囲むか、どちらかにする。
注: 常に丸かっこ( )で囲むスタイルも許容される。eslintでは“always” オプションをオンにし、jslintの場合はdisallowParenthesesAroundArrowParamをすることで設定できる。

理由: 可読性の向上。

// Bad
[1, 2, 3].map((x) => x * x);

// Good
[1, 2, 3].map(x => x * x);

// Good
[1, 2, 3].map(number => (
  `${number}を含むとても長い文字列。あんまり長いので.map行で場所を取りたくない!`
));

// Bad
[1, 2, 3].map(x => {
  const y = x + 1;
  return x * y;
});

// Good
[1, 2, 3].map((x) => {
  const y = x + 1;
  return x * y;
});

8.5 【スタイル】比較演算子(<=>=)と紛らわしい場合はアロー関数記法(=>)の利用を避ける。

// Bad
const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize;

// Bad
const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize;

// Good
const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize);

// Good
const itemHeight = (item) => {
  const { height, largeSize, smallSize } = item;
  return height > 256 ? largeSize : smallSize;
};

  • 1〜8: 型、参照、オブジェクト、配列、関数ほか — 本記事
  • 9〜14: クラス、モジュール、イテレータ、プロパティ、変数、ホイスティング
  • 15〜26: 比較演算子、ブロック、制御文、型変換、命名規則ほか

関連記事(JavaScript)

Vue.jsサンプルコード(01〜03)Hello World・簡単な導入方法・デバッグ・結果の表示とメモ化

JavaScript、jQuery入門ーフォーム作成で実際に使った例を振り返りながら

JavaScriptでElement.styleがnullになって焦った

HTML + CSS + JavaScript で簡単に導入できるdatetimepicker の比較

Rails アプリケーション開発で役に立ったJavaScript デバッグの小技

JSの非同期処理を初めてES6のPromiseを使ったものに書き換えてみた

JavaScriptでモーダルウィンドウを出すなら

Vue.jsサンプルコード(21)[はい][いいえ]ボタンを押すと表示を変える

$
0
0

21. [はい][いいえ]ボタンを押すと表示を変える

  • Vue.jsバージョン: 2.5.2
  • [はい]または[いいえ]ボタンを押すと「ありがとうございました!」に変わります。
  • 画面をリロードすると最初の状態に戻ります。

サンプルコード


ポイント: HTMLコード中のdata-vはHTML 5のカスタムデータ属性です。

    <div @click="a" data-v="yes">
      はい
    </div>
    <div @click="a" data-v="no">
      いいえ
    </div>

カスタムデータ属性は、JavaScriptのHTMLElement.datasetでリードオンリーで読み取れます。

   this.answer = e.target.dataset.v

バックナンバー(Vue.jsサンプルコード)

Vue.jsサンプルコード(01〜03)Hello World・簡単な導入方法・デバッグ・結果の表示とメモ化

Vue.jsサンプルコード(22)YouTube風の[👆][👇]ボタンで1度だけGood/Bad評価する

$
0
0

22. YouTube風の[👆][👇]ボタンで1度だけGood/Bad評価する

  • Vue.jsバージョン: 2.5.2
  • [👆]または[👇]ボタンを押すと、カウントが1上がります。
  • ボタンをもう一度押してもカウントは変わりません。
  • 画面をリロードすると最初の状態に戻ります。

サンプルコード


ポイント: $once()で定義したコードは、$emitで「1度だけ」実行されます。

    created: function() {
      this.$once("foo", function(e) { this[e.target.dataset.key] += 1 })
    },
    methods: {
      a: function(e) { this.$emit("foo", e) },
    },

バックナンバー(Vue.jsサンプルコード)

Vue.jsサンプルコード(01〜03)Hello World・簡単な導入方法・デバッグ・結果の表示とメモ化

JavaScript: 5分でわかるPromiseの基礎(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

JavaScript: 5分でわかるPromiseの基礎(翻訳)

JavaScriptのPromiseをわかりやすく解説しました。Promiseの基本を5分で学びましょう。

この記事で学べること

本チュートリアルでは、JavaScriptの「Promise」の基礎を学びます。Promiseのすべてを網羅的に説明するものではありませんが、Promiseを理解してコードで使い始めるのに必要な知識を固めることができます。

Promiseが必要になるとき

Promiseを使うと、(コールバックのように)特定のコードの実行完了を待ってから次のコード片を実行できるようになります。

Promiseが重要な理由は何でしょうか。あるWebサイトがAPIからデータを読み込んで処理し、データを整形してからユーザーに表示するところを考えてみましょう。APIから情報を取得する前にデータの処理と整形を行おうとすれば、エラーか空白ページのどちらかが表示されておしまいです。Promiseを使うと、API呼び出しが成功するまでAPIデータが処理されたり整形されたりしないようにできます。

Promiseとは何か

JavaScriptのPromiseは、同期操作の最終的な結果を表す、一種のプレースホルダと考えてください。本質的にこのプレースホルダは、コールバックをアタッチできる1つのオブジェクトです。

Promiseは、以下の3つのステートの1つを取ります。

  • pending: 非同期操作が完了していない
  • fulfilled: 操作が完了し、Promiseが値を1つ持つ
  • rejected : 操作がエラーまたは失敗で終了した

pending状態でないPromiseは安定しています。いったん安定したPromiseのステートはずっとそのままになり、他のステートに移行することはできません。

Promiseを使う

Promiseを使う場合、関数から返された既成のPromiseを使うことがほとんどですが、関数のコンストラクタからPromiseを作ることもできます。

シンプルなPromiseは以下のような感じになります。

runFunction().then(successFunc, failureFunc);

上のコード例では、最初にrunFunction()を実行しています。runFunction()はPromiseを1つ返すので、Promiseが安定した場合にのみsuccessFunc関数かfailureFunc関数のいずれかを実行できるようになります。PromiseがfulfilledになるとsucessFunc関数が呼ばれ、Promiseが失敗するとfailureFunc関数が呼ばれます。

Promiseの例

次は独自のPromiseを作成するコード例です。読んですぐわからなくても問題ありません。

function delay(t){
  return new Promise(function(resolve){
    return setTimeout(resolve, t)
  });
}
function logHi(){
  console.log('hi');
}
delay(2000).then(logHi);

このコードにはdelay()関数とlogHi()関数という2つの関数があります。logHi()関数は単にコンソールに'hi'と出力します。delay()関数はもう少し複雑で、指定のタイムフレームを経過した後で解決するPromiseを1つ返します。

then()メソッドを使って、最終的にfulfilledまたはrejectedのどちらかの値を受け取るコールバックを登録します。

以上を念頭に置いてdelay(2000).then(logHi)を呼び、2000ms(=2秒)という値をdelay関数に渡します。2秒経過するとPromiseが解決し、その場合に限ってlogHi関数が呼ばれます。

Google Chrome Developer Toolsを開いてこのコード例を入力することでお試しいただけます。

Promiseのチェイン

Promiseの主なメリットのひとつは、いくつもの非同期操作をチェインできることです。つまり、最初の操作が成功した場合に限って次の操作を開始するように指定できるということです。これをPromiseチェインと呼びます。以下の例をご覧ください。

new Promise(function(resolve, reject) {
  setTimeout(() => resolve(1), 2000);

}).then((result) => {
  alert(result);
  return result + 2;
}).then((result) => {
  alert(result);
  return result + 2;
}).then((result) => {
  alert(result);
  return result + 2;
});

最初のPromiseは、2000ms経過すると解決して値1を返します。解決後にthen()ハンドラが呼び出され、アラートボックスに値1が表示されます。最後に値が2に足され、新しい値3が返されます。この値が次のthen()ハンドラに渡され、この処理を繰り返します。

見てのとおり実地のコード例ではありませんが、Promiseが互いにチェインする様子がここに示されています。これは、JavaScriptで外部リソースを読み込む場合や処理前にAPIデータの到着を待つといった特定のタスクで非常に役立ちます。

エラーハンドリング

ここまでに扱ったPromiseは、どれも「解決済み」のものばかりでしたが、ここからは趣向を変えます。.catch()を使うと、Promiseチェインのエラーをすべてキャッチできます。次のコード例で.catch()の動作をご覧ください。

// ....
})
.catch((e) => {
  console.log('error: ', e)
}

上は.catch()のシンプルなコード例です。これは、返されたエラーメッセージをコンソールにログ出力します。先のコード例にエラーハンドリングを追加してみましょう。

以下のコード例は先ほどのものと2箇所しか違っていません。2番目の.then()の後ろにエラーとエラーメッセージを追加しました。また、チェインの末尾に.catch()も追加してあります。このコードを実行するとどうなるでしょうか。

new Promise(function(resolve, reject) {
  setTimeout(() => resolve(1), 2000);

}).then((result) => {
  alert(result);
  return result + 2;
}).then((result) => {
  throw new Error('FAILED HERE');
  alert(result);
  return result + 2;
}).then((result) => {
  alert(result);
  return result + 2;
}).catch((e) => {
  console.log('error: ', e)
});

結果は次のとおりです。

  • 2秒経過するとPromiseが値1で解決する
  • この値が最初の.thenに渡されてアラートダイアログが画面に表示される。2が足され、新しい値3が2番目の.then()に渡される。
  • 新しいErrorがスローされる。実行は直ちに停止してPromiseはrejectedステートで解決する。
  • .catch()はエラー値を受け取って画面にログを出力する。コンソールには次のように表示されます。

最後に

お読みいただきありがとうございました。Promiseのもっと詳しい情報についてはこちらこちらこちらをご覧ください。本記事がPromise導入のよいきっかけとなればと願っています。最終的にWeb開発を学ぶ用意のある方は、6か月でフルスタックWeb開発を学ぶ究極ガイドをぜひチェックしてみてください。

私はWeb開発記事を週に4本公開しています。毎週配信しているメーリングリストを購読したい方はフォームから登録いただくか、Twitterで私をフォローして下さい。

本記事が皆さまのお役に立ちましたら、元記事の下にある[👏]ボタンを数回クリックしてサポートをお願いします。⬇⬇

関連記事

JavaScriptの正規表現のコンセプトを理解する(翻訳)

JavaScriptスタイルガイド 1〜8: 型、参照、オブジェクト、配列、関数ほか (翻訳)

Vue.jsサンプルコード(23)テキストフィールドの行数に応じて縦幅を自動拡張する

$
0
0

23. テキストフィールドの行数に応じて縦幅を自動拡張する

  • Vue.jsバージョン: 2.5.2
  • テキストフィールドの行数が増えるとフィールドが下に拡張し、行数が減ると上に縮小します。
  • 画面をリロードすると最初の状態に戻ります。

サンプルコード


ポイント: jsファイルはほぼ空です。HTML側のVue.jsコードでは、改行の数を行数と仮定しています。

<textarea class="form-control" v-model="a" :rows="a.split(/\n/).length"></textarea>

バックナンバー(Vue.jsサンプルコード)

Vue.jsサンプルコード(01〜03)Hello World・簡単な導入方法・デバッグ・結果の表示とメモ化

Vue.jsサンプルコード(24)テキストフィールドの文章量に応じて縦幅を自動拡張する

$
0
0

24. テキストフィールドの文章量に応じて縦幅を自動拡張する

  • Vue.jsバージョン: 2.5.2
  • テキストフィールドの文章量が増えるとフィールドが下に拡張し、文章量が減ると上に縮小します。
  • 画面をリロードすると最初の状態に戻ります。

サンプルコード


ポイント: 前回の23. テキストフィールドの文章量に応じて縦幅を自動拡張するほど厳密ではありません。行数を予測可能なテキストについてのみ使えます。

<textarea class="form-control" v-model="a" :rows="4 + a.length / 40"></textarea>

バックナンバー(Vue.jsサンプルコード)

Vue.jsサンプルコード(01〜03)Hello World・簡単な導入方法・デバッグ・結果の表示とメモ化

Rails: JSON Patchでパフォーマンスを向上(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

Rails: JSON Patchでパフォーマンスを向上(翻訳)

FormAPIはPDFを生成するサービスです。バックエンドはRuby on Railsで書かれており、PDFテンプレートエディタはReactで作られています。PostgreSQLを用いてテンプレートのフィールドデータをjsonbカラムに保存しています。

テンプレートエディタの最初のバージョンでは、素朴なアプローチでフィールドデータを保存していました。フィールドが変更されるたびにarray全体をサーバーにpostしていたのです。立ち上げ当初のMVP(Minimum Viable Product)はこの方法で十分動きましたが、やがて顧客がPDFテンプレートに500個ものフィールドを設定するようになり、実行に5〜10秒も要するリクエストにアラートが出始めました。

明らかな解決法は、変更点だけをサーバーに送信することです。ReactアプリではReduxを使っているので、Reduxのアクションを送信することも考えましたが、Reduxのアクションには複雑なロジックが若干含まれているため、私たちのFormAPIではその手が使えませんでした。コードをRubyで書き直したくなかったのですが、仮にNode.jsを使っていたなら同じJavaScriptコードをサーバー側で再利用できたかもしれません。

他の選択肢としては、純粋なJSONオブジェクトの差分を取り出してそれをサーバーに送信する方法が考えられます。その場合JSON Patchを利用できます。

JSON Patchは、JSONドキュメントの変更点を記述するフォーマットです。JSONの一部だけが変更された場合にドキュメント全体の送信を避けるために利用できます。

こういうものは独自にこしらえる1よりも、IETF標準(RFC6902)を利用する方がほとんどの場合よい結果が得られると思います。以下のオープンソースライブラリを利用しました。

  • fast-json-patch — クライアント側でJSON Patchを生成するNPMパッケージです
  • hana — RailsサーバーでJSON Patchを適用するRuby gemです

以下のようにしてRailsモデルのjsonカラムにJSON Patchを適用しました。

class Template < ApplicationRecord
  # エラーが発生したらここでエラーを保存し、
  # バリデーション中にそれらを追加する
  attr_accessor :json_patch_errors
  validate :add_json_patch_errors
  after_save :clear_json_patches

  attr_reader :fields_patch

  # メソッドが呼び出されると即座にJSON Patchが適用される
  def fields_patch=(patch_data)
    # 後でアクセスしたい場合
    @fields_patch = patch_data
    self.json_patch_errors ||= {}
    json_patch_errors.delete :fields

    unless patch_data.is_a?(Array)
      json_patch_errors[:fields] =
        'JSON patch data was not an array.'
      return
    end

    hana_patch = Hana::Patch.new(patch_data)
    begin
      hana_patch.apply(fields)
    rescue Hana::Patch::Exception => ex
      json_patch_errors[:fields] =
        "Could not apply JSON patch to \"fields\": #{ex.message}"
    end
  end

  # データ再読み込み時にすべてのJSON Patchとエラーをクリア
  def reload
    super
    clear_json_patches
  end

  private

  def add_json_patch_errors
    return unless json_patch_errors.present?
    json_patch_errors.each do |attribute, errors|
      errors.add(attribute, errors)
    end
  end

  def clear_json_patches
    @fields_patch = nil
    self.json_patch_errors = nil
  end
end

こちらのRSpecテストをコピーして実装が正しいことを確認できます。そのうちこれをgemとしてリリースするかもしれません。

permittedパラメータとしてfields_patchをコントローラに追加しました。

params.require(:template).permit(
  fields: {},
).tap do |permitted|
  # arrayやhashのネストはややこしい
  if params[:template][:fields_patch].is_a?(Array)
    permitted[:fields_patch] = params[:template][:fields_patch].
      map(&:permit!)
  end
end

上のコードは、:fields_patchを通常の属性として扱い、update_attributesの間にJSON Patchを適用することを示しています。JSON Patchの適用に失敗すると、バリデーション中にエラーが追加されます。

フロントエンド側の実装は実に簡単です。改修前のコードは次のようになっていました。

if (!Immutable.is(template.fields, previousTemplate.fields)) {
  data.fields = template.fields.toJS()
}

新しいコードでは、JSON Patchをfields_patch属性として送信します。

import { compare as jsonPatchCompare } from 'fast-json-patch'

if (!Immutable.is(template.fields, previousTemplate.fields)) {
  data.fields_patch = jsonPatchCompare(
    previousTemplate.fields.toJS(), template.fields.toJS())
}

以下は新しいコードから送信されるAJAXリクエストの例です。

{
  "template": {
    "fields_patch": [
      {
        "op": "replace",
        "path": "/2/name",
        "value": "image_field_2"
      }
    ]
  }
}

コードの変更はわずか数行で済んだにもかかわらず、送信されるデータを大きく削減できました。

JSON Patchのもうひとつのメリットは、複数ユーザーによる同時編集をずっと楽にサポートできることです。JSON Patchは多くの場合任意の順序で適用することができます。replaceinsert操作しか含まれていない場合は特にそうです。衝突が発生した場合、最新のデータをリロードしてユーザーにやり直してもらうだけで済みます。また、WebSocketを使えばサーバーからブラウザにJSON Patchを送信して全クライアントを同期することもできます。

お読みいただきありがとうございました。コメントはHacker Newsまでどうぞ。

関連記事

Rails: ActiveModelSerializersでAPIを作る–Part 1(翻訳)

RailsでGraphQL APIをつくる: Part 1 – GraphQLとは何か(翻訳)

Ruby on Railsで使ってうれしい19のgem(翻訳)


  1. FormAPIの最初のバージョンではフィールド名記述に独自文法を使っていましたが、その後でJSON Pointersというものを発見しました。
    それまでスラッシュ文字/をエスケープするなど思いもよりませんでしたが、仕様の一部に含まれています。 

Vue.jsサンプルコード(25)クイズの答えをダイアログに入力しないと送信できないようにする

$
0
0

25. クイズの答えをダイアログに入力しないと送信できないようにする

  • Vue.jsバージョン: 2.5.2
  • [送信]ボタンを押すとダイアログにクイズが表示されます。正解しない場合は「不正解」と表示されます。
  • 画面をリロードすると最初の状態に戻ります。

サンプルコード


ポイント: formタグで@submit="c"のようにイベントと関数を指定できます。イベントはform以外のタグでも設定できます。

<form @submit="c" method="POST" action="" ref="form">

サンプルでは正解した場合の送信も抑制してあります。

...
else {e.preventDefault()}
...

@で指定できるイベントは他にもありますが、以下によるとイベントの種類の公式なリストはまだないようです。


バックナンバー(Vue.jsサンプルコード)

Vue.jsサンプルコード(01〜03)Hello World・簡単な導入方法・デバッグ・結果の表示とメモ化

Vue.jsサンプルコード(26)多数のアンケートを同一画面で順に表示する

$
0
0

26. 多数のアンケートを同一画面で順に表示する

  • Vue.jsバージョン: 2.5.2
  • [応募]ボタンを押すと、メッセージの主語を切り替えてアンケートを表示します。
  • 3つの回答の中から1つをクリックするとページを切り替えずに次の質問に進み、下の値リストを更新します。
    • (終了処理がないのでアンケートが終わると最初の質問に戻ります)
  • 画面をリロードすると最初の状態に戻ります。

サンプルコード


ポイント: Vue.jsの他にlodash.jsdelayも使っています。

    methods: {
      next: function(e) {
        this.answers.push(this.a)
        _.delay(() => { this.index++; this.a = null }, 500)
      },
    },

バックナンバー(Vue.jsサンプルコード)

Vue.jsサンプルコード(01〜03)Hello World・簡単な導入方法・デバッグ・結果の表示とメモ化

クリスマスだしcanvasで雪を降らせるJS書いてみた

$
0
0

こんにちは、デザイナーのスギヤマです。
今日は、クリスマスイブですね。
個人的にクリスマスは静かに雪が降っていて、綺麗と言ったイメージがあります。
なので気分だけでもそんな雰囲気を味わうべく、色々参考にしながらcanvasの勉強を兼ねて雪を振らせるJSを書いてみました。
以下で簡単説明していきます。

サンプルコード

サンプルおよび記事トップ写真 : 東京駅丸の内正面通りからの夜景

色々準備する

canvasを使うための準備をしていきます。
今回はブラウザーのウィンドウにフィットする様に表示させる方向で進めます。
canvasの表示領域は、設定しないと可変にならないようなので、
リサイズイベントを検知してcanvasのサイズを変えるよう指定します。

snow.js

/*------------------------
canvas要素の取得と設定
-------------------------*/
//canvas要素の取得
var canvas = document.getElementById('canvas');//canvasを取得
var ctx = canvas.getContext('2d');//canvasのコンテキストを取得

//canvasサイズの設定
var wd_width = window.innerWidth; //ウィンドウ幅をキャンバス幅に。
var wd_height = window.innerHeight; //ウィンドウ高をキャンバス高に。

ctx.canvas.width  = wd_width;
ctx.canvas.height = wd_height;

/*------------------------------------------------
ループ処理「requestAnimFrame」のベンダープレフィクス
-------------------------------------------------*/
var animFrame = window.requestAnimationFrame   ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame    ||
            window.oRequestAnimationFrame      ||
            window.msRequestAnimationFrame     ||
            function(callback){
              window.setTimeout(callback, 1000 / 60);
            };

/*------------------------
canvasサイズを可変にする
-------------------------*/

function canvas_resize(){
  var rswd_width = window.innerWidth;
  var rswd_height = window.innerHeight;

  canvas.setAttribute('width',rswd_width);
  canvas.setAttribute('height',rswd_height);
}
//リサイズイベントを拾って実行
window.addEventListener('resize',canvas_resize,false);
canvas_resize();

雪の粒を描く

雪の粒を作成していきます。
雪の粒は、

  • 丸い
  • ふわふわしている
  • 白い

感じなので、これを表現できるようにします。
白い円が外側に向かって、グラデーションで透明になっていけば大丈夫そうです。

また、雪の粒をランダムな位置に表示したいので、乱数を使う準備が必要です。
以下の個所で諸々指定しています。

snow.js

/*------------------------
乱数
min から max までの乱整数を返す関数
Math.round() を用いると、非一様分布になります
-------------------------*/
function getRandomInt(min, max) {
  return Math.floor( Math.random() * (max - min + 1) ) + min;
}

/*------------------------
雪の設定
-------------------------*/
// 雪の粒の初期位置とサイズの設定
function snow(){
  this.position_x = getRandomInt(0, wd_width);
  this.position_y = getRandomInt(0, wd_height);
  this.snow_size = getRandomInt(1, 4);
  this.speed = getRandomInt(1, 3); //落下速度
  this.wind = getRandomInt(0, 0.5); //横風の強さ
}

// 雪の粒の描画
snow.prototype.draw = function() {
  var snow_grad  = ctx.createRadialGradient(
    this.position_x,
    this.position_y,
    this.snow_size * 0.6,
    this.position_x,
    this.position_y,
    this.snow_size
  );
  /* グラデーション終点のオフセットと色をセット */
  snow_grad.addColorStop(0,'rgba(225, 225, 225, 0.8)');
  snow_grad.addColorStop(0.5,'rgba(225, 225, 225, 0.2)');
  snow_grad.addColorStop(1,'rgba(225, 225, 225, 0.1)');
  ctx.beginPath();
  ctx.fillStyle = snow_grad; // グラデーションをfillStyleプロパティにセット
  ctx.arc( this.position_x, this.position_y, this.snow_size, 0, Math.PI * 2);
  ctx.fill();
  ctx.closePath();
}

以下を参考にしました。

これで、雪の粒が作成できました。

雪を動かす

雪の粒ができたので、今度は動かしていきます。
雪はふわふわ落ちて行くので、ただ、上から下へ移動するするのではなく、左右に揺れながら波の様な動きで落ちて欲しいです。
丁度、サイン波がそんな動きなので、使ってみます。
以下の個所で指定しています。

snow.js

snow.prototype.move = function() {
  this.position_x += this.wind + Math.sin(this.position_y/20)*0.3;
  this.position_y += this.speed;

  if (this.position_y > ctx.canvas.height) {
      this.position_y = 0;
      this.position_x = getRandomInt(0, wd_width);
  }
}

画面内の雪の量を決める

雪の降る量を調整します。
たとえば、snow_density(80); とかで実行すると画面内に80粒の雪が降ります。
以下の個所で指定しています。

snow.js

// 雪の粒の密度(雪の量)
function snow_density(snow_count) {
  for(var num = 0; num < snow_count; num++){
    snows[num] = new snow();
  }
}

雪を降らせる

雪を降らせます。
流れとしては、

  1. 雪を初期位置に描画する
  2. 描画した雪の表示座標を更新して移動させる
  3. 1~2をくり返す

となります。
以下の個所で指定しています。

snow.js

/*------------------------
雪を降らす処理
-------------------------*/
//雪の粒を描画する
function snow_draw(){
  ctx.clearRect(0,0, wd_width, wd_height);
  for(var num = 0; num < snows.length; num++){
    snows[num].draw();
  }
}

//雪の粒の座標を更新する
function snow_move() {
  for (var num = 0; num < snows.length; num++) {
    snows[num].move();
  }
}

//ループ処理
function snowy(){
  snow_draw();
  snow_move();
  animFrame(snowy);
}

snow_density(80);
snowy();

まとめ

どうでしょう?少しはクリスマスっぽい気分になれたでしょうか?
僕は、少しはクリスマスな気持ちになりました。
まだまだ、分からないことが多いですが、canvasを使うと色々な表現ができそうで、楽しそうです。
それではみなさん、ハッピークリスマス。

参考

デザインに関するお問い合わせ

キャンペーンサイトやメディアサイト、コーポレートサイト制作など、幅広く対応致します。デザインに関するお問い合わせ、ご相談は下記ページより承っております。お気軽にお問い合わせください。

BPS x DESIGN

URL: https://design.bpsinc.jp

WebサイトをPWA(Progressive Web App)に変える簡単な手順(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

Webサイトを簡単にPWA(Progressive Web App)に変える方法(翻訳)

Progressive Web Appって何?

PWAは基本的にWebサイトの一種で、モバイルでアクセスするときにユーザーのデバイスに保存可能で、ネイティブアプリと同じように振る舞います。読み込み画面を備えていて、ChromeのUIを非表示にでき、接続が切れてもコンテンツが表示されます。最大のメリットはユーザーのエンゲージメント(engagement)を促進することです。AndroidのChromeブラウザ(他のモバイル用ブラウザについてはわかりませんが)がそのWebサイトをPWAとして検出すると、アプリをユーザーの好みのアイコンでデバイスのホームスクリーンに保存するようダイアログで促します。

PWAが重要な理由

PWAはクライアント向けビジネスで有利です。中国版Amazonとも呼ばれるAlibabaは、ブラウザにWebサイトをインストールするよう促すダイアログを表示するとユーザーのエンゲージメント率が48%向上することに注目しました(引用元)。

このことから、PWAに取り組む価値は十分あります。

このメリットを可能にしたのは、静的アセット(HTML、CSS、JavaScript、JSONなど)をユーザーのシステムに保存できるService Workerと呼ばれる技術です。これは、インストールされたWebサイトの振る舞いを記述するmanifest.jsonに沿って行われます。

PWAの例

以下のサイトは、本記事で説明しているのと同じ技術を使って私が作成したものです。

その他についてはpwa.rocksで多数の例をご覧いただけます。

セットアップ

WebサイトのPWA化は一見大変そうですが(Service Workerって何よ?)、それほど難しくありません。

1. 必要なもの: HTTPS(HTTPではなく)

PWAの一番面倒な部分は、http://ではなくhttps://を使うセキュアなドメインでWebサイトを動作させることです。
手動のHTTPS化は非常に面倒ですが、自前のサーバーを使っていればletsencryptを使って実に簡単かつ自動的にHTTPS化できます。しかも無料です。

2. ツール

2.1 lighthouse test

  • lighthouse testはGoogleが開発およびメンテナンスしている自動化テストサイトであり、Progressive、Performance、Accessibilityの3つの尺度に基いてWebサイトをテストします。各項目の点数はパーセントで表示され、それぞれ解決方法も示してくれます。

didiermotte.beをLighthouse testにかけた結果

2.2 realfavicongenerator.net

realfavicongenerator.netはPWAの表示レイヤの面倒を見てくれます。上述のmanifest.jsonファイルを生成し、Webサイトをモバイルデバイスに保存するときに必要な全バージョンのアイコンや、ページの<head>タグに追加するHTMLスニペットも生成してくれます。

注意: このサイトはアセットをサブフォルダに置くことを勧めますが、この方法だとPWA化が非常に面倒になります。作業を軽減するには、すべての画像やmanifestファイルをWebサイトのルートフォルダに置きます。

2.3 upup.jsでサービスワーカーを使う

Service WorkerはJavaScriptの技術です。私の頭では理解するのがつらいのですが、ありがたいことにとある賢いドイツ人女性が教えてくれたTal Atler氏(「オフライン優先」哲学を推進しています)が作成してくれたあるJavaScriptライブラリは、überへの接続が切れたときにもうまく振る舞えるようにしてくれます。Ola Gasidloさん、ダンケシェーン!

UpUpチュートリアルでさっと学ぶだけですぐ使えるようになります。

3. 手順

  1. RealfavicongeneratorでHTMLと画像のコードを生成し、自分のWebサイトに追加する
  2. HTTPSドメインを公開する
  3. lighthouse testを実行する
  4. 結果を分析する
  5. 問題をひとつずつ修正する
  6. 手順3.に戻って繰り返す
  7. どの項目もほぼ100点になるまで繰り返す: Progressiveは100点を目指す
  8. モバイルで実際の動作をテストする: うまくいけばAndroidで以下のようなポップアップが画面下部に表示され、Webサイトをモバイルのホームスクリーンに保存するよう促す

もっと知りたい方へ

必要なPWAの情報は以下の本にすべて盛り込まれています。

PWAで幸せになりましょう!

関連記事

CSS GridがBootstrapよりレイアウト作成に向いている理由(翻訳)

新しいRailsフロントエンド開発(1)Asset PipelineからWebpackへ(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

新しいRailsフロントエンド開発(1)Asset PipelineからWebpackへ(翻訳)

前書き

本記事は、フロントエンドのフレームワークに依存しないRailsプレゼンテーションロジックを現代的かつモジュール単位かつコンポーネントベースで扱う方法を独断に基いて解説するガイドです。3部構成のチュートリアルで、例を元に最新のフロントエンド技術の最小限に学習し、Railsフロントエンド周りをすべて理解しましょう。

Part 2Part 3もご覧ください。

混乱しがちな部分

今新たに実地のRailsフルスタック開発者になるということは、混乱の渦中で頑張るということです。Asset Pipeline/Sprockets/CoffeeScript/Sassを用いてフロントエンドを扱う「昔ながらのRails」wayは、2017年ともなると色褪せて見えます。Rails 3.1時代に選択された技術の多くは、現代の想定に応えられていません。昔ながらの手法にこだわり続ければ、この5年間にフロントエンド界隈で起こったあらゆる新技術を見送ることになってしまいます。全てを統べるJavaScriptパッケージマネージャnpmの台頭、頼もしいJavaScript文法であるES6の隆盛、連勝を重ねるBabelトランスパイラビルドツールかつてないほど成長を続ける CSSプリプロセッサPostCSS。フロントエンドのコードを「ページ」から「コンポーネント」にパラダイム変換したReactやVueなどのフロントエンドフレームワークが驚異的な成功を収めていることは申し上げるまでもありません。

これほどまでに複雑になったフロントエンド技術を1人の開発者の頭に押し込めようとすれば(キャリアの浅い人はなおさら)、認知機能が悲鳴を上げていわゆるJavaScript疲労になるのがオチです。

しかし、確立されたワークフローに疑問を感じるのは、時代に取り残された気持ちになるからとか、フロントエンド屋との技術トークがどんどん通じなくなるからとか、将来の見通しに不安を感じるからという理由だけではありません。つまるところ、プログラマーとは理性的な人間なのです。

Asset Pipelineの何が問題だったのか

昔ながらの方法が今も使えることについては触れないことにしましょう。今でも、Rails標準のフロントエンド向けセットアップ(とCoffeeScript)を使って成果を出せます。ビューテンプレート、スクリプト、スタイルシートは従来同様Asset Pipelineで結合/最小化されて配信されます。本番環境では2つの(少なくとも人間には)読めないファイルの形を取るので、重要性は変わりません。

しかし、開発者は普通次のような点に心を配っています。

  • 分離され、再利用可能かつテスト可能な、理解しやすいコードであること
  • コードの変更->結果の表示を短いサイクルで繰り返せること
  • 依存関係の管理が面倒でないこと
  • ツールがちゃんとメンテされていること

言うまでもなく、「昔ながらの」Railsは私たちのコードに何らかの構造を与えます。ビューテンプレート、JavaScript、スタイルシート、画像ごとに個別のフォルダが用意されます。しかしフロントエンドが複雑になればなるほど、これらを追っているうちに認知能力が低下(訳注: 参考)してしまいます。

「昔ながらの」フルスタックRails wayをおそろかにすれば、たちまちCSSやJSは死んだコードのグローバルなゴミ捨て場と化してしまうでしょう。

乗り換えを検討する理由の中には「スピード」もあります。Sprocketが遅いという問題については山ほどドキュメントがありますし、Herokuにいたっては、Asset Pipelineのパフォーマンス最適化方法に特化したガイドまで公開しています。そこではRailsアプリのデプロイで最も時間がかかるのがアセットの扱いであることを認めています。曰く「平均すると、依存関係をbundle installでインストールするときよりも20倍以上遅い」。

開発中にCSSを1行変更してページを再読み込みするときも、結果が表示されるまでに多少待たされます。この待ち時間はすぐに長くなります。

依存関係についてはどうでしょうか。Asset Pipelineを常に最新に保つのは大仕事です。プロジェクトにJavaScriptライブラリを1つ追加する場合、CDNから読み込んだコードをコピペしてapp/assetslib/assetsvendor/assetsに置くか、誰かがgem化してくれるまでぼんやり待つ方法があります。その間にも、JavaScriptコミュニティは同じことをnpm installコマンド、今ならyarn addコマンド一発で管理しています。アップデートも同様です。YarnはJavaScriptをBundlerのように便利に扱うことができます。

最後は、Asset Pipelineを支えるビルドツールであるSprocketsです。Sprocketsの最近のメンテ状況ははかばかしくありません。

Sprocketsはかれこれ5年以上ほこりを被ったままです(左)。同じ時期のWebpackは頻繁に更新されています(右)。

風向きが変わった

2017年、DHHとRailsコミュニティはフロント周りの変更に手を付け始めました。Rails 5.1ではwebpacker gemによるWebpack統合、Yarnを介したnode_modules、すぐに使えるBabel/React/Vue/PostCSSが(その気になればElmも)導入されました

しかしAsset PipelineとCoffeeScriptは今も彼らがメンテしています。rails newだけでプロジェクトを開始すると、昔ながらのRailsになります。JavaScript関連のトピックをググるときには、相変わらずコード例を脳内トランスパイルして隅々まで理解しなければなりません。

くよくよすることはありません。今日のRailsアプリではあらゆる現代的な手法を利用可能です。本シリーズで私たちと一緒に基礎を固めましょう。Rails/JavaScript/CSSの基本的な知識が多少あれば十分始められます。本シリーズでは設定やツールを最小限に抑えるため、最新のRails 5.1以降の機能も積極的に使います。

本チュートリアルのシリーズでは、Evil Martiansで培われた現代的で練り上げられたフロントエンド構築ベストプラクティスの一部を皆さまにご紹介いたします。

心の壁

Reactは私たちにコンポーネントで思考するよう指導します。その他の現代的なフロントエンドフレームワークもこれに準じています。モジュラリティは、BEMをはじめとする主なCSS方法論を支える哲学です。モジュラリティのコンセプトは「UIを構成するあらゆる論理的な部品は自己完結(self-contained)すべきである」というシンプルなものです。

Railsでは、ビューを論理的な部品に分割する方法が組み込まれています(ビューのパーシャル)。しかしパーシャルがJavaScriptに依存すると、おそらく他の現代的なコンポーネントと同様にapp/assets/javascriptsの下にある深いフォルダにアクセスしなければならなくなります。

パーシャルを使うときに一切合財をまとめることができれば、各パーシャルのスクリプトやスタイルシートを1箇所にまとめられるのではないでしょうか?

これからご紹介するアプローチは、React/Vue/Elmのアーキテクチャに依存していません。そしてそのように作られています。自分が使うツールの利用法を気兼ねなく学ぶことができますが、急いで学ぶ必要はありません。既にRailsで使えるツールを現代的なフロントセットの思考様式に徐々に合わせていけばよいのです。

Sass vs. PostCSS

SassはRailsに愛されています。しかし私たちの心はPostCSSに傾きつつあります。何より、PostCSSはRailsでCSSを処理するRuby組み込みのSassより36.4倍高速です。PostCSSは100%純粋なJavaScriptで書かれており、多くのプラグインを使って簡単に拡張/カスタマイズできます。cssnextというプラグインは、ブラウザでサポートされていない機能のポリフィルをすぐ使うことができますが、必要がなければ使わなくてよいのです。理由があれば、好みのプリプロセッサの上でPostCSSを使うこともできます。

私たちは何を作っているのか?

そろそろ実際に手を動かさないといけませんね。フロントエンドの新しいアプローチをご紹介するために、最小限の認証とActionCableを用いた何の変哲もない標準的なチャットアプリを作ることにします。アプリ名はevil_chatとしました。このサンプルアプリは複雑ではありませんが、それでも「フルスタック」の経験に十分なぐらいに洗練されています。

私たちのプロジェクトでは、Asset PipelineやデフォルトのRailsジェネレータが生成する大量の.scssファイルや.coffeeファイルたちとおさらばすることにします。テンプレートエンジンは従来どおりERBを使い、好みに応じてSlimやHamlを使える余地を残しています。

A new frontend folder in your app

左に見えるのがEvil Frontのフォルダ構造です

後でまたこのフォルダ構造を振り返ります。アプリのトップレベルにあるfrontendフォルダの中ですべてのことを行います。app/assetsは完全に置き換えられます。

一発ですべて理解できなくても問題ありません。ひとつずつ手順を追って進めましょう。

プロジェクトの開始方法

rails newだけでは不要なものを切り落とせないので、次の新しいマジックコマンドを使います(アプリ名はevil_chatとします)。

$ rails new evil_chat --skip-coffee --skip-sprockets --skip-turbolinks --webpack --database=postgresql -T

ご覧のとおり、CoffeeScriptやSprockets関連の機能は不要になります。本チュートリアルではテスティングまではカバーしないので、-Tオプションでテストファイル作成をスキップしています。作成後Herokuにすぐデプロイできるよう、--database=postgresqlでPostgreSQLを指定しています。

一番肝心なのは--webpackオプションです。これを指定することで、Webpackですべてのアセットをバンドルするwebpacker gemがRailsで使われるようになります。

  • node_modulesフォルダにはJS依存ファイルがすべて含まれます(誤って余分なファイルを大量にリポジトリにコミットしないよう、.gitignoreにも追記されます)。
  • package.jsonはすべての依存関係を宣言します。yarn.lockも同様なので、npm installではなく(より機能豊富な)yarn addでパッケージを追加できます。
  • .babelrcファイルは、ES6を、マーケットシェア1%以上のあらゆるブラウザに準拠するJavaScriptコードに変換します。
  • .postcssrc.ymlpostcss-smart-importプラグインやpostcss-cssnextプラグインで設定済みです。これらのプラグインのおかげで、cssnextに記載されている全機能を利用できます。

まだ何か忘れているような気がします。特に、Autoprefixerなどのツールのbrowserslistグローバル設定は、今後コードを正しくクロスブラウザ互換処理するうえで必要です。ありがたいことに、プロジェクトのルートディレクトリに以下のファイルを作成すれば簡単に修正できます。

$ touch .browserslistrc

おかげで要求される知識が随分少なくなりました(まったくというわけにはいきませんが)。

それではこのファイルを開いて> 1%という1行を追加しましょう。これさえ知っておけばブラウザの互換性を保てます。

最初の段階で正しくやっておきたいことがもうひとつあります。Railsジェネレータのデフォルトの振る舞いの再設定です。既にネタバレしていますが、app/assetsには今後何も置く必要がないので、以下の手順でこのフォルダを削除します。application.rbを開いて以下の行を追記します。

# config/application.rb
config.generators do |g|
  g.test_framework  false
  g.stylesheets     false
  g.javascripts     false
  g.helper          false
  g.channel         assets: false
end

Asset Pipelineに引導を渡すときが来ました。app/assetsフォルダを削除します。

しかし置き換え方法はどうすればよいのでしょうか。次の手順を実行します。

  • rails new--webpackオプションを追加したことでapp/javascriptというフォルダが作成されています。このフォルダをプロジェクトのルートに移動してフォルダ名をfrontendに変更します(名前は好みで構いませんが、frontendが一番わかりやすいと思います)。移動の際フォルダの中身は変更しないようにします。frontend/packsの中にあるapplication.jsは、このアプリのWebpack「エントリ」ポイントとして使われます。
  • application.html.erbを開き、javascript_include_tag "application"javascript_pack_tag "application"に置き換えます。メソッド名の単語が1つ変わることですべての違いが生じます。include_tagは、SproketsでコンパイルされたアプリレベルのJavaScriptファイルに参照を1つ挿入する昔ながらの方法ですが、pack_tagは先のエントリポイント(つまりfrontend/packs/application.js)から生成されたWebpackバンドルが使える新しい方法です。この段階で、<head>にあるpack_tag<body>の末尾、つまりyieldステートメントの直前に移動します。

  • stylesheet_link_tag 'application', media: 'all'stylesheet_pack_tag 'application'に置き換えます。WebpackとES6のimportステートメントの助けを借りて、今後はCSSをコンポーネント単位で使うことにします。これにより、CSSもすべてWebpackで管理されます。

  • 次に、バンドルするファイルを探索する場所をWebpackerに指定する必要があります(デフォルトのフォルダをリネームしたので)。Webpacker 3.0によると、Railsのconfigフォルダ内にあるwebpacker.ymlで設定できます。このファイルの最初の数行に、以下の要領でアプリのフォルダ構造の変更を反映します。

default: &default
  source_path: frontend
  source_entry_path: packs
  public_output_path: packs
  cache_path: tmp/cache/webpacker
  • ERBのパーシャルもfrontendフォルダ配下に置かれるので、application_controller.rbで次のように指定しないとコントローラから見つけられません。
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  # 以下を書くだけでよい
  prepend_view_path Rails.root.join("frontend")
end
  • Webpackerが3.0になったことで、開発中にオンデマンドでアセットをコンパイルするための別プロセスが不要になりました。しかしJSコードやCSSコードの変更時にページを自動更新するには、rails sするときに従来同様webpacker-dev-serverを実行しておく必要があります。そのためのProcfileが必要なので作成しましょう。
$ touch Procfile

このファイルに以下の設定を書きます。

server: bin/rails server
assets: bin/webpack-dev-server

このProcfileを置くことで、Foremanなどのツールを使ってすべてのプロセスをコマンド一発で起動できるようになりますが、Evil MartiansのHivemindをぜひおすすめいたします。Hivemindの兄貴分であるOvermindは実行中のプロセスを一時停止せずにpryでデバッグできるツールですので、こちらもご覧ください。

訳注: 同社は昔のSFになぞらえた命名を好んでいるようです。Hivemindは集合精神、Overmindは「幼年期の終わり」に登場する神っぽい宇宙規模の集合知性を指します。

スモークテスト

それでは新しいセットアップが正しいかどうかテストしましょう。application.jspacksの下にあります)にシンプルなDOM操作コードを少し足し、Webpackerでちゃんと動くようにします。最初に、基本となるコントローラとデフォルトルーティングをそれぞれ生成する必要があります。

$ rails g controller pages home
# config/routes.rb
Rails.application.routes.draw do
  root to: "pages#home"
end

views/pages/home.html.erbの中身は完全にからっぽにしてください。次にapplication.jsの中身もからっぽにし、以下のコードに置き換えます。

// frontend/packs/application.js
import "./application.css";

document.body.insertAdjacentHTML("afterbegin", "Webpacker works!");

同じフォルダにapplication.cssファイルを作成し、CSSも効いていることを確認できるようにします(CSSはPostCSSで処理されます)。

/* frontend/packs/application.css */
html, body {
  background: lightyellow;
}

いよいよアプリの初立ち上げです。Hivemindをインストール済みであることが前提です。インストールしない場合はforemanなどのプロセス管理ツールをお使いください(私たちとしてはぜひHivemindの素晴らしさをご検討いただければと思います)。

$ hivemind

ではhttp://localhost:5000を開きましょう(Hivemindは$PORTに5000を設定するので、Railsも同じ環境変数で実行ポートを決定します)。次のように表示されるはずです。

A smoke test for our app

真っ赤なエラー画面が出ていなければ成功です

ここでWebpackのクールな点を1つご紹介します。application.jsファイルで"Webpacker works!"の部分を変更して保存すると、ブラウザの[更新]ボタンを押さなくても画面が自動更新されます。

実際のコードを書き始める前に、コーディングスタイルを定めましょう。

ところでJSのlintはどうする?

Prettierには著名なエディタがすべて統合されているので、ボタンひとつでコードを整形できます。ESLintにもあらゆる主要なエディタ向けのプラグインがあるので、即座に結果をビジュアル表示できます。

JavaScriptの文法は年単位で更新を繰り返すので、コーディングスタイルは多岐にわたり、書き始める前からスタイルが混乱しがちです。たとえばセミコロン使う派と使わない派の争いは終わりそうにありません。StandardPrettierのように、独自の色を出すコードフォーマッタもあります。私たちはPrettierにしました(Prettierはデフォルトでセミコロンを使いますが、いつでもオフにできます)。

lintはESLintである程度自動化するので、コーディングスタイルは常にチェックされます。また、メンテしやすいJSコードを書くベストプラクティスを豊富に収録しているAirbnbのJavaScriptスタイルガイドにも頼ることにします。

訳注: AirbnbのJavaScriptスタイルガイド日本語版は以下をどうぞ

JavaScriptスタイルガイド 1〜8: 型、参照、オブジェクト、配列、関数ほか (翻訳)

現時点ではwebpack-dev-serverしか含まれていないpackage.jsondevDependenciesを少々追加します。JavaScriptのlintに必要な部分をカバーすると、以下のような感じになるはずです。

{
  "name": "evil_chat_codealong",
  "private": true,
  "dependencies": {
    "@rails/webpacker": "^3.0.1"
  },
  "devDependencies": {
    "webpack-dev-server": "^2.9.1",
    "babel-eslint": "^8.0.1",
    "eslint": "^4.8.0",
    "eslint-config-airbnb-base": "^12.0.1",
    "eslint-config-prettier": "^2.6.0",
    "eslint-import-resolver-webpack": "^0.8.3",
    "eslint-plugin-import": "^2.7.0",
    "eslint-plugin-prettier": "^2.3.1",
    "lint-staged": "^4.2.3",
    "pre-commit": "^1.2.2",
    "prettier": "^1.7.3"
  }
}

lint-stagedpre-commitは、後でgit addgit commitなどにフックするときに便利です。これは、残念なコードを誤ってリポジトリに登録しないようにするためのものです。

最後の仕上げに、ルートフォルダに.eslintrcファイルが必要です。適用するルールをESLintに指示するのに使います。

$ touch .eslintrc

.eslintrcに以下を書きます。

{
  "extends": ["eslint-config-airbnb-base", "prettier"],

  "plugins": ["prettier"],

  "env": {
    "browser": true
  },

  "rules": {
    "prettier/prettier": "error"
  },

  "parser": "babel-eslint",

  "settings": {
    "import/resolver": {
      "webpack": {
        "config": {
          "resolve": {
            "modules": ["frontend", "node_modules"]
          }
        }
      }
    }
  }
}

"extends"キー以下の要素の順序は重要です。ここで、最初にAirbnbのルールを適用し、Prettierのフォーマットガイドとコンフリクトするものがあれば常に最新のものを使うようESLintに指示しています。eslint-import-resolver-webpackへの依存を指定する"import/resolver"キーも必要です。これによって、JSファイル内のimportで指定したライブラリが、Webpackが管理するフォルダ(このアプリの場合はfrontendフォルダ)内に実際に存在するようになります。

CSSはどうする?

CSSにも何かlintツールが必要です。評判のよいツールであるnormalize.cssをnormalizeに使うことにします。CSSのエラーやスタイル違反の検出用にstylelintも使います。package.jsonに開発用の依存ライブラリを2つ追加しましょう。

"devDependencies": {
    ...
    "stylelint": "^8.1.1",
    "stylelint-config-standard": "^17.0.0"
  }

プロジェクトのルートフォルダに.stylelintrcを置いてlinterに指示する必要もあります。

$ touch .stylelintrc

ファイルの中身は次のとおりです。

{
  "extends": "stylelint-config-standard"
}

package.json(今度はdevDevdependenciesではありません!)の"dependencies"キーの配下に次のようにnormalize.cssを追加します。

"dependencies": {
    "@rails/webpacker": "^3.0.1",
    "normalize.css": "^7.0.0"
  },
  ...

次はgit hooksをいくつか導入し、git commit時に自動チェックが走るようにしましょう。package.json"scripts"を追加します。

...
"scripts": {
    "lint-staged": "$(yarn bin)/lint-staged"
  },
  "lint-staged": {
    "config/webpack/**/*.js": [
      "prettier --write",
      "eslint",
      "git add"
    ],
    "frontend/**/*.js": [
      "prettier --write",
      "eslint",
      "git add"
    ],
    "frontend/**/*.css": [
      "prettier --write",
      "stylelint --fix",
      "git add"
    ]
  },
  "pre-commit": [
    "lint-staged"
  ],
  ...

これで、コミット時にstagedファイルのエラーがすべてチェックされ、自動整形されます。

最終的なpackage.jsongistにあるような形になるはずです。ターミナルでyarnを実行して依存ライブラリをインストールします。

自動lintを早く使ってみたくてウズウズしているかと思います。frontend/packs/application.jsを開いてセミコロンを削除してから、git add . && git commit -m "testing JS linting"を実行すると、ただちにセミコロンが復元されます。だらしないコーディングスタイルともこれでお別れです。

Linterの設定

設定がすべて正しければ、プロジェクトのルートファイルにこれらのファイルがすべてあるはずです

最初のコンポーネント(React未使用)

本ガイドのPart 2で扱う内容の一部を軽くご紹介します。最初のコンポーネントを作ってみましょう。

最初にapplication.cssを削除します。これはスモークテスト以外では不要です。同様にapplication.jsのコードもすべて削除します。ここからは、application.jsにはimportステートメントだけを書きます。このエントリポイントにすべてが集約されます。アプリ全体で使うCSSやJavaScriptの置き場所が必要なので、作ってみましょう。このフォルダの名前はinitにしました。

$ mkdir frontend/init
$ touch frontend/init/index.js
$ touch frontend/init/index.css

続いてエントリポイント内部の新しいフォルダの登録が必要です。packs/application.jsに以下を追加します。

// frontend/packs/application.js
import "init";

新しいファイルに入れるコードも必要です。以下はinit/index.jsです。

// frontend/init/index.js
import "./index.css";

以下はinit/index.cssです。

/* frontend/init/index.css */
@import "normalize.css/normalize.css";

body {
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-size: 16px;
  line-height: 24px;
}

ここでは、アプリのすべてのフォントに一般的なスタイルをいくつか適用しています。initフォルダはバンドル時に最初にチェックされる場所なので、ここにnormalize.cssをインクルードするのが自然です。同じフォルダを使って、後でポリフィルやエラー監視など、私たちのコンポーネントに直接関係のない機能をセットアップして必要が生じたら可能な限り読み込むこともできます。

initは特殊ケースです。このコンポーネントについてはどうでしょうか。

各コンポーネントは、3つのファイル(ERBパーシャルとそれ用のJS/CSSファイル)を含む1つのフォルダで構成されます。

私たちのコンポーネントはすべて、frontend配下のcomponentsフォルダ内に置かれます。ここに最初のコンポーネントを作成しましょう。レイアウトのテンプレートとみなしてこのコンポーネントをpageと呼ぶことにします。

$ mkdir -p frontend/components/page
$ touch frontend/components/page/{_page.html.erb, page.css, page.js}

私たちがこのコンポーネントのJSファイルをindex.jsと呼んでいないことにご注意ください。この名前はinitフォルダで予約されているからです。後でエディタでタブをいくつも開いたときに見つけやすいよう、JSファイルにはコンポーネントと同じ名前を付けることにしています。他のチュートリアルではindex.jsを使っていることが多いので、この手法はあまり見かけないかもしれませんが、後でコードを書くときに時間を大きく節約できます。

まだ私たちのコンポーネントに関連するJSロジックがないので、page.jsにはCSSファイルをimportする行しかありません。

// frontend/components/page/page.js
import "./page.css";

page.cssの方にはコンポーネントに関連するスタイルがあります。

/* frontend/components/page/page.css */
.page {
  height: 100vh;
  width: 700px;
  margin: 0 auto;
  overflow: hidden;
}

最後の_page.html.erbにはマークアップが含まれています。ERBや、コンポーネント同士をネストするのに使うyieldステートメントもすべて使える点にご注目ください。

<!-- frontend/components/page/_page.html.erb -->
<div class="page">
  <%= yield %>
</div>

application.jsimport "components/page/page";を追加して、新しいコンポーネントを参照できるようにするのをお忘れなく。

A structure for the first component

この時点でのfrontendフォルダ構造

それではhome.html.erbビューにERBコードを書いてみましょう。

<!-- app/views/pages/home.html.erb -->
<%= render "components/page/page" do %>
  <p>Hello from our first component!</p>
<% end %>

最初のコンポーネントが実際に動作することを確認しましょう。サーバーを再び起動してページを更新します。うまく行けば、以下のように表示されるでしょう。

A structure for the first component

最初のコンポーネントのブラウザ表示とコンソール出力

おめでとうございます。チュートリアルPart 1はこれでおしまいです。ぜひPart 2もご覧いただき、アプリの体裁を整えてチャット関連の機能に必要なコンポーネントを導入するところまでやってみてください。少ないコード量でコンポーネントをレンダリングするヘルパーや、コンポーネント作成を自動化するジェネレータも追加します。


Part 1 | Part 2 | Part 3

スタートアップをワープ速度で成長させられる地球外エンジニアよ!Evil Martiansのフォームにて待つ。

関連記事

Rails 5.2ベータがリリース!内容をざっくりチェックしました

5.1 beta1リリースノートに見るRails 5.1の姿

Viewing all 164 articles
Browse latest View live