- 投稿日:2020-10-18T23:54:11+09:00
【Ruby】配列
配列とは
- 複数の情報を順番にまとめて管理するもの
- 1つの変数で複数の値を持つことができる
- 配列の中には複数の値を入れることができる
入力
names = ["田中","山田","佐藤"] puts names出力
田中 山田 佐藤値の追加(<<)
配列の一番後ろに値を追加する。
入力
names << "鈴木" puts names出力
田中 山田 佐藤 鈴木配列の値の取得
添字を指定し取得する。
入力name = names[0] puts name name = names[2] puts name出力
田中 佐藤配列の値の変更
入力
puts name[1] names[1] = "佐々木" puts name[1]出力
山田 佐々木
- 投稿日:2020-10-18T23:09:46+09:00
RubyでJSONからTSV、TSVからJSONへ変換
はじめに
やりたいこと
TSVファイルをJSON形式に、またJSONをTSVファイルに変換
環境
Ruby 2.6.5
Mac OS 10.15.5この記事内で使用するサンプル
meibo.json[{"name":"john","gender":"m","age":"18"}, {"name":"paul","gender":"m","age":"20"}, {"name":"alice","gender":"f","age":"15"}, {"name":"dabid","gender":"m","age":"17"}, {"name":"jasmin","gender":"f","age":"17"}]meibo.txtName gender age john m 18 paul m 20 alice f 15 dabid m 17 jasmin f 17JSONからTSVへ変換する方法
require "json" #JSONを扱うために記述 File.open("meibo.json") do |json| #JSONファイルを開く File.open("meibo.txt", "w") do |txt| #出力先のTSVファイルを開く array = JSON.load(json) #JSONの読み込み column = ["name","gender","age"] #ヘッダー txt.puts(column.join("\t")) #ヘッダを入れる array.each do |line| #JSON配列の各要素をTSVファイルの各行に入れる attr = [line["name"],line["gender"],line["age"]] txt.puts(attr.join("\t")) end end endまずは、JSONファイルを開きます。出力先のTSVファイルも書き込みモードで開いておきます。
次に、JSONファイルを読み込んでいきます。下記のように、JSONはJSON.load([Fileオブジェクト])
でJSONファイルを読み込んで、ハッシュを要素とする配列で返してくれます。require "json" File.open("meibo.json") do |json| array = JSON.load(json) p array end #=> [{"name"=>"john", "gender"=>"m", "age"=>"18"}, {"name"=>"paul", "gender"=>"m", "age"=>"20"}, {"name"=>"alice", "gender"=>"f", "age"=>"15"}, {"name"=>"dabid", "gender"=>"m", "age"=>"17"}, {"name"=>"jasmin", "gender"=>"f", "age"=>"17"}]返ってきた配列を
each
メソッドで回し、それぞれのハッシュをTSVでの一行一行にしていくイメージです。
TSVの各行に入れていくテクニックとしては、each
で取り出した各ハッシュのバリューを、キーを直接指定するかたちで取り出し、それらを一度、配列に入れ直します。そして、join
メソッドを用いてタブ区切りで配列を結合し、その文字列をTSVのファイルの各行に挿入していきます。出力結果は以下の通りです。
meibo.txtname gender age john m 18 paul m 20 alice f 15 dabid m 17 jasmin f 17ハマったところ
最初は以下のように、
JSON.load
したあとの配列をeach
処理に回す際、各オブジェクトのバリューの並び順通りに、TSVへ格納していきましたが、
JSONのメンバー(キーとバリューのセット)の並び順は関係ないとのこと。つまり
{"name":"john","gender":"m","age":"18"}
と{"name":"john","age":"18","gender":"m"}
は区別しません。よって、今回はたまたま、JSONのメンバーの並び順通りでTSVに変換しても、問題ないですが、メンバーの並び順がバラバラのJSONだと、TSVに変換した時おかしくなってしまうことがわかりました。
なので、面倒ですが一度、ハッシュのキーを指定する形でバリューを取り出し、それをカラムの並び順通りに配列にいれてから、TSVへ入れ込むと、確実です。
require "json" File.open("meibo.json") do |json| File.open("meibo.txt", "w") do |txt| array = JSON.load(json) column = ["name","gender","age"] txt.puts(column.join("\t")) array.each do |line| txt.puts(line.values.join("\t")) #JSONの各オブジェクトのバリューの並び通りにTSVへ入力 end end endTSVからJSONへ変換する方法
require "json" require "csv" hash_ary = [] File.open("meibo.json","w") do |json| CSV.foreach("meibo.txt",col_sep: "\t", headers: true) do |line| h = {name: line[0], gender: line[1], age: line[2]} hash_ary << h end JSON.dump(hash_ary,json) #to_jsonを使用 endまず、書き込みモードでJSONを書くためのファイルを用意します。
TSVファイルはCSVクラスで
foreach
メソッドを使って各行をハッシュに変換していきます。変換したハッシュは一度配列(ここでは、hash_ary
)にいれて、全ての行が入れ終わったあとに、JSON.dump
でハッシュからJSONへの変換を行い、ファイルに書き込みます。出力結果は以下の通り。
meibo.json[{"name":"john","gender":"m","age":"18"},{"name":"paul","gender":"m","age":"20"},{"name":"alice","gender":"f","age":"15"},{"name":"dabid","gender":"m","age":"17"},{"name":"jasmin","gender":"f","age":"17"}]ハマったところ
require "json" require "csv" hash_ary = [] File.open("meibo.json","w") do |json| CSV.foreach("meibo.txt",col_sep: "\t", headers: true) do |line| h = {name: line[0], gender: line[1], age: line[2]} hash_ary << h.to_json end json << hash_ary end最初は、上記のように書いてましたが、このようにすると。。
meibo.json["{\"name\":\"john\",\"gender\":\"m\",\"age\":\"18\"}", "{\"name\":\"paul\",\"gender\":\"m\",\"age\":\"20\"}", "{\"name\":\"alice\",\"gender\":\"f\",\"age\":\"15\"}", "{\"name\":\"dabid\",\"gender\":\"m\",\"age\":\"17\"}", "{\"name\":\"jasmin\",\"gender\":\"f\",\"age\":\"17\"}"]このように
"
の前に\
が入ってうまく変換できませんでした。
to_json
メソッドはJSON形式の文字列で返して来るので、これではなく、JSON.dump
を使用したら、解決しました。
- 投稿日:2020-10-18T21:56:28+09:00
ActiveSupport の underscore は camelize の逆変換じゃない
複数の単語からなる言葉を識別子に使うとき,スペースを含めることができないので,何らかの方法で単語の区切りを表さなければなりません。
たとえば以下のようなさまざまな表記方法が使われています。
- UpperCamelCase
- lowerCamelCase
- snake_case
ActiveSupport にはこれらの間の変換をする便利なメソッド群が用意されています。
CamelCase を snake_case に変換するには
String#underscore
が使えます。
snake_case を UpperCamelCase に変換するにはString#camelize
が使えます1。以下のように:
require "active_support/inflector" p "BookTitle".underscore # => "book_title" p "book_title".camelize # => "BookTitle"これを見ると,
underscore
とcamelize
は互いの逆変換になっており,require "active_support/inflector" p "BookTitle".underscore.camelize # => "BookTitle" p "book_title".camelize.underscore # => "book_title"のように両者を重ねると(常に)元に戻るように思えます。
しかし,そうならない場合もあります。それは
require "active_support/inflector" p "OfficialURL".underscore # => "official_url" p "CSVFile".underscore # => "csv_file"の
URL
やCSV
のように大文字だけで構成される単語を含むケースです。
underscore
メソッドは,人間が期待するとおり,CSVFile
をCSV
とFile
に分割して snake_case 化してくれました。
しかし,これにより,変換後のurl
やcsv
がもともと大文字だけで綴られていたという情報が失われてしまうので,これらをcamelize
しても元には戻らないのです。require "active_support/inflector" p "OfficialURL".underscore.camelize # => "OfficialUrl" p "CSVFile".underscore.camelize # => "CsvFile"便利なメソッドも仕様をよく理解して使わないと落とし穴に落ちる可能性がある,という例でした。
(つか,ハマったんだよ2,実際に!)
- 投稿日:2020-10-18T20:51:14+09:00
railsで共通のレイアウトをまとめてみる
今回はrailsのhtmlファイルで共通のレイアウトを一つのファイルにまとめて書く方法をシェアしたいと思います。
viwes/layout配下にapplication.html.erbというファイルがあると思います。
そのファイルに以下のように共通のレイアウト部分を纏めてあげることで同じ内容のコードを書かずに済みます。<!DOCTYPE html> <html> <head> <title>Tweet</title> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> </head> <body> <header> <div class="header-logo"> <%= link_to("TweetApp","/") %> </div> <ul class="header-menus"> <li> <%= link_to("TweetAppとは","/about") %> </li> <li> <%= link_to("投稿一覧","/posts/index") %> </li> </ul> </header> <%= yield %> </body> </html>上記の
タグの中身は元々、他のhtmlファイルのものでした。
今回のように一つにまとめる方法もあるので知っておくと便利でしょう。
- 投稿日:2020-10-18T20:50:33+09:00
[Rails]本番環境で画像が表示されないエラーについて
はじめに
ローカル環境ではしっかりと画像が表示されていたのに本番環境にデプロイすると画像が表示されなくなった時の対応。
環境
・Rails 6.0.3.
・Haml状況
以下の表記みたいにRailsのヘルパーメソッドの
image_tag
を用いて画像をパス指定していてローカル環境ではうまく表示されていた。= image_tag "assets/material/icon/icon.png"しかし、いざデプロイすると画像が表示されなくなりました。(この表記もあやしいですが)
もちろん
scss
ファイルに記載しているbackground-image
も表記されませんでした(この対応ついては下の方に記載してます)原因
本番環境では、画像もコンパイルされ、画像ファイル名が変わりディレクトリも変わってしまいます!
本番環境とローカル環境での違いは、
パスが変わる(app/assets/images/icon.png => /assets/icon.png)
名前が変わる(icon.png => icon-xxx… .png) ※xxx… はdigest
これが変わってしまいます!対策
assets_path()
というRails
のオプションを使用します。
これは便利で本番環境へデプロイしファイル名やディレクトリが変わってもそれに対応しているのでローカルも本番もこれ一つで対応できてしまいます!具体的には以下のように修正
= image_tag asset_path("assets/material/icon/icon.png")これでうまく行くと思いきや、、、、
ローカル環境でも表示されなくなりました。。。。ダメ元でデプロイしても結果は同じ(当然です)
asset_pathでもうまくいかない原因
asset_path
というオプションはapp/assets/images
の直下にあるファイルに対応しているが、どうやらその先のディレクトリの中にあるファイルには対応していないみたいです。対応
ディレクトリで管理していた画像をすべて
app/assets/images
直下へ移動し、表記も下記のように修正しました。= image_tag asset_path("icon.png")これでデプロイするとようやく画像が表示されました!!
まとめると
asset_path
というオプションを使うことと画像ファイルの場所をimages
直下にすることが解決策でした。[scss]background-imageについて
こちらも以下のようにただ画像ファイルのパスを指定してもうまくいきませんでした。
background-image: url("main-image.jpg");対応
image-url
を使うとローカルでも本番環境でも対応してくれます。background-image:image-url("main-image.jpg");このように記述すると対応できます。
もちろん画像はapp/assets/images
直下のものを指定ここで注意が必要なのは
:(コロン)
の後はスペースを開けずに記述する点です。SCSSの記述はコロンのあとは大体半角スペースを開けて記述していたのでクセになってました。
なので誤っているとは知らずになかなか原因に辿り着けずにいました。これでデプロイすると正常に画像が表示されました!!
- 投稿日:2020-10-18T20:22:08+09:00
RailsチュートリアルでCloud9の容量がいっぱいになったときの対処法
対処法
Railsのチュートリアルの公式から、対処法が記載されています。
ただ、私が本エラーに遭遇した時、Railsのチュートリアルが記載してる対処法を見つけられなかったので、アクセスの多いQiitaに投稿しておきます。RailsチュートリアルでCloud9の容量がいっぱいになったときの対処法
コメント
一人でも多く、本事象から抜け出して楽しくRailsチュートリアルを完走できることを祈ってます!
- 投稿日:2020-10-18T19:49:37+09:00
[Rails]Userモデルの単体テストコード
はじめに
Rspecというgemを用いてテストコードを実装しました。
今回はユーザー登録機能の単体テストコードについて、正常系と異常系の挙動の確認を行いました。目次
1必要なgemの導入
2FactoryBotの記述
3テストコードの記述1. 必要なgemの導入
gemfileのgroup :development, :testの中に、以下を追記します。
その後、bundle installを実行します。gemfilegem 'pry-rails' gem 'rspec-rails' gem 'factory_bot_rails' gem 'faker'次に、Rspecを書くためのディレクトリを作成します。
ターミナル
rails g rpsec:install実行すると、以下のようにディレクトリとファイルが生成されます。
ターミナル
create .rspec create spec create spec/spec_helper.rb create spec/rails_helper.rbテストコードの結果をターミナル上で確認するための記述をします。
.rspec--format documentation2. FactoryBotの記述
FactoryBotとはインスタンスをまとめておくことができるgemです。
specディレクトリにfactoriesディレクトリを作成し、その中にuser.rbファイルを作成します。
user.rbを以下のように編集します。spec/factories/user.rbFactoryBot.define do factory :user do nickname { Faker::Name.last_name } email { Faker::Internet.free_email } password = Faker::Internet.password(min_length: 6) password { password } password_confirmation { password } end endFakerはランダムな値を生成するgemです。
3. テストコードの記述
spec/models/user_spec.rbを以下のように編集します。
spec/models/user_spec.rbrequire 'rails_helper' RSpec.describe User, type: :model do describe User do before do @user = FactoryBot.build(:user) end describe 'ユーザー新規登録' do context '新規登録がうまくいくとき' do it 'nicknameとemail、passwordとpassword_confirmationが存在すれば登録できること' do expect(@user).to be_valid end it 'passwordが6文字以上あれば登録できること' do @user.password = '123456' @user.password_confirmation = '123456' expect(@user).to be_valid end end context '新規登録がうまくいかないとき' do it 'nicknameが空では登録できないこと' do @user.nickname = nil @user.valid? expect(@user.errors.full_messages).to include('ニックネームを入力してください') end it 'emailが空では登録できないこと' do @user.email = nil @user.valid? expect(@user.errors.full_messages).to include('Eメールを入力してください') end it '重複したemailが存在する場合登録できないこと' do @user.save another_user = FactoryBot.build(:user, email: @user.email) another_user.valid? expect(another_user.errors.full_messages).to include('Eメールはすでに存在します') end it 'passwordが空では登録できないこと' do @user.password = nil @user.valid? expect(@user.errors.full_messages).to include('パスワードを入力してください') end it 'passwordが存在してもpassword_confirmationが空では登録できないこと' do @user.password_confirmation = '' @user.valid? expect(@user.errors.full_messages).to include('パスワード(確認用)とパスワードの入力が一致しません') end it 'passwordが5文字以下であれば登録できないこと' do @user.password = '12345' @user.password_confirmation = '12345' @user.valid? expect(@user.errors.full_messages).to include('パスワードは6文字以上で入力してください') end end end end end以下のコマンドでテストコードを実行します。
ターミナル
bundle exec rspec spec/models/user_spec.rb今回は、'rails-i18n’を用いているためエラーメッセージが日本語表記になっています。
pry-railsというgemを用いることで、テストコードの実行を途中で止め、エラーメッセージの確認を行なっています。参考リンク
https://github.com/rspec/rspec-rails
https://github.com/faker-ruby/faker
- 投稿日:2020-10-18T19:41:39+09:00
【Ruby on Rails】RSpec導入まで
開発環境
ruby 2.5.7
Rails 5.2.4.3
OS: macOS CatalinaRSpecとは?
Railsで用いられることが多いテストツールであり、テストコードを書いておくと自動でアプリケーションの動作をテストしてくれます。
またgemでinstallすることが可能です。RSpecの構成
- describe:テストの題名
- context:題名を詳細に分ける場合使用
- before:itの内容を実行する前に必要な記述があれば使用
- it:テスト内容
この中でも、describe と it は必須になります。
RSpecのインストール
Gemfileの group :test do の中を変更します。
デフォルトの記述は削除し、代わりに以下の4つのgemを記述します。Gemfilegroup :test do gem 'capybara', '>= 2.15' gem 'rspec-rails' gem "factory_bot_rails" gem 'faker' endターミナル$ bundle install $ rails g rspec:install実行後、app配下にspecフォルダが作られますので、
これを編集しながらテストを実行していきます。また、config/environments/test.rb の下部にある下記を:silenceに変更します。
config/environments/test.rbconfig.active_support.deprecation = :stderr ↓ config.active_support.deprecation = :silenceそして下記を実行します。
ターミナル$ rails db:migrate RAILS_ENV=testまた以下を追加するとテストを通過した文言も表示されるようになるため、
設定をおすすめします。.rspec--require spec_helper --format documentation <--追加ここまでで導入はOKです。
次回から
controller、model、view
をテストする方法を紹介します。
- 投稿日:2020-10-18T19:38:08+09:00
[Rails] 投稿一覧をカテゴリー別で表示する方法
前提
- Ruby 2.6.3
- Rails 5.2.4.4
- devise導入済(しなくてもいい)
- 基本的なページは完成している
- コントローラー名は今回gears
- gearモデルにはcategoryカラムが存在する
- 今回はログインユーザーにしか見えない投稿一覧なので、他ユーザーも閲覧できるようにしたい場合は
user_id: @user.id
を省いてください。やり方
Controller
編集前
gears_controller.rbdef index @user = current_user @gear = Gear.where(user_id: @user.id) end編集後
gears_controller.rbdef index @user = current_user @gear1 = Gear.where(user_id: @user.id, category: "住居系") @gear2 = Gear.where(user_id: @user.id, category: "料理系") @gear3 = Gear.where(user_id: @user.id, category: "火周り系") @gear4 = Gear.where(user_id: @user.id, category: "その他") endView
index.html.erb<div class="category bg-success">住居系</div> <% @gear1.each do |gear| %> <div class="gear-index-item mb-20"> <% if gear.image.attached? %> <%= image_tag gear.image, class: "index-img" %> <% else %> <img class="index-img" src="<%= "/images/default_gear.jpg" %>" alt="Index image cap"> <% end %> <%= link_to(gear.name, "/gears/#{gear.id}") %> </div> <% end %> <div class="category bg-warning">料理系</div> <% @gear2.each do |gear| %> <div class="gear-index-item mb-20"> <% if gear.image.attached? %> <%= image_tag gear.image, class: "index-img" %> <% else %> <img class="index-img" src="<%= "/images/default_gear.jpg" %>" alt="Index image cap"> <% end %> <%= link_to(gear.name, "/gears/#{gear.id}") %> </div> <% end %> . . .それぞれeach文で繰り返し処理を行う。
結果
まとめ
whereメソッドを使えば簡単に特定カラムでまとめられる!
- 投稿日:2020-10-18T19:25:41+09:00
初心者がGitHubに自分のアプリを上げる方法
前提条件
Githubに登録していること
アプリがあること大きな流れ
①ワーキングツリーを作成
②インデックスにファイルを登録
③ローカルリポジトリにファイルを登録
④リモートリポジトリと接続
⑤リモートリポジトリにファイルを登録前提
GitHubにアップしたいファイルがあるフォルダにがカレントディレクトリであること
①ワーキングツリーを作成
git init
.gitが生成されていることを確認
ls -a
②インデックスにファイルを登録
git add *
備考
*はワイルドカードで、カレントディレクトリにある全てのファイルをインデックスに登録している③ローカルリポジトリにファイルを登録
git commit -m 'test'
備考
-mはコミットに対してコメントを付ける機能でつけてもつけなくてもいいです。
確認
コミットされたことを確認
git log
Author: xxx
Date: Sun xxx xx 17:35:23 2020 +0900test
④リモートリポジトリと接続
git remote add origin https://github.com/xxxxxxxx/xxxxxxxx.git
備考
originはリモートリポジトリ名で、任意の名前をつけても良い。基本はorigin
https/://github.com/xxxxxxxx/xxxxxxxx.gitはリモートリポジトリURL。GitHubのウェブページでリポジトリを作成すると付与される。(多分リポジトリページにそれらしきものはある)
確認
git remote -v
備考
接続しているリモートリポジトリ先が表示される⑤リモートリポジトリにファイルを登録
git push origin master
備考
masterはブランチ名。デフォルトがmaster。
自分の今いるブランチを確認したい場合は以下コマンド
git branch --contains
確認
自分のGitHubのリポジトリページにアクセスしてアップロードされていることを確認
- 投稿日:2020-10-18T18:07:23+09:00
Ruby/Rust 連携 (7) インストール時ビルドの Rust 拡張 gem を作る
連記事目次
- Ruby/Rust 連携 (1) 目的
- Ruby/Rust 連携 (2) 手段
- Ruby/Rust 連携 (3) FFI で数値計算
- Ruby/Rust 連携 (4) Rutie で数値計算①
- Ruby/Rust 連携 (5) Rutie で数値計算② ベジエ
- Ruby/Rust 連携 (6) 形態素の抽出
- Ruby/Rust 連携 (7) インストール時ビルドの Rust 拡張 gem を作る
はじめに
今回は,ソースに Rust コードを含んだ gem の例。
以下の記事が参考になる。
Rust でつくるかんたん Ruby Gem - Qiita上記の記事では,開発時に Rust のコンパイルを行い,生成物を gem のパッケージに含める,という形を取っていた。
それに対し,gem のインストール時に Rust のコンパイル(ビルド)を行う方法 を取ってみよう。
そのためには,当然インストール先の環境に Rust がインストールされていなければならない。今回試作した gem を GitHub にさらす。gem 名は rust_gem_sample01 とした。
https://github.com/scivola/rust_gem_sample01やり方を示すための試作品なので RubyGems.org には入れていない。
macOS,Linux,Windows のいずれでもインストールできるように書いたつもり。
題材
この gem は実用目的ではないので,なるべく単純な例を。
Ruby の組込みメソッドや標準添付ライブラリーには,一様分布以外の分布を持つ乱数生成機能が無いので,それをやろう。
とりあえず正規分布の乱数を返すやつ。こんな感じで使えるもの:
require "rust_gem_sample01" standard_deviation = 2.0 p RustGemSample01.rand_norm(standard_deviation) # => -3.1674786173729506RustGemSample01 モジュールに特異メソッド
rand_norm
を定義。引数に標準偏差を与えると平均値が 0 の乱数を返す。
正規分布は平均値と標準偏差の二つのパラメーターを持つが,平均値を変えたければ足し算すればいいだけなので,引数は標準偏差だけとした。引数が多いほど FFI のコストが大きくなるので,これでいいと思う。インストールしてみよう
RubyGems.org には載せていないので,
gem install rust_gem_sample01
ではインストールできない。
以下の手順で,リポジトリーをクローンし,gem のビルド&インストールをしてみてほしい。
いろんな環境でちゃんとインストールできるかどうか知りたいので,協力していただけるとありがたい。git clone https://github.com/scivola/rust_gem_sample01.git cd rust_gem_sample01 rake installこの最後の
rake install
というのは,gem のプロジェクトファイルから gem のパッケージを作り1,システムに(つまりグローバルに)その gem をインストールする,というもの。上を実行するためには,Git がインストールされていることと,Rust(バージョン 1.40.0 以上)がインストールされている必要がある2。
インストールに失敗するとすれば,Rust コードのビルドのところか,ビルドの生成物を移動するところだと思う。
インストール時に Rust のコンパイルが行われるが,そこでは依存クレートのネットからのダウンロードなどなども行われるので,場合によって数十秒かかることもある。応答が無くても気長に待ってほしい。
sudo gem install の場合
Linux とかだと,システムワイドに Ruby をインストールしていて,gem をインストールするとき
sudo gem install hogeのように
sudo
を付けなければならない場合も多いと思う。
この場合,cargo も sudo で動くようになっていなければならない。Rust を使ってみよう
require "rust_gem_sample01" n = 10000 bin = Hash.new(0) n.times do bin[RustGemSample01.rand_norm(2.5).round] += 1 end bin.keys.minmax.then{_1.._2}.each do |b| puts "%4d %s" % [b, "*" * (bin[b].fdiv(n) * 60 * 4).round] endこれは,平均が 0,標準偏差が 2.5 の正規分布の乱数を 10000 回発生させ,その値を整数に丸めたもののヒストグラム(頻度分布図)を描くもの。
だいたいこんなものが出力される。-11 -10 -9 -8 -7 * -6 ** -5 ***** -4 *********** -3 ****************** -2 ****************************** -1 ********************************** 0 ************************************ 1 ********************************** 2 ***************************** 3 ****************** 4 *********** 5 ****** 6 ** 7 * 8 9 10うん,なんかそれっぽいね。
もし,プログラムが動かなくてエラーが出るようなら教えて欲しい。
ファイル構成
. ├── bin │ ├── console │ └── setup ├── Cargo.toml ├── CHANGELOG.md ├── ext │ └── Rakefile ├── Gemfile ├── lib │ ├── rust_gem_sample01 │ │ └── version.rb │ └── rust_gem_sample01.rb ├── LICENSE ├── pkg ├── Rakefile ├── README.md ├── rust_gem_sample01.gemspec ├── src │ └── lib.rs └── test ├── rust_gem_sample01_test.rb └── test_helper.rbちなみに,この図は Rust 製のコマンドラインツール exa で作った。
gem 用ファイルと Rust パッケージ用ファイルの混在
見てのとおり,gem のためのファイルと Rust パッケージのファイルが同階層に混在している。
かいつまんで説明すると,bin
,lib
,pkg
,Rakefile
,rust_gem_sample01.gemspec
,test
あたりが gem のファイル。
Cargo.toml
,src
が Rust パッケージのファイル。Rust のビルドを行うとtarget
ディレクトリーなんかも出来る。ファイル名,ディレクトリー名がかぶらないので,これで問題ないわけだが,これがよい流儀かどうかは判らない。Rutie のサンプルなんかはこういう構成になっていた(たしか)。
少なくとも初見で直ちに区別ができるかというと,心もとない。
Rust 関係を一つのディレクトリーに押し込めたほうがいいかもしれない3。Rust コード
Rust コードはライブラリークレート
rand_distr_for_ruby
だけを持つパッケージとなっている。
依存クレートはCargo.toml[dependencies] rand = "0.7.3" rand_distr = "0.3.0"だけ。
rand はお馴染み。rand_distr のほうは,もともとrand
に入っていた,正規分布,コーシー分布,二項分布,ポアソン分布などなどの〈一様でない〉分布の乱数を発生させる関数を独立させたものらしい(知らんけど)。コード本体はこれだけ:
src/lib.rsuse rand_distr::{Normal, Distribution}; #[no_mangle] pub extern fn rand_norm(variance: f64) -> f64 { let normal = Normal::new(0.0, variance).unwrap(); normal.sample(&mut rand::thread_rng()) }呼ばれるたびに
rand_distr::Normal
を生成しているのは無駄な気がする。
まあ実用目的じゃないので。同じ分布で多数の乱数を発生させるなら,ここは一度で済ませたい。生成した
rand_distr::Normal
をずっと保持するのは,Rutie を使えば簡単にできるが((5) 参照),FFI だけでどうやるのかよく分からない。さて,開発中は,この状態で
cargo checkだの
cargo build --release
だのできる。
プロジェクトのルートディレクトリーに Rust のファイルを置くことで,階層を行ったり来たりしなくてすむのは,このディレクトリー構成の利点。Ruby コード
Ruby 側の中核となるコードがこれ。
rust_gem_sample01/lib/rust_gem_sample01.rbrequire "ffi" require "rust_gem_sample01/version" module RustGemSample01 extend FFI::Library lib_name = "rand_distr_for_ruby" file_name = case RbConfig::CONFIG["host_os"].downcase when /darwin/ then "lib#{lib_name}.dylib" when /mingw|mswin/ then "#{lib_name}.dll" when /cygwin/ then "cyg#{lib_name}.dll" else "lib#{lib_name}.so" end ffi_lib File.expand_path(file_name, __dir__) attach_function :rand_norm, [:double], :double endffi という gem を使って Rust の関数を Ruby のメソッドに割り当てている。
FFI(Foreign Function Interface)という,異なる言語間でやり取りするための仕組みを簡単に扱えるようにしたもの。このモジュール定義のコードの大部分が,
ffi_lib
メソッドに渡す引数の決定に費やされている。
このメソッドには読み込むべきライブラリーファイルのパスを渡す。
Rust でコンパイルして出来たライブラリーファイルは,OS によってファイル名が少し異なるのでこんなややこしいことになっているのだ。
この処理が本当に合っているのかよく分からないが,Rutie のコード rutie-gem/lib/rutie.rb を参考にした。attach_function :rand_norm, [:double], :doubleの部分は,読み込んだライブラリーに基づいて,モジュールに Ruby のメソッドを生やす。
第一引数はライブラリーの関数の名前。これが Ruby のメソッドの名前にもなる。
第二引数は関数の引数の型。引数は複数ありうるので配列で与える。:double
というのは,FFI の仕様で決められた型の名前で,倍精度浮動小数点数を表す。よくは知らないけど C 言語のdouble
のことだろう。Ruby の Float がこれに対応する。
第三引数は関数の返り値の型。Rust で作ったライブラリーの関数を,FFI を通して Ruby 側で利用するのは,もうホントにこれだけで済む。簡単。
gem に仕立てる
gemspec
gemspec について,とくに説明を要するところはここ。
rust_gem_sample01/rust_gem_sample01.gemspec# 抜粋 Gem::Specification.new do |spec| # 中略 spec.add_dependency "ffi", "~> 1.13.1" spec.extensions = %w[ext/Rakefile] endffi gem を使っているのでそれをランタイム依存性に追加する(
add_dependency
)のは当然として,重要なのはその次のspec.extensions = %w[ext/Rakefile]のところ。これはなんじゃらほい?
これは gem のインストール時にやるべきことを記述した Rake ファイルのパスを指定したもの。
C 拡張の場合,その役目はふつうextconf.rb
というファイルが担う。sqlite3 gem であれば sqlite3-ruby/ext/sqlite3/extconf.rb がそれにあたる。
全然よく分かってないのだが,C 拡張はこのファイルにしたがって C のコンパイルやらなんやらをやっているらしい。しかし,どうやら
extconf.rb
は C を前提としているらしく,Rust の場合にどう書くのかよく分からない。というか,そもそも Rust では使えないような気がした。
他に手段はないのかと探してみると,どうも Rake タスクを使う方法もあると分かった。次節で具体的に述べる。Rakefile
gemspec で
spec.extensions =
として指定した Rake ファイルに,default
タスクとして書いたことがインストール時に実行されるらしい。rust_gem_sample01/ext/Rakefile# gem のインストール時の処理 # default タスクとして記述する task :default do # Rust がインストールされているか # cargo コマンドが動作するかで判断 begin cargo_v = `cargo -V` rescue Errno::ENOENT raise "Cargo not found. Install it." end # Rust のバージョン(Cargo のバージョンと一致)が一定以上か cargo_version = cargo_v.match(/\Acargo (\d+)\.(\d+)\.(\d+) /)[1..3].map(&:to_i) if (cargo_version <=> [1, 40, 0]).negative? raise "Too old Cargo (ver. #{cargo_v}). Update it." end # Rust のビルド system "cargo build --release", chdir: __dir__ + "/.." # 生成物のファイル名 # OS によって異なる lib_name = "rand_distr_for_ruby" file_name = case RbConfig::CONFIG['host_os'].downcase when /darwin/ then "lib#{lib_name}.dylib" when /mingw|mswin/ then "#{lib_name}.dll" when /cygwin/ then "cyg#{lib_name}.dll" else "lib#{lib_name}.so" end # 生成物を lib/ 直下に移動 FileUtils.mv __dir__ + "/../target/release/#{file_name}", __dir__ + "/../lib/" FileUtils.rmtree __dir__ + "/../target/" endコード中にコメントを書いたが,やっていることは要するに
- Rust 入ってる?
- Rust のバージョン OK?
- ビルド
- ライブラリーファイルの移動
と,ただこれだけ。
Rust コードのビルドの生成物のファイル名を得るところがlib/rust_gem_sample01.rb
とかぶってて DRY じゃないのが気になるけど。
まあ,target/release/
直下のファイルを全部移動してもいいのかもしれない。ところで,Rust でビルドを行うと,必要なライブラリーをダウンロードしてコンパイルして,という過程でいろいろなファイルが出来る。今回の gem の場合,18 MB くらいのファイルがビルド時に生成される。欲しいのはファイル一つ(今回は 300 KB 程度)で,ほかは全部不要。
そこで,タスクの最後の行
FileUtils.rmtree __dir__ + "/../target/"によりお掃除している。
おわりに
C 拡張の場合,gem にコンパイル済みのファイルを含めて配布すべきか否かについて議論がある。
参考:
一長一短があるが,Rust の場合はどうだろう。
おそらくふつうは事前コンパイルを選ぶのだろう。だって,Ruby ユーザーの多くは Rust のコンパイル環境を持っていないだろうから。なので,この記事ではあえて多くの人がやらなさそうな方法を試してみた。
gem のパッケージを作るのは
build
という別の Rake タスクなのだが,install
タスクがbuild
タスクに依存しているので,rake install
だけで両方やってくれる。 ↩Rust のバージョンを 1.40.0 以上としたのは全く何の根拠もない。もっと低いバージョンでも何ら問題ないはずだが,どのバージョンで何がどうなったか把握してないので,最新版(現時点で 1.46.0)よりちょっと古いものにしてみた。バージョン下限は
ext/Rakefile
の 9 行目あたりで設定している。 ↩ゼロから gem を作る場合に,そのほうが作業しすいかもしれない。最初に
bundle gem hoge
とやって gem のプロジェクトを作り,その中に入ってcargo new fuga --lib
とやって Rust のプロジェクトを作ればいいから。 ↩
- 投稿日:2020-10-18T18:03:06+09:00
whereメソッドを使って別のモデルの値を参考に絞り込みを行う。
概要
whereメソッドを使った複数の条件の絞り込みを行ったのでまとめる。
やりたいこと
productモデルが作成された時間の直前に作成されたdeveloperモデルの情報を一つ取得したい。
前提条件
Product(製品)モデルとDeveloper(開発者)を作成。この2つのモデルは関連付けされていない。(ここはあえてです)
Developerモデルはどんな製品を作ったのかの情報(developed_product_number)を持っている。productには製品ナンバー(number)がついておりこの2つの値は等しい。
(だったら関連付けさせようよって話ですが今回はこの条件でいきます。)
同一の製品を作成した複数の開発者の中から製品ができる直前に作成された開発者の値を取得したい。実装
ではまず実際のコードを載せます。
product = Product.find(params[:id]) @Developer = Developer.where(developed_product_number: product.id).where('created_at < ?', product).order(created_at: :desc).limit(1)こんな感じです。解説いきます
product = Product.find(params[:id])対象となるproductのIDを取得して変数productに代入。
ここで変数@Developerに条件をつけて絞り込み検索をした値を入れます。@Developer = Developer.where(developed_product_number: product.id).where('created_at < ?', product).order(created_at: :desc).limit(1)部分的に分けます。
@Developer = Developer.where(developed_product_number: product.number)ここでは先程取得した変数productのidを取得しています。
次に値を絞り込みます。
where('created_at < ?', product).order(created_at: :desc).limit(1)ここでproductが作成された時間より前に作成された複数の開発者の情報取得し、降順から数えて1つ目の開発者の情報を取得しました。
かなり長くなってしまったのでここのコードはスコープなどに切り分ける必要がありますね。
- 投稿日:2020-10-18T17:14:09+09:00
データベースになくても、データとして扱える活動的なハッシュ
はじめに
アンケートなんかでよく見かける、都道府県をプルダウンから選択させる機能。、form_withメソッドを使用して投稿する機能をもたせることができる。しかし、その都道府県はテーブル上では数値で管理されている方が使い勝手がよい。数値で管理できるようにするには、予め、都道府県名とそれに紐づく数字を用意しておく必要がある。そのようなときに便利な機能が、ActiveHashである。
ActiveHashとは
基本的に変更することのないデータをモデル内に記入することで、データベースへ保存せずにデータを取り扱うことができるようになるGem。主に、ビューで、プルダウンメニューとして表示させたいときに使う。
公式ドキュメントはこちら
準備の流れ
1. Gemの記述
2. Gemをインストール
3. モデルを生成1. Gemの記述
ファイルのいちばん下の行でOK
Gemfilegem 'active_hash'2. Gemをインストール
% bundle install
3. モデルを生成
% rails g model モデル名 --skip-migration
モデル名には、プルダウンメニューを一括りにするような名前が望ましい。
ex)野球、サッカー、テニス…
のようなプルダウンを作成する場合は、モデル名はsport
となる。
--skip-migrationオプション
をつけることで、マイグレーションファイルを生成しなくなる。ActiveHashはデータベースに保存しないので、マイグレーションファイルを必要としない。以後、スポーツのプルダウンメニューを例に説明する。
記述から表示までの流れ
4. クラスを定義し継承する
5. プルダウンメニューの項目を記述
6. アソシエーションを記述
7. バリデーションを記述
8. プルダウンメニューを表示4. クラスを定義し継承する
「3. モデル生成」で作られたモデルを以下の記述に変更する。
app/models/sport.rbclass Sport < ActiveHash::Base self.data = [] endActiveHash::Baseを継承することで、ActiveRecordのメソッドが使用できる。
5. プルダウンメニューの項目を記述
プルダウンの中身は、配列にハッシュで入れる。
app/models/sport.rbclass Sport < ActiveHash::Base self.data = [ { id: 1, name: '---' }, { id: 2, name: 'Baseball' }, { id: 3, name: 'Soccer' }, { id: 4, name: 'Tennis' } ] end6. アソシエーションを記述
以下、すでに、別の投稿したものを保存するテーブルが存在する前提の説明。
app/models/post.rbclass Post < ApplicationRecord extend ActiveHash::Associations::ActiveRecordExtensions belongs_to_active_hash :sport end
extend ActiveHash::Associations::ActiveRecordExtensions
の記述によって、belongs_to_active_hashメソッド
が使えるようになる。sportモデルにアソシエーションの記述の必要はない。
※通常のアソシエーションと記述が異なる点に注意
7. バリデーションを記述
app/models/post.rbclass Post < ApplicationRecord extend ActiveHash::Associations::ActiveRecordExtensions belongs_to_active_hash :sport validates :sport_id, presence: true #空の投稿を保存できない validates :sport_id, numericality: { other_than: 1 } #プルダウンの選択が「--」の時は保存できない end8. プルダウンメニューを表示
collection_selectメソッド
collection_select(保存されるカラム名, オブジェクトの配列, カラムに保存される項目, 選択肢に表示されるカラム名, オプション, htmlオプション)collection_select(:sport_id, Sport.all, :id, :name, {}, {class:"sport-select"})保存されるカラム名:テーブルのどのカラムに保存をするのか。
オブジェクトの配列:配列のハッシュ全て表示させる場合は、.all
でOK
カラムに保存される項目:app/models/sport.rb
の中で、id:
としていた部分を、カラムに保存させたい
選択肢に表示されるカラム名:プルダウンでapp/models/sport.rb
のname:
としていた部分を表示させる
オプション:必要に応じて
htmlオプション:クラス名など最後に
とにかく、collection_selectメソッドの引数が多すぎて、混乱しそう…
- 投稿日:2020-10-18T16:43:50+09:00
「ruby」2重ハッシュの取り出し ※復習
ruby.rbuser_data = [ { user: { profile: { name: 'takeshi' } } }, { user: { profile: { name: 'tanaka' } } }, { user: { profile: { name: 'tosiki' } } } ]内容
上記のコードには配列の中に個別のデータが二重ハッシュで存在します。
ユーザー名だけを全て順番にしたい!!!実装
1.
ruby.rb#eachメソッド 使用しないパターン puts user_date[0]["user"]["profile"]["name"] puts user_date[1]["user"]["profile"]["name"] puts user_date[2]["user"]["profile"]["name"]2.
ruby.rb#eachメソッド使用パターン user_date.each do |date| puts date [:user][:profile][:name] end!重要
ハッシュが複数重なっている時はハッシュの1番最初の”キー”から順に連続で表示したいキーまで指定していくことでnameキーのバリューを表示できる。
結果
見事に名前だけ出力することができました!!
1. eachメソッドを使用しない場合は1つずつ添字を指定しキーを連続で記述した
2. eachメソッドを使用し簡潔にキーを連続で指定することができた???????????@MacBook-Pro ruby % ruby posi.rb takeshi tanaka tosiki考察
1番上のコードの記載方法だと見にくく感じるは初心者だからなのかもしれないが、下記の様なコードの記述方法に変換して見た、、、
user_date = [ { user: { profile: { name: "takeshi" }}}, { user: { profile: { name: "tanaka" }}}, { user: { profile: { name: "tosiki" }}} ]
- 投稿日:2020-10-18T16:16:25+09:00
【Rails】お気に入り登録とお気に入りした一覧を表示する巻
はじめに
ネットショッピングしてていいなと思った商品見つけたらお気に入り登録したことありますよね?
そのとき、画面が更新されずにお気に入りやいいねすると♡マークの色が変わったりしますよね。あとは、お気に入りした商品を一覧で確認できたらいいですよね。今回はその実装のメモ書きです。※出品や投稿機能(itemsテーブル)及びログイン機能(usersテーブル)が備わっていることを前提にお話を進めます。
実装の手順(同期編)
完成のイメージはクリックした時に画面の一部が更新されて数字のカウントが増えていく・・・みたいなイメージ。ついでに削除機能も搭載します。お気に入りに追加したものは一覧で確認できるページも作成しちゃいます。
まずは、itemsテーブルとusersテーブルの中間テーブルとして
likesテーブルを作成。↓追加するカラムとアソシエーションはこんな感じ↓
itemsテーブル
Column Type Options ===略===
※追加なし※Association
- has_many :likes
- has_many :liked_users, through: : likes, source: :user #お気に入一覧作りたい時使うが今回は使わない
likesテーブル #中間テーブル
Column Type Options item_id integer null: false user_id integer null: false Association
- belongs_to :item
- belongs_to :user
usersテーブル
Column Type Options ===略===
※追加なし※Association
- has_many :likes
- has_many :liked_items, through: :likes, source: :item #お気に入一覧作りたい時使うが今回は使わない
これにてDB設計は完了
つづいてモデルの作成。以下のコマンドをターミナルに入力rails g model likeテーブルを作成するためマイグレーションファイルを編集
2XXXXXXXXXXXX5_create_likes.rbclass CreateLikes < ActiveRecord::Migration[5.2] def change create_table :likes do |t| t.integer :item_id, null: false t.integer :user_id, null: false end end endからのrails db:migrateします。
そしたらモデルが生成されるのでアソシエーションを書いていきます。user.rbhas_many :likes #追加item.rubyhas_many :likes #追加like.rbclass Like < ApplicationRecord belongs_to :item belongs_to :user validates :user_id, presence: true validates :item_id, presence: true validates_uniqueness_of :item_id, scope: :user_id def self.like_method(item, user) Like.find_by(item_id: item.id, user_id: user.id) end endself.like_method(item, user)
ここでIF分の設定をしちゃいます。
likeテーブルにitem_idとuser_idに入ってるーーーって条件を設定します!
次にコントローラーを作成するため以下のコマンドを実行rails g controller likes無事にコントローラーが作成されたら、
・ビューファイル
・コントローラー
・ルーティング
それぞれ設定していきます。routes.rbーー略ーー resources :items, only: [:new, :create, :edit, :update, :show, :destroy] do resources :likes, only: [:create, :destroy] end ーー略ーー resources :users, only: [:show] do resources :likes, only: [:index] end ーー略ーーcreateとdestroyはitemsにネスト
indexはusersにネストlikes_controller.rbclass LikesController < ApplicationController before_action :set_item, only: [:create, :destroy] def index @items = Item.includes(:item_images).order("created_at DESC") end def create @like = Like.create(item_id: params[:item_id],user_id: current_user.id) redirect_to item_path(@item.id) end def destroy @like = Like.find_by(item_id: params[:item_id],user_id: current_user.id) @like.destroy redirect_to item_path(@item.id) end def set_item @item = Item.find(params[:item_id]) end endcreateとdestroyはビューファイルがないのでリダイレクトの設定が必要になります。
index.html.hamlーー略ーー - if user_signed_in? && Like.like_method(@item, current_user) .check_btn__like--add{data: {item_id: @item.id}} = link_to "/items/#{@item.id}/likes/:id" , method: :DELETE do = icon( "fa", "star") お気に入り = @item.likes.length - elsif user_signed_in? .check_btn__like{data: {item_id: @item.id}} = link_to "/items/#{@item.id}/likes", method: :POST do = icon( "fa", "star") お気に入り = @item.likes.length - else .check_btn__like = link_to new_user_session_path do = icon( "fa", "star") お気に入り = @item.likes.length ーー略ーーお気に入り登録する時の画面はこちら
index.html.haml.mypage_Wrapper %section.mypage_title お気に入り一覧 %section.like_Wrapper .like__box - @items.each do |item| - if user_signed_in? && Like.like_method(item, current_user) .like__box__product .like__image = link_to item_path(item.id) do = image_tag item.item_images[0].image.url .like__name = item.name %ul %li = item.price.to_s %li = "円" %li.like__tax = "(税込)" %li.like__icon = link_to "/items/#{item.id}/likes/:id" , method: :DELETE do = icon("fas", "star") .bottom_Wrapperこれが、お気に入りした商品一覧で表示するもの
like.scss.like_Wrapper { width: 100%; } .like__box { background-color:white; margin: 0 auto; width: 65%; display: flex; justify-content: center; flex-wrap: wrap; &__product { width: 180px; height: 90%; margin: 25px 2% 0; transition: all .3s ease-in-out; .like__image { width: 180px; height: 140px; margin-bottom: 3%; img{ width: 100%; height: 100%; object-fit: cover; } } .like__name { font-weight: bold; } ul{ display: flex; font-size: 16px; li{ font-weight: bold; } .like__tax { font-size: 10px; writing-mode: lr-tb; width: 30px; line-height: 30px; margin-left: 2%; margin-right: 24%; } .like__icon { i { font-size:1.1em; color: orange; } } } } &__product:hover { cursor: pointer; transform: scale(1.1, 1.1); } }一応、CSSも
ここまでの実装で同期した状態で実装できるかと思います!
いったんここまで、Ajaxを利用した非同期通信を実装するとよりリッチになるのでそのことを書いて行けたらいいなと・・・
それはまた後日・・・参考
https://techtechmedia.com/favorite-function-rails/#i
https://qiita.com/naberina/items/c6b5c8d7756cb882fb20
https://qiita.com/hisamura333/items/e3ea6ae549eb09b7efb9
https://qiita.com/shh-nkmr/items/48fe53282253d682ecb0
- 投稿日:2020-10-18T14:54:05+09:00
オブジェクト指向まとめ
前置き
今更ながらオブジェクト指向の概要をまとめていく。私もオブジェクト指向を理解するのに苦しんだのでなるべく初学者にわかりやすく簡潔に書いていきます。
マサカリ上等ですのでコメントお待ちしております。1.オブジェクト
オブジェクトとは「対象」「物」という意味で、プログラミングではデータ処理の集まりのことを指している。
Rubyでいうならば、配列やHash、数字や文字列など全てのデータは(モノ)オブジェクトである2.オブジェクト指向
オブジェクト指向プログラミングとは、モノ(オブジェクト)の作成と操作として見る考え。
3.クラス(class)
クラス(class)はオブジェクト指向において、データと処理をひとまとめにした設計図のようなもの。
基本的にクラスを元にしてオブジェクトを生成することで使えるようになる4.プロパティ(property)
オブジェクトが持っているデータのことをプロパティ(属性)という。
5.メソッド(method)
オブジェクトが持っている処理のこと。人でいえば「走る、歩く、止まる」などのオブジェクトが何らかのアクションを起こす処理の事
def メソッドの名前 やりたい処理 end例えば「こんにちは」と呼びだすhelloメソッドを定義するのであれば
def hello puts 'こんにちは' end hello #=> こんにちはといった感じになる。
6.インスタンス(instance)
インスタンスとはクラスから生成されたオブジェクトの事。
インスタンス作成の際にはnewメソッドを使用します。class human def initialize(name) @name = name end def hello puts "こんにちは" end end human = human.new("徳川家康")ここで定義されているinitializeメソッドはインスタンスが生成された時に自動で実行されるメソッドです。
これでnameという属性に「徳川家康」という値がセットされました。humanクラスから生成されたhumanというインスタンスは徳川家康という固有の名前をもち、helloというメソッドを使うことができます。
この1行だけでこれだけの情報を持ったオブジェクトが作成できるのできます。7.カプセル化
オブジェクトが持つデータや処理のうち、別のオブジェクトから直接利用される必要のないものを隠すことを言い、利用する場合は外部から操作するために作られた処理を設けることを言う。先ほどのレーシングカーの例でも出てきた、オブジェクト指向の基本概念。
カプセル化にはpublicとprivateがあります。
publicに記述したメソッドなどは外部からのアクセスは可能だが、privateを指定すると、呼び出す際にエラーとなります。publicの場合
class human #public => 何も指定しなければrubyはデフォルトでpublicになる。基本的に省略可能。 def initialize(name) @name = name end def hello puts "こんにちは" end end human = human.new("徳川家康") puts human.name #=> 徳川家康が出力されるprivateの場合
class human private def initialize(name) @name = name end def hello puts "こんにちは" end end human = human.new("徳川家康") puts human.name #=> エラーが出力される8.継承
特定のオブジェクトの機能を引き継いで使う事。
似たようなオブジェクトを複数作る時に、全てのプロパティやメソッドをいちいちプログラミングするのは非常に手間が掛かるが、継承を使うことにより、同じ機能を実装できる。class Human def work puts '歩きました' end end #Humanクラスを継承 class IeyasuTokugawa < Human def unification puts "天下統一しました" end end IeyasuTokugawa = IeyasuTokugawa.new puts IeyasuTokugawa.unification #=> 天下統一しました puts IeyasuTokugawa.work #=>歩きましたクラスを継承した場合、クラス自体で定義されたメソッドの他に親クラスで定義されているメソッドも使用することが出来ます。先ほどの例で言えば「IeyasuTokugawa」クラスのオブジェクトは「unification」メソッドの他に親クラスの「work」メソッドも実行することが出来ます。
クラス内で定義されたメソッドだけではなく、スーパークラス(親クラス)で定義されているメソッドも実行できることが確認できます。
9.ポリモーフィズム
ポリモーフィズムとは(Polymorphism)とは、オブジェクト指向プログラミングにおける概念、手法の一つです。日本語で「多様性」とも言います。
異なるクラスに対し、同一のインターフェースが提供されていることを言います。クラスを使う側は、クラスの実態を意識せずにメソッドを呼び出せます。
class Human def speak(voice='') "#{self.name}: #{voice}" end end #継承 class NobunagaOda < Human def speak(voice='鳴かぬなら 殺してしまえ ホトトギス') super end end class IeyasuTokugawa < Human def speak(voice='水よく船を浮かべ 水よく船を覆す') super end end NobunagaOda = NobunagaOda.new NobunagaOda.name = '織田信長' IeyasuTokugawa = IeyasuTokugawa.new IeyasuTokugawa.name = '徳川家康' NobunagaOda.speak #=>織田信長: 鳴かぬなら 殺してしまえ ホトトギス IeyasuTokugawa.speak #=>徳川家康: 水よく船を浮かべ 水よく船を覆す同じspeakメソッドであっても、クラスでの定義に応じて挙動の変化することがわかりました。
10.オブジェクト指向のメリット・デメリット
メリット
1.プロブラムのメンテナンスがしやすくなる
・プログラムの一つ一つを小さなまとまりで考える事が出来る為、複雑なプログラムが減ります。改修による影響度が減り、メンテナンスがしやすくなります。
2.分業化ができる
・システムの規模が大きくなるほど、プログラムの実装は膨大になります。オブジェクト指向は平行で大人数での実装が可能の為生産性が上がります。また似た機能等を他に影響を与える事なく機能を実装することも可能です。
3.プログラムの品質が高くなる
・あらゆるところが部品化されている為に問題箇所の特定が容易です。まとまりのあるプログラムのためコードの可読性も高く直感的にわかりやすい。
デメリット
1.難易度が高く教育コストがかかる
・開発現場で活躍する為には付け焼き刃の知識では役に立たない。抽象的な概念が多く、しっかり理解する為にはそれなりに
現場経験が必要2.設計力が試される
・継承やポリモーフィズムなど便利な概念が使用できる代わりに、他のコードとの影響性や拡張性、共通化などより可読性高く品質を上げる為には設計力が必要不可欠。
最後に
私もまだまだ初学者のため技術力がありません。プログラムを書いている際に基本的概念は少なくとも頭に入れておくことが重要だと思います。他の初学者の皆さんも一緒に覚えていって欲しいと思います。
- 投稿日:2020-10-18T13:15:42+09:00
post to Qiita patching version
すでにqiitaにあげた記事を更新するversion
に最初に投稿するversionの作成記録があります.さらに,すでにqiitaにあげた記事を更新するversionです.下のcodeを
ruby post_final.rb post_final.orgなんかでできます.最初のpostでは新しいのを作り,そのあとitemsのidをorgに記します.それがあるとそのidにpatchします.
さらに,'open', 'teams'を選べます.defaultは'private'.
ruby post_final.rb post_final.org teamsなんてね.
keyは
code
#! /usr/bin/env ruby
1 require "net/https" 2 require "json" 3 4 def get_title_tags(src) 5 $conts = File.read(src) 6 title = $conts.match(/\#\+(TITLE|title|Title): (.+)/)[2] || "テスト" 7 m = [] 8 tags = if m = $conts.match(/\#\+(TAG|tag|Tag|tags|TAGS|Tags): (.+)/) 9 m[2].split(',').inject([]) do |l, c| 10 l << {name: c.strip} #, versions: []} 11 end 12 else 13 [{ name: "hoge"}] #, versions: [] }] 14 end 15 p tags 16 return title,tags 17 end 18 19 src = ARGV[0] || 'README.org' 20 title, tags = get_title_tags(src) 21 p title 22 p tags 23 24 system "emacs #{src} --batch -l ~/.emacs.d/site_lisp/ox-qmd -f org-qmd-export-to-markdown --kill" 25 26 lines = File.readlines(src.gsub('org','md')) 27 lines << "------\n - **source** #{Dir.pwd}/#{src}\n" 28 29 m = [] 30 patch = false 31 if m = $conts.match(/\#\+qiita_id: (.+)/) 32 qiita_id = m[1] 33 patch = true 34 else 35 qiita_id = '' 36 end 37 38 39 case ARGV[1] 40 when 'teams' 41 qiita = 'https://nishitani.qiita.com/' 42 p access_token = ENV['QIITA_TEAM_WRITE_TOKEN'] 43 private = false 44 when 'open' 45 qiita = 'https://qiita.com/' 46 p access_token = ENV['QIITA_WRITE_TOKEN'] 47 private = false 48 else 49 qiita = 'https://qiita.com/' 50 p access_token = ENV['QIITA_WRITE_TOKEN'] 51 private = true 52 end 53 54 params = { 55 "body": lines.join, #"# テスト", 56 "private": private, 57 "title": title, 58 "tags": tags 59 } 60 61 if patch 62 path = "api/v2/items/#{qiita_id}" 63 else 64 path = 'api/v2/items' 65 end 66 p qiita+path 67 uri = URI.parse(qiita+path) 68 69 http_req = Net::HTTP.new(uri.host, uri.port) 70 http_req.use_ssl = uri.scheme === "https" 71 72 headers = {"Authorization" => "Bearer #{access_token}", 73 "Content-Type" => "application/json"} 74 if patch 75 res = http_req.patch(uri.path, params.to_json, headers) 76 else 77 res = http_req.post(uri.path, params.to_json, headers) 78 end 79 80 p res.message 81 82 res_body = JSON.parse(res.body) 83 res_body.each do |key, cont| 84 if key == 'rendered_body' or key == 'body' 85 puts "%20s brabrabra..." % key 86 next 87 end 88 print "%20s %s\n" % [key, cont] 89 end 90 system "open #{res_body["url"]}" 91 qiita_id =res_body["id"] 92 unless patch 93 File.write(src,"#+qiita_id: #{qiita_id}\n"+$conts) 94 endえっと,refactoringしてください.
課題
openとteamsで二箇所に出したいときは,どっちがどっちかわからなくなりますね.その辺り,自動で更新してくれるようにできんかな...
- source ~/git_hub/ruby_docs/qiita/qiita_post/post_final.org
- 投稿日:2020-10-18T13:15:05+09:00
Gem paranoiaを使った論理削除
備忘録
今回自作アプリ作成の際に論理削除を用いたので備忘録として記録します。
プログラミング初心者のため間違いがあればご指摘お願いします!
今回はrails、MySQLを使い開発を進めています。論理削除とは
一般的な削除は物理削除と言い削除したらDB上からもデータが削除されます。
それに対し、論理削除とは一般的な削除とは違い、あたかも削除されたように見せてDBにはデータが残っている状態のことを言います。
削除履歴を表示したり、データの復元などにも使われます。ステップ1 Gemの導入
論理削除の手間を大幅にカットしてくれるGemのparanoiaを導入する。
Gemfileに記述しターミナルでbundle installgem 'rails_12factor'ステップ2 カラムを追加
論理削除をするとこちらのカラムに削除した日時が挿入され、削除フラグを立てる事ができます。
class AddDeletedAtToCategories < ActiveRecord::Migration[6.0] def change add_column :categories, :deleted_at, :datetime end end正しければ、$ rails db:migrateして下さい。
モデルに以下の記述をします。ステップ3 モデルを編集する
acts_as_paranoid
まとめ
以上の設定でparanoiaを使った論理削除の設定が完了しました!
普通に実装しようとするとかなり大変だと思いますが、paranoiaを使う事で簡単に実装する事ができました!
プログラミング初心者の方の役に立てると幸いです。
- 投稿日:2020-10-18T11:51:17+09:00
Railsガイド(Action Controller の概要)を改めて読んで
5. セッション
Railsアプリケーションにはユーザーごとにセッションオブジェクトが生成され、前のリクエスト情報を継続して利用することができる。セッションは
Controller
とView
でのみ利用でき、少量のデータがセッションに保存される。
セッションの保存先としては、以下のようなストレージから選ぶことができる。
ActionDispatch::Session::CookieStore
: すべてのセッションをクライアント側のブラウザのcookieに保存するActionDispatch::Session::CacheStore
: データをRailsのキャッシュに保存するActionDispatch::Session::ActiveRecordStore
: Active Recordを用いてデータベースに保存する (activerecord-session_store gemが必要)ActionDispatch::Session::MemCacheStore
: データをmemcachedクラスタに保存する (この実装は古いのでCacheStoreを検討すべき)セッションは固有のIdを持っており、それを
cookie
に保存します。セッションIDをURL
で渡すことはセキュリティ上とても危険があるため、Rails
ではデフォルトで許可されていない。なので、セッションIDは必ずcookie
で渡さなくてはならない。多くの
SessionStore
(のセッションID)は、サーバー上のセッションデータを検索するために使用される。
ただしCookieStore
だけは、cookie
自身にすべてのセッションデータを保存する(必要であればセッションIDも利用可能)。
Rails
ではCookieStore
がデフォルトで使われ、Rails
での推奨セッションストアとなっている。
CookieStore
の利点は、
- 非常に軽量な点
- 新規で利用する時の準備が全く不要な点
cookie
データは改竄防止のために暗号署名が与えられており、cookie自身も暗号化されているため、内容を他人に読目内容になっている。(改竄されたcookie
はRails
が拒否してくれる)
CookieStore
には約4KBのデータを保存でき、容量はこれで十分。利用するセッションストアの種類に関わらず、セッションに大量のデータを保存することは好ましくない。ユーザーセッションに重要なデータが含まれていない場合や、セッションを長期保存する必要のない場合は
ActionDispatch::Session::CacheStore
を検討するとよい。
メリットは、Webアプリケーションに設定されているキャッシュ実装を利用して保存するため、既存のキャッシュインフラをそのまま利用することができる。よって、特段準備する必要がない。
デメリットは、セッションが短命なので、セッションがいつでも消える可能性がある点である。5-1. セッションにアクセスする
セッションへのアクセスは、コントローラ内の
session
インスタンスメソッドを使って可能となる。セッションの値は、ハッシュに似たキー/値ペアを使って保存される。class ApplicationController < ActionController::Base private # キー付きのセッションに保存されたidでユーザーを検索する # :current_user_id はRailsアプリケーションでユーザーログインを扱う際の定番の方法。 # ログインするとセッション値が設定され、 # ログアウトするとセッション値が削除される。 <方法①> def current_user @current_user ||= session[:current_user_id] && User.find_by(id: session[:current_user_id]) end <方法②> def current_user if session[:current_user_id] @current_user ||= User.find_by(id: session[:current_user_id]) end end endセッションに何かを保存したければ、ハッシュのようにキーを割り当てる。
class LoginsController < ApplicationController # "Create" a login, aka "log the user in" def create if user = User.authenticate(params[:username], params[:password]) # セッションのuser idを保存し、 # 今後のリクエストで使えるようにする session[:current_user_id] = user.id redirect_to root_url end end endセッションからデータの一部を削除したい場合は、そのキーバリューペアを削除します。
class LoginsController < ApplicationController # ログインを削除する (=ログアウト) def destroy # セッションidからuser idを削除する session.delete(:current_user_id) # メモ化された現在のユーザーをクリアする @_current_user = nil redirect_to root_url end endセッション全体をリセットするにはreset_sessionを使う。
5-2. Flash
flash
はセッションの中の特殊な部分で、リクエストごとにクリアされる。リクエスト直後のみ参照可能となる特徴を持っており、エラーメッセージをview
に渡したりする。
flash
はハッシュとしてアクセスし、これをFlashHash
インスタンスと呼ぶ。
例として、ログアウトする動作を扱ってみる。コントローラでflashを使うことで、次のリクエストで表示するメッセージを送信することができる。class LoginsController < ApplicationController def destroy session.delete(:current_user_id) flash[:notice] = "You have successfully logged out." redirect_to root_url end end
flash
メッセージはリダイレクトの一部として割り当てることもできる。オプションとして:notice
、:alert
の他に、一般的な:flash
も使用可能。redirect_to root_url, notice: "You have successfully logged out." redirect_to root_url, alert: "You're stuck here!" redirect_to root_url, flash: { referral_code: 1234 }6. Cookie
Webアプリケーションでは、
cookie
と呼ばれる少量のデータをクライアント側(ブラウザ)に保存できる。HTTP
は「ステートレス」なプロトコルなので、基本的にリクエストとリクエストの間には何の関連も持たないが、cookie
を使うとリクエスト同士の間で (あるいはセッション同士の間であっても) このデータが保持されるようになる。Rails
ではcookies
メソッドを使ってcookie
に簡単にアクセスできる。アクセス方法はセッションの場合とよく似ていて、ハッシュのように動作する。class CommentsController < ApplicationController def new # cookieにコメント作者名が残っていたらフィールドに自動入力する @comment = Comment.new(author: cookies[:commenter_name]) end def create @comment = Comment.new(params[:comment]) if @comment.save flash[:notice] = "Thanks for your comment!" if params[:remember_name] # コメント作者名を保存する cookies[:commenter_name] = @comment.author else # コメント作者名がcookieに残っていたら削除する cookies.delete(:commenter_name) end redirect_to @comment.article else render action: "new" end end endセッションを削除する場合はキーに
nil
を指定することで削除しましたが、cookie
を削除する場合はこの方法ではなく、cookies.delete(:key)
を使用しなければならない。
Rails
では、機密データ保存のために署名済みcookie jar
と暗号化cookie jar
を利用することもできる。
- 署名済み
cookie jar
では、暗号化した署名をcookie
値に追加することで、cookie
の改竄を防ぐ。- 暗号化
cookie jar
では、署名の追加と共に、値自体を暗号化してエンドユーザーに読まれることのないようにしてくれる。これらの特殊な
cookie
ではシリアライザを使って値を文字列に変換して保存し、読み込み時に逆変換(deserialize
)を行ってRuby
オブジェクトに戻している。Rails.application.config.action_dispatch.cookies_serializer = :json新規アプリケーションのデフォルトシリアライザは
:json
となっている。
cookie
には文字列や数字などの「単純なデータ」だけを保存することが推奨されている。cookie
に複雑なオブジェクトを保存しなければならない場合は、後続のリクエストでcookie
から値を読み出す場合の変換については自分で面倒を見る必要がある。
cookie
セッションストアを使う場合、session
やflash
ハッシュについても同様のことが該当する。7. XMLとJSONデータを出力する
ActionController
のおかげで、XML
データやJSON
データの出力 (レンダリング) は非常に簡単に行える。scaffold
を使って生成したコントローラは以下のようになっている。class UsersController < ApplicationController def index @users = User.all respond_to do |format| format.html # index.html.erb format.xml { render xml: @users } format.json { render json: @users } end end end上のコードでは、
render xml: @users.to_xml
ではなくrender xml: @users
となっていることにご注目。Rails
は、オブジェクトがString
型でない場合は自動的にto_xml
を呼んでくれる。
.to_xml
配列またはハッシュをXML形式に変換するメソッド。
array.to_xml([オプション]) hassh.to_xml([オプション])8. フィルタ
フィルタは、コントローラにあるアクションの「直前 (before)」、「直後 (after)」、あるいは「直前と直後の両方 (around)」に実行されるメソッド。
フィルタは継承されるので、フィルタをApplicationControllerで設定すればアプリケーションのすべてのコントローラでフィルタが有効となる。
「before系」フィルタのよくある使われ方として、ユーザーがアクションを実行する前にログインを要求するというのがあります。このフィルタメソッドは以下のようになる。
class ApplicationController < ActionController::Base before_action :require_login private def require_login unless logged_in? flash[:error] = "You must be logged in to access this section" redirect_to new_login_url # halts request cycle end end endこのメソッドはエラーメッセージを
flash
に保存し、ユーザーがログインしていない場合にはログインフォームにリダイレクトするというシンプルなものである。この例ではフィルタを
ApplicationController
に追加したので、これを継承するすべてのコントローラが影響を受ける。つまり、アプリケーションのあらゆる機能についてログインが要求されることになる。当然だが、アプリケーションのあらゆる画面で認証を要求してしまうと、認証に必要なログイン画面まで表示できなくなるという困った事態になってしまうので、このようにすべてのコントローラやアクションでログイン要求を設定すべきではない。skip_before_action
メソッドを使えば、特定のアクションでフィルタをスキップすることが可能となる。class LoginsController < ApplicationController skip_before_action :require_login, only: [:new, :create] end上記のようにすることで、
LoginsController
のnew
アクションとcreate
アクションをこれまでどおり認証不要にすることができる。特定のアクションについてだけフィルタをスキップしたい場合には:only
オプションを使うことができる。逆に特定のアクションについてだけフィルタをスキップしたくない場合は:except
オプションを使う。これらのオプションはフィルタの追加時にも使うことができるので、最初の場所で選択したアクションに対してだけ実行されるフィルタを追加することもできる。8-1.
after
フィルタとaround
フィルタ「before系」フィルタ以外に、アクションの実行後に実行されるフィルタや、実行前実行後の両方で実行されるフィルタを使うこともできる。
「after系」フィルタは「before系」フィルタと似ているが、「after系」フィルタの場合アクションは既に実行済みであり、クライアントに送信されようとしている応答データにアクセスできる点が「before系」フィルタとは異なる。当然ながら、「after系」フィルタをどのように書いても、アクションの実行が中断するようなことはない。ただし、「after系」フィルタは、アクションが成功した後にしか実行されず、リクエストサイクルの途中で例外が発生した場合は実行されないのでご注意する。
「around系」フィルタを使う場合は、フィルタ内のどこかで必ず
yield
を実行して、関連付けられたアクションを実行してやる義務が生じます。これはRackミドルウェアの動作と似ている。たとえば、何らかの変更に際して承認ワークフローがあるWebサイトを考えてみる。管理者はこれらの変更内容を簡単にプレビューし、トランザクション内で承認できるとする。
class ChangesController < ApplicationController around_action :wrap_in_transaction, only: :show private def wrap_in_transaction ActiveRecord::Base.transaction do begin yield ensure raise ActiveRecord::Rollback end end end end「around系」フィルタの場合、画面出力 (レンダリング) もその作業に含まれることにご注意する。特に上の例では、ビュー自身がデータベースから (スコープなどを使って) 読み出しを行うとすると、その読み出しはトランザクション内で行われ、データがプレビューに表示される。
あえて
yield
を実行せず、自分でレスポンスをビルドするという方法もある。この場合、アクションは実行されない。9. リクエストフォージェリからの保護
- 投稿日:2020-10-18T11:01:07+09:00
git add .した後にgit statusするとChanges not staged for commit:と出る時の対処法
内容
GitHubにcommitしようと思いgit add . をしてgit status で確認したところ、"Changes not staged for commit:"と出てうまく反映されてなかった
実際出たエラー内容
$ git add . $ git status On branch login Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: app/controllers/users_controller.rb modified: db/mysql/volumes/ib_logfile0 modified: db/mysql/volumes/ibdata1 modified: db/mysql/volumes/ibtmp1 modified: db/mysql/volumes/myapp_test/users.ibd modified: db/mysql/volumes/mysql/innodb_index_stats.ibd modified: db/mysql/volumes/mysql/innodb_table_stats.ibd modified: spec/requests/users_signup_spec.rb Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: db/mysql/volumes/ibdata1解決方法
このエラーの場合"db/mysql/volumes/ibdata1"がうまくステージされてないよと言われているので,ファイル名を指定してgit addコマンドを実行する
$ git add db/mysql/volumes/ibdata1 $ git status On branch login Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: app/controllers/users_controller.rb modified: db/mysql/volumes/ib_logfile0 modified: db/mysql/volumes/ibdata1 modified: db/mysql/volumes/ibtmp1 modified: db/mysql/volumes/myapp_test/users.ibd modified: db/mysql/volumes/mysql/innodb_index_stats.ibd modified: db/mysql/volumes/mysql/innodb_table_stats.ibd modified: spec/requests/users_signup_spec.rbちゃんと反映されました!
最後に
コミットはこまめに行うので、エラーが出たら焦らず対処していきたいです。
- 投稿日:2020-10-18T10:32:59+09:00
migrateでカラムに外部キーを追加する
プログラミング初心者でも理解できる内容を書いていければと思います。
railsでmigrateする際に、カラムにモデルの関連づけのために外部キーを追加する方法を整理します。
前提としては、ユーザーと、そのユーザーの複数の投稿(tweet)を紐付けたい、ということです。
◉外部キーをテーブルに追加する
そのためには、新しく作成するtweetsテーブルに user_id というカラムを追加して、投稿を所有しているユーザーの id を格納するようにします。
このような場合では、referenceというデータ型を使用して外部キーのカラムを追加します。
tweetsテーブルを作成するマイグレーションファイルを、こんな感じに記述します。
class CreateTweets < ActiveRecord::Migration[5.2] def change create_table :tweets do |t| t.string :text, null: false t.references :user, foreign_key: true, null: false t.timestamps end end endt.references :user, foreign_key: true ここですね。
t.referencees :user で、user_idというカラムを作成してくれます。そしてindexを自動で付与してくれます。indexにより検索が楽になります。
ただし、データの読み込みや取得が早くなるメリットがあるかわりに、書き込みの速度が遅くなるというデメリットがあるみたいです。
これだけだと、外部キー制約はつかないので、
foreign_key: true をつけます。
また、既存のテーブルに外部キーを追加したい場合は次のようになります。
class AddColumn < ActiveRecord::Migration[5.0] def change add_reference :tweets, :user, foreign_key: true end endちなみに、、、
null: false と記述することで、NOT NULL制約をつけることができます。データベースカラムの値にNULLを格納したくない場合に、つまり、あるモデルの属性の値が必ず入らなければいけないと期待するときに、このような制約をつけます。
- 投稿日:2020-10-18T00:39:39+09:00
【Rails】画面からアップロードされたXMLファイルをHash型で読み込む方法
Rails(5系)を使ったアプリケーションで、画面からアップロードされたXMLファイルをHash型で読み込みたい場合の実装例です。
アップロードするXMLファイル
今回は検証用に以下のXMLファイルを使用
<?xml version="1.0" encoding="UTF-8"?> <items> <item>AAA</item> <item>BBB</item> <item>CCC</item> </items>html.erbの記述
適当にファイルとボタンタグを配置します。(デザイン・スタイルはお好みでどうぞ!)
<%= form_tag xxx_path, multipart: true do %> <label>XMLファイルの読み込み</label> <div class="row"> <div class="col-sm-2"> <%= file_field_tag :file, class: 'btn btn-primary' %> </div> </div> <div class="row"> <div class="col-sm-4"> <%= submit_tag '送信', class: 'btn btn-primary' %> </div> </div> <% end %>Controller.rbの記述
xml = REXML::Document.new(File.new(params[:file].path).read) xml_h = Hash.from_xml(xml.to_s)上記の結果以下のようなHash形式でファイルの中身を取得できます。
{"items"=>{"item"=>["AAA", "BBB", "CCC"]}}
- 投稿日:2020-10-18T00:11:38+09:00
Active StrageをRSpecでテスト
はじめに
Active Strageを使用し、画像投稿ができるアプリを制作中です。
その際のテストの書き方の例を紹介します。factories
FactoryBot.define do factory :post do ... trait :post_image do image { fixture_file_upload("app/assets/images/XXX.PNG") } end ...letを定義
let(定義名) { 定義の内容 } let(:post_image) { FactoryBot.create(:post_image) }使用する(例)
post = FactoryBot.create(:post,:post_image)参考にさせて頂いたサイト
https://qiita.com/maca12vel/items/ee4d16827f24f69080ae
https://shuttodev.hatenablog.com/entry/2019/09/04/015756
- 投稿日:2020-10-18T00:10:59+09:00
[Rails] ActiveStorageを使ってpostsにアバターを表示
やりたいこと
Posts#index(以下タイムライン)にアバターやユーザー名を表示したい
前提
devise導入
ActiveStorage導入
アバターをusersモデルに追加やり方
postsモデルに
:avatar
のhas_one_attached
とuserインスタンスメソッドを追加post.rbclass Post < ApplicationRecord validates :content, {presence: true, length: {maximum: 140}} validates :user_id, {presence: true} has_one_attached :avatar def user return User.find_by(id: self.user_id) end endviewに.userを追加
index.html.erb<% @posts.each do |post| %> <div class="posts-index-item mb-20"> <div class="posts-index-user"> <!--avatar--> <div class="posts-index-img d-inline"> <% if post.user.avatar.attached? %> <%= image_tag post.user.avatar, class: "avatar-index rounded-circle mx-auto" %> <% else %> <img class="avatar-index rounded-circle mx-auto" src="<%= "/images/default_user.png" %>" alt="Userimage"> <% end %> </div> <!--username--> <div class="posts-index-username d-inline"> <%= link_to post.user.username, users_show_path %> </div> </div> <!--content--> <%= link_to(post.content, "/posts/#{post.id}") %> </div> <% end %>結果
まとめ
userメソッドによってparamsの使えないposts#indexでもUserモデルを扱える。
⇒post.user.avatar
によりUserモデルに紐づいた:avatar
を取り扱える
- 投稿日:2020-10-18T00:00:04+09:00
データベースの操作(SQL編)
データベースの操作 ~SQL編~
- SQLの使用方法を知る
- データベースの基本的な操作を理解
- テーブルの基本的な操作を理解
ターミナルでの使用方法
ターミナル# ホームディレクトリに戻る % cd # MySQLに接続 % mysql -u rootSequel Proでの使用方法
Sequel Proを使用してログインする際は、下記のように入力
ターミナルで以下のSQLを実行
ターミナルmysql> CREATE DATABASE sqltest;MySQLで以下のSQL文を実行
ターミナルmysql> SHOW DATABASES;データベースを削除
ターミナルmysql> DROP DATABASE sqltest;データベースを指定
データベースを作成して、選択
ターミナルmysql> CREATE DATABASE sqltest; Query OK, 1 row affected (0.00 sec)コマンドでテーブルを作成
ターミナルmysql> CREATE TABLE goods (id INT, name VARCHAR(255));作成したテーブルを表示
ターミナルmysql> SHOW TABLES;テーブルの構造を確認
ターミナルmysql> SHOW columns FROM goods;カラムを追加
ターミナルmysql> ALTER TABLE goods ADD (price int, zaiko int);カラムを変更
ターミナルmysql> ALTER TABLE goods CHANGE zaiko stock int;カラムを削除
ターミナルmysql> ALTER TABLE goods DROP stock;現場からは以上です!