クラスのスーパークラスはモジュール

お久しぶりです。さのです。
お客様先に出たおかげで、ソースコードがコピーできなくてブログ書けないとです。
言い訳です。

最近、がっつりRubyの勉強にはまってます。
Rubyの資格の勉強をしつつ、Railsでアプリ作りつつ、本を読んでいます。
今読んでる本がこちらです。

オライリー社のメタプログラミングRuby
https://www.oreilly.co.jp/books/9784873117430/

これがめっちゃ面白い!
まだ1章の途中ですが、すでにわくわくしています。
中でも今回気になったのが、RubyのClassの親クラスはModuleということ。

Class.superClass
=> Module

Moduleって何

Moduleって何かと簡単に説明しますと、メソッドを格納しておける入れ物みたいなものです。
こんな感じに。

module Greetable
  def say_morning
    "Good morning!"
  end

  def say_evening
    "Good evening!"
  end
end

どうやって使うかというと、includeを使ってクラスの中に埋め込みます。

class Sano
include Greetable
end

これで根暗なさのも挨拶ができるようになります。

sano = Sano.new
sano.say_morning
=> "Good morning!"
sano.say_evening
=> "Good evening!"

Moduleは使いまわせますので、同じメソッドを複数個所に記述する必要がまったく無くなる優れものです。
この機能はMix-inと呼ばれ、Rubyの特徴のひとつです。
さらに、同一クラスにModuleを複数Mix-inすることもできます。

module Refusable
  def refuse
    "No thank you"
  end
end

class Sano
  include Greetable
  include Refusable
end

sano = Sano.new
sano.say_morning
=>"Good morning!"
sano.refuse
=>"No thank you"

これで気弱なさのも挨拶しつつ、先輩の無茶振りを拒否することができるようになりました。
だんだん成長していきますね。
ちなみにメソッド名がかぶった場合、あとからincludeしたものでオーバーライドされます。

ここで最初のコードを再度確認してみましょう。

Class.superClass
=>Module

本によると、

ClassModuleに下記のメソッドを追加したもの
[:allocate, :new, :superclass]

だそうです。
:methods()でClassで定義されているメソッドが分かるので、それで確認ができます。
:methods()は引数にfalseを渡すことで継承されているメソッドを除外して表示します。

Class.methods(fase)
[:allocate, :new, :superclass]

つまり、newできないだけでModuleとClassは同じものということになります。
ここからわくわくポイントですが、

ってことはMix-inって継承と一緒じゃん

って思いませんか?
だって、メソッドを再利用できるんですから。
そして、

複数Mix-inって多重継承じゃん

ってことになりますよね。
Rubyにも当然Classの継承はありますが、多重継承はできないようになっています。
なので私は混乱しました。
おかしいですよね、Classの多重継承は禁止なのに、その親であるModuleの複数Mix-inはOKなんて。

そもそもなんで多重継承できないの?

多重継承を禁止している理由は2つです。

・親クラスに同じメソッド名があった場合、どれをオーバーライドすればいいかわからなくなる
・ひし形継承時に同一の親のインスタンスが複数作られる

1個目はいいでしょう。
それぐらいなんとかなる気がしますが、親1のメソッドと親2のメソッドがかぶったらどっちが優先されるとか、確かに覚えたくないです。
2個目はなんでしょうか。
ひし形継承を図で説明すると以下のようになります。

f:id:taiki_sano:20160312123209p:plain

クラスのインスタンスを作成するとします。
インスタンス作成時に親クラスのインスタンスも作成されるので、親3は2個インスタンスが作成されてどちらを参照すればいいかわからないというわけです。
これは確かにだめそうですね。

複数Mix-inは大丈夫なの?

Mix-inでは後からincludeされたものでオーバーライドすると明確に決まっています。
だから1個目の問題は解決でしょう。
2個目の問題に関してもModuleはnewできないClassなので問題ないです。

大丈夫じゃん!複数Mix-in!

なるほどnewできなければ多重継承もOKなんですねぇ。  

