gulp: command not found エラー解決方法

久しぶりにgulpを触っていたところ、タイトルのエラーが発生しました。

調べたところhomebrewでnodeを入れ直せば治るっぽいので、
nodebrewを使って直そうと思います

パッケージでインストールしたnodeを削除

# nodeがhomebrewで入っているか確認
$ brew list
arp-scan        cocoapods       heroku-node     kubernetes-helm openssl         postgresql      readline
autoconf        heroku          icu4c           mysql@5.6       pkg-config      rbenv           ruby-build

# 入っていないので既存のnodeを削除してhomebrewで入れ直す
# インストーラーでインストールしたnode.jsがこれで削除されるらしい
$ lsbom -f -l -s -pf /var/db/receipts/org.nodejs.pkg.bom \
> | while read i; do
>   sudo rm /usr/local/${i}
> done
can't open /var/db/receipts/org.nodejs.pkg.bom: No such file or directory
**** Can't open /var/db/receipts/org.nodejs.pkg.bom.
MacBook-Pro-de-Masahiro:~ masahiro$ sudo rm -rf /usr/local/lib/node \
>      /usr/local/lib/node_modules \
>      /var/db/receipts/org.nodejs.*
Password:

# その他諸々
$ rm -r /usr/local/include/node/
$ sudo rm /usr/local/bin/node
$ sudo rm /usr/local/bin/iojs

# 削除できたかどうかを確認 → 無事削除できていた。
$ node -v
bash: /usr/local/bin/node: No such file or directory
<br>

nodebrewでnodeを入れる

# nodebrewをインストール
$ brew install nodebrew

# nodebrewをセットアップ
$ nodebrew setup
$ nodebrew install-binary v6.10.1

# インストール可能なバージョンを表示
$ nodebrew ls-remote
v0.0.1 v0.0.2 v0.0.3 v0.0.4 v0.0.5 v0.0.6

v0.1.0 v0.1.1 v0.1.2 v0.1.3 v0.1.4 v0.1.5 v0.1.6 v0.1.7
v0.1.8 v0.1.9 v0.1.10 v0.1.11 v0.1.12 v0.1.13 v0.1.14 v0.1.15
v0.1.16 v0.1.17 v0.1.18 v0.1.19 v0.1.20 v0.1.21 v0.1.22 v0.1.23
v0.1.24 v0.1.25 v0.1.26 v0.1.27 v0.1.28 v0.1.29 v0.1.30 v0.1.31
v0.1.32 v0.1.33 v0.1.90 v0.1.91 v0.1.92 v0.1.93 v0.1.94 v0.1.95
v0.1.96 v0.1.97 v0.1.98 v0.1.99 v0.1.100 v0.1.101 v0.1.102 v0.1.103
v0.1.104

# ver 10以上を入れる予定なので、v10.10.0をセレクト
$ nodebrew install-binary v10.10.0

# 使いたいバージョンの指定
$ nodebrew use v10.10.0

# 設定されているバージョンの確認
$ nodebrew ls
v6.10.1
v10.10.0

current: v10.10.0

# PATHを通す
$ echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.bash_profile
$ source ~/.bash_profile

# PATHが通っているか確認
$ node -v
v10.10.0

 

無事通りました!

gulp実行

 

# gulpが入っているか確認
$ gulp -v
bash: gulp: command not found

# gulpが入っていなかったのでインストール
$ npm i -g gulp
+ gulp@4.0.2
added 383 packages from 239 contributors in 22.896s

$ gulp -v
CLI version: 2.2.0
Local version: 4.0.2

 

もしかしたらgulpが入っていなかったから問題が起きていたのかもしれませんが、
ひとまず解決できました!

 

参考記事

gulpが(多分)インストールできたのに、gulp: command not foundと言われるのは何故でしょう…?

Node.js(io.js) & npmをアンインストールしてHomebrewとnvmで管理する

Mac, Homebrew] Node.jsのバージョン管理ツール、nodebrew導入手順

 

 

 

pymysql.err.IntegrityError 1452, ‘Cannot add or update a child row: a foreign key constraint fails

