ImageMagickで服の色を変える

こんにちは岡野です。
現在開発中のプロジェクトで、服の色を変更する処理を実装したので紹介します(簡易的な方法です)。

環境

ImageMagick 6.9.12-59 Q8

手順

1. 元となる画像を用意します。例:Tシャツ ホワイト。
2. 例えばネイビーへ変更するとして変更前後の色についてRGBそれぞれの差分を計算します。
ホワイト: rgb(255, 255, 255)
ネイビー: rgb(32, 47, 85)
R: 32 - 255 = -223
G: 47 - 255 = -208
B: 85 - 255 = -170
3. ImageMagickのevaluate Addに差分の値を渡して色を変換します(Q16環境では各値を256倍します)。
convert src.png \
-channel R -evaluate Add -223 \
-channel G -evaluate Add -208 \
-channel B -evaluate Add -170 \
dest.png

まとめ

ImageMagickだけでも結構良い感じになりました。将来はAIで着色したいと思います。

その他

最近Zabbixでハマったことです。グラフ内の文字が出力されないため悩んでいたのですがopen_basedirの設定不足でした(zabbix_server.logに気をとられNginxのログに気づくのが遅れた)。何か問題があった際は落ち着かないとダメだと感じました。

SymfonyとVue.jsを組み合わせるときはSymfony Formを使うのは諦めた方が良さそう

初めまして。イメージ・マジックのスズキです。
いきなりですが、本稿は『SymfonyとVue.jsを組み合わせるときは「Symfony Forms」の機能を使うのは諦めた方がいいかもしれない』ということを伝えたい、ただそれだけの記事です。『エンジニアたる者、どんなときでもまずは結論から話せ』と、物心ついた頃(嘘)から聞かされてきましたしね。
Symfonyで一般的なデータの入力や保存を行う画面をつくるなら、「Symfony Forms」という機能を利用するのが基本のようです。
公式ドキュメントでも、基本的な実装方法として紹介されています。

Symfony Formsを使うことで、コントローラー側でフォームと画面項目の定義をしておいて、画面側(テンプレート側)では自力でタグを書いたりせずに表示することができます。
試しに、テキスト項目が2つあるだけの画面を作る場合、こういう感じになるでしょうか。
// エンティティ
namespace App\Entity;

class Master
{
    private string $name = '';
    private string $description = '';

    public function getName(): string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;
        return $this;
    }

    public function getDescription(): string
    {
        return $this->description;
    }

    public function setDescription(string $description): self
    {
        $this->description = $description;
        return $this;
    }
}
// フォーム
namespace App\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;

class MasterEditType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name', TextType::class)
            ->add('desctiption', TextType::class)
        ;
    }
}
// コントローラー
namespace App\Controller;

use App\Entity\Master;
use App\Form\Type\MasterEditType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class MasterEditController extends AbstractController
    public function edit(Request $request) : Response
    {
        $master = new Master();
        // ...
        
        $form = $this->createForm(MasterEditType::class, $master);

        return $this->render('master/edit.html.twig', [
            'form' => $form->createView(),
        ]);
    }
}
<!-- 画面テンプレート(Symfony Form使用) -->
<div>
    <div>
        <label>名称</label>
        {{ form_widget(form.name) }}
    </div>
    <div>
        <label>説明</label>
        {{ form_widget(form.description) }}
    </div>
</div>
画面のテンプレートには <input type="text"> といったごく普通のHTMLタグが登場しません。Symfony formsがタグを生成してくれるのですね。

ただ、リッチなフロント画面をVue.jsでごりごり書きたいときは、<input type="text" v-model="master.name">などとVue.jsのディレクティブを入れることになるのですが、Symfony Formsを使っているとそれがしにくくなります。
そんなわけで、Vue.jsでいろいろやりたいならば、Symfonyは使ったとしてもSymfony Formsの機能は使わない方が良さそう、といった印象です。

Symfony Formsを使わずに書くなら、サーバ側ではSymfony Formの代わりに単純なオブジェクトを作ってリターンし、画面側ではVue.jsのお作法でそれを表示するような作りが良さそうです。
先ほどのコードを、Symfony Formsを使わずに(Vue.jsを使えるように)書き直すと、こんな感じでしょうか。
// 変更後のコントローラー(Symfony Formを使わない)
namespace App\Controller;

