- 投稿日:2019-06-27T23:01:09+09:00
QiitaAPIで各プログラミング言語のタグの記事とフォロワーの数を調べて纏めたりする(pythonとrubyを使う)
今どのプログラミング言語がどれくらい人気なのか気になるので、qiitaで書かれた記事のタグを調べて見ましたが、一々調べるよりもコードを書いてQiitaAPIからデータを取得して分析する方が楽なので、書いてみました。この記事では試してみたこととその結果を纏めてみます。
QiitaAPIについて
QiitaAPIにアクセスしたらタグの記事の数とフォローしている人数を調べることができます。
例えばc++のタグ https://qiita.com/api/v2/tags/c++
QiitaAPIについて詳しくは https://qiita.com/api/v2/docs
このようなjsonデータが出ます
{"followers_count":24875,"icon_url":"https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/fe7df47710bdae8b8565b323841a6b89e2f66b89/medium.jpg?1515774066","id":"C++","items_count":5919}使うプログラミング言語
基本的にQiitaAPIのようなAPIは色んな言語で簡単に扱えるようですが、今回はrubyとpythonを使ってQiitaAPIを通じて色んなプログラミング言語のタグの記事とフォロワーの数を調べて纏めてみます。
最初は全部rubyでやってみたいと思っていたのですが、データの分析やグラフを描くことがやはりpythonのpandasとmatplotlibを使ったほうがずっと楽です。
なのでrubyでデータをcsvに保存して、pythonでグラフを描くという形になります。
ここで挙げるのは自分が知っている言語だけです。それ以外知らない言語もあるかもしれないです。その他に、私の知っている言語の中にも IDLなどqiitaで全然記事が見つからない言語もあります。
プログラミング言語だけ比べるので、htmlやxmlみたいなマークアップ言語や、cssみたいなスタイルシート言語などは含まれません。
rubyでタグのデータをcsvに保存する
rubyでは標準モジュールとしてopen-uriというスクレイピング用のモジュールがあるので、簡単にQiitaAPIからデータを取得できます。
csvの扱いも簡単にcsvモジュールが使えます。これも標準モジュールなので個別にインストールする必要がありません。
require "open-uri" require "json" require "csv" gengo = %w!c c++ csharp cobol clojure delphi elm erlang fortran golang haskell java javascript julia kotlin lisp lua objective-c pascal perl php prolog python r ruby rust scala swift typescript vb.net! col = %w!id items_count followers_count! CSV.open('qiitaprogramming.csv','w'){|csv| gengo.each{|gg| data = JSON.parse(open('https://qiita.com/api/v2/tags/'+gg).read) csv << col.map{|c|data[c]} } }こういうcsvファイルが出来ます。左の方が記事の数で、右の方がフォロワーの数です。
C,2632,21744 C++,5919,24875 C#,7427,23077 cobol,56,45 Clojure,619,550 Delphi,347,148 Elm,452,463 Erlang,524,446 Fortran,255,196 golang,2886,2025 Haskell,1904,9497 Java,12165,39527 JavaScript,26386,59733 Julia,570,620 Kotlin,2576,2243 lisp,267,358 Lua,452,320 Objective-C,3946,17766 Pascal,82,14 Perl,1469,12188 PHP,16169,37330 Prolog,207,96 Python,28671,55570 R,2727,1912 Ruby,21943,34065 Rust,1298,1527 Scala,2830,10363 Swift,11791,6603 TypeScript,3036,2325 VB.Net,359,322pythonでデータを並べて棒グラフを描く
pythonにもスクレイピング用のモジュールがたくさんあります。ここではrequestsというモジュールを使います。
データを数によって並べるにはpandasでは一番やりやすいです。
グラフを描くにはmatplotlibが一番です。
どれでも標準モジュールではないからインストールする必要がありますが、anacondaとか使っていたら最初から含まれているはずです。
pip install requests pandas matplotlibちなみに、rubyにもdaruというpandasと似ているモジュールが存在します。https://github.com/SciRuby/daru/wiki/pandas-vs-daru
import requests import matplotlib.pyplot as plt import pandas as pd lis_tagid = '''c c++ csharp cobol clojure delphi elm erlang fortran golang haskell java javascript julia kotlin lisp lua objective-c pascal perl php prolog python r ruby rust scala swift typescript vb.net '''.split() gengo = [] n_follow = [] n_item = [] for tagid in lis_tagid: r = requests.get('https://qiita.com/api/v2/tags/'+tagid) r.raise_for_status() data = r.json() gengo.append(data['id']) n_follow.append(data['followers_count']) n_item.append(data['items_count']) df = pd.DataFrame(index=gengo) df['記事'] = n_item df['フォロワー'] = n_follow print(df) y = range(len(df)) plt.figure(figsize=[6,6]) plt.gca(ylim=[min(y)-0.5,max(y)+0.5]) df.sort_values('フォロワー',inplace=True) plt.yticks(y,['%s: %6s'%x for x in df['フォロワー'].iteritems()]) plt.barh(y,df['フォロワー'],color='#882244') plt.title(u'フォロワㄧ',family='AppleGothic') plt.tight_layout() plt.figure(figsize=[6,6]) plt.gca(ylim=[min(y)-0.5,max(y)+0.5]) df.sort_values('記事',inplace=True) plt.yticks(y,['%s: %6s'%x for x in df['記事'].iteritems()]) plt.barh(y,df['記事'],color='#337744') plt.title(u'記事',family='AppleGothic') plt.tight_layout() plt.show()結果
記事 フォロワー C 2632 21744 C++ 5919 24875 C# 7427 23077 cobol 56 45 Clojure 619 550 Delphi 347 148 Elm 452 463 Erlang 524 446 Fortran 255 196 golang 2886 2025 Haskell 1904 9497 Java 12165 39527 JavaScript 26386 59733 Julia 570 620 Kotlin 2576 2243 lisp 267 358 Lua 452 320 Objective-C 3946 17766 Pascal 82 14 Perl 1469 12188 PHP 16169 37330 Prolog 207 96 Python 28671 55570 R 2727 1912 Ruby 21943 34065 Rust 1298 1527 Scala 2830 10363 Swift 11791 6603 TypeScript 3036 2325 VB.Net 359 322纏め
結果から見ると、フォロワーの数はjavascriptの方が一番ですが、記事の数はpythonの方が一番です。
javascriptはウェブ開発に欠かせない言語ですし、データサイエンスや機械学習のおかげでこの数年の間にpythonはどんどん人気な言語になってきたようです。
二年前のこの記事を調べてみたら https://qiita.com/ty-edelweiss/items/b8172c2e22726bc08aeb
あの時pythonの記事はrubyよりも少なかったようです。javaとphpとrubyもその次に人気のようです。
C言語などはフォロワーが多い割には記事が少ないです。
結果としてフォロワーと記事の数を見ると、この数年間のプログラミング言語の使う傾向をある程度示せるはずです。
編集: 2019年6月28日に、Rustを追加しました
- 投稿日:2019-06-27T22:17:48+09:00
選挙の演説の候補者の計算【ruby初心者】
はじめに
演説が終わるたび「他のそれぞれの立候補者の支持者から 1 人ずつ」および「誰も支持していない有権者から 1 人」支持者が増える。
その場合、最終結果で一番多い支持を集めた立候補者の番号を取得する。実際のコード
#M は立候補者の人数を、N は有権者の人数を、K は演説が行われる回数 m,n,k = gets.split(" ").map &:to_i #誰がどの順番で演説したかを表す整数 a_1, ..., a_K a = readlines.map &:to_i #立候補者の人数に応じて、支持者の人数を図れる配列を用紙 r=[] m.times {r.push(0)} #すべての演説が終わった後、最も支持者が多い立候補者の番号(複数ある場合は、すべて出力) a.each do |i| #誰も支持していない人が、指示をする if n >0 n -= 1 r[i-1] += 1 end #他の人を支持している人が、指示をする r.count.times do |t| if r[t] >0 r[t] -= 1 r[i-1] += 1 end end end #最大数を返す r.each_index do |t| if r[t] == r.max puts t+1 end end
- 投稿日:2019-06-27T20:56:51+09:00
数あてゲームをrubyでつくる【初心者】
はじめに
特定の数よりも大きいか、小さいか、割り切れるかを複数回受け取り。1つの数字に絞られるまで条件を出していく、計算ゲーム。
数字は1〜100に制限。入力例と出力例
入力例
4 / 4 < 90 / 6 > 77出力例
84実際のコード
#条件の入力合計回数 N = gets.chomp.to_i #入力されたデータの取得 results = [] N.times do |timesCount| results.push(gets.chomp.split(" ")) end #1~100の配列を用意しておく i= [*(1..100)] #条件に応じて、処理することを変える results.each do |r| conpare,number = r.to_a if conpare == ">" i = i.select {|item| item > number.to_i} #指定数値よりも大きい数字を選定 elsif conpare == "<" i = i.select {|item| item < number.to_i} #指定数値よりも小さい数字を選定 else i = i.select {|item| item % number.to_i == 0} #指定数値で割り切れる数字のみを選定 end end puts i
- 投稿日:2019-06-27T20:26:09+09:00
RSpecを起動ようとしたら、「unknown command: Cannot call non W3C standard command while in W3C mode」と言われて怒られた
導入
以下の書籍を読みながら、RSpecについて学んでいます。
写経しながら、RSpecでテストコードを書いて、いざはじめてのRSpecと思って、
ワクワクしながらコマンドを叩くと、以下のエラーが出た。Selenium::WebDriver::Error::UnknownCommandError: unknown command: Cannot call non W3C standard command while in W3C modeGoogle Chromeとchromedriverのバージョンを最新の75にアップデートしたことで、W3CモードがデフォルトONになったみたい。
解決策
オプションに「w3c:false」を渡してやると、うまく解決が出来た。
自分の場合はオプションの設定方法が分からなくてハマったけど、何とかテストコードが動いて、
All Greenになった!RSpec.configure do |config| config.before(:each, type: :system) do # 修正前 driven_by :selenium_chrome_headless # 修正後 caps = Selenium::WebDriver::Remote::Capabilities.chrome("chromeOptions" => {"w3c" => false}) driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400], options: { desired_capabilities: caps } end参考にしたURL
- 投稿日:2019-06-27T18:42:05+09:00
Elasticsearch aliasを用いた運用
背景
社内では既にElasticsearchを取り入れていましたが、運用においていくつか不便な点がありました。
既存index運用方法
- ある用途に特化したindexを作成
- 初期データをインポート
- 追加発生 => インポート
- 更新発生 => インポート
- 削除発生 => 削除タスクの生成と実行
問題点
更新する場合は問題がありませんが、インポート対象が削除されていた場合は別途削除する為に削除リクエストをElasticsearchに送信する必要があります。
そのために発生したのが下記のようなもの。
- 削除条件が増えるたびに専用の削除用rakeタスクができた。
- インポートしてから削除するといった運用手順が生まれる。
- 削除用rakeタスクのメンテ忘れによる、必要データが消えてしまう事件発生。
- 不要なデータが残り続ける。
またindexの定義を更新または刷新したい時にAPI側はindex名を直指定しているため、容易に切り替えたり更新することができません。
新規index運用方法
新規運用方法といっても大したものではないですが、既存の問題を解決する為に alias を参照する用に変更して、裏に紐づくindexを切り替えるという方法を導入しました。
これにより、データを刷新したものに切り替えができるため削除用のタスクが不要になり、indexの定義更新等も容易になります。
例
下記を一つのタスクとすることで、シンプルな運用とする。
- index作成
- データのインポート
- alias切替
- 旧indexの削除
※更新の場合はインポートのみを実行
elastic/elasticsearch-rubyを利用してます。
※例なので、省略などを行なっておりそのまま実行はできません。def create # indexの定義を読み込む json = open(file_path) { |io| JSON.load(io) } # suffixにタイムスタンプを設定 timestamp = Time.now.strftime('%Y%m%d%H%M%S') index_name = "#{alias_name}_#{timestamp}" # indexを作成 client.indices.create index: index_name, body: json # 必要なデータのインポート client.bulk body: data # alias切り替え switch_alias(index: index_name) end def switch_alias(index:) # aliasに紐づいた古いindexを取得 old_indices = client.indices.get_alias(name: alias_name).keys # alias削除リクエスト作成 for_remove = old_indices.map { |old_index| { remove: { index: old_index, alias: alias_name } } } # alias追加リクエストを追加 actions = for_remove + [{ add: { index: index, alias: alias_name } }] # リクエスト実行 client.indices.update_aliases body: { actions: actions } # aliasに紐づいた古いindexを削除 client.indices.delete index: old_indices end
- 投稿日:2019-06-27T18:30:48+09:00
jQueryとsave時のエラー
はじめに
- 条件を満たしているはずなのに新規登録時に登録に失敗しましたと出る
- 登録失敗時のメッセージ「登録に失敗しました」が消えずにずっと表示されたままになっている。
この二つの問題解決にあたるが,いざ取り組むとあっさり解決した。途中まで書いてしまっていたので,一応投稿しておく。
内容はかなりしょぼいです。
JavaScript
「登録に失敗しました」が5秒で消えない。
とりあえず各ファイルの記述を見ていく。users_controller.rbclass UsersController < ApplicationController def new @user = User.new end def create @user = User.new(user_params) if @user.save redirect_to root_path, success: '登録が完了しました' else flash.now[:danger] = "登録に失敗しました" render :new end end private def user_params params.require(:user).permit(:name, :email, :password, :password_confirmation) end endapplication.html.erb<!DOCTYPE html> <html> <head> <title>Pictgram</title> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> </head> <body> <% flash.each do |key, value| %> <div class="alert alert-<%= key %>" role="alert"><%= value %></div> <% end %> ~ ~ <%= yield %> <script> $(function(){ $(".alert").fadeOut(5000); }); </script> </body> </html>group :development do # Access an interactive console on exception pages or by calling 'console' anywhere in the code. gem 'web-console', '>= 3.3.0' gem 'listen', '>= 3.0.5', '< 3.2' # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring' gem 'spring-watcher-listen', '~> 2.0.0' gem 'pry-rails' gem 'pry-doc' gem 'pry-byebug' gem 'pry-stack_explorer' gem 'jquery-rails' gem 'bcrypt' gem 'carrierwave' endapplication.js// This is a manifest file that'll be compiled into application.js, which will include all the files // listed below. // // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's // vendor/assets/javascripts directory can be referenced here using a relative path. // // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the // compiled file. JavaScript code in this file should be added after the last require_* statement. // // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details // about supported directives. // //= require rails-ujs //= require activestorage //= require turbolinks //= require bootstrap-sprockets //= require jquery //= require_tree .各記述に加え,もちろんbundle installも行っている。
そこで少し調べたところ,Gemfileの記述に
gem 'jquery-ui-rails'application.jsに
application.js//= require jquery_ujs
を加えることで解決することがあるらしいので試してみるとあっさり解決。
Rubyのバージョンが古かったのかな?また今度調べてみる。ROLLBACK
一番の問題はここ。
条件を満たしているはずの新規登録時にターミナルに以下のメッセージが出る。Started POST "/users" for ::1 at 2019-06-26 17:51:29 +0900 Processing by UsersController#create as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"VKkdJMhrlEzltFp cD4OJLPN/LhgoyBHmIRj7R6Y+1Z/vn2QXJ60dLptQuvzdmYi/FBCRlSHsBljz/BpW9 PaQQg==", "user"=>{"name"=>"***", "email"=>"***", "passeword"=>"***", "password_confimation"=>"[FILTERED]"}, "commit"=>"登録"} Unpermitted parameters: :passeword, :password_confimation (0.5ms) BEGIN ↳ app/controllers/users_controller.rb:8 (0.3ms) ROLLBACK ↳ app/controllers/users_controller.rb:8 Rendering users/new.html.erb within layouts/application Rendered users/new.html.erb within layouts/application (2.1ms) User Load (0.2ms) SELECT `users`.* FROM `users` WHERE `users`. `id` IS NULL LIMIT 1 ↳ app/controllers/concerns/common_actions.rb:5 Completed 200 OK in 69ms (Views: 58.3ms | ActiveRecord: 1.1ms) Started GET "/users/new" for ::1 at 2019-06-26 17:54:52 +0900 Processing by UsersController#new as HTML Rendering users/new.html.erb within layouts/application Rendered users/new.html.erb within layouts/application (2.3ms) User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`. `id` IS NULL LIMIT 1 ↳ app/controllers/concerns/common_actions.rb:5 Completed 200 OK in 83ms (Views: 80.0ms | ActiveRecord: 0.4ms)ここで以下の記述に注目した
(0.3ms) ROLLBACK ↳ app/controllers/users_controller.rb:8 Rendering users/new.html.erb within layouts/application Rendered users/new.html.erb within layouts/application (2.1ms)
何かがロールバックしている。追っていくとまずはusers_controller.rbの8行目に何かあるよと。
users_controller.rb# 8行目↓ if @user.saveじゃあこの処理はどこへ行ってるのかというとnew.html.erbである。このファイル内の記述を見ていく。
new.html.erb<div class="users-new-wrapper"> <div class="container"> <div class="row"> <div class="col-md-offset-4 col-md-4 users-new-container"> <h1 class="text-center text-white">Sign up</h1> <%= form_for @user do |f| %> <div class="form-group"> <%= f.label :name, class: 'text-white' %> <%= f.text_field :name, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :email, class: 'text-white' %> <%= f.text_field :email, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :password, class: 'text-white' %> <%= f.passeword_field :password, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :password_confirmation, class: 'text-white' %> <%= f.password_field :password_confimation, class: 'form-control' %> </div> <%= f.submit "登録", class: 'btn-block btn-white' %> <% end %> <%= link_to 'ログインはこちら', login_path, class: 'text-white' %> </div> </div> </div> </div>パッと見では問題がなさそう。
そこで調べてみるとusers_controller.rbif @user.save ↓ if @user.save!へ変更してみるとエラー個所がわかるよという記事を見つけた。
早速変更してrailsサーバーを再起動,新規登録を行ってみるとターミナルに新しい動きがあった。ActiveRecord::RecordInvalid (Validation failed: Password can't be blank, Password is too short (minimum is 8 characters), Password i s invalid): app/controllers/users_controller.rb:8:in `create'
なにやらパスワードがおかしいのかなと思う。下記の通りにValidationを設定しており,条件は満たしているはずなのに「空白はダメだよ(8文字以下になっているよ)」と怒られている。
user.rbclass User < ApplicationRecord validates :name, presence: true, length: { maximum: 15 } VALID_EMAIL_REGEX = /\A([\w+\-].?)+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/ validates :email, presence: true, format: { with: VALID_EMAIL_REGEX } VALID_PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?\d)[a-z\d]{8,32}+\z/i validates :password, presence: true, length: { minimum: 8, maximum: 32 }, format: { with: VALID_PASSWORD_REGEX } has_secure_password has_many :topics endここでは特になにもなさそうなのでpasswordの定義を行っているnew.html.erbへ戻って問題を確認する。
new.html.erb<%= form_for @user do |f| %> ~ ~ <div class="form-group"> <%= f.label :password, class: 'text-white' %> <%= f.password_field :passeword, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :password_confirmation, class: 'text-white' %> <%= f.password_field :password_confimation, class: 'form-control' %> </div>ここで間違いに気がつく。一箇所passwordがpassewordになっている。これを修正するとあっさり登録完了。問題は解決した。
おわりに
次回こそは
- 投稿する画像の制限
- 非ログイン時の投稿ボタンの非表示
を実装していく。
- 投稿日:2019-06-27T17:49:02+09:00
[Ruby on Rails]メールの送信結果をブラウザ上で確認する
メール送信結果をブラウザ上で確認する方法です。
letter_openerというgemを使います。
本番環境では実際に送信することになるので、開発環境でのみ確認できる状態を作ります。環境
- MacOS 10.14.5(18F132)
- ruby 2.3.7p456
- rails 5.2.3
手順
1.
letter_opener
をインストール今回は開発環境でのみ確認できる状態を作るため、groupの設定をします。
すでにdevelopmentの設定が出来ている場合は、gem名のみを追記してください。Gemfilegroup :development do gem 'letter_opener_web' end追記したらコマンド
$ bundle install2. 各種設定ファイルの編集
- config/routes.rb
config/routes.rb# 追記 if Rails.env.development? mount LetterOpenerWeb::Engine, at: "/letter_opener" end
- config/envirronments/development.rb
config/envirronments/development.rb#追記 ActionMailer::Base.delivery_method = :letter_opener_web #他にaction_mailerの設定をしている場合はコメントアウトする #config.action_mailer.delivery_method = :smtp #config.action_mailer.smtp_settings = { #省略 #}3. ブラウザから確認する
$ rails s
でサーバーを起動して、ブラウザでlocalhost:3000/letter_openerにアクセスします。
このような画面が表示されればOKです。
4. メールを送信する
メールを送信する処理を実行します。
5. 再度ブラウザから確認する
ブラウザでlocalhost:3000/letter_openerにアクセスして、メールを確認します。
※deviseのパスワード再設定メールを送ってみました。
- 投稿日:2019-06-27T17:08:39+09:00
投稿詳細ページを作成時のエラー
作ろうとしている物
・ブログ投稿サイト
困っていること
・ユーザーの投稿一覧画面から、タイトルをクリックすることで投稿詳細ページに飛ばしたい。が、できない。
※投稿内容の文字についてはふざけていてごめんなさい。挙動を確認したかっただけなので、テキトーに書いておりました。この画面で記事一覧のタイトルをクリックすると↓
Unable to autoload constant Blog_content, expected /Users/horiguchihiroki/blog/app/models/blog_content.rb to define it
Extracted source (around line #13):
11 def show
12 @blog = Blog_content.find_by(id: params[:id])
13 end
14
15
16
Rails.root: /Users/horiguchihiroki/blogApplication Trace | Framework Trace | Full Trace
app/controllers/blogs_controller.rb:13:in `show'発生しているエラー(上記が見にくいのでマークダウンにしました)
Unable to autoload constant Blog_content, expected /Users/horiguchihiroki/blog/app/models/blog_content.rb to define it @blog = Blog_contents.find_by(id: params[:id]) →コントローラのこの部分に赤線コードレビュー
1、モデル名の確認
blog_content(app/model/以下に記載されているファイル名)2、投稿一覧画面(app/blogs/index.html.erb)
飛ばす際に、paramsにクリックした投稿のid番号をパラメータとして付加し、コントローラのshowアクションに飛ばす。
※前提
投稿一覧画面では、インスタンス変数に@blogを用いています。<div class="contents"> <% @blog.each do |content|%> <p> <%= link_to(content.title_name, "/blogs/#{content.id}") %> </p> <% end %> </div>3、ルーティング(config/routes.rb)
Rails.application.routes.draw do root to: 'pages#index' resources :blogs devise_for :users, controllers:{ regisrations: 'users/regisrations', sessions: 'users/sessions' } # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end補足でrake routesの結果↓
Prefix Verb URI Pattern Controller#Action root GET / pages#index blogs GET /blogs(.:format) blogs#index POST /blogs(.:format) blogs#create new_blog GET /blogs/new(.:format) blogs#new edit_blog GET /blogs/:id/edit(.:format) blogs#edit blog GET /blogs/:id(.:format) blogs#show PATCH /blogs/:id(.:format) blogs#update PUT /blogs/:id(.:format) blogs#update DELETE /blogs/:id(.:format) blogs#destroy new_user_session GET /users/sign_in(.:format) users/sessions#new user_session POST /users/sign_in(.:format) users/sessions#create destroy_user_session DELETE /users/sign_out(.:format) users/sessions#destroy new_user_password GET /users/password/new(.:format) devise/passwords#new edit_user_password GET /users/password/edit(.:format) devise/passwords#edit user_password PATCH /users/password(.:format) devise/passwords#update PUT /users/password(.:format) devise/passwords#update POST /users/password(.:format) devise/passwords#create cancel_user_registration GET /users/cancel(.:format) devise/registrations#cancel new_user_registration GET /users/sign_up(.:format) devise/registrations#new edit_user_registration GET /users/edit(.:format) devise/registrations#edit user_registration PATCH /users(.:format) devise/registrations#update PUT /users(.:format) devise/registrations#update DELETE /users(.:format) devise/registrations#destroy POST /users(.:format) devise/registrations#create rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create3、コントローラのshowアクション
paramsで付加したid番号から投稿を探し出して、インスタンス変数に代入する。(app/controller/blogs_controller)class BlogsController < ApplicationController before_action :authenticate_user! #showを省いた before_action :set_blog, only: [:edit, :update, :destroy] def index @blog = current_user.blog_contents end ーーーーーーーーーーーーーーーーーーーーーーーー def show @blog = Blog_contents.find_by(id: params[:id]) →エラーはここのコードを指定して、赤線を出しています。 end ーーーーーーーーーーーーーーーーーーーーーーーー def new @blog = current_user.blog_contents.build end def edit end def create @blog = current_user.blog_contents.build(blog_params) respond_to do |format| if @blog.save format.html { redirect_to blogs_path, notice: 'Note was successfully created.' } format.json { render :show, status: :created, location: @blog } else format.html { render :new } format.json { render json: @blog.errors, status: :unprocessable_entity } end end end def update respond_to do |format| if @blog.update(note_params) format.html { redirect_to @blog, notice: 'Note was successfully updated.' } format.json { render :show, status: :ok, location: @blog } else format.html { render :edit } format.json { render json: @blog.errors, status: :unprocessable_entity } end end end def destroy @blog.destroy respond_to do |format| format.html { redirect_to notes_url, notice: 'Note was successfully destroyed.' } format.json { head :no_content } end end private def set_blog @blog = current_user.blog_contents.find_by(id: params[:id]) redirect_to root_path if @blog.nill? end def blog_params params.require(:blog_content).permit(:title_name, :content) end end5、投稿詳細画面
インスタンス変数に代入された投稿のタイトルと、中身を表示する。(app/views/show.html.erb)<div class="content"> <%= @blog.title_name %> <%= @blog.content %> </div>試したこと
発生しているエラー文をググって確認。
okerra.hatenablog.com/entry/2018/01/03/082902
https://ja.stackoverflow.com/questions/46942/ruby-on-rails-model-%E3%82%A8%E3%83%A9%E3%83%BC
https://teratail.com/questions/87201
https://teratail.com/questions/139281
https://teratail.com/questions/13505
https://ja.stackoverflow.com/questions/46942/ruby-on-rails-model-エラー
https://stackoverflow.com/questions/53486622/loaderror-unable-to-autoload-constant-board-game-expected-models-board-game-r上記のサイトに書いてあることを以下のようにまとめてエラー処理を開始。
1、「モデルのファイル名とクラス名がサイトで指摘されているように間違っている」
→間違っていないことを確認。2、「クラス名が呼ばれるはずの部分で、ファイル名の_(アンダーバー)が存在していることが原因?と疑って他のファイルを確認してみる」
→全てのファイルで検索をかけてみました。検索した文字(blog_content)。どこかで_(アンダーバー)を入れてたりしないのか?と探してみる。上記のサイトに書いてあること以外で試してみたこと。
1、「そもそもモデルのファイルは読み込まれているのか?」
→一旦、モデルのファイルを消去してみる。そうするとモデルがないよ!という意味で「uninitialized constant BlogsController::Blog」が表示されるので、モデルを読み込もうとしている?ことは確認できたとする。
♦︎それなのにエラーが出るってことはやっぱり名前の部分がおかしいのか・・・?
♦︎でも名前部分に関するエラーは直したんだが・・・というループに陥る。2、「同じコントローラ内で、別のアクションは機能しているか、問題ないのか?」
→ブログ新規投稿機能、一覧表示機能については問題なく動作する。ますますよくわからん。な状態です。3、「そもそもautoloadとはなんぞや???と思い調べてみる」
→https://railsguides.jp/autoloading_and_reloading_constants.html
よくわからないまま今に至ります。適当に名前をいじってみたら詳細ページにはアクセスできた件
クラス名を、以下のように変更しました。
class Blog_content < ApplicationRecord belongs_to :user endそうすると詳細機能にはアクセスできるようになりました。
しかし、投稿一覧機能にはアクセスできなくなりました。しかも、調べたサイト的には正しくない?書き方になっています。モデルのクラス名がRailsでどうやって呼ばれているのかを確認してみます。
ちょっと待てよ?と気づいた点
ご教授いただいてから気づいた、ちょっと待てよ?なところ。
クラス名を変更した(=コントローラで設定したBlogContentをBlog_contentsに変更した)らうまく行ったってことは、クラス名をコントローラで直接指定できるということじゃないか・・・?
至らな過ぎた勉強量
find_byメソッドに関して、使用するのはモデル名だけだと思い込んでおりました。
違いますね。モデルのクラス名またはインスタンスメソッドを指定してモデルのクラスを指定しているんですね。
まだまだ勉強足りないというか今までの開発って奇跡的にエラーが出てこないだけだったんだ。このエラーには出会ってよかった。
- 投稿日:2019-06-27T16:41:43+09:00
演算子の優先順位と意味
はじめに
用語と定義をしっかり覚える事が好きな自分用です。
演算子の優先順位とその意味を見やすくまとめようと思い投稿します。優先順位
[高] +(単項) ! ~ ! 否定演算子, ~a ビット演算子、aが1ならば0、またその逆 ** ** 代数演算子, a**b aのb乗 -(単項) * / % % 代数演算子, a%b aをbで割った時の余り + - > >= < <= <=> == === != =~ !~ <=> 宇宙船演算子, =~ 正規表現演算子 && && 論理演算子, a && b aかつbが真ならば真 || || 論理演算子, a || b aまたはbが真ならば真 .. ... .. 範囲演算子 ?:(条件演算子) ?= 条件演算子, a ? b : c aが真ならばbその他はc = not [低] and or
- 投稿日:2019-06-27T15:56:25+09:00
配列によるメソッドの可変長引数!
可変長引数とは???
個数に制限のない引数のこと!
自分で、定義するメソッドで可変長引数を使いたい場合は、引数名の手順に*をつけます!sample.rbdef メソッド名(引数1,引数2,*可変長引数) #メソッドの処理 end可変長引数は配列として受け取ることができます!
具体例を見ていきましょう!
次のコードは、引数として渡されたメニューの名前を、注文していくメソッドです。sample.rbdef order(*food) puts "#{food.join('と')},お願いします!" end order('ハンバーガー') #ハンバーガー,お願いします! order('ハンバーガー','ポテト') #ハンバーガーとポテト,お願いします! order('ハンバーガー','ポテト','コーラ') #ハンバーガーとポテトとコーラ,お願いします!
- 投稿日:2019-06-27T15:49:18+09:00
TECH ~Day5~
学習五日目を終えて、新たにまた新しい用語を学び始めました。
一旦レビュー管理アプリケーションを作る作業を終え、今回は「クラス」と「インスタンス」を学び始めました。
これを学びながら再びレビュー管理アプリケーションを作っていく作業を始めていきます。学習内容
・クラスとインスタンス
・クラスの定義
・newメソッド
・クラスメソッド、インスタンスメソッド
・インスタンスの定義
・クラス変数、インスタンス変数・クラスとインスタンス
クラス
とある種類のオブジェクトの共通の属性とメソッドをまとめて定義しておく型のようなもの。
(例)「人クラス」の場合
性:名前、年齢、性別など
メソッド:歩く、寝る、食べるなどこのような型を予め、定義しておくことによってこの方に沿った個別のオブジェクトを作ることができる。
インスタンス
クラスに基づいて生まれたオブジェクトをインスタンスと呼ぶ。
クラスが先。クラスからインスタンスが生まれるイメージ。
Rubyには予め定義されているクラスが存在する。
(例)
配列オブジェクト:Arrayクラス
数位オブジェクト:Integerクラス
ハッシュオブジェクト:Hashクラス など・クラスの定義
書き方
class クラス名
!変数 / メソッドの定義
end(例)
class Reviewend
↑これはReviewクラスの定義
もっともシンプルなクラスの定義の形・newメソッド
定義したクラスからインスタンスを生成するには、「newメソッド」を使うとできる。
(例)
class Review
endreview = Review.new
この「あるクラス.new」というインスタンス生成のコードは、「クラスがメソッドを利用している」形である。・クラスメソッド、インスタンスメソッド
クラスメソッド
クラスが使用できる。クラスで共通の情報を使った処理に使用する。
クラスの定義内で行う。インスタンスメソッド
インスタンスが使用できる。インスタンスごとの個別の情報(属性値)を使った処理に使用。・インスタンスの定義
インスタンスメソッドの定義はインスタンスのクラスの「定義内」で行う。
書き方
class クラス名
def メソッド名
! 処理
end
end・クラス変数、インスタンス変数
クラス変数
クラス全体で使える変数。
クラス内ではどこでも使えるため、クラスメソッド、インスタンスメソッドの両方で使える。
クラスを通して、値が共通の情報に使用する。インスタンス変数
共通の属性としてインスタンスに定義できる変数。
その値は個々のインスタンスによって別別に設定できる。とりあえず五日目の学習内容は以上になります。
前回まで行っていた作業よりは理解しやすいないように感じました。引き続き学習に取り組みたいと思います。
- 投稿日:2019-06-27T15:16:50+09:00
【Ruby】配列の要素を追加・削除
【1章】はじめに
今回はRubyの配列におけるメソッドの中でも、要素を追加・削除するメソッドについてまとめました!
どれもよく使うメソッドなのでぜひ参考にしてください
では早速はじめます!【2章】配列に要素を追加する
末尾に追加(<<)
array.rbnum = [1, 2, 3, 4] num << 5 puts num # 出力結果 ==> [1, 2, 3, 4, 5]末尾に追加(push)
array.rbnum = [1, 2, 3, 4] num.push(5) puts num # 出力結果 ==> [1, 2, 3, 4, 5] num.push(6, 7) puts num # 出力結果 ==> [1, 2, 3, 4, 5, 6, 7]こちらは上記のように複数一気に追加できます!
要素と要素の間に追加(insert)
array.rbnum = [1, 2, 3, 4] num.insert(2,100) #insert(何番目の要素に入れるか指定, 入れる要素) #要素の番号は0から始まるので、今回は2を指定しているということは実際は3番目に100を追加するという意味 puts num # 出力結果 ==> [1, 2, 100, 3, 4]【3章】配列の要素を削除する
要素の番号を指定して削除(delete_at)
array.rbnum = [1, 2, 100, 3, 4] num.delete_at(2) #要素の番号は0から始まるので、今回は2を指定しているということは実際は3番目を削除するという意味 puts num # 出力結果 ==> [1, 2, 3, 4]要素の範囲を指定して削除(slice!)
array.rbnum = [1, 2, 3, 4, 5] num.slice!(1, 3) #slice!(最初に削除する要素の番号, ←で指定した番号から順番にいくつの要素を削除するか指定) #今回は2番目の要素から3つ分削除 puts num # 出力結果 ==> [1, 5]要素名を指定して、該当する要素を全て削除(delete)
array.rbanimals = ["cat", "dog", "rabbit", "dog"] animals.delete("dog") puts animals # 出力結果 ==> ["cat", "rabbit"]配列の先頭から指定した数だけ削除(shift)
array.rbnum = [1, 2, 3, 4, 5] num.shift(2) #先頭から2つ分の要素を削除 puts num # 出力結果 ==> [3, 4, 5]【4章】さいごに
どうでしょうか?
配列の要素の追加や削除は自由自在に操れるようになっておいて損はありません!
ぜひ活用してみてください!
最後までご覧いただきありがとうございました
- 投稿日:2019-06-27T13:20:22+09:00
出力メソッドの使い分け
はじめに
初投稿です。備忘録としてまとめています。
print puts p ppメソッドの特性と出力結果を大まかに書いてみた。
参考:たのしいRubyarray = ["秋山","山本","馬場" ] print "ロバート" print 3 print array結果
ロバート3["秋山", "山本", "馬場"]printメソッドは改行、スペース無し。配列は展開されずに出力。
puts
array = ["秋山","山本","馬場" ] puts "ロバート" puts 3 puts array結果
ロバート 3 秋山 山本 馬場printメソッドと違い、一行ごとに改行が入り見やすくなる。
p
array = ["秋山","山本","馬場" ] p "ロバート" p 3 p array結果
"ロバート" 3 ["秋山", "山本", "馬場"]文字列は""(ダブルクオーテーション)込みで出力される。文字列、数値、配列の型を分けて出力してくれる。
pとppの比較
require "pp" array = [ {title:"羅生門", auther:"芥川龍之介"}, {title:"銀河鉄道の夜", auther:"宮沢賢治"}, {title:"こころ", auther:"夏目漱石"} ] p array pp array結果
[{:title=>"羅生門", :auther=>"芥川龍之介"}, {:title=>"銀河鉄道の夜", :auther=>"宮沢賢治"}, {:title=>"こころ", :auther=>"夏目漱石"}] [{:title=>"羅生門", :auther=>"芥川龍之介"}, {:title=>"銀河鉄道の夜", :auther=>"宮沢賢治"}, {:title=>"こころ", :auther=>"夏目漱石"}]ppメソッドはハッシュをキー毎に改行して見やすくしてくれる。
デバック時に便利
- 投稿日:2019-06-27T13:14:17+09:00
【Rails 備忘録】has_many先が存在しない場合、除外して格納する書き方。
はじめに
忘れてしまいがちなので備忘録。
条件
Project
モデルに、has_manyでParticipation
モデルが関連付けされている。createされた
project
にuserが参加アクションをすると、participation
が該当のprojectにぶら下がる形でcreateされる。なので、Projectのインスタンスであるprojectがparticipationを持っていない(
@project.participations => []
)ということがありえる。project.rbclass Project < ApplicationRecord has_many :participations ・ ・ ・viewでparticipationを持っているprojectだけを表示したい。
やり方として一番簡単そうなのは、例えばcontrollerで
@projects = Project.where(user: current_user)
みたいな感じにして、viewテンプレートでshow.html.erb<%= @projects.each do |project| %> <% if project.participations.present? %> <% project.name %> <% end %> <% end %>↑と書いてしまえばよいが、if文で毎回チェックさせるのもイマイチなので、
controllerで@project
を格納するタイミングでparticipation
がないものを除外させたい。participationを持っているprojectだけを格納したい
答えを先に書くと、
includes
とwhere
を駆使して、
.includes(:participations).where(participations: Participation.all)
みたいな書き方をすればよい。↓例:
participations_controller.rbclass ParticipationsController < ApplicationController def show @participation = Participation.find(params[:id]) @projects = Project.where(client: current_client).where.not(status: "finished"). includes(:participations).where(participations: @participations) end
- 投稿日:2019-06-27T13:14:17+09:00
【Rails 備忘録】has_many先が存在しない場合に除外してインスタンス変数に格納する書き方。
はじめに
忘れてしまいがちなので備忘録。
条件
Project
モデルに、has_manyでParticipation
モデルが関連付けされている。createされた
project
にuserが参加アクションをすると、participation
が該当のprojectにぶら下がる形でcreateされる。なので、Projectのインスタンスであるprojectがparticipationを持っていない(
@project.participations => []
)ということがありえる。project.rbclass Project < ApplicationRecord has_many :participations ・ ・ ・viewでparticipationを持っているprojectだけを表示したい。
やり方として一番簡単そうなのは、例えばcontrollerで
@projects = Project.where(user: current_user)
みたいな感じにして、viewテンプレートでshow.html.erb<%= @projects.each do |project| %> <% if project.participations.present? %> <% project.name %> <% end %> <% end %>↑と書いてしまえばよいが、if文で毎回チェックさせるのもイマイチなので、
controllerで@project
を格納するタイミングでparticipation
がないものを除外させたい。participationを持っているprojectだけを格納したい
答えを先に書くと、
includes
とwhere
を駆使して、
.includes(:participations).where(participations: Participation.all)
みたいな書き方をすればよい。↓例:
participations_controller.rbclass ParticipationsController < ApplicationController def show @participation = Participation.find(params[:id]) @participations = Participation.search_by_client(current_client) @projects = Project.where(client: current_client).where.not(status: "finished"). includes(:participations).where(participations: @participations) endshow.html.erb<%= @projects.each do |project| %> <% project.name %> <% end %>
- 投稿日:2019-06-27T12:35:37+09:00
Rails6 のちょい足しな新機能を試す42(multi-db db:migrate:status編)
はじめに
Rails 6 に追加されそうな新機能を試す第42段。 今回は、
multi-db db:migrate:status
編です。
Rails 6 では、 複数データベースに対応しているため、db:migrate:status
も複数データベースに対応しています。Ruby 2.6.3, Rails 6.0.0.rc1 で確認しました。Rails 6.0.0.rc1 は
gem install rails --prerelease
でインストールできます。$ rails --version Rails 6.0.0.rc1今回の準備
今回は、 Rails6 のちょい足しな新機能を試す35(複数データベース migration --database オプション編) をベースとして進めます。第35段まで終わっている状態を前提として話しを進めます。
一旦DBを作り直す
必要ないかも知れませんが、念のため、綺麗な状態から作業したいため、DBを作り直すことにしました。
bin/rails db:drop db:create db:migrateカラムを追加するマイグレーションを作成する
これもまあ、必要ないと言えば必要ない気がするのですが、
backbone
のusers
テーブル に
library
のbooks
テーブルにisbn
カラムを追加するマイグレーションを作成します。
library
側に追加するためには、--database (--db)
オプションが必要です。$ bin/rails g migration AddEmailToUser email $ bin/rails g migration AddIsbnToBook isbn --database=library
db:migrate:status
で確認する
db:migrate:status
で確認すると、両方のデータベースのマイグレーションの状況が確認できます。$ bin/rails db:migrate:status database: backbone_development Status Migration ID Migration Name -------------------------------------------------- up 20190608225735 Create user down 20190622004922 Add email to user database: library_development Status Migration ID Migration Name -------------------------------------------------- up 20190608225808 Create book down 20190622005021 Add isbn to bookどちらか1つのデータベースのマイグレーションの状態を知りたい場合は、
status
に続けて:backbone
か:library
を指定します。$ bin/rails db:migrate:status:backbone database: backbone_development Status Migration ID Migration Name -------------------------------------------------- up 20190608225735 Create user down 20190622004922 Add email to user$ bin/rails db:migrate:status:library database: library_development Status Migration ID Migration Name -------------------------------------------------- up 20190608225808 Create book down 20190622005021 Add isbn to book試したソース
試したソースは以下にあります。
https://github.com/suketa/rails6_0_0rc1/tree/try042_multidb_migrate_status注意
上のブランチで試す場合は、以下のように実行すれば、
bin/rails db:migrate:status
の結果が同じになります。$ bin/rails db:drop db:create $ bin/rails db:migrate:backbone VERSION=20190608225735 $ bin/rails db:migrate:library VERSION=20190608225808参考情報
- 投稿日:2019-06-27T12:14:54+09:00
SVG画像を3x3で並べる
原子構造をベクター画像で描くツールを作りました。
https://github.com/NuNoKehai/xyzb2image
が、このプログラムだと1ユニットセル分しか描かないので、そのままでは見た目がイマイチ。
もともとイラストレータで3x3に並べていましたが、イラストレーターって画像の位置合わせがイマイチ苦手ですね。だからと言って描画ユニットセルを増やすオプションはつけたくない。
と言うことで、ベクトル画像を3x3するrubyスクリプトを書きました。svg3x3.rbrequire 'rubygems' require 'cairo' require 'rsvg2' def read_suffix(filename) index_period=filename.rindex(".") suffix=filename[index_period+1..filename.size-1] return suffix end def cairo_imagesurface_general(filename_image, width, height) suffix_image=read_suffix(filename_image) if suffix_image.casecmp("pdf")==0 surface = Cairo::PDFSurface.new(filename_image, width, height) elsif suffix_image.casecmp("svg")==0 surface = Cairo::SVGSurface.new(filename_image, width, height) else format = Cairo::FORMAT_ARGB32 surface = Cairo::ImageSurface.new(format, width, height) end return surface end def context_finish_general(filename_image,context) suffix_image=read_suffix(filename_image) if suffix_image.casecmp("pdf")==0 || suffix_image.casecmp("svg")==0 context.target.finish elsif suffix_image.casecmp("png")==0 context.target.write_to_png(filename_image) end end filename_in=ARGV[0] filename_out=ARGV[1] handle_in = RSVG::Handle.new_from_file(filename_in) width_in=handle_in.dimensions.width height_in=handle_in.dimensions.height surface_out = cairo_imagesurface_general(filename_out, width_in*3, height_in*3) context_out = Cairo::Context.new(surface_out) context_out.render_rsvg_handle(handle_in) context_out.translate(width_in,0) context_out.render_rsvg_handle(handle_in) context_out.translate(width_in,0) context_out.render_rsvg_handle(handle_in) context_out.translate(-2*width_in,height_in) context_out.render_rsvg_handle(handle_in) context_out.translate(width_in,0) context_out.render_rsvg_handle(handle_in) context_out.translate(width_in,0) context_out.render_rsvg_handle(handle_in) context_out.translate(-2*width_in,height_in) context_out.render_rsvg_handle(handle_in) context_out.translate(width_in,0) context_out.render_rsvg_handle(handle_in) context_out.translate(width_in,0) context_out.render_rsvg_handle(handle_in) context_finish_general(filename_out,context_out)このスクリプトを動作させるためには rubygem の cairo と rsvg2 が必要です。
最初に定義した3つの関数は出力ファイルの拡張子の汎用性を増やすためです。
やってることはユーザー空間の原点をずらして入力画像を描画してるだけ。使い方です。
ruby svg3x3.rb input.svg output.pdf
のように、入力ファイル名と出力ファイル名をargumentで指定。
入力ファイルはsvg形式である必要があります。
- 投稿日:2019-06-27T12:03:20+09:00
YAMLとSlimで論文リストを作る
概要
以下のようなYAMLファイル(
publication.yaml
)を食わせて、- title: How to write web sites with slim author: R. Robota, T. Tanaka journal: Ruby Journal year: 2010 volume: 10 page: 1-7 arxiv: 1234.56789 - title: Using YAML file with slim author: R. Robota journal: Journal of Slim year: 2018 volume: 8 page: 1275 - title: This is a sample file for Slim and YAML author: S. Yaml, T. XML and R. Robota journal: J. Yaml. Phys. year: 2017 volume: 3 page: 12-18 arxiv: 1908.01234以下のようなHTMLを吐くSlimテンプレートのサンプルです。
ソースは以下においておきます。
何をしたいか
論文リストとかそうですが、とりあえずなんかリストがあり、それを静的なHTMLにしたい、ということはよくあります。とりあえず論文リストはJSONかYAMLで用意されてるとしましょう。これをHTMLにしたい。さほど更新頻度も高くないので、CMSとかデータベースを導入するほどでもない、でもHTMLを直書きするのは嫌だ、という気持ち、わかっていただけますよね?
で、どうしましょう。
そういう用途では、とりあえずPHPでゴリゴリ書いちゃう、という方法が考えられます。PHPはそれが本職の言語なので、わりとまっとうな気がします。
もしくはJSON+JavaScriptというのもありな気がしますね。JSのテンプレートエンジンは豊富にあるので、好きなのを使ってなんとかする、というのが最近では良い気がします。
ですが、個人的に母国語がRubyということもあって、Rubyスクリプトの埋め込みができる軽量テンプレートエンジン、Slimを使うことにします。
Slimとは
Slimは軽量テンプレート言語です。おそらくRuby on Railsと一緒に使われることが多いと思いますが、ローカルでHTMLを吐くのにも使えます。日本語の公式ドキュメントがわかりやすいので、使うのは簡単だと思います。
Rubyとgemが入っていればSlimのインストールは簡単です。
$ sudo gem install slimこのあと、
$ slimrb -vとかやってバージョン情報が出てきたらインストールされています。
例えば、
test.slimdoctype html html head title My first Slim body h1 This is test h2 Hello Slimこんなファイルを作って、
$ slimrb test.slim
とslimに食わせると、
<!DOCTYPE html><html><head><title>My first Slim</title></head><body><h1>This is test</h1><h2>Hello Slim</h2></body></html>と、HTMLにしてくれます。
-p
オプションをつけると人間が読みやすい形にしてくれます。$ slimrb -p test.slim<!DOCTYPE html> <html> <head> <title>My first Slim</title> </head> <body> <h1> This is test </h1> <h2> Hello Slim </h2> </body> </html>さて、Slimの特徴は、Rubyスクリプトをそのまま埋め込めることです。ハイフン
-
のあとにRubyスクリプトが書けます。また、=
を使って、HTML要素にRubyの実行結果を代入できます。たとえば変数a
を定義して、h1
に突っ込むにはこうします。test2.slimdoctype html html head title My first Slim body - a = "Hello" h1 = atest2.html<!DOCTYPE html> <html> <head> <title>My first Slim</title> </head> <body> <h1> Hello </h1> </body> </html>上記のように
a
に代入された値を、h1
要素に突っ込むことができました。また、配列などに対して
each
を使い、その要素を列挙できます。test3.slimdoctype html html head title My first Slim body ol - ["Dog", "Cat", "Cow"].each do |animal| li = animalこれをslimrbに食わせるとこんなHTMLになります。
test3.html<!DOCTYPE html> <html> <head> <title>My first Slim</title> </head> <body> <ol> <li> Dog </li> <li> Cat </li> <li> Cow </li> </ol> </body> </html>リストとeachのからみは直感的なので、特に苦労することはないと思います。
YAMLから論文リストを作る
まずはナイーブに作る
ではいよいよYAMLで書かれた論文リストからHTMLにしましょう。とりあえず
- タイトル
- 著者
- ジャーナル情報
- (もしあれば)arXivへのリンク
を出力することにします。
RubyはYAMLを読み込めば、それがそのままハッシュとして使えるので、先程のeachの例を参考にすれば簡単に書けます。
test1.slimdoctype html html - require 'yaml' head title Publication List body h1 Publication List ol - YAML.load_file("publication.yaml").each do |d| li :dl dt = d["title"] dd == "#{d["journal"]}, <strong>#{d["volume"]}</strong>, #{d["page"]} (#{d["year"]})" dd = d["author"] - if d.has_key? "arxiv" dd == "<a href=\"https://arxiv.org/abs/#{d["arxiv"]}\">arXiv:#{d["arxiv"]}</a>"事前に
require 'yaml'
しておき、YAML.load_file("publication.yaml")
でYAMLファイルをロードして、each
でそれぞれのアイテムを表示します。if
文も使えるので、特に難しいことはありませんが、テンプレートとしてはかなり読みづらいので、少し修正しましょう。ハッシュアクセスをプロパティっぽくする
いちいち
d["title"]
とか書くより、d.title
と書きたいものです。Hashie
を使えばできるっぽいのですが、なぜかローカルで動かなかったので、Hash
クラスにメソッドを追加してしまいましょう。myhash.rbclass Hash def method_missing(method, *args) key = method.to_s if self.has_key? key self[key] else false end end def存在しないメソッドを読んだら、そのメソッド名のキーがあればその値を、そうでなければ
false
を返す関数を作ってやります。これにより、slimファイルはこんな感じになります。yaml
をrequireした後に自分で作ったmyhash.rb
をrequireしています。test2.slimdoctype html html - require 'yaml' - require './myhash.rb' head title Publication List body h1 Publication List ol - YAML.load_file("publication.yaml").each do |d| li :dl dt = d.title dd == "#{d.journal}, <strong>#{d.volume}</strong>, #{d.page} (#{d.year})" dd = d.author - if d.arxiv dd == "<a href=\"https://arxiv.org/abs/#{d.arxiv}\">arXiv:#{d.arxiv}</a>"生のハッシュっぽさが消えて、少し読みやすくなりました。
そうそう、Slimでは普通にHTMLタグを使うとエスケープされてしまいます。それを防ぐためには
=
ではなく==
を使います。もう少しすっきりさせる
少し読みやすくなったものの、まだ
"#{d.journal}, <strong>#{d.volume}</strong>, #{d.page} (#{d.year})"
みたいなのがあって嫌です。これもmyhash.rb
に逃がしてしまいましょう。myhash.rbclass Hash def journal_ref "#{journal}, <strong>#{volume}</strong>, #{page} (#{year})" end def arxiv_ref "<a href=\"https://arxiv.org/abs/#{arxiv}\">arXiv:#{arxiv}</a>" end defこれで、slimファイルはこんな感じになります。
test3.slimdoctype html html - require 'yaml' - require './myhash.rb' head title Publication List body h1 Publication List ol - YAML.load_file("publication.yaml").each do |d| li :dl dt = d.title dd == d.journal_ref dd = d.author - if d.arxiv dd == d.arxiv_refだいぶすっきりしましたね。
まとめ
軽量テンプレート言語Slimを使って、ローカルでYAMLファイルから論文リストを作ってみました。知っている人には簡単なのでしょうが、そのものずばりのサンプルがなくてちょっと苦労したのでここに公開しておきます。
ずいぶん昔、文献リストをXMLで管理していたことがありました。その時に「RubyとYAMLでやれば楽じゃない?」と言われたのですが、当時はPerlを使っていてRubyを使えなかったのと、将来XSLTを使う予定があったので、その時はXMLを選びました。でもXSLTは(僕には)難易度が高く、そのうち第一言語がRubyになったので、結局Ruby+YAMLでなんかすることが多くなりました(YAMLで履歴書とか)。
Slimは簡単で、かつRubyが埋め込めるので、HTMLを手書きに近い管理をしていて、かつRubyistな人(つまり私のような人)にはお勧めです。
- 投稿日:2019-06-27T11:12:02+09:00
【Rails】Action Mailer でメール送信機能をつくる
概要
Action Mailerを使ってメールの送信機能をつくります
今回は管理者がユーザーからのお問い合わせに対して、管理者画面から返事をすると、
ユーザーにメールで送信される機能を実装していきます。学習メモとして記録
参考
(1)RAILS GUIDES: Action Mailer の基礎
(2)【Ruby on Rails】メール送信の実装手順(ActionMailer)とはまったエラーなど
(3)Railsでメール自動配信機能をつくるまでの道程
(4)Rails の ActionMailer でメール送信処理
(5)RailsのAction Mailerでメール送信Action Mailerとは
Ruby on Rails に組み込まれているメール送信機能のこと。
Action Mailer を使うと、Ruby on Rails からメールを送信してくれます・メールマガジンの一斉送信
・ウェブサイトに会員登録した時のthank youメール
・お問い合わせフォームの記入内容が管理者にメールでも送信されるのような時にアプリケーションからメールを送信する機能と解釈してます。
アプリケーションのメーラークラスやビューからメールを送信することができる便利な機能です!導入手順
1. メイラーを生成
railsコマンド(
rails generate
)で生成します。 ContactMailer は任意クラス名。
今回はお問い合わせ関係のメール送信機能を作成したいためContactMailerです。terminal.生成$ rails generate mailer ContactMailer↓
terminal.結果Running via Spring preloader in process 1893 create app/mailers/contact_mailer.rb invoke erb create app/views/contact_mailer invoke rspec create spec/mailers/contact_mailer_spec.rb create spec/mailers/previews/contact_mailer_preview.rb無事に生成されましたね
2. サーバーを設定
メールを送信するときは、送信するサーバーが必要。と言うことで、
config/environments/development.rb
にメール送信設定を記述します。
今回はg-mailを使う記載方法です。development.rbRails.application.configure do #--- 中略 ---# config.action_mailer.raise_delivery_errors = true config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { port: 465, address: 'smtp.gmail.com', domain: 'gmail.com', user_name: '<YOUR EMAIL ADDRESS>', password: '<YOUR EMAIL PASSWORD>', authentication: 'login', enable_starttls_auto: true } end上から順々に見ていきます。
ここでは、config.action_mailer
というパラメーターに色んなオプションを指定してます。1行目
raise_delivery_errors
メールの送信に失敗した時にエラーを出すかどうか (出したいので true)2行目
delivery_method
メールの送信方法。 デフォルトで:smtd
なので気にする必要もないのですが、
わたしみたいに「なにそれ!?」ってなった方は以下の引用を読んでみてください。「SMTP」とは「Simple Mail Transfer Protocol(シンプル・メール・トランスファー・プロトコル)」の略で、あえて>訳せば「簡単なメールの送信の手順」というところだろうか。お約束ごとと考えてもいい。
あなたがメールを書き、宛先のアドレスを入力して「送信」アイコンをクリックする。このとき、あなたのスマホやパソコン>は、この「SMTP」のお約束ごとに従って、あなたが契約しているメールサーバーと、こんなやり取りをするのである。
「メールを送るよ〜」「ええで!」「宛先は〇◯だよ」「りょ」「本文はかくかくしかじかだよ」「受け取ったで!」――とまぁそんな具合。出典: メール設定で最初につまずく「SMTP」「POP」「IMAP」。その意味&設定方法は?
3行目
smtp_settings
smtpの詳細設定って感じです。
- port => SMTPサーバーのポート番号
- address => SMTPサーバーのホスト名
- domain => HELOドメイン
- user_name => メール送信に使用するgmailのアカウント
- password => メール送信に使用するgmailのパスワード
- authentication => 認証方法
- enable_starttls_auto => メールの送信にTLS認証を使用するか
GmailのSTMPサーバーの設定
上記に関しては以下を参考に設定しました。
『GmailのSMTPをメールクライアントに設定する方法』の部分を参照しました。
GmailのSMTPサーバーを使ってGmail以外からメールを送信する方法3. メーラーを編集
メーラーってRailsのコントローラーと似てるんですね。
「アクション」と呼ばれるメソッドがあり、メールの内容をつくるのにビューを使います。生成直後は、以下のような application_mailer.rb と
app/mailer/application_mailer.rbclass ApplicationMailer < ActionMailer::Base default from: 'from@example.com' layout 'mailer' end空のメーラー
app/mailer/contact_mailer.rbclass ContactMailer < ApplicationMailer endがあるはずです。
application_mailer
には全メーラー共通の設定を、
sample_mailer
にはメーラー個別の設定をします。application_mailerを編集します
app/mailer/contact_mailer.rbclass ApplicationMailer < ActionMailer::Base default from: "管理人 <from@example.com>", layout 'mailer' end共通の処理・設定を記述する場合には
defaultメソッド
を使用します。
プロパティ 役割 to 送信先の指定 cc 一斉送信先の指定 bcc 非表示送信先の指定 from メールの送信元名 subject メールタイトル date メールの送信日時 reply_to 返信用アドレスの指定 などが指定できます。
contact_mailer.rb を編集します
今回は管理者の返信がユーザーのe-mailに届くようにします。
なのでメソッドをsend_when_admin_reply
と定義しました。app/mailer/contact_mailer.rbclass ContactMailer < ApplicationMailer def send_when_admin_reply(user, contact) #メソッドに対して引数を設定 @user = user #ユーザー情報 @answer = contact.title #返信内容 mail to: user.email, subject: '【サイト名】 お問い合わせありがとうございます' end end個別の設定には
mailメソッド
を使用します。
send_when_replyedメソッド
を呼び出す時に渡されるユーザーの情報から、
emailアドレスだけを取り出してメールの送信先としてします。
mailメソッド
が呼び出されると、メール本文が記載されているビューが読み込まれます。
インスタンス変数(@xxx)でメーラービューに値を渡してあげたいので、インスタンス変数を用意してる感じです。次は、そのメールの本文を作成していきます
4. メールの本文を作成する(メーラービューの作成)
app/views/contact_mailerディレクトリ
下にファイルを2つ作成します。1つはHTMLフォーマット、もう一つはテキストメール。
顧客によってはHTMLフォーマットのメールを受け取ることができない / 受け取りたくない人もいるので、テキストメールも作成しておくのが最善です。HTMLファイルの作成
views/contact_mailer/send_when_admin_reply.html.erb<h2><%= @user.name %> 様</h2> <p>この度は、お問い合わせありがとうございました。<br> 以下でご質問の回答となっておりますでしょうか。</p> <p><%= @reply %></p> <p>今後とも XXX をよろしくお願いいたします。</p>テキストファイルの作成
views/contact_mailer/send_when_admin_replyd.text.erb=============================== <%= @user.name %> 様 =============================== この度は、お問い合わせありがとうございました。 以下でご質問の回答となっておりますでしょうか。 <%= @reply %> 今後とも XXX をよろしくお願いいたします。5. 実際に処理を走らせるアクションにメール送信処理をさせる
メールの設定を記述しただけではメールは送信できません。
メーラー(ここではapplication_mailer.rb
/contact_mailer.rb
)は各コントローラーのアクションからの呼び出しによって起動します。管理者がアプリケーション管理画面フォームより返信を送信し、その内容をメールでユーザーに送信します。
実際に処理を走らせるアクションはadmin/contacts_controller.rb
となります。app/controllers/contacts_controller.rbclass Admin::ContactsController < Admin::ApplicationController def update contact = @contact #contact_mailer.rbの引数を指定 user = contact.user #contact_mailer.rbの引数を指定 ContactMailer.send_when_admin_reply(user, contact).deliver end end例えば、ユーザーが新規アカウント登録をしてメールを送信する場合は、
app/controllers/users_controller.rbclass UsersController < ApplicationController before_action :set_user def create if @user.save #ユーザーのインスタンスが新しく生成されて保存されたら NotificationMailer.send_when_signup(@user).deliver #確認メールを送信 redirect_to @user else render 'new' end end endcreateアクションに処理を走らせることになります。
わたしのアプリケーションでは、DBの設計上
ユーザーが問い合わせをした時点で、Contactsテーブルの中に、
1つレコードが準備されていて、その中に'返信'というカラムが用意されています。なので、updateアクションを使って
すでにあるレコードの中に管理者の返信のデータだけを更新してあげる形になります6. テストしてみる
以上でメーラーができるはずです・・!
実際にアプリケーションを動かしてみて、メールを受信できたら成功以上、学習メモでした
- 投稿日:2019-06-27T10:22:52+09:00
神経衰弱ゲームをrubyでつくる【ruby初心者】
はじめに
神経衰弱ゲームを作ってみる。
実際のコード
#縦の数、横の数、プレイヤーの数を受け取る h,w,n = gets.chomp.split(" ").to_a #カードの並びを行列で入れる変数を宣言 cards = [] #高さの数だけ、受け取りを繰り返す。 h.to_i.times do array = gets.chomp.split(" ").map &:to_i cards.push(array) end #プレイヤーの数だけ、スコアの配列を用意する score=[] n.to_i.times do score.push(0) end #実行回数 l = gets.chomp.to_i #ターンの宣言 turn = 0 #ゲームの開始 l.times do #実行回数分、めくったカードを配列で受け取る。 array = gets.chomp.split(" ").map &:to_i #受け取った配列を、各回の行列に代入する。 a_1,b_1,a_2,b_2 =array.to_a #めくったカードの指定をする first = cards[a_1 -1][b_1 -1] second = cards[a_2 -1][b_2 -1] #カードが一致だったらスコアをあげる if first == second score[turn] += 2 #一致しなければ、次のターンに移る。 else turn += 1 if score.count <= turn turn=0 end end end puts score
- 投稿日:2019-06-27T09:26:26+09:00
文字列が合うまで、何回配列を動かせばいいか計算するプログラム 【ruby初心者】
はじめに
工場などで、向きを調整するプログラム。
実際のコード
#入力の受付 input_line = gets.chomp.split(" ") #文字を配列になおす collect = input_line[1].split("") #答えの文字列 testings = input_line[2].split("") #受け取った文字列 #試行回数をカウントする変数 a = 0 testings.each do #もしも文字列が一致すれば、終了して試行回数を表示する if collect == testings puts a break else #文字列が一致しなければ、配列を入れ替える t = testings[0] testings.shift() testings.push(t) end a += 1 end
- 投稿日:2019-06-27T08:56:17+09:00
算数のあなぬけ問題の回答作成プログラム
はじめに
a + b = c
または
a - b = c
などで、a,b,cのいずれかが、虫食いになっている場合の、回答作成方法実際のコード
#入力は、 a + b = x や、 a - x = c などの形で来る #入力の受付 input_line = gets.chomp.split(" ") #それぞれ変数に代入する #これ一行でも表せる。 a,enzan, b, equal, c = input_line.to_a a = input_line[0] b = input_line[2] c = input_line[4] enzan = input_line[1] #"x"の位置と、"enzan"の値によって、出力する計算式を変える。 if a == "x" if enzan == "+" puts c.to_i - b.to_i else puts c.to_i + b.to_i end elsif b == "x" if enzan == "+" puts c.to_i - a.to_i else puts a.to_i - c.to_i end else if enzan == "+" puts a.to_i + b.to_i else puts a.to_i - b.to_i end end
- 投稿日:2019-06-27T07:47:58+09:00
エレベーターの距離を計算するプログラム【ruby初心者】
はじめに
エレベーターの距離計算機能。絶対値などの計算の練習メモ。
作る機能
①移動データを、配列として取得する
②数値の初期設定
③距離を絶対値で計算する実際のコード
#入力データを、配列として取得する input_lines = readlines.map &:to_i #現状の階数と、距離の初期設定 floor_now = 1 distance = 0 input_lines.each do |a| #距離の絶対値で計算をする if (a - floor_now) >0 distance += a - floor_now else distance -= a - floor_now end #距離計算後に、階数を更新する floor_now = a end puts distance絶対値の計算をもっと簡単に
コメントでのアドバイスを反映。5行で書いていた下記の絶対値の計算。
#距離の絶対値で計算をする if (a - floor_now) >0 distance += a - floor_now else distance -= a - floor_now end下記のように1行で書き直すことが可能。
#距離の絶対値で計算をする distance += (a - floor_now).abs
- 投稿日:2019-06-27T02:23:40+09:00
gem impressionist を使ってユニークpv数を計測【Rails】
ruby 2.5.1
rails 5.2.1はじめに
impressionistを使ってpv数を計測しようと思い、色々調べながら実装するもなかなかユニークpv数が取得できずに苦戦していました。結果的にreadme(https://github.com/charlotte-ruby/impressionist) を見て解決したので、その記録として残しておきます。最初からreadme見ればよかった。。。
導入方法
Gemfilegem 'impressionist'$ bundle install次にpvを保存するテーブルを作成します。
$ rails g impressionistこのようになっていれば大丈夫です。
Running via Spring preloader in process 40810 invoke active_record create db/migrate/20190626153731_create_impressions_table.rb create config/initializers/impression.rbmigrateします。
$ rails db:migrateModel
今回はQ&Aサイトの質問ページのpv数を取得したいためQestionモデルに以下のように記載します。
question.rbclass Question < ApplicationRecord is_impressionable endControllers
セッションハッシュでフィルタリングされたモデルからユニークpv数を取得します。IPで判別すると同じIPを使用する訪問者を計測できないのでセッションハッシュで判別するのがいいっぽいです。
question_controller.rbclass QuestionsController < ApplicationController impressionist unique: [:session_hash] def show @question = Question.find(params[:id]) impressionist(@question, nil, unique: [:session_hash]) end endViews
show.html.erb<%= @question.impressionist_count %>更新してもテーブルのレコードや表示されるpv数が増えていなければ完璧!
他の記事を参考したけど全然できなかったのでreadmeを読んだのですが、これが足りなかったようです。
impressionist unique: [:session_hash]
公式リファレンスを見る習慣をつけます。
- 投稿日:2019-06-27T01:25:50+09:00