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

JavaScriptのUIライブラリ ReactのHook使用してToDoアプリを作成してみました

$
0
0

はじめに

この記事ではJavaScriptのライブラリであるReactとReactの機能であるhookを使用して簡単なToDoアプリの実装を行います。
以前に書いた記事をベースに書き換えるので、そちらも参考にしてみてください。
JavaScriptのUIライブラリ ReactでToDoアプリを作成してみました
Reactのドキュメントチュートリアル(三目並べ)を一通り行った後の練習になるように書きたいと思います。
環境構築に関しては、create-react-appを使用して作成しています。
環境構築に関しては以前書いた記事があります。
ソースコード

目次

  1. コンポーネントの確認
  2. 各コンポーネントの解説
  3. まとめ

1. コンポーネントの確認

すぐに動かせる環境を置いておきます。


See the Pen
ReactToDo with Hook
by oq-Yuki-po (@oq-yuki-po)
on CodePen.



ReactはUIのパーツをコンポーネントという独立した一つの部品とみなして構成していきます。
今回の例では下記の様に分割しました。
以前は、これらのコンポーネントを全てクラスで定義して行きましたが今回は関数コンポーネントで定義して行きます。

ReactToDoApp.png

ToDoアプリケーションを構成するコンポーネントは全部で4つあります。

  • ToDo
    ToDoアプリケーションの全体を表します

  • TaskAdd
    新しいタスクの追加を行います

  • TaskList
    追加されたタスクをリストにして表示します

  • TaskItem
    一つのタスクを表します

2. 各コンポーネントの解説

2-1. ToDo.js

2-1-1. ソース全体

ToDo.js
importReact,{useState,useReducer}from"react";importTaskAddfrom'./TaskAdd';importTaskListfrom'./TaskList';functionreducer(state,action){switch(action.type){case'add':return[...state,action.NewTask];case'delete':letTaskIndex=0;for(vari=0;i<state.length;i++){if(state[i].key.toString()===action.TaskId.toString()){TaskIndex=i;}}returnstate.filter((_,index)=>index!==TaskIndex);default:returnstate;}}functionToDo(){const[TaskId]=useState(0)const[ToDoList,dispatch]=useReducer(reducer,[])return(<mainclassName='todo-component'><TaskAddid={TaskId}dispatch={dispatch}TaskList={ToDoList}/><TaskListTaskList={ToDoList}/></main>);}exportdefaultToDo;

2-1-2. モジュールのインポート

import
importReact,{useState,useReducer}from"react";importTaskAddfrom'./TaskAdd';importTaskListfrom'./TaskList';

useStateuseReducerがhookと呼ばれるものです。
これを使用することで、クラスコンポーネントで行なっていた状態管理が関数コンポーネントでも行えます。

2-1-3. ToDoコンポーネント

ToDoコンポーネント
functionToDo(){const[TaskId]=useState(0)const[ToDoList,dispatch]=useReducer(reducer,[])return(<mainclassName='todo-component'><TaskAddid={TaskId}dispatch={dispatch}TaskList={ToDoList}/><TaskListTaskList={ToDoList}/></main>);}

クラスで定義していた際は、constructorを定義していたと思います。
以前の記事では、下記の様に定義していました。

ToDoコンポーネントのconstructor
constructor(props){super(props);this.state={TaskList:[],TaskId:0};this.deleteTask=this.deleteTask.bind(this);this.addTask=this.addTask.bind(this);}

関数でコンポーネントを定義する際は以下の様になります。

ToDoコンポーネント(クラス版)
const[TaskId]=useState(0)const[ToDoList,dispatch]=useReducer(reducer,[])

stateを定義する際にuseStateを使用します。
const [状態管理したい変数, 状態を変更する関数] = useState(状態管理したい変数の初期値)
TaskIdは子コンポーネントに渡して、そちらで管理するので変更する関数を定義していません。
(定義してる場所が、そもそもどうなの?みたいなツッコミはご勘弁を。後々にAPIとの連携を見越した実装だと解釈お願いします。。。)
const [状態管理したい変数, 状態を変更する関数] = useState(初期値)
配列や複雑なロジックをstateに持たせる時は、TaskListの様にuseReducerを使用します。
const [状態管理したい変数, reducerで定義した関数(dispatch)] = useReducer(reducer, 状態管理したい変数の初期値);
公式の引用

通常、useReducer が useState より好ましいのは、複数の値にまたがる複雑な state ロジックがある場合や、
前の state に基づいて次の state を決める必要がある場合です。また、useReducer を使えば
コールバックの代わりに dispatch を下位コンポーネントに渡せるようになるため、
複数階層にまたがって更新を発生させるようなコンポーネントではパフォーマンスの最適化にもなります。

引用にも書いてある通り、<TaskAdd id={TaskId} dispatch={dispatch} TaskList={ToDoList}/>
dispatchをTaskAddに渡しています。

2-1-4. reducerの定義

reducerの定義
functionreducer(state,action){switch(action.type){case'add':return[...state,action.NewTask];case'delete':letTaskIndex=0;for(vari=0;i<state.length;i++){if(state[i].key.toString()===action.TaskId.toString()){TaskIndex=i;}}returnstate.filter((_,index)=>index!==TaskIndex);default:returnstate;}}

reducerの定義を行います。
stateは状態管理したい変数、今回はTaskListが入っています。
actiondispatchで、この関数を実行するときに指定したパラメータが格納されています。
action.typeで、どの操作なのかを判定するのに使用しています。
adddeleteはそれぞれ、TaskListに新規にタスクを追加、指定したタスクを削除の処理を行なっています。

2-2. TaskAdd.js

2-2-1. ソース全体

TaskAdd.js
importReact,{useState}from"react";importTaskfrom'./Task'functionTaskAdd(props){const[NewTask,setTask]=useState('')const[TaskId,setTaskId]=useState(props.id)const[ErrorMessage,setErrorMessage]=useState('')functionhandleClick(){if(NewTask===''){setErrorMessage('入力が空です。')return0}for(vari=0;i<props.TaskList.length;i++){if(props.TaskList[i].props.name===NewTask){setErrorMessage('タスク名が重複しています。')return0}}props.dispatch({type:'add',NewTask:<Taskkey={TaskId}id={TaskId}name={NewTask}dispatch={props.dispatch}/>})setTaskId(TaskId+1)setTask('')setErrorMessage('')}return(<sectionclassName='task-creator'><h2>Task Add</h2><inputclassName='task-item-text'type="text"placeholder="Task"value={NewTask}onChange={(e)=>setTask(e.target.value)}/><buttonclassName='task-add-btn'type="button"onClick={handleClick}>
                Add
                </button><pclassName='error-msg'>{ErrorMessage}</p></section>)}exportdefaultTaskAdd;

2-2-2. stateの定義

stateの定義
const[NewTask,setTask]=useState('')const[TaskId,setTaskId]=useState(props.id)const[ErrorMessage,setErrorMessage]=useState('')

Task.jsと同じ様に定義しています。
const [状態管理したい変数, 状態を変更する関数] = useState(状態管理したい変数の初期値)

2-2-3. handleClick

TaskAdd.jsのhandleClick
functionhandleClick(){if(NewTask===''){setErrorMessage('入力が空です。')return0}for(vari=0;i<props.TaskList.length;i++){if(props.TaskList[i].props.name===NewTask){setErrorMessage('タスク名が重複しています。')return0}}props.dispatch({type:'add',NewTask:<Taskkey={TaskId}id={TaskId}name={NewTask}dispatch={props.dispatch}/>
})setTaskId(TaskId+1)setTask('')setErrorMessage('')}

定義したstateにアクセスする際はthis.state.NewTaskの様に行なっていましたが
hookでは単純にNewTaskでアクセスできます。(this.stateの呪縛から開放される!!)

stateの更新の際はthis.setState({ ErrorMessage: '入力が空です。' })ではなく
state定義時の関数をそのまま使用できます。
つまりsetErrorMessage('入力が空です。')と書けます。

タスクの追加時にはTask.jsからpropsとして受け取ってあるdispatchを使用しています。

props.dispatch({
    type: 'add',
    NewTask: <Task key={TaskId} id={TaskId} name={NewTask} dispatch={props.dispatch} />
})

typeNewTaskはactionで取れる様に追加しています。

2-2-4. render

TaskAdd.jsのrender
return(<sectionclassName='task-creator'><h2>Task Add</h2><inputclassName='task-item-text'type="text"placeholder="Task"value={NewTask}onChange={(e)=>setTask(e.target.value)}/><buttonclassName='task-add-btn'type="button"onClick={handleClick}>
            Add
            </button><pclassName='error-msg'>{ErrorMessage}</p></section>)

this.stateで取得することが無くなったので、少しスッキリしたと思います。

2-3. Task.js

2-3-1. ソース全体

Task.js
importReact,{useState}from"react";functionTask(props){const[isDone,setStatus]=useState(props.isDone)consttask_id='task-id-'+props.id.toString()constcss_label='task-item-label'constcss_isDone=`${css_label} isDone`constcss_Wip=`${css_label} WorkInProgress`return(<liclassName='task-item-row'><inputid={task_id}className='task-item-checkbox'type='checkbox'onChange={()=>(isDone===true)?setStatus(false):setStatus(true)}></input><labelhtmlFor={task_id}className={(isDone===true)?css_isDone:css_Wip}>{props.name}</label><iclassName="material-icons icon"onClick={()=>props.dispatch({type:'delete',TaskId:props.id})}>
                delete
            </i></li>);}exportdefaultTask;

2-3-2. stateの定義

stateと定数の定義
const[isDone,setStatus]=useState(props.isDone)consttask_id='task-id-'+props.id.toString()constcss_label='task-item-label'constcss_isDone=`${css_label} isDone`constcss_Wip=`${css_label} WorkInProgress`

stateとclassNameの定数を定義しているのみです。

2-3-3. render

Task.js
return(<liclassName='task-item-row'><inputid={task_id}className='task-item-checkbox'type='checkbox'onChange={()=>(isDone===true)?setStatus(false):setStatus(true)}></input><labelhtmlFor={task_id}className={(isDone===true)?css_isDone:css_Wip}>{props.name}</label><iclassName="material-icons icon"onClick={()=>props.dispatch({type:'delete',TaskId:props.id})}>
            delete
        </i></li>);

onChange={() => (isDone === true) ? setStatus(false) : setStatus(true)}で単純なif文を省略しています。
onClick={() => props.dispatch({type:'delete', TaskId:props.id})}でpropsで受け取ったdispatchを使用してタスクの削除を行なっています。

2-4. TaskList.js

TaskList.js
importReactfrom"react";functionTaskList(props){return(<sectionclassName='task-list'><h2>Task List</h2><ul>{props.TaskList}</ul></section>);}exportdefaultTaskList;

特にクラスと関数で違いは無いでしょうか、強いて言えばクラスで書くより短いくらいでしょうか??

3. まとめ

Reactのhookを使用して、クラスコンポーネントを関数コンポーネントのみで書き換えてみました。
スッキリ書き換えられるのはメリットに感じました。
ドキュメントを読む限りでは、完全に互換性がある訳では無いそうなので更に勉強が必要そうです。。。
最後まで見てくださり、ありがとうございます。
質問や、指摘は大歓迎ですので、よろしくお願いします。


Railsチュートリアルメモ - 第7章

$
0
0

メモの目次記事はこちら

Railsチュートリアル第7章へのリンク

第7章 ユーザー登録

7.1 ユーザーを表示する

ポイント

デバッグ

  • <%= debug(params) if Rails.env.development? %>で画面にデバッグ情報を表示できる。
    • if Rails.env.development?で開発環境のみに表示するよう制限している
  • コントローラー内にdebuggerを記載して画面を表示すると、rails serverを起動しているコンソールがコマンド待受け状態になり、debuggerが呼び出された状態のままでrails consoleの操作を実行できる

Sassのミックスイン

@mixinで定義した内容を@includeで呼び出せる

@mixinbox_sizing{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;}/* miscellaneous */
.debug_dump{clear:both;float:left;width:100%;margin-top:45px;@includebox_sizing;}

ルーティング