newできないClassってJavaにもありませんでしたっけ?

そう、interfaceです。
そしてそして、そういえばJava8からデフォルト実装といかいう謎の機能ありましたよね。

デフォルト実装が何かというと、interfaceに最初から中身のあるメソッドを記述することができる機能です。
つまり、以下のように書くことで

public interface GreetingInterface{
  // デフォルト実装
  default String sayMorning(){
    return "Good morning!";
  }
  default String sayEvening(){
    return "Good evening!";
  }
}
public interface RefuseInterface{
    default String refuse(){
      return "No thank you";
    }
}
class Sano implements GreentingInterface, RefuseInterface{
}

class Main{
  public static void main(String[] args){
    System.out.println(new Sano.sayMorning());
    // => Good morning!
    System.out.println(new Sano.refuse());
    // => No thank you
  }
}
一緒や!Mix-inと!

まてまて落ち着け、interfaceは型も継承するやん、Moduleは型継承なんて。。。

はっ!Rubyには型がねえ!

誤解があるといけないのできっちり書くと、Rubyの変数の型は実行時に決定されるのでコーディング時に意識する必要が無いのです。

結論:デフォルト実装=Mix-in

つながりましたね。
Ruby、噛めば噛むほど面白いですよー。

Spring SecurityでAuthenticationSuccessHandlerが動かない件

さのです。意外とブログ続いてて自分でもびっくりです。
今日はSpring Frameworkシリーズ第2弾。
Spring SecurityでAuthnticationSuccessHandlerが動かない件について。

ログイン成功時になんやかんやの処理をしたくてAuthenticationSuccessHandlerを追加したんですが、動きませんでした。

@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    SuccessHandler successHandler;
    @Autowired
    FailureHandler failureHandler;
    
    @Bean
    SuccessHandler successHander() {
        return new SuccessHandler();
    }
    @Bean
    FailureHandler failureHandler() {
        return new FailureHandler();
    }

    @Override
    public void configure(WebSecurity web) throws Exception{
        web.ignoring().antMatchers("/js/**", "/css/**","/img/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
            .antMatchers("/loginForm").permitAll()
            .anyRequest().authenticated();

        http.formLogin()
            .loginProcessingUrl("/login")
            .loginPage("/loginForm")
            .successHandler(successHandler)
            .failureHandler(failureHandler)
            .defaultSuccessUrl("contents/index")
            .usernameParameter("mailaddress").passwordParameter("password")
            .and();

        http.logout()
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout**"))
            .logoutSuccessUrl("/loginForm");
    }

    public class SuccessHandler implements AuthenticationSuccessHandler {
        @Override
        public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse res, Authentication auth) throws IOException, ServletException {
            // ログイン成功時のなんやかんやの処理
        }
    }
    
    public class FailureHandler implements AuthenticationFailureHandler {
        @Override
     public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse res, AuthenticationException ex)throws IOException, ServletException {
            // ログイン失敗時のなんやかんやの処理
        }
    }
}

ログイン失敗時のAuthenticationFailureHandlerは動いているので書き方は間違っていないはず。
ということはなんかの処理と競合しててそっちが呼ばれてるのかなという読み。
怪しいのはdefaultSuccessUrlあたりでしょうか。 コメント化してみました。

       http.formLogin()
            .loginProcessingUrl("/login")
            .loginPage("/loginForm")
            .successHandler(successHandler)
            .failureHandler(failureHandler)
//         .defaultSuccessUrl("contents/index")
            .usernameParameter("mailaddress").passwordParameter("password")
            .and();

どうやら正解だったようでSuccessHandllerが呼ばれるようになりました。
しかし、今度はログイン後にindexに飛ばなくなるので、SuccessHandlerからindexにリダイレクトするようにします。

   public class SuccessHandler implements AuthenticationSuccessHandler {
        @Override
        public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse res, Authentication auth) throws IOException, ServletException {
            // ログイン成功時のなんやかんやの処理

       // リダイレクト先を設定
       res.redirect("contents/index.html", false);
        }
    }

