RubyでInterface

そもそもRubyにInterfaceが必要とか、必要じゃないとかの議論は置いておいて、 無いのであれば、類似したことをやりたい場合はどうすればいいのかが気になって調べてた。

1. まずは簡単にやってみる

module TestInterface
  def method1
    raise NotImplementError
  end
end

class SomeClass
  include TestInterface
  def method1
    puts "hoge"
  end
end

SomeClass.new.method1

実装はココから拝借。

blog.wktk.co.jp

愚直にmoduleで書いたほうが良いんじゃないかなぁと思っている。 そのほうが明示的でわかりやすいというか… 業務でどうしても欲しい場合は、消去法でコレで良いんじゃないんですか。という感じ。

2. Gem使う

その名もズバリなInterFaceというGemがあるので、コレを使ってみることにする。

github.com

実装もここから読めるし、100行もないくらいの短いコードなので、興味がある人は読んでみて欲しい。 interface/interface.rb at master · djberg96/interface · GitHub

require 'rubygems'
require 'interface'

TestInterface = interface{
  required_methods :method1
}

class SomeClass
  def method1
    puts "hoge"
  end
  implements TestInterface
end

SomeClass.new.method1

Interfaceをそれっぽく、かつRubyっぽい感じ*1でかける。 ただ一番しっくりこないのは、implements*2を必ずメソッドの定義よりも下に書かないと動かないことだろう。 多分これは実装上の都合なだけな気がするので、もしかすると修正されて、メソッド定義よりも上に書いてみても大丈夫になるかもしれない。 業務で使うのはあまりオススメ出来ない…

まとめ

結局少し試してみたけど、これならやらんでもいいかな…となってしまった。

*1:ココでいうRubyっぽいというのは簡潔という意味で、そもそもInterface使おうとしている時点でRubyっぽくないのは間違いない。

*2:コード見れば解るけど、このGemのimplementsの実体はただのincludeのエイリアスだったりする。 https://github.com/djberg96/interface/blob/master/lib/interface.rb#L106

お手軽に管理しやすいWordPressテーマを目指すとっても簡単なTips

これは前に書いたブログの記事を再編集したものです。

こんにちは。 簡単なphpコードとWordPressの関数を使用すれば、とっても管理しやすい Wordpressのテーマが出来上がることを知っていますか? あまりphpの詳しくない方でも簡単に始めるモダンなWordPress環境への手引きを記載していきたいと思います。

ちなみに自分はfunctions.phpに1万行も書かれているような案件には配属されたことなくて、せいぜい1000行くらいだったきがする。(もう書くことは殆ど無いけど)

1. home.phpなどのテンプレートファイルにWP_Queryとかたくさん書いてあってグチャグチャ

何でもかんでもテンプレートファイルに書くのはやめて、functions.phpに書こう

この書き方をすることによって得られるメリットはご覧になって分かる通り、 home.phpのコード量が減るだけでなく、関数名を付けることが必要なので「このWP_Queryが何を取得しているのか」を非常に把握しやすくなる。

before

<?php

// home.php

$query = new WP_Query(array(
  'post_type' => 'post',
  'tax_query' => array(
    'relation' => 'OR',
    array(
      'taxonomy' => 'category',
      'field'    => 'slug',
      'terms'    => array( 'quotes' ),
    ),
    array(
      'relation' => 'AND',
      array(
        'taxonomy' => 'post_format',
        'field'    => 'slug',
        'terms'    => array( 'post-format-quote' ),
      ),
      array(
        'taxnoomy' => 'category',
        'field'    => 'slug',
        'terms'    => array( 'wisdom' ),
      ),
    ),
  ),
));

if ( $query->have_posts() ): $query->the_post();
?>

after

<?php
  // home.php
  $query = getMainLoop();

  if ($query->hava_posts() ) : $query->the_post();
?>
<?php 
// functions.php
function getMainLoop()
{
  return new WP_Query(array(
    'post_type' => 'post',
    'tax_query' => array(
      'relation' => 'OR',
      array(
        'taxonomy' => 'category',
        'field'    => 'slug',
        'terms'    => array( 'quotes' ),
      ),
      array(
        'relation' => 'AND',
        array(
          'taxonomy' => 'post_format',
          'field'    => 'slug',
          'terms'    => array( 'post-format-quote' ),
        ),
        array(
          'taxnoomy' => 'category',
          'field'    => 'slug',
          'terms'    => array( 'wisdom' ),
        ),
      ),
    ),
  ));
}

