Progate Ruby on Rails5

Ruby on Railsの環境構築をしてみよう!

windowsOS

  • Ruby のインストール(略)
  • SQLite3のインストール
    • https://sqlite.org/index.html にアクセスして Download
    • Precompiled Binaries for Windows (sqlite-dll-win64-x64-3320100.zip) (64bitの場合) をインストールして, sqlite3.dllC:\Ruby26-x64\bin にコピー (32bitのときは32bit用のものをインストール)
    • Precompiled Binaries for Windows (sqlite-tools-win32-x86-3320100.zip) をインストールして, sqlite3.exeC:\Ruby26-x64\bin にコピー
  • Rails のインストール
gem install rails -v "5.2.3"  # gem はパッケージマネージャー
rails -v
  • Rails アプリケーションの作成
rails new sample_app -G  # sample_app はアプリケーション名
## 以下は, sqlite3のインストールに失敗してエラーが出た場合
### 多分, Installing sqlite3 1.4.2 with native extensions のこと
cd sample_app
ridk exec pacman -S mingw-w64-x86_64-sqlite3
bundle install
## 以上, sqlite3のインストールに失敗してエラーが出た場合
  • ローカルにサーバを立てる
cd sample_app
rails s  # これで localhost:3000 にアクセスできる
# Ctrl + C でサーバは止まる

macOS

  • Homebrew, rbenv はインストール済みとする
rbenv versions # Rails 5系はRuby 2.2.2以上対応
  • Rails のインストール
gem install rails -v "5.2.4.1"
rails -v
  • アプリケーションの作成とローカルにサーバを立てる
rails new sample_app # sample_app はアプリケーション名
cd sample_app
rails s  # これで localhost:3000 にアクセスできる
# Ctrl + C でサーバは止まる

Ruby on Rails5 I

  • アプリケーションの作成: $ rails new アプリケーション名
    • カレントディレクトリに アプリケーションと同名のディレクトリが出来て, その下にフォルダなどが自動生成される。
  • サーバの起動: $ rails server

  • ページの生成: $ rails generate controller home top(コントローラー名, アクション名)

    • views: app/views/home/top.html.erb が自動生成される
    • controller: app/controllers/home_controller.rb が自動生成され, top アクションが追加される。
    • routing: config/routes.rbget "home/top" => "home#top" が自動追記される。
    • scss: app/assets/stylesheets/home.scss が自動生成される
  • ただし, コントローラーが存在する場合はこのコマンドは使えないので,

    • routing を追記する
    • 対応する controller のアクションを追記する
    • それで呼び出すファイルを新規作成する
  • 処理の流れ

    • GET /home/top を受け取る
    • routing により, home コントローラーの top アクションが呼ばれる
    • それが, app/views/home/top.html.erb を返す。
  • 構成要素

    • views: ページの見た目を作るためのHTMLファイル
    • controller: ブラウザに返すビューを views フォルダの中から見つけ出して返す役割をもつ。コントローラ内のメソッドをアクションと呼ぶ。
    • routing: get "URL" => "コントローラー名#アクション名" という文法(対応表のような感じ)で, リクエストを受け取り, コントローラーを呼び出す。
  • ルーティングは手動で変更可能
    • /get "/" =>
    • /topget "top" =>
      • <a href="/top"> のようにリンクを張る
  • 「stylesheets」フォルダの中に保存されているCSSファイルはすべてのビューに適用される。
  • 画像は public フォルダに置く。
    • public/hoge.png<img src="/hoge.png" >のようにアクセスできる。

Ruby on Rails5 II

  • $ rails generate controller$ rails g controller と省略可能
  • view ファイルの拡張子の erbEmbedded Ruby
    • コードは<% %>で囲む
    • 変数埋め込みは <%= 変数名 %>
# view で使う変数は一般にアクションで定義する
# ただし, その場合の変数名は @ で始める必要がある
def index
    @items = ["A", "B", "C"]
end
<% @items.each do |item| %><!-- 呼び出しも当然 @ が付く-->
    <li><%= item %></li>
<% end %>
  • マイグレーションファイル(データベースに変更を指示するためのファイル)
    • $ rails g model Post content:textにより, db/migrate の下にマイグレーションファイルが自動生成される。
      • ggenerate も可
      • Post とすると, デフォルトで posts テーブルになる
      • content カラムのデータ型が text
        • 自動で id, created_at, updated_at が管理される。
    • このコマンドで, app/models/post.rb が生成され, Post クラスが定義されている。
    • 次に $ rails db:migrateマイグレーションファイルの内容が DB に反映される
  • $ rails console インタラクティブRuby を実行できる
# app/models/post.rb
class Post < ApplicationRecord
end
$ rails console
# Post のコンストラクタやsaveメソッドはApplicationRecordを継承している
> post1 = Post.new(content: "Hoge") # レコードの生成
> post1.save # DB に登録
> p1 = Post.first # DB の最初のレコードの取り出し
> p1.content # カラムの値をみる。
> pall = post.all # 全てを配列として取り出す
> post.all[0].content 
  • 例えば, 次のように DB の値を埋め込む
def index
    @records = Post.all
end
  • views/layouts/application.html.erb に共通のHTMLを書いておく
    • つまり, 表示の共通部分(ヘッダなど)はここに書いて, それ以外を各 erb ファイルにこれまで通り書く。それが, このファイルの <%= yield %> に埋め込まれて表示される。
  • リンク生成メソッド link_to
    • <%= link_to("hoge","/top") %><a href="/top">hoge</a> として埋め込まれる

Ruby on Rails5 III

  • モデル名.find_by(カラム名: 値) で, カラムがその値なレコードを返す。
  • パラメータをとるルーティング
# config/routes.rb
Rails.application.routes.draw do
  get "posts/index" => "posts#index" # ルーティングは合致するURLを上から順に探す。
  get "posts/:id" => "posts#show"    # /posts/hoge などが該当
end
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def index
    @posts = Post.all.order(created_at: :desc)
    # created_at カラムの値を降順(:desc)に並び替えた配列にする
  end
  def show
    @id = params[:id]
    @post = Post.find_by(id: @id)
  end