Flaskでalembicのファイルを作成するコマンドを入力したところエラーが発生しました。
このファイルを実行するとき、カラムのデータはnullになってしまうので
エラーになっているっぽいです

なので、

  • 該当テーブルのデータを削除してnullになった時エラーを発生させないようにして回避する
  • テーブルの情報をalembicで修正するときにデータをいれてやる

 

とかすれば治ります

ちなみに私はデータを削除して対応しました

 

 (pymysql.err.IntegrityError) (1452, 'Cannot add or update a child row: a foreign key constraint fails (`test`.`#sql-484f_1e`, CONSTRAINT `tinfo_watch_id_fk` FOREIGN KEY (`watch_id`) REFERENCES `watch` (`id`) ON DELETE CASCADE)') [SQL: 'ALTER TABLE tinfo ADD CONSTRAINT tinfo_watch_id_fk FOREIGN KEY(watch_id) REFERENCES watch (id) ON DELETE CASCADE']

 

参考記事

なし

 

Android TextViewで改行させずに文字を全て表示させない方法

Androidにちょこっとだけ慣れてきました

前はUnityとかも毛嫌いしていましたが、TextとDesighを使いこなすことによって
UIを作ることができるのは非常に便利なものだと思えてきました

さて、今回やりたかったことは

  • ListViewで1行の中にサムネイル(左側)とタイトル(中央)、番号(右)を表示したい
  • サムネイルは固定サイズ
  • タイトルは長さが1行で、長ければカット

以上3件を満たすことです

とりあえず、私がとった方法は、TextVIewにmaxLinesを1に設定して改行を防ぎ
途中からは見えないようにすることです

コードはこちら

  <LinearLayout
   ・・・>
        <!-- アイコン表示用ImageView -->
        <ImageView
   ・・・/>
        <!--  ここにmaxLinesを入れたらできました  -->
        <TextView   
            android:maxLines="1"/>
        <TextView
   ・・・/>
    </LinearLayout>

 

参考記事

Keeping Textview content on 1 line and prevent wrapping

Angular6 Universal実行してみたらエラーが発生した

Angular何年も使ってきて初めてサーバーサイドレンダリングをやりました
いうまでもなくエラーが結構出てきましたが、最後のやつだけ記録できればと思います

ターミナルで以下のコマンドを実行してブラウザで確認したところこのようなエラーが出てきました

$ npm run build:ssr && npm run serve:ssr

Node server listening on http://localhost:4000
Error: renderModule[Factory]() requires the use of BrowserModule.withServerTransition() to ensure
the server-rendered app can be properly bootstrapped into a client app.

 

調べてみたところBrowserModule.withServerTransition()が必要みたいなので、
app.module.tsに入れてやります

@NgModule({
    imports: [
        // ここから
        BrowserModule.withServerTransition({
            appId: 'my-app-id'
        }),
        // ここまで
        HttpClientModule,
        AppRoutingModule,
        FormsModule
    ],
    providers: [
        ContentService
    ],
    bootstrap: [AppComponent]
})
export class AppModule {
}

これで無事に動きました!

参考記事

Server side rendering using Angular4(Angular Universal) [closed]

 

Flaskでcorsの問題に対処する

Angularからflaskサーバーにアクセスしようとしたらcorsの問題が発生したので
対処しました

2019年4月時点でも問題なくできました

コードは以下の通りです

# まずはターミナルでインストール
$ pip install -U flask-cors

# flaskのメインファイル。appの次の行に追加すれば問題なく動きました。
from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

 

参考にした記事

Solve Cross Origin Resource Sharing with Flask

 

 

Android JavaでGoogle Photos APIと繋げてデータを取ってみる

JavaでAndroidアプリ作るのが初めてレベルですごく大変だったので、
手順とコードをまとめてみました

ネットにある程度情報は乗っていますが、
おそらくJavaの先輩方はあれでもわかるのかもしれんですけど
javascriptメインの僕にはきつかったので
Java新米の僕宛に書くつもりで書いてきます

コードはgitにアップしておきました
Photos APIとCredentialsが出来上がっていればすぐに使えるようになっているので、
参考にしてください

 