config/routes.rbにresourceを追加すると、名前付きルートが使用可能になり、RESTに従ったいくつかのURLが使用可能になる

config/routes.rb
Rails.application.routes.drawdoresources:usersend

Gravatar

RailsというよりGravatarの使用方法の説明なので割愛

7.2 ユーザー登録フォーム

ポイント

form_forによるフォームの作成

.html.erb内でform_forを使用すると、フォームを生成してくれる

.html.erb

<%=form_for(@user)do|f|%>
...
<%=f.label:name%><%=f.text_field:name%>
...
<%end%>


html

<formaction="/users"class="new_user"id="new_user"method="post">
...
<labelfor="user_name">Name</label><inputid="user_name"name="user[name]"type="text"/>
...
</form>
  • また、emailであれば、モバイル端末から入力フォームをタップすると、メールアドレスに最適化された特別なキーボードが表示される。passwordであれば、文字が隠蔽されて表示されるようにHTMLを生成してくれる。
  • CSRF対策のトークンの生成も行ってくれる

paramsハッシュでの値の受け渡し

フォームで指定した値はparamsというハッシュに保存されてコントローラーに渡されるルールになっている(チュートリアルでの説明があまりにさらっとしすぎていて、paramsがどこで定義されているのかわからず少し混乱した)

7.3 ユーザー登録失敗

ポイント

Strong Parameters

マスアサインメント:
DB登録・更新で複数のカラムを一括で指定して登録すること。メソッドにハッシュを渡して更新カラムと値を指定する。
意図的に重要なカラムの値を書き換えられる危険性があるため、Strong Parametersというテクニックを使ってController層で対策することが推奨されている(かつてはModel層で対策していたらしい)

params.require(:user).permit(:name, :email, :password, :password_confirmation)

privateキーワード

クラス内でprivateと記載された後に記載された要素はprivate属性となる(外部クラスからは見えない)

エラー表示

  • Rails全般の慣習として、複数のビューで使われるパーシャルは専用のディレクトリ「shared」によく置かれる
  • pluralizeメソッドによって、英語の単数系と複数形を良い感じに処理して表示してくれる
    • e.g. 0 errors, 1 error, 2 errors
  • Railsは、無効な内容の送信によって元のページに戻されると、CSSクラスfield_with_errorsを持ったdivタグでエラー箇所を自動的に囲んでくれる
  • (エラー表示とは関係ないが、)class form-controlを追加すると、Bootstrapでフォームがきれいになる

Sassの@extend関数

@extendで特定の位置に属性を追加できる

.field_with_errors{@extend.has-error;.form-control{color:$state-danger-text;}}

統合テスト

  • rails generate integration_test users_signupで統合テストの作成

assert_no_difference, assert_difference

以下のように記載することで、ブロック内の実行前後で引き数が変化していないこと、変化していることを検証できる

assert_no_difference'User.count'doendassert_difference'User.count'doend

assert_template

以下のように記載することで、意図どおりページが再描画されているかどうかを検証できる(エラーメッセージの表示によるDOMの差異は無視される?)

assert_template'users/new'

7.4 ユーザー登録成功

ポイント

redirect_to

コントローラーに以下のように記載すると、自動的に名前付きルート付きで解釈してくれる

if@user.saveredirect_to@userelserender'new'end

if@user.saveredirect_touser_url(@user)elserender'new'end

flash変数

flash変数に代入したメッセージは、リダイレクトした直後のページでのみ表示できる(二回目は表示されない)

シンボル => 文字列の自動変換

Railsではシンボルをテンプレート(.html.erb)内に表示しようとすると、文字列に自動変換する
e.g. :success => "success"

7.5 プロのデプロイ

ポイント

WebサーバPumaの導入

  • config/environments/production.rbconfig.force_ssl = trueをコメントインすると、本番環境でのSSL化が有効になる
  • Rails4.2まではconfig/puma.rbの手動作成が必要だったが、Rails5以降はデフォルトで作成済み
  • Procfileをプロジェクトルートに作成してherokuにgit pushすれば反映される

./Procfile

web: bundle exec puma -C config/puma.rb

【swift】ボタンタップ&スワイプによるページ遷移機能の実装

$
0
0

前置き

スワイプによるページ遷移、ボタン押下によるページ遷移、までは実装できたものの、スワイプによるページ遷移時のtopviewのデザインの変更ができないとつまずいた人間の、備忘録。

環境

xcode 11.3
swift 5.1.3

実現したい機能

  • スワイプでページ遷移 : 完了
  • ボタン押下でページ遷移 : 完了
  • ページ遷移に従い、ページ遷移しない部分のデザインの変更 : これ!!!

現状

ファイル構成

  • TopViewController.swift <- 元となるViewController
  • PageViewController.swift <- page遷移機能をまとめたViewController
  • View1Controller.swift
  • View2Controller.swift
  • View3Controller.swift

storybord

  • 元となるViewControllerが存在
  • その中に、3つのボタンと、ページのタイトルラベル、そしてcontainerViewが存在
  • containerViewの先はpageViewControllerとなっている。
  • 遷移先のページとして、viewControllrが3つ(view1,view2,view3)存在している。
  • 各viewには、それぞれtopView,view1,view2,view3のstorybordIDをつけている

スクリーンショット 2020-01-12 20.14.45.png

コード

