主婦がアプリをリリースするまでの軌跡

自宅でお小遣い稼ぎをしたい主婦がアプリリリースを目指すブログです。

【CoffeeScript】 ->と=>の違い

f:id:song-of-life1352607:20190505165514p:plain

こんにちは、マリンです。

祝!令和!!無事新しい時代が幕を開けましたね!

私も怒涛の長女卒園からの入学が終わって一安心です。

令和の時代も平和が続きますように!

そしてスキルアップできますように!!


さて、令和元年一発目の投稿は、

CoffeeScriptの=>(ファットアロー)についてです。

名前初めて知りました。。。

今までよくわからないので->こちらのみを使っていましたが、

先輩のコードを読んでいて頻繁に出てきたため勉強してみました。





ファットアローとは?

ファットアローとは、CoffeeScriptにおける関数定義の方法の一つです。

現在のthisを、定義した関数の中でも保持することができるというシロモノです。

って、なんぞや?という感じだったので詳しく調べました。




thisを保持するとは?

そもそもthisとは、呼び出す場所や方法によってその中身が変わる特殊な変数です。

構文やオプション設定などもなく、単体でどこでも呼び出すことが可能です。

CoffeeScriptにおいては、@で表します。

基本的には「現在のオブジェクト等」を表しており、

thisが呼び出された場所や方法によって、その「現在」が変わるため、

中身が変わってきます。


class myObj
        func: () ->
            console.log @
            do ->
                console.log @
            return

myobj = new myObj
myobj.func()


#=> myObj {}
#=> Window { postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, ...}

上記では呼び出す場所によってthisの中身が変わっていることを検証しています。

myObjクラスの中で定義したクラスメソッドのfuncでは、

自身のクラスをthisに格納しているようです。

しかし、そのfuncの中で呼び出している即時関数のthisにはWindowが入っています。

thisが保持されていない」状態ですね。

コールバック関数やイベントハンドラの中ではthisがその対象だったりしますが、

これが保持されていない状態です。


class MyObj
        func: () ->
            console.log @

            that = @

            do ->
                console.log that

            return

myobj = new MyObj
myobj.func()


#=> myObj {}
#=> myObj {}

今度はfuncの中で、即時関数を実行する前に@thatという変数に入れてみました。

そして即時関数ではこのthatを出力するようにしています。

すると、実行結果がfuncと即時関数で同じになりましたね。

これが「thisを保持する」という状況です。




->と=>の違い

通常JavaScriptなどでは前述のように、

that = thisなど変数に格納することによって保持するようです。

(格納する変数名はthatもしくはselfが慣習となっています。)

このthisを保持するための変数格納の処理を自動的に行いつつ関数宣言するのが

=>(ファットアロー)です。


先程の例を使って->=>の違いを見てみます。

class MyObj
    func: ->
        console.log @
        
        do ->
            console.log @

        do =>
            console.log @

        return

myobj = new MyObj
myobj.func()


#=> myObj {}
#=> Window { postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, ...}
#=> myObj {}

->(アロー?でいいのかな)の方の即時関数は、最初の例と同じように、

thisが保持されることなくWindowが格納されています。

しかし=>(ファットアロー)の方は、

改めて変数を定義することなくthisが保持されていることがわかります。

これが->=>の関数定義の違いです。


ちなみにこの2つがどのようにコンパイルされているか見てみましょう。

  var Pages, pages;

  Pages = (function() {
    function Pages() {}

    Pages.prototype.func = function() {

      console.log(this);

      (function() {
        return console.log(this);
      })();

      (function(_this) {
        return (function() {
          return console.log(_this);
        });
      })(this)();

    };

    return Pages;

  })();

  pages = new Pages;

  pages.func();

10~12行目が->の部分、14~18行目が=>の部分です。

ちょっと見づらいですが、=>の方は即時関数に_thisを渡し、

それを元に無名関数を呼び出しています。

この_thisが先ほどで言うthatの働きをしており、

即時関数の部分がthat = thisの代わりのような役割になっているようです。

即時関数の実引数にMyObjでのthisが渡されていますね。