Android プロジェクトを作成

この内容で作成

 

Google Photos APIを登録する

 この辺りはネットで探せばすぐに出てくると思うのでこちらを参考にして
わからなければ探してみてください

Get started with Java client library

 

Credentialsを登録する

Credentialを作る
Credentialはandroidアプリ毎にpackage nameをつけないといけないので、
このプロジェクトでは「com.example.googlephotosapi」となっていますが、
自身のプロジェクトでOAuth認証を行いたい場合は自分のパッケージネームをいれてください

Credentialのタイプは「OAuth client ID」

 

 

これで完成

コード実装

app/bundle.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.example.googlephotosapi"
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    packagingOptions {
        exclude 'META-INF/DEPENDENCIES'
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/license.txt'
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/NOTICE.txt'
        exclude 'META-INF/notice.txt'
        exclude 'META-INF/ASL2.0'
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation 'com.squareup.okhttp3:okhttp:3.10.0'
    // Google Photos
    implementation 'com.google.photos.library:google-photos-library-client:1.1.0'
    // AUth関連をここでimportできる
    implementation 'com.google.android.gms:play-services-auth:15.0.1'
    // Google Credentials
    implementation('com.google.api-client:google-api-client-android:1.23.0') {
        exclude module: 'guava-jdk5'
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.googlephotosapi">


    <uses-permission android:name="android.permission.INTERNET" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

 

MainActivity.java

package com.example.googlephotosapi;

import android.accounts.Account;
import android.content.Intent;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import com.google.android.gms.auth.GoogleAuthException;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.UserRecoverableAuthException;
import com.google.android.gms.auth.api.Auth;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.auth.api.signin.GoogleSignInResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.Scope;

import java.io.IOException;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class MainActivity extends AppCompatActivity {

    private GoogleSignInAccount mGoogleSignInAccount = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                choseAccount();
            }
        });
    }

    public void choseAccount() {
        try {
            Scope mScope = new Scope("https://www.googleapis.com/auth/photoslibrary");
            GoogleSignInOptions gso = new GoogleSignInOptions
                    .Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                    .requestScopes(mScope)
                    .requestEmail()
                    .build();
            GoogleApiClient mGoogleApiClient = new GoogleApiClient
                    .Builder(this)
                    .enableAutoManage(this, null)
                    .addScope(mScope)
                    .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
                    .build();
            Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
            startActivityForResult(signInIntent, 1);
        } catch (Exception e) {
            Log.d("TAG",  e.getMessage());
        }
    }

    @Override
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		if (requestCode == 1) {
			GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
            if (result.isSuccess()) {
                mGoogleSignInAccount = result.getSignInAccount();
            }
            if (mGoogleSignInAccount != null) {
                AsyncTask<Void, Void, String> task = new AsyncTask<Void, Void, String>() {
                    @Override
                    protected String doInBackground(Void... params) {
                        String accessToken = null;
                        try {
                            accessToken = GoogleAuthUtil.getToken(
                                    MainActivity.this,
                                    new Account(mGoogleSignInAccount.getEmail(), GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE),
                                    "oauth2:https://www.googleapis.com/auth/photoslibrary");
                            Request request = new Request.Builder()
                                    .url("https://photoslibrary.googleapis.com/v1/mediaItems")
                                    .addHeader("Authorization", "Bearer " + accessToken)
                                    .build();
                            OkHttpClient client = new OkHttpClient();
                            Response response = null;
                            try {
                                response = client.newCall(request).execute();
                                String r = response.body().string();
                                Log.i("TAG", r);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        } catch (IOException transientEx) {
                            Log.e("TAG", transientEx.toString());
                        } catch (UserRecoverableAuthException e) {
                            Log.e("TAG", e.toString());
                        } catch (GoogleAuthException authEx) {
                            Log.e("TAG", authEx.toString());
                        }
                        return accessToken;
                    }
                    @Override
                    protected void onPostExecute(String token) {
                        Log.i("TAG", "Access token retrieved:" + token);
                    }
                };
                task.execute();
            }
		}
	}
}

 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Google Photos API呼び出し"
        tools:layout_editor_absoluteX="95dp"
        tools:layout_editor_absoluteY="292dp" />

