Spring Bootのサービス稼働環境
弊社では食品の検索に対してXMLで結果を返すRESTfulのAPIを運営しているのですが、今回、機能追加やJSONでの返却を可能にするバージョンアップを計画しています。
今まではJSP+Servletでの実装だったんですが、今回はSpring Bootで実装してみました。 Spring BootではFully Executable JARで簡単にサービス稼働環境を作ることができたので、ここで紹介します。
Fully Executable JARとは
Spring Bootでは、作成したJARファイルをjava -jar
で起動することができますが、UNIX環境でより簡単に起動することができる仕組みです。
詳しくは
Spring Boot Reference Guide
をどうぞ。
JARファイルの作成
APIのプロジェクトはmavenで構成管理をしているのですが、mavenのpluginで簡単にFully Executable JARを作ることができました。
まず、pom.xmlのspring-boot-maven-plugin定義に<executable>true</executable>
を追加します。
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> + <configuration> + <executable>true</executable> + </configuration> </plugin>
次に、maven package
でビルドします。
$ mvn package [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building XXXXXXXX [INFO] ------------------------------------------------------------------------ ... [INFO] --- maven-jar-plugin:3.0.2:jar (default-jar) @ xxxx --- [INFO] Building jar: /xxx/xxx/target/XXXXXXXX.jar [INFO] [INFO] --- spring-boot-maven-plugin:2.0.3.RELEASE:repackage (default) @ xxxx --- [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 10.709 s [INFO] Finished at: 2018-11-09T13:03:46+09:00 [INFO] Final Memory: 42M/290M [INFO] ------------------------------------------------------------------------ $
という流れで、targetフォルダ下にJARファイルが作成されます。
起動のしかた
Fully Executable JARができると、起動はそのJARファイル自身を実行するだけです。例えば、JARファイルをapp.jarで作ったとすると、
$ cd target/ $ ./app.jar . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.0.3.RELEASE) 2018-11-09 13:51:58.709 INFO 11707 --- [ main] xxx.Application : Starting Application v2.0 on xxx with PID 11707 (app.jar started by xxx in /xxx/xxx/xxx/target) 2018-11-09 13:51:58.721 INFO 11707 --- [ main] xxx.Application : No active profile set, falling back to default profiles: default 2018-11-09 13:51:58.886 INFO 11707 --- [ main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@12f40c25: startup date [Fri Nov 09 13:51:58 JST 2018]; root of context hierarchy
という感じで、簡単に起動できます。
全てのライブラリはJARファイル内に含まれていますので、JARファイルを好きなところに配置して起動することができます。 先ほどの公式ドキュメントでは、init.dやsystemdのサービスとして起動する方法も書いてありました。
設定のしかた
Fully Executable JARでは、JARファイルと同じフォルダに、JARと同じ名前の.confファイルを作ることで、起動時の設定を行うことができます。
今回、Spring Bootを使うにあたり、JSP+Servletのシステムと異なるバージョンのJAVAを使用します。 また、アプリケーションサーバーのポート番号や、環境に応じたプロファイルの選択をしたいと思っています。
そのため、app.jarと同じフォルダに、以下の内容のapp.confというファイルを作成しました。
export JAVA_HOME=/usr/java/jre1.8.0_192 export JAVA_OPTS="-Dserver.port=8080 -Dlog4j.configuration=log4j-production.properties" export SPRING_PROFILES_ACTIVE=production export MODE="service"
JREは、既存の環境の/usr/javaフォルダ配下に展開だけして、デフォルトでの使用はしないようにしていますが、app.jarの起動時にJAVA_HOMEを指定して使用しています。
アプリケーションサーバーのポートやlog4jのプロパティは、JAVA起動オプションで指定して、Springのプロファイルも環境変数で与えています。
MODEはserviceを指定することで、start,stop等を使い、デーモンとして起動できます。
起動用の様々なオプションはこちら Spring Boot Reference Guide
Spring Bootの様々なオプションはこちら Appendix A. Common application properties
を参考にして下さい。
なぜ動くの?
なぜJARファイル自身を実行して、このようなことができるのでしょうか?
少し調べてみたところ、JARファイル自身の先頭に細工があるようです。
JARファイルの中身を見てみると
$ less app.jar #!/bin/bash # # . ____ _ __ _ _ # /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ # ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ # \\/ ___)| |_)| | | | | || (_| | ) ) ) ) # ' |____| .__|_| |_|_| |_\__, | / / / / # =========|_|==============|___/=/_/_/_/ # :: Spring Boot Startup Script :: # ### BEGIN INIT INFO # Provides: caloriecheck-api # Required-Start: $remote_fs $syslog $network # Required-Stop: $remote_fs $syslog $network # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: XXXXXXXX # Description: Parent pom providing dependency and plugin management for applications built with Maven # chkconfig: 2345 99 01 ### END INIT INFO [[ -n "$DEBUG" ]] && set -x # Initialize variables that cannot be provided by a .conf file WORKING_DIR="$(pwd)" # shellcheck disable=SC2153 [[ -n "$JARFILE" ]] && jarfile="$JARFILE" [[ -n "$APP_NAME" ]] && identity="$APP_NAME" ...
となっており、JARファイルの先頭がサービス起動のシェルスクリプトになっています。
そういえば、JARパッケージ時にspring-boot-maven-plugin:2.0.3.RELEASE:repackage
と、何かの処理をされていたようです。
JARファイルというのは、実体はZIPファイル(拡張子を.zipに変更するとunzipできる)なのですが、こちらのブログ( Spring Boot の Fully Executable Jar はなぜ動くのか - RAMBO )によると、ZIPの解凍はファイルの後ろ側から処理をしていくために、先頭にスクリプトを載せても動くようです。
面白いことをするなあと感心しました。
Javaの単体テストJunit入門
こんにちは、EatSmartの新人エンジニアです。弊社のサービスの一つであるカロリーチェックAPIの開発を行った際にJavaの単体テストを行ったのでその流れを振り返ってみたいと思います。
JUnitとは
ユニットテストを書くことで正しく動作しているかを検証することができます
気を付けたいポイント
- 「前提条件、実行、検証」がわかりやすいコードを書く
- ユニットテストし易いように対象のクラスを作る
- 想定されうるケースをできるだけ作る
- プログラム単体の振る舞いを確認する
ex) 引数のデータの組み合わせが正しいか、例外が発生した際の振る舞いが正しいか
今回の開発環境
開発環境 | |
---|---|
サーバー | Tomcat |
IDE | Eclipse |
標準API | JavaEE |
フレームワーク | Spring boot |
テスト | Junit 4 |
ビルドツール | Apache Maven |
利用手順(Eclipseで利用の場合)
- サンプルプログラムを作成する
- テスト用のソース・フォルダーを作成する
- Junitへのビルドパスを追加する
- テストクラスを配置する
- テストを実行する
テスト作成ルール
JUnitで利用するアノテーション
アノテーション | 機能 |
---|---|
@Before | 全てのテスト実行前に行われる処理 |
@After | 全てのテスト後に行われる処理 |
@BeforeClass | テストメソッド実行前に一度だけ行われる処理 |
@AfterClass | テストメソッド実行後に一度だけ行われる処理 |
@Test | テストメソッドを知らせる |
今回使用したアノテーション
アノテーション | 機能 |
---|---|
@JsonIgnore | フィールドが変換対象外になる |
@JsonProperty | Jsonのプロパティ名を変更する |
@JsonInclude | 値がnullの場合は出力しない |
サンプルコード
テスト対象のSample.javaは次の通りです。
<使用したアノテーションの設定>
import java.math.BigDecimal; import java.util.List; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; public class Sample { @JsonInclude(JsonInclude.Include.NON_NULL) private BigDecimal rowNumber; private String foodName; @JsonIgnore private BigDecimal standardAmount; private String standardAmountUnit; @JsonProperty(value="contentsAmount") @JacksonXmlProperty(localName="contentsAmount") public String getContentsAmountStr() { if (contentsAmount == null) { return "0"; } return FormatUtil.format(contentsAmount, 2, "#########");
上のサンプルに対するJUnitは次のようになります
- テストメソッドはassertTrueで設定
- テストに成功すればグリーンで返ってくる
import static org.junit.Assert.*; import java.util.HashMap; import java.util.Map; import org.junit.Before; import org.junit.Test; public class SampleTest { @Before public void setUp() throws Exception { TestUtil.initialize(); } @Test public void testXml_OK() throws Exception { //結果取得 SamplelBean bean = new SampleBean(); bean.setFoodCode("00"); bean.setNutritionProvideKbn("01"); SampleBean ret = new SamplelAction().xml(bean); //パラメーター設定 Mapparam = new HashMap (); param.put("foodCode",bean.getFoodCode()); param.put("nutritionProvideKbn",bean.getNutritionProvideKbn()); //検証 assertTrue(TestUtil.validate(ret,"/calorie/detail",param)); }
Google Map APIについて
クスパでは料理教室の所在地をGoogle Map APIを利用して表示していますが、2018年6月にGoogle Map APIのサービスが見直され、Google Maps Platformというサービスに変更されました。その際に調査したことなどを記載したいと思います。
Google Maps Platformの変更点
Google Maps Platformの大きな変更点としては、API等を利用する場合、利用アカウントに(クレジット カードの)決済情報を設定しないと利用できない点です。
GoogleMapPlatform 既存ユーザー用ガイド
User Guide | Google Maps Platform | Google Cloud
Google Maps Platform利用までの流れ
手順としては、「APIキーの取得」「APIの設定と有効化」があり、利用までの大まかな流れを記載します。
APIキーの取得
Googleアカウントにログインした状態で、Google Maps Platformにアクセス https://cloud.google.com/maps-platform/
マップの利用する製品(マップ/ルート/プレイス)を選択
プロジェクトを作成
請求情報の登録
APIキーの発行
APIの設定と有効化
(上記で作成した)プロジェクトを選択
「Maps JavaScript API」を有効にする※「Maps JavaScript API」利用の場合
利用するAPIの追加
認証情報の設定
Google Maps Platformの料金体系について
今回のリニューアルにより、大幅に料金体系が見直されました。 変更点としては、いままでは「個別のAPIごとに」無償枠があり超過分だけを支払う形態でしたが、 Google Maps Platformでは「Google Maps API全体での無償枠」を超えた分だけ支払う形に変更されました。 Google Maps Platformの無償枠は毎月$200となり、(2018/10/26に調査した時点の)各APIの無償枠内での利用数は以下の通りです。
マップ(Maps)
API | 月額無償枠(200$) |
---|---|
Mobile Native Static Maps | 無制限のロード |
Mobile Native Dynamic Maps | 無制限のロード |
Embed | 無制限のロード |
Embed Advanced | 最大14,000のロード |
Static Maps | 最大100,000のロード |
Dynamic Maps | 最大28,000のロード |
Static Street View | 最大28,000のパノラマ |
Dynamic Street View | 最大14,000のパノラマ |
ルート(Routes)
API | 月額無償枠(200$) |
---|---|
Directions | 最大40,000件のコール |
Directions Advanced | 最大20,000件のコール |
Distance Matrix | 最大40,000の要素 |
Distance Matrix Advanced | 最大20,000の要素 |
Roads - Route Traveled | 最大40,000件のコール |
Roads - Nearest Road | 最大40,000件のコール |
プレイス(Places)
API | 月額無償枠(200$) |
---|---|
Autocomplete - Per Character | 最大70,000文字 |
Autocomplete without Places Details - Per Session | 最大11,000セッション |
Autocomplete(included with Places Details) - Per Session | 無制限のコール |
Query Autocomplete – Per Character | 最大70,000文字 |
Places Details - Basic | 最大11,000件コール |
Places Details - Basic + Contact | 最大10,000件コール |
Places Details - Basic + Atmosphere | 最大9,000件コール |
Places Details - Basic + Contact + Atmosphere | 最大8,000件コール |
Places Photo | 最大28,000件コール |
Find Place - Basic | 最大11,000件コール |
Find Place - Basic + Contact | 最大10,000件コール |
Find Place - Basic + Atmosphere | 最大9,000件コール |
Find Place - Basic + Contact + Atmosphere | 最大8,000件コール |
Places - Nearby Search - Basic + Contact + Atmosphere | 最大5,000件コール |
Places - Text Search - Basic + Contact + Atmosphere | 最大5,000件コール |
Geocoding | 最大40,000件コール |
Geolocation | 最大40,000件コール |
Time Zone | 最大40,000件コール |
Elevation | 最大40,000件コール |
今後の対応
クスパでは、Static Maps(静的地図の表示)/Geocoding(住所から緯度経度を検索)をメインに利用しており、現状は無償枠内で利用しておりますが、今後のトラフィック増に伴い月額利用料が増額する可能性がある為、下記対応も検討していきたいと思います。
Static Mapsから地図埋め込みによるロード数削減
無償の地図API(Leaflet等)の利用
Javaの開発環境を構築する(Windows編)
イートスマートの新人エンジニアが、今回はJavaの開発環境の構築について、業務を通して行ったので 流れを簡単に振り返ってみたいと思います。以下の書籍を参考にしました。
- 作者: 中山清喬,国本大悟
- 出版社/メーカー: インプレス
- 発売日: 2014/08/07
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (19件) を見る
Javaで開発するために必要なもの
❶JDK
JDK とは、Javaのアプリケーション開発に必要なソフトウェアのキット(コンパイラ、デバッガなどが揃っている)です。現在は、Oracleがより多くの開発者に使ってもらうためにJDKの配布、アップデートを繰り返してしています。JDKと対比されるJREは、Javaの実行環境のみであり、簡単に言うとJDK=Javaの開発、JRE=Javaの実行のイメージになります。
❷Eclipse
Javaの開発に必要な統合開発環境(エディタ、コンパイラ、インタプリタが揃ったもの)になります。Eclipseは、エディタで行われるような文字のカラーリング、キー入力の予測変換、デバッグの支援など多様な機能が搭載されています。Java以外のRubyやPythonといったプログラミング言語にも用いることができます。
今回の環境について
・Windows 10 (64bit版)
JDKの導入
JDKのダウンロード元:https://www.oracle.com/technetwork/java/index.html
上記のURLより、JDKをダウンロードします。ダウンロードページにて、Java SE8u~(アップデートのタイミングによって~は異なります。)と書かれたものを選択します。ライセンス契約に同意し、今回であれば環境がWindows64bit版なので、Windows x64と書かれたものを選択します。ダウンロード実行後、セットアップに移ります。
JDKのセットアップ
こちらは、画面の案内に従って進行していきます。注意点として、JDKのインストール先を覚えておく必要があります。
例) C:¥Program Files¥Java¥jdk1.8.0_181
ここまでは、指示通りに進めば問題がないかと思います。
設定で躓いたのが、環境変数の設定になります。
コントロールパネル>システム>システムのプロパティ>環境変数から以下のように設定をします。
<ユーザー環境変数>
変数名: JAVA_HOME
変数値: C:\Program Files (x64)\Java\jdk1.8.0_181
JAVA_HOMEの環境変数が既に<システム環境変数>の方にある場合には、そちらが優先して
読まれる場合があるので、参照元を確認し不要であれば削除します。
コマンドプロンプトで設定する場合には、
一旦閉じて、設定を反映させます。
再び、コマンドプロンプトを開き以下のコマンドが通れば
設定完了になります。
C:\Users\eatsmart>java -version
結果:java version "1.8.0_181"
C:\Users\eatsmart>javac -version
結果:javac 1.8.0_181
Eclipseの導入
続いて、Eclipseを導入していきます。今回、Eclipseに拡張機能を追加し、日本語化を行ったパッケージであるPleiadesをダウンロードして行いました。Pleiadesには、Eclipse本体に加えて、日本語化のプラグイン、JRE(Java実行環境)、その他便利なプラグインが含まれています。
Pleiadesのダウンロード:http://mergedoc.osdn.jp/
Eclipseの起動について
上記URLよりダウンロードしたzipファイルを開き、eclipseと書かれたフォルダの中のeclipse.exeを開きます。
ワークスペースのディレクトリがデフォルトで設定されているので、そのまま「OK」を押せばEclipseが立ち上がります。上記の流れでJavaの開発環境が整いました。
画面のカスタマイズなどもできますので、普段使っているエディタのように文字のカラーや背景色の設定を自由にできます。
参考URL: http://hatehate-masaki.hatenablog.com/entry/2017/01/18/235633
番外編~VSCodeを使ったJavaの環境構築
最近になって、VSCodeのJavaサポートが充実してきているようなので、Mac環境にてVSCodeを使って環境を構築してみました。
以下、参考にした記事になります。
簡単に流れをざっとご紹介しましたが、快適なJavaライフを送るために参考になればと思います。
DockerコンテナでJenkinsを稼働させる
今回は、イートスマートのサービスのビルド・デプロイに利用しているJenkinsについて書きます。 DockerコンテナでJenkinsを稼働させるための手順をまとめました。 現在はJenkins以外にも同様のツール・サービスが多数ありますが、手元のサーバ上で手軽に利用できるのがお大きな利点ではないかと思います。
Jenkinsについて
Jenkinsは、継続的インテグレーションを行うため に利用するツールです。 主な用途は以下となります。
アプリケーションのビルド
Javaやその他の言語で造られたアプリケーションのビルドを行います。 ビルドを行う手順はJenkins上に直接記述することもできますが、バージョン管理を行うためスクリプトにまとめておき、Jenkinsではそのスクリプトを実行するのみとしています。
バッチの実行
定期的に実行したいバッチを「ビルド・トリガ」の「定期的に実行」を利用して処理しています。 以前はCronを利用していましたが、Jenkinsへ移行することで
- スケジュールの管理
- 状況の把握
- 結果の把握
が容易になりました。 アプリケーションのビルドも同様ですが、「ビルド後の処理」へ失敗時にシステム部メンバーへメールで通知する設定を行っています。 また、手順もスクリプトにまとめてバージョン管理を行っています。
サービスの監視
インフラ・サービスの監視はZabbixを利用していますが、補助的にJenkinsでも行っています。 サービスごとに監視用のスクリプトを用意して、バッチと同様に失敗時に通知を行っています。
Dockerコンテナで稼働させる
今年の3月にインフラをデータセンターからさくらインターネットのクラウド・専用サーバへ移行しました。 この時、Jenkinsをはじめ上記のZabbixやタスク管理に利用するRedmine、バージョン管理に利用するsvn/gitbucket等も同様に移行しましたが、すべてDockerコンテナにしています。
Dockerイメージを作成する
以前の環境ではJava6を利用してビルドを行っていました。移行のタイミングでバージョンアップすると問題が発生した場合に切り分けが難しいので、Dockerコンテナも同じバージョンにしました。 公式のイメージもありますが、上記の理由で以前の環境を再現することにりました。
FROM java:6
Java6のイメージをベースにしました。
ADD jenkins.war /var/ ADD apache-ant-1.7.0-bin.tar.gz /var/ ENV PATH $PATH:/var/apache-ant-1.7.0/bin
JenkinsのwarとJavaアプリケーションのビルドに利用するAntのjarをコピーし、パスを通します。
RUN apt-get update \ && apt-get install -y zip \ && apt-get install -y rsync
デプロイに利用するコマンド(zip,rsync)をインストールします。
ENTRYPOINT ["java", "-Duser.timezone='Asia/Tokyo' -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 -Xmx1024m", "-jar", "/var/jenkins.war"]
Jenkinsを起動します。タイムゾーンやエンコードを指定してます。 また、大きめのアプリケーションのビルドとテストを行うので、メモリの指定も追加しています。
実際はサービスごとの細かな設定も追加していますが、これらの内容でイメージを作成しました。
Dockerコンテナを起動する
Dockerイメージができたら起動します。起動には、以下のようなオプションを指定しています。
--env="JENKINS_HOME=/var/jenkins/jenkins_home/" \ -v /var/jenkins/jenkins_home:/var/jenkins/jenkins_home \
Jenkinsのジョブやワークスペースの保存先をしています。 保存先をホストサーバとすると、バックアップ等扱いやすくなります。
--env="TMPDIR=/dev/shm" \
長期間かどうさせるとサーバのメモリが不足し、Jenkinsの動作が不安定になることがありました。 Zabbixで確認すると、アプリケーション自体が利用するメモリが増加するのではなく、「cached」や「slab_cache」とよばれるメモリが増加していました。 以下の記事を参考に、tmpfsを利用するための設定となります。
この設定を行うことで、最大5GB程度あったスワップが200MB程度に収まるようになりました。
-v /etc/localtime:/etc/localtime:ro \
ホストサーバと同じタイムゾーンで稼働させるための設定です。
-v /root/.ssh:/root/.ssh:ro \
デプロイsshを利用する際に、ホストのものを利用します。
アプリケーションのビルド
Antを利用するプロジェクトは、Dockerコンテナ内でビルドすることができるようになりました。 これ以外のMavenやGolang等のプロジェクトは、Jenkinsとは別のコンテナを利用しています。
- Mavenを利用するプロジェクト
Mavenコンテナを利用してビルドします。 /root/.m2ディレクトリの保存先をホストサーバすることで、ダウンロードにかかる時間を短縮できます。
docker run -i --rm --name maven -v <プロジェクトのルート>:/target -v /root/.m2:/root/.m2 -w /target maven:3.3.3-jdk-8 mvn package
- Golanのプロジェクト
プロジェクトのルート用意したビルド用のスクリプト(build.sh)を利用します。 ビルド用のスクリプトではgo get/go buildを行います。
docker run -v <プロジェクトのルート>:/go/src/project/ golang:latest \ sh /go/src/project/build.sh
まとめ
以上、イートスマートでのJenkinsの利用方法を書きました。 利用を開始してから6年近く経ちますが、ジョブの数・実行時間も増え、キューにジョブが溜まることも増えました。そのため、バッチの実行に関しては、別のツールへの移行を検討しているところです。
webpackを使ったVue.jsのモジュール開発
前回の続きで、今度はクライアント側でVue.jsを使い、前回作ったNode.jsのAPIからデータを取得して表示するページを作りたいと思います。
インストール
まず、プロジェクトのフォルダを作り、初期化します。
$ npm init
次に、webpack系のライブラリをインストールしました。
$ npm i -D webpack webpack-cli webpack-dev-server
その次に、Vue.jsをインストールしました。
$ npm i --save vue
その後、css系のライブラリと、js系のライブラリをインストールしました。 vue-loaderは最新版をインストールすると、
vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config.
というエラーが出て、こちらhttps://github.com/symfony/webpack-encore/issues/311を参考に、ver14を使うようにしました。
# vue $ npm i -D vue-loader@14 vue-template-compiler css-loader sass-loader # babel $ npm i -D babel-loader @babel/core @babel/preset-env babel-preset-es2015 # ajax通信用 $ npm i -D axios
設定
まず、開発用サーバーとビルドの設定をpackage.jsonに追加します。
"scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "dev": "webpack-dev-server --hot", + "build": "webpack -p" },
次に、webpack用の設定ファイル(webpack.config.js)を作成します。
module.exports = { // エントリポイントとなるファイル entry: './src/index.js', // ビルド先設定 output: { filename: 'bundle.js', path: `${__dirname}/dest`, }, module: { // ローダー設定 rules: [ { test: /\.vue$/, loader: 'vue-loader', }, { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader' } }, { test: /\.(css|sass|scss)$/, loader: 'sass-loader', }, ] }, resolve: { extensions: ['.js', '.vue'], alias: { vue$: 'vue/dist/vue.esm.js', }, }, // 開発サーバ設定 devServer: { contentBase: 'dest', }, }
実装
いよいよVue.jsでの実装ですが、まずエントリポイントとしてsrcフォルダのindex.jsを以下のようにしました。 componentsフォルダのTimeineの内容を#appに出力します。
import Vue from 'vue' import Timeline from './components/Timeline' new Vue({ el: '#app', components: { Timeline }, })
次に、src/componentsフォルダのTimeline.vueファイルに実体を実装します。
Vue.jsではHTMLのtemplateとjsの処理を同一ファイルに記述します。 jsの処理としては、APIサーバーからtimelineの一覧を取得し2件表示し、「もっと見る」で2件づつ追加するようにします。
- はじめにAPIサーバーから一覧を取得する
created:function(){ var items = this.items; axios.get(ENDPOINT_TIMELINE_LIST+'?name='+this.name) .then(function(res) { for(var i=0;i<res.data.length;i++){ items.push(res.data[i]); } }); },
- 「もっと見る」の処理
computed:{ showCount:function() { return this.first + this.count * this.step; }, hasMore:function() { return (this.showCount < this.items.length); } }, methods: { more:function() { this.step++; } } }
- 処理をバインドするテンプレート
<template> <div class="snsWrap"> <div class="feedBox" v-for="(item, index) in items"> <span v-show="index < showCount" v-html="item"></span> </div> <button v-if="hasMore" v-on:click="more">もっとみる</button> </div> </template>
確認
開発サーバーはnpm run devで起動できます。
$ npm run dev > timeline@1.0.0 dev /XXXXX/XXXX > webpack-dev-server --hot ℹ 「wds」: Project is running at http://localhost:8081/ ℹ 「wds」: webpack output is served from / ℹ 「wds」: Content not from webpack is served from dest ⚠ 「wdm」: Hash: 8fa2fd3d7a6fca1710e3 Version: webpack 4.20.2 Time: 1760ms Built at: 2018-10-06 17:20:11 Asset Size Chunks Chunk Names bundle.js 254 KiB 0 [emitted] [big] main Entrypoint main [big] = bundle.js ・・・ WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB). This can impact web performance. Assets: bundle.js (254 KiB) WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance. Entrypoints: main (254 KiB) bundle.js
と起動でき、ブラウザでhttp://localhost:8081/にアクセスする事で、動作の確認ができました。
ビルド
jsファイルのビルドは、npm run buildでできます。
$ npm run build > timeline@1.0.0 build /XXXXX/XXXX > webpack -p Hash: d77cd19e816f5c62ca5c Version: webpack 4.20.2 Time: 3393ms Built at: 2018-10-06 18:33:39 Asset Size Chunks Chunk Names bundle.js 104 KiB 0 [emitted] main Entrypoint main = bundle.js [1] (webpack)/buildin/global.js 509 bytes {0} [built] [31] ./src/index.js + 4 modules 5.5 KiB {0} [built] | ./src/index.js 129 bytes [built] | + 4 hidden modules + 30 hidden modules
となり、destフォルダにbundle.jsが出力されます。
最終的に、コンテンツのHTMLにbundle.jsを組み込む事で、以下のページができました。
まだ理解できていなくてオマジナイ的な所も多いですが、とりあえずシンプルな形での実装ができました!
RailsでMinitestを実行する
EatSmartの新人エンジニアです。現在、社内向けツールの開発をRuby on railsを用いて行っています。 今回、テストコードを書くにあたりMinitestを使用したので実装内容を振り返ってみいたいと思います。
テストに関する知見が浅いのでこちらの本を読みました。
- 作者: Kent Beck,和田卓人
- 出版社/メーカー: オーム社
- 発売日: 2017/10/14
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
簡単に「テスト駆動開発について」話します。
テスト駆動開発とは
・機能そのものが存在しない状態でテストだけを先に書きそのテストを通すことを目的に最小限の実装を行う
↓
・このサイクルを繰り返すことによりプログラムを作成する開発手法のこと
【3つのステップから構成される】
◆レッド
1 プロダクトコードを書く前にテストコードを書きそれが失敗することを確認する ・大切なことは実装コードが一切なにも書かれていなくてもとにかく先に書くこと
◆グリーン
2 テストに成功するようにテストコードを書く
・どんな手段であったとしてもとにかく成功させる
3 プログラムの振る舞いを変えないようにプロダクトコードの重複などを整理する ・意味のあるソースコードにもっていく
なぜテストコード書くのか?
◆コマンド一発で速く、かつ楽に動作確認ができる
◆今後、メンテナンスをすることになる他の開発者のために書く
・コードを書いた人の意図や頭のおく
◆将来、Railsやライブラリのアップデートするときのために書く
使用したライブラリ
gem 'minitest'
gem 'minitest-doc_reporter'
実際のコード
【コントローラーの単体テスト】
require'test_helper' #デフォルト設定 require 'minitest/autorun' #ファイルを実行するだけでテストが実行される class FoodcategoriesControllerTest < Minitest::Test def test_category_search0 c = FoodcategoriesController.new c.category_search0('01') xml_result = c.instance_variable_get('@xml_result') p xml_result status = c.instance_variable_get('@status') assert_equal(status, '0') largecategoryname = c.instance_variable_get('@largecategoryname') assert_equal(largecategoryname, 'お菓子') largecategorycode = c.instance_variable_get('@largecategorycode') assert_equal(largecategorycode, '01') end end
◆インスタンス変数を確認するメソッド
instance_variable_get
レシーバが持っているインスタンス変数の値を返す
def test_category_search0 c = FoodcategoriesController.new c.category_search0('01') xml_result = c.instance_variable_get('@xml_result') p xml_result largecategoryname = c.instance_variable_get('@largecategoryname') assert_equal(largecategoryname, 'お菓子') end
テスト実行コマンド
$rake test TEST=test/controllers/foodcategories_controller_test.rb
テスト結果は以下のように返ってきました
Minitest::Result list0 2 tests run in 10.833853009 seconds. Errors: 0 | Failures: 0 | Skips: 0