use App\Entity\Master;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class MasterEditController extends AbstractController
    public function edit(Request $request) : Response
    {
        $master = new Master();
        // ...

        return $this->render('master/edit.html.twig', [
            'master' => [
                'name' => $master->getName(),
                'description' => $master->getDescription(),
            ],
        ]);
    }
}
// 変更後の画面テンプレート(Vue.js使用)
<div>
    <div>
        <label>名称</label>
        <input type="text" v-model="mst.name">
    </div>
    <div>
        <label>説明</label>
        <input type="text" v-model="mst.description">
    </div>
</div>
<!-- Vue.jsのスクリプト部分 -->
<script>
export default {
  props: {
    master: {},
  },
  setup(props) {
    const mst = ref(props.master);
    return {
      mst,
    };
  },
}
</script>
ということで今回は、SymfonyとVue.jsを組み合わせるときのお話でした。Symfony単体やVue.js単体での使い方は情報があっても、組み合わせ方の情報はなかなか無いので書いてみました。
Web界隈はフレームワークやライブラリがたくさんあって、組み合わせるときは試行錯誤が要りますね。

PHP触ったことのないプログラマーが動的な型付けに頭を抱えた話


こんにちは!5月に入社した黒羽(くろは)です!


隔週での更新を目標に開発一同で再開しましたテックブログ、再開後としては2つ目(?)の記事になります。
いきなり社歴の浅い新人が書いているので驚かれる方もいると信じて簡単な自己紹介をしつつ、本題に入っていこうと思います。 
さて、文系の学部を出てプログラマーになって6年目も中盤に差し掛かっておりますが、大学在学中よりも時間の経過が速かった気がします。
今までに取り扱った言語としては以下のような感じです。
  • Java
  • C#
  • Python
  • javascript
  • CSS
  • HTML
  • その他にコマンドプロンプト(世の人々がプログラムと聞いてイメージするであろう『黒い画面に文字がダーッとでる』やつ)で使用するスクリプト類など…
  最後のは言語じゃなくてコマンドの塊じゃん!と言われそう イメージマジックの開発で主に使われている言語はPHPなので入社して初めてPHPを触りました。
今まで触ってきた言語とは書き味や仕様が違っていて日々調べながら習得を進めています。
今日はそんな言語仕様に振り回されてハマってしまった時の原因について調べたので(社内ではそりゃ当たり前だろといわれそうなので未来のPHP初学者に向けて)共有できればと思います。

きっかけ


イメージマジックの各プロジェクトのPHPのソースコードを眺めているとどのソースにも
declare(strict_types=1);
と出てきます。 これは何ぞやと調べてみたところ次の記事に行き当たりました。
この記事の冒頭でこのように記述されていました。
 
 
declare(strict_types=1); とは、PHP7から導入された、厳格な型検査モードの指定構文です。

厳格な型検査モード・・・・?????と思いつつ読み進めてみると更に以下の記述が。
<?php
function add(int $a, int $b): int
{
    return $a + $b;
}

var_dump(add(1.0, 2.0));
この状態で単体実行すると、int(3)が出力されます。
  この時の私の頭の中では1つの考えに支配されていました。
「int型の変数にdouble型を堂々と代入するな!!!!!!」


言語による型付け


あまりに理解ができなくて大声になりましたが気を取り直していきます。
プログラミング言語には開発者が変数や戻り値になど事前にどういう型のデータが入るかを指定する静的な型付けと実行時にコンパイラやインタプリンタがコードを解釈してよしなにデータ型を判断してくれる動的型付けがあります。
PHPは言語仕様として動的な型付けを行っています。
私はJava()静的な型付け)からプログラミングをはじめて、当時の学習用テキストにもデータ型が一致しないとエラー吐くと脅されて教えられました。
そのため、データ型が定義されている変数に異なるデータ型を突っ込まれると困ってしまうわけです。
しかもdouble同士の加算でint型で返ってくるとはこれ如何に。
上記のサンプル関数では$a=1.0≒1(int型)、$b=2.0≒2(int型)としてPHPの方で判断しているようです。
話は逸れましたがdeclare(strict_types=1);を付けてさきほどの関数を実行すると

PHP Fatal error: Uncaught TypeError: Argument 1 passed to add() must be of the type integer, float given, called in /Users/hiraku/sandbox/stricttypes/A.php on line 9 and defined in /Users/hiraku/sandbox/stricttypes/A.php:4 Stack trace: #0 /Users/hiraku/sandbox/stricttypes/A.php(9): add(1, 2) #1 {main} thrown in /Users/hiraku/sandbox/stricttypes/A.php on line 4