WP_Queryの設定値が配列地獄になってしまうのはどうしようもないので、後方互換性を切り捨てた、次世代型のWP_Queryに期待しましょう。

2. 今度はfunctions.phpがカオスになってきてしまった。

各役割ごとにファイルを分けてfunctions.phpを管理する

具体的なコード例

<?php
  // functions.php
  require_once('/lib/functions/admin.php');
  require_once('/lib/functions/model.php');
<?php
   // /lib/functions/admin.php
   //管理画面に関する処理....
<?php
// /lib/functions/model.php

  //WP_Queryを中心とした、データを取得・操作する処理....

  function getCategories()
  {
      return WP_Query(array(
          //....
      ));
  }

別にルールとかはなんでもいいのだけど、 1ファイルに全部まとまらないように書いていくのが重要。

ちなみに僕は小規模なテーマであれば、クラスとかは使わないで データを取得するロジックは lib/functions/model.php, 管理画面に機能を追加するロジックは lib/functions/admin.phpに … と言った感じにざっくり分けて書いている。

そんなに大規模なものでないならクラス設計しないで、そのまま雑にさっさと書いたほうが早いし、簡単。

個人的にはWordPressMVCみたいなのをいちいち書くのはアホらしいと感じてて、そこまでするんだったら、さっさとWordPress捨てて、何かフレームワーク使ったほうが絶対に幸せなのにな。っていつも思ってる。(事情があって使っているのはわかるけれども…)

無名関数を使ってフィルターイベントを書く

※ これはphp5.3以降で使用できるTipsなので、使用する場合はphpのバージョンを確認してから使用するようにしましょう。

これはWordPressのhook処理を追加する時に重宝するTipsです。 関数名が他のものと被ってしまうと処理がうまく行かなかったりするかもしれません。 この書き方ではそれを解決し、それぞれのadd_filterを追加する時に関数名がぶつかるかどうかを気にする必要がないことがメリットです。

ただしこれを使用すると、他の部分で使いまわすことができなくなるので、いろいろなhookで同じ処理を使い回したいときは、 この書き方をするべきではないでしょう。 他のHook処理等に使いまわしていない処理はこのように積極的に無名関数で書いていって良いと思います。

before

<?php

add_filter('the_excerpt_rss','add_thumbnail');

function add_thumbnails($content){
  global $post;
  if(has_post_thumbnail($post->ID)) {
      $content = '<p>' . get_the_post_thumbnail($post->ID) . '</p>' . $content;
  }
  return $content;
}

after

<?php

add_filter('the_excerpt_rss',function($content){
  global $post;
  if(has_post_thumbnail($post->ID)) {
      $content = '<p>' . get_the_post_thumbnail($post->ID) . '</p>' . $content;
  }
  return $content;
});

配列の短縮構文をちゃんと使おう

※ これはphp5.4以降で使用できるTipsなので、使用する場合はphpのバージョンを確認してから使用するようにしましょう。

ちゃんとサポートされているバージョンで開発しているのであれば、積極的に利用しましょう。 php 5.4以下をサポートする必要がないのに、使わない理由はまったくないと思ってください。 arrayなんでタイプしている暇があればもっと他のことに時間を使いましょう。

PHP: 配列 - Manual

before

<?php 
function getMainLoop()
{
  return new WP_Query(array(
    'post_type' => 'post',
    'tax_query' => array(
      'relation' => 'OR',
      array(
        'taxonomy' => 'category',
        'field'    => 'slug',
        'terms'    => array( 'quotes' ),
      ),
      array(
        'relation' => 'AND',
        array(
          'taxonomy' => 'post_format',
          'field'    => 'slug',
          'terms'    => array( 'post-format-quote' ),
        ),
        array(
          'taxnoomy' => 'category',
          'field'    => 'slug',
          'terms'    => array( 'wisdom' ),
        ),
      ),
    ),
  ));
}

after