これでindexに飛ぶようになりました。 めでたしめでたし。

@OneToManyで相互参照したEntityをThymeleafを使ってJavaScript内で呼び出すとStackOverFlowする件

こんにちはさのです。
今回業務で4つのSpringFrameworkに触れることができました。

Spring Boot
Spring Security
Spring Data Jpa (hibernate)
Spring MVC

まだまだ「理解した!」とは言い難いですが、せっかく使ったんでばんばん書いていこうと思います。
いきなり濃いめですが今回はJpaの@OneToManyで相互参照したEntityにJavaScript内からThymelafを使ってアクセスしたら無限ループが発生してStackOverFlowしてしまった件についてです。

Entityクラスは以下のとおりです。

会社Entityクラス

@Entity
public class Company {

    @Id
    private Integer id;

    private String name;

    @OneToMany
    @JoinColumn(name="companyId", referencedColumnName="id")
    private List<Employee> employees;

    // setter getter constructorは省略
}

社員Entityクラス

@Entity
public class Employee {

    @Id
    private id;

    private companyId;

    private name;

    @ManyToOne
    @JoinColumn(name="companyId")
    private Company company;

    // getter setter constructorは省略
}

2つのEntityクラスは1対多の関係で相互にgetterで参照することができます。
これをモデルにつっこんで、Thymeleafを使ってHTML内に挿入します。

<html>
  <script th:inline="javascript">
    
    $(function(){
          /* エラーが発生する */
          var company = /*[[${company}]]*/ null;
      });

  </script>
  <body>
        /* script外から参照する場合はエラーは発生しない */
        <div th:text="${company.name}"></div>
        /* 以下略 */

${company}はThymeleafの記法で、Model内の"company"と名付けられた要素を参照します。
しかし、今回は以下のエラーが発生しました。

java.lang.StackOverflowError: null
    at java.beans.WeakIdentityMap.get(WeakIdentityMap.java:56)
    at java.beans.ThreadGroupContext.getContext(ThreadGroupContext.java:57)
    at java.beans.Introspector.getBeanInfo(Introspector.java:167)
    at org.thymeleaf.util.JavaScriptUtils.printObject(JavaScriptUtils.java:354)
    at org.thymeleaf.util.JavaScriptUtils.print(JavaScriptUtils.java:184)
    at org.thymeleaf.util.JavaScriptUtils.printKeyValue(JavaScriptUtils.java:347)
    at org.thymeleaf.util.JavaScriptUtils.printMap(JavaScriptUtils.java:338)
    at org.thymeleaf.util.JavaScriptUtils.printObject(JavaScriptUtils.java:366)
    at org.thymeleaf.util.JavaScriptUtils.print(JavaScriptUtils.java:184)
    at org.thymeleaf.util.JavaScriptUtils.printKeyValue(JavaScriptUtils.java:347)
    at org.thymeleaf.util.JavaScriptUtils.printMap(JavaScriptUtils.java:338)
    at org.thymeleaf.util.JavaScriptUtils.printObject(JavaScriptUtils.java:366)
    at org.thymeleaf.util.JavaScriptUtils.print(JavaScriptUtils.java:184)
    at org.thymeleaf.util.JavaScriptUtils.printCollection(JavaScriptUtils.java:323)
    at org.thymeleaf.util.JavaScriptUtils.print(JavaScriptUtils.java:173)
    at org.thymeleaf.util.JavaScriptUtils.printKeyValue(JavaScriptUtils.java:347)
    at org.thymeleaf.util.JavaScriptUtils.printMap(JavaScriptUtils.java:338)
    at org.thymeleaf.util.JavaScriptUtils.printObject(JavaScriptUtils.java:366)
    at org.thymeleaf.util.JavaScriptUtils.print(JavaScriptUtils.java:184)
    at org.thymeleaf.util.JavaScriptUtils.printKeyValue(JavaScriptUtils.java:347)
    at org.thymeleaf.util.JavaScriptUtils.printMap(JavaScriptUtils.java:338)
    at org.thymeleaf.util.JavaScriptUtils.printObject(JavaScriptUtils.java:366)