とエラーが発生します。
つまりdeclare(strict_types=1);とは動的な型付けをしているPHPで静的な型付けのように振舞わせる宣言という風に解釈できます。
ちなみにこの宣言をしているファイルを呼び出している場合のみ有効であるため、別ファイルからuse文等で参照して同じように関数を呼び出しても緩い型検査のままであるのでint型で返ってきます。
つまり引数を$a=1.0,$b=2.0とする場合、
  • declare(strict_types=1); を宣言しているファイルAのadd関数を呼び出す⇒Typeエラーが発生
  • declare(strict_types=1); を宣言しているファイルAを参照しているがdeclare(strict_types=1); を宣言していないファイルBでファイルAのadd関数を呼び出す⇒int型の戻り値が返却される。 
ということです。

最後に


つまるところ何が言いたかったのかというとカルチャーショックを受けたという話でした。
動的な型付けは手軽な一方で想定していない値が返ってくる可能性があるのでやはり多少面倒でも静的な型付けできっちりとデータ型を指定して書く方が私は好きです。(というかぱっと見で何の値が入ってくるのかソースから読み取れないと後々困ることもある)
最後に余談となりますがこの記事を書いてる間に裏どりがてらに色々調べていたのですが 「動的な型付け」と「型推論」は同じものだと思っていましたが違うもののようです。
ここら辺も少し調べてみようと思います。
ここまで読んでいただきありがとうございました。何かの参考になれば幸いです!


LaravelのCollectionパッケージでPHPの配列操作を劇的に快適にする

こんにちは、池田です。最近家の鍵をsesame4というスマートロックにしました。鍵の向きを考えずにスマートフォンをかざすだけでドアが開くというのは、スマートフォンの充電端子がType-Cになったときと、駅の改札を交通系ICで通れるようになったときを合わせたような感動があります。まあどっちも昔のことで大して覚えていないので、適当言ってるだけなんですが。

本題

さて、皆さんはこの世で最も邪悪なものを知っていますか。そう、PHPでの配列操作のやりにくさですね。どれだけ配列操作がやりにくいかについては、以下の記事を読むのが良いと思います。
PHPを使いもせずDISってる君達へ 
特に記事のPHP Tips 5 : array_filterが歯抜けになるのに気をつけようの現象には、
自分「よし、配列をフィルタして最初の値を取ろう!」
array_filter([1,2,3], fn(int $num) => $num > 1)[0]
PHP「だめだよ」
自分「???」
理由:array_filterされた配列は[1 => 2, 2 => 3]になっており、0のキーは存在しないので取れないから
と何度も苦しめられました。さすがに学習した今でもこの挙動に関しては納得できず、思い出す度にFワードが出そうになります。会社のテックブログじゃなければ出してます。
また、連続で配列操作関数を使用した際のネストの深さ、読みにくさもピカイチです。こちらは先程の記事で一番最初に紹介されてますね。
これは Symfony という素晴らしいフレームワークを使っていても、如何ともし難いものがありました。先程の記事にもあったように、Symfonyでは配列をラップしたDoctrineの Collectionクラス が存在するんですが、メソッドの数が痒い所に手が届かない感じで(ソートができない!)(フラットにできない!)(ユニークにできない!)、複雑な配列操作をしようとするなら一度ネイティブの配列に戻すことが必要になります。なんか気持ち悪いですね。継承してオレオレ実装もあまりしたくありません。絶対バグを生み出すので。
ならばどうすれば良いのか……?ここで本題です。LaravelのCollectionパッケージを使いましょう。

何が良いのか

公式ページ にアクセスして、メソッド一覧を見てください。たくさんのメソッドありますね。131個あるらしいです。これだけ数があるということは、ニーズにあったメソッドも存在する可能性が高いということです。
例えば、先程挙げた3個の「ソート」「フラット」「ユニーク」は全て用意されたメソッドで可能です。また、無駄にネストを深くする必要もありません。
collect([1, [2, 3, [4, 2]]])
  ->flatten()
  ->unique()
  ->sort();