まとめ

->=>はともにCoffeeScriptにおける関数定義の方法で、

2つの違いは、関数定義前のthisを保持するか否か?ということでした。

先輩のコードではこの2つが入り乱れており、今まで見てみぬふりをしていましたが、

今回の大型連休のおかげで一つ謎が解けました。

ほかにも90%近く見てみぬふりをしていますが、

一つ一つ解読して私もマスターできるようになりたいです!

令和の目標はスキルアップです!!


Special thanks!!

ism1000ch.hatenablog.com

umatomakun.hatenablog.com

【jQuery】イベントオブジェクト ー親要素で発火するイベントを特定の子孫では解除したいー

f:id:song-of-life1352607:20180113005923j:plain

こんにちは、マリンです。

今回はJSでのイベントを発火させる要素の特定です。

親要素で指定するが、特定の子孫要素では実行してほしくない。

そんな時どのように指定すればよいでしょうか?

:notとか調べてみたのですが、

子孫要素でのやり方がイマイチよくわからず。。。

そこで今回はイベントオブジェクトを使ってこれを実装しました。


もっと簡単に実装できる、

セレクタとか、便利なメソッドとか、

皆様ぜひ教えてください。




概要

  • tableの概要を記述したtrをクリックした時、詳細の列が表示されるようにする。

  • trの中には承認ボタンがある。

  • 親のtrにイベントを付けているので、子要素のボタンのクリックでもshow hideが実行されてしまう。

以上のことから、子要素のボタンだけクリックイベントを外したいわけです。

最初は:notなどを使って特定しようとしたのですが、

うまくボタンだけを取ることが出きませんでした。

そこで、イベントオブジェクトを使ってみました。


詳細

サンプルコード

まずは解説に使うサンプルコードを記載しておきます。

<form action="sample/create" method="post">
    <table class="table">
        <tr id="sample-tr">
            <td>Name: Yamada Hanako</td>
            <td>Age: 20</td>
            <td><button name="approve" type="submit" value="approve">承認</button></td>
        </tr>
        <tr id="sample-hidden-tr style="display: none">
             <td>Address: Tokyo Minato-Ku</td>
             <td>Email: sample@example.com</td>
             <td>Password: password</td>
        </tr>
    </table>
</form>

上段のtrが表示されている行。

下段のtrが非表示部分で、上段をクリックすると表示されます。

しかし、上段3つ目のカラムにあるボタンだけはクリックの対象外にします。


以下が今回のCoffeeです。

$('#sample-tr').on 'click', (e)->
    target = $(e.target)
    if target[0].nodeName == "BUTTON"
        return
    if $(@).hasClass('show')
        $(@).removeClass('show')
        $('#sample-hidden-tr').hide()
    else
        $(@).addClass('show')
        $('#sample-hidden-tr').show()
    retrun




詳細解説

HTMLは省略します。

$('#sample-tr').on 'click', (e)->
    target = $(e.target)

まずは最初の2行です。

1行目は上段trへのクリックイベントで、引数eがイベントオブジェクトです。


2行目はイベントが発生したjQueryオブジェクトを変数に入れています。

targetの中身は以下のようになっています。

target: jQuery.fn.init(1)
  0: button
    accessKey: ""
    assignedSlot: null
    attributeStyleMap: StylePropertyMap {size: 0}
    attributes: NamedNodeMap {0: type, 1: name, type: type, name: name, length: 2}
    autocapitalize: ""
    .
    .
    .
    nextElementSibling: null
    nextSibling: null
    nodeName: "BUTTON"
    nodeType: 1
    .
    .
    .




次の2行です。

if target[0].nodeName == "BUTTON"
    return

if文でイベントが発生した要素がボタンだったら何もしないで終了させています。


if文の内容と、先ほどのtargetの中身を見比べてみてください。

target: jQuery.fn.init(1)
  0: button
    accessKey: ""
    .
    .
    .
    nodeName: "BUTTON"
    .
    .
    .
target[0].nodeName == "BUTTON"

イベントオブジェクトのtarget

その中に入っているキー[0]でDOM要素を取得し、

