- 投稿日:2019-08-11T19:59:21+09:00
【JS】動的なカテゴリーボックスの実装方法
はじめに
動的なセレクトボックスを実装するにあたり、非同期通信を用いたので備忘録としてここ記す。
テーブル
categoriesテーブル
Column Type Options category-name string null false ancestry string null false ancestryのGemを使い多階層としている。
親、子、孫が一つのカラムの中に存在している。実装
category.js$(function(){ function appendOption(category){ // optionの作成 var html = `<option value="${category.id}">${category.name}</option>`; return html; } function appendChidrenBox(insertHTML){ // 子セレクトボックスのhtml作成 var childSelectHtml = ''; childSelectHtml = `<div class='product-select-wrapper' id= 'children_wrapper'> <div class='product_category-select'> <select class="category_select-box" id="child_category" name="item[category_id]"> <option value="---">---</option> ${insertHTML} </select> <i class='fa fa-chevron-down'></i> </div> <div class= 'product_select-grandchildren'> </div> </div>`; $('.product_select-children').append(childSelectHtml); } function appendgrandChidrenBox(insertHTML){ // 孫セレクトボックスのhtml作成 var grandchildrenSelectHtml = ''; grandchildrenSelectHtml = `<div class='product-select-wrapper' id= 'grandchildren_wrapper'> <div class='product_category-select'> <select class="category_select-box" id="grandchild_category" name="item[category_id]"> <option value="---">---</option> ${insertHTML} </select> <i class='fa fa-chevron-down'></i> </div> <div class= 'product_select-grandchildren'> </div> </div>`; $('.product_select-grandchildren').append(grandchildrenSelectHtml); } $(document).on('change', '#category_select', function(){ // 親セレクトボックスの選択肢を変えたらイベント発火 var productcategory = document.getElementById('category_select').value; // ↑ productcategoryに選択した親のvalueを代入 if (productcategory != ''){ // ↑ productcategoryが空ではない場合のみAjax通信を行う。選択肢を初期選択肢'---'に変えると、通信失敗となってしまうため。 $.ajax({ url: 'category_children', type: 'GET', data: { productcategory: productcategory }, dataType: 'json' }) .done(function(children){ // 送られてきたデータをchildrenに代入 var insertHTML = ''; children.forEach(function(child){ // forEachでchildに一つずつデータを代入。子のoptionが一つずつ作成される。 insertHTML += appendOption(child); }); appendChidrenBox(insertHTML); $(document).on('change', '#category_select', function(){ // 通信成功時に親の選択肢を変えたらイベント発火。子と孫を削除。selectのidにかけるのではなく、親要素にかけないと残ってしまう $('#children_wrapper').remove(); $('#grandchildren_wrapper').remove(); }) }) .fail(function(){ alert('カテゴリー取得に失敗しました'); }) } }); // documentにしないとリロードしなければイベントが発火しない $(document).on('change', '#child_category', function(){ var productcategory = document.getElementById('child_category').value; if (productcategory != ''){ $.ajax ({ url: 'category_grandchildren', type: 'GET', data: { productcategory: productcategory }, dataType: 'json' }) .done(function(grandchildren){ var insertHTML = ''; grandchildren.forEach(function(grandchild){ insertHTML += appendOption(grandchild); }); appendgrandChidrenBox(insertHTML); $(document).on('change', '#child_category',function(){ $('#grandchildren_wrapper').remove(); }) }) .fail(function(){ alert('カテゴリー取得に失敗しました'); }) } }); });routes.rbRails.application.routes.draw do devise_for :users root to: "items#index" # ここから itemsコントローラのAjax通信でのアクション先指定 resources :items do collection do get 'category_children' get 'category_grandchildren' end end # ここまで enditems_controller.rbdef new @category = Category.all.order("id ASC").limit(13) # categoryの親を取得 end def category_children @category_children = Category.find(params[:productcategory]).children end # Ajax通信で送られてきたデータをparamsで受け取り、childrenで子を取得 def category_grandchildren @category_grandchildren = Category.find(params[:productcategory]).children end # Ajax通信で送られてきたデータをparamsで受け取り、childrenで孫を取得。(実際には子カテゴリーの子になる。childrenは子を取得するメソッド)category_children.json.jbuilderjson.array! @category_children do |child| json.id child.id json.name child.category_name endategory_grandchildren.json.jbuilderjson.array! @category_grandchildren do |grandchild| json.id grandchild.id json.name grandchild.category_name endnew.html.haml.product_select-details .product_select-group .product_header カテゴリー .product_require 必須 .product_category-select = f.collection_select :category, @category, :id, :category_name, { prompt: "---" }, { class: "category_select-box", id: "category_select" } %i.fa.fa-chevron-down .product_select-childrenさいごに
間違っているところがあればご指摘下さい
- 投稿日:2019-08-11T18:06:25+09:00
Dockerでrailsローカル開発環境構築
概要
- Dockerを使用したRailsの開発環境を構築
- DBはMySQLを使用
- ローカル開発環境で構築(MySQLのユーザー名、パスワードは簡易なものを使用)
前提
- Mac、Windows10のローカル開発環境で動作
- 「Docker for Windows」か「Docker for Mac」がインストール済みであること
ディレクトリ構造
├─src │ ├─Gemfile │ └─Gemfile.lock ├─Dockerfile └─docker-compose.yml
準備するファイル
ディレクトリ構造にある4つのファイルの内容は下記になります。
Dockerfile
DockerfileFROM ruby:2.5.3 ENV LANG C.UTF-8 RUN apt-get update -qq && \ apt-get install -y build-essential \ libpq-dev \ nodejs RUN mkdir /app RUN mkdir /app/src ENV APP_ROOT /app/src WORKDIR $APP_ROOT ADD ./src/Gemfile $APP_ROOT/Gemfile ADD ./src/Gemfile.lock $APP_ROOT/Gemfile.lock RUN bundle install ADD . $APP_ROOTdocker-compose.yml
docker-compose.ymlversion: '2' services: mysql: image: mysql:5.7 command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci ports: - "3306:3306" volumes: - ./db/mysql_data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: 'root-pass' restart: always logging: options: max-size: 5m max-file: "10" app: build: . image: rails container_name: 'app' command: bundle exec rails s -p 80 -b '0.0.0.0' ports: - "80:80" environment: VIRTUAL_PORT: 80 volumes: - ./src:/app/src depends_on: - mysql restart: alwaysGemfile
src/Gemfilesource 'https://rubygems.org' gem 'rails', '5.2.2'Gemfile.lockは、ファイルの中身は空でOK
src/Gemfile.lock手順
スケルトンアプリ作成
$ docker-compose run app rails new . --force --database=mysql --skip-bundledatabase.ymlを修正
src/config/database.ymldefault: &default adapter: mysql2 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: root-pass host: mysqlイメージ再ビルド
※処理完了までちょっと時間がかかることがあります。。。
$ docker-compose build
コンテナ立ち上げ
$ docker-cocmpose up -dMySQLにRailsのDBを作成
$ docker-compose run app rails db:create
これで、http://localhostにブラウザでアクセスすると、Railsのインストール完了画面がでてきました。
- 投稿日:2019-08-11T13:31:09+09:00
Ruby on Railsの環境構築した時に出たエラーとか
はじめに
サーバーサイドの知識があまりなかったため、Railsをなんとなーく導入してみようと思い、なんとなーく環境構築を勧めていたら、無知も相まって色々と悩んだのでメモを残しておく。今回予め、Gitlab,Redmineあたりを勉強ついでに導入しており、その関係でRails側の設定やインストール工程を一部省いたりもしたため、rubyとかbundlerとかpostgresqlとかをrailsのためにインストールし直したりはしていない。
Railsを導入するにあたって参考にしたサイト
環境
- OS (CentOS7)
- ruby (2.4.1)
- bundler (1.17.3)
- gem (3.0.4)
- rails (5.2.3)
- postgresql (9.2.24)
本題
bundler command not found: spring
Bundle complete! 18 Gemfile dependencies, 60 gems now installed. Gems in the groups development and test were not installed. Bundled gems are installed into `./vendor/bundle` run bundle exec spring binstub --all bundler: command not found: spring Install missing gem executables with `bundle install`rails new 'アプリ名' -d postgresqlを実行した時に出たエラー。
単純にspringというコマンドがなく、次の工程に進めないという話なのだけど、なぜ存在しないのかが分からなかった。
結論から言うとbundlerのconfigに問題があり、developmentとtestグループがBUNDLE_WITHOUTに設定されていたため、Gemfileの特定の行を通っていなかった。
参考URL(http://ruby.studio-kingdom.com/bundler/bundle_config/).bundle/config--- BUNDLE_PATH: "vendor/bundle" BUNDLE_WITHOUT: "development:test"ひとまず以下の様に修正
.bundle/config--- BUNDLE_PATH: "vendor/bundle" BUNDLE_WITHOUT: "test"これでspringコマンド含め、諸々インストールされた。
Could not find a JavaScript runtime
JSのランタイムがないとかなんとか。nodejsインストールで解決した。
参考URL(http://djandjan.hateblo.jp/entry/2018/07/25/224929)Address already in use
正しくは
Address already in use - bind(2) for "127.0.0.1" port 3000 (Errno::EADDRINUSE)と出力されていた。
多分3000番ポートが既に使用されているので、ポートの使用状況を lsof -i:3000 で確認してみたら、どうやらGitlabで使われてるっぽい...のでRailsのポートを変更することにした。
以下のコマンドでひとまず解決。$ rails s -p 3001Linux上ではひとまず起動したようなので、よしこれでページアクセスできるわ!と思い、ブラウザーにアドレス入力。エンターキーに渾身の力をこめてターンッしたら想定していたページが表示されない。このサイトにアクセスできませんとのこと。
これについては、下のURLに詳しいことが書いてあった。
参考URL
結果$ rails s -b 0.0.0.0 -p 3001これでアクセス"は"できるようになった。
あとファイアウォールのポートの設定忘れてるとアクセスできないので注意。
role "ユーザー名" does not exist
データベース側の設定ができておらんのでアクセスできてもページの表示ができなかった。
まずはユーザーが存在しないところから。下記URLの通りにすれば作成できた。ユーザー名はRails sを実行しているユーザー名にすればいい思う。(多分)
自分の場合はrootでやっていたので、$ sudo su – postgres $ createuser -s -r rootで作成は完了。
参考URL(https://kirohi.com/rails_pg_error_role_doesnotexist)データベースが存在しない。
下記URLの通り設定していきましょう。"Rails の設定とか"の辺りに詳しく書いてある。
データベースの名前はエラーページに表示されている通り。(***の部分はrails new で作成したアプリの名前)
***_development
***_test
***_production
参考URL余談
改めて記事にしてみて、アドレスの指定のところとか、そもそもDBまわりの知識があまりないとかちらほらあったので改めて調べ直したら記事にして考えを整理したいと思う。
また、もし誤った情報があれば、ご指摘いただけると幸いです。
- 投稿日:2019-08-11T10:05:39+09:00
Rails Tutorialの知識から【ポートフォリオ】を作って勉強する話 #7 ログイン下準備編
こんな人におすすめ
- プログラミング初心者でポートフォリオの作り方が分からない
- Rails Tutorialをやってみたが理解することが難しい
- ポートフォリオを作成しながら勉強したい
リードミー
- 目的:ポートフォリオを作成して会社に面接を受ける準備をする
- AWSによるCloud9で作成
- 間違いの可能性大、一緒に勉強させてください
![]()
ログインまでの色々を実装する
Tutorialは8.1 セッションに突入。
これからログインするまでのあれこれを実装していこう
流れとしては下記の3つを行う。
- コントローラ生成
- ビュー生成
- それらのテスト生成
機能としてのログインは次回#8。
ログインのためのコントローラ生成
とりあえずTutorial通りにSessionsコントローラを生成する。
くどいけどコントローラテストはRequest specで書く。bash$ rails g controller Sessions new $ rails g rspec:request sessionspec/requests/sessions_spec.rbrequire 'rails_helper' RSpec.describe "Sessions", type: :request do describe "GET /login" do it "returns http success" do get login_path expect(response).to have_http_status(:success) end end endあとはホーム画面の'Login'ボタンのリンク先も書く。
app/views/layouts/_header.html.erb<!-- 中略 --> <div id="menu" class="collapse navbar-collapse"> <ul class="navbar-nav ml-auto"> <li class="nav-item"> <%= link_to "Login", login_path, class: "btn btn-info btn-md" %> </li> </ul> </div> <!-- 中略 -->以前failureとなったテストがようやくこれで通る。
ではSessionsコントローラを実装しよう。app/controllers/sessions_controller.rbclass SessionsController < ApplicationController def new end def create user = User.find_by(email: params[:session][:email].downcase) if user && user.authenticate(params[:session][:password]) # ユーザーログイン後にユーザー情報のページにリダイレクトする else # エラーメッセージを作成する render 'new' end end def destroy end endcreateアクションにこんな記述があると思うけど、
if user && user.authenticate(params[:session][:password])Tutorialではこんな風に述べてくれている。
Rubyではnilとfalse以外のすべてのオブジェクトは、真偽値ではtrueになる
だからUserクラスが真偽値っぽくなくてもif文が通るのね。
あと何気にuserがインスタンスじゃないのも特徴。
createアクションに対するビューとかが必要なく(ログインできましたよ!みたいな画面を別で作るなら考慮してよい)アクション内で収まるのでインスタンスでない。ログインのためのビュー生成
form_for(:session, url: login_path)について考える
続いてログイン画面を作ろうと思うんだけど、Tutorialでこんな記述がある。
form_for(@user)Railsでは上のように書くだけで、「フォームのactionは/usersというURLへのPOSTである」と自動的に判定しますが、セッションの場合はリソースの名前とそれに対応するURLを具体的に指定する必要があります。
form_for(:session, url: login_path)なにこれ??最初は意味が分かりませんでした。
で調べたわけだけど、リソース名を'session'にすることでparams[:session]にキーと値が保存されるのね。つまり渡しているのはそれぞれこう。
form_for(@user) → params[:user] form_for(:session, url: login_pash) → params[:session]このあたりはこちらが分かりやすい!↓
rails ログイン機能のform_forの作りについての疑問
[Rails4.0] フォームの基本とStrongParametersを理解するform_withで書き換える
このポートフォリオではform_forではなくform_withを使う(理由は#6)。
その際はリソース名をscopeに指定するので、最終的にはこうなる。app/views/sessions/new.html.erb<% provide(:title, "ログイン") %> <div class="container form-container login-container"> <div class="row"> <div class="col"> <div class="form-logo-img"> <%= link_to image_tag('lantern_lantern_logo.png', width: 100), root_path, class: "logo-img" %> </div> <h1 class="form-title">ログイン</h1> <%= form_with(scope: :session, url: login_path, local: true) do |form| %> <div class="form-group"> <%= form.email_field :email, class: 'form-control', placeholder: "メールアドレス" %> </div> <div class="form-group"> <%= form.password_field :password, class: 'form-control', placeholder: "パスワード" %> </div> <div class="form-group"> <%= form.submit "ログイン", class: "btn btn-info btn-lg form-submit" %> </div> <% end %> <p class="form-go-to-signup-or-login">新しくはじめる方は<%= link_to "こちら", signup_path %></p> </div> </div> </div>書き換えでお世話になりました↓
【Ruby】チュートリアルのform_forをform_withで書き換え (おまけ:capybaraでのテスト)ログインのE2Eテスト生成
Tutorial 8.1.5 フラッシュのテストにならってテストをSystem specで書こう。
Tutorialとの差分も用意する。bash$ touch spec/systems/login_spec.rbspec/systems/login_spec.rb(RSpec:ポートフォリオ)require 'rails_helper' RSpec.describe "Logins", type: :system do context "login with invalid information" do it "is invalid because it has no information" do visit login_path expect(page).to have_selector '.login-container' fill_in 'メールアドレス', with: '' fill_in 'パスワード', with: '' click_on 'ログイン' expect(page).to have_selector '.login-container' expect(page).to have_selector '.alert-danger' end it "disappears flash messages when users input invalid information then other links" do visit login_path expect(page).to have_selector '.login-container' fill_in 'メールアドレス', with: '' fill_in 'パスワード', with: '' click_on 'ログイン' expect(page).to have_selector '.login-container' expect(page).to have_selector '.alert-danger' visit root_path expect(page).not_to have_selector '.alert-danger' end end endtest/integration/users_login_test.rb(Minitest:Tutorial)require 'test_helper' class UsersLoginTest < ActionDispatch::IntegrationTest test "login with invalid information" do get login_path assert_template 'sessions/new' post login_path, params: { session: { email: "", password: "" } } assert_template 'sessions/new' assert_not flash.empty? get root_path assert flash.empty? end end大きな変更点は5つ。
- テストは1つから2つに
- getはvisitに
- assert_templateはhave_selectorに
- postはfill_inに
- flash_empty?はhave_selectorに
1.
・フォームに入力しない場合エラーが発生する
・その場合別ページでflashが消える
を独立させたかったので2つに分けた。2.
System specでgetやpostを指定するとうまくいかない。
代わりにvisitを使おう。3.
assert_templateもRequest specでしか動作が確認できないので、have_selectorを使用しdivを検証することで描画をテストする。4.
E2Eテストの性格上、直接postをリクエストするのではなくフォームに値を入力する方がテストらしいのでfill_inを使う。5.
flashに対してbe_emptyを使用したテストは実装できなかった。
System specではこういう書き方もできない。expect(flash[:danger]).to be_truthybashNoMethodError: undefined method `flash' for nil:NilClass(Request specでは無事機能する)
Request specと異なりSystem specはコントローラで使用できるFlashHashオブジェクトと紐づいていないっぽい??
代わりにalert時に生成されるdiv要素を検証した。参考にさせていただきました↓
Ruby on Rails ガイド 5.2 Flash次回はいよいよ機能としてのログイン実装
おそらく次回がログインのメインです。
Sessionsヘルパーに各メソッドを与えます。
(超初心者向け)解説が載っているのでぜひご参考に〜
- 投稿日:2019-08-11T09:38:58+09:00
Railsチュートリアル1章まとめ
はじめに
Railsチュートリアルで発生したエラーとか詰まったところをまとめていこうと思います。
備忘録として。
2週目の今回はbitbucketではなくgithubで進めていこうと思います。rails newの時点でエラー発生
$rails _5.1.6_ new hello_app2 #エラー発生 can't find gem railties (= 5.1.6) with executable rails (Gem::GemNotFoundException)対処方法が下記コマンド
$gem install rails -v 5.1.6こちらで再度rails newコマンドすれば無事作成できました。
githubでのリポジトリ作成
いつもはgithubからローカルにクローンするやり方でやってたので今回はこちらで。
ローカルからプッシュするやり方で。まずgithubでリポジトリ作成する。
名前はhello_app2にしました。# ローカルリポジトリを作成するディレクトリに移動する cd hello_app2 # ローカルリポジトリを作成する $ git init $ git add . $ git commit -m "first commit # リモートリポジトリのアクセス先を設定する $ git remote add origin https://github.com/GitHubのユーザ名/GitHubのリポジトリ名(こちらではhello_app2).git # pushする $ git push -u origin mastergit push heroku でエラー
エラー文
remote: ! remote: ! Could not detect rake tasks remote: ! ensure you can run `$ bundle exec rake -P` against your app remote: ! and using the production group of your Gemfile. remote: ! Activating bundler (2.0.1) failed: remote: ! Could not find 'bundler' (2.0.1) required by your /tmp/build_b85aaac02a3a812e844f20844711c628/Gemfile.lock. remote: ! To update to the latest version installed on your system, run `bundle update --bundler`. remote: ! To install the missing version, run `gem install bundler:2.0.1` remote: ! Checked in 'GEM_PATH=vendor/bundle/ruby/2.5.0', execute `gem env` for more information remote: ! remote: ! To install the version of bundler this project requires, run `gem install bundler -v '2.0.1'` remote: !こちらのエラー分の中にあるように下記コマンド
$ gem install bundler -v 2.0.1 $ rm Gemfile.lock $ bundle install $ git add . $ git commit -m 'hoge' $ git push heroku masterこちらで解決し無事デプロイ完了しました。
- 投稿日:2019-08-11T09:06:08+09:00
rails db:〇〇 は連続して書ける (メモ)
$bundle exec rake db:drop db:create db:migrate
こういう書き方で連続していける