</android.support.constraint.ConstraintLayout>

検証

デバッグモードで実行していきます

デバッグのポイントをいくつかつけときます

デバッグスタート

ちょうどここで実機ではこのような画面が出てきます

Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);

 

選択画面が出てくるのでGoogle Photoに画像をいれている方を選びます

 

 

アカウントを選択するとデバッグが進みますのでここまで行くとGoogle Photosから
responseデータが帰ってきています!!!
ちょうど「r」のところです
この「r」の中身をしっかりとみるために下のVariablesのところから
「r」のある行の一番みぎにある「View」をクリックして中身を確かめます

よかったちゃんときてる!!
今回はアルバムデータではなく写真を取ってくるmedeiaItemsを指定していたので
このようなデータの型になってます

ここまでデータを取れればあとはこれを処理してやるだけなので簡単だと思います

 

終わりに

今回初めてまともにJava使ってみましたが、最初は長くてめんどくさかったけど
型がしっかりしているのでちゃんとエラー出してくれてすごく安心してできたなー
と最後には思いました
連結部分だけでしたがやはり新米には難しいと感じることが多々あったので、
この記事が誰かの役に立てれば幸いです!

GItのレポ

https://bitbucket.org/hiyashikyuri/google-photos-api/src/master/

参考記事 

Get started with Java client library

AndroidアプリでGoogle Photos APIsを使ってみた!!

Access google photos API via Java

/google-auth-library-java

AndroidのGoogleSignInAPIを使ってOAuth認証を行う

Androidの新しいGoogleサインインAPIについて

GoogleAuthUtilを使ってOAuth 2.0 tokenを取得する

“Calling this from your main thread can lead to deadlock and/or ANRs while getting accesToken” from GoogleAuthUtil(Google Plus integration in Android) 

Method: mediaItems.list

Android InvalidArgumentException JKS not found sheets API の解決方法

Android開発あまりやったことないのでやはりエラー頻発中です

このエラーはGoogle oauthの認証に伴うエラーです

エラー文と修正方法です

// エラー文
InvalidArgumentException JKS not found sheets API

// 該当箇所
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();

// 修正後
HttpTransport httpTransport = new com.google.api.client.http.javanet.NetHttpTransport();

 

参考記事

InvalidArgumentException JKS not found sheets API

Caused by: com.android.builder.merge.DuplicateRelativeFileException: More than one file was found with OS independent path ‘META-INF/DEPENDENCIES’ の解決方法

最近javaでandroid開発するようになって頻繁にエラーに出くわすので、
自分が忘れた時のために書き残したいと思います

具体的な問題点は後ほど調べますが、とりあえず以下のコードを
Project/app/bundle.gradleに追加したら治りました!

 

android {

      packagingOptions {
        exclude 'META-INF/DEPENDENCIES'
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/license.txt'
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/NOTICE.txt'
        exclude 'META-INF/notice.txt'
        exclude 'META-INF/ASL2.0'
      }

    }

 

 

参考記事

More than one file was found with OS independent path ‘META-INF/LICENSE’

Angular Arrayの要素を削除する方法

Arrayの要素削除の方法としてはsliceという選択肢が手っ取り早いですが、
こちらの方法でもいけるみたいです

 

// data_itemのところは削除したい要素を記述することで削除することができます

this.data = this.data.filter(item => item !== data_item);

 

参考記事

remove item from stored array in angular 2

 

 

Angular [select [value]=”val” ]で[object, object]になるエラー解消

ngForでループさせて[value]でデータやりとりできるかと思っていましたが、どうやらできないようでした

 

このように修正すると解決できました

// 動かない
<option *ngFor="let model of customize_models" [value]="model">{{ model.customization_id | json }}</option>

//動く
// value -> ngValueに変更することで動きます
<option *ngFor="let model of customize_models" [ngValue]="model">{{ model.customization_id | json }}</option>

 

参考記事

Binding select element to object in Angular