TopViewController.swift
importUIKitclassTopViewController:UIViewController{// pageのタイトル@IBOutletweakvarpageTitle:UILabel!// buttons@IBOutletweakvartoView1Btn:boundButton!@IBOutletweakvartoView2Btn:boundButton!@IBOutletweakvartoView3Btn:boundButton!// toView1Btn押下時@IBActionfunctoView1Action(_sender:Any){letvc=storyboard?.instantiateViewController(withIdentifier:"view1")as!View1ControllerpageViewController!.setViewControllers([vc],direction:.forward,animated:false,completion:nil)toView1Design()}// toView2Btn押下時@IBActionfunctoView2Action(_sender:Any){letvc=storyboard?.instantiateViewController(withIdentifier:"view2")as!View2ControllerpageViewController!.setViewControllers([vc],direction:.reverse,animated:false,completion:nil)toView2Design()}// toView3Btn押下時@IBActionfunctoView3Action(_sender:Any){letvc=storyboard?.instantiateViewController(withIdentifier:"view3")as!View3ControllerpageViewController!.setViewControllers([vc],direction:.reverse,animated:false,completion:nil)toView3Design()}// 画面遷移用varpageViewController:UIPageViewController?overridefuncviewDidLoad(){super.viewDidLoad()// 最初はview1を出しとくtoView1Design()}}// デザイン変更用関数のまとめextensionTopViewController{// view1に遷移するとき呼ばれるfunctoView1Design(){// pageTitle変更pageTitle.text="view1"// ボタンの色変更(選ばれているボタンを赤色に、それ以外は灰色に)toView1Btn.setTitleColor(UIColor.red,for:[])toView2Btn.setTitleColor(UIColor.lightGray,for:[])toView3Btn.setTitleColor(UIColor.lightGray,for:[])}// view2に遷移するとき呼ばれるfunctoView2Design(){// pageTitle変更pageTitle.text="view2"// ボタンの色変更(選ばれているボタンを赤色に、それ以外は灰色に)toView1Btn.setTitleColor(UIColor.lightGray,for:[])toView2Btn.setTitleColor(UIColor.red,for:[])toView3Btn.setTitleColor(UIColor.lightGray,for:[])}// view3に遷移するとき呼ばれるfunctoView3Design(){// pageTitle変更pageTitle.text="view3"// ボタンの色変更(選ばれているボタンを赤色に、それ以外は灰色に)toView1Btn.setTitleColor(UIColor.lightGray,for:[])toView2Btn.setTitleColor(UIColor.lightGray,for:[])toView3Btn.setTitleColor(UIColor.red,for:[])}}
PageViewController.swift
importUIKitclassPageViewController:UIPageViewController{overridefuncviewDidLoad(){super.viewDidLoad()self.setViewControllers([getView1()],direction:.forward,animated:true,completion:nil)self.dataSource=self}// それぞれ、viewControllerを返す関数funcgetView1()->View1Controller{returnstoryboard!.instantiateViewController(withIdentifier:"view1")as!View1Controller}funcgetView2()->View2Controller{returnstoryboard!.instantiateViewController(withIdentifier:"view2")as!View2Controller}funcgetView3()->View3Controller{returnstoryboard!.instantiateViewController(withIdentifier:"view3")as!View3Controller}funcgetTop()->TopViewController{returnstoryboard!.instantiateViewController(withIdentifier:"topView")as!TopViewController}}//datasource用の関数まとめextensionPageViewController:UIPageViewControllerDataSource{// 現在表示されているページの、Beforeに位置する、つまり左側に位置するviewを呼び出す関数funcpageViewController(_pageViewController:UIPageViewController,viewControllerBeforeviewController:UIViewController)->UIViewController?{print(viewController)ifviewController.isKind(of:View3Controller.self){// 3の時 -> 2returngetView2()}ifviewController.isKind(of:RepoViewController.self){// 2の時 -> 1returngetView1()}ifviewController.isKind(of:CalendarViewController.self){// 1の時 -> 3returngetView3()}returnnil}// 現在表示されているページの、Afterに位置する、つまり右側に位置するviewを呼び出す関数funcpageViewController(_pageViewController:UIPageViewController,viewControllerAfterviewController:UIViewController)->UIViewController?{print(viewController)ifviewController.isKind(of:CalendarViewController.self){// 1の時 -> 2returngetView2()}ifviewController.isKind(of:RepoViewController.self){// 2の時 -> 3returngetView3()}ifviewController.isKind(of:UserViewController.self){// 3の時 -> 1returngetView1()}returnnil}}

課題

スワイプによるページ遷移に伴うTopViewController上のデザインの変更メソッドを呼ぶ場所がわからない。
datasource用の関数が呼ばれるタイミングは、現在表示されているviewcontrollerとずれている。

解決策

検索結果

調べた結果、delegateメソッドでスワイプしたタイミングを検知してくれるメソッドがあるらしい。これを使う。

UIPageViewControllerでページ遷移時に処理したいことを書くときのTip
Apple Developer : pageViewController(_:didFinishAnimating:previousViewControllers:transitionCompleted:)

実装

PageViewController.swift
overridefuncviewDidLoad(){super.viewDidLoad()self.setViewControllers([getView1()],direction:.forward,animated:true,completion:nil)self.dataSource=self// ここから追記self.delegate=self// ここまで追記}// 以下を追記extensionPageViewController:UIPageViewControllerDelegate{// スワイプによるページ遷移が行われたときに呼ばれるメソッドfuncpageViewController(_pageViewController:UIPageViewController,didFinishAnimatingfinished:Bool,previousViewControllers:[UIViewController],transitionCompletedcompleted:Bool){// 現在のviewcontrollerを取得letcurrentVC=pageViewController.viewControllers![0]// topViewControllerを取得lettopView=getTop()// デザインを変更処理ifcurrentVC!.isKind(of:View1Controller.self){topView.toView1Design()}ifcurrentVC!.isKind(of:View2Controller.self){topView.toView2Design()}ifcurrentVC!.isKind(of:View3Controller.self){topView.toView3Design()}}}

課題2

しかしながらこのコードでは、デザイン変更関数が呼ばれた時に、toView1Btnがない、と言ったエラーが発生する。
pageViewController上でデザイン変更関数のみを呼び出しており、TopViewController上で行われている変数設定等を行っていないからだ。

解決策2

検索結果2

どうやら、pageVioewControllerをTopViewController内部で設定する方法もあるらしい。
この方法を用いれば、上記のエラーも発生しなくなるはず。

qiita : 【Swift】UIPageViewControllerとContainerViewを利用してタップで画面遷移

実装2

PageViewController.swiftを削除し、TopViewController.swiftに書き足し。

TopViewController.swift
importUIKitclassTopViewController:UIViewController{// pageのタイトル@IBOutletweakvarpageTitle:UILabel!// buttons@IBOutletweakvartoView1Btn:boundButton!@IBOutletweakvartoView2Btn:boundButton!@IBOutletweakvartoView3Btn:boundButton!// toView1Btn押下時@IBActionfunctoView1Action(_sender:Any){letvc=storyboard?.instantiateViewController(withIdentifier:"view1")as!View1ControllerpageViewController!.setViewControllers([vc],direction:.forward,animated:false,completion:nil)toView1Design()}// toView2Btn押下時@IBActionfunctoView2Action(_sender:Any){letvc=storyboard?.instantiateViewController(withIdentifier:"view2")as!View2ControllerpageViewController!.setViewControllers([vc],direction:.reverse,animated:false,completion:nil)toView2Design()}// toView3Btn押下時@IBActionfunctoView3Action(_sender:Any){letvc=storyboard?.instantiateViewController(withIdentifier:"view3")as!View3ControllerpageViewController!.setViewControllers([vc],direction:.reverse,animated:false,completion:nil)toView3Design()}// 画面遷移用varpageViewController:UIPageViewController?overridefuncviewDidLoad(){super.viewDidLoad()// ここから追加// PageViewControllerの設定pageViewController=children.first!as?UIPageViewControllerpageViewController!.setViewControllers([getView1()],direction:.forward,animated:true,completion:nil)pageViewController!.dataSource=selfpageViewController!.delegate=self// ここまで追加// 最初はview1を出しとくtoView1Design()}}// デザイン変更用関数のまとめextensionTopViewController{// view1に遷移するとき呼ばれるfunctoView1Design(){// pageTitle変更pageTitle.text="view1"// ボタンの色変更(選ばれているボタンを赤色に、それ以外は灰色に)toView1Btn.setTitleColor(UIColor.red,for:[])toView2Btn.setTitleColor(UIColor.lightGray,for:[])toView3Btn.setTitleColor(UIColor.lightGray,for:[])}// view2に遷移するとき呼ばれるfunctoView2Design(){// pageTitle変更pageTitle.text="view2"// ボタンの色変更(選ばれているボタンを赤色に、それ以外は灰色に)toView1Btn.setTitleColor(UIColor.lightGray,for:[])toView2Btn.setTitleColor(UIColor.red,for:[])toView3Btn.setTitleColor(UIColor.lightGray,for:[])}// view3に遷移するとき呼ばれるfunctoView3Design(){// pageTitle変更pageTitle.text="view3"// ボタンの色変更(選ばれているボタンを赤色に、それ以外は灰色に)toView1Btn.setTitleColor(UIColor.lightGray,for:[])toView2Btn.setTitleColor(UIColor.lightGray,for:[])toView3Btn.setTitleColor(UIColor.red,for:[])}}// ここから下全部追記// view取得系の関数まとめextensionTopViewController{// それぞれ、viewControllerを返す関数funcgetView1()->View1Controller{returnstoryboard!.instantiateViewController(withIdentifier:"view1")as!View1Controller}funcgetView2()->View2Controller{returnstoryboard!.instantiateViewController(withIdentifier:"view2")as!View2Controller}funcgetView3()->View3Controller{returnstoryboard!.instantiateViewController(withIdentifier:"view3")as!View3Controller}funcgetTop()->TopViewController{returnstoryboard!.instantiateViewController(withIdentifier:"topView")as!TopViewController}}// datasource用の関数まとめextensionTopViewController:UIPageViewControllerDataSource{// 現在表示されているページの、Beforeに位置する、つまり左側に位置するviewを呼び出す関数funcpageViewController(_pageViewController:UIPageViewController,viewControllerBeforeviewController:UIViewController)->UIViewController?{print(viewController)ifviewController.isKind(of:View3Controller.self){// 3の時 -> 2returngetView2()}ifviewController.isKind(of:RepoViewController.self){// 2の時 -> 1returngetView1()}ifviewController.isKind(of:CalendarViewController.self){// 1の時 -> 3returngetView3()}returnnil}// 現在表示されているページの、Afterに位置する、つまり右側に位置するviewを呼び出す関数funcpageViewController(_pageViewController:UIPageViewController,viewControllerAfterviewController:UIViewController)->UIViewController?{print(viewController)ifviewController.isKind(of:CalendarViewController.self){// 1の時 -> 2returngetView2()}ifviewController.isKind(of:RepoViewController.self){// 2の時 -> 3returngetView3()}ifviewController.isKind(of:UserViewController.self){// 3の時 -> 1returngetView1()}returnnil}}// delegate用の関数まとめextensionTopViewController:UIPageViewControllerDelegate{// スワイプによるページ遷移が行われたときに呼ばれるメソッドfuncpageViewController(_pageViewController:UIPageViewController,didFinishAnimatingfinished:Bool,previousViewControllers:[UIViewController],transitionCompletedcompleted:Bool){// 現在のviewcontrollerを取得letcurrentVC=pageViewController.viewControllers?.first!ifcurrentVC!.isKind(of:View1Controller.self){toView1Design()}ifcurrentVC!.isKind(of:View2Controller.self){toView2Design()}ifcurrentVC!.isKind(of:View3Controller.self){toView3Design()}}}

参考サイト

二つの画面をpageViewControllerで繋ぎ、スワイプでページ遷移

【決定版】UIPageViewControllerの使い方(Swift)

タブを作成して、タブ操作とページ遷移を紐付ける

UICollectionViewControllerとUIPageViewControllerでSmartNewsっぽいあのUIをお手軽に実現する

Dockerを触ってみる

$
0
0

はじめに

前回の記事でDockerってどんなものなんだろうってことを軽く調べて書かせて頂きました。
今回は実際にDockerを使用して開発環境を構築してみよう!って内容を書きたいと思います。
まずは操作に慣れたいのでイメージを取得してコンテナを作成して起動までをやってみたいと思います。

主なコマンド

  • docker pull リポジトリ:タグ (イメージの取得)
  • docker run イメージ名 (コンテナの生成から起動)
  • docker start コンテナID(もしくはコンテナ名)
  • docker stop コンテナID(もしくはコンテナ名)
  • docker rm コンテナID(もしくはコンテナ名)
  • docker ps (起動中のコンテナ表示)
  • docker ps -a (状態に関係なくすべてのコンテナを表示)
  • docker ps -aq (コンテナIDのみ表示)
  • docker commit コンテナID(もしくはコンテナ名) イメージ名:タグ (コンテナからイメージ作成)

イメージの取得

まずcentosのイメージを取得します。

centos7: Pulling from library/centos
ab5ef0e58194: Pull complete
Digest: sha256:4a701376d03f6b39b8c2a8f4a8e499441b0d567f9ab9d58e4991de4472fb813c
Status: Downloaded newer image for centos:centos7
docker.io/library/centos:centos7

イメージ一覧を表示して確認。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos              centos7             5e35e350aded        2 months ago        203MB

これでイメージの取得は完了です。めっちゃ簡単!!

イメージからコンテナを作成、起動

$ docker run -it -d --name centos7 centos:centos7
Unable to find image 'centos:centos7' locally
centos7: Pulling from library/centos
ab5ef0e58194: Pull complete
Digest: sha256:4a701376d03f6b39b8c2a8f4a8e499441b0d567f9ab9d58e4991de4472fb813c
Status: Downloaded newer image for centos:centos7
9ac1b47222b99cb76f33837836e30859d7a0420fdccb694d0deb795a5da7b7b7

起動できているか確認

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
9ac1b47222b9        centos:centos7      "/bin/bash"         2 minutes ago       Up 2 minutes                            centos7

STATUSの欄を見るとUp 2となっているので起動できているかと思います。

実際にコンテナを操作してみる

$ docker exec -it centos7 /bin/bash
[root@9ac1b47222b9 /]#

これでコンテナ内に入れたのでコマンド操作が可能になります。
コンテナから抜けるにはexitで抜けれました。

コンテナの停止

docker stop コンテナID(またはコンテナ名)
上記でコンテナを停止できます。

$ docker stop centos7
centos7

停止しているか確認

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

プロセスが表示されていないので正しく停止してそうです。

意外と直感的に操作できた

linux環境を触ったことある人なら意外と簡単に操作できるなって感じました。
これからは自分でイメージを作成したりdockerfileについても勉強して記事にしていきたいと思います。

【axios】HTTPリクエストメソッド別の引数一覧表(エイリアスを使用した場合)

$
0
0

はじめに

PromiseベースのHTTPクライアントであるaxiosのHTTPリクエストメソッド別の引数一覧表を作成しました。(エイリアスを使用した場合)

環境

OS:macOS Catalina 10.15.1Vue:2.6.10vuex:3.1.2axios:0.19.0

結論:まとめ

公式ドキュメント

For convenience aliases have been provided for all supported request methods.

axiosにはHTTPリクエストを簡単に投げられるように以下のようなエイリアスが用意されています。

sample.js
axios.エイリアス(1引数,2引数,3引数)
エイリアス第1引数第2引数第3引数
request(省略可)config--
geturl(config)-
deleteurl(config)-
posturldata(config)
puturldata(config)
patchurldata(config)

※1 configには、オブジェクトで任意のHTTPリクエストに必要な情報を渡すことが出来ます。
※2 (config)は必須ではありません。

使用例

axios.request({//axios({ のように.request省略可method:'post',url:'/user/12345',data:{firstName:'Fred',lastName:'Flintstone'}});
axios.get('anyUrl')
axios.post('anyUrl',anyParams)

余談:なぜ書こうと思ったか

DELETEリクエストにparamsを渡したい状況になり、以下コードを書きました。

sample.js
axios.delete('/api/v1/any',anyParams)

その前にaxiosを使って以下POSTリクエストを投げるコードが動いており、そのまま流用すればうまくいくと思っていたものの、DELETEリクエストのBodyにparamsが入ってくれませんでした。

sample.js
axios.post('/api/v1/any/create',anyParams)

【解決】

以下のように修正して解決しました。

sample.js
//これではanyParamsが、paramsとして認識されないaxios.delete('/api/v1/any',anyParams)

sample.js
//これでconfig扱いとなり、anyParamsをparamsとしてDELETEリクエストに含めて渡すことが出来るようになるaxios.delete('/api/v1/any',{params:anyParams})

postdelete、同じ書き方で大丈夫と考えていたので問題の対象と捉えておらず、ハマってしまい反省です:sweat_smile:

おわりに

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

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

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

dockerfileについて調べてapache起動するまでのメモ

$
0
0

はじめに

前回はDockerのコマンド操作を学ぶために主な操作の記事を書かせて頂きました。
他にもいろいろ調べていたらdockerfileなるのもがありそれを使用してイメージを作成するとのことでした。
見た感じ難しそうで抵抗があったのですが、そういうものこそ試していかないといけないと思いやってみましたので備忘録件まとめ用として記事にしてみたいと思います。

dockerfielってなんぞ?

コンテナを作成する場合、docker hubから欲しいイメージを取得してコンテナを作成したり、最小限のイメージから自分で新しくミドルウェアを追加したものをイメージ化して展開したりするとたくさんの情報が出ていましたが、自分でイメージを一から作成する場合などに使用するファイルです。
dockerfileはベースとするイメージに対して行う操作を記載する。

dockerfileを使用する利点

  • OSや各種ミドルウェアなどの設定をコードとして管理できる
  • 上記により細かく設定した環境を漏れなく簡単に再作成したり同一環境を用意したい人に配布できる。
  • jenkinsなどのCIツールを使用することでイメージ作成やコンテナのデプロイを自動化することもできる(らしい)

書き方について

調べてみたのですが結構難しそうでした(笑)
まずは簡単にコードについて

  • FROM イメージ名 →ベースにするイメージを指定する
  • LABEL →管理用の情報などを記載する バージョン情報やなにを作るためのdockerfileですよ的な
  • RUN →コマンド実行を命令するリターンキーの役割(こいつがコマンドを実行していく感じ)
  • CMD →コンテナ起動時にファイルやコマンドを実行する命令を記載する
  • ENV →環境変数を設定する

おおまかにはこんな感じでした。ほかにもたくさんありましたが、いったんこれくらいにしておきました。(覚える自信ない)

実際に書いてみる

簡単にapache用のdockerfileを書いてみます。

dockerfile
FROM centos:centos7        #ベースになるイメージ指定
RUN yum -y update          #パッケージ更新
RUN yum -y upgrade         #更新、不要なもの削除(しれくれるらしい)
RUN yum install -y httpd   #apacheインストール
RUN systemctl enable httpd #apache自動起動設定
EXPOSE 80                  #ポート開放

できたので上記ファイルを使用してイメージの作成をしてみます。

$ docker build -t apache .

ずーーーーと実行されて終わったみたいなのでイメージができてるか確認してみます。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
apache              latest              827d4f21614f        15 seconds ago      599MB

ありました!apacheという名前で作成したのでできています。

イメージからコンテナ作成して起動

起動

$ docker run --privileged -d -p 80:80 apache /sbin/init
8a86bb505eca2b7143ac70a1033e34205ffbbccf48a3ca89e96031194b00ffd9

確認
しっかり起動しているのがわかりました!

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                NAMES
8a86bb505eca        apache              "/sbin/init"        9 seconds ago       Up 8 seconds        0.0.0.0:80->80/tcp   sweet_hodgkin

実際にアクセスして確認!!

20200113001.JPG

テストページが表示されたので問題なさそうです!!!

ちなみにcentos7の場合はコンテナの起動を上記のようにしないとうまくapacheがうまく起動しないようです。(めっちゃはまった)
ここら辺の起動の仕方とコンテナ内の動きについては調べなおして書かせていただければと思います。

まとめ

  • Dockerfileはベースのイメージに対して行う命令を記載するもの
  • 構築過程のコマンドや起動時の命令もコードとして管理できる
  • 実行するだけで何回でも同じ環境をミスなく作れる
  • まだまだ勉強しないとdockerを扱って環境構築は厳しそうw

Rails6 rails consoleからカラムのデータ型を確認する

$
0
0

目的

  • rails consoleからのカラムのデータ型の確認方法をまとめる

実施方法

  1. railsアプリ名フォルダ直下で下記コマンドを実行してrails consoleを起動する。

    $rails console
    
  2. rails console上で下記コマンドを実行してカラムのデータ型を確認する。

    >モデルの名前.columns_hash['カラムの名前'].type
    

Dockerの手ほどき

$
0
0

Dockerとは

簡単に軽量な仮想マシンを作ることができます。
コンテナ型のアプリケーション実行環境です。
※コンテナについてはこちらの記事がわかりやすいです!

そもそも仮想マシンとは?

仮想マシンとは1台のコンピュータで複数のコンピュータを動かす技術です。
手元にあるMacの中にWindows10やCentOSなど沢山のOS入るイメージです。

なぜ仮想マシンを使うの?

実際に動いているWebサービスは本番環境と呼ばれる場所に置かれています。
開発をする際は実際に動いているものを触る訳にはいかないので、開発環境を準備します。

開発環境は本番環境と一緒の設定・ソフトウェアを使わなければなりません。
バージョンが違うと関数が使えなくなったりエラーが吐かれてしまう事態が発生し、
いわゆる「本番環境しか再現しない」という現象が起きてしまいます。

VirtualBox+Vagrantを使って仮想環境を作ることは可能ですが、
・メモリを沢山消費してしまう
・個別にソフトなどインストールをするなど手間がかかります。
(例:ソースコード・DBサーバーなど・・・)

それを容易にしたものがDockerです。


Dockerの知識

Docker は Dockerfile から命令を読み込み、自動的にイメージを構築できます。

Docker File

テキスト形式のドキュメントで、イメージを作り上げる命令を全て記述します。
「docker build」でファイルに書かれた命令を順次実行してイメージを自動構築できます。

Docker Image

コンテナを起動する時に必要となる設定ファイルをまとめたもの
どのバージョンのPHPを取得するかの設定・どのソフトウェアに関する情報が多いです。

Docker Fileの書き方

DockerFileを書く時、よく使うものなどをまとめてみました。
1コンテナ1DockerFileです。

FROM    : ベースとなるDockerImageを指定。(一番最初に書くこと!)
RUN     : 実行したいコマンドを書く
WORKING : ディテクトの指定
COPY    : コンテナの中へファイルを送るコマンド。
ADD     : コンテナの中へファイルを送るコマンド。COPYにはないファイル展開機能がある。
ENV     : 環境変数の設定
VOLUME  : データを永続化できるボリュームと言われる場所を指定し、マウントします。

複数のコンテナを立ち上げる

仕事で開発をする際、一般的には複数のコンテナを立上げる必要があります。
コンテナを立上らせる「docker build」を何度も実行するのは効率が良くありません。
そんな時はDocker composeという機能を利用します。

Docker composeとは

複数のコンテナをコードで管理します。
DB用、アプリケーション用など複数のコンテナからなるサービスを簡単に自動的に構築できます。

Docker composeを使う3-Step

  1. DockerFileを準備
  2. docker-compose.ymlを作り、コンテナの起動定義を書きます
  3. docker-compose up を実行(docker-compose.ymlに書かれたコンテナが起動します)

docker-compose.ymlの書き方

docker-compose.ymlを書く時、よく使うものなどをまとめてみました。

SERVICE : 
 hoge_name      : サービス名 
 image          : 使用するDockerImage
 links          : コンテナを他サービスへ繋げる
 container_name : コンテナ名を指定する(ない場合が自動的に命名される)
 enviroment     : 環境変数を追加
 volumes        : マウントするディレクトリ
 ports          : exportするディレクトリ(ホストポート=コンテナポート)

docker-compose コマンド

build    : docker-compose.ymlで書いたserviceで指定したビルドする
restart  : サービスの再起動をする
run      : サービスを起動
up       : サービスのimageをビルド・起動(build+run)
exec     : サービスに対してコマンド実行
logs     : サービスのログを出力

※コンテナIDを指定してdocker-conpose upすると、特定コンテナだけ起動することができます。

イメージ構築コンテナ構築コンテナ起動
build××
up
start××
run

PythonでABC151のA~Cを解く

$
0
0

はじめに

レートが少しあがりました。今回は、A,Bしか解けませんでした。

A問題

問題

考えたこと
入力されたアルファベットをasciiに変換して1を足して、戻してprintすればいい。
前に似たような問題を解いた時は、zが出てきたのでif文を使ったので今回も同じような問題だと思って遅れた。

c=ord((input()))print(chr(c+1))

B問題

問題

考えたこと
それぞれの教科の合計点数とMの差で分ける。

n,k,m=map(int,input().split())a=map(int,input().split())score=0foriina:score+=iif(score+k)/n<m:print(-1)elif(m*n-score)<0:print(0)else:print(m*n-score)

C問題

問題

考えたこと
時間内に解けませんでした。TLEになってしまった。
ACした問題のWAをカウントしない、WAした問題でもACしていなかったらカウントしないという条件をうまく実装できなかった。

importcollectionsn,m=map(int,input().split())p=[]foriinrange(m):p.append(input().split())del_l=[]forjinrange(m):ifp[j][1]=='AC':del_l.append(p[j][0])p[j][0]='pass'p[j][1]='pass'ifp[j][1]=='WA'andp[j][0]indel_l:p[j][0]='pass'p[j][1]='pass'pen=0forsinrange(m):ifp[s][1]=='WA'andp[s][0]indel_l:pen+=1print(len(collections.Counter(del_l)),pen)

自分の力では、なんでTLEになってるか分からなかったので、ryuki氏に教えてもらった。感謝

TLEしていた理由は、p[j][0] in del_lの$in$で計算量が増えているらしい。
そして、ACしているかはWA or ACなのでboolで書ける。という色々なアドバイスを貰ってACできました。

n,m=map(int,input().split())a=[input().split()foriinrange(m)]p=[int(i[0])foriina]q=[j[1]forjina]ac=0wa=0wa_c=[0]*nac_c=[False]*nfortinrange(m):ifac_c[p[t]-1]:continueifq[t]=='AC':ac+=1ac_c[p[t]-1]=Truewa+=wa_c[p[t]-1]else:wa_c[p[t]-1]+=1print(ac,wa)

まとめ

初めてTLEが原因でACできなかったので、これからは計算量を意識したように書けるようになりたい
学校のテストがやばい

【動画解説付き】Windows10からTeraTerm(テラターム)を使ってRaspberryPi(ラズパイ)を遠隔操作する方法

$
0
0

図1.png
どうも、プログラミングの鬼シヨツ鬼です。
この記事では「Raspberry Pi(ラズパイ)をWindowsから遠隔操作したいぜ」って人に向けてTeraTermという無料のソフトを使って遠隔操作する方法を説明します。
初心者の人がこの記事を見ただけで操作できるように、IPアドレスを調べる方法などの準備からコマンドの意味などできるだけ丁寧に説明しているので、ぜひ参考にしてみてください。

ちなみに、この記事は次の動画と同じ内容ですので、記事だけだと分かりづらい部分は、そちらもご活用ください。
(ボタンの位置や操作方法などがわかるので、初心者の方は動画のほうが分かりやすいかも)
YouTube:【電子工作】TeraTerm(テラターム)を使ってRaspberryPi(ラズパイ)を遠隔操作する方法

準備するもの

・Raspberry Pi 3 model B(OSインストール済のもの。OSのインストール方法はこちら
・遠隔操作されていることが分かる部品(本記事で使っているLチカのやり方はこちら)
・Windows10が入っているPC
・無線LAN環境(Wi-fi)
・フリーソフトへの感謝の気持ち

WindowsへTeraTermをインストールする

Windowsからラズパイを操作するためにTeraTermという無料のソフトをインストールします。インストールは簡単ですので、下記に紹介するページを参考にインストールしてください。
TeraTermの配布ページ
インストール方法

ラズパイのIPアドレスを調べる

IPアドレスとはネットワークに接続された機器に割り当てられた数値のことです。電話番号のようなイメージで、通信の際に必要に必要になるので調べます。

1.ラズパイをネットワークに繋げる

画面右上の電波のマークをクリックしてWi-Fiに接続してください。

2.IPアドレスを表示する

ラズパイのプロンプトを開いて以下のコマンドを打ち込みます。
ifconfig

3.IPアドレスを確認する

先ほどのコマンドによりネットワークの情報が画面に出力されるので、inetと書かれた部分にどのような値が記載されているか確認します。
(例)inet 192.168.1.27

WindowsのIPアドレスを調べる

1.Windowsをネットワークに繋げる

画面右下の電波マークをクリックしてラズパイと同じネットワークに接続されていることを確認してください。

2.IPアドレスを表示する

「コマンドプロンプト」を開いて、以下のコマンドを打ち込みます。
ipconfig

3.IPアドレスを確認する

先ほどのコマンドによりネットワークの情報が画面に出力されるので、IPv4アドレスと書かれた部分にどのような値が記載されているか確認します。
(例)IPv4 アドレス . . . . . . . . . . . .: 192.168.1.24

ラズパイの設定を行う

TeraTermでの遠隔操作を実現するためにラズパイの設定を行います。
次の設定を行うことで、遠隔操作を行うための通信が可能になります。(難しい言葉で言うと「SSH」というプロトコルによる通信を許可する設定)

1.設定画面を開く

次のコマンドを打ち込み設定画面を開きます。
sudo raspi-config

2.「5 Interfacing Opetions」を選択する

3.「P2 SSH」を選択する

4.「<はい>」を選択する

5.「<了解>」を選択する

6.「< Finish>」を選択する

TeraTermより接続を行う

1.WindowsでTeraTermを起動する

2.「新しい接続」の画面でホスト欄にラズパイのIPアドレスを入力して「OK」

初めての接続で「セキュリティ警告」が出た場合は「続行」を選択

3.「SSH認証」の画面でユーザ名とパスフレーズ欄にラズパイのアカウント情報を入力

コマンド入力の画面が表示されれば、成功です。
ラズパイをWindowsからコマンドベースで接続することができます。

まとめ

コマンドを打ったりする操作は、慣れないと時間がかかったり、打ち間違いで動かなかったりするかもしれないけど、だんだん慣れるよ。
得体の知れない怖いものではないから、落ち着いて使ってみてね。
最後まで、読んでくれてありがとう。
参考になったら「:thumbsup_tone2:」押してね。

エラー「Uncaught Error: Access to undeclared static property: 〜」の対処方法

$
0
0

発生状況

phpでクラスから別クラスの変数を取得しようとした際にエラー発生しました。

簡単な処理を以下に記載します。
別クラスから取得した変数を出力する処理で、yamadaと出力されれば成功です!

処理内容
$person=newGetInfo;$person->getName();classPostInfo{public$name='yamada'.PHP_EOL;}classGetInfo{publicfunctiongetName(){echoPostInfo::$name;}}
結果
Fatal error: Uncaught Error: Access to undeclared static property: PostInfo::$name

このように PostInfoクラスの $name変数が取り出せずにエラーになってしまいます。

解決方法

$name変数前に staticを記載することで解決しました。

example.php
$person=newGetInfo;$person->getName();classPostInfo{staticpublic$name='yamada'.PHP_EOL;}classGetInfo{publicfunctiongetName(){echoPostInfo::$name;}}
結果
yamada

このように成功しました!

staticプロパティについて

こちらのエラーをわかりやすく解説しているサイトがありましたので、学んだことを簡単に引用させていただきました。

staticキーワードを付けたプロパティには、外部から直接アクセス出来ます。
staticキーワードを付けて宣言したプロパティは、クラスをインスタンス化せずに使用できます。
staticを指定していないプロパティにstatic形式でアクセスすると、「PHP Fatal error: Access to undeclared static property」エラーが発生します。

staticプロパティの参考サイト
http://akutaka00.blog102.fc2.com/blog-entry-69.html

まとめ

つまり今回「staticを指定していないプロパティにstatic形式でアクセス」していたことが原因だったことがわかりました。
プログラミングの学習をしていると様々なエラーが発生し心折れそうになりますがきちんと原因がわかると、知識が増えた・理解が深まったとポジティブな方向に持っていけることが良いことだなと思いました。

これからも理解したことを少しずつアウトプットしていければと思います。

Web担当者として身につけるべきスキルとは?

$
0
0

明けましておめでとうございます!ついに2020年が始まりました!皆さん、お正月はいかがお過ごしでしょうか?新年の始まりにあたり、今年の目標/やりたいことを立てる方が多いと思います。Web担当者としての私、2020に何かの新しいWebスキルを勉強するのか、いろいろ考えてみました。

グーグルしたところ、まだ身につけていないスキルはたくさんあります。今日はWeb担当者として身につけるべきWebスキルをまとめて、皆さんに紹介したいと思います。

Web担当者とは?

そもそもWeb担当者はどんな人でしょうか?Web担当者とは、Webマーケター、Webマーケティング担当者とも呼ばれ、Webサイトへの集客に関する業務を担当します。Webサイトの登録者数やPVなどの増加を目的として、様々な施策を行い、アクセス解析の結果を分析し、施策の検証・改善を行います。

Web担当者の仕事内容は?

Web担当者はどんな仕事をするのでしょうか?まずはある求人情報に掲載した仕事内容を見てみましょう。
图片1.png

いかがでしょうか?Web制作、SEO対策、リサーチ、分析、企画提案などたくさんありますね。私自身もそれらの仕事をしています。もちろん、企業の大小や業種によって、仕事内容は変わりますが、必要とされる事はあります。

  • SEO(検索エンジン最適化)
  • Webサイト・ホームページ制作
  • リスティング広告・ディスプレイ広告
  • SNSの運用
  • メールマーケティング
  • コンテンツマーケティング

など、これらの手法を通じて、集客の目的を達成します。

Web担当者に必要なスキル

1.情報収集・分析のスキル

新しい製品・サービスを立ち上げるとき、必ず市場分析、競合分析を行います。分析を行う前、たくさんの情報を収集しなければなりません。情報収集を効率的に進めるために、情報収集力は必須なスキルです。それに、収集した情報を整理して、目的に応じて分析、解析できるスキルを身につけておくのも重要です。

利用可能リソース:

Web情報を収集するには、Octoparseというスクレイピングツールがオススメです。プログラミングする必要がなく、クリックするだけで素早くデータを抽出できます。

スクレイピングツールにオススメの10選

データ分析・可視化なら、Tableauがオススメです。Tableauは非常に強力で柔軟な分析プラットフォームで、プログラミングなどの専門知識・スキル不要で複数のユーザのコラボレーションも可能です。

データ分析にオススメのツール31選

2.サイト企画/制作/デザイン/更新のスキル

自社で新しいサービスを行なう場合、新しくホームページを作らなければいけないことがあります。以前作ったホームページを更新や修正などことも時々ありますね。ホームページをイチから作るには、企画書作成、レイアウト作成、ドメイン・サーバー準備、サイトマップ作成など、様々なノウハウや作業が必要になります。可能であれば、プログ(THML・CSSの知識)やデザインのスキルを身につけておきましょう。

利用可能リソース:

今では、Instapage、WiXやStrikinglyなど、LPを作れるツールがたくさんあります。テンプレートが豊富で、PowerPointのように簡単でおしゃれなページを作れます。

3.SEO対策のスキル

新しいサイトを立ち上げた後、検索順位を上げるために、サイトのリンクやコンテンツを最適化しなければなりません。それはSEO対策です。SEOを行うことで、検索結果で上位表示できれば、より多くの検索ユーザーにリーチすることができます。SEOでは、キーワード、被リンク、コンテンツが重要で、最適化するのに一定の需要がずっと続くことになります。ですから、成果を出すのは時間かかるし、難しいことです。

利用可能なリソース:

Moz、SEMrush、AhrefsなどSEOに役に立つツールもたくさんあります。それらのツールを利用して、キーワード、リンクの分析を簡単にすることができます。

4.良質なコンテンツを書くスキル

質が良いコンテンツとは、検索ユーザーが喜び記事のことです。さっき言ったSEO対策では、コンテンツ作りは重要です。コンテンツマーケティングの一環として、多くの企業はブロクを立ち上げました。ですから、相手が読みやすく、分かりやすい記事を書くノウハウが必要になります。

利用可能なリソース:

WEB上では多くのノウハウ記事がありますので、ここでは一本をオススメします。

良質なコンテンツとは:良い記事を書くための14のポイント

5.SNS運用のスキル

SNSの普及に従って、SNSマーケティングを行う企業も増えました。多くの人に自社の製品やサービスを見てもらえる機会を増加するために、どれだけ拡散できるかが重要です。ですからフォロワー、「いいね」や「シェア」を増やすのもWeb担当者の仕事となります。そのため、FacebookやTwitter、Instagramなど、それぞれサービスの特徴とできることを把握する上で、ふさわしいコンテンツを配信することも重要です。

利用可能なリソース:

SNSでマーケティング活動を行う際に、SNS分析はとても有用です。より正確でタイムリーな情報を入手するには、ツールを活用するのが効率的です。無料ツールなら、HootsuiteとSocial Mentionがオススメです。

まとめ

いかがでしたか?以上は、私自身の仕事を元にして、仕事内容とそれに必要なスキルと利用可能なソースの紹介でした。Web担当者になった1年以上の私はスキル不足を感じた時が多いです。今年もWeb担当者の必要なスキルの向上に日々努力をしたいと思います。もしその中では、皆さんが身につけたいスキルがあれば嬉しいです。

元記事:https://www.octoparse.jp/blog/web-marketing-skills-you-must-have/

Laravel の環境構築でつまづいた

$
0
0

はじめに

macOSでの話です。

Laravel公式のインストールサイト(日本語訳)通りに進んでいたが、

composer global require laravel/installer

の入力が上手くいかず、下記のエラーが出て数時間つまづいたので誰かの役に立ったらと思い書きます。

Your requirements could not be resolved to an installable set of packages.

Problem 1

Installation request for laravel/installer ^2.1 -> satisfiable by laravel/installer[v2.1.0].
laravel/installer v2.1.0 requires ext-zip * -> the requested PHP extension zip is missing from your system.

どのようにエラーを取ったか

同じ症状で悩んでいる人が teratailにいたので質問していたのでその回答通りにやってみました。(pecl install zip は peclコマンドが使えなかったのでスルーした。)

brew で phpを入れるのは、https://laracasts.com/series/laravel-6-from-scratch/episodes/1の動画サイトを1~3まで見ればわかります。

同じ症状で悩んでいる人書いてくれている

brew link php

を実行する。

そうすると、

Linking /usr/local/Cellar/php/7.4.1... 
Error: Could not symlink bin/pear
Target /usr/local/bin/pear
already exists. You may want to remove it:
  rm '/usr/local/bin/pear'

To force the link and overwrite all conflicting files:
  brew link --overwrite php

To list all files that would be deleted:
  brew link --overwrite --dry-run php

というエラーが出てくるので

 brew link --overwrite php

を実行、そして再起動してcomposer global require laravel/installer
を実行して完了です。

補足

・・・composerのダウンロード
https://kohkimakimoto.github.io/getcomposer.org_doc_jp/doc/00-intro.html
これを手順に

composerの使い方。composer.jsonやcomposer.pharを使わなきゃいけなくなった時このサイトを勝つよすると良い。https://laboradian.com/php-composer/

初心者向けVue.js × Railsでのアプリ作成(ToDoリスト編)

$
0
0

Vue.jsとRailsでアプリを作る記事が少ない!!!(泣)

私はRubyエンジニアで普段Railsを使って仕事をしているのですが、業務でVue.jsをメンテする必要があるため、空いた時間に勉強をしています。
ですが、Vue.jsとRailsでアプリを作る記事が少ない印象をうけ、勉強するのに少し不便でした。

Vue.js × Railsの記事が少ないと言っても探せばそれなりに見つかるのですが、私みたいなフロントエンドの事よくわかってない人間には、理解するのに時間がかかったりします。

ネットで記事をあさったり、そもそもJavaScriptが良くわかってないので、JavaScriptから勉強し直してみたり、Vue.js × Railsでアプリを作るだけにしては非常に遠回りしてしまいました。

この記事について

この記事は、私みたいにVue.js × Railsのアプリ作成で遠回りな勉強をしている人をなくす事を目的としています。

初心者向けにVue.js × Railsでアプリを作る記事を書いて、実装のイメージを掴んでもらえれば、私のような遠回りはなくなるはず。。。1度小さいアプリを作ってしまえば、理解度がグッと上がり、他の記事も読みやすくなるのできっと大丈夫!

また、この記事は私がRubyエンジニアなので、Rubyエンジニアから見てVue.jsをどう実装しようかという視点になってます。

この記事を読む対象のレベル

Vue.js × Rails両方ともチュートリアルをやったくらいのレベルを対象としています。
Vue.jsもRailsもだいたいこんな感じというのがわかっていれば作れると思います。

どんなものを作るか? Vue.js × Railsそれぞれの役割とは?

この記事では、Vue.js × RailsでミニマムなToDoリストアプリを作っていきます。定番ですね。

私はToDoアプリを作ろうとした時、Vue.js × Railsがそれぞれどんな役割をしているのかよくわからず、実装のイメージが掴めなかったのですが、いろんな記事を読んだ結果、RailsでAPIを作り、APIへのリクエストと返ってきたデータの表示をVue.jsで行うというのが多かったです。今回もこの役割分担でアプリを作っていきます。

スクリーンショット 2020-01-01 3.16.36.png

WEB業界での経験が浅い、もしくはこれからWEB業界を目指す方はAPIのイメージがつかみにくいかもしれませんが、一言で言うとURLのリクエストを受けたら、URLに応じたデータを返すものです。

この役割のイメージが分かればRailsの部分をFirebaseに置き換えようとか、Vue.jsをReactに置き換えようとか応用が効くような気がします。

(注)私は現時点でFirebaseもReactも詳しくないので応用が効く気がするというだけ。。。詳しい方がいたら教えて下さると助かります。

完成後のイメージ

スクリーンショット 2020-01-13 14.31.32.png

  • テキストボックスにタスクを入力して追加ボタンを押すとリストに追加されて表示される。
  • チェックボックスをチェックすると取り消し線が引かれる
  • 削除ボタンを押すと削除

実際に作ってみよう

RailsでAPIを作る

では、実際にアプリを作ってきます。まずはRailsでAPIを作るところから。
以下のコマンドで、Railsアプリを作ります。--webpack=vueをするとwebpackとVue.jsのインストールが出来ます。

rails new todo_list --webpack=vue

「webpackとかまた新しい言葉出すなよ!」って方は以下のリンクを見てください!
【5分でなんとなく理解!】Webpack入門
Webpackとは、js、cssなどフロントで作るファイルをバンドリングしてくれるものです。

ToDoリストを表示するHome画面を作成

ToDoを表示するHome画面を作るため、コントローラーを作成します。

rails g controller home

作成したコントローラーにindexだけ追加しておきましょう。

/app/controllers/home_controller.rb
classHomeController<ApplicationControllerdefindexendend

viewからVue.jsが呼び出せるか試しますために追加します。

/app/views/home/index.erb
<%= javascript_pack_tag 'hello_vue' %>

routes.rbに以下を追加。

/config/routes.rb
Rails.application.routes.drawdorootto: 'home#index'end

rails sしてlocalhost:3000にアクセスします。
以下のような画面が表示されてればOKです。

スクリーンショット 2020-01-02 23.00.33.png

ちなみにVue.jsを変更したら

bin/webpackで更新してあげる必要があります。(重要)

APIの処理を作る

まずは、ToDoリストにタスクを追加するためにモデルを作っていきます。

railsgeneratemodelTaskname:stringis_done:boolean

ルーティングに以下を追加します。
表示用のhomeとデータを返すAPI用のapi::tasksを追加。

/config/routes.rb
Rails.application.routes.drawdorootto: 'home#index'namespace:api,format: 'json'doresources:tasks,only: [:index,:create,:destroy,:update]endend

リクエストを受けたらデータを返すため、Tasksのコントローラーを作ります。

rails g controller Api::Tasks

マイグレーションします。

rails db:migrate

コントローラーの中身は以下のような感じ。

/app/controllers/api/tasks_controller.rb
moduleApiclassTasksController<ApplicationControllerskip_before_action:verify_authenticity_tokendefindex@tasks=Task.order('created_at DESC')enddefcreate@task=Task.new(task_params)if@task.saverenderjson: @task,status: :createdelserenderjson: @task.errors,status: :unprocessable_entityendenddefdestroyTask.find(params[:id]).destroy!enddefupdateTask.find(params[:id]).toggle!(:is_done)endprivatedeftask_paramsparams.require(:task).permit(:name,:is_done)endendend

APIを返す時は、htmlではなくJSONで返してあげたいので以下を追加します。
自分は実際にWEB業界に入るまでJSONに馴染みがなかったのですが、以下の形で書きます。

/app/views/api/tasks/index.json.jbuilder
json.set!:tasksdojson.array!@tasksdo|task|json.extract!task,:id,:name,:is_done,:created_at,:updated_atendend

APIの動作確認

DBにデータを入れて確認してみましょう。
コンソールを立ち上げます。

rails c

Taskモデルにデータを追加してみましょう。

Task.create(name: 'テスト用タスク')

もう一度サーバー立ち上げ

rails s

以下のアドレスで追加したデータがJSONでデータが返ればOK。

http://localhost:3000/api/tasks.json

スクリーンショット 2020-01-02 23.40.55.png

Vue.jsでフロント作成

コンポーネント

コンポーネントとは、名前付きの再利用可能な Vue インスタンスです。
再利用出来そうなパーツごとにコンポーネントを区切って実装するのが、どうやら重要らしい。
今回、最小限の構成でアプリを構成するためコンポーネントについては省こうか迷ったのですが、重要なので組み込みます。

わかりやすいイメージで言うと、ヘッダー、フッター、サイドナビ等は色んなページで再利用するのでコンポーネントを分けて実装するといった感じでしょうか。
今回もヘッダーとToDoリストを表示するボディ部分でコンポーネントを分けて実装したいと思います。

では、まず以下のようにToDoリスト表示部分を作って下さい。

/app/views/home/index.erb
<div id="app">
  <navbar></navbar>
</div>

<%= javascript_pack_tag 'todo' %> # todoに変更する事に注意

はヘッダーのコンポーネントを表示します。
<%= javascript_pack_tag 'todo' %>でapp/javascript/packs配下のtodo.jsファイルを読み込みます。

ヘッダーの作成

まずはヘッダー部分のコンポーネントを用意します。

/app/javascript/packs/components/header.vue
<template><h1>ToDoリスト</h1></template>

次に/app/views/home/index.erbから呼び出されているapp/javascript/packs/todo.jsにコンポーネントの設定をしていきます。
以下のように書くとapp/views/home/index.erb<navbar></navbar>/app/javascript/packs/components/header.vueをマウントして表示してくれるようです。

/app/javascript/packs/todo.js
importVuefrom'vue/dist/vue.esm.js'importHeaderfrom'./components/header.vue'varapp=newVue({el:'#app',components:{'navbar':Header}});

Vue.jsの読み込み設定

webpackはVue.jsの読み込み方がわからないので以下を実行します(重要)

/config/loaders/stylus.js
module.exports={test:/\.styl$/,use:['style-loader','css-loader','stylus-loader']}
/config/webpack/environment.js
const{environment}=require('@rails/webpacker')const{VueLoaderPlugin}=require('vue-loader')constvue=require('./loaders/vue')conststylus=require('../loaders/stylus')// 作ったstylusをrequireするenvironment.plugins.prepend('VueLoaderPlugin',newVueLoaderPlugin())environment.loaders.prepend('vue',vue)module.exports=environmentenvironment.loaders.prepend('stylus',stylus)// 作ったstylusをロード

webpackを再読み込みしてからサーバーを再起動しましょう(重要)

bin/webpack
rails s

rails sしてヘッダーが表示されて入ればOKです

スクリーンショット 2020-01-13 14.10.00.png

ToDoリストを表示するボディ部分

axiosというライブラリを使って、フロントエンドからHTTPリクエストをします。
以下のコマンドでyarnでaxiosを追加して下さい。

yarn add axios

ToDoアプリのメイン部分の実装です。解説は後ほど詳しく説明します。

/app/javascript/packs/components/index.vue
<template><div><div><inputv-model="newTask"placeholder="to doを追加して下さい"><divv-on:click="createTask"><i>追加</i></div></div><ul><liv-for="(task, index) in tasks"><inputtype="checkbox"v-model="task.is_done"v-on:click="update(task.id, index)"><spanv-bind:class="{done: task.is_done}">{{task.name}}</span><buttonv-on:click="deleteTask(task.id, index)">削除</button></li></ul></div></template><script>importaxiosfrom'axios';exportdefault{data:function(){return{tasks:[],newTask:''}},mounted:function(){this.fetchTasks();},methods:{fetchTasks:function(){axios.get('/api/tasks').then((response)=>{for(leti=0;i<response.data.tasks.length;i++){this.tasks.push(response.data.tasks[i]);}},(error)=>{console.log(error,response);});},createTask:function(){if(this.newTask=='')return;axios.post('/api/tasks',{task:{name:this.newTask}}).then((response)=>{this.tasks.unshift(response.data);this.newTask='';},(error)=>{console.log(error,response);});},deleteTask:function(task_id,index){axios.delete('/api/tasks/'+task_id).then((response)=>{this.tasks.splice(index,1);},(error)=>{console.log(error,response);});},update:function(task_id){axios.put('/api/tasks/'+task_id).then((response)=>{},(error)=>{console.log(error);});}}}</script>

作ったindex.vueを読み込んであげましょう。

/app/javascript/packs/todo.js
importVuefrom'vue/dist/vue.esm.js'importHeaderfrom'./components/header.vue'importIndexfrom'./components/index.vue'// 追加varapp=newVue({el:'#app',components:{'navbar':Header,'contents':Index// 追加}});

ヘッダーのしたにindex.vueを表示するため、<navbar></navbar>の下に<contents></contents>を追加します。

/app/views/home/index.erb
<div id="app">
  <navbar></navbar>
  <contents></contents>
</div>

<%= javascript_pack_tag 'todo' %>

チェックボックスが押されたら取り消し線を表示するためcss追加。

app/assets/stylesheets/home.scss
#appli>span.done{text-decoration:line-through;}

rails sして動くか確認してみて下さい。
実際にToDoリストを追加してみましょう。

スクリーンショット 2020-01-13 16.31.25.png

ToDoアプリのコード解説メモ

学習し始めだと、Vue.jsのどの行が何をやっているのかわからない事があったのでメモ付きのコードをのせます。

まずはtemplate

<template><div><div><inputv-model="newTask"placeholder="to doを追加して下さい">
      # 追加ボタンを押すとcreateTaskを実行する
      <divv-on:click="createTask"><i>追加</i></div></div><ul>
      # fetchしたタスク一覧(tasks)から一つずつtaskとindexを取り出す処理
      <liv-for="(task, index) in tasks">
        # チェックボックスが押されたらv-modelのis_doneを変更して取り消し線を引く
        # updateでAPI側のデータも更新
        <inputtype="checkbox"v-model="task.is_done"v-on:click="update(task.id, index)">
        # タスクの表示。v-bind:classでis_doneを参照して取り消し線が引かれるかどうか判定
        <spanv-bind:class="{done: task.is_done}">{{ task.name }}</span>
        # 削除ボタンを押すとdeleteTaskを実行
        <buttonv-on:click="deleteTask(task.id, index)">削除</button></li></ul></div></template>
<script>importaxiosfrom'axios';exportdefault{data:function(){return{tasks:[],newTask:''}},mounted:function(){this.fetchTasks();},methods:{// APIからタスク一覧を取得fetchTasks:function(){axios.get('/api/tasks').then((response)=>{for(leti=0;i<response.data.tasks.length;i++){this.tasks.push(response.data.tasks[i]);}},(error)=>{console.log(error,response);});},// 新しいタスク作成createTask:function(){// テキストボックスが空の場合はreturnして終了if(this.newTask=='')return;// apiへ追加リクエストaxios.post('/api/tasks',{task:{name:this.newTask}}).then((response)=>{// unshiftで現在のtasksの先頭にタスクを追加this.tasks.unshift(response.data);// 追加したらテキストボックスを空にするthis.newTask='';},(error)=>{console.log(error,response);});},// タスク削除deleteTask:function(task_id,index){// apiへ削除リクエストaxios.delete('/api/tasks/'+task_id).then((response)=>{this.tasks.splice(index,1);},(error)=>{console.log(error,response);});},// タスク更新。今回はis_doneのみ更新だが、タスク名とか色々更新するようカスタムしても良いと思うupdate:function(task_id){// apiへ更新リクエストaxios.put('/api/tasks/'+task_id).then((response)=>{},(error)=>{console.log(error);});}}}</script>

まとめ

小さいアプリをとりあえず作ってみると理解度がかなり深まると思うので、今回のようなToDoアプリを作ってみると良いと思います。
かけ足で記事を書いてしまったのですが、これで私みたいな人間を救えるのか...???
今後も私のようにVue.js × Railsでアプリを作りたい人向けに記事を改訂して行きたいです。

コンテナの起動とログインについて調べてみたメモ

$
0
0

はじめに

前回はdockerfileを作成してイメージ作成からapacheの起動までをやってみました。
その際いろいろ調べていて、起動やコンテナへのアクセスで不明点が結構あったので詳しく調べてみた内容をまとめたいと思います。

docker runしたのに起動しない!?

ubuntuのイメージを使ってみます。
イメージ名を指定してrunします。

$ docker run ubuntu
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

確認してみると、起動していません。
あれ?
-aオプションですべてのコンテナを確認してみます。
ありますね...

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
d1d595f53e6b        ubuntu              "/bin/bash"         14 seconds ago      Exited (0) 13 seconds ago                       funny_wozniak

STATUS欄がExted(0)...???
docker startしてみます。

$ docker start d1d595f53e6b
d1d595f53e6b
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

う・ご・か・な・い!!

動かない:pray_tone1:
なぜでしょうか。

docker run -it

いろいろ調べてみると、runするタイミングで-itオプションを付与していないのが原因らしい。
なにこれ。

$ docker run -d -it ubuntu
4af17d141f3e5f037539a49ee367785abda784d8cff501644820de7d1fdb9009

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
4af17d141f3e        ubuntu              "/bin/bash"         4 seconds ago       Up 4 seconds                            boring_hamilton

-i →標準入力を開いたままにする
-t →擬似ttyに接続。
-dはコンテナを作成後、コンテナプロセスの標準入出力から抜けておく感じのオプション。なのでいったん置いときます。

これをつけると問題なく起動して停止、起動も問題なくできました。

ログイン方法で挙動が違う。attachとexec

以下の記事を参考にさせていただきました。
@RyoMa_0923さん
Dockerコンテナ内で操作 attachとexecの違い

docker attach

コンテナ上で起動しているPID=1の標準入出力(STDIN/STDOUT)に接続するらしい。
attachはコンテナで立ち上がってるPID=1の標準入出力に接続する。
よってexitするとコンテナもろとも停止する模様。(恐ろしいですね)

docker exec

docker exec はコンテナ内で新たなプロセスを実行する。
よってexitを実行してもコンテナ自体は停止しないみたいです。

いつもの癖でexitで抜ける人はexecを使うほうがいいかもしれないなって思いました。

前回記事ではまったコンテナ起動してもapachのテストページが表示されな問題

dockerfileから作成したapacheのイメージを使用してコンテナを作成して起動します。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
apache              latest              b5f41134a3a1        5 minutes ago       599MB
ubuntu              latest              549b9b86cb8d        3 weeks ago         64.2MB

$ docker run -d -p 80:80 apache /sbin/init
1fdab23f462e139188cf552d17ae88d8c4137f12e9ab9b4d50393264ec225ce8

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                NAMES
1fdab23f462e        apache              "/sbin/init"        9 seconds ago       Up 9 seconds        0.0.0.0:80->80/tcp   dazzling_pare

ちゃんと起動してますね。
実際にアクセスしてみますが、このサイトにアクセスできません。と例の悲しい表示がでます。

調べてでてきた謎のオプションを付与して再度コンテナを作成、起動します。

$ docker run --privileged -d -p 80:80 apache /sbin/init
8d83edca0037bf9a11c097084e2940a92dc3c8473d5c99c7a5e5faf1439bdbdb

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                NAMES
8d83edca0037        apache              "/sbin/init"        6 seconds ago       Up 6 seconds        0.0.0.0:80->80/tcp   admiring_varahamihira

起動までは問題なくできました。
ブラウザからアクセスしてみると、テストページが表示されました!!
変わったことといえば謎の「--privileged」これなに?
調べてみました。

docker run --privileged

dockerコンテナはデフォルトでは権限がないため、コンテナ中のデーモンを動かすことができないみたいです。
そこで使用するのが--privileged
これを使うとdockerはホスト上のすべてのデバイスに対して接続可能となるようです。

まとめ

  • docker run して起動せず、docker startもできない場合はオプション問題なのかも。
  • attachとexecは同じログインでも挙動が全く別物
  • コンテナはデフォルトではデーモンを動かすことができない。

以上です。調べながらもやや不明な点があったりもしたので間違ってるところなどあったらご指摘いただけると幸いです。


Pythonで自然言語処理を行うための環境構築

$
0
0

自分用のメモですが、ご参考になれば。

1. 環境構築後のバージョン

  • Python 3.6.5
  • Homebrew 2.2.2
  • pyenv 1.2.13
  • Numpy 1.15.0
  • SciPy 1.1.0
  • Pandas 0.23.3
  • Scikit-learn 0.19.2
  • MeCab-python3 0.996.2
  • neologdn 0.3.2
  • TensorFlow 1.14.0
  • Keras 2.1.6
  • Gensim 3.5.0
  • Hyperopt 0.1.1
  • Matplotlib 3.1.2

2-1. 【Macの場合】 HomebrewによるPythonのインストール

「Homebrew」をインストール
  ↓
「Homebrew」を用いて「pyenv」をインストール
  ↓
「pyenv」を用いて「Python」をインストール

(1) Homebrewのインストール

Homebrewの公式ドキュメント:https://brew.sh/index_ja

$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

途中でreturn/enterキーの押下を求められるので、指示に従って押下。
また、途中でPCにログインする際のPWの入力も求められる。指示に従ってパスワードを入力し(ただし、画面上は入力内容が表示されない)、return/enterキーを押下。

インストール完了後、下記コマンドでバージョンを確認。

$ brew -v
Homebrew 2.2.2
Homebrew/homebrew-core (git revision d7286b; last commit 2020-01-01)

(2) pyenvのインストール

Homebrewを用いてpyenv(同じ端末内で複数バージョンのPython環境を構築・管理するためのツール)をインストール。
pyenvのGitHubリポジトリ:https://github.com/pyenv

$ brew install pyenv

インストール完了後、下記コマンドでバージョンを確認。

$ pyenv -v
pyenv 1.2.13

pyenvの設定:.bash_profileに設定用のコードを追加。
以下のコマンドを一行ずつ実行(何も起きていないように見えるが、それでOK)。

$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
$ source ~/.bash_profile

(3) Pythonのインストール

pyenvを用いてPythonをインストールする。

まず、下記コマンドを実行してpyenvでインストール可能なPythonのバージョンを確認する。

$ pyenv install --list
# 実行結果のうち、先頭にアルファベットのついていないものがインストール可能なPythonのバージョン
 2.1.3
 2.2.3
 2.3.7
 ・・・
 3.6.5
 3.6.6
 3.6.7
 ・・・

このうち、比較的新しいPython3.6.5をインストール。

$ pyenv install 3.6.5

インストール完了後、下記コマンドでインストールされたPythonのバージョンを確認。

$ pyenv versions
system
* 3.6.5 (set by /Users/hoge/.pyenv/version)

以下のコマンドを実行し、上記でインストールしたバージョンのPythonを使用するように設定を変更(この処理をしないと、Macに標準でインストールされているバージョンのPythonが継続使用される)。

$ pyenv global 3.6.5

下記コマンドを実行して、使用中のPythonのバージョンを確認。

$ Python --version
Python 3.6.5

2-2. 【Windowsの場合】 公式サイトからPythonをインストール

下記サイトからRelease version = Python 3.6.5を選択してダウンロードし、指示に従ってインストール。

Python3.6.5のダウンロードページ(公式):https://www.python.org/downloads/release/python-365/

ダウンロードするファイルは、Windows用であればどれでもよいと思われるが、特にこだわりがなければWindows x86-64 executable installerを選択。

ダウンロードしたファイルを開くと、表示された画面の一番下にAdd Python 3.6 to PATHというチェックボックスがある。これをチェックしてInstall Nowをクリック。

インストール完了後、コマンドプロンプト(いわゆる黒画面)で下記コマンドを実行し、バージョンを確認。

$ Python --version
Python 3.6.5

※ 「‘python’ は . . . 認識されていません。」と表示される場合、Pythonをインストールする際にAdd Python 3.6 to PATHにチェックを入れ忘れた可能性がある。この場合は最初にダウンロードしたファイルを再度開き、Uninstallをクリックしていったん削除し、再度インストールする。

※ Visual Studio Codeからコマンドプロンプトを使用する方法
Terminal > Integrated > Automation Shell: Windowssettings.jsonに以下の一行を追加。

settings.json
{"terminal.integrated.shell.windows":"C:\\WINDOWS\\System32\\cmd.exe"}

3. 必要なライブラリのインストール【Mac/windows共通】

(1) Numpyのインストール

$ pip install numpy===1.15.0

(2) SciPyのインストール

$ pip install scipy===1.1.0

(3) Pandasのインストール

$ pip install pandas===0.23.3

(4) Scikit-Learnのインストール

$ pip install scikit-learn===0.19.2

(5) MeCabのインストール

$ pip install mecab-python3===0.996.2

※Windowsの場合
参考:https://qiita.com/menon/items/f041b7c46543f38f78f7

$ pip install ipykernel
$ pip install mecab-python-windows

(6) neplogdnのインストール

$ pip install neologdn===0.3.2
$ git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git
$ cd mecab-ipadic-neologd
$ ./bin/install-mecab-ipadic-neologd -n -a

[install-mecab-ipadic-NEologd] : Start..
[install-mecab-ipadic-NEologd] : Check the existance of libraries
・・・
[test-mecab-ipadic-NEologd] : Please check difference between default system dictionary and mecab-ipadic-NEologd

default system dictionary        |     mecab-ipadic-NEologd
佐野 史郎                         |     佐野史郎 
警察 学校                         |     警察学校 
みぞ れ                           |     みぞれ 
ころ ん                           |     ころん 
たら こ ー ち                      |    たら こーち 
一 乗 谷 城                      |     一乗谷城 
ら じ ら ー                        |    らじらー 
林 遣 都                          |    林遣都 
マツコ 会議                        |    マツコ会議 
アド 街                            |     アド街 

[test-mecab-ipadic-NEologd] : Finish..
[install-mecab-ipadic-NEologd] : Please check the list of differences in the upper part.
[install-mecab-ipadic-NEologd] : Do you want to install mecab-ipadic-NEologd? Type yes or no.
yes # ここで「yesを入力」
[install-mecab-ipadic-NEologd] : OK. Let's install mecab-ipadic-NEologd.
[install-mecab-ipadic-NEologd] : Start..
[install-mecab-ipadic-NEologd] : /usr/local/lib/mecab/dic is current user's directory
・・・
[install-mecab-ipadic-NEologd] : Install completed.
[install-mecab-ipadic-NEologd] : When you use MeCab, you can set '/usr/local/lib/mecab/dic/mecab-ipadic-neologd' as a value of '-d' option of MeCab.
[install-mecab-ipadic-NEologd] : Usage of mecab-ipadic-NEologd is here.
Usage:
    $ mecab -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd ...

[install-mecab-ipadic-NEologd] : Finish..
[install-mecab-ipadic-NEologd] : Finish..

# 以上でインストール完了。以下のコマンドでmecabで使用する辞書にNEologdを指定
$ mecab -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd
近年は自然言語処理の研究が盛んです
近年    名詞,副詞可能,*,*,*,*,近年,キンネン,キンネン
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
自然言語処理    名詞,固有名詞,一般,*,*,*,自然言語処理,シゼンゲンゴショリ,シゼンゲンゴショリ
の      助詞,連体化,*,*,*,*,の,ノ,ノ
研究    名詞,サ変接続,*,*,*,*,研究,ケンキュウ,ケンキュー
が      助詞,格助詞,一般,*,*,*,が,ガ,ガ
盛ん    名詞,形容動詞語幹,*,*,*,*,盛ん,サカン,サカン
です    助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
EOS
# デフォルトのIPAdicでは「自然」「言語」「処理」の3語に分かれてしまうが、NEologdは一語で処理。
# Pythonのコードから利用する場合は、MeCab.Tagger()の引数に'-d /usr/local/lib/mecab/dic/mecab-ipadic-neologd'を指定。

※Windowsの場合
参考:https://www.pytry3g.com/entry/MeCab-NEologd-Windows

(7) TensorFlowのインストール

$ pip install tensorflow===1.14.0

(8) Kerasのインストール

$ pip install Keras===2.1.6

(9) Gensimのインストール

$ pip install gensim===3.5.0

(10) Hyperoptのインストール

$ pip install hyperopt===0.1.1

(11) Matplotlibのインストール

$ pip install matplotlib===3.1.2

インストール済みのライブラリを確認

以下のコマンドで、インストール済みのライブラリとそのバージョンを確認。

$ pip list

捕捉: pipのSSLエラー

pipコマンドで各ライブラリをインストールしようとする際、以下のようなエラーメッセージが出ることがある。

# pip3 install hoge
pip is configured with locations that require TLS/SSL, however the ssl module in Python is not available.
Collecting hoge
  Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError("Can't connect to HTTPS URL because the SSL module is not available.",)': /simple/hoge/
  Retrying (Retry(total=3, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError("Can't connect to HTTPS URL because the SSL module is not available.",)': /simple/hoge/
  Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError("Can't connect to HTTPS URL because the SSL module is not available.",)': /simple/hoge/
  Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError("Can't connect to HTTPS URL because the SSL module is not available.",)': /simple/hoge/
  Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError("Can't connect to HTTPS URL because the SSL module is not available.",)': /simple/hoge/
  Could not fetch URL https://hoge/hoge/: There was a problem confirming the ssl certificate: HTTPSConnectionPool(host='hoge', port=hoge): Max retries exceeded with url: /hoge/hoge/ (Caused by SSLError("Can't connect to HTTPS URL because the SSL module is not available.",)) - skipping
  Could not find a version that satisfies the requirement hoge (from versions: )
No matching distribution found for hoge

これは、opensslがインストールされていないことが原因。
この場合、以下のようにopensslをインストールした上で再度Pythonをインストールするとうまくいく(Macの場合のみ。Windowsは未確認)。

# 1. opensslをインストール。
$ brew install openssl

# 2. 優先適用されるSSLをLibreSSLから上記でインストールしたOpenSSLに変更。
$ echo 'export PATH="/usr/local/opt/openssl/bin:$PATH"' >> ~/.bash_profile

# 3. ターミナルを一旦終了し再起動する。その後、バージョンを確認しOpneSSLが表示されたらOK。
$ openssl version
OpenSSL 1.1.1d  10 Sep 2019

# 4. Pythonを再インストールする。
$ pyenv install 3.6.5

【動画解説付き】初心者のためのWebAssembly開発環境構築ガイド

$
0
0

アイキャッチ2.png
どうも、プログラミングの鬼シヨツ鬼です。
この記事では「WebAssemblyとかいうやつの開発環境構築したいぜ」って人に向けてWindows機に仮想マシンを作って開発環境構築する方法を説明しています。
初心者の人がこの記事を見ただけで操作できるように、仮想マシンの準備から説明しますので、ぜひ参考にしてみてください。
(「仮想マシンの作り方は分かるよ!」って方は「開発環境構築」の項目から読むのでOK)

ちなみに、この記事は次の動画と同じ内容ですので、記事だけだと分かりづらい部分は、そちらもご活用ください。
(ボタンの位置や操作方法などがわかるので、初心者の方は動画のほうが分かりやすいかも)
YouTube:初心者のためのWebAssembly開発環境構築ガイド

準備するもの

・インターネット接続されたWindows10のPC
・未知への好奇心

VirtualBoxのインストール

Windows上に仮想マシンを作るためにVirtualBoxというソフトをインストールします。
過去の僕の動画の「VirtualBoxのインストール」の部分を参考にインストールを行ってください。
(動画の中ではCentOSのインストールも行っていますが、この部分は不要です)

Ubuntuのインストールメディア準備

こちらのサイトよりUbuntuのisoイメージをダウンロードします。

VirtualBoxに新規のマシンを追加する

VirtualBox上に新規の仮想マシンを作ります。
設定内容は自分のPCのスペックに応じて適宜決めてください。
僕が設定した内容は、動画の4:06~を参考にしてください。

Ubuntuのインストール

以下の手順で仮想マシンにOSを入れて立ち上げます。
1.追加した仮想マシンのストレージより、[光学ドライブ]空となっている項目をクリックし、「ディスクイメージを選択」をクリックする
2.Ubuntuのインストールメディアを選択する
3.「起動」ボタンを押す

Ubuntuのインストール設定

Ubuntuのインストール設定を以下の操作で行います。
1.「Ubuntuをインストール」を選択
2.「Ubuntuのインストール準備」の画面で「Ubuntuのインストール中にアップデートをダウンロードする」と「グラフィックス、Wi-fi機器、Flash~(略)」の項目にチェックを入れ、「続ける」を押す
3.「インストールの種類」の画面で「ディスクを削除してUbuntuをインストール」にチェックを入れ「インストール」を押す

*この後、キーボードの設定やアカウントの設定を行いますが、特に難しい項目はないため、ここでの説明は省略します。

開発環境構築

環境確認

仮想マシン上から次の事項を確認してください。(確認の方法は動画中で紹介)
・インターネットに接続できること
・Pythonがインストール済みのこと
・gccコンパイラが入っていること
・gitが入っていること

Emscriptenをインストールする

1.次のコマンドを実行する
git clone http://github.com/emscripten-core/emsdk.git
2.1の作業で作成される「emsdk」ディレクトリに移動し、次のコマンドを実行する
./emsdk install latest
3.次のコマンドを実行する
./emsdk activate latest
4.次のコマンドを実行する
source ./emsdk_env.sh --build=Release

コーディングと動作確認

サンプルプログラム作成

お決まりの「Hello world」を表示させるプログラムを作ります。

hello.c
#include <stdio.h>
intmain(intargc,char**argv){printf("Hello World!!\n");}

コンパイル

サンプルプログラムを作成したディレクトリに移動して、次のコマンドを実行します。
emcc hello.c hello.html

動作確認

下記コマンドでブラウザを開いてください。
emrun --browser firefox hello.html

ブラウザ上の画面の下の部分に「Hello world!!」が表示されていれば成功です!

No WebAssembly support found.Build with -s WASM=0 to target JavaScript instead.」のエラーが出ている場合は、firefoxのバージョンが古いことが原因と考えられます。
firefoxを最新版にインストールしてください。(WebAssemblyの対応はver.52以降)

まとめ

新しい技術に挑戦するのは楽しいね。
最後まで、読んでくれてありがとう。
参考になったら「:thumbsup_tone2:」押してね。

【復習】JavaScriptで複数のclassに反映させる方法

$
0
0

困ったこと

classを指定しているのに一つしか反映されない。。。

理解と対応

classの場合は配列になるので一つ一つ取り出さなければいけない

そんな時に利用するのが
for文

for(初期値;繰り返す条件;増減値){// 繰り返す処理を書く}

配列の取り出し方としては
box[1]やbox[2]などで取り出すため
変数を用いてbox[i]と記載しfor文で変数の値を増やしていけば取り出すことが可能

実際のコード

<style>.box1{width:100px;height:100px;background:red;}.box2{width:100px;height:100px;background:red;}</style><body><divclass="box1"id="jj"></div></br><divclass="box2"id="hh"></div></br><divclass="box2"id="hh"></div><script>varbox=document.getElementsByClassName('box2');for(vari=0;i<box.length;i++){box[i].style.background='pink';}</script>

【swift】FSCalendarの諸々の色をコード上から変更したい!

$
0
0

やりたいこと

タイトルの通り。
FSCalendarのカラーをコードで変更したい。

環境

xcode 11.3
swift 5.1.3

実装方法

// storyboardから繋いであるFSCalendar@IBOutletweakvarcalendar:FSCalendar!// calendarの色の設定calendar.appearance.todayColor=UIColor.redcalendar.appearance.headerTitleColor=UIColor.redcalendar.appearance.weekdayTextColor=UIColor.red

calendar.appearanceの後に続けて、以下の写真のようにstoryboard上で表示されているプロパティ名をかけばいい。
スクリーンショット 2020-01-13 18.26.38.png

テーマカラーとして設定するには、todayColorとtitleColorとweekdayTextColorを変えればいいかな。
その三つをUIColor.systemPurpleに設定したのが、以下

スクリーンショット 2020-01-13 18.26.56.png

まとめ

記事を色々探すより、ソースコードをきちんと読むの、だいじ。

参考文献

github : WenchaoD/FSCalendar

【初学者向け】セキュリティ対策入門①〜XSS編〜

$
0
0

前提

確認環境

以下と同様です。
【初学者向け】セキュリティ対策入門⓪〜環境構築編〜

本シリーズの目的

以下と同様です。
【初学者向け】セキュリティ対策入門⓪〜環境構築編〜

本記事の目標

XSS(クロスサイトスクリプティング)の概要、原因、対策について理解することです。

本記事を読み進める上での必要事項

以下の内容を終えていることです。
【初学者向け】セキュリティ対策入門⓪〜環境構築編〜

概要

XSS(クロスサイトスクリプティング)とは

不正なスクリプトを混入・実行させることです。掲示板などエンドユーザからの入力値をWebページに表示させる機能を持ったWebアプリケーション全般で行われる可能性があります。

これだけだとわかりにくいと思いますので実際に見てみましょう。

実際に体験してみよう

本シリーズで使うコンテナを起動して
http://localhost:8080/xss/bad.php?name=J
にアクセスしてみましょう。

『こんにちは、Jさん』とブラウザに表示されていると思います。これはnameパラメータの内容をもとに挨拶文言を表示させているものです。ソースコードはtutorial-php-security/www/html/xss/bad.phpにあります。

tutorial-php-security/www/html/xss/bad.php
<?php
$name = $_GET['name'];
?>

<p>こんにちは、<?= $name; ?>さん</p>

$_GET['name']でクエリストリングスのnameパラメータを参照しています。これが上述するところの『エンドユーザからの入力値』の一例です。クエリストリングスだけではなく、POST送信で渡ってきたデータやDBに格納されたデータを出力する場合も、エンドユーザの入力によるものであれば同様です。

次に
http://localhost:8080/xss/bad.php?name=<script>alert(1)</script>
にアクセスしてみましょう。

以下2点が重要です。

  1. ダイアログが表示
  2. ダイアログで『OK』をクリックしたのち『こんにちは、さん』と表示

何が起きているかというとnameパラメータの<script>alert(1)</script>がスクリプトと見なされてJavaScriptのコードが実行されてしまっているのです。alert()はダイアログを表示させるJavaScriptの関数です。

今回は単にダイアログが表示されただけなので大したことがないと思うかもしれません。しかし、別のサーバにリダイレクトさせられたりCookie情報を盗まれてしまったり様々な被害が生じてしまいます。

原因

不正なスクリプトを表示させていることです。これを避けるにあたって様々な対策があります。その一部を以下で具体的に紹介していきます。

対策

エスケープする

以下にアクセスしてみましょう。

http://localhost:8080/xss/good.php?name=<script>alert(1)</script>

bad.phpではなくgood.phpにアクセスしていることに注意しましょう。実際にアクセスすると『ダイアログが表示されない』『「こんにちは、<script>alert(1)</script>さん」と表示されている』ことがわかると思います。

good.phpではある処理を追加しています。それを次に確認しましょう。

tutorial-php-security/www/html/xss/good.php
<?php
$name = $_GET['name'];
?>

<p>こんにちは、<?= htmlspecialchars($name, ENT_QUOTES | ENT_HTML5, 'UTF-8'); ?>さん</p>

ポイントはhtmlspecialchars()の部分です。これはエスケープするための関数です。エスケープとは<>などHTMLにおいて特殊な意味を持つ文字列をそれぞれ&lt;&gt;など安全な文字列に置き換えることです。詳細は『文字参照』などで検索してみてください。&lt;は画面上では<と表示されるため『こんにちは、<script>alert(1)</script>さん』と表示されているわけです。

そもそも出力しない

エスケープが機能しない場合もあります。たとえば

<script><?= $js_code; ?></script>
<a href=<?= $link_url; ?>>危ないリンク</a>

というケースを考えてみましょう。

前者ではalert(2)という文字列が$js_codeに格納されている場合、エスケープしたところで意味がありません。bad.phpと同様ダイアログが表示してしまいます。

後者ではJavaScript:alert(3)という文字列が$link_urlに格納されている場合、エスケープしたところで意味がありません。bad.phpと同様ダイアログが表示してしまいます。ちなみにJavaScript:XXXのような記述をJavaScript擬似プロトコルといいます。JavaScript擬似プロトコルで記述できる箇所ではエスケープが意味を成しません。

こういう場合では、ユーザが入力したデータをそのまま出力しない仕様にするのが得策です。

入力チェックを行う

ユーザの入力をDBに格納するときやユーザの入力データを出力する際に、入力チェックを行うことも有効です。極端な例ですが、nameパラメータに12という文字列以外入力を認めないようにバックエンドでチェックすればスクリプトは混入しなくなります。ちなみに、フロントエンドでの入力チェックやHTMLタグの属性などエンドユーザ側で書き換えできてしまうチェック方法は意味がないので気をつけてください。

ライブラリを使う

ブログアプリケーションを作成するときのように、ユーザにタグ入力を認める機能を実装する場合は、ユーザの入力データからスクリプトだけを上手く取り除くロジックが必要です。それを0から作るのはあまり現実的ではないためライブラリを導入ことが検討に値する解決策となります。

一例ですがHTML Purifierというものがあります。

参考文献

独習PHP 第3版

今回の内容は以上です。最後までご覧いただきありがとうございました。

Viewing all 21607 articles
Browse latest View live