<?php 
function getMainLoop()
{
  return new WP_Query([
    'post_type' => 'post',
    'tax_query' => [
      'relation' => 'OR',
      [
        'taxonomy' => 'category',
        'field'    => 'slug',
        'terms'    => [ 'quotes' ],
      ],
      [
        'relation' => 'AND',
        [
          'taxonomy' => 'post_format',
          'field'    => 'slug',
          'terms'    => [ 'post-format-quote' ],
        ],
        [
          'taxnoomy' => 'category',
          'field'    => 'slug',
          'terms'    => [ 'wisdom' ],
        ],
      ],
    ],
  ]);
}

FSSMで特定条件のLinux環境で使えないバグを修正してプルリクエストを投げた

いきなりまとめ

  • Linux環境のGemのrb-inotifyがインストールされている環境で使用した場合、落ちるエラーを修正した。
  • まだ反応がなくMergeされていないので、GithubからFork版を一旦使ってほしい。 github.com
  • PRはこちら github.com

そもそもFSSMとは?

Rubyでファイルを監視してなにかしらの処理を行うためのGem。 ファイルを監視して、変更を検知した場合にSlackで何か通知したり、ファイルバックアップデータを取ったりするスクリプトをささっと書くために利用されている。 FSSM自体に関してはここではあまり言及しないので下記サイトをご参照してもらえればと。 www.virment.com

どんな不具合があったのか

Gemのrb-inotifyがインストールされており、Linux環境だった場合に下記エラーを吐いて落ちてしまうというものでした。