上記のように、非常にシンプルな記述をすることが可能です。
中でも、私がイチオシのメソッドは mapWithKeys です。皆さんも幼稚園くらいの頃に「以下のように連想配列に変換したいな……」と考えたことがあると思います。
↓これを
[
    [
        'name' => 'John',
        'department' => 'Sales',
        'email' => 'john@example.com',
    ],
    [
        'name' => 'Jane',
        'department' => 'Marketing',
        'email' => 'jane@example.com',
    ]
]
↓こうする
[
    'john@example.com' => 'John',
    'jane@example.com' => 'Jane',
]
ネイティブの配列操作関数でやるなら、恐らくarray_reduceでやる必要があると思います。ですが読みにくくなるので、使わないに越したことはありません。LaravelのCollectionを使えば以下のように簡単に変換することができます。
$collection = collect([
    [
        'name' => 'John',
        'department' => 'Sales',
        'email' => 'john@example.com',
    ],
    [
        'name' => 'Jane',
        'department' => 'Marketing',
        'email' => 'jane@example.com',
    ]
]);

$keyed = $collection->mapWithKeys(function ($item, $key) {
    return [$item['email'] => $item['name']];
});

$keyed->all();

/*
    [
        'john@example.com' => 'John',
        'jane@example.com' => 'Jane',
    ]
*/
別の言語だとJavaScriptは結構配列操作がやりやすいと思っているんですが、それでもこの変換をここまでシンプルにはできなかったと思うので、mapWithKeysには驚きました。 しかし、中にはLaravelを使っていない人もいるでしょう。Laravelのパッケージをまるごと入れてCollectionだけ使うしかないのでしょうか。当然そんなことはありません。以下のコマンドでこれだけ(+α)導入可能です。
composer require illuminate/support
これにより、Symfony環境であってもLaravelのCollectionの素晴らしさを享受することができます。
……え、フレームワーク使ってない?Composerなんてない?……それは……その……どうすればいいんでしょうね……?

まとめ

  • PHPネイティブの配列操作はやりにくい
  • LaravelのCollectionパッケージを使えば途端にやりやすくなる
  • 単体(+α)導入も可能

Symfonyで2要素認証

こんにちは、岡野です。
最近Symfonyを使っているWebサイトで2要素認証を実現しました。その際に使用したscheb/2faバンドルは非常に使いやすく、Symfony公式マニュアルを見ながら容易に実現できました。但し一点問題があり、認証コードを入力しないままユーザが画面遷移すると中途半端な認証状態になってしまうので困りました。
今回はその回避方法を共有します。
 

環境

Symfony: 4.4.20
scheb/2fa-*: 5.13.1
 

起きていた現象

1. ユーザがID/PWを入力。
2. 認証コードを尋ねるポップアップを表示。
3. ユーザが他の画面へ遷移。

→ 2.でSymfony内部の認証状態が「Partially authenticated」になるため、3.でController#getUser()すると認証が完了していないユーザエンティティが取得できてしまう(※1)。
また3.のページがfirewallで制限している場合は認証コードを尋ねるページが表示される。一般的な通常サイトの様にログイン画面へ遷移させたい(※2)。
 

※1(完全認証前のユーザが取得できてしまう)の回避方法

EventListenerを使って、認証コードの確認が完了するまでuserをanonymousにする。
 
class AuthAttemptListener implements EventSubscriberInterface
{
  public static function getSubscribedEvents(): array
  {
      return [
          TwoFactorAuthenticationEvents::ATTEMPT => ['onTwoFactorAuthAttempt'],
          TwoFactorAuthenticationEvents::COMPLETE => ['onTwoFactorAuthComplete'],
          TwoFactorAuthenticationEvents::FAILURE => ['onTwoFactorAuthFailure'],
          KernelEvents::RESPONSE => ['onKernelResponse'],
          KernelEvents::TERMINATE => ['onKernelTerminate'],
      ];
    }

  /**
   * セッション内の情報が正しければ、バンドル内の2要素認証処理用にトークンへユーザを設定する。
* ユーザは後で解除する。
   */
  public function onTwoFactorAuthAttempt(TwoFactorAuthenticationEvent $event): void
  {
      // ...
      $this->attemptingToken->setUser($user);
    }

  /**
   * 2要素認証が成功した場合、トークンを認証状態に設定する。
   */
  public function onTwoFactorAuthComplete(TwoFactorAuthenticationEvent $event): void
  {
      // ...
      $this->attemptingToken->getAuthenticatedToken()->setAuthenticated(true);
    }

  /**
   * 2要素認証失敗時、トークンのユーザを元に戻す
   */
  public function onTwoFactorAuthFailure(TwoFactorAuthenticationEvent $event): void
  {
      if ($this->attemptingToken) {
          $this->revertTokenUser();
      }
    }

