TECH EXPERT 57日目

TECH::EXPERT

カテゴリーの1つ目を選ぶと中カテゴリーのセレクタが出現して、さらに中カテゴリーを選ぶと小カテゴリーが出現するような機能を実装中。
JavaScriptで処理するのが早いけど、JavaScript使わずにcssだけで実装できないかな?
targetやcheckedなどの疑似クラスで強引に解決できないか模索中。

あとしばらくpythonやってないのでちょっとだけ自分アプリを作成中。

css

target疑似クラス

aタグでhrefにidを指定した場合、ページ内コンテツに飛べる。
この際、taget疑似クラスが付与されるので、class名:tagertとするとtargetされた時のcssを指定できる。
この機能を用いてハンバーガーメニューを呼び出すといった機能がcssのみで制御できる。

注意点としてはurlが変わることで、ページ移動の履歴が汚れる。

>

通常無指定でclass classとスペースつなぎにすると孫要素も含め、全ての要素に適応されるが、class>classとすると子要素のみに適応できる。

rails

select_tag

=select_tag :"#{category.name}", options_for_select(category.children.map{|i| [i.name, i.id, id: "category_#{i.id}"]}), { prompt: 'すべて', class: 'category-child' }

=select_tag :category_id, options_for_select(@categories.map{|i| [i.name, i.id, id: "category_#{i.id}"]}), { prompt: 'すべて', class: 'search-root' }

collection_check_boxes

input type=hiddenが出力されるために、空文字が選択要素としてparamsに混ざってしまう。
解決するにはinclude_hidden: falseを引数に渡すとtype=hiddenを出力させないようにできる。

= f.collection_check_boxes :grandchild_ids, cc.children, :id, :name, include_hidden: false

layout

controllerに記載する。利用するレイアウトファイルを標準のapplication.html.erbから指定したファイルへ切り替える。

items_controller.rb
class ItemsController < ApplicationController
  layout 'hogehoge'

  def index
    @items = Item.includes(items_users: :users)
  end
end

アソシエーションのメソッド名を変更

通常アソシエーションを組む際には相手のモデル名のみを指定する。
この時、モデル名はclass_nameとして渡し、メソッド名を宣言することでメソッド名を変更できる。

class Group < ActiveRecord::Base
has_many :menmers, class_name:  "User"

group.membersでuserモデルのインスタンスを取得できる。

validation

validates :name, length: { maximum: 50 }, presence: true
validates :place, length: { maximum: 100 }, presence: true
validates :content, length: { maximum: 2000 }, presence: true
validates :start_time, presence: true
validates :end_time, presence: true
validate :start_time_should_be_before_end_time

def created_by?(user)
  return false unless user
  owner_id == user.id
end

private

def start_time_should_be_before_end_time
  return unless start_time && end_time

  if start_time >= end_time
    errors.add(:start_time, 'は終了時間よりも前に設定してください')
  end
end
VALID_YOUTUBE_URL_REGEX = /\A.*youtube.*\z/
validates :text, presence: true, format: { with: VALID_YOUTUBE_URL_REGEX }

キャメルケース

# キャメルケースは先頭が小文字で、単語の区切りを大文字で表す
# アッパーキャメルケースは先頭から大文字を使用する

キャメルケース: adminUserCommentCreator
アッパーキャメルケース: AdminUserCommentCreator


# スネークケースは単語の区切りをアンダースコアで表す
スネークケース: admin_user_comment_creator


Railsの慣習的な命名規則として、下記のように使い分ける。

クラス名:アッパーキャメルケース
メソッド名:スネークケース
変数名:スネークケース

また、Javascriptでは一般的にキャメルケースを用いて記述する。

model

scope

よく使うメソッドをmodelにscopeとして設定しておくことで、controller側で記述を簡略化できる。

class User < ApplicationRecord
 scope :team_red, -> { where(team: 0) }
 scope :captain, ->{ where(captain: true) }
end

user.team_red.captainのように記述できる。

未解決

primary_keyの変更

id: falseにしてprimary_keyを設定するとそちらがprimary_keyになる。

class CreateCompanies < ActiveRecord::Migration[5.2]
  def change
    create_table :companies, id: false do |t|
      t.integer   :code, primary_key: true
      t.string    :name
      t.timestamps
    end
  end
end

ただし、アソシエーション先からの連携が複雑になる。

モデルファイルにてprimary_keyを宣言し、アソシエーションでも連携用にプライマリーキーを指定する。

class Company < ApplicationRecord
  self.primary_key = :code
  has_many :users_companies, primary_key: :code
  has_many :users, through: :users_companies
  has_many :irs, primary_key: :code
