- 投稿日:2020-02-11T23:30:40+09:00
【備忘録】Ruby : 基本的なprocオブジェクトの使い方とyieldの使い方
参考: https://shinmedia20.com/ruby-proc
sample.rbaddAB = Proc.new { |a,b| a.to_i+b.to_i } #ブロックをオブジェクト化 puts "和は#{addAB.call(2,4)}です。" #callで実行することでオブジェクト化したブロックの処理を行える。 puts "三つ目の引数を加えた和は#{addAB.call(2,4,4)}" #三つ目の引数は無視される def sample(&sample_proc) #引数にブロックを渡すsampleメソッド #引数に&をつけることでブロックだと認識させている。 puts sample_proc.call(2) #渡されたブロックに引数を2を渡して実行する。 end sample { |n| n**4 } #sampleメソッドに引数 { |n| n**4 } を渡している。 #sampleメソッド内でブロック変数2を渡してブロックを実行している。 def sample2 (&block) puts block.call(3) end sample2 { |n| n*n } ##出力値は 9 def sample3(&block) puts yield(5) # yield は引数で渡されたブロックを実行する # つまり渡された引数(ブロック)に対してcallメソッドを使わなくても良い。 end sample3 { |n| n*n } def sample4 # sample3の引数(ブロック)の記述なしver puts yield(6) end sample4 { |n| n*n } def sample5 if block_given? #メソッドに引数としてブロックを渡したか判定 #渡しているなら true , 渡していないなら false puts yield else puts "blockは渡されませんでした" end end sample5 ## 引数を渡していない : expect => blockは渡されませんでした sample5 { puts "blockです" } ## 引数を渡している : expect => blockです
- 投稿日:2020-02-11T22:54:49+09:00
[Ruby on Rails] includesメソッドの書き方まとめ
実務でまさかのincludesメソッドに戸惑う
「この画面の情報をスプレッドシートに出力しておいて」
「はーい」
(データを取ってくるだけなら余裕だろ、、、)(あれ、関連するモデル多すぎじゃね?)
(関連するモデルに関連するモデルも取得、、あれ、、頭が、、、)
って感じで整理できていない部分がありましたので、今回まとめておきます。
前提
- モデルの関連付けだけをテーマに書きます。
- includesする際、単数形か複数形かはアソシエーションによって判断します。
- N+1問題を解決するためにincludesします。( 参考記事 )
今回例示するモデル
- Tweets:ツイート
- Users:ユーザー
- Comments:ツイートに対するコメント
- Tags:ツイートにつけるタグ
- Addresses:ユーザーの住む住所
ここでは便宜上、関連するモデルを子モデル、子モデルに関連するモデルを孫モデルと呼ぶことにします。
① 子モデルが1つの場合
Tweet.includes(:user)こちらはTweetに紐づくUser(子)を取得します。
② 子モデルが2つの場合
Tweet.includes(:user, :tags)こちらはTweetに紐づくUser(子)およびTag(子)を取得します。
([:user, :tags])
と書く場合もあるので、それは所属するチームでの決まりに従うと良いでしょう。③ 子モデル・孫モデルが1セットの場合
Tweet.includes(user: :address):(コロン)の位置に注意です。こちらはTweetに紐づくUser(子)とそのUserに紐づくAddress(孫)を取得します。
孫モデルが複数ある場合
Tweet.includes(user: [:address, :comments])明示的に[]が必要です。こちらはTweetに紐づくUser(子)とそのUserに紐づくAddress(孫)およびComment(孫)を取得します。
③ 子モデル・孫モデルが1セットと子モデルが1つの場合
Tweet.includes(:tags, user: :address)注意すべき点は、孫モデルまで参照するものを後ろに記述することです。こちらはTweetに紐づくTag(子)およびUser(子)とそのUserに紐づくAddress(孫)を取得します。
※
Tweet.includes(user: :address, :tags)
とすると、筆者の場合SyntaxError: unexpected ')', expecting =>
と表示されてしまいます。
※ もし子モデル・孫モデルのセットが増えたなら、後ろに加えれば大丈夫です。【募集】 STIをincludesする場合
今現在まだ実務で出会っていませんが、STIを参照する際のincludesの形はどうなるのだろう、、と今から不安です。もし、参考記事などございましたらご教授いただければ幸いです。
これからも新しい書き方を見つけたら更新していきます
実務未経験から働き始めて約1週間。まだまだこんなものではないと思いますので、気付き次第随時更新していきます。
参考記事
- 投稿日:2020-02-11T20:42:07+09:00
rails内の環境構築 その2
scaffoldでサンプルアプリを作る
docker-compose run web rails g scaffold user name:string address:string age:integerそして
docker-compose run web rails db:migrateブラウザでlocalhost:3000/usersでアクセスできますが、毎回めんどいのでrootに指定します
confing/routes.rbRails.application.routes.draw do resources :users root 'users#index' endslimを導入
gem 'slim-rails' gem 'html2slim'そして
docker-compose run web bundle installさらに
docker-compose build既存の
erbファイルをslim可する
docker-compose run web bundle exec erb2slim app/views/layouts/ --delete docker-compose run web bundle exec erb2slim app/views/users/ --deleterspec導入
group :development, :test do gem 'rspec-rails' gem 'factory_bot_rails' endそして
docker-compose run web bundle install docker-compose buildさらに
docker-compose run web rails g rspec:install rm -r ./testrspec使い方
docker-compose run web rspecSystem Spec導入
Dockerfileに書き込む
- dockerコンテナの中へ新たにchromedriverを準備なければなりません。
FROM ruby:2.5 RUN apt-get update -qq && apt-get install -y nodejs postgresql-client # chromeの追加 RUN apt-get update && apt-get install -y unzip && \ CHROME_DRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE` && \ wget -N http://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_linux64.zip -P ~/ && \ unzip ~/chromedriver_linux64.zip -d ~/ && \ rm ~/chromedriver_linux64.zip && \ chown root:root ~/chromedriver && \ chmod 755 ~/chromedriver && \ mv ~/chromedriver /usr/bin/chromedriver && \ sh -c 'wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -' && \ sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list' && \ apt-get update && apt-get install -y google-chrome-stable RUN mkdir /myapp WORKDIR /myapp COPY Gemfile /myapp/Gemfile COPY Gemfile.lock /myapp/Gemfile.lock RUN bundle install COPY . /myapp # Add a script to be executed every time the container starts. COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3000 # Start the main process. CMD ["rails", "server", "-b", "0.0.0.0"]ディレクトリ作成
mkdir spec/system mkdir spec/support touch spec/support/capybara.rb設定を書く
spec/support/capybara.rbrequire 'capybara/rspec' require 'selenium-webdriver' Capybara.register_driver :selenium_chrome_headless do |app| options = ::Selenium::WebDriver::Chrome::Options.new options.add_argument('--headless') options.add_argument('--no-sandbox') options.add_argument('--disable-dev-shm-usage') options.add_argument('--window-size=1400,1400') driver = Capybara::Selenium::Driver.new(app, browser: :chrome, options: options) end Capybara.javascript_driver = :selenium_chrome_headlessspec/rails_helper.rbDir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } #↑コメントアウトする これによりspec/support/capybara.rbが読み込まれます。 RSpec.configure do |config| config.before(:each) do |example| if example.metadata[:type] == :system if example.metadata[:js] driven_by :selenium_chrome_headless, screen_size: [1400, 1400] else driven_by :rack_test end end end end #type: :systemのときのみchromeを使うという設定です。テストの実行
docker-compose run web rspec spec参考
- 投稿日:2020-02-11T19:01:48+09:00
TwitterAPI 利用申請の手順
TwitterのAPIを利用するためには、事前の申請が必要です。
英語のサイト上で色々と入力が必要なので、手順をまとめました。目的
TwitterAPIを利用してrubyからツイートするために、利用申請を行うこと。
手順1 Developerにアクセスする
以下サイトにアクセスして下さい。
ツイッターデベロッパー手順2 ツイッターにサインインする
画面右上の「Sign in」ボタンからサインインして下さい。
手順3 Applyページにアクセスする
サインインが完了したら、同じく画面右上の「Apply」ボタンをクリックして下さい。
手順4 Apply for a developer account をクリックする
手順5 デベロッパーツールの利用目的を選択する。
「Making a bot」にチェックを入れ、「Next」をクリックする。
手順6 本人確認を行う
以下のような画面が表示されるので、アカウント名等に誤りが無いか確認してください。
ページ下部では、メールアドレスを確認後、居住国と名前(ニックネームで可)を入力し、「Next」をクリックする。
手順7 TwitterAPIの利用目的等を入力する
英語200文字以上でAPiの利用目的を入力してください。
Google翻訳に突っ込んだだけの文章ですが、よければコピペしてください。I am studying programing. I have never used the API, so I want to try making a bot. In the future, I want to create a web application that can get information about tweets that users have searched for.その他の項目についても以下のように入力していきます。
自動ツイート等の利用予定については、以下のような文章で問題ありません。
全ての項目の入力が完了しましたら、「Next」ボタンを押してください。I do automatic tweeting as a practice for using Twitter api. However, there are no plans to use it thereafter.手順8 登録内容に誤りが無いか確認する。
手順9 ポリシー等を読んで、問題なければチェックを入れて送信ボタンを押す
10 完了確認
この後、実際にRubyでbotを作成するには、デベロッパー上でappを作成する必要があります。
その手順については、次の記事に記載してあります。
- 投稿日:2020-02-11T18:57:04+09:00
DockerでRailsの環境構築
docker-composeでrails環境を構築します。データベースはpostgresqlを使用します。
Setup
以下のような構成からスタートします。
./ |- docker-compose.yml |- rails-app/ |- DockerfileDockerfileの作成
DockerfileFROM ruby:2.5.0 RUN apt-get update && apt-get install -y build-essential libpq-dev postgresql-client vim less RUN gem install rails -v '5.2.1' RUN mkdir /app WORKDIR /apprailsのバージョンは5.2.1を指定しています。
マウント先のディレクトリとしてappディレクトリを作成しておきます。docker-compose.ymlの作成
docker-compose.ymlversion: "3" services: rails-app: build: rails-app ports: - "3001:3001" environment: - "DATABASE_HOST=db" - "DATABASE_PORT=5432" - "DATABASE_USER=postgres" - "DATABASE_PASSWORD=<パスワード>" links: - db volumes: - "./rails-app:/app" stdin_open: true db: image: postgres:10.1 ports: - "5432:5432" environment: - "POSTGRES_USER=postgres" - "POSTGRES_PASSWORD=<パスワード>"ポートは3001番を指定していますが、3000番でもOKです。
コンテナの起動
docker-compose build docker-compose up -d
コンテナにログインします。
docker-compose exec rails-app bash
以降はコンテナ内で操作を行います。
Railsプロジェクトの作成
cd /app rails new . -d postgresql -BT
-B
でbundle installをスキップし、-T
でテストファイルの作成をスキップしています。
Gemfileにtherubyracerを追加します。Gemfilegem 'therubyracer', platforms: :rubyこの状態でbundle installします。
bundle install —-path vendor/bundleこれでプロジェクトが作成されました。
データベースの設定
config/database.yml
を編集します。
- defaultにhost, port, username, passwordを追加
- データベース名を rails_app_development に変更
config/database.ymldefault: &default adapter: postgresql encoding: unicode # For details on connection pooling, see Rails configuration guide # http://guides.rubyonrails.org/configuring.html#database-pooling pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + host: <%= ENV.fetch("DATABASE_HOST") { "localhost" } %> + port: <%= ENV.fetch("DATABASE_PORT") { 5432 } %> + username: <%= ENV.fetch("DATABASE_USER") { "root" } %> + password: <%= ENV.fetch("DATABASE_PASSWORD") { "password" } %> development: <<: *default - database: app_development + database: rails_app_developmentデータベースを作成します。
bundle exec rails db:create
起動
IPアドレスとポートを指定してサーバを起動します。
rails s -b 0.0.0.0 -p 3001ブラウザで
http://localhost:3001/
を開いて、正しく起動できているか確認します。
ホストPCとdockerコンテナ間でプロジェクトディレクトリを共有しているので(./rails-app:/app)、編集はホストPCで(vscode等で!)、実行は環境構築したコンテナで、という開発ができます。
幸せになりましょう。
- 投稿日:2020-02-11T17:56:52+09:00
ruby,railsの特徴
ruby
- スクリプト言語である。
プログラミング言語にはスクリプト言語、コンパイラ言語と言われる言語があり、
人間が書いたコードを機械が実行するには翻訳を行う必要がある。
コンパイラ言語は事前に翻訳し、スクリプト言語は随時コンパイル(翻訳)しつつ実行される。
スクリプト言語であるrubyは翻訳する必要がないため開発しやすい反面、実行速度が遅めである。
- オブジェクト指向である。
最初からオブジェクト指向の言語として開発されたため、
変更に強く、再利用しやすいコードを書きやすくなっている。上記の理由から記述量が少なく済んだり、コンパイルする必要がないため生産性が高い。
そのため、フレームワークであるrailsと合わせて短期間で開発を進める必要があるスタートアップ企業などで好まれて使用されやすい。rails
言語としてrubyを使用しているため、比較的習得しやすい。
習得したスキルを幅広く使用できる。
rubyを取得してしまえばフレームワークはrailsほぼ一択になるため幅広く活用することが出来る。開発を効率化する仕組みが多数用意されているため生産性が高い。
rails newでアプリの雛形を作成することができたり、ActiveRecordによってSQL操作ができるなど、
開発効率を高める仕組みが多数用意されている。
- 投稿日:2020-02-11T16:25:50+09:00
RubyでAtCoder 版!蟻本 (初級編)を解いてみた
前置き
業務での実装力を高めようと、AtCoderをやっています。
AB問題の過去問を200問以上解いたのですが、なかなか灰コーダーから抜け出せないため、また、C++やPythonで取り組む人に比べるとRubyでAtCoderに取り組んでいる人は少ないかなと思い、初見で解けなかった問題も解説や他の解答見て作ることで残していきます。引用元: AtCoder 版!蟻本 (初級編)
例題 1-1-1 くじびき
n,m=gets.split.map &:to_i p=n.times.map{gets.to_i}+[0] # 半分全列挙 a=p.repeated_combination(2).to_a.map{|i|i.inject(:+)}.sort.reverse s=0 # 全探索 a.each do |left| # 二分探索 right=a.bsearch{|r|r+left<=m} s=[s,left+right].max unless right.nil? end puts s【参考リンク】
例題 1-6-1 三角形
N=gets.to_i xy=N.times.map{gets.split.map &:to_i} ans=0 # 全探索 (二重の for 文) N.times do |i| N.times do |j| next if i==j x1=xy[i][0] y1=xy[i][1] x2=xy[j][0] y2=xy[j][1] # 三角形の成立条件 res=((x1-x2)**2+(y1-y2)**2)**0.5 ans = [ans, res].max end end puts ansk,s=gets.split.map &:to_i l=k+1 a=0 l.times do |x| l.times do |y| z=s-x-y a+=1 if z>=0 && z<=k end end puts a【別解(微妙かも)】
k,s=gets.split.map &:to_i puts [*0..k].repeated_permutation(2).map{|a,b|a+b}.select{|i|i<=s}.count{|xy|(z=s-xy)>=0 && z<=k}N,Y=gets.split.map &:to_i n=N+1 f=false n.times do |x| n.times do |y| break if x+y>N z=(Y-10000*x-5000*y)/1000 if x+y+z===N && z>=0 puts "#{x} #{y} #{z}" f=true break end end break if f end puts "-1 -1 -1" unless f例題 1-6-2 Ants (POJ No.1852)
Atcoder Grand Contest 013 解説 C Ants on a Circle より
ここは分かった。
T 秒後にゼッケンのある座標の集合は、当然、T 秒後に蟻のいる座標の集合と同じなので、T 秒後にそれぞれの蟻がどこにいるかを求めると、番号は分からないが、どの座標にゼッケンがあるのかが分かります。
それ以外は解説読んでもコードに起こせない。
二つの蟻がゼッケンを交換する時、時計回りに歩いてる蟻のゼッケンの番号は 1 増え、反時計回りに歩いている蟻のゼッケンの番号は 1 減ります。ある二つの蟻が T 秒間にすれ違う回数は、O(1)で求めることができます。よって、最初に 1 のゼッケンをつけていた蟻が、T 秒間に他の蟻とすれ違う回数の合計は、O(N) で分かります
例題おまけ 累積和・しゃくとり法
つい先日のABC。頻出らしいのでついでに載せておく。
【累積和解法】
N,K=gets.split.map &:to_i # 期待値の前計算 p=gets.split.map{|i|(i.to_f+1)*0.5} # 累積和の計算 a=[0] N.times do |i| a << a[i]+p[i] end # 隣接するK項の和の計算 s=0 (N-K+1).times do |i| r=a[i+K]-a[i] s=[s,r].max end puts s【しゃくとり法解法】
N,K=gets.split.map &:to_i # 期待値の前計算 p=gets.split.map{|i|(i.to_f+1)*0.5} # しゃくとり法・隣接するK項の和の計算 r=s=p[0..K-1].inject(:+) (N-K).times do |i| r+=p[i+K]-p[i] s=[s,r].max end puts s【参考リンク】
例題 2-1-1 部分和問題
解答中
- 投稿日:2020-02-11T16:12:22+09:00
bundle execって必要なの?
rails s
って書いても、bundle exec rails s
って書いても同じ動きをするように見えるんですが、何が違うのでしょうか。気になったので調べてみました。結論
bundle exec
をつけないと、PCにグローバルインストールされているgemが動くbundle exec
をつけると、そのプロジェクトで管理しているgemが動くbundle exec rails
とbin/rails
は同じ動きをする → rails以外のコマンドの場合は違うっぽい。bin
ディレクトリに該当のファイルが無いとbin/◯◯
って書き方はできないそもそもBundlerとは
- プロジェクト内で使うGemのパッケージ管理ツール。Gemをプロジェクト単位で管理することができる。
- rbenv(rubyのバージョンをプロジェクト単位で管理するツール)のGem版
bundle install
コマンドを叩くと、Gemfileに記載されたGemをインストールするbundle installした時のGemの保存先
rbenvを使っている場合
$ gem which byebug /Users/***/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/byebug-11.0.1/lib/byebug.rb思ったこと
bundle exec
は毎回付けた方が良いってことですかね。
めんどくさいので、早く開発環境をdockerで管理したいです。
dockerを使えば、そのプロジェクト以外のことは全無視できるはず。参考
- 投稿日:2020-02-11T15:45:30+09:00
gem install pg が上手くいかない時の対処法
開発環境
ubuntu(wsl)
Ruby 2.5
Rails 5.2エラー内容
bin/rails db:create
を実行するとCould not find gem 'pg (>= 0.18, < 2.0)' in any of the gem sources listed in your Gemfile. Run `bundle install` to install missing gems.言われてた通り
bundle install
を実行すると、An error occurred while installing pg (1.2.2), and Bundler cannot continue. Make sure that `gem install pg -v '1.2.2' --source 'https://rubygems.org/'` succeeds before bundling.
gem install pg -v 1.2.2
を確認しろとのことなので、gem install pg -v 1.2.2
を実行する。To see why this extension failed to compile, please check the mkmf.log which can be found here: /home/username/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/extensions/x86_64-linux/2.5.0/pg-1.2.2/mkmf.log extconf failed, exit code 1 Gem files will remain installed in /home/username/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/pg-1.2.2 for inspection. Results logged to /home/username/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/extensions/x86_64-linux/2.5.0/pg-1.2.2/gem_make.out解決策
下記のコマンドを実行して、その後
gem install pg -v 1.2.2
をする。sudo apt install libpq-dev参考文献
- 投稿日:2020-02-11T15:24:14+09:00
あんまり頭を使わないで構築するRailsAPI on Docker
すでに多くの記事がありますが、APIモードのRailsがDocker上で動くところまで、必要最小限の情報でまとめてみます。RubyMineでの設定も最後にちょっと書いてあります。
参考: 公式 Quickstart: Compose and Rails
ファイル準備
アプリケーションを作成したいディレクトリで設定ファイルを作成します。
$ touch Gemfile Gemfile.lock Dockerfile docker-compose.yml entrypoint.sh各ファイルの内容を埋めます。
Gemfile
source 'https://rubygems.org' gem 'rails', '~>6'
Dockerfile
FROM ruby:2.6 RUN apt-get update -qq && apt-get install -y nodejs postgresql-client RUN mkdir /app WORKDIR /app COPY Gemfile /app/Gemfile COPY Gemfile.lock /app/Gemfile.lock RUN bundle install COPY . /app # Add a script to be executed every time the container starts. COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3000 # Start the main process. CMD ["rails", "server", "-b", "0.0.0.0"]entrypoint.sh#!/bin/bash set -e # Remove a potentially pre-existing server.pid for Rails. rm -f /app/tmp/pids/server.pid # Then exec the container's main process (what's set as CMD in the Dockerfile). exec "$@"docker-compose.ymlversion: '3' services: db: image: postgres volumes: - ./tmp/db:/var/lib/postgresql/data ports: - 5438:5432 app: build: . command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" volumes: - .:/app ports: - "3008:3000" depends_on: - db environment: LANG: C.UTF-8 BUNDLE_PATH: vendor/bundle RAILS_ENV: development DB_USER: postgres DB_NAME: app_db DB_PASSWORD: password DB_HOST: dbファイルの内容ちょっと補足
- Rubyは2.6、Railsは6系です
- 公開するポートはrailsが3008、postgresqlが5438です
- 後述するdatabase.ymlではwebサービスに設定した環境変数を読んで設定を行います
作る
$ docker-compose run app rails new . --force --no-deps --database=postgresql --api※
--api
を消したら通常のRailsアプリケーションが作れます。$ docker-compose buildしたら一通りRailsが必要なファイルを作ってくれます。自動で作成された
database.yml
を編集します。database.ymldefault: &default adapter: postgresql encoding: unicode host: <%= ENV['DB_HOST'] %> username: <%= ENV['DB_USER'] %> password: <%= ENV['DB_PASSWORD'] %> pool: 5 development: <<: *default database: app_development test: <<: *default database: app_test$ docker-compose run app rails db:create動作確認
$ docker-compose uphttp://localhost:3008/で動いてるはずです。
補足: RubyMine使ってる方、ちょろっと設定編
インタプリタの設定
Preferences > Languages & Frameworks > Ruby SDK and Gems
で、追加 > Docker Composeをクリック > OK
読み込みが完了したら、該当のSDKをチェックし、APPLYして完了です。補完がいい感じに効くようになります。
DB設定
Database > 追加 > Data Source > PostgreSQL
- Host: localhost
- Port: 5438
- User: postgres
- Password: password
で接続ができるはずです。うまくいかない場合は、タブから適切なスキーマを選択したり、DBが起動しているか確かめたりしてください。
- 投稿日:2020-02-11T13:51:27+09:00
form_withの初歩的な事
= form_with ~..... = text_field.... = submitなどの入力フォームを作る際、
submitしsaveされたあとのビュー、もしくはリダイレクト先をしっかりと明記しておかないとエラーが出る
- 投稿日:2020-02-11T13:47:32+09:00
【Rails】投稿とユーザーの紐付け(一対多)のメモ
経緯
現在ポートフォリオ用に「book memory phrase」というアプリを作っています。
自分が印象に残ったフレーズを引用した書籍とともに紹介して本との出会いを作る場です。そこでユーザーと投稿の紐付けを行って、誰がどの投稿をしたか一覧で見れるような機能を実装したのですが、has_manyやbelongs_toなど学ぶことが多かったのでアウトプットしとこうと思います。
投稿したユーザーid
まずはデータベース上のidを紐付ける必要があります。
今回の場合だと、UserテーブルのidをPostテーブルに突っ込んで、誰の投稿かわかるようにします。
最初からこの機能は実装する気でいたので、Postテーブルを作る時点でidカラムとは別にuser_idカラムを作り、投稿する際にユーザーのidを突っ込めるようにしてました。
その辺については以前書いた【Rails】railsブログサイトの練習で躓いたところメモにも載せていますのでそちらを参考にしてもらえたらと思います。
Modelの設定
idが突っ込めたら次は紐付けです。これはModelで設定します。
has_many
has_manyは一対多の多の方です。
ユーザー1に対して投稿は多数あるので、ユーザーモデルにhas_manyを記述します。user.rbhas_many :posts, dependent: :destroy投稿は複数あるので複数系にしてください。
ちなみにdependent: :destroyはユーザーが削除されたら自動的に投稿も削除されるという優秀な機能です。
belongs_to
次は一対多の一の方、ユーザーの紐付けです。
post.rbbelongs_to:user def user return User.find_by(id: self.user_id) end一の方はbelongs_toで紐付けられます。
また、userメソッドをモデルで定義しておけば汎用性が高くなるので書いときます。Controllerの設定
私はuser_showというページで該当ユーザーの投稿一覧を表示したかったので、コントローラーに以下のように記述しました。
home_controller.rbbefore_action :set_post, only[:user_show, :edit, :update, :destroy] #省略 #@userにモデルで定義したuserメソッドを@post.userで代入 #@postsに@userの投稿を全て代入 def user_show @user = @post.user @posts = @user.posts end #省略 private #Post.find_by(id: params[:id])はよく使うので定義しといてbefore_actionで反映。 def set_post @post = Post.find_by(id: params[:id]) endこれでコントローラーの準備は整いました。
Viewの設定
コントローラーで作った@postsをeach文で回してあげれば一覧が確認できます。
user_show.html.erb<% @posts.each do |post| %> <div class="book-img col-lg-4 col-md-6 col-sm-12 center-block mx-auto"> <div class="box"> <div class="image-box"> <h5><%= post.phrase %></h5> <p class="name"> <span>投稿者  </span><%= link_to("#{post.post_user}", "/home/#{post.id}/user_show")%> </p> <img src='<%= post.book_image %>' class="img-fluid d-block"> </div> <div> <p class="title"> 『<%= post.title %>』 </p> <p class="author"> 著者 <%= post.author %> </p> <p>★ おすすめポイント</p> <p class="content"> <%= post.content%> <% if user_signed_in? && current_user.id == post.user_id %> <span class="edit-delete"> <%=link_to("編集" , "/home/#{post.id}/edit",class:"text-muted") %> <%=link_to("削除" , "/home/#{post.id}/destroy",method: :post,class:"text-muted") %> </span> <% end %> </p> </div> </div> </div> <% end %>まとめ
投稿の紐付けは必須機能ですが、結構躓くところでもあると思います。
かく言う私も何度も躓いたのですがSQLを勉強したら結構スッと入ってこれました。
イメージって大事ですね。もしこの記事が誰かの助けになれたら幸いです。
ではでは。
- 投稿日:2020-02-11T12:40:26+09:00
WSLでRuby on Railsの環境構築
やること
- windowsでUbuntuを使えるようにする
- Rubyのインストール
- Railsのインストール
- DBのインストール
windowsでUbuntuを使えるようにする
Ubuntuの入れ方については既に多くの記事がでているので省略します。
Ubuntuを立ち上げたら、ユーザ名とパスワードを入れます。
その後、パッケージを最新にするため下記のコマンドを以下のコマンドを実行してください。sudo apt update sudo apt upgrade -yRubyのインストール
Rubyをインストールする前にバージョン管理のために、rbenvをインストールします。
下記のコマンドを実行してくださいgit clone https://github.com/rbenv/rbenv.git ~/.rbenv echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc //パス設定と初期化 echo 'eval "$(rbenv init -)"' >> ~/.bashrcここで一旦終了し再び立ち上げ、下記のコマンドを入力しバージョンが返ってきたらokです。
rbenv -v
ruby-build
のセットアップとパッケージのインストールをします。git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build sudo apt-get install autoconf bison build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm5 libgdbm-devここからRubyのインストールです。
rbenv install 2.6.5 rbenv global 2.6.5 ruby -v which ruby
ruby -v
を実行して返ってきたらokです。さらに、gemのアップデートとbundlerのインストールもしておきます。
gem update gem install bundlerRailsのインストール
gem install rails -v 6.0.2 rails -vここでもバージョンが返ってきたらokです。
後に必要となるnode.jsもここでインストールしておきます。
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash - //バージョンは最新のものを選択してください。 sudo apt install nodejsDBのインストール
下記のコマンドを実行してください。
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ 18.04.4 -pgdg main" >/etc/apt/sources.list.d/pgdg.list' wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - sudo apt update sudo apt install postgresql psql -Vバージョンが返ってきたらokです。
Postgresqlを起動します。
sudo service postgresql start sudo su postgres -c 'createuser -s ユーザ名' psql postgres\qを入れて終了します。
下記のコマンドで「pg」が利用するパッケージをインストールしておきます。sudo apt install libpd-dev参考文献
- 投稿日:2020-02-11T12:08:29+09:00
railsの環境構築 その1
はじめに
これからdockerで開発環境の構築からAWSデブロイまで数回に分けてまとめてみます
私が使った有料教材
Dockerについて
Dockerについては下記のサイトを見てください。
* 入門 DockerDockerでrailsの環境構築
参考
作業手順
ディレクトリを作成しそこで作業
mkdir ror_app
Dockerfileを作成
、Dockerfile内に下記を記述
touch DockerfileFROM ruby:2.5 RUN apt-get update -qq && apt-get install -y nodejs postgresql-client RUN mkdir /myapp WORKDIR /myapp COPY Gemfile /myapp/Gemfile COPY Gemfile.lock /myapp/Gemfile.lock RUN bundle install COPY . /myapp # Add a script to be executed every time the container starts. COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3000 # Start the main process. CMD ["rails", "server", "-b", "0.0.0.0"]コマンド
FROM: ベースとなるDocker Image を指定します。 COPY: Docker内へホストのファイル/ディレクトリをコピーします。 COPY は基本的に2つの引数を設定します。1つ目はホスト側のディレクトリ、2つ目はDocker側のディレクトリです。 RUN: Docker内でコマンドを実行します。 CMD: Docker起動時にデフォルトで実行されるコマンドを定義します。 WORKDIR: Dockerfileでコマンドを実行する際に基準となるディレクトリを設定します。 EXPOSE: コンテナ起動時に公開することを想定されているポートを記述します ENTRYPOINT: 指定されたコマンドを実行します。
GemfileとGemfile.lockの作成
- rails newするときに必要
touch Gemfile && touch Gemfile.locksource 'https://rubygems.org' gem 'rails', '~>5'
entrypoint.sh
を作成
- 特定のserver.pidファイルが存在するときにサーバーが再起動しないようにするRails固有の問題を修正するエントリポイントスクリプトを提供します。このスクリプトは、コンテナが開始されるたびに実行されます。
touch entrypoint.sh
entrypoint.sh#!/bin/bash set -e # Remove a potentially pre-existing server.pid for Rails. rm -f /myapp/tmp/pids/server.pid # Then exec the container's main process (what's set as CMD in the Dockerfile). exec "$@"
docker-compose.yml
の作成touch docker-compose.ymldocker-compose.ymlversion: '3' services: db: image: postgres ports: - '5432:5432' volumes: - postgresql-data:/var/lib/postgresql/data web: build: . command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" volumes: - .:/myapp ports: - "3000:3000" depends_on: - db volumes: postgresql-data: driver: localdocker上でrailsのファイルを作成
docker-compose run web rails new . --force --no-deps --database=postgresql # rails new . とすることで、カレントディレクトリ配下にrailsファイルが作成される --forceで事前に作成したファイルを上書き、今回はGemfile等gem 'pg'のバージョン変更
gem 'pg', '~> 0.20.0'イメージをbuild
docker-compose buildすると
ror_app_web:latestになります。databese.ymlの書き換え
default: &default adapter: postgresql encoding: unicode host: db username: postgres password: pool: 5 # host,username,passwordを追加しました。 hostをdbにすることでdocker-compose.ymlのdbを指定 本番環境や開発環境でdatabase関連のエラーが良く出てきます。 特に本番環境をAWSにするとproduciton:配下を書き換えないといけません。データベースの作成
docker-compose run web rake db:createサービスの起動
docker-compose up # 終了はCtrl+Cブラウザで
localhost:3000
にアクセスするとrailsの初期画面が出現したと思います。docker-composeコマンドまとめ
ローカル環境 docker環境 rails s docker-compose up rails db:migrate docker-compose run web rails db:migrate bundle install docker-compose run web bundle install rails g model モデル名 docker-compose run web rails g model モデル名 git push
railsの初期画面が出てきたらgit pushしておきましょう。
お疲れさまでした。
- 投稿日:2020-02-11T10:43:42+09:00
Ruby コーティング規約について 命名規則編 2
はじめに
命名規則編 1 はこちらをクリック願います。
レイアウト編 3 はこちらをクリック願います。過去記事リンクあり。
構文編 2 はこちらをクリック願います。過去記事リンクあり。
Rubyの基礎を学習中の方に向けて記載致します。
私自身これからチーム開発を行う上で大事にしたい。知っておきたいことをOutputします。命名規則について
① シンボル、メソッド、および変数名において、文字と数字を分離しないようにする。
qiita.rb# 悪い例 :some_sym_1 some_var_1 = 1 var_10 = 10 def some_method_1 # 処理 end # 良い例 :some_sym1 some_var1 = 1 var10 = 10 def some_method1 # 処理 end② 述語メソッドに is、 does、 canのような助動詞をつけるべきではない。
- 冗長になるため。
- Rubyコアライブラリの述語メソッドのスタイル ( empty?や include?など)と矛盾するため。
qiita.rb# 悪い例 class Person def is_buff? #筋肉がある人? true end def can_play_rugbyfootball? false end def does_like_chocolate? true end end # 良い例 class Person def buff? true end def rugbyfootball_player? false end def likes_chocolate? true end endさいごに
毎日更新します。
皆様の復習等にご活用頂けますと幸いです。
- 投稿日:2020-02-11T09:34:08+09:00
Leetcode: Reverse Linked List
require 'minitest/autorun' require 'pry' class ReverseLinkedList < Minitest::Test def test_run # Input: 1->2->3->4->5->NULL # Output: 5->4->3->2->1->NULL head = ListNode.new(1) head.next = ListNode.new(2) head.next.next = ListNode.new(3) head.next.next.next = ListNode.new(4) head.next.next.next.next = ListNode.new(5) new_head = reverse_list(head) assert_equal(5, new_head.val) assert_equal(4, new_head.next.val) assert_equal(3, new_head.next.next.val) assert_equal(2, new_head.next.next.next.val) assert_equal(1, new_head.next.next.next.next.val) assert_equal(nil, new_head.next.next.next.next.next) end def reverse_list(head) return head if head.nil? || head.next.nil? p = head q = p.next q = reverse_list(q) # qはp.nextなので、p.next.next = p は q.next = pと同じ。なので、この行でq.nextが決まる。 p.next.next = p p.next = nil q end end class ListNode attr_accessor :val, :next def initialize(val) @val = val @next = nil end end
- 投稿日:2020-02-11T07:16:28+09:00
[-bash: rbenv: コマンドが見つかりません�]aws(ec2)上のrbenvの初期設定エラーの解決方法
1.エラーの内容
Neverland:.ssh kontatomoya$ ssh -i freemarket_sample_62d.pem ec2-user@3.115.38.38 Last login: Mon Feb 10 20:51:18 2020 from xx000000000000.au-net.ne.jp __| __|_ ) _| ( / Amazon Linux AMI ___|\___|___| https://aws.amazon.com/amazon-linux-ami/2018.03-release-notes/ -bash: rbenv: コマンドが見つかりません #↑ec2に接続した時に出るこのエラーが本題です [ec2-user@ip-000-00-00-00 ~]$2.考えられる原因
1.そもそもrubyに関する環境構築をしていない
2.環境構築の際コマンドの順番を誤り
.bash__profile
でサーバーサイドが想定した記載になっていない。(自分が確認した限りではデプロイしたアプリへの影響はありませんでしたが、どこでバグの原因となるかわからないため解消することを推奨します)3.解決方法
1.そもそもrubyに関する環境構築をしていない
この場合は下記の手順に従ってコマンドを打ち込むことで解消します
中途半端に設定した方はひとまず指示に従って全て打ち込んでください(ダブってしまう不要な箇所を4.で確認および削除し解決します。一度アプリケーションをデプロイして、再度違うアプリケーションをデプロイし直す方も同様にしてください)[ec2-user@ip-000-00-00-00 ~]$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv #rbenvをEC2にインストール [ec2-user@ip-000-00-00-00 ~]$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile #パスを通すための記述を.bash_profileに追記 [ec2-user@ip-000-00-00-00 ~]$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile #rbenvを呼び出すための記述を.bash_profileに追記 [ec2-user@ip-000-00-00-00 ~]$ source .bash_profile #.bash_profileをEC2に読み込み [ec2-user@ip-000-00-00-00 ~]$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build #ruby-buildのインストール [ec2-user@ip-000-00-00-00 ~]$ rbenv rehash #rehashを行う [ec2-user@ip-000-00-00-00 ~]$ rbenv install 2.0.0 #rubyのインストールを行う(2.0.0は自分のアプリのrubyバージョンに合わせる) [ec2-user@ip-000-00-00-00 ~]$ rbenv global 2.0.0 #rubyをEC2上共通のバージョンとする [ec2-user@ip-000-00-00-00 ~]$ rbenv rehash #rehashを行う [ec2-user@ip-000-00-00-00 ~]$ ruby -v #バージョンを確認→"ruby 2.0.0p648 (2015-12-16) [x86_64-linux]"が出ればok #設定の内容を見たい場合は下記コマンド [ec2-user@ip-000-00-00-00 ~]$ vim ~/.bash_profile #.bash_profile(初期設定の隠しファイル)をviエディターで確認する2.環境構築の際コマンドの順番を誤り
.bash__profile
でサーバーサイドが想定した記載になっていない。エラーを起こしている
.bash__profile
ファイルは下記のようになっています[ec2-user@ip-000-00-00-00 ~]$ vim ~/.bash_profile #.bash_profile(初期設定の隠しファイル)をviエディターで確認する.bash_profile# .bash_profile # Get the aliases and functions if [ -f ~/.bashrc ]; then . ~/.bashrc fi # User specific environment and startup programs PATH=$PATH:$HOME/.local/bin:$HOME/bin export PATH eval "$(rbenv init -)" export PATH="$HOME/.rbenv/bin:$PATH" #↑環境構築のコマンドにより最下の行を追記しました。正しいコマンド通りだとした2行が逆転していなければなりませんコメントに従って下記の行を入れ替えることで正しい環境構築となります。エラーの理由はパスを通す前にrbenvを呼び出す記述となっているからでした。
4.中途半端に設定したあとにコマンドをした方の重複箇所のと確認と削除について
rbenv の環境構築で重複する箇所は
.bash_profile
だけなのでそちらを下記コマンドで確認し、一番最後に記載した.bash_profile
のテンプレートと異なる部分の削除を行なっていただくことで正しく設定できます。※また、注意点として、ローカルの
.bash_profile
とEC2(サーバー側)の.bash_profile
は記載内容が異なり、今回は全てEC2(サーバー側)で作業を行なうようにしてください。[ec2-user@ip-000-00-00-00 ~]$ vim ~/.bash_profile #.bash_profile(初期設定の隠しファイル)をviエディターで確認する※1.でなぜもう一度全て打ち直していただいたかというと
.bash_profile
にrubyやruby-buildが入っているか表示されないため、インストールされず進んでしまうおそれがあるからです。また、rubyやruby-buildは何度インストールしても自動でダブらないようにしてくれるため重複は気にしなくて大丈夫である(alreadyの文が出ます)ため、記載漏れしないように再度全て記述していただきました。(慣れている方はruby -vで環境の有無の確認の後.bash_profile
に手打ちでもいいのですが、初期設定のファイルの存在自体がわかりにくく、これを見られているのは初学者の方が多いと思うのでコマンドから打って、最後に不要部分を削ることをお勧めします。)<
.bash_profile
のテンプレート>.bash_profile# .bash_profile # Get the aliases and functions if [ -f ~/.bashrc ]; then . ~/.bashrc fi # User specific environment and startup programs PATH=$PATH:$HOME/.local/bin:$HOME/bin export PATH export PATH="$HOME/.rbenv/bin:$PATH" eval "$(rbenv init -)"以上の方法でエラー文は解消されます
- 投稿日:2020-02-11T06:47:46+09:00
メタプログラミング理解の覚書
メタプログラミングとは、「プログラミングするためのプログラムを書くこと」らしい。
実務で遭遇した例としては、「任意の数の変数を作成し、その変数を利用する」場面。[*1..3].each do |count| # user_1のような変数を作成し、値を「ユーザー1」と設定する instance_variable_set("@user_#{count}", "ユーザー#{count}") # evalを使って文字列をRubyプログラムとして評価させてputsする puts eval("@user_#{count}") end # 実行結果: # ユーザー1 # ユーザー2 # ユーザー3eval使うと、Rubocopに警告もらうので推奨されたやり方ではないのですが、こういうのがメタプログラミングというものだそうです。
もう少し実践的なメタプログラミングの概念は「オープンクラス」。
あるクラスを拡張しようと思ったら継承が必要ですが、継承せずに実行できること。
例えば、「このプロジェクトでは、足し算をするときに、足した結果にさらに1をプラスする必要がある」という要件を満たそうとする場合。class Integer def plus_one(v) self + v + 1 end end puts 1.plus_one 1 # 実行結果: # 3Integerクラスを継承することなく、plus_oneというメソッドを追加できました。
追加だけではなく、既にあるメソッドと同名を定義すれば上書きできます(これがモンキーパッチ)。先程の要件を、メソッドの拡張ではなくモンキーパッチでやってみます。
def +(v)
を上書きするわけです。
けれど以下の書き方だと+の定義が無限ループに陥るのでダメです。class Integer def +(v) self + v + 1 end end # 実行結果: # `+': stack level too deep (SystemStackError)こういう場合は、
alias_method
を使用します。
alias_methodとは「メソッドに別名を定義する」というもの。class Integer alias_method :plus, :+ def +(v) self.plus(v).plus(1) end end puts 1 + 2 #=> 4※この問題は、お仕事の面接の時に問題として出題されたものです。
当時はRubyを触って半年でメタプログラミング自体分かってなかったので答えられなかったのですが、最近勉強を始め、「これについて聞いていたのか!」と分かりました。
すごくすっきりした……!
- 投稿日:2020-02-11T02:55:46+09:00
herokuにデプロイするときの単純ミス
Railsで作成したアプリをHerokuでデプロイしようとした時のミス
terminal.remote: ! remote: ! Failed to install gems via Bundler. remote: ! Detected sqlite3 gem which is not supported on Heroku: remote: ! https://devcenter.heroku.com/articles/sqlite3 remote: !結論
Gitリポジトリにデプロイするためgemを変更した修正を上げていなかった。。。
githubのリポジトリからデータがHerokuに送られているため、githubのコードを更新しなければならない!!!!!(そう言えばそうだった笑)terminal.$ git add * $ git commit -m 'add to gemfile(メッセージは何でもいい)' $ git push $ git push heroku master解決方法
HerokuにデプロイするときはたいていRails Tutorialを参考にしています。
・Rails Tutorial "1.5デプロイする"Gemfile.group :development, :test do gem 'sqlite3', '1.3.13' gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] end group :production do gem 'pg', '0.20.0' end・gem 'sqlite3'
・gem 'pg'
を挿入してgit push する!terminal.remote: Verifying deploy... done.無事デプロイできました!
簡単なミスしないようにしたい。。。
これからも頑張ろう!
- 投稿日:2020-02-11T01:10:06+09:00
敢えてRubyで学ぶ「ゼロから作るDeep Learning」第3章のニューラルネットワークのMNIST推論処理
PyCallで取り込んだNumPyのデータを無理やりNumo::NArrayに変換してRubyで行列計算をする。
require 'numo/narray' require 'numo/gnuplot' require 'datasets' require 'mini_magick' require 'pycall/import' require './neuralnet' include PyCall::Import def init_network pyimport :numpy pyimport :pickle pkl = open("sample_weight.pkl", "rb") pickle.load(pkl) end # 独自適当メソッド # 無理やりnumpy形式をlist -> Ruby標準Array -> NArrayに変換する荒業 def numpy_to_narray(w) row = w.shape[0] return Numo::NArray.concatenate(Array(w.tolist).flatten) if w.shape.length == 1 col = w.shape[1] Numo::NArray.concatenate(Array(w.tolist).flatten).reshape(row, col) end def predict(network, x) w1 = numpy_to_narray(network['W1']) w2 = numpy_to_narray(network['W2']) w3 = numpy_to_narray(network['W3']) b1 = numpy_to_narray(network['b1']) b2 = numpy_to_narray(network['b2']) b3 = numpy_to_narray(network['b3']) a1 = x.dot(w1) + b1 z1 = sigmoid(a1) a2 = z1.dot(w2) + b2 z2 = sigmoid(a2) a3 = z2.dot(w3) + b3 softmax(a3) end # MNISTデータ取り込み # train = Datasets::MNIST.new(type: :train) test = Datasets::MNIST.new(type: :test) x = Numo::NArray.concatenate(test.map{|t| t.pixels }).reshape(10000,784) t = Numo::NArray.concatenate(test.map{|t| t.label }) network = init_network # とりあえず一気に変換 y = predict(network, x) accuracy_cnt = 0 10000.times do |i| p = y[i,true].to_a.index(y[i,true].max) accuracy_cnt += 1 if p == t[i] end p "Accuracy:#{Float(accuracy_cnt/10000.to_f)}"