さらにその中からnodeNameプロパティで対象となった要素名を取得します。

そして、要素名が"BUTTON"だったら何もせずreturnというわけです。


ちなみに、最初にe.targetを変数targetに入れましたが、

今回のような単純なコードでしたら入れる必要もないと思います。

if $(e.target)[0].nodeName == "BUTTON"

でいけます。

個人的に見やすかっただけです、、、


まとめ

今まで苦手意識のあったイベントオブジェクトやjQueryオブジェクトでしたが、

一度じっくり眺めてみるとなんとなく解ってきました。

やはりぱっと見よくわからない!と思っても、

テンパることなく冷静に眺めていることって必要ですね。

余談ですが、今回のことで

なぜ$()を使ってjQueryオブジェクトにするのか?

ということが今更ながら理解できました。

根本が解っていないと理解が遅くなる一方ですね。。。

まだまだ奥が深いと思うので、これを機にちゃんと勉強してみます。

【Git】ブランチ間違えてコミットしてた!?

f:id:song-of-life1352607:20180113002051p:plain

皆さん明けましておめでとうございます。

なんとか1月終わる前に更新できました(^ ^;)

本年も地味に更新してまいりますので、

どうぞよろしくお願いいたします。

さて、今回は年明け早々やらかしたブランチ切り替え忘れ、

そしてそのまま作業した挙句コミットまでしちゃった!

その時の解決方法を載せておきます。





経緯

先日、仕事開始時にブランチを切り替え忘れ、

そのままずーっと作業し、コミットももちろん複数回行い、

作業終了時に初めて「ブランチ前回のままじゃん!!」と気づきました。

ひどい失敗で、誰かの役にたつのかわかりませんが・・・

その時行った、

間違えたブランチから本来のブランチへ変更部分を移してコミット。

という解決方法を記述します。


手順

  1. 間違えたブランチをコミットする前の状態に戻す。

  2. 変更部分を一度退避する。

  3. ブランチを切り替える。

  4. 切り替えたブランチで退避した変更部分を復活。

  5. コミットする。


詳細

間違えたブランチを、コミットする前の状態に戻す。

$git log

$git reset --soft コミットのハッシュ値

まずはgit logでどこまで戻せば良いか確認します。

次にgit reset --soft コミットのハッシュ値でブランチを

コミット前の状態に戻します。

この時絶対に--softを使ってください!

このオプションで作業ディレクトリの変更内容は残したまま、

コミットだけを取り消すことができます。


ちなみに指定するコミットのハッシュ値は、

このコミットまで戻したい!というコミットのハッシュ値を指定します。

指定したコミットの次にコミットしたものまでが取り消されます。

これに限らず、どれを指定するとどこまで影響するのか?にいつも迷います。。。


変更部分を一時退避する

$git stash

このままではcheckoutしても変更部分を反映させられないので、

この変更部分は一時的に退避させます。


ちなみに、退避させるものが複数ある場合、

$git stash save "hogehoge"

のように名前をつけておくこともできます。


ブランチを切り替える

わざわざ紹介することもないと思いますが、

$git checkout ブランチ名




切り替えたブランチで退避した変更部分を復活

$git stash pop

このコマンドは、退避した部分を復活と同時に

退避リストから削除するものです。

もちろん復活と削除を別々に行うことも可能ですが、

今回は全変更部分を一括でやってしまったので。。。

あとは切り替えたブランチでコミットすれば作業終了です。


なお、git stashの使い方については、こちらで詳しく説明してくださっています。

qiita.com

解決方法の参考にさせていただいたのはこちらです。

vatscy.github.io

www.kaasan.info




まとめ

今回は焦っていたので全変更部分を一括で移して1コミットで終わらせてしましました。

本来ならコミットごとに、

状態を一つ戻す→名前をつけて退避

を繰り返し、退避終了後

ブランチ切り替え→一つずつ復元→コミット

とすべきだったと思います。

初めてのことでパニクっていたのと、子供の迎え時間が迫っていたので

テンパって一気にどーん!とやってしまいました。

反省。。。。