  /**
   * 例外発生時、トークンのユーザを元に戻す
   */
  public function onKernelResponse(): void
  {
      if ($this->attemptingToken) {
          $this->revertTokenUser();
      }
    }

  /**
   * 安全のためSymfonyの処理が終わる際に、万が一処理中のトークンが残っていたら
* 例外を発生させてエラー通知する。
   */
  public function onKernelTerminate(): void
  {
      if ($this->attemptingToken) {
          throw new \LogicException();
      }
    }

  private function revertTokenUser(): void
  {
      $this->attemptingToken->setUser('anon.');
      $this->attemptingToken->getAuthenticatedToken()->setAuthenticated(false);
      $this->attemptingToken = null;
  }
}

※2(認証コード要求ページが表示される)の回避方法

認証コードが要求された場合、ログイン画面へ遷移する。
 
services.yaml
  app_2fa_required_handler:
        class: App\Security\TwoFactorAuth\AuthRequiredHandler

class AuthRequiredHandler implements AuthenticationRequiredHandlerInterface
{
  public function onAuthenticationRequired(Request $request, TokenInterface $token): Response
  {
      $this->tokenStorage->setToken(new AnonymousToken('', 'anon.'));
      throw new AccessDeniedException();
  }
}
参考になれば幸いです。

同じ名前で中身がちがうもの

こんにちは。イメージマジック三浦です。 最近めっきり寒くなってきました。体調には十分に気を付けてください。今日は「同じ名前で中身がちがうもの」と題して書いてみます。

アニメの場合

アニメ作品では、異なる登場人物を同じ声優が担当することがあります。 最近一番驚いたのは、アニメ「ルパン三世」の次元大介役の声優交代の話です(本投稿執筆時点の約3か月前にインターネット記事で出ています)。交代前後の声優は以下の通りです。
  • 交代前:小林清志さん
  • 交代後:大塚明夫さん
お二人はOVA「機動戦士ガンダム0083 STARDUST MEMORY」にて、「デラーズ・フリート」という陣営の上官と部下として共演されています。
  • 小林清志さん:エギーユ・デラーズ
  • 大塚明夫さん:アナベル・ガトー
記事を見て 「次元大介の声が デラーズからガトーへ 受け継がれるか」と感じました。 「デラーズからガトーへ」というキーワードでインターネット検索すると、この声優交代の情報が出てくるところに反響の大きさを感じました。 劇中、2人の登場人物には深い信頼関係があったので、熱い気分になりました。

PowerShellの場合

話はPowerShellに変わります。こちらは、はまりポイントになったという話です。

きっかけ

Windows11がWindows Updateから利用できるようになったのを受けて、自分の作業用PCをWindows11にアップグレードしました。 そのPCで自社開発アプリの動作確認を実施していましたが、アプリの1つでPowerShellスクリプトが動かないエラーが出ました。
このシステムではスクリプトの実行が無効になっているため、ファイル XXXX を読み込むことができません。
PowerShellの実行権限が無いときに出るエラーメッセージです。

調査してみる

実行権限の不足ということで、普段使っているPowerShellでLocalMachineに「RemoteSigned」を設定してみたが動きません。
PS C:\> Get-ExecutionPolicy LocalMachine
RemoteSigned
PS C:\>
OS再起動しても動きません。 アプリを開発したメンバーにも確認しましたが「PowerShell」の権限設定に問題はなさそうでした。 Windows11ではPowerShellを起動するときに何か特殊な対応が必要なのか?と思って一度は諦めました。

謎が解けた

後日、アプリのログファイルに「PowerShell.exe」と出ているのを見つけて、自分のPCに2種類のPowerShellがいることに気づきました。

2種類の「PowerShell」

2種類のPowerShellとは以下の通りです。
  • Window10プリインストール版
    • PowerShell5.1
    • プロセス名は「powershell.exe」
  • 普段使っているPowerShell(後でインストールした後継版)
    • PowerShell7.2
    • プロセス名は「pwsh.exe」
「powershell.exe」 と「pwsh.exe」は、それぞれでスクリプト実行権限を管理していて、一方で権限を開放しても他方に反映されません。 「普段使っているPowerShell」=「pwsh.exe」で権限を開放しても、「powershell.exe」には一切効果なし。そして 「powershell.exe」 がスクリプト実行権限を開放していなかったので、
このシステムではスクリプトの実行が無効になっているため、ファイル XXXX を読み込むことができません。 
が延々と出ていました。実際に「powershell.exe」で実行権限を開放したら、無事に動きました。