=================以下ループ===================

これは${company}で参照する際、すべてのgetterメソッドを呼び出す仕様のためです。
CompanyクラスのgetEmployees()を呼び、更にEmployeeクラスがgetCompany()を呼び、またそのCompanyが・・・と、無限ループしてしまいます。
対策は相互参照をやめるかThymeleaf用にオブジェクトを作るしかないかと思われます。

ちなみに、script外では問題なく呼び出せます。
script内では参照されたオブジェクトが操作されるため、先にオブジェクト内部をすべて把握しておく必要があるのでしょう。
そう考えると改善は難しいかもしれません。

一意でランダムな6桁の数字

さのです。
メガネとカバンかってウキウキです。
ブログは少しサボってたので、少しがんばって更新します。
今回は新規登録したユーザーに一意なIDを振りたいと思います。

IDの条件
・6桁の数字(左0埋め)
・登録順がわからないようにランダムに振る
・一度振った番号は使わない
という感じです。DBはPostgreSQLを使います。
 

いろんなID採番方法

Javaにはランダムな文字列を作成するためのユーティリティーが沢山あります。 有名なのはこのあたりでしょうか。

クラス メソッド
UUID (Java Platform SE 8) randomUUIDメソッド
Math (Java Platform SE 8) randomメソッド
RandomStringUtils (Apache Commons Lang 3.5-SNAPSHOT API) randomNumericメソッド

この中ではRandomStringUtilsが数値のみで桁数指定できるので条件と合致しますが、そもそも6桁では100万通りしかないため、すぐに重複してしまいます。
すぐに思いつくのはDBを検索して同じ会員番号が無いことを確認し、重複があれば作り直すという処理にすることです。
しかし、それでは件数が増えるにつれ重複する確率が上がり、IDを生成する回数もDBにアクセスする回数も増えてしまいます。

どうせDBにアクセスするのであればDBで採番しましょう。
 

採番テーブルを利用する

まず、6桁のIDだけ持つテーブルを作成し、000000〜999999までのデータをINSERTします。

CREATE TABLE numbering(
id VARCHAR(6) NOT NULL
);

INSERT INTO numbering('000000');
・・・
INSERT INTO numbering('999999');

PostgreSqlにはRANDOMという関数が用意されており、うまく使うことでデータをランダムに取得することができます。

SELECT * FROM numbering ORDER BY RANDOM() LIMIT 1;

各データにランダムな数値を付加してその順番で並び替えを行い、先頭のデータを取得しています。
※LIMITの件数を変えることで好きな件数をランダムで取得することができます。

最後に取得したデータを削除して完了です。
この方法の利点は、未使用のIDの中から選択するので同じIDを取得してしまうことが無いところです。
また、避けたい番号(444444など)の制御もDBに登録しなければいいだけなので簡単にできます。
論理削除を活用すれば退会ユーザーのIDの再利用もできます。
 

注意

どちらの場合でも同様ですが、同時アクセスされると重複した番号を採番してしまう可能性があります。
最終的に登録するテーブルのIDにUNIQUE制約をかけて、同じIDが登録されたら例外エラーを発生させ採番し直すような処理にしたほうがいいでしょう。

Hello Swift!

こんにちは、さのです。
Mac Book Air買って口座の数字が恐ろしいことになりました。
しかし、キーも軽いし、動作も軽いし、本体も軽いし、いい感じです。
良いことづくし(値段以外)ですが、コーディングせずには評価できません。

せっかくMacなのでXcode触ってみましょう。

Create Project

Xcodeを起動したらCreate a new Xcode projectを選択。
すると今度はProjectのtemplateを聞かれるので iOSOS Xか選びます。 iOSならiOSシミュレーターで動かしてくれるらしいので今回はiOSのSingleViewApplicationを選択。
次にProjectの情報を入力しますが、Languageという項目があり、Objective-CSwiftか選べます。
実はMac購入の理由の一つは「Swift触ってみたい!」だったのでSwiftを選択します。
最後にディレクトリを選択してCreateします。
作成されたプロジェクトの中身は以下の通りです。

