css上で動的に値の変更はできないけどhtmlに書けばできる
cssに設定する値を動的に変更するとかできないのか?なんて思ったことないでしょうか?
例えば、
- ユーザー毎にボタンの色や背景色などを変更したい!
- 条件に応じてbackgroundに画像を入れたり、差し替えたりしたい!
結論から言うとできます!
ただcssでは出来ないのでhtmlにstyle属性を指定してcssを書き込めばokです!
今回はナビゲーションバーにある予約ボタンをユーザー毎に変更する例です。
model
前提としてユーザー = userモデルがあり、そこにカラーコードが設定されているとします。
id | name | color | created_at | updated_at |
---|---|---|---|---|
1 | opiyo | 30a8b5 | ||
2 | kuma | 000 | ||
3 | fuzo | fff |
シンプルな使い方
view
haml
.event__nav = link_to '予約', '#', style: "background-color: ##{user.color}"
html
<div class="event__nav"> <a style="background-color: #30a8b5" href="#">予約</a> </div>
条件がいっぱいあるときの使い方
view
haml
.event__nav = link_to '予約', '#', style: event_nav_btn_style(user)"
link_toは第三引数にstyle属性を使えば、htmlに直接cssの設定ができます。
html
<div class="event__nav"> <a style="background-color: #30a8b5" href="#">予約</a> </div>
helper
条件が多かったりする場合はhtmlからhelperメソッドを呼び出して上げればokです!
def event_nav_btn_style(user) if user.name == 'hogehogehoge' return 'background-color: #FF7A00;' elsif user.name == 'piyopiyopiyo' return "background-color: ##{user.color};" else return 'background-color: black;' end end
ActiveJobを使った非同期処理の方法
Ruby on Railsを使って非同期で処理する方法 ActiveJobについてです。
ざっくり手順
- generateでjobファイルを作成する
- jobをキューに登録する
- jobを実行する
generateでjobファイルを作成する
コマンドで
$ bundle exec rails g job hoge_job
手作業で
jobs/hoge_job.rb
に.rbファイルを作成する
jobをキューに登録する
HogeJob.perform_later(event_id: @event.id)
perform_later: 実行キューが積まれて随時処理する
perform_now: 今すぐに実行する
時間指定する方法
明日の正午("2019/06/12 12:00")
HogeJob.set(wait_util: Date.tomorrow.noon).perform_later()
1週間後
HogeJob.set(wait: 1.week).perform_later()
※要注意は時間を指定してはいけない。あくまで日数を指定すること。
呼び出された方
#jobs/hoge_job.rb class HogeJob queue_as :default URL = Rails.application.secrets.url SUCCESS = 200 FAILED = 400 def perform(event_id, status_code = 100) response = RestClient.get "#{URL}?event_id=#{event_id}&status_code=#{status_code}" result = JSON.parse(response.body).dig('root') if result['code'] == FAILED Bugsnag.notify(exception, { event_id: event_id, error_message: result['message']}) end end end
コールバックを指定することもできるので、model的な感じで色々できそうです。
参考サイト
Ruby on Railsでcsvダウンロード機能の作り方
Ruby on Railsを使ってcsvファイルのダウンロード機能を作りたくて調べました。
大枠の手順
- routesを設定
- csvを受け付けるアクションをコントローラーに設定
- 対象データを抽出しcsvフォーマットのファイルへ渡す
- CSVデータを作成する
- 画面にダウンロードボタンを作成する
コントローラーにアクションを定義する
# controller/users_controller.rb def index @users = User.all respond_to do |format| format.html format.csv do filename = ERB::Util.url_encode('ユーザー一覧.csv') send_data render_to_string, filename: filename, type: :csv end end end
ERB::Util.url_encode
は引数に渡された文字列をURLエンコードしてくれる。これによって日本語や半角スペースをよしなにしてくれる
send_data
は動的に生成されたデータをダウンロードする。
render_to_string
は表示結果を文字列として取得するメソッド。
なので、これらを組み合わせる事でCSVファイルに出力する為の文字列を作ってダウンロードを可能にしている。
参考: https://docs.ruby-lang.org/ja/latest/class/ERB=3a=3aUtil.html
参考: http://railsdoc.com/references/send_data
参考: http://railsdoc.com/references/render_to_string
View
# views/users/index.haml = link_to 'CSVダウンロード' users_path(format: :csv)
ポイントはリンクを生成するpathにフォーマット: csvを指定してあげます。これによってコントローラー側で切り分けれるようになります。
csvファイル
# views/users/index.csv.ruby require 'csv' require 'nkf' csv_data = CSV.generate do |csv| csv << %w(id 名前 年齢) @users.each do |user| csv << [ user.id, user.name, user.age ] end end NKF::nkf('--sjis -Lw', csv_data)
CSV.generate
は文字列csv形式=カンマ区切りの文字列を生成してくれます。
NKF::nkf
は文字コードを強制的に変換する為のプログラムです。
【Rails】Strong Parametersで`param is missing...`エラーになる
こんちには。opiyoです。
ActionController::ParameterMissing in ImagesController#create param is missing or the value is empty: image def image_paprams params.require(:image).permit(:name, :picture) end end
こんな感じでエラーになるのだが、params
が空っぽだよと。
実際にparams[:image]
ってやるとnil
が返ってくる。
が、すげー凡ミスでpermit
て定義している:name
をview側に書いてなかった。
だから、来るはずのデータが来てなくてエラーになったのではなかろうかと思ってる。
<%= form_for(@image) do |f| %> <div class="form-group"> <%= f.text_field :name %> # ここを書いてなかったけど、書いたらエラーにならなくなった <%= f.file_field :picture %> <%= f.submit "画像を投稿する" %> </div> <% end %>
そんな感じ。
Docker For MacでPostgreSQL9.5系の環境を作ってみた!
こんばんは。opiyoです。
brew
でインストールするとなんだかPostgreSQL9.6系がインストールされてしまうのだけど、本番環境と同じバージョンをローカルに使いたくてウズウズしていたのだが...
Docker
使えば良いじゃんってアドバイス貰ったので教えてもらった
Docker For Macのインストール
https://www.docker.com/docker-mac
docker-compose.ymlを作る
docker-compose.ymlの場所を作る
$ mkdir -p ~/compose/postgresql95/ $ vi docker-compose.yml
PostgreSQL9.5を配置する場所を作る
$ mkdir ~/postgres95
docker-compose.ymlを修正
postgresql: image: postgres:9.5.8-alpine environment: POSTGRES_USER: taku POSTGRES_INITDB_ARGS: "--encoding=UTF8 --no-locale" LANG: ja_JP.UTF-8 ports: - "5432:5432" volumes: - /Users/taku/postgres95:/var/lib/postgresql/data #「/Users/taku/postgres95」の箇所を、さっき作った場所を指定する restart: always
やることは、
POSTGRES_USER
を各自設定するvolumes
のパスをさっき作った場所を指定する
image
の部分には、こちらのサイトに書いてあるように好きなバージョンを書けばおk
https://hub.docker.com/r/library/postgres/tags/
自動起動されているpostgresqlを止める
本当は9.6系と9.5系を共存させたいのだけど、上手くいかないので既にPostgreが動いている場合は止める。
私の場合は、brew
でインストールしてたので簡単だった。
$ brew services list Name Status User postgresql started taku $ brew services stop postgresql $ brew services list Name Status User postgresql stopped
パスを通す
$ sudo vi ~/.bash_profile export PGHOST=localhost $ env | grep HOST PGHOST=localhost
これは教えてもらうがままにやったけど、何してるのかいまいち分かってないぞ。
構築と起動
$ docker-compose up -d
でpsql -l -p5432
とかやればアクセスできるはず!
めちゃくちゃ簡単でした。
その他Dockerコマンド
ステータス
$ docker ps
停止
$ docker stop CONTAINER_NAMES
コンテナに入る
$ docker exec -it CONTAINER_NAMES /bin/bash
こんな感じ。
【Rails】ファイルのフルパス、ファイル名を取得する
過去データとかでファイルを一括で読み込みたい場合で使える技です。
> files = Dir.glob("/Users/taku/rails/gist/test/*.xls") => ["/Users/taku/rails/gist/test/1.xls", "/Users/taku/rails/gist/test/2.xls", "/Users/taku/rails/gist/test/3.xls", "/Users/taku/rails/gist/test/4.xls", "/Users/taku/rails/gist/test/5.xls"]
これでfiles
をグルグル回して一つ一つのファイルを読み込んでやれば色々処理できますね。
取り込み処理も出来るし、csvを作ったりも出来るし。
glob
を使うと拡張子で絞り込めるので、結構便利ですね。
> files = Dir.entries("/Users/taku/rails/gist/test/") => [".", "..", ".DS_Store", "1.csv", "1.txt", "1.xls", "1.xlsx", "2.csv", "2.txt", "2.xls", "2.xlsx", "3.csv", "3.txt", "3.xls", "3.xlsx", "4.xls", "5.xls"]
単純にディレクトリの中にあるファイル名だけ取得したい場合はentries
を使うそうです。
【Ruby】配列の中身が重複しているかをチェックする方法(select、find)
csvのデータを取り込み別のcsvへ吐き出す処理をしていたのですが、値が重複していることに気がつきました。
こんな感じ。
data = [] inport = CSV.read("./inport.csv") inport.each do |c| data << c end CSV.open("./export.csv", "w") do |export| data.each do |d| export << d end end
この中で取り込んだデータが重複しているかどうかチェックする方法が分からず、グルグル回さないとダメなのかと思っていたのですが良い方法がありました。
> ff6 = [["Tina", 1],["Rokku", 2],["Edoga", 3],["Masshu", 4]] => [["Tina", 1], ["Rokku", 2], ["Edoga", 3], ["Masshu", 4]] > ff6.select{|f| f[0] == "Masshu"} => [["Masshu", 4]] irb(main):003:0>
select
を使うと合致したものだけを取ってきてくれます。
なので重複しているかチェックしたい場合は、size
を使って数を調べてやれば良いです。
> ff6.select{|f| f[0] == "Masshu"}.size => 1 > if ff6.select{|f| f[0] == "Masshu"}.size > 0 > # 処理 > end
めでたし、めでたし
追記
find
を使った方が早いとのこと。
> ff6 => [["Tina", 1], ["Rokku", 2], ["Edoga", 3], ["Masshu", 4]] > ff6.find {|ff| ff[0] == "Masshu" } => ["Masshu", 4] > ff6.find {|ff| ff[0] == "Masshu" }.size => 2
select
との違いは、見つけた最初の配列を一つだけ返す。ってことですかね。
例えば、
# 「カイエン 4」を追加します > ff6 = [["Tina", 1],["Rokku", 2],["Edoga", 3],["Masshu", 4], ["Kaien", 4]] => [["Tina", 1], ["Rokku", 2], ["Edoga", 3], ["Masshu", 4], ["Kaien", 4]] # selectの場合 > ff6.select {|ff| ff[1] == 4 } => [["Masshu", 4], ["Kaien", 4]] # findの場合 > ff6.find {|ff| ff[1] == 4 } => ["Masshu", 4]
最初に見つけた一つを返すんだから、そりゃー早いは。
今回やりたいことは、重複しているかのチェックだから1件でもあればNG。
ってことでfind
を使うことにした。
【bootstrap3】ナビゲーションバー(Navbar)で起きたレイアウト崩れと右寄せへの対応方法
こんばんは。opiyoです。
結論としては勝手な解釈をして余計なことしてた!に尽きるのだけど、同じようなハマり方すると時間もったい無いので共有です。
<header> <nav class="navbar navbar-inverse"> <div class="container-fluid"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbarEexample1"> <span class="sr-only">Toggle navigation</span> <% if logged_in? %> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> <% else %> <span class="icon-bar"></span> <span class="icon-bar"></span> <% end %> </button> <a class="navbar-brand" href="#"> <%= link_to "RChannel", root_path, id: 'logo' %> </a> </div> <div class="collapse navbar-collapse" id="navbarEexample1"> <ul class="nav navbar-nav navbar-right"> <% if logged_in? %> <li class="inline-block"><%= link_to "キーワード登録", new_user_keyword_path(user_id: @current_user.id) %></li> <p class="navbar-text inline-block"><%= @current_user.name %> さん</p> <li class="inline-block"><%= link_to "マイリスト", my_list_topic_path(id: @current_user.id) %></li> <li class="inline-block"><%= link_to "ログアウト", logout_path, method: :delete %></li> <% else %> <li class="inline-block"><%= link_to "アカウント登録", new_user_path %></li> <li class="inline-block"><%= link_to "ログイン", login_path %></li> <% end %> </ul> </div> </div> </nav> </header>
ポイントはサンプル通りそのまま記載してください!
で僕が何をしていたのかご覧ください。
画面小さくした時に文字が隠れない
header側にもリンクが必要だと思ったんだよ。
<div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbarEexample4"> <span class="sr-only">Toggle navigation</span> <% if logged_in? %> <span class="icon-bar"><%= link_to "キーワード登録", new_user_keyword_path(user_id: @current_user.id) %></span> <span class="icon-bar"><%= link_to "マイリスト", my_list_topic_path(id: @current_user.id) %></span> <span class="icon-bar"><%= link_to "ログアウト", logout_path, method: :delete %></span> <% else %> <span class="icon-bar"><%= link_to "アカウント登録", new_user_path %></span> <span class="icon-bar"><%= link_to "ログイン", login_path %></span> <% end %> </button> <a class="navbar-brand" href="#"> <%= link_to "RChannel", root_path, id: 'logo' %> </a> </div>
最初こんな感じで.navbar-header
のspan
タグにもリンクが必要だと思って記載していたのですが、これやるとレイアウトが崩れます!
なので、span
タグには何も書いてはいけません!
左寄せが出来ない
<div class="collapse navbar-collapse" id="navbarEexample1"> <ul class="nav navbar-nav"> <% if logged_in? %> <li class="inline-block navbar-right"><%= link_to "キーワード登録", new_user_keyword_path(user_id: @current_user.id) %></li> <p class="navbar-text inline-block navbar-right"><%= @current_user.name %> さん</p> <li class="inline-block"><%= link_to "マイリスト", my_list_topic_path(id: @current_user.id) %></li> <li class="inline-block"><%= link_to "ログアウト", logout_path, method: :delete %></li> <% else %> <li class="inline-block navbar-right"><%= link_to "アカウント登録", new_user_path %></li> <li class="inline-block navbar-right"><%= link_to "ログイン", login_path %></li> <% end %> </ul> </div>
サンプル見ると、p
タグにnavbar-right
設定しているから真似してたのだけど全然右寄せにならない。
で、Chromeで確認してみると
分かりづらいけど、既に領域が固定されているからfloat
かけたって無駄。
なので、上の要素div
タグに対して.navbar-right
を設定してやることでうまくいった!
まとめ
勝手な解釈はせずに、基本に忠実にやりましょう!
【Rails】XX件以上登録できないようにする独自バリデーションメソッドを設定する方法
こんばんは。opiyoです。
Railsアプリで「XX以上は登録できない!」というバリデーションを設定したかったので、やり方を調べてみました。
やりたいこと
Keyword
というテーブルにデータを登録する処理で、5つまでしか登録できないようにバリデーションを設定します。
もし5つ以上の登録を行おうとした場合はsave
でエラーになるようにします。
Model
class Keyword < ApplicationRecord validates :title, presence: true validates :url, presence: true validate :check_count def check_count errors.add(:keyword, "は5つまで登録可能です。") if Keyword.count >= 5 # errors.add(:keyword, "は5つまで登録可能です。") if Keyword.where(user_id: self.user_id).count >= 5 end end
モデルにバリデーションメソッドを定義します。
validate
にメソッド名を記述し、そのメソッド内でKeyword
の件数を取得しif
文を記述します。
もし5件以上あれば、errors
の中に名前とエラーメッセージを追加します。
またコメントアウトしている部分ですが、self
を使うことで自分が登録したデータのみを対象とすることが出来ます。こうやって使うことが基本になりそうですね。
また、errors
の正体についてはActiveModel::Errors
で詳細はこちらを。
Controller
class KeywordsController < ApplicationController def create @keyword = Keyword.new(keyword_params) @keyword.check_count if @keyword.save redirect_to root_url else render 'new' end end private def keyword_params params.require(:keyword).permit(:title, :url) end end
コントローラーではKeyword
をインスタンス化して、メソッドを呼び出すだけです。
5件以上ある場合はerrors
にエラーが追加されているので、save
する時に失敗します。
という流れです。
【Bootstrap】navbarの背景色、文字色の悩みはこれ一発で解決だわー
何か作る時bootstrapにお世話になることあると思うのですが、cssいじっただけだと何故か上手くいかない。
例えば今回のnavbar
ですね。
ナビゲーションメニューがかっこよく決まらないとモチベーションが全く上がらないので、いろいろ探していたらあったわー。神様が。 https://work.smarchal.com/twbscolor/
自分の好きなカラーコードを入力したら勝手にcssのコード吐き出してくれる。
scss
sass
less
css
全部あるよ。
haml
.navbar.navbar-default.navbar-static-top .container %button.navbar-toggle(type="button" data-toggle="collapse" data-target=".navbar-responsive-collapse") %span.icon-bar %span.icon-bar %span.icon-bar %a.navbar-brand(href="#") ホーム .navbar-collapse.collapse.navbar-responsive-collapse %ul.nav.navbar-nav %li= link_to "Link 1", "/path1" %li= link_to "Link 2", "/path2" %li= link_to "Link 3", "/path3"
scss
$bgDefault : #dd4444; $bgHighlight : #e8350e; $colDefault : #ecf0f1; $colHighlight : #ecdbff; $dropDown : false; .navbar-default { background-color: $bgDefault; border-color: $bgHighlight; .navbar-brand { color: $colDefault; &:hover, &:focus { color: $colHighlight; }} .navbar-text { color: $colDefault; } .navbar-nav { > li { > a { color: $colDefault; &:hover, &:focus { color: $colHighlight; }} @if $dropDown { > .dropdown-menu { background-color: $bgDefault; > li { > a { color: $colDefault; &:hover, &:focus { color: $colHighlight; background-color: $bgHighlight; }} > .divider { background-color: $bgHighlight;}}}}} @if $dropDown { .open .dropdown-menu > .active { > a, > a:hover, > a:focus { color: $colHighlight; background-color: $bgHighlight; }}} > .active { > a, > a:hover, > a:focus { color: $colHighlight; background-color: $bgHighlight; }} > .open { > a, > a:hover, > a:focus { color: $colHighlight; background-color: $bgHighlight; }}} .navbar-toggle { border-color: $bgHighlight; &:hover, &:focus { background-color: $bgHighlight; } .icon-bar { background-color: $colDefault; }} .navbar-collapse, .navbar-form { border-color: $colDefault; } .navbar-link { color: $colDefault; &:hover { color: $colHighlight; }}} @media (max-width: 767px) { .navbar-default .navbar-nav .open .dropdown-menu { > li > a { color: $colDefault; &:hover, &:focus { color: $colHighlight; }} > .active { > a, > a:hover, > a:focus { color: $colHighlight; background-color: $bgHighlight; }}} }