「謎」の正体

今回の謎の正体は「PowerShell違い」でした。幸いにして特殊な対応は不要でした。

2つのPowerShellの見分け方

最後に2つのPowerShellの見分け方をご紹介します。
※本記事執筆当時、三浦の作業用PCで確認した動作です。Windows UpdateやPC本体の設定により異なる可能性があります。

起動直後のメッセージ

  • powershell.exe は「新機能と改善のために最新の PowerShell をインストールしてください!」とメッセージを表示します。
  • pwsh.exe は「PowerShell 7.2.0」のように、バージョン番号を先頭行に表示します。

バージョン番号

このコマンドでPowerShellのバージョン番号が分かります。
$PSVersionTable | find --% "PSVersion"
powershell.exe
PS C:\> $PSVersionTable | find --% "PSVersion"
PSVersion                      5.1.22000.282
PS C:\>
pwsh.exe
PS C:\> $PSVersionTable | find --% "PSVersion"
PSVersion                      7.2.0
PS C:\>

最後に

PowerShell5.1, PowerShell7.2, どちらも「PowerShell」と呼んで間違いではないので、混乱したという話でした。
「PowerShell」と呼ばれる異なるバージョンのアプリが同居している可能性を常に考えておきたい、と思った出来事でした。 本日は以上です。 2022.06.02 誤字脱字を訂正しました。

一度に1つのことを(リーダブルコードより)

こんにちは。3行コードを書くとそれより前に書いたコードの記憶が飛んでいる池田です。
決して口開けて空眺めながらコードを書いているわけではないと弁明させてほしいんですが、それでも1日経てばもう終わりです。昨日書いたコードの記憶は消し飛び、「このコード書いたの誰だよ…。…俺か…」と自己嫌悪の波に飲まれ……ることはありませんが、自分の書いたコードの解釈に体力を使うことになります。過去の自分に苦しめられることほど腹の立つことはありません。ではどうすれば良いか。読みやすいコードを書けばいいんです。簡単ですね。何が簡単なんだ。

リーダブルコード――より良いコードを書くためのシンプルで実践的なテクニック

https://www.oreilly.co.jp/books/images/picture_large978-4-87311-565-8.jpeg これは私が新卒の頃に借りて読んだ本です。新卒の頃は今の100分の1程度の技術力しかありませんでしたし、初版も2012年(原著は2011年)と古めですが、それでもこの本は非常に読みやすかったです。それから数年経ち、改めてまた読みたくなったので自分で買いました。この本の内容は現在も通用し、全て重要で、どの部分が一番であるといった優劣は付け難いんですが、今回はこの本の中から私が一番気を付けていることを紹介します。タイトルにもある一度に1つのことをです。

一度に1つのことを

本よりの引用ですが、以下の2つのJavaScriptコードを見比べてみてください。vote_changedはユーザがブログで投票するときに使われる関数です。初版が10年近く前なのでconstではなくvarを使っていたりしますが、原文ママで載せています。皆さんはconstを、定数ではどうしようもないときだけletを使用してください。letのほうが読みやすいし、くらいの理由ならconstを使ってください(過激派)。
var vote_changed = function(old_vote, new_vote) {
    var score = get_score();

    if (new_vote !== old_vote) {
        if (new_vote === 'Up') {
            score += (old_vote === 'Down' ? 2 : 1);
        } else if (new_vote === 'Down') {
            score -= (old_vote === 'Up' ? 2 : 1);
        } else if (new_vote === '') {
            score += (old_vote === 'Up' ? -1 : 1);
        }
    }
    
    set_score(score);
}
var vote_value = function (vote) {
    if (vote === 'Up') {
        return +1;
    }
    if (vote === 'Down') {
        return -1;
    }
    return 0;
}