しかしまずは作業前に確認しましょう。。。

本年もご覧いただき有難うございました。

f:id:song-of-life1352607:20180115004800j:plain

こんにちは、マリンです。

2018年最後の投稿です。

いよいよ今年も残すところ12時間弱。

年々1年が短くなっている様に感じます〜(´༎ຶོρ༎ຶོ`)

来年は1日1日、1仕事1仕事、1コード1コード大事に、

毎日勉強していく!!!(つもりです…(≧∀≦))

では、本年も皆さま当ブログをご覧いただき、

誠にありがとうございました!

2019年も地味に更新して参る所存ですので、

どうぞよろしくお願い申し上げます。

皆さま良いお年をお迎えくださいm( )m

【Rails】Date型↔︎文字型変換

f:id:song-of-life1352607:20180314224659p:plain

こんにちは、マリンです。

最近よく日付関係を扱うことがよくあるのですが、

Date(日付)型と文字列型の変換方法をよく忘れるので、

備忘録としてここに書かせていただきます。





Date型 → 文字列型

Date型から文字列型への変換方法。

strftime

today = Date.today    # 2018-12-05  Date型の日付(変換前)
today.strftime("%Y年 %m月 %d日")    # "2018年 12月 05日"  文字列型の日付(変換後)

Date型のオブジェクトに対し、strftimeメソッドを使うことで文字列型に変換できます。

引数にフォーマットを指定することで、その指定通りの形に変換できます。

ちなみに、%Y%yにすると年の下二桁だけを出せます。




フォーマット一覧は以下の通りです。

*文字は基本的に英語表記です。

フォーマット 変換後の出力
%A 曜日の名称
%a 曜日の省略名
%B 月の名称
%b 月の省略名
%c 日付と時刻
%d 日(01-31)
%H 24時間制の時(00-23)
%I 12時間制の時(01-12)
%j 年中の通算日 (001-366)
%M 分(00-59)
%m 月を表す数字(01-12)
%p 午前または午後(AM,PM)
%S 秒(00-60 *60はうるう秒)
%U 週を表す数。最初の日曜日が第1週の始まり(00-53)
%W 週を表す数。最初の月曜日が第1週の始まり(00-53)
%w 曜日を表す数。日曜日が0(0-6)
%X 時刻
%x 日付
%Y 西暦を表す数
%y 西暦の下2桁(00-99)
%Z タイムゾーン
%% パーセント文字




文字列型 → Date型

文字列型からDate型への変換。

to_date

to_dateメソッドを使う。

str_today = "2018-12-05"    # 文字列型の日付(変換前) 
str_today.to_date    # Date型の日付(変換後)
=> Wed, 05 Dic 2018

to_dateは楽なのでよく使います。

ただ、西暦の下二桁のみには対応していません。


strptime

Date型のstrptimeを使う。

Date.strptime('2018-12-05', '%Y-%m-%d')    # (文字列型の日付, 日付を表すフォーマット)
=> Wed, 05 Dic 2018 




to_dateに比べたらちょっと複雑になりますが、

こちらは西暦下二桁のみにも対応可能です。

Date.strptime('18-12-05', '%y-%m-%d')
=> Wed, 05 Dic 2018




まとめ

日付についてはDateやらDateTimeやらいろいろあっていつも混乱します。

また取得したものがDateなのか文字列なのか?とか。

計算するときに型が違うとエラーになっちゃいますしね(汗)

日付に関する型の違いもいずれまとめられたらと思います。

【Git】git reset --hard HEADを取り消したい!!

f:id:song-of-life1352607:20180113002051p:plain

こんにちは、マリンです。

今日Gitでやらかしました。

表題の通りなのですが、それにプラスしてDBのロールバックとかも関わってきて、

「ギャーーーー!!!」

ってなったので残しておきます。





概要

やらかした内容が多すぎるので、時系列で箇条書きします。

  1. 新規でDBのテーブルを作成するのにrails g migration ~~で作ってしまった。

  2. テーブルを作った後しばらく作業を進めてからModelが無いことに気づく。(この間何度かcommitしてる)

  3. Modelだけ作れば良いところ、わざわざrake db:rollbackしようとした。

  4. だがロールバックする前に何を思ったかCommitをテーブル作る以前まで戻してしまった。

  5. しかもGitの取り消しをgit reset --hard HEAD@{n}でやってしまった。(ここが一番問題)

  6. ロールバックしようにも当然マイグレーションファイルまで消えてしまったのでできない。




やはり一番のやらかしポイントは、commitのやり直しで

git reset --hard HEAD@{n}

を使ってしまったことでしょう。

--hardオプションをつけると、ワーキングツリーまで変更されてします。

では、これに対する対処を次から書いていきます。


対処法

1、2、3まではおそらくModelファイルだけ作ってあげれば良かった話だと思うので省略します。

4、5、6について詳細と対処法を記載しようと思います。


そもそも何が起こったのか?

私が不用意に打ったgit reset --hard HEAD@{n}

これによって何が起こったのかを解説します。

飛ばしていただいて結構です。


$ git reset --hard HEAD^
  • git reset:これは任意のcommitを取り消すコマンドです。

  • --hard :resetコマンドのオプションです。コミットだけでなく、ワーキングツリーまで元の状態に書き換えます。

  • HEAD@{n} :HEADの位置をどこまで戻すかを指示しています。@{n}で遡ることn番目のcommitを表します。




問題は真ん中の--hardオプションです。

これによりワーキングツリーまで元の状態に戻ってしまったため、

テーブル作成のために書いたマイグレーションファイルまで消えてしまいました。

(今回はテーブル作成をする一つ前のコミットまで戻ったので・・・)

そしてこれをrake db:rollbackを実行する前にやってしまいました。

該当のマイグレーションファイルが無いのにロールバックしたってできるわけがありません。

なのでこの取り消し(git reset)を取り消して、マイグレーションファイルを復活させる!

それが今回の目標です。


ちなみに問題のオプションですが、

--softにを指定すればコミットだけをなかったことにしてくれます。

これにしていればアワアワすることもなかったでしょう・・・

他にもコミットとインデックスを取り消す--mixedもあります。

オプション無しのデフォルトもこれです。

こちらでより詳しく説明してくださっています。

[git reset (--hard/--soft)]ワーキングツリー、インデックス、HEADを使いこなす方法




コミット取り消しの取り消し

ここからが本題です。

消してしまった事実をなかったことにして、

マイグレーションファイルを復活させます。


$ git reflog
$ git reset --hard HEAD@{n}
$ git log

こちらが一連の流れです。

git reflog

まずどこまでコミットを戻すか履歴を見ます。

コマンドがgit logでない理由は、

リセットされたコミットはgit logの履歴に出てこないからです。

git reflogは消されたコミットも履歴として見ることができます。


git reset --hard HEAD@{n}

ここで取り消しを無かったことにします。

今回は--hardオプションで大丈夫です。

消えてしまったワーキングツリーのファイルを復活させたいので。

HEAD@{n}のnにはログで確認した該当コミットが現在地から何番目にあたるかの番号を入れてください。

reflogの時に番号入りで表示されるはずなのでわかりやすいと思います!


git log

最後に念のため確認をしましょう。

この場合はこちらの履歴で大丈夫です。

以上が解説になります。

なお、実例を交えたより詳しい説明をこちらでしてくださっていますので、

ぜひご覧ください。

git reset --hardした内容を取り消す (git reset --hard, reflog, HEAD@{x}, 取り消してしまったコミットを元に戻す) - いろいろ備忘録日記




まとめ

今までadd、commit、pushくらいしかやってこなかったGitです。

正直他のことはよくわからないから触りたく無かったし、

そもそも何ができるのかも曖昧でした。

しかし、今回初めて「履歴が残っている」ということの有り難みを感じました。

そしてむやみに手元まで変えてはならない!!ということも・・・

基本は--softでやるのが安全だし、一般的な様ですね。

大変勉強になりました!

【Bootstrap/Rails】popoverのdata-contentの中身をHelperに切り出してみた。

f:id:song-of-life1352607:20170220105406j:plain

こんにちは、マリンです。

今回はBootstrapのpopoverです。

popoverはtitleに表示するタイトル。data-contentに表示する内容を書きます。

でも長い内容をタグ内に書きたくないでしょ!

ってなわけでRailsのHelperにメソッド切って表示内容を切り出してみました。

注)HTML上ではベタ書きになっちゃいますけど。




概要

- 環境 -

Rails5

Bootstrap3


- 例に使うテーブル構造 -

Usersテーブル

  • id

  • name

Childrenテーブル

  • id

  • name

  • user_id




- 概要 -

Userの名前をクリックすると、

ポップオーバーでChildrenの名前が表示されるようにします。

この時data-contentの中身が複雑になるので、

Helperにメソッドを切ってそれをdata-contentに渡します。


詳細

そもそもなんでこんなことになるのか?ちょっと詳細を説明します。

まず前提として、対象のページはindex.html.erbで、

Userの名前はUser.alleachで回して表示しているとします。

さらにChildrenは複数いる可能性があります。


そこで問題となるのが、

  1. どのUserの子供であるかを特定するためにuserを渡す必要があること。

  2. Childrenはuser.childrenの配列になること。

  3. その配列から名前を取得して表示するには、ループを回す必要があること。




ポップオーバーの内容は複雑になる場合、別エリアに切り出すことも可能です。

しかし今回の場合、まず一番外側でUser.allをループしています。

切り出すにはこのループの外になるでしょう。

となると1番のどのUserかを特定するuserを渡すのが大変です。

Ajaxを駆使してコントローラに値を渡して〜・・・なんてことが予想されます。




2、3番は表示内容が複雑になる理由です。

てことでHelperにメソッドを切ってみてはどうか?となったわけです。

ちなみに子の名前は一人一人改行して表示します。

HTML上はベタ書きになってしまいますが、コードはすっきりします。


ソースコード

さて前置きが長くなりましたが、ここから本題です。

まぁやってることは真新しいことではないのですが・・・

ControllerとModelは特に特筆することはないので、コードだけで解説は省略します。

Controller

# controllers/users_controller.rb

def index
    @users = User.all
end




Model

# user.rb

has_many :children

# child.rb

belongs_to :user




View

<!-- views/users/index.html.erb -->

<% @users.each do |user| %>
    <button class="btn" data-html="true" data-toggle="popover" title="子ども" data-cotent="<%= children_popover(user) %>">
        <%= user.name %>
    </button>
<% end %>




まずbuttonタグの中にあるdata-html="true"をご覧ください。

今回のメソッド切り出しとは直接関係はありませんが、

これを入れることでポップオーバーの中で改行<br>を使うことができるようになります。




次に本題のdata-content="<%= children_popover(user) %>"です。

このerb表記の中身が、後述のHelperに切り出すメソッド名です。

引数にuserを渡していることにご注目ください。

別エリアへの切り出しではないため、大枠のループのuserをそのまま使うことができます。




Helper

# helpers/users_helper.rb

def children_popover(user)
    users_children = []
    user.children.each do |child|
        users_children << child.name
    end
    users_children.join("<br>")
end




まず最初に子どもの名前を入れる空の配列を用意します。

そしてuser.childrenのループを回し、

その中で名前を先ほどの配列に格納していきます。

最後にループの外で出来上がった配列を、

区切り文字に<br>改行を指定して返します。

これを使うために先ほどViewでdata-html="true"を指定しました。

解説は以上になります。


まとめ

今回は値の取得などにたいした時間がかからないものでしたのでこの方法を使用しました。

しかし、表示する値の取得に時間がかかるようでしたら、

やはり別エリアに表示内容を切り出し、

値の取得にはAjaxを使って行うなどの方法をとった方が良いかと思います。

私も機会がありましたら挑戦してみようと思います。

ちなみに、今回の検証でpopoverを使用するためにclass="btn"が必ず必要だったのですが、

これは仕様でしょうか?それとも私の設定ミスでしょうか?

どなたかわかる方いらっしゃいましたら是非教えてください。

よろしくお願い致します。