/home/vagrant/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/fssm-0.2.10/lib/fssm/support.rb:58:in `>': comparison of String with 0 failed (ArgumentError)
    from /home/vagrant/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/fssm-0.2.10/lib/fssm/support.rb:58:in `rb_inotify?'
    from /home/vagrant/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/fssm-0.2.10/lib/fssm/support.rb:9:in `usable_backend'
    from /home/vagrant/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/fssm-0.2.10/lib/fssm/support.rb:25:in `backend'
    from /home/vagrant/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/fssm-0.2.10/lib/fssm.rb:54:in `const_missing'
    from /home/vagrant/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/fssm-0.2.10/lib/fssm/monitor.rb:4:in `initialize'
    from /home/vagrant/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/fssm-0.2.10/lib/fssm.rb:67:in `new'
    from /home/vagrant/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/fssm-0.2.10/lib/fssm.rb:67:in `monitor'
    from app.rb:15:in `<main>'

中身自体は至って単純で、 下記コミットログを参照してもらうとわかるけど、rb-inotifyのINotify::VERSIONがArrayからStringに変更されていた。(ちょっとコミットログが長い…) github.com

なので、下記PRにて、対応をざっくりと行ってとりあえず動くようにしておいた。 github.com

正直修正内容に関しても筋がちょっと悪いかも…と思ってて、 本来はGem::Versionで比較するべきだよなぁ…と思ったのだが、 とにかく動かないことに対して不満だったので、今回はこれで投げて様子を見ることにした。

その後

投げて2日位たったけど、作者や他のメンテナー(いるのか?)から特に反応がない。 もう少しして特に反応がない場合は、メンション付きのコメントをして反応があるかを見てみようと思う。

日本郵便での日本からのEMS配送料を算出するAPIを公開してみた。

前回の記事に引き続き、 過去の作ったものでブログを書いてみるシリーズの第2弾です。

前回はこっち。 webuilder240.hatenablog.com

前にこの記事を書きましたが、 webuilder240.hatenablog.com

これをAPIで公開しました。 Productionで使われることはそこまで気にしていないんで、HerokuのHobbyインスタンスで適当にやっています。 自前で欲しい場合はHerokuButtonもないので、公開しているリポジトリから適当にやってください。 他の404も手抜きです。今後使う人が増えてくればやろうかなぁと言う感じ。

API: https://from-jp-ems.herokuapp.com

Repo: github.com

使い方

curl -X GET https://from-jp-ems.herokuapp.com?area=asia&weight=100 | jq

Response

{
  "result": 200,
  "area": "asia",
  "weight": 100,
  "amount": 1400
}

ざっくり仕様

  • weightは数値型で1〜30000のグラム単位の値。
  • area は “asia”, “oceania”, “north_america”, “middle_east”, “central_america”, “europa”, “africa”, “south_america"のいずれか。

料金表についてはをここを参照してください。(エンドポイントを追加しようかな。) www.post.japanpost.jp

ちょっと工夫した点

ほとんど算出するロジックはGemで実装しているし、ほとんどやることは無かったと言っても過言ではない。 まぁでも若干苦労したところがあったので、簡単に書いておく。

weightが文字列だったときの対応

このAPIの場合、weightに渡すパラメータは数値型、Integerである必要がある。 なのでざっくりIntegerに変換する方法をとった。 ちなみにto_iだったら、"100kg"がパラメータとして渡ったときに100と返してしまうので使えなかった。 Integerで変換して文字列だったときはrescueでキャッチして、数値を0にするようにした。(もっと他にありそう。)

  def weight=(value)
    begin
      value = Integer(value)
    rescue TypeError, ArgumentError
      value = 0
    end
    @weight = value
  end

そのうち何らかの形でkg単位のAPIなんかもサポートしたいと思っている。

実行できるメソッド

areaで渡ってきたパラメータをそのままバリデーションして実行したかったので、 ライブラリで定義されているクラスメソッドの一覧を取得して、そのリストでValidateするようにした。 特異メソッドなので、singleton_methodsで取得。 ソースコードで言うとこんなところ。

  ALLOW_AREAS = JpEmsFee.singleton_methods.map(&:to_s)
  validates :area, inclusion: { in: ALLOW_AREAS }

あとはなるべくControllerに処理を書かないようにしたくらい。 本当はDecorator層とかに書けよってものもModelに書いてるけど、 本当に小さなAPIなので気にしない。

反省点・今後のTODO

Docker

今回は面倒だったのでやらなかった。 これは今後Herokuで動かせるようにしつつやっつける。

Rails API

API用途だけなので、Rails API使ってみればよかった。 他の不要なGemを掃除できたのでそのうち対応してみる。

Request Rspec

まだ書いてない。正直書くボリュームでもないかな。とも。

Swagger

使ってみればよかった…

最後に

僕の働いているオシロ株式会社ではエンジニアを募集しています。

手前味噌ですが、面白い福利厚生や会社制度もありますし、 普通のWebサービスではあまりないような課題があるので、Railsをバリバリ書ける環境で、やりがいがあるんじゃないかなと思っています。 Railsを覚えたい他の言語でのご経験のあるエンジニアさんも大歓迎です!!

まずは話を聞いていただけるだけでもとても嬉しいので、まずはWantedlyの募集ページを見てみてください! 何卒よろしくお願いいたします。

www.wantedly.com

PHPのシンプルなファイルキャッシュの仕組みを作ってみた。

大げさな事を言っているけど、単にAPI何かのレスポンスをキャッシュしたいときに雑に扱えるものを作った。という感じ。 類似ライブラリはあるっちゃあるけど、このくらいの実装だったら自分でメンテしたほうが良いと思ってる。 公開も面倒なので雑にGistにした。使い方もGistを参照して欲しい。

simple file base cache for php

Expireは?

expireは用途によってExpireするタイミングとかって違うだろうし、メンテも面倒なので気が向いたら書くか〜という感じ。

Packgistに公開は?

現状していない。ぶっちゃけ自分でPHP書く案件はとても小さな案件が多いだろうし、Composerすらいれるのが面倒なのである。(主にWordPressだったり素のPHP案件) とはいえ、このライブラリで市民権を得たいという気持ちもあるので、テストが書けたら1.0.0としてリリースを検討している。

まとめ

  • こういったとってもシンプルなライブラリを作っておく or 類似ライブラリを見つけることで自分の小さいサービスを作るときに多いに役立つ。
  • FBやインスタなんかのAPIを叩いて作るライブラリなんかはこういうのを雑に導入しておくととても効率が良い。
  • そのうちRuby版作ってみる。

最後に

僕の働いているオシロ株式会社ではエンジニアを募集しています。

メインで触っているのはRubyだったりするのですが、少しPHPの業務もあったりするし、 Rubyをやってみたくて悶々としているPHPエンジニアさんも大歓迎です!

まずは話を聞いていただけるだけでもとても嬉しいので、まずはWantedlyの募集ページを見ていただけると幸いです。 何卒よろしくお願いいたします。

www.wantedly.com