end
<!-- app/views/posts/show.html.erb -->
<p><%= "idが #{@id} のとき" %></p>
<p><%= @post.content %></p>
  • post
# config/routes.rb
Rails.application.routes.draw do
  get "posts/new" => "posts#new"
  post "posts/create" => "posts#create" # post を受ける
end
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def new
  end
  def create
    @post = Post.new(content: params[:content]) # params[:content] でリクエストパラメータを受け取る
    # 代わりに content: "hoge" のようにすればデフォルトの値を設定できる。
    @post.save
    redirect_to("/posts/index") # view を用意する代わりにリダイレクトする
  end
end
<!-- app/views/posts/new.html.erb -->
<%= form_tag("/posts/create") do %> <!-- <form action="/posts/create" method="post"> -->
    <!-- 上で, do , 下で, end を忘れないように -->
    <textarea name="content"></textarea>
    <input type="submit" value="投稿">
<% end %> <!-- 今回は </form> -->

Ruby on Rails5 IV

  • DB の書き換え
post = Post.find_by(id: 1) # インスタンスをDBから取り出して
post.content = "hoge" # 代入するだけ
post.save # 変更後のインスタンスをDBに保存
  • DB のレコードの削除
post = Post.find_by(id: 1) # インスタンスをDBから取り出して
post.destroy # 破棄して DB 更新
  • 編集ページ
    • get "posts/:id/edit" => "posts#edit"
    • 編集ページ app/views/posts/edit.html.erb の用意
      • edit コントローラで @post = Post.find_by(id: params[:id]) とレコードを変数としてページに渡す
      • <textarea name="content"><%= @post.content %></textarea> のように初期値を設置する
      • /posts/:id/update とかに post する(:id@post.idを埋め込む)
    • 表示ページ(/posts/show/:id) から, <%= link_to("編集", "/posts/#{@id}/edit") %> のようなリンクを張っておく)
    • アクション posts#update の用意
      • params[:id], params[:content] を受け取る
      • レコードを書き換えて
      • /posts/#{params[:id]}/show とかにリダイレクトする
  • 破棄(ページを用意しない分簡単)
    • link_to("破棄", /posts/#{@post.id}/delete, {method: "post"})/posts/:id/show とかからリンクを張る
      • link_to の第三引数でメソッドを指定できる(デフォルトでget)

Ruby on Rails5 V

  • validation
# app/models/post.rb
class Post < ApplicationRecord
  validates: content, {presence: true, length: {maximum: 140}}
  # これで Post のインスタンス(e.g. post)は content カラムが
  # 空, または 文字数140字以上
  # だと post.save 出来なくなる
  # さらに, post.errors.full_messages にエラーメッセージの配列が格納される。(デフォルトは空の配列)
end
  • save の戻り値は true または false なので, これを受けてその後の動きを変更すれば良い
  • 表示
    • redirect_to("URL"): get "URL" にリダイレクトされる。そのため, ルーティングされたコントローラのアクションを経由する。(e.g. redirect_to("/posts/index"))
    • render("PATH"): 別のアクションを経由せずに、直接ビューを描画する。PATH は pwd が views で, 拡張子はいらない。(e.g. render("posts/index"))
  • ページ上に1度だけ表示(リロードすると消える)されるメッセージをフラッシュと呼ぶ。
    • アクションで flash というハッシュを flash[:hoge] = "fuga" とすると, view で flash[:hoge] を使用できる。
    • この hoge: "fuga" は1度表示された後に自動で削除される。

Ruby on Rails5 VI

  • name, email カラムを持つ users データベースの作成
    • $ rails g model User name:string email:string
    • $ rails db:migrate
  • validation
# app/models/user.rb
class User < ApplicationRecord
  validates :name, {presence: true}
  validates :email, {presence: true, uniqueness: true} # 他のレコードと重複無し
end

Ruby on Rails5 VII

  • テーブルの新規作成
    • $ rails g model User name:string email:string: モデルとマイグレーションファイルの作成
      • db/migrate/20170418070645_create_users.rb が自動生成される
      • app/models/user.rb が自動生成される。
    • $ rails db:migrate: マイグレーションファイルのchangeメソッドの実行
      • users テーブルが自動生成される
# db/migrate/20170418070645_create_users.rb
class CreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :users do |t|
      t.string :name
      t.string :email
      t.timestamps
    end
  end
end
# app/models/user.rb
class User < ApplicationRecord
end
  • テーブルのカラム追加
    • $ rails g migration add_image_name_to_users: マイグレーションファイルのみの作成
      • db/migrate/20200531023644_add_image_name_to_users.rb が自動生成される。
      • これに下のように追記する。
# db/migrate/20200531023644_add_image_name_to_users.rb
class AddImageNameToUsers < ActiveRecord::Migration[5.0]
  def change
    # ここから先を追記する
    add_column :users, :image, :string # テーブル名, カラム名, データ型を指定する。
    # 同じ要領で, remove_column でカラムの削除が可能
  end
end
  • 画像の投稿
<%= form_tag("/users/upload",{multipart: true}) do %> <!-- {multipart: true} を与える -->
    <input type="file" name="gazo">   <!-- type="file" -->
    <input type="text" name="name">
    <input type="submit" value="投稿">
<% end %>
  • これを ユーザid.jpg としてDBにファイル名を登録し,
  • public/user_images/ユーザid.jpg としてファイル本体を保存する
# app/controllers/users_controller.rb
class UsersController < ApplicationController
  def update
    @user = User.find_by(id: params[:id])
    @user.name = params[:name]
    if params[:gazo] # 画像がある場合のみ
      @user.image_name = "#{@user.id}.jpg" # このファイル名で保存するので一意である必要がある。
      image = params[:gazo] # 画像のバイナリが入っている
      # 保存先のパスと image.read
      File.binwrite("public/user_images/#{@user.image_name}", image.read)
      # cf. テキストの保存は write メソッド
      # File.write("public/sample.txt", "Hello World!")
    end
    @user.save
  end  
end

Ruby on Rails5 VIII

  • ルーティングで, 同じURLにget, postを設置可能。ただし, コントローラーは違うアクションにする必要がある。
  • ログイン機能
    • ユーザ名とパスワードを入力するフォームを作る
      • ユーザ名({uniqueness: true}なカラム)とパスワードを "/login" とかに post する
      • <input type="password">: 入力文字が伏字になる。
    • post "/login" をうけるコントローラーを作る
      • @user = User.find_by(name: params[:name], password: params[:password])
        • 複数カラムをアンド検索することができる
      • @user が存在するかで挙動を分岐
        • あれば flash[:notice] とかしてログインページにリダイレクト
      • 存在しなければ, ログインフォームを再描画
        • リダイレクトではないのは, 受け取ったパラメータを @email, @password に代入してこれを埋め込むため。
        • エラーメッセージは @error_message とかに固定値を代入してこれを埋め込めばよい
          • 最初にフォームをGETしたときはこれらは nil なので, <%= @email %> で何も埋め込まれない?
    • ログイン後のユーザー情報の保持
      • ログイン時に, session[:id] = 243 とすると, クライアント側にこの値(セッションID)が送信される。(ID に紐づく中身のほうはサーバ側で管理する。)
        • session を送信するときは POST を使う
      • クライアント側はこれを Cookie(デベロッパツールのApplicationとかで確認可能)に保存して, そのサーバにリクエストするたびにこれを送信する。
      • ログアウト時に, session[:id] = nil として消しておく。
        • ログアウト後はログインページにリダイレクトするとよい。
    • ユーザ情報の表示
      • view側で管理する方法(あんまりよくない)
        • app/views/layouts/application.html.erb(各htmlを埋め込む先のファイル) に <% current_user = User.find_by(id: session[:id]) %> を書いて置く
          • この共通htmlで <% if current_user %> でログイン中かで分岐可能
          • 各ページで <% current_user.name %> でカラムにアクセス可能
      • controller で before_action として定義する方法
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :set_current_user # ※ 全アクションの前に実施するアクション
  def set_current_user
    @current_user = User.find_by(id: session[:user_id])
  end
  def authenticate_user
    # ログインしていない場合(@current_user == nil)に表示させないためには, アクションの前にリダイレクトさせてしまえばよい。
    if @current_user == nil
      flash[:notice] = "ログインが必要です"
      redirect_to("/login") # 当然だが, このページをログイン必須にするとリダイレクトがループする。
    end
  end
  def forbid_login_user # 逆にログインしているとアクセスできないページ
    if @current_user
      flash[:notice] = "すでにログインしています"
      redirect_to("/posts/index")
    end
  end
  def ensure
end
# app/controllers/users_controller.rb
class UsersController < ApplicationController
  # 上の before_action :set_current_user (※の行)は継承されている
  before_action :authenticate_user, {only: [:index, :show, :edit, :update]}
  # :authenticate_user は継承されている。
  # only をつけた場合は配列で指定されたアクションの前だけに実行される。
  before_action :forbid_login_user, {only: [:new, :create, :login_form, :login]}
  before_action :ensure_correct_user, {only: [:edit, :update]}

  def index
    @users = User.all
  end
  # 以下略
  def ensure_correct_user # ここで定義することもできる
    if @current_user.id != params[:id].to_i
      # これで id が違う場合に書き換え系のアクション にはアクセスできなくなる。
      flash[:notice] = "権限がありません"
      redirect_to("/posts/index")
    end
  end
end

Ruby on Rails5 IX

# app/models/post.rb
class Post < ApplicationRecord
  validates :user_id, {presence: true}
  def user
    # 自身(postsテーブルのレコード)のuser_id と一致する id を持つusersテーブルのレコードを返す
    return User.find_by(id: self.user_id)
  end
end
  • レコードの検索
    • find_by メソッドはレコードを一件だけ見つけてそのレコードを返す。
    • where メソッドはレコードを全て見つけて, レコードの配列を返す。(引数などはfind_byと同じ。)

Ruby on Rails5 X

  • コントローラーは手動でも作成可能(viewファイルが不要な時)
  • html要素に対してlink_toを使うとき, 第一引数の文字列の代わりに下のようにする。
    • 第二引数以降は同じ。
<%= link_to("URL", {method: "post"}) do %>
  <!-- 上で do が必要, 下で end が必要, -->
  <img src="URL"> # 画像など
<% end %>

Ruby on Rails5 XI

  • Gemfile (package.jsonみたいな感じ)
    • gem 'パッケージ名', 'バージョン' を列挙
      • バージョンの指定はオプションで, 範囲指定もできる。
      • 指定なしの場合は最新のものとなる。
    • dependencies, devDependencies よりも細かくインストールする環境を指定できるらしい。
source 'https://rubygems.org'

gem 'rails', '5.0.3'
# Use Puma as the app server
gem 'puma', '3.6.2'
# Use SCSS for stylesheets
gem 'sass-rails', '5.0.6'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '3.0.4'
gem 'pry-rails', '0.3.4'
# Use jquery as the JavaScript library
gem 'jquery-rails', '4.2.2'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '5.0.1'

group :development, :test do
  gem 'sqlite3', '1.3.13'
  gem 'byebug', '9.0.6', platform: :mri

  gem 'web-console', '3.4.0'
  gem 'listen', '3.0.8'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring', '2.0.1'
  gem 'spring-watcher-listen', '2.0.1'
end
  • Gemfile.lock: bundle などで自動生成されるgemの依存関係を示したもの(package-lock.jsonみたいな感じ)
  • $ bundle install
    • Gemfile.lock を元にインストールを行います。
    • ただし, Gemfile.lock になく, Gemfile にある gem があるとき, 必要な gem たちをインストールした上で Gemfile.lock を更新する。($ npm ci に近い)
  • $ bundle update

    • $ npm i にほぼ同じ。
  • パスワードの暗号化

    • gem 'bcrypt'
    • パスワードを暗号化したいDBテーブルのmodelのクラスを下記のようにする。
    • そのうえで, カラム名password_digest とする。
    • ただし, テーブル上のカラム名password_digest だが, パスワードのセットは password に対して行う(それがpassword_digest)に自動反映される。
    • パスワードの比較は, レコードに対して authenticate メソッドを使う
      • @user.authenticate("hoge") で, @userpassword_digest が文字列 hoge を暗号化したものに一致するかを真偽値として返す。
# app/models/user.rb
class User < ApplicationRecord
  has_secure_password
end

Progate Ruby

Rubyの開発環境を用意しよう!

windowsOS

ruby -v
echo puts 1 + 2 > fuga.rb
ruby fuga.rb

macOS

# macOS には標準でインストールされている
ruby -v

# homebrew はインストール済みの前提
rbenv -v # command not found なはず
brew install rbenv ruby-build
rbenv -v

echo $SHELL
## /bin/bash の場合
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
source ~/.bash_profile
## /bin/zsh の場合
echo 'eval "$(rbenv init -)"' >>  ~/.zshrc
source ~/.zshrc

rbenv install --list
rbenv install 2.7.1
rbenv versions
rbenv global 2.7.1
ruby -v

Ruby I

  • Python との共通点
    • 文末セミコロンなし
    • 文字列のクォーテーションはどちらでも可
    • コメントは #
    • 四則演算の記号
    • + による文字列の連結
      • ただし, 数値と文字列を連結することはできない。(->#{}を使う)
    • 変数名 = 値 による変数定義
    • 変数は snake_case
    • 比較演算子, (複合)代入演算子
  • Python との相違点
    • 13/4 -> 3
    • コーディング規約の例: https://shugo.net/ruby-codeconv/codeconv.html
    • 文字列中での変数展開: #{変数名} を文字列に入れる (ダブルクォーテーションの場合のみ)
    • if文の記述(動きはPythonと同じ)
    • true, false (Python は True, False)
    • puts のようなメソッドの括弧は省略可能(省略しなくてもよい)
if a > 1  # 後ろに : はつけない
    puts "a>1"  # インデントは 2つのスペース を推奨(インデント自体必須ではない)
elsif a < 0  # elif でも, elseif でもない
    puts "a<1" 
else
    puts "a==1"
end  # end で閉じる
  • 論理演算に関して
    • and, &&, or, || がある
    • 違いは優先度のみ (&&>=>and)
    • 評価値はオペランドの一方(JavaScriptと同じ)
      • A && B で A が偽なら A そのもの, A が真なら B そのものを返す。

Ruby II

  • Python との共通点
    • 配列: numbers = ["A","B","C"][] によるアクセス
  • Python との相違点

    • ハッシュ: info = {"name" => "hoge", "age" => 23}
      • アクセス, 要素の追加, 更新などは同じ
        • 存在しない要素へのアクセスは nil となる
          • nilputs で表示されない。
          • nil の評価は false
      • キーをシンボルとすることもできる。(:name)
        • シンボルは文字列に似ているが, 互換性はない
        • キーにシンボルを用いるとき, :name => "hoge"name: "hoge" と書ける
          • このときも info[:name]
  • 表示メソッド

    • puts: 引数が配列のとき各要素を出力(要素ごとに改行される)。引数が文字列のとき文字列はクォートで囲まれない。
    • print: 引数が配列のとき配列として出力。引数が文字列のとき文字列はクォートで囲まれない。
    • p: 引数が配列のとき配列として出力。引数が文字列のとき文字列はクォートで囲まれる。
items.each do |item| # 配列 items の各要素を item に格納しながらループ
    puts item
end

Ruby III

def method1  # ruby のメソッドはいわゆる関数
  puts "hoge"
end  # endつける
method1  # 引数無しのメソッド呼び出しの() はなくても分かるので普通省略する

def method2(arg1)
  puts "#{arg1}です"
  return arg1
end
  • 真偽値を返すメソッド名は慣用的に isEmpty ではなく empty? とする
  • キーワード引数(呼び出し順序は不問になる)
def method3(a,b: 3)  # b のデフォルト値は 3(デフォルト値は省略可能。b: とする。(:b ではない))
    return a + b  # ここでは :b ではなく b
end
method3(2, b: 4)  # 呼び出しはシンボルとして
method3(2, :b => 4)  # なので, これも可

Ruby IV

class Hoge
  attr_accessor :name  # name はインスタンス変数
  def initialize(name)  # コンストラクタ(キーワード引数を用いてもよい)
    self.name = name  # this ではない
  end
  def getName  # インスタンスメソッド
    return self.name
  end
end
hoge = Hoge.new("fuga") # インスタンス生成
puts hoge.name
puts hoge.getName
  • require "ファイルパス" ファイルパスは拡張子省略
  • gets.chomp入力を文字列として受け付ける
a = gets.chomp.to_i  # to_i は整数に変換するメソッド

Ruby V

  • 継承 class 子クラス < 親クラス
    • オーバーライドの動作は同じ
    • オーバーライドしたメソッドの中で super メソッドを呼び出すと親クラスの同名のメソッドとされる。(super には必要なら実引数を渡す)
  • ライブラリの利用
require "date"
date = Date.new(2020,5,29)
puts date  # 2020-05-29
if date.sunday?  # デフォルトで用意されているメソッド
today = Date.today  # today はクラスメソッド
  • インスタンスメソッドの定義: def method
  • クラスメソッドの定義: def Hoge.method (自身のクラス名をつける)
    • 呼び出しも Hoge.method とする。(クラス内のインスタンスメソッドからも呼び出せる)
  • インスタンス変数の定義: attr_accessor :var
  • クラスインスタンス変数の定義: @var (親クラスからは参照できない)
  • クラス変数の定義: @@var (継承されたサブクラスでも参照できる)

Progate PHP

PHP I

  • 機能はHTMLだが, 拡張子はphp
    • 冒頭は <!DOCTYPE html>
  • <?php ?> の中がphpが有効な領域。この領域がHTMLに変換された上で埋め込まれて解釈される。
  • 文末;。ただし, 一行の<?php ?>に1文であれば省略できる。
  • コメントは //
  • 文字列のクォートは不問
  • 変数定義 変数名 = 値; ただし, 変数名は $ から始まり, 次は英字。
  • ++, -- もある
  • 変数は <?php ?> をまたいでも有効。
  • 文字列や変数の連結は 文字列.文字列
    • .= も存在する。
  • ダブルクォーテーションで文字列を囲んだ場合、文字列の中で{変数名}として変数を埋め込み可能
    • シングルクォーテーションで文字列を囲んだ場合は変数展開されない。
  • true, false, &&, ||, !
  • if(条件){} elseif(条件) {} else {}: elseif の間にスペースがないことに注意
  • switch(式){} 仕様は C++ に同じ。
  • for($i=0;$i<5;$i++){}
  • while(条件){}
  • break;, continue;: while, for, foreach 等の繰り返しで利用
  • 配列
    • 配列の初期化: 配列名 = array(値1, 値2, ・・・);
    • 末尾に追加: 配列名[] = 値;
    • 値の上書き: 配列名[2] = 値;
  • 連想配列
    • 配列の初期化: 配列名 = array('キー名' => '値1', ・・・);
    • 値の追加, 上書き: 配列名[キー] = 値;
  • 配列ループ
    • foreach(配列 as $value): 配列の要素値(または連想配列)を次々$valueに渡してループ
    • foreach(配列 as $key => $value)
      • $key: 配列のインデックス または 連想配列のキー
      • $value: 配列の要素値 または 連想配列の値
  • echo: 後ろに置いたものが評価されて埋め込まれる。(echo "hoge";, echo 2 + 3;)
    • 埋め込まれるだけで, 改行はされない。
      • echo '<br>'; や, echo $hoge."<br>"; など改行タグを埋め込めばよい。
  • 組み込み関数
    • strlen(文字列): 文字列の文字数を返す。
    • count(配列): 配列の要素数を返す。
    • rand(2,4): 2,3,4 のいずれかをランダムに返す。
<?php
    // 使い方
    $radius = 4;
    $area = $radius * $radius * 3.14;
    echo $area;
?>
  • 関数定義
<?php
    function add($arg1, $arg2){ // 関数名に $ 不要
        $tmp = $arg1 + $arg2
        echo $tmp;
        return $tmp; // 呼び出しは add(3,5)
    }
?>
<!-- php 要素を持たない部分は <?php ?> は不要-->
<form method="post" action="sent.php"> <!-- {AAA:"...",BBB:"..."} をphpに渡す -->
    <input type="text" name="AAA">
    <!-- name属性がjsonのキー, 記述(単一行)がvalueとなる -->
    <textarea name="BBB"></textarea>
    <!-- name属性がjsonのキー, 記述(複数行可)がvalueとなる -->
    <!-- この下に送信ボタンを作りましょう -->
    <select name="CCC">
        <?php 
            for($i=0;$i<100;$i++) {
                echo "<option value='opt{$i}'>選択肢{$i}</option>";
                // 変数展開されて, <option value='opt1'>選択肢1</option> とかが埋め込まれる
                // ブラウザに表示されるのは「選択肢0」から「選択肢99」
            }
        ?>
    </select>
    <input type="submit" value="送信">
    <!-- 送信ボタンで, value属性でボタンの文字を指定 -->
</form>
<!-- sent.php
送信ボタンを押すと, phpが埋め込まれたhtmlが表示される -->
<?php
    echo $_POST["CCC"]."<br>"; // opt0.<br> , opt1.<br>, ... のいずれかが埋め込まれる。
    // $_POST に post した json が格納されている。
?>

PHP II

  • クラス
class Hoge { //クラス名に $ 不要
    // public, private は Java 等と同じ。
    public $prop1; // $ 必要
    private $prop2 = ""; // 初期化も可能
    public function __construct($prop1){ // コンストラクタの名前は __construct で固定
        $this->prop1 = $prop1;
    }
    public function method1(){ // メソッド名に $ 不要
        echo $this->prop1."<br>";
        // $this はクラス内のメソッドの定義の中でのみ使用可能
        return $this->prop1; // return も可能
    }
}
$hoge = new Hoge("hoge");
$hoge->prop1 = "fuga"; // プロパティ名に $ 不要
$hoge->method1();
  • PHP のコードを各部分は, 埋め込まない部分は大きな塊で書き, 埋め込む部分は小さく書く。
  • 次の二つは等価(下のようにすると, HTML の埋め込みが分かりやすい)
    • { を :
    • } を endforeach
      • endif endfor endwhile endswitch
    • ちなみに, if(): else: endif
<?php
$words = array("hoge", "fuga");
foreach($words as $word){
    echo "<p>".$word."</p>";
}
?>
<?php
$words = array("hoge", "fuga");
?>
<?php foreach($words as $word): ?>
    <p><?php echo $word ?></p>
<?php endforeach ?>
  • 他のファイルの埋め込み
    • require("ファイルパス"): 埋め込む
    • require_once("ファイルパス"): 1回目に呼び出された時だけ埋め込む

PHP III

  • クラスプロパティ
    • 定義: public static $hoge = 0; のように static をつける(Javaと同様)
    • クラス外でのアクセスは $instance->hoge ではなく Hoge::$hoge ($ の位置に注意)
    • クラス内でのアクセスは $this->hoge ではなく self::$hoge ($ の位置に注意)
  • クラスメソッド(個々のインスタンスのデータに関係ない処理を行いたい時)
    • 定義: public static function fn() {} のように static をつける(Javaと同様)
    • アクセスは $instance->fn() ではなく, Hoge::fn() ($ 無しに注意)
  • 継承
    • 継承する前に親クラスのファイルをrequire_once()すること。
    • class サブクラス名 extends スーパークラス名 : Java と同じ
    • protected, オーバーライド も Java と同じ。
    • 親クラスで定義したメソッドを呼び出したいとき parent::メソッド名() で呼び出せる
      • parent->メソッド名 ではない
      • super ではなく parent
  • インスタンス instanceof クラス名 -> true, false

PHP IV

  • クエリパラメータ URL?キー=値&キー=値

Progate Go

Go I

  • go fmt ファイル名 でフォーマットできるらしい。
  • 文法の特筆事項
    • コメントは // または /* */
    • 文末セミコロンなし
    • 13/3 -> 4
    • 数値, 文字列, 真理値 とその演算あたりは C++Java とほぼ同じ。
    • 変数定義は var 変数名 変数型 。初期化しなくてもよい。
      • 初期化は var hoge int = fuga であるが, 型は省略可能。(TypeScriptに近い)
        • 特に, var hoge = 10 など自明なとき。
        • さらに, 上は hoge := 10 と省略して定義することもできる。
        • hoge = 10 は定義済み変数に対する代入。
    • 変数型
      • 文字列は string 型 (+で連結できる)で, ダブルクォート限定。
      • int
    • 変数関連エラー
      • 現時点で未定義な変数の呼び出し
      • 定義済み変数の再定義
      • 定義済み変数の未使用
      • 変数に対して定義された時と違う型を代入
  • if 条件 {処理} else if 条件 {処理} else {処理} で条件に括弧不要
  • switch 式 {処理} も同様に式に括弧不要
    • case 3,5: とすれば 3または5 のときと解釈される。
    • casebreak 無しで終えると, 自動で break が付与される。
package main
func main() {
    var hoge int = 10 // 変数の初期化
    println("Hello", "World") // 標準出力は Python に近い
    if hoge > 10 { // 括弧不要。囲ってもエラーにはならないが。
        println(hoge)
    }
}

Go II

  • 使用しないパッケージをインストールするとエラーになる。
  • パッケージ
    • fmt: 出力関連
      • fmt.Println(): println() より表示できるデータ型の種類が多い
      • fmt.Printf(): 書式付き出力。第2引数以降にに埋め込む値を並べる。
        • %s: 文字列, %d: 整数
        • 型が違うとエラーになる。
    • math/rand: 乱数関連。import "math/rand"
      • rand.Intn(10): 0,1,2,...,9 の乱数を生成して返す。
        • import "time" したうえで rand.Seed(time.Now().Unix()) を最初に呼び出すとよい。
package main // import よりも先に書く
import "fmt" // fmt 標準パッケージのインストール
func main(){ //ファイル実行時に呼び出される関数。
    fmt.Println("hoge") // 自動で改行される
    fmt.Printf("%s is Hoge, %s is Fuga\n", "Hoge", "Fuga") // 自動で改行されない
}
  • for i:=0; i<3 ;i++ {処理} のように, forC++ から条件の括弧を取った形。
    • var i int = 0 と初期化するとエラーになるので, 必ず := の省略形を使う

Go III

  • fmt.Scan(&変数名): 標準入力を受け取って変数に代入する(変数のポインタを渡している)
  • 関数の特筆事項
    • 利用するより先に定義しなくてもよい。(変数は先に定義する必要)
    • 引数は値渡し
// 関数定義 fn2("hoge") で呼び出せる。
func fn2(s string) string { // 引数や戻り値の型の指定は TypeScript に近い
    // s は値渡し。
    return s + s
}

Go IV

  • 変数のポインタは &変数名 で得られる。Type のポインタの型は *Type
    • Type* ではない。
    • var intPtr *int = &intNum としたとき, *intPtrintPtr の指す変数(すなわち intNum)と同一になる。

Progate Java

C++ と文法がかなり近いので, 比較してまとめる。

Java I

  • C++ との共通点
    • 文字, 文字列のクォーテーション
    • 文末セミコロン
    • コメント
    • 四則演算
    • 文字列の連結
    • 変数, 変数の=による初期化, 代入
    • ODR
    • int
    • +=, ++
    • 暗黙の型変換(ルールが同じかは分からない)
    • (int)x 等のキャスト
    • インデントできるが文法的な強制力はない
  • C++ との相違点
    • String
    • 変数名は camelCase 推奨
    • 下記基本型(Javaは速度ではなく汎用性重視)
  • 基本型
    • 論理型: boolean
    • 整数型: byte, short, int, long
      • これらは 1,2,4,8 バイト符号付き整数 と確定している
    • 浮動小数点型: float, double (4,8バイト)
    • 文字型: char (Unicode (UTF-16) と決まっている)
class Main {
  public static void main(String[] args) {
    System.out.println("Hello World"); // 末尾は改行される
  }
}

Java II

  • C++ との共通点
    • true, false, 比較演算子
    • &&, ||, !
    • if, switch, while, do, for (おそらく構文が完全に一致)
    • break, continue
    • [] による配列アクセス
  • C++ との相違点
    • boolean 型 (C++bool 型)
    • int[] v = {0,1,2} (C++int v[] = {0,1,2} など)
    • v.length (C++sizeof(v)/sizeof(v[0]))
  • 拡張for文
    • for(int item: intlist) pythonにおける for item in intlist: 値渡しらしい。
    • cf. C++ の範囲 for と同じ
      • C++ で使えるのは コンテナ, 配列, {0,1,2}(初期化リスト)
      • C++ では書き換えるなら for(auto &item: intlist) と参照渡しにすればよい。

Java III

  • C++ との相違点(クラス)

    • Java には関数はない(多分)ので, まとまった処理はクラス内でメソッドとして定義する。
    • クラス内のメソッドは メソッド名() で呼び出せる。
    • クラス外のメソッドは クラス名.メソッド名() で呼び出す
    • main メソッドの戻り値の型は int ではなく void ?
    • Java は「 1 ファイル 1 クラス 」を原則とする (ので, 以下はその場合を想定)
      • Javaはファイルではなくクラスを実行する。
      • ファイル名を クラス名.java とする(必須)
        • 外部ファイルのクラスはローカルにあれば import 不要(コンパイル時に指定)
        • 外部ライブラリのクラスは import クラス名; と, 使用するファイル内で import する。
          • import java.lang.Math; としたときは, クラス名は Math なので Math.round(3.5) のように呼び出せる。
          • ただし, java.lang にあるクラスは import 不要
      • クラスを実行すると, そのクラスの main メソッドが実行される。
        • ゆえに, main メソッドを持つクラスしか実行できない。
        • クラス名に関係なく, クラスの実行時には main メソッドが呼ばれる。
  • C++との共通点(の抜粋)

    • メソッドは多重定義可能
    • クラス名は PascalCase
// Main.java <-- クラス名が Main だから。
class Main {
  public static void main(String[] args) { // この main とクラス名の Main は関係ない。
    String added = hello("ABC"); // クラス内メソッドの呼び出しではクラス名は指定不要
    Hoge.hoge(); // クラス外メソッドの呼び出し
  }
  public static String hello(String name) {
    return "Hello "+name;
  }
}
// Hoge.java
import java.util.Scanner; // import は利用するファイルで。
class Hoge { // クラス定義
  public static void hoge() {
    Scanner scanner = new Scanner(System.in); // Scanner クラスのインスタンス生成
    String name = scanner.next(); // 空白までの入力を受け付けて, 文字列として格納する
    // scanner.nextLine() は改行までの一行を受け付ける。
    // scanner.nextInt()
    // scanner.nextDouble()
    System.out.println(name);
  }  
}

Java IV

  • C++との相違点
    • インスタンスの生成 new クラス名()
      • クラス型 変数名 = new クラス名() のクラス型はクラス名に同じ。
    • フィールド
      • インスタンスフィールド
        • インスタンスに属する。宣言に static はつけない.
        • クラス中でのアクセスは this.変数名, this.メソッド名() とする
      • クラスフィールド
        • クラスに属する。宣言に static をつける
        • クラス中でのアクセスは this をつけない
    • コンストラクタを(他のコンストラクタ中で)呼び出す
      • this() とすると、コンストラクタから同一クラス内の他のコンストラクタを呼び出せる。括弧内には適当な引数を与える(コンストラクタも多重定義できるので。)
      • this() はコンストラクタの先頭でしか呼び出せない。
    • 変数の定義時に値を代入しなかった場合, 固定的なデフォルト値が変数に代入される。
      • String: null
      • int: 0
      • double: 0.0
      • boolean: false
  • C++との共通点
    • コンストラクタ定義
      • コンストラクタ名はクラス名と同じにする
      • 戻り値を書いてはいけない(voidも書かない)
      • public もつけない
      • インスタンスを生成したときに、コンストラクタが呼び出される。
    • カプセル化:
      • public はクラスの外部からアクセス可能
      • private はクラスの外部からアクセス不可能
class Person {
    // 変数の定義(クラスの最初に書く)
    public String name; // python と違い, クラス側に定義が必要?
    public static int id = 0; // クラスフィールドは static を付ける。Person.id++ のように使う
    // コンストラクタの定義
    Person(String name) {
        this.name = name;
    }
    // インスタンスメソッドの定義
    public void hello() { // static がない 
        System.out.println("hello"+this.name);
    }
    // クラスメソッドの定義
    public static void hoge(){ // static がある
        System.out.println("hoge");
    }
}
Person person1 = new Person();
person1.hello(); // インスタンスメソッドは インスタンス名.メソッド名() としてよびだす。
Person.hoge(); // クラスメソッドは クラス名.メソッド名() としてよびだす。

Java V

  • 継承: class サブクラス名 extends スーパークラス名
    • スーパークラスインスタンスメソッドなどはそのまま使える。
    • 同名であればサブクラスで定義されたものが優先される(オーバーライド)。
    • サブクラス内で, スーパークラスで定義された private なものは参照できない(ゲッタやセッタ使う)
      • private の代わりに, protected を使えばサブクラス内からはアクセスできるようになる。
    • サブクラス内で, スーパークラスで定義されたメソッドを使うとき,
      • 同名のものが同じサブクラス内で定義されていない場合: メソッド名() でよい
      • 同名のものが同じサブクラス内で定義されている場合: super.メソッド名()
    • サブクラスでコンストラクタを定義するとき, 必ずコンストラクタの先頭でスーパークラスのコンストラクタを呼びださなければならない。スーパークラスのコンストラクタは super(); で呼び出せる(括弧内には実引数を与える).
    • 抽象メソッド: abstract public void hello(String name) ; のようにメソッドの先頭にabstractをつける。; に注意。
      • 具体的な処理が未確定なメソッドであることを示す。
      • サブクラスでそのメソッドをオーバーライドしていなければエラーになる。
        • つまり, サブクラスがオーバーライドし処理内容を定義することを強制できる。
      • 抽象メソッドを1つでも持つクラスは「抽象クラス」と呼ばれ、クラス定義を abstract class Hoge {} とする。
  • クラスの変数として他のクラスのインスタンスを持たせることもできる。
  • 多態性(要素が複数の型に属することを許すという性質):
    • つまり, クラス B が クラス A を継承する場合, クラス B のインスタンス b は, B型でもあると同時に A型でもある。よって, A b = new B() が許される。このとき, b は B型でもあるので, Bでオーバーライドされる場合はそちらが優先される。

Progate Python

Pythonの開発環境を用意しよう!

windowsOS

  1. https://www.python.org/
  2. Downloads タブ
  3. Python3.8.3 のボタン
  4. Add Python 3.6 to PATH にチェックを入れて Install Now
$ python --version

macOS

  • Homebrew のインストール(略)
  • pyenv のインストール
brew install pyenv
pyenv -v
## 以下は, bash のとき
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
## 以下は, zsh のとき
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(pyenv init -)"' >> ~/.zshrc
source ~/.zshrc

pyenv install --list ## 切り替え可能なリストの表示
pyenv install 3.8.2
pyenv versions ## git branch みたいな感じ(versionではなくversions)
pyenv global 3.6.5 ## git checkout みたいな感じ
python --version ## これで通常の python コマンドで指定したバージョンが実行できる

Python I

  • 文末に ; 等は不要
  • コメントは #(複数行のコメントはなく, 三重クォートの文字列にする)
  • 文字列はシングルクォートでもダブルクォートでもよい。
  • 5/2 は 2.5, 5//2 は 2
  • クラスは PascalCase, それ以外は snake_case
  • 文字列の連結は "hoge" + "fuga"
  • print は文字列や数値型など複数の型を引数にとれる。最後に改行される。
  • 文字列の連結で数値型が混ざるときは型変換が必要 ("answer:" + str(5))
if 条件:
    処理 
elif 条件:
    処理 # インデントで意味が変わる
else:
    処理
  • 真理値は True False
  • and or not
  • 変数の受け取り: 変数 = input('コンソールに表示したい文字列') ただし, 受け取ったのは文字列扱い

Python II

  • リスト: [], append,
    • for 変数名 in リスト:
  • ディクショナリ: Python 3.7以降はキーの順序を保証する。
    • 辞書名[新しいキー名] = 値 : 辞書に新しい要素を追加
    • ただし、辞書にすでにあるキー名を指定すると、値の追加ではなく更新
    • for 変数名 in 辞書: 変数名に格納されるのはキー
  • while 条件式:
  • break, continue

Python III

  • 関数定義 def 関数名(仮引数の並び):
    • デフォルト引数, return
  • モジュール(hoge.py) -> hoge がモジュール名になる。
    • 呼び出される側は同じ
    • 呼び出す側は import hoge (拡張子不要, クォーテーション不要)
    • hoge.関数名() で呼び出せる。
  • import random
    • random.randint(x, y) : xからyまでの整数をランダムに取得

Python IV

class Hoge:
    def __init__(self, value): # インスタンス生成時に自動で呼び出される(つまり, コンストラクタ)
        self.value = value # Hoge(5) のようにインスタンスを生成
    def method1(self): # メソッドの第一引数は変数を使わなくても self とする。__init__ も同様
        # self はインスタンス自身(つまり hoge_instance など)
        # 呼び出しは hoge_instance.method1() となる
        pass #何もしない文
    def method2(self,x):
        print(self.value + round(x)) # round(小数) で小数点第一位を四捨五入
  • from モジュール名 import クラス名 のようなインポートも可能

Python V

  • クラスの継承 class クラス名(親クラス名):
    • デフォルトでそのまま親クラスのインスタンスメソッドが引き継がれる
    • 親クラスにないメソッドは同様に追加可能
    • 親クラスにあるメソッドはオーバーライドされる(親クラスのメソッドは見えなくなる)
    • その場合でも, 呼び出しsuper().メソッド名() とすることで, 親クラスのメソッドを呼び出せる。(2つ目の括弧の中身は親クラスのメソッドを呼び出すときの実引数)
def __init__(self, name,price,amount):
    super().__init__(name,price)
    self.amount()

Progate React

React I

  • HTML like な JSX を用いる。HTML との違いは以下の通り。
    • 複数の要素がある場合は、<div></div> で囲む
    • コメントアウト{/**/} で囲む
    • <img src='URL'> のように閉じタグがないやつは <img src='URL'/> とする。
    • JSX のクラス属性は className='クラス名' (CSSclassと同じ)
  • React の記述ファイル
// app.js
import React from "react"; // react のインポート
class App extends React.Component { // React.Component クラスを継承する
  render() { // render メソッドを定義(このなかにreturnを書く)
    const text = "Hello World!";
    const imgUrl = "https://...";
    return ( // return の内側には JSX, 外側には JavaScript を書く
      <h1>{text}</h1> // JSX の内部での参照は {変数}
      <img src={imgUrl}/> // タグ内にも持ち込める
    );
  }
}
export default App; // クラスをエクスポートする
  • イベントの設置
<button onClick={アロー関数とか}>クリック時の動作を規定</button>
  • state の定義
    • 変数の保存, 参照, 変更
class App extends React.Component {
  constructor(props) {
    super(props); // props はこのクラスを呼び出す側で指定するやつ
    this.state = {count: 5}; // state はオブジェクトとして定義する
  }
  counterSetting(num) {
    this.setState({count: num}) // メソッド化できる
    // this.state.count = 0 のような代入による変更は不可
    // 変更はリアルタイムに反映される
  }
  render() {
    return (
      <h1>{this.state.count}</h1> {/* 参照 */}
      <button onClick={()=>{this.counterSetting(0)}}>0セット</button>
    );
  }
}

React II

  • 構成: App.js は index.js を介して index.html に挿入される
    • CSS も有効
/index.html
/src/index.js
/src/components/App.js
/stylesheet.css
// /src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App'; // クラスのインポート
// インポートしたクラスの return の中身を id が 'root' な位置に突っ込む.
ReactDOM.render(<App />, document.getElementById('root'));
<link rel="stylesheet" href="stylesheet.css">
<!-- /index.html の一部 -->
<div id='root'></div>
<script src="bundle.js"></script>
  • React は1つのクラス(Hoge)が1つのコンポーネントになるような使い方をする。
    • Hoge クラスを App.js でインポートして, App クラスの render の return 内で <Hoge />Hoge で return されたものが埋め込まれる。(当然複数回呼び出せる)
  • 呼び出す側(App.jsのreturn内)でpropsを指定可能。
<Hoge name="hoge" age=25 />
{/*改行すると見やすい*/}
<Hoge
  name={ValueDefinedInThisFile}
  age=5
/>
  • 呼び出される側(Hogeクラス)では this.props.age などとして使える。
  • return の中で return を呼び出せる
render(){
    const list = ["hoge", "fuga"];
    return(
        <div>
            { list.map((item)=>{
                return(<p>{item}</p>)
            })}
        </div>
    )
}

React III

  • 変数にJSXを格納
render() {
  let text;
  let modal
  if(this.state.isDisplay){
    text=<p>変数にJSXを格納可能</p>;
    modal=(
      <div>
        <p>複数行にわたるときは括弧で囲んで代入(returnも同じ)</p>
      </div>
    );
  }
  return(
    <div>
      <h2>{text}<h2> {/*ブロック内変数なのでthisはいらない*/}
      {modal} {/*空なら表示されない*/}
    </div>
  )
}

React IV

  • HTML と JSX の違い
    • <input> : <input/>
    • <textarea></textarea> : <textarea/>
  • イベント
    • <form onSubmit={()=>{}}> </form>
    • <button onClick={()=>{}}> </button>
    • <input onChange={()=>{}}> 変更が起きた時の処理

入力値をstateにセットする

<input value={this.state.inputValue} onChange={(event)=>{
  this.handleMethod(event);
}}>
handleMethod(event) {
  const inputString = event.target.value;
  this.setState({inputValue:inputString});
}
constructor(props) {
  super(props);
  this.state = {inputValue:""};
}
  • ab と入力するとき
    1. inputValueの初期値が""なので, 入力欄は空
    2. a を押した時点で即座に
    3. 入力欄が空欄から a に変わったので, onChange イベントが反応して event.target.value が "a" になる
    4. これがinputStringに代入されてinputValueにセットされる
    5. すると, 入力欄の値が a になる。(inputValueの更新をしないと, 入力欄は元の空欄に引き戻される)
    6. さらに b を押した時点で
    7. 入力欄が a から ab に変わったので, event.target.value が ab になる。
    8. 以下略