.
├── helloworld
│   ├── AppDelegate.swift
│   ├── Base.lproj
│   │   ├── LaunchScreen.xib
│   │   └── Main.storyboard
│   ├── Images.xcassets
│   │   └── AppIcon.appiconset
│   │       └── Contents.json
│   ├── Info.plist
│   └── ViewController.swift
├── helloworld.xcodeproj
│   ├── project.pbxproj
│   ├── project.xcworkspace
│   │   ├── contents.xcworkspacedata
│   │   └── xcuserdata
│   │       └── sano.xcuserdatad
│   │           └── UserInterfaceState.xcuserstate
│   └── xcuserdata
│       └── sano.xcuserdatad
│           └── xcschemes
│               ├── helloworld.xcscheme
│               └── xcschememanagement.plist
└── helloworldTests
    ├── Info.plist
    └── helloworldTests.swift

いくつか.swiftファイルができてて感動。
info.plistはprojectの設定ファイルかな。

Hello Swift

さて作成されたファイルのうち、Main.storyboadをクリックするとview画面が開きます。
f:id:taiki_sano:20150714224236p:plain
画面右下のLabelをView上にドラッグするだけで配置できるようです。
VisualStudioみたいですね。

これで完成ですが、予想外に何もコーディングしてませんのでコンソールにも出力してみます。
ViewControllerクラスのviewDidLoad()メソッドインスタンス化された直後に呼ばれるようなのでそこでHelloSwiftを出力させてみましょう。

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        println("Hello Swift!");
    }
}

import UIKitはiOS用のライブラリ(OSX用はcocoa)
  
では実行してみましょう。 まずはコンソールから。
f:id:taiki_sano:20150714225106p:plain

右下に小さくHello Swift!と表示されているのがわかります。(小っさ)
  
次にお待ちかねiOSシミュレータ。
f:id:taiki_sano:20150714225102p:plain 中央にHelloSwif!が表示されました。
かなり簡潔かつ、直感的に使えそうな感じですね。

会議は2人3脚

こんにちはさのです。
まぁまぁ仕事が忙しくてブログのこと忘れておりました。
せっかく忙しいので、忙しくなった原因と対策を考えてみます。
(今回文字ばっかりでとても読みづらいです、すみません。)

現在とあるアプリの要件定義をしております。
当然クライアントと会議をして要件を定めていくわけですが、これがえらい時間がかかっております。

会議の流れ

・画面のモックアップを作成
・一日前にクライアントへモックアップ含む資料を送付
・画面順に処理を説明
・逐一質問や要望を聞き、検討議論

さて既に問題点がいくつか見えているような気もしますが、
会議の中で問題が浮き彫りになったやり取りがあったのでそれもピックアップします。

時間を浪費したやり取り

・クライアント同士の議論
クライアント同士でどういう機能が必要かという議論がされておらず、時間がかかってしまった。

・似たような機能に対する繰り返しの議論
例えば検索の方法など項目の違いはあるにせよ、基本的には同じ動作をする箇所で 同じような説明をし、時間がかかってしまった。

・一度決めたことが覆る
こうしようと決めたのに、後でやっぱり不可能という結論になってしまった。

・クライアントが必要としていない機能の提案
このままじゃ使いづらいと思い、新機能を提案したがクライアントはそこまでする必要はないと考えていた。

・きわどい機能の議論
あれば便利だが、そこまでする必要があるのかという機能について不要とする決定打がなく、 議論が長引いた挙句結論が出ていない。

こうやって見るとてんでダメダメですね。
先輩方に頂いたアドバイスをもとに考えた解決策をまとめます。  

会議を長引かせないための10の約束

1.資料提出は早めに
資料を早く提出することでクライアント同士の議論を先にしてもらいます。何日前に出せばいいのかはクライアントに直接聞くのがいいと思います。 議論しておいてくれと伝えるのも忘れないようにします。