var vote_changed = function (old_vote, new_vote) {
    var score = get_score();
    
    score -= vote_value(old_vote); // 古い値を削除する
    score += vote_value(new_vote); // 新しい値を追加する
    
    set_score(score);
}
恐らく下のほうが読みやすいと感じたと思います。
「下のほうはコメントがあるからずるい!」という意見が聞こえてきました。ではscore += (old_vote === 'Down' ? 2 : 1);にコメントを付けるとすると、どう付ければ良いでしょうか。少なくとも「古い値を削除する」だけでは実態に合っていません。下は一度に「古い値を削除する」というひとつのことが行われているからこそ、これほど単純なコメントを付けるだけで済んでいます。
バグがあったとしても、上の方は「一番最初の条件分岐か?それとも2個目?それとも三項演算子?」と考えることが多くありますが、下の方は「多分vote_valueの条件分岐だろう」くらいには当たりを付けることができます。

最後に

今回書いた内容はとても基本的なことで、「この世に生を受けたときからこんなの知ってるわ」という方もいると思います。実際、この本の内容は非常に基本的なことが紹介されていて、ドメイン駆動設計クリーンアーキテクチャといった完璧な秩序をプロジェクトにもたらす内容、というものではないと思います。ただ、この本の内容を完璧に守ったコードがあったとしたら、それは高尚な設計思想が使われていなくても非常に読みやすいコードだと思います。
簡単な内容で200ページとちょっとなので、一度図書館とかで借りて流し読みしてみましょう。それだけでもきっと明日の自分が書くコードが少しだけ晴れ晴れとして見えると思います。

ついでに

株式会社イメージ・マジックではエンジニアを募集しています。PHP未経験者でも大丈夫です。
「PHPはちょっとなあ…」という方もいるかもしれませんが、弊社ではSymfonyというMVCフレームワークを採用しており、JavaのSpring BootやRuby on Railsと同じような書き味ですし、何より使いやすいです。PHPはわかりませんが、Symfonyのことはきっと好きになれると思います。

Slackのポストをリリース作業のチェックリストとして使ってみる

こんにちわ、サボっていて久しぶりにブログを投稿するURAです。
現在、maker town というデザインシミュレーター付クラウド型オンデマンドEC(長い!)なるものを開発中です。
maker town自体についてはまたの機会にご紹介するとして、今回はmaker townのリリース作業で煩雑に感じていた部分を、最近ちょっと改良してみたお話をしたいと思います。

前提

  • GitHub Flowを採用している
  • 機能追加・バグ修正をある程度まとめてリリースすることが多いため、事前にリリースする対象のリストを作っている。
  • リリース対象のリストはSlackで共有していた(リリースが終わったら流れてもOK)

問題点

  • プルリクエスト(PR)を出す人とリリース作業をする人が同じとは限らないため、すべてのPRのレビューが済んだのかが、リリース作業をする人から見て分かりづらい。
  • GitHubでリリース対象すべてのPRがCloseしたかチェックする作業が無駄。
  • リリースする人以外から見ても、リリース作業の進捗が分かりづらい。

解決策

Slackで共有していたリリース対象のリストを共同編集できるようにして、PRのレビューが済んだらチェックを付けて進捗を見える化する。

Slackのポスト機能をリストの共同編集に利用する!

「知らんけど、Slackになにかしら共同編集するための機能あるでしょ?」と言ってみたところ
たにすぎ池田がSlackのポスト機能を探してきてくれました。グッジョブ。

Slackのポストの使い方

1. 左のショートカットボタンから「ポスト」を検索して選択する

2. リリース対象のチェックリストを作成

3. 右上の「共有」を開いて「他のメンバーの編集を許可する」にチェックして共有

まとめ

改良したばかりでまだリリース作業がラクになったかは分かりませんが、運用しやすくなるよう開発フローは継続して改善していきたいです。

ものすごく基本的な話ですが…

はじめに

こんにちは、イメージ・マジックのもあいです。
bashで簡単なツールを作っているときに、つまらない内容ではまってしまったので、備忘録としておいておきます。

bashで関数

あまり、bashで関数は作らないのですが、スクリプトの長さがそこそこあるのとパラメータによる分岐があったので関数かしてソースコードを整理していた時に、スクリプトが正常に動作しなくなってしまいました。そのソースコードの主な抜粋は下記のようになります。
#!/bin/bash
FIRST=${1}

function add ()
{
  echo add function
}

function remove ()
{
  echo remove function;
}

if [ "${FIRST}" = "add" ]; then
  add
elif [ "${FIRST}" = "remove" ]; then
  remove
else
  echo ${0} add|remove 以下使い方
fi
そしてこのスクリプトを実行したときの結果は下記の通り
remove function
想定としては「add|remove 以下使い方」が表示されることでしたが、remove関数実行されてしまいました。

