4月メモ・リンク集

4 月に調べたことのメモです。

Angular 関連

Angular 6

リリース前の予習に。

モーダルの状態を ngrx で管理する

f:id:ryotah:20180506150121p:plain
  • UI state management with Redux in Angular 4 (Example) | hack.guides()
    • core => layout => openedModalName: string という state を用意
    • openedModalName を受け取るコンポーネントAppComponentに配置
    • @Input の setter でうけとりモーダルの Open/Close をハンドリングする
  • angular-modal-state-with-ngrx-wip - StackBlitz
    • ngrx で管理する方法と今まで通りの方法の比較デモ
    • (途中まで)
  • 今の考え、実装に必要なタスク
    • 複数の種類のモーダルを管理するファサードっぽいモーダルコンポーネントを用意
    • 非同期で取得したデータも渡す必要
    • Esc キーや Backdrop クリックでモーダルを閉じた時のアクション
      • => 閉じた時に ModalRef からイベントなど取得できたかも
    • モーダル内で新たなアクション、かつコンテキストに左右されるようなアクションをどうハンドリングするか
    • それらを踏まえたメリット
    • 開発中のアプリケーションでは、アクション発行 => Effect 層でモーダルオープン
      • (中途半端な気もしている)
      • ModalRef を利用したい場合

ngrx の状態管理を Router (URL) 起点にする

f:id:ryotah:20180506150105p:plain

Rails 関連

マイグレーション

  • Railsのmigrationの基本とレシピ集 - Rails Webook
    • ※upとdownは、changeメソッドの代わりに使います。 upはrake db:migrateの実行時に実行され、downはrake db:rollback時に実行されます。

    • データの投入 rake db:seedコマンド

  • rake db:drop, rake db:schema:load

Active Record クエリインターフェイス

Active Record コールバック

class Order < ApplicationRecord
  before_save :normalize_card_number, if: :paid_with_card?
end
class Order < ApplicationRecord
  before_save :normalize_card_number,
    if: Proc.new { |order| order.paid_with_card? }
end

JSのテスト 関連

spyOn を利用してインスタンスメソッドの挙動を確認する

it('calls self bar method', () => {
  spyOn(Foo.prototype, 'bar');
  new Foo();
  expect(Foo.prototype.bar).toHaveBeenCalled();
});

Docker

Circle CI

- run:
      name: Install Chrome
      command: |
        wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
        sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
        sudo apt-get update
        sudo apt-get -y install google-chrome-stable

TypeScript

http://www.typescriptlang.org/docs/handbook/advanced-types.html#mapped-types

Mapped types は便利。

type Keys = 'option1' | 'option2';
type Flags = { [K in Keys]: boolean };

// 以下は自分の利用例
export const DateFormats: {
  [key in DateFormatTypes]: DateTimeFormatOptions
}
...
export const DateFormats: {
  [key in DateFormatTypes]: DateTimeFormatOptions
}

Partial<T>も最近使うようになった。

Note that Readonly and Partial are so useful, they are included in TypeScript’s standard library along with Pick and Record:

Pick, Recordはまだ使ったことない)

その他

3月メモ・リンク集

3月に調べたことのメモです。

Angular関連

React関連

CircleCI関連

Angularで作成したアプリのCircleCI環境を2.0に移行した時のメモです。

2.0 Docs - CircleCI(オフィシャルドキュメント)

オフィシャルからは、これくらいの資料を確認。

Angular対応

It has 3 main components: a version, a list of jobs, and a list of workflows.

...

Common things that you can achieve within a job include: - Installing the tools you need to run/build/test your project - Executing bash commands - Storing or restoring items from the CircleCI cache

...

The key of the cache is generated using the checksum function, which will output a base64 encoded hash of the package.json file’s content.

...

We run the build command. Notice that we are going to use a multi-line command with each line running in the same shell, so we start the command with the pipe (|) character.

...

その他

Rails関連

Android関連

もしかしたらAndroidエンジニアになるかも、という状況があったので設計まわりについて調べてみた時の記事リンクです。Androidアーキテクチャも面白そう。

その他

Rails チュートリアル復習用メモ

以下を参考にした、Rails アプリを作成する手順の覚書です。