2.似たような機能は似たような画面(設計)を
ユーザーのことを考えても似た機能は似た画面のほうが操作がわかりやすいです。 どうしても画面や設計が少し変わってしまう場合は、資料に注釈をつけておくことで同じような説明を省略します。

3.機能ごとにまとめておく
画面順に説明すると何故かはるか先の機能の質問などが飛んできました。機能ごとにまとめて説明し全体のどの部分の議論をしているか明確にすることで、議論があっちこっち飛ぶのを防ぎます。

4.細かく時間を決めておく
どの機能にどれぐらい時間をかけるか決めて、事前にクライアントと共有します。 お互いが次の会議で何を重要視しているか明確にするのが目的です。 クライアントが重要だと思っているのに自分が軽く見ていた機能があって、準備できていない状態で議論する羽目になります。

5.説明する順番は軽い項目から
一概に軽い順が良いとは言えませんが、基本的に軽いものは予定より早く終わるため、重要なものに使える時間を増やせます。

6.想定外の提案は持ち帰る
即答できるものはいいですが、その場で議論するとどうしても見落としが出てしまいます。せっかくまとまったのに実はできませんでしたということがないよう、なるべく持ち帰るようにします。

7.アプリの役割を明確にする
便利な機能はいくらでも思いつきます。
役割を明確にすることで、ガンガン機能を追加して際限なくアプリが巨大化していくのを防ぎます。 ユーザーの絞込みをしてから考えると間違えづらくなると思います。

8.期限、予算を明確にする
予算や期限を超えるような機能は最初から議論する価値はありません。

9.きわどい機能は実際に使ってみてからでも遅くない
それでもいるのかいらないのか当落選上の機能はたくさん出てくると思います。 以下の方法を試してみます。
・実際のデータを集め、判断材料にする。 ・実際使ってみてから必要であれば実装という方法をとる。

10.会議を想像する
会議に限らずですが、ある程度、質問や議論になりそうな箇所を想定して回答や落とし所を考えておきます。 そうすることで議論の時間をコントロールします。

まとめ

会議はクライアントとの2人3脚だなぁと感じました。会議の前から細かく意思の疎通ができていないと全然進みません。 うざいぐらいに電話して次回以降の会議はスムーズに進めたいです。

最後に、アドバイスくださった先輩方ありがとうございました。

$.ajax()が動かない!

こんにちは、さのです。

先輩からアドバイスいただきまして、軽い内容のものも挟んでいこうと思います。 昨夜、JQueryajax()が思い通りの動きをしなかったのでその件について。

 
やりたかったこと
setInteval()メソッドを使用して、1分おきにサーバーのデータにアクセス。
データに変更があればsubmitする。

そのコードが以下。

var type;

$(function(){
    type = /*[[${type}]]*/ null;
    setInterval("check", 60000);
});

function check(){
    $.ajax({url:"/check",
            type:"GET",
            dataType:"text",
            success: function(data){
            if(data != type){
                $("#form").submit();
            }
        }
    });
}

Thymeleafというテンプレートエンジンを利用して、画面ロード時にtypeを取得。
$.ajax()メソッドで取得したdataがtypeと異なればsubmitする。

ちなみにサーバー側は以下。

@Controller
@RequestMapping("/")
public class Controller {

    @RequestMapping(value = "check",  method = RequestMethod.GET)
    @ResponseBody
    private String check(){
     String type;
     //判定処理
         return type; 
    }
}

Chromeではうまくいきましたが、Android端末だと1回変更を認識したきりそれっきり。 どうやらキャッシュを取っていてサーバーと通信せずに値を勝手に返していたようです。
まったく、さぼらんといてほしいですね。

キャッシュを取らないように設定し無事解決。

function check(){
    $.ajax({url:"/check",
            type:"GET",
            dataType:"text",
            cache:false,
            success: function(data){
            if(data != type){
                $("#form").submit();
            }
        }
    });
}