結論

結論はechoに|removeと書いてしまっているからでした。あまりBashで深くスクリプトを作成していないので、この行に行き着くのにものすごく時間がかかりました。このechoの後ろの文字を”で囲めば想定した動作になりました。 使い方として記載したechoで関数が呼び出されることになるのは考えていなかったのでびっくりしました。

どっちなの問題

こんにちは、イメージマジック三浦です。
弊社本社がある小石川一帯は最近大きいビルの建設が終わり、テナントも入り始めてきました。工事はまだまだ継続中ですが、工事が終わったらどんな街になっているのか気になります。
最近解釈が分かれやすい表現は極力避けたいと感じた例を見かけたので、人によって解釈が分かれる、または分かれやすい例を「どっちなの問題」と名付けて書いてみます。

こんな数式を見かけました

いきなりですが、四則演算の問題です。以下の式を計算するといくつになるでしょうか。
6÷2(1+2)
回答は2通りに分かれるそうです。
説1:6÷2(1+2)=3×3=9
説2:6÷2(1+2)=6÷6=1

真実が実は2つ?

回答が2通りに分かれるポイントは、カッコの中を計算した後の掛け算と割り算をどういう順番で扱っているかです。「2(1+2)」は「2×(1+2)」の掛け算記号が省略されているのですが、省略される掛け算を扱う優先順位が異なることで答えが2説出てきます。
  • 説1:カッコの中を計算した後、左から順番に割り算と掛け算を実行する。
  • 説2:カッコの中を計算した後、省略されている掛け算を優先して実行し、その後で割り算を実行する。

プログラム言語での扱い方

コンピューターではこの数式をどう処理するのか調べてみましたが、そもそもプログラム言語では掛け算記号の省略表記ができませんでした。参考として、手元にあるpython3.7で省略表記しようとした時のTraceback・説1の結果を出す式表記・説2の結果を出す式表記を書いた結果を掲載します。
(結果が少数表記になる点は、ここでは考慮しないことにします。)
Python 3.7.1 (v3.7.1:260ec2c36a, Oct 20 2018, 14:57:15) [MSC v.1915 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> 6/2(1+2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
>>>
>>> 6/2*(1+2)
9.0
>>>
>>> (6/2)*(1+2)
9.0
>>>
>>> 6/(2*(1+2))
1.0
>>>
カッコをつけることで、掛け算・割り算の優先順位が明示されています。

正しい答えはどっち?

正しい答えは説2の方でした。実際に関数電卓で計算してみると、説2の答えが出てきます。
これは解釈が分かれやすい「どっちなの問題」です。

その他の「どっちなの問題」

今回のテーマから派生して、解釈が分かれる「どっちなの問題」の例を挙げます。

0の0乗はいくつ?

「xの0乗」は、0でない実数(-1のルートを考えなくてよい世界)では常に1ですが、0の0乗はどうなるのかは数学の中でも統一されていません。1と決めると都合がいい分野と「定義しない」と決めると都合がいい分野があり、各分野が都合のよい方を定義に採用して理論構築しています。

旧暦2033年問題

今使われている「グレゴリオ暦」のひとつ前「天保暦」の問題です。天保暦では2月・5月・8月・11月を設定し、設定後に他の月を設定します。しかし、100年に一回程、設定できる月が1つに定められないという事態が発生するケースがあり、直近では2033年~2034年に起こるというものです。
俳句で使う季語は旧暦の春夏秋冬で区分けされる等、決められないことによる影響は小さくないようです。こちらは一定のルールを決めて解決しようとしているようですが、解決方法が複数あって検討中のようです。

詰将棋【最後の審判】

詰将棋は、限られた自軍の駒を動かしながら相手の王将を詰ますパズルで、本将棋のルールに加えて詰将棋独自のルールがあります。本将棋のルールでは「禁じ手」が規定されていますが、「最後の審判」は詰将棋のルール上「禁じ手」を指すしかない状況が双方で生じた時に勝敗を判断できない、という状態が発生する作品です。
便宜上、詰将棋の1種として記載していますが、実は詰将棋として成立するかも結論が出ていません。 こちらについて興味のある方は、【詰将棋 最後の審判】で検索してみてください。

注:単に【最後の審判】で検索すると、ミケランジェロの「最後の審判」がヒットします。これも 「最後の審判」と言われて、絵なのか詰将棋なのかで解釈が分かれる「どっちなの問題」の一例です。