end

ただ、migrationファイルの書き方がよくわからない

class CreateUsersCompanies < ActiveRecord::Migration[5.2]
  def change
    create_table :users_companies do |t|
      t.integer           :code, foreign_key: true
      # t.references       :company, foreign_key: true

t.referencesを用いると、companies_idはない!って怒られるし、integerでやると外部キー制約が張れない。張らなくていいのか?

コードレビューツール

質の高いレビューを自動で・高速に行えるようになります。代表的なものとしてRubocop、ESlintなどがあります。

Rubocop

rubyの文法を静的に解析してくれる。

以下を記入してbundle install

gem 'rubocop', require: false

rubocopを実行

bundle exec rubocop

設定ファイルを作成

.rubocop.ymlをprojectフォルダに作成
以下のように記載

AllCops:
  # 除外するディレクトリ(自動生成されたファイル)
  # デフォルト設定にある"vendor/**/*"が無効化されないように記述
  Exclude:
    - bin/*
    - db/schema.rb
    - node_modules/**/*
    - db/migrate/*
    - vendor/**/*
  # Rails向けのRails copsを実行。"rubocop -R"と同じ
  Rails:
    enabled: true

# "Missing top-level class documentation comment."を無効
Style/Documentation:
  Enabled: false

# "Prefer single-quoted strings when you don't need string interpolation or special symbols."を無効
Style/StringLiterals:
  Enabled: false

# "Line is too long"を無効
Metrics/LineLength:
  Enabled: false

#'frozen_string_literal: true'を無効
Style/FrozenStringLiteralComment:
  Enabled: false

修正の手順

  • 1.現在の違反をすべて記録した上で、いったん違反では無いことにする
  • 2.記録した違反のうち1つを、違反していることに戻す
  • 3.その違反を解消する
bundle exec rubocop --auto-gen-config

コマンドを実行すると、現在の規約違反がまとめられた.rubocop.todo.ymlというファイルが生成される。.rubocop.todo.ymlには違反を許容するような記述となっている。

また.rubocop.ymlに以下が追記され、上記の違反を許容させている。

inherit_from: .rubocop_todo.yml

つづいて規約違反を1つ削除してrubocopを実行。
エラーが出るので必要に応じて修正する。

gem fileの並び順がおかしいなど、オートコレクトが必要な場合には-aオプションが利用できる場合もある。

bundle exec rubocop -a

sider

コードレビューツールを一括導入できるサービス。
githubのリポジトリごとに適応できる。

ESlint

Javascriptの静的コード解析ツール

ancestry

catch_depth

階層で要素を取得できるようになる。
オプションなのでモデルに追加指定が必要なことに加え、カラムも足す必要がある。

Jquery

objectがnill/undefinedか判定

sizeやlengthで要素を測る。

object.length==0

selectタグの選択された状態を取得

selectタグに対してoption:selectedを指定。
onイベントはchangeが良さそう。

$('#search-category-select').on('change', function(e){
    var id = $('#search-category-select option:selected').val();
}

子要素の取得

直下の子要素の取得は.children()で可能
引数にclassやidを指定すればその要素のみ取得する。

$('.child').children().addClass("hide")

python

printf %s, %d

% 演算子 (モジュロ) 。文字列 書式化 または 補間 演算子とも呼ばれる。
要は置換してくれる。
%sはstring、%dはdigit(数値)
辞書を渡すこともでき、その場合は
print( ‘%(key)s’ % { “key”: “value”, “number”: 2 })

組み込み型 — Python 3.9.0 ドキュメント

mySQLを使う

mysql-connector-pythonで行きます。これが公式。
他にもいろいろあるが、mysqlclientか上記のどっちがメジャー。

pip install mysql-connector-python

mysqlへ接続する

conn = mysql.connector.connect(
    host='localhost',
    port='3306',
    user='root',
    password='パスワード',
    database='データベース名'
)

データベース操作にカーソルが必要なので作成する。

cur = conn.cursor()

テーブルへ保存する

query = "INSERT INTO sample_table (id, name) VALUES (%s, %s)"
1つのレコードを保存
cur.execute("INSERT INTO テーブル名 (id, name) VALUES (%s, %s)", (1, "hoge"))
複数のレコードを保存
records=[(1,"hoge","fuga"),(2,"hoo","foo")]
cur.executemany("INSERT INTO test_table VALUES (%s, %s, %s)", records)

テーブルの変更を行なった場合は最後にcommitすることで保存する。

conn.commit()

コネクションを閉じる。

conn.close()

コメント