モデルとコントローラーの基本的な部分を知ることが目的だったので、モデルの応用的な部分や View 周りはできるだけ省いたアプリケーションに仕上がっています。具体的には 4, 11, 12, 14 章をスキップし、それ以外の各章もスタイルやインテグレーションテストについては省いています。(発展的なログイン機構、アカウント有効化のメール送信、パスワード再設定、ユーザーのフォローなどが機能として未実装。)

新規アプリ作成

rails _5.1.4_ new first_app
rails server
# => http://localhost:3000/

Gemfile 更新

feat: upadte Gemfile · ryotah/rails-tutorial@6336633

bundle install --without production

モデルを作成

User モデルと Micropost モデルを作成します。

User モデルを作成

rails generate model User name:string email:string password_digest:string
# => app/models/user.rb
# => db/migrate/xxx_create_users.rb
# => test/fixtures/user.yml
# => test/models/user_test.rb

rails db:migrate
# => db/schema.rb
# Rails 4以前
# bundle exec rake db:migrate

すでに存在するモデルにインデックスを追加。

rails generate migration add_index_to_users_email

生成されたdb/migrate/[timestamp]_add_index_to_users_email.rbを更新してから migrate を実行。

class AddIndexToUsersEmail < ActiveRecord::Migration[5.0]
  def change
    # 以下の行を追加
    #
    # unique: true => 一意性を強制
    add_index :users, :email, unique: true
  end
end

User モデルに validates, has_secure_password, has_many :microposts, dependent: :destroy などを追加。

class User < ApplicationRecord
  has_many :microposts, dependent: :destroy
  before_save { self.email = email.downcase }
  validates :name,  presence: true, length: { maximum:  50 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 },
                    format: { with: VALID_EMAIL_REGEX },
                    uniqueness: { case_sensitive: false }
  has_secure_password
  validates :password, presence: true, length: { minimum: 6 }, allow_nil: true

  def User.digest(string)
    cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
                                                  BCrypt::Engine.cost
    BCrypt::Password.create(string, cost: cost)
  end
end

(メモ)rails console からユーザーを追加

user = User.create(name: "Michael Hartl", email: "mhartl@example.com", password: "foobar", password_confirmation: "foobar")

user.authenticate("foobaz")
# => false
!!user.authenticate("foobar")
# => true

Micropost モデル

rails generate model Micropost content:text user:references

生成されたdb/migrate/[timestamp]_create_microposts.rbを更新。

class CreateMicroposts < ActiveRecord::Migration[5.1]
  def change
    create_table :microposts do |t|
      t.text :content
      t.references :user, foreign_key: true

      t.timestamps
    end
    # 以下の行を追加
    #
    # インデックスを追加して、user_id に関連付けられたマイクロポストを
    # 作成時刻の逆順で取り出しやすくする
    add_index :microposts, [:user_id, :created_at]
  end
end

Micropost モデルに validates, default_scope を追加。(belongs_to :user は最初から用意されている。)

class Micropost < ApplicationRecord
  belongs_to :user
  default_scope -> { order(created_at: :desc) }
  validates :user_id, presence: true
  validates :content, presence: true, length: { maximum: 140 }
end

テスト

# 全テストを実行
rails test

# modelsのみテストを実行
rails test:models

# 特定ファイルのテストを実行
rails test test/models/micropost_test.rb

コントローラー作成

必要なControllerを作成。

rails generate controller StaticPages home
rails generate controller Users new
rails generate controller Sessions new
rails generate controller Microposts

ルーティングを設定

Rails.application.routes.draw do
  # root_path
  root 'static_pages#home'

  # 名前付きルートを定義
  get '/signup', to: 'users#new'
  post '/signup', to: 'users#create'
  get '/login', to: 'sessions#new'
  post '/login', to: 'sessions#create'
  delete '/logout', to: 'sessions#destroy'

  resources :users
  resources :microposts, only: [:create, :destroy]
end

セッションヘルパー

  • 以下 5 つを定義
    • ログイン
    • セッション情報から現在のユーザー(ログインユーザ)を取得
    • ログインユーザーか確認
    • ログイン済みか確認
    • ログアウト
module SessionsHelper
  def log_in(user)
    session[:user_id] = user.id
  end

  def current_user
    @current_user ||= User.find_by(id: session[:user_id])
  end

  def current_user?(user)
    user == current_user
  end

  def logged_in?
    !current_user.nil?
  end

  def log_out
    session.delete(:user_id)
    @current_user = nil
  end
end

ApplicationControllerincludeする。(各ビューとコントローラーで利用できるようになる)

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  include SessionsHelper
end

サンプルデータを追加

https://github.com/ryotah/rails-tutorial/blob/b27827169748425dbff08188d97c366eb73ec1fb/db/seeds.rb

rails db:seed

ビューとコントローラーを調整

ヘッダー

(メモ)デバッグ

<%= debug(params) if Rails.env.development? %>

ユーザー一覧

views/users/index.html.erbを追加。

<h1>Users#index</h1>
<%= render @users %>

views/users/_user.html.erbを追加。(render @usersに対応するパーシャル)

<div><%= user.name %>, <%= user.email %></div>

コントローラーにindexを追加。

class UsersController < ApplicationController
  # ...
  def index
    @users = User.all
  end
  # ...
end

ログイン

views/sessions/new.html.erbにフォームを追加。

<%= form_for(:session, url: login_path) do |f| %>
  <%= f.label :email %>
  <%= f.email_field :email, class: 'form-control' %>
  <%= f.label :password %>
  <%= f.password_field :password, class: 'form-control' %>
  <%= f.submit "Log in", class: "btn btn-primary" %>
<% end %>

<p>New user? <%= link_to "Sign up now!", signup_path %></p>

コントローラーにcreateを追加。

def create
  user = User.find_by(email: params[:session][:email])
  if user && user.authenticate(params[:session][:password])
    log_in user
    redirect_to user
  else
    flash.now[:danger] = 'Invalid email/password combination'
    render 'new'
  end
end

(メモ)Flash

<% flash.each do |message_type, message| %>
  <div><%= message_type %> | <%= message %></div>
<% end %>

ユーザー作成

<%= form_for(@user, url: signup_path) do |f| %>
<%= render 'shared/error_messages', object: f>
# ...

ユーザー詳細

ホーム(マイクロポスト作成)

  • feat: post a micropost · ryotah/rails-tutorial@98a775c
    • ログイン済みユーザーかどうか確認するlogged_in_userApplicationControllerに追加
    • MicropostsControllercreateを実装
      • before_action :logged_in_user, only: :create
    • app/views/static_pages/home.html.erbにマイクロポスト用のフォームを追加
<h1>StaticPages#home</h1>
<p>Find me in app/views/static_pages/home.html.erb</p>  <p>Find me in app/views/static_pages/home.html.erb</p>
<% if logged_in? %>
  <%= current_user.name %>, <%= current_user.email %>
  <%= form_for(@micropost) do |f| %>
    <%= render 'shared/error_messages', object: f.object %>
    <%= f.text_area :content, placeholder: "Compose new micropost..." %>
    <%= f.submit "Post", class: "btn btn-primary" %>
  <% end %>
<% end %>

元記事のメモ/リンク

@user = users(:michael)
# このコードは慣習的に正しくない
@micropost = Micropost.new(content: "Lorem ipsum", user_id: @user.id)

Rails ガイドより

users = User.all
user = User.first
david = User.find_by(name: 'David')

2月メモ・リンク集

2月に調べたことのメモです。

f:id:ryotah:20180303165052p:plain f:id:ryotah:20180303165054p:plain

リサイザー

@Directive({
  selector: '[grabber]',
})
export class GrabberDirective {
  // ...
  constructor(
    private elm: ElementRef,
    @Host() @SkipSelf() private resizable: ResizableDirective
  ) { }
  // ...
}

Show More/Less コンポーネント

function getLineHeight(element: HTMLElement): number {
  let lineHeight = parseInt(
    window.getComputedStyle(element, undefined).getPropertyValue('lineHeight'),
    10
  );
  if (isNaN(lineHeight)) {
    const clone = element.cloneNode() as HTMLElement;
    clone.innerHTML = '<br>';
    element.appendChild(clone);
    lineHeight = clone.clientHeight;
    element.removeChild(clone);
  }
  return lineHeight;
}

ドラッグアンドドロップdnd

実装の必要がなくなったので、参考にしようとしたリンクのみ。

Googleスプレッドシート的なスクロール

  • angular-scrollbar-like-spreadsheet - StackBlitz
    • コンテンツ(A)とスクロールエリア(B)を用意
    • A自体のスクロールバーは利用しない
    • Bの内部の要素をAの高さに合わせる
    • Bのスクロールに合わせてAを移動、A上発生したホイールイベントを利用してBを移動
// スクロール内部の高さを対象の高さに合わせる
const height = this.contentInner.nativeElement.clientHeight;
this.scrollInner.nativeElement.style.height = `${height}px`;

// スクロール時に
this.scroll.nativeElement.addEventListener('scroll', (e) => {      
  this.content.nativeElement.scrollTop = e.target.scrollTop;
});

// コンテンツエリアでマウホイールイベント発生時に
this.content.nativeElement.addEventListener('wheel', (e) => {
  const result = this.content.nativeElement.scrollTop + e.deltaY
  this.content.nativeElement.scrollTop = result;
  this.scroll.nativeElement.scrollTop = result;
});

AngularのDIに関して

Ruby, Rails関連

VS CodeでRubyを書く

.vscode/settings.json

"ruby.lint": {
  "rubocop": true,
  "ruby": {
      "unicode": true //Runs ruby -wc -Ku
  },
},

その他

1月メモ・リンク集

1月に調べたことのメモです。

Angular関連

Date系ライブラリとIANA

最近はLuxonを利用しています。Intlのブラウザ対応状況、他のライブラリのタイムゾーン関連のサポートについて気になったので、そのあたりを調べた時のメモです。

複数のプロジェクタで共有したいロジックとコンポーネントをパッケージ化した

必要な前提知識

最終的に上記環境を利用し、簡単にパッケージ作成ができるようになったのだが、実際には色々とつまづいた。原因は、前提となる基礎知識がかけていたから。
(基本的な公式ドキュメントはしっかり読みましょうという話)

ReactのHOCs

その他

AngularのCDK Tableのコードを読みながら、Viewの組み立て方について調べる

段階的に実装してみたサンプルコードを晒します。完成版とおまけを除くと8ステップにわかれています。

Viewの組み立て部分を参考にしたかったので、DataSourceやIterableDiffers, TrackByFunctionなどを利用したコードはありません。

CDK Tableのドキュメント

0: 完成版

f:id:ryotah:20180124234812p:plain

1: コンポーネントを動的にaddする

2: 対象のコンポーネント内にaddする

3: コンポーネントのaddからテンプレートを取得してaddする方式に変更

f:id:ryotah:20180124232835p:plain

4: table.ts, row.ts, cell.tsを用意

  • angular-cdk-datatable-04-celldefs - StackBlitz
    • 各セルの内容を定義するために、CellDefという考え方を導入
    • この段階では実態は export class CellDef { constructor(public template: TemplateRef<any>) {} } というDirective

5: 行定義を追加(RowDef)

app.component.html

<my-table [data]="data">
    <!-- cellを定義 -->
    <ng-container *cellDef="let data;">
        <span style="border: 1px solid cyan;">[cell-1] {{data}}</span>
    </ng-container>
    <ng-container *cellDef="let data;">
        <span style="border: 1px solid yello;">[cell-2] {{data}}</span>
    </ng-container>
    <ng-container *cellDef="let data;">
        <span style="border: 1px solid blue;">[cell-3] {{data}}</span>
    </ng-container>
  
</my-table>  

table.html

<!-- rowを定義 -->
<ng-container *rowDef>
    <div style="border: 1px solid red; background-color: rgba(255,0,0,.2)">[row]
        <ng-container cellOutlet></ng-container>
    </div>
</ng-container>

6: ヘッダー用の行定義を追加(HeaderRowDef)

f:id:ryotah:20180124234656p:plain

app.component.html

<my-table [columns]="columns" [data]="data">

  <!-- header cellを定義 -->
    <ng-container *headerCellDef="let data;">
        <span style="border: 1px solid cyan;">[ttl-1]{{data.title}}</span>
    </ng-container>
    <ng-container *headerCellDef="let data;">
        <span style="border: 1px solid green;">[ttl-2]{{data.title}}</span>
    </ng-container>
    <ng-container *headerCellDef="let data;">
        <span style="border: 1px solid blue;">[ttl-2]{{data.title}}</span>
    </ng-container>

    <!-- cellを定義 -->
    <ng-container *cellDef="let data;">
        <span style="border: 1px solid cyan;">[cell-1] {{data}}</span>
    </ng-container>
    <ng-container *cellDef="let data;">
        <span style="border: 1px solid green;">[cell-2] {{data}}</span>
    </ng-container>
    <ng-container *cellDef="let data;">
        <span style="border: 1px solid blue;">[cell-3] {{data}}</span>
    </ng-container>
  
</my-table>  

table.html

<!-- header rowを定義 -->
<ng-container *headerRowDef>
    <div style="border: 1px solid magenta; background-color: rgba(255,0,255,.2)">[row]
        <ng-container cellOutlet></ng-container>
    </div>
</ng-container>

<!-- rowを定義 -->
<ng-container *rowDef>
    <div style="border: 1px solid red; background-color: rgba(255,0,0,.2)">[row]
        <ng-container cellOutlet></ng-container>
    </div>
</ng-container>

7: accessorを追加

8: header cell, cellの定義をcolumnの定義でまとめる

f:id:ryotah:20180124235744p:plain

app.component.html

<my-table [data]="data">

    <!-- [name] columnを定義 -->
    <ng-container columnDef="name">
        <ng-container *headerCellDef>
            <span style="border: 1px solid cyan;">Name</span>
        </ng-container>
        <ng-container *cellDef="let row;">
            <span style="border: 1px solid cyan;">{{row.name}}</span>
        </ng-container>
    </ng-container>
    <!-- [age] columnを定義 -->
    <ng-container columnDef="age">
        <ng-container *headerCellDef>
            <span style="border: 1px solid green;">Age</span>
        </ng-container>
        <ng-container *cellDef="let row;">
            <span style="border: 1px solid green;">{{row.age}}</span>
        </ng-container>
    </ng-container>
    <!-- [id] columnを定義 -->
    <ng-container columnDef="id">
        <ng-container *headerCellDef>
            <span style="border: 1px solid blue;">Id</span>
        </ng-container>
        <ng-container *cellDef="let row;">
            <span style="border: 1px solid blue;">{{row.id}}</span>
        </ng-container>
    </ng-container>

</my-table>

9: おまけ

12月メモ・リンク集

12月に調べたことのメモです。

Angular関連

View作成や再利用について

f:id:ryotah:20180107131908p:plain f:id:ryotah:20180107131911p:plain f:id:ryotah:20180107131913p:plain
<div [myItem]="item1" #dir="myItem">
  {{dir.item.header}}: {{dir.item.content}} | <button (click)="dir.onRemove()">remove</button>
</div>

Form関連

f:id:ryotah:20180107131717p:plain
private markFormGroupTouched(formGroup: FormGroup) {
  (<any>Object).values(formGroup.controls).forEach(control => {
    control.markAsTouched();
    if (control.controls) {
      control.controls.forEach(c => this.markFormGroupTouched(c));
    }
  });
}

Dateライブラリ

VSCode

コードフォーマット

{
  "singleQuote": true,
  "trailingComma": "es5"
}
  • tslint --fix
    • 今まで利用してなかったけど、便利すぎた
    • TypeScript Hero のSort and organize your imports (sort and remove unused)をわざわざする必要なくなった
"tslint.autoFixOnSave": true,
"no-unused-variable": true,
"ordered-imports": true,

Go環境用意

  • インストール
    • brew install go
export GOPATH=$HOME/foo/bar
export PATH=$GOPATH/bin:$PATH
  • GOPATH以下に開発環境を構築する
    • go get などのコマンドを実行するとGOPATHフォルダにダウンロードされる
  • PATHを設定しておくとコマンドうつのが楽

Reactチュートリアル

Tutorial

  • Tutorial: Intro To React - React
  • in React apps to use on names for the attributes and handle for the handler methods.

  • Why Immutability Is Important
    • Easier Undo/Redo and Time Travel
    • Tracking Changes
    • Determining When to Re-render in React
  • It’s strongly recommended that you assign proper keys whenever you build dynamic lists.

  • react-tutorial-tic-tac-toe - StackBlitz
    • 自分でやったサンプル

その他

Google Analytics

コンポーネントを設計するときに

f:id:ryotah:20180107131350p:plainf:id:ryotah:20180107131359p:plain
  • 「飛行機のパーツ」とみるか、「2×4のブロック」とみるか
  • 細かくつくる
  • 汎用化と抽象化
  • Viewにどこまで関係するのか(テンプレートにどこまで関係するのか)
    • それは「ロジック」ではないか
    • それは「振る舞い」ではない