20191004のRubyに関する記事は20件です。

railsでscopeがnilを返すとallが返る

railsのscope

  • 文字通り、そのクラスの一部のインスタンスを返します
  • 意外と知られてないですが、nilを返すとallが返ります
  • クラスメソッドと全く同じ挙動ではない。classメソッドはnilを返せばnilが返る

scope

[1] pry(main)> class User < ApplicationRecord
[1] pry(main)*   scope :nil_scope, ->() {  
[1] pry(main)*     nil    
[1] pry(main)*   }    
[1] pry(main)* end  
=> :nil_scope
[2] pry(main)> User.nil_scope
  User Load (4.0ms)  SELECT `users`.* FROM `users`
=> [#<User id: 1, email: "xxx@gmail.com", created_at: "2018-04-14 12:05:50", updated_at: "2019-09-05 01:01:24", provider: "facebook", uid: ...]

pry(main)> User.nil_scope.size
   (0.9ms)  SELECT COUNT(*) FROM `users`
=> 355

class method

[1] pry(main)> class User < ApplicationRecord
[1] pry(main)*   def self.nil_scope
[1] pry(main)*     nil
[1] pry(main)*   end  
[1] pry(main)* end  
=> :nil_scope
[2] pry(main)> User.nil_scope
=> nil
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

turbolinksをfalseのページとtrueのページに分ける

こんばんは!
ポートフォリオ作成中にハマったエラーの簡単な解決方法を備忘録として。

いいね機能をremote: trueで動かす

railsチュートリアルにて学習したremote: trueを使ったajax通信。jsが苦手な私にぴったり!

turbolinksが必要みたい

Uncaught ReferenceError: Turbolinks is not defined

application.js
/=require turbolinks

demo

動きます。

次はこっちが動かないのね

<%= link_to "新規投稿", new_meal_path, class:'hoge'%>に遷移してfile_fieldに画像添付したらimageがpreview表示されなくなった

リロードすれば正常に動作します

demo

解決方法

index.html.erb
<%= link_to "新規投稿", new_meal_path, class:'hoge',data: {"turbolinks"=>false} %>


data: {“turbolinks" => false}は遷移先ページのみturbolinksを切ることが出来ます

まとめ

jsファイルにajax通信を記述すればturbolinksがtrueでも問題ないみたいです

remote: trueを使ったajax通信とjsイベントを両方実装している時にどちらかのイベント発火しない場合があればdata: {"turbolinks"=>false}が役立ちます

終わり

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]動画プレビュー機能

はじめに

今回以下の様な動画のプレビュー機能を追加したのでその方法について、説明します。3c6162c281ea3a7829bf724726d7461d.gif

環境

Rails 5.2.3
Ruby 2.5.1

導入方法

ビューファイル

気を付ける点は、file_fieldid: "upload-image"をjQueryを使用する為に記述します。

new.html.haml
= form_for @video do |f|
      省略
 .movie-group-form__field--right--mask
   = f.label :image, for: "upload-image", class: "movie-group-form__movie" do
     = fa_icon "cloud-upload fa-spin", class: "icon"
     = f.file_field :image, class: "hidden", id: "upload-image"
     = f.label :動画を選択してください , class: "movie-group-form__choice"
 = f.submit "投稿する", class: "btn btn-dark"

jQuery

次にJSファイルに動画プレビューをさせる記述をします。
$fileFieldにビューのidに記述したupload-image#を付けて記述します。

$previewには動画をプレビュー表示させたい部分(今回は.movie-group-form__choice)に記述します。
$preview.append($('<video>').attr({内にCSSを記述してます。autoplay: "autoplay"loop: "loop"で動画のプレビューをオート再生させています。
playsinline: "true"はスマホで動画を投稿した際に、オート再生するのに使用します。

preview.js
$(document).on('turbolinks:load', function() {
  $fileField = $('#upload-image')

  $($fileField).on('change', $fileField, function(e) {

      file = e.target.files[0]
      reader = new FileReader(),
      $preview = $(".movie-group-form__choice");

      reader.onload = (function(file) {
        return function(e) {
          $preview.empty();
          $preview.append($('<video>').attr({
            src: e.target.result,
            width: "45%",
            height: "110px",
            class: "preview-image",
            autoplay: "autoplay",
            loop: "loop",
            playsinline: "true",
            title: file.name
          }));
        };
      })(file);
      reader.readAsDataURL(file);
    });
  });

今回はJS内にCSSを直接記述しているので、CSSファイルの変更は必要ないです。後は好みで調整して下さい。

まとめ

動画のプレビューも画像のプレビューとほぼ同じで、後はappendに'<video>''<img>'を記述するだけで、動画か画像のプレビュー表示かを変更する事が出来ます。気になったら試してみて下さい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

moduleとmixin

モジュール

module SampleModule
  CONST_NUM =100

  def const_num
    CONST_NUM
  end
end

puts SampleModule::CONST_NUM

# moduleをincludeしたあとならモジュール名を省略して定数に直接アクセスできるようになる
include SampleModule
puts CONST_NUM
puts const_num

module_functionを使うことでmodule名に.をつけてメソッドを呼び出せるようになる

module SampleModule
  def module_function_sum(a, b)
    a + b
  end

  module_function :module_function_sum
end

SampleModule.module_function_sum(1, 100)
=> 101

ミックスイン

モジュールの機能をクラスに取り込むには
  include モジュール名
とする

module SampleModule
  def sum(a, b)
    a + b
  end
end

class Sample
  include SampleModule

  def class_sum(a, b)
    sum(a, b)
  end
end

実行してみる

sample = Sample.new
puts sample.sum(1, 100)
=> 101

puts sample.class_sum(1, 100)
=> 101

使い道

  • 複数のクラスで使う共通のメソッドをmoduleとして定義し、適宜includeして使う
  • 特定のメソッドをmoduleとしてまとめておきユーティリティーとして使用する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

imagemagickを使って画像をアップロードするとSSLが外れてしまう時の対処法

minimagickのgemを使って画像をアップロードすると、その画像が表示されるページのSSLマークが外れるという問題が発生したので、その時の解決方法を残します。

このサイトで目にする画像は、悪意のあるユーザーによって差し替えられたものである可能性があります

事象

・minimagickのgemを使って画像をアップロードすると、その画像が表示されるページでSSLマーク(鍵マーク)が外れ、びっくりマークに変わる
・びっくりマークをクリックすると「このサイトで目にする画像は、悪意のあるユーザーによって差し替えられたものである可能性があります」のメッセージがでる。

解決方法

nginxのconfファイルにproxy_set_header X-Forwarded-Proto https;を追記

/etc/nginx/conf.d/hoge.conf
location @app {
 proxy_set_header Host $http_host;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_set_header X-Forwarded-Host $http_host;
 proxy_set_header X-Forwarded-Server $hostname;
 proxy_set_header X-Forwarded-Proto https; #この一行を追加
 proxy_set_header X-Real-IP $remote_addr;
 proxy_redirect off;
 proxy_pass http://app_server;
 }

Nginx再起動

systemctl restart nginx

解決までの流れ

①画像ファイルへのリンクの確認

この事象はサイト内で使用している画像に、httpアクセスで記述している箇所があると発生するらしいので、デベロッパーツールで画像リンクのプロトコルを確認。
参考:https://stackdesign.jp/how-to-create-ssl-wordpress-site/

→きちんとhttpsになってた。

②Railsの環境設定を変更

production.rbのconfig.force_ssl = trueを有効にすると、本番環境でSSL通信を強制できるので、コメントアウトを外し設定を有効にして再デプロイ。
参考:https://railsguides.jp/configuring.html

/config/environments/production.rb
config.force_ssl = true

→ページにアクセスできなくなる
メッセージ
・このページは動作していません
・リダイレクトが繰り返し行われました。

→再びコメントアウト

③nginxのconfファイル修正

HTTPSアクセスであることをX-Forwarded-ProtoでRailsへ通知することでリダイレクトのループを回避する。
参考:https://keruuweb.com/rails-nginx-https%E3%81%A7%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%82%92%E3%81%99%E3%82%8B/

/etc/nginx/conf.d/hoge.conf
location @app {

 proxy_set_header X-Forwarded-Proto https; #この一行を追加

 }

→無事に全ページでSSL化が有効になりました

まとめ

「proxy_set_header X-Forwarded-Proto https;」はリダイレクトのループを回避するための設定で、本来はSSL化をする際に設定しておく必要があったらしい。

SSL化の際にきちんと設定ができていたら、そもそも今回のエラーは生じなかったってことですね、、、

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[初学者]findメソッドについて(ruby)

目的

学習の備忘録と初学者の参考資料として投稿

findメソッド

該当するモデルのテーブルからデータを検索する際に使われるメソッド。

例えば・・・

userテーブル

id name age gender
1 taro 12 male
2 hanako 11 female
3 jiro 10 male
4 keiko 9 female

検索して表示させたい→

id name age gender
○○○ ○○○ ○○○ ○○○
users_controller.rb
 def show
  @user = User.find(user:id) idを引数として検索する
 end

 *viewは省略

findは検索条件として指定できるものは「idのみ」です。データを複数取得できます。

findとfind_byの違い

find_byは、複数の条件を指定したりid以外のカラムでも検索できます。データは条件があった中で最初の一つのみ取得できる。


find_by(gender:"male")

上の例から、こんな感じで検索する。

まとめ

テーブルからデータを表示や更新や削除する場合によく使うメソッドです。他にもwhereというメソッドもあります。

今後も学習で気づきや参考になるものがあれば、アップしていきます。
もし参考になったらいいね!!よろしくお願いします:bow_tone1:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】jQueryでフォームに文字数カウントをつける

フォームに文字を入力していく時、あと何文字まで打てるのか分かった方がユーザーに優しいと思ったため、文字数カウント機能を作りました。

▷今回やること
フォームに残り何文字入力できるかを表示する

ezgif.com-video-to-gif.gif

文字数カウント方法を考える

・文字を入力したり、消したりするたびフォームの文字数を数える → 残り何文字まで入力できるか表示する
・文字数がオーバーしていた場合、文字色を赤にする → ユーザーに警告する

文字数カウントのアルゴリズムを考える

jQueryで文字数カウントをする

▷jsが読み込まれたら以下の処理をする

 ・処理内容(ページが読み込まれた時、フォームに残り何文字入力できるかを数えて表示する)
 フォームのvalueを取得して文字数を数える
 <!-- Macの改行コードは1文字(\n)であるが、Rails側では改行コードが2文字(\r\n)で保存されるため、\nは2文字とカウントするように設定 -->
 最大文字数 ー 取得した文字数 (=残りの入力できる文字数)

 if 最大文字数より取得文字数の方が大きい
  表示する文字色を赤にする

 “あと (残りの入力できる文字数)文字” と表示する

  ▷jQueryのkeyup()でキーボードが押されて離れるたびに処理を行う

   ・処理内容(キーボードを押した時、フォームに残り何文字入力できるかを数えて表示する)
    フォームのvalueを取得して文字数を数える。
    <!-- Macの改行コードは1文字(\n)であるが、Rails側では改行コードが2文字(\r\n)で保存されるため、\nは2文字とカウントするように設定 -->
    入力最大文字数 ー 取得した文字数 (=残りの入力できる文字数)

   if 最大文字数より取得文字数の方が大きい
    表示する文字色を赤にする
    “あと (残りの入力できる文字数)文字” と表示する
   else
    表示する文字色を黒にする
    ”あと (残りの入力できる文字数)文字” と表示する

  処理終了
処理終了

コードで文字数カウント処理を書く

1.htmlでフォーム入力タグと文字数表示タグにclassを設定し、jsで指定できるようにする

フォーム入力タグtext_areaのクラスにjs-text、文字数表示タグpのクラスにjs-text-countを指定する
(フォームにbootstrapを適用しています)

form.html.erb
<%= form_for(@profile) do |f| %>
  <div class="panel-body">
    <div class="form-group">
    <%= f.label :yourself, '自己紹介(最大150文字)' %>

    <%# js-textをclassに追加 %>
    <%= f.text_area :yourself, class: 'form-control  js-text', rows: 6, placeholder: '改行は反映されません' %>

    <%# js-text-count をclassに追加 %>
    <P class="js-text-count pull-right"></P>
  </div>
      
  <div class="text-center">
     <%= f.submit '保存する', class: 'btn btn-info'%>
  </div>
<% end %>    

2.jsファイルにフォームと文字数表示に対する処理を書く

js-textのvalueを数えて、js-text-countに結果を表示する
app/assets/javascripにcount.jsファイルを作成し、文字数カウントの処理を書いていく
(今回のフォームの最大文字数は150)

app/assets/javascrip/count.js
// jquery書きはじめ
$(function (){
  // 処理(ページが読み込まれた時、フォームに残り何文字入力できるかを数えて表示する)

  //フォームに入力されている文字数を数える
  //\nは"改行"に変換して2文字にする。オプションフラグgで文字列の最後まで\nを探し変換する
  var count = $(".js-text").text().replace(/\n/g, "改行").length;
  //残りの入力できる文字数を計算
  var now_count = 150 - count;
  //文字数がオーバーしていたら文字色を赤にする
  if (count > 150) {
    $(".js-text-count").css("color","red");
  }
  //残りの入力できる文字数を表示
  $(".js-text-count").text( "残り" + now_count + "文字");

  $(".js-text").on("keyup", function() {
    // 処理(キーボードを押した時、フォームに残り何文字入力できるかを数えて表示する)
    //フォームのvalueの文字数を数える
    var count = $(this).val().replace(/\n/g, "改行").length;
    var now_count = 150 - count;

    if (count > 150) {
      $(".js-text-count").css("color","red");
    } else {
      $(".js-text-count").css("color","black");
    }
    $(".js-text-count").text( "残り" + now_count + "文字");
  });
});

3.リンクをクリックしてページ遷移した時、jacascriptを読み込むようにする

現在のままでは、ページ遷移してもjsが機能しない

▷原因
rails4.0からデフォルトで導入されたgem「turbolinks」が読み込まれているため

〜turbolinksとは〜

画面遷移を高速化させるライブラリ。
ページ遷移するとき、現在のページのtitleとbodyだけを抜き取り、
新しいhtmlのtitleとbodyに交換することで、ページ遷移を高速化させる。
→ページ読み込みを起点としたJavascriptは機能しなくなる
jQuery を使用している場合、
下記の様にページが読み込まれたときの処理を記述出来ますが、
Turbolinksによって、実行されなくなります。
$(function() {
#処理内容
});

『Turbolinks』って、なんぞ?より抜粋

▷解決方法
count.jsを読み込みたいリンクのtubolinksを無効にする
aタグdata-turbolinks="false"link_toメソッドdata: {"turbolinks" => false}でtubolinksを無効化できる

リンク設定がaタグの場合

<a href=“◯◯” data-turbolinks="false">turbolinksを使わずにリンクする</a>

リンク設定がlink_toメソッドの場合

<%= link_to "turbolinksを使わずにリンクする", ◯◯_path, data: {"turbolinks" => false} %>

これでページ遷移した時、count.jsが読み込まれるようになり、文字数カウント処理が行われる。

参考

[簡単すぎる]Rails + jQueryで文字数カウント
【JavaScript】文字列から改行を全て削除する方法
turbolinksチートシート
『Turbolinks』って、なんぞ?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ブログ投稿サイトを作ってみました

こんにちは。
本日は初投稿ということで、プログラミングを始めて2週間少しの初心者が0から作った作品を紹介します。
ーーーーーーーー

ブログ投稿サイト :airplane:

トップ画面

スクリーンショット 2019-10-04 14.46.50.png
スクリーンショット 2019-10-04 14.45.14.png

ユーザー新規登録、ログイン画面

スクリーンショット 2019-10-04 19.58.52.pngスクリーンショット 2019-10-04 19.59.01.png

投稿画面

スクリーンショット 2019-10-04 19.59.38.png

詳細確認画面

スクリーンショット 2019-10-04 19.57.44.png
(写真引用元:https://www.travel.co.jp/guide/article/24996/)

1. 実装機能

・ユーザ登録、ログイン、ログアウト可能
・投稿はタイトル、本文、画像のみ(非ログイン時は投稿不可)
・投稿したユーザ本人による投稿の編集、削除が可能
・ログインしていなくても投稿一覧、詳細が閲覧可能
・ページネーション機能(投稿は新着順)

2. 使用言語・ツール

・Ruby
・Haml/Sass
・JQuery
・Ruby on Rails
・Bootstrap

3. 工夫した点

bootstrapというwebアプリケーションフレームワークを使用し、
ボタンやページネーション等を簡単に作成、装飾してみました。
フォームなどにも適用させて、もっと簡単に作成するのが理想でしたが、
公式サイトはもちろん英語だったのでなかなか上手く使えなかったのが少し残念なところです。。。
あとは綺麗な画像を貼ることで見栄えを良くしました!笑

4. 苦労した点

devise gem をインストール後、userテーブルをmigrateする際にエラーが発生し、
解決に時間が掛かりました。
エラー内容は「max key length is 767 bytes(768バイト以上のカラムに対するインデックスキーは設定できないよ)」と言った内容。
調べてみると、MySQLの単一カラムインデックスのインデックスキーは、デフォルトで
最大 767 バイトまでとのことでした...。
マイグレーションファイルに於いて、string 型の容量にリミットをつけてあげることで解決出来ました。
ex) t.string   :reset_password_token, limit: 191

参考文献
https://blog.e2info.co.jp/2017/04/17/mysql%E3%81%AE%E3%82%A4%E3%83%B3%E3%83%87%E3%83%83%E3%82%AF%E3%82%B9%E3%82%B5%E3%82%A4%E3%82%BA%E3%81%AB767byte%E3%81%BE%E3%81%A7%E3%81%97%E3%81%8B%E3%81%A4%E3%81%8B%E3%81%88%E3%81%AA%E3%81%84/

5. 感想

制作時間の半分以上がエラーとの奮闘でしたが、解決した時にはこの上ない感動でした。
普段使っているウェブサイト、アプリ、何から何までこんなに苦労しているんだなと感じることができました。
「こんな機能があるといいな」「こんな風に配置したいな」と、色々とこだわりを入れたかったのですが、そこは自分の今の技術的に厳しめだったので断念。
これからも毎日勉強して、良いものを作ろうと思いました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

日時の扱い方

日時の扱い方

Rubyには日付や時刻を扱うためのDateやTimeといったクラスがあります。そのため、Rialsアプリケーションで日付や時刻を扱う際にもただこれらのクラスを使えばいいと思われるかもしれませんが、ひとつ課題があります。それは、タイムゾーンです。

例えば、Railsアプリケーションがデータベース内に、ある日時を保存するとしましょう。その日時を東京に住むユーザーが見たときは東京時間、シンガポールに住む人が見たときはシンガポール時間で表示した方が良いでしょう。このように、地域の時間に合わせた日時を表示する必要があるということです。そのため、Railsでは日時のデータをタイムゾーンとともに取り扱うことができるようになっている。

タイムゾーンとともに日時を扱うには、Timeの代わりにActiveSupport::TimeWithZoneクラスを用います。Railsが日時を扱う際(日時型のデータをデータベースから読むときや、created_atなどのタイムスタンプを記録する際)には、自動的にこのクラスが利用されます。

それでは、Railsコンソールを立ち上げて挙動を確認してみましょう。(※Rialsアプリケーションのコンソールで動作します。)

まず、現在有効なタイムゾーンを確認してみましょう。Time.zoneと売ってみてください。何も手を加えていないRailsアプリケーションでは次のように、"UTC"という名前を持つActiveSupport::TimeZoneオブジェクトが返されるはずです。これは、UTC(協定世界時)タイムゾーンを意味しています。

2.6.0 :013 > Time.zone
 => #<ActiveSupport::TimeZone:0x000000000a8e9568 @name="UTC", @utc_offset=nil, @tzinfo=#<TZInfo::DataTimezone: Etc/UTC>> 

続いて、そのUTCタイムゾーンにおける現在時刻を表すオブジェクトを取得しましょう。それには、Time.zone.nowを実行します。

2.6.0 :003 > Time.zone.now
 => Fri, 04 Oct 2019 13:52:47 JST +09:00 

Time.zone.nowは、ActiveSupport::TimeWithZoneクラスのオブジェクトとなっています。試しに、classメソッドを調べてみましょう。

2.6.0 :004 > Time.zone.now.class
 => ActiveSupport::TimeWithZone 

確かにそのようになっていますね。

オブジェクトの作成日時を表すreated_atなどもActiveSupport::TImeWithZoneのオブジェクトとなっています。

タイムゾーンの変更

タイムゾーンは「現在のタイムゾーン」に当たります。データベースからモデルオブジェクトを取ってくると、オブジェクト内のcreated_atやupdated_atは、Time.zoneで示されたタイムゾーンに対応する時間オブジェクトとなります。

したがって、もしもモデルのcreated_atなどを日本時間での時間オブジェクトとして取得したいのであれば、Time.zoneを日本時間に指定してからオブジェクトを取得すれば良いということになります。試しに、Time.zoneに日本時間を指定してみましょう。次のように、Time.zoneにAsia/Tokyoを代入してみてください。

2.6.0 :009 > Time.zone = 'Asia/Tokyo'
 => "Asia/Tokyo" 

これで設定が変わるので、Time.zone.nowを実行すると、今度は日本時間での現在時刻が取得できます。

2.6.0 :010 > Time.zone
 => #<ActiveSupport::TimeZone:0x000000000a8d9e10 @name="Asia/Tokyo", @utc_offset=nil, @tzinfo=#<TZInfo::DataTimezone: Asia/Tokyo>> 
2.6.0 :011 > Time.zone.now
 => Fri, 04 Oct 2019 14:19:30 JST +09:00 

同様に、クラスのcreated_atも、日本時間のオブジェクトを取得できるようになりました。

2.6.0 :018 > User.first.created_at
=> Fri, 04 Oct 2019 02:14:57 UTC +00:00 
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyの標準ライブラリ net/http を使って kintone の REST API を叩いてみる

このところ学習用にkintoneのREST API を色々と叩いているのですが、そういえば大好きなRubyからだってHTTPリクエスト投げれればできそうじゃんね。と思ったんでやってみました。

色々試しながら上手くいったものから、都度追加していくよ。

レコード

レコードの取得(GET)

https://developer.cybozu.io/hc/ja/articles/202331474

get_record.rb
url = "https://#{ENV['SUBDOMAIN']}.cybozu.com/k/v1/record.json"
uri = URI.parse(url)
api_token = ENV['SELECT_TOKEN']
req = Net::HTTP::Get.new(uri.path)
req['X-Cybozu-API-Token'] = api_token
req['Content-Type'] = 'application/json'
req.body = JSON.generate({"app" => "100", "id" => "1"})

Net::HTTP.start(uri.host, uri.port, :use_ssl => true) {|http|
  res = http.request(req)
  case res.code.to_i
  when 200
    pp JSON.parse(res.body)
  else
    pp %Q(#{res.code} #{res.message})
    pp JSON.parse(res.body)
  end
}
get_records.rb
url = "https://#{ENV['SUBDOMAIN']}.cybozu.com/k/v1/records.json"
uri = URI.parse(url)
api_token = ENV['SELECT_TOKEN']
req = Net::HTTP::Get.new(uri.path)
req['X-Cybozu-API-Token'] = api_token
req['Content-Type'] = 'application/json'
fields = ["\$id", "商品名", "金額"]
query = %q(金額 >= "50000")
req.body = JSON.generate({"app" => "100", "fields" => fields, "query" => query, "totalCount" => true})

レコードの一括取得

https://developer.cybozu.io/hc/ja/articles/360029152012

カーソルの作成

get_records_cursor.rb
url = "https://#{ENV['SUBDOMAIN']}.cybozu.com/k/v1/records/cursor.json"
uri = URI.parse(url)
api_token = ENV['SELECT_TOKEN']
req = Net::HTTP::Post.new(uri.path)
req['X-Cybozu-API-Token'] = api_token
req['Content-Type'] = 'application/json'
fields = ["\$id", "商品名", "金額"]
query = %q(金額 >= "50000")
req.body = JSON.generate({"app" => "100", "fields" => fields, "query" => query, "totalCount" => true, "size" => "500"})
cursor = ""
Net::HTTP.start(uri.host, uri.port, :use_ssl => true) {|http|
  res = http.request(req)
  case res.code.to_i
  when 200
    pp JSON.parse(res.body)
    cursor = JSON.parse(res.body)["id"]
    pp cursor
  else
    pp %Q(#{res.code} #{res.message})
    pp JSON.parse(res.body)
  end
}

カーソル範囲のレコード取得

get_records_cursor.rb
req = Net::HTTP::Get.new(uri.path)
req['X-Cybozu-API-Token'] = api_token
req['Content-Type'] = 'application/json'
req.body = JSON.generate({"id" => cursor})
Net::HTTP.start(uri.host, uri.port, :use_ssl => true) {|http|
  res = http.request(req)
  case res.code.to_i
  when 200
    pp JSON.parse(res.body)
  else
    pp %Q(#{res.code} #{res.message})
    pp JSON.parse(res.body)
  end
}

ファイルアップロード

https://developer.cybozu.io/hc/ja/articles/201941824

kintoneの領域にファイルをアップロードします。

ファイルアップロード

file.rb
begin
  upload_file = File.open '1.txt', "r"
  data = [["file", upload_file, {"filename" => "1.txt", "content_type" => "text/plain"}]]
  url = "https://#{ENV['SUBDOMAIN']}.cybozu.com/k/v1/file.json"
  uri = URI.parse(url)
  api_token = ENV['SELECT_TOKEN']
  req = Net::HTTP::Post.new(uri.path)
  req['X-Cybozu-API-Token'] = api_token
  req['Content-Type'] = 'multipart/form-data'
  req.set_form(data, "multipart/form-data")
  pp req  
  Net::HTTP.start(uri.host, uri.port, :use_ssl => true) {|http|
    res = http.request(req)
    res.each_header do |name, val|
      puts "name=#{name}, val=#{val}"
    end
    case res.code.to_i
    when 200
      pp JSON.parse(res.body)
    else
      pp %Q(#{res.code} #{res.message})
      pp JSON.parse(res.body)
    end
  }
rescue => exception
  pp exception
  upload_file.close
end

アップロードしたファイルとレコードの関連付け

https://developer.cybozu.io/hc/ja/articles/201941784

アップロードしたファイルの filekey と kintoneレコードを紐付けします。
レコードの更新APIを利用します。

file_upload.rb
begin
  url = "https://#{ENV['SUBDOMAIN']}.cybozu.com/k/v1/record.json"
  uri = URI.parse(url)
  api_token = ENV['UPDATE_TOKEN']
  req = Net::HTTP::Put.new(uri.path)
  req['X-Cybozu-API-Token'] = api_token
  req['Content-Type'] = 'application/json'

  req.body = JSON.generate({
      "app" => "100",
      "id" => "1",
      "record" => {"file" => {"value" => [{"fileKey"=>"590279cd-6657-419f-8fac-d760a4a6caaa"}]}}
    })
    Net::HTTP.start(uri.host, uri.port, :use_ssl => true) {|http|
    res = http.request(req)
    res.each_header do |name, val|
      puts "name=#{name}, val=#{val}"
    end
    case res.code.to_i
    when 200
      pp JSON.parse(res.body)
    else
      pp %Q(#{res.code} #{res.message})
      pp JSON.parse(res.body)
    end
  }
rescue => exception
  pp exception
end

アプリ

フォームの設定の取得

フィールドの一覧を取得する

https://developer.cybozu.io/hc/ja/articles/204783170#anchor_getform_fields

get_app_fields.rb
require 'rubygems'
require 'bundler/setup'
require 'dotenv/load'
require 'net/http'
require 'uri'
require 'json'
require 'base64'
require 'pp'

url = "https://#{ENV['SUBDOMAIN']}.cybozu.com/k/v1/app/form/fields.json"
uri = URI.parse(url)
pass_auth = Base64.strict_encode64("#{ENV['UID']}:#{ENV['PASS']}")

req = Net::HTTP::Get.new(uri.path)
req['Content-Type'] = 'application/json'
req['X-Cybozu-Authorization'] = pass_auth
req.body = JSON.generate({"app" => "100"})
Net::HTTP.start(uri.host, uri.port, :use_ssl => true) {|http|
  res = http.request(req)
  case res.code.to_i
  when 200
    pp JSON.parse(res.body)
  else
    pp %Q(#{res.code} #{res.message})
    pp JSON.parse(res.body)
  end
}

アプリのプロセス管理の設定の取得

https://developer.cybozu.io/hc/ja/articles/216972946

プロセスにはAPIトークン認証は使えません。代わりにパスワード認証を使います。

status.rb
url = "https://#{ENV['SUBDOMAIN']}.cybozu.com/k/v1/app/status.json"
uri = URI.parse(url)
req = Net::HTTP::Get.new(uri.path)
pass_auth = Base64.strict_encode64("#{ENV['UID']}:#{ENV['PASS']}")
req['X-Cybozu-Authorization'] = pass_auth
req['Content-Type'] = 'application/json'
req.body = JSON.generate({"app" => "100", "lang" => "ja"})

Net::HTTP.start(uri.host, uri.port, :use_ssl => true) {|http|
  res = http.request(req)
  case res.code.to_i
  when 200
    pp JSON.parse(res.body)
  else
    pp %Q(#{res.code} #{res.message})
    pp JSON.parse(res.body)
  end
}
  • クエリ文字列

結果は同じです。

status_url.rb
url = "https://#{ENV['SUBDOMAIN']}.cybozu.com/k/v1/app/status.json?app=100&lang=ja"
uri = URI.parse(url)
pass_auth = Base64.strict_encode64("#{ENV['UID']}:#{ENV['PASS']}")

Net::HTTP.start(uri.host, uri.port, :use_ssl => true) {|http|
  res = http.get("#{uri.path}?#{uri.query}", {'X-Cybozu-Authorization' => pass_auth})
  res.each_header do |name, val|
    puts "name=#{name}, val=#{val}"
  end
  case res.code.to_i
  when 200
    pp JSON.parse(res.body)
  else
    pp %Q(#{res.code} #{res.message})
    pp JSON.parse(res.body)
  end
}

スペース情報の取得

https://developer.cybozu.io/hc/ja/articles/202166200

スペース情報の取得はAPIトークン認証が使えません。
代わりにパスワード認証を使ってください。

space.rb
url = "https://#{ENV['SUBDOMAIN']}.cybozu.com/k/v1/space.json"
uri = URI.parse(url)
req = Net::HTTP::Get.new(uri.path)
pass_auth = Base64.strict_encode64("#{ENV['UID']}:#{ENV['PASS']}")
req['X-Cybozu-Authorization'] = pass_auth
req['Content-Type'] = 'application/json'
req.body = JSON.generate({"id" => "1"})

Net::HTTP.start(uri.host, uri.port, :use_ssl => true) {|http|
  res = http.request(req)
  case res.code.to_i
  when 200
    pp JSON.parse(res.body)
  else
    pp %Q(#{res.code} #{res.message})
    pp JSON.parse(res.body)
  end
}

API情報

API 一覧の取得

https://developer.cybozu.io/hc/ja/articles/201941934

認証情報は必要ありません。

apis.rb
url = "https://#{ENV['SUBDOMAIN']}.cybozu.com/k/v1/apis.json"
uri = URI.parse(url)
req = Net::HTTP::Get.new(uri.path)

Net::HTTP.start(uri.host, uri.port, :use_ssl => true) {|http|
  res = http.request(req)
  case res.code.to_i
  when 200
    pp JSON.parse(res.body)
  else
    pp %Q(#{res.code} #{res.message})
    pp JSON.parse(res.body)
  end
}

環境

  • macOS 10.13.6
  • ruby 2.5.0

リンク

雑記

やってみたら、まあ普通にできた。
やっぱりRubyいいな。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RailsアプリにRSpecを導入する手順

はじめに

RSpecはRubyのテストフレームワークです。これ以外にMinitestというものがありますが、実際の現場では、RSpecが使われることが多いようなので導入する手順をメモしておきます。

環境
Widows 10
cloud9

RSpecをインストール

gemを追加
最近はsystemスペックの利用が推奨されています。systemスペックは、バージョンが3.8.0以上である必要があるためそれをインストールしましょう。

Gemfile
group :development, :test do
 gem 'rspec-rails' # バージョン指定する必要があるかも'~> 3.8.0' 
end
$ bundle install

RSpecをインストールする

$ rails generate rspec:install

必須ではないが、RSpec の出力をデフォルトの形式から読みやすいドキュメント形式に変更できる。やっといたほうが見た目が良くなるのでおすすめです。

.rspec
--require spec_helper
--format documentation

テストを速くする
このgemを追加するとSpringにより、テストスイートが速くなる。必要だと思ったら入れてください。

Gemfile
group :development do
  gem 'spring-commands-rspec'
end

# bundle installを忘れずに!

そしたら、以下のコマンドを実行して下さい。

$ bundle exec spring binstub rspec

そしてこれからは、RSpecのコマンドを打つ際先頭に、bin/をつけるようにしましょう。

ジェネレータ
rails g する際に一緒にスペックファイルも生成されるようにする。また、あまり使うことのないファイルは生成されないようにします。コードが複雑になってきたら使うかもしれないので、その時は削除していってください。

config/application.rb
.
.
module Sample
  class Application < Rails::Application

     config.generators do |g|
       g.test_framework :rspec,
         fixtures: false, # factory_bot_railsを使うなら削除してください
         view_specs: false,
         helper_specs: false,
         routing_specs: false
    end

  end
end

これで基本的なセットアップは終了です!

便利なgem

ここからはRSpecを使っていく際に便利なgemを紹介します。最新のものを使うようにしましょう。

Gemfile
group :development, :test do
  gem 'factory_bot_rails' # テストのサンプルデータを生成できる
end

group :test do
  gem 'capybara' #統合テスト(system)をするのに必要
  gem 'launchy' # save_and_open_page をスペック内で呼びだしたときに、HTMLを自動的に開く
  gem 'webdrivers' # js付きののテストができるようになる。
end

Capybaraのセットアップ
Capybaraを読み込めるように、requireする。

spec/rails_helper.rb
# その他の設定
require 'capybara/rspec'

Capybaraで、JavaScriptを使ったテスト

1 設定ファイルを書く
rails_helper.rbに直接書いてもいいが、今後設定が多くなってくると見にくくなるので、独立したファイルに設定を書いていきます。

spec/rails_helper.rb
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } #このコメントアウトを外す。

これで、supportディレクトリ下にあるファイルを読み込めるようになる。このディレクトリを作り、そこにcapybara.rbというファイルを生成し、設定を書きます。

spec/support/capybara.rb
Capybara.javascript_driver = :selenium_chrome
or
Capybara.javascript_driver = :selenium_chrome_headless # ヘッドレスモードを使いたい場合

2 gemを追加
webdriversは、selenium-webdriver も一緒にインストールしてくれるので、selenium-webdriver がある場合は削除しといてください。

Gemfile
gem 'webdrivers'

これで、js: trueオプションをつけることで、JavaScript付きのテストができる。

cloud9でヘッドレスモードを使えるようにする
僕は今、cloud9を使っているので、ここで使えるようにしていきます。cloud9では、ヘッドレスモードしかできないみたいです。

1 Chromeブラウザをインストール
初期のAWSは、Chromeがインストールされてないのでインストールする。

$ curl https://intoli.com/install-google-chrome.sh | bash

2 ヘッドレスモードにする

spec/support/capybara.rb
Capybara.javascript_driver = :selenium_chrome_headless

3 selenium-webdriverとcapybaraを最新版にする

$ bundle update selenium-webdriver capybara

Deviseのヘルパーメソッドを使えるようにする

これを記述することで、コントローラー、システム、リクエストスペックでDeviseのヘルパーメソッドである、sign_inが使えるようになる。
また、リクエストスペックの場合、Deviseのヘルパーメソッド使うために、少し設定が必要。

spec/rails_helper.rb
# その他の設定
RSpec.configure do |config|
  # その他の設定
  config.include Devise::Test::ControllerHelpers, type: :controller 
  config.include Devise::Test::IntegrationHelpers, type: :system
  config.include RequestSpecHelper, type: :request
end

リクエストスペックの追加設定
新しいファイルを作って設定を記述。

spec/support/request_spec_helper.rb
module RequestSpecHelper
  include Warden::Test::Helpers

  def self.included(base)
    base.before(:each) { Warden.test_mode! }
    base.after(:each) { Warden.test_reset! }
  end

  def sign_in(resource)
    login_as(resource, scope: warden_scope(resource))
  end

  def sign_out(resource)
    logout(warden_scope(resource))
  end

 private

  def warden_scope(resource)
    resource.class.name.underscore.to_sym
  end
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[初学者]よく起こるエラー〜ActiveRecord::PendingMigrationError 〜

目的

学習の備忘録と初学者の参考資料として投稿

ActiveRecord::PendingMigrationErrorというエラー

MVCの一角であるモデルを作成するため、ターミナルで・・・・

<ターミナル>

$ rails g model ○○○○(モデル名)

・・・ちょっくら確認してみるか

$ rails s

エラー発生!!!!

ActiveRecord::PendingMigrationError〜、あちゃ〜:persevere:

忘れてた:bulb:

解決方法

<ターミナル>

$ rails db:migrate

>>解決!!

そもそもマイグレーションとは

SQLを書くことなくRubyでデータベース内にテーブルを作成することができる機能。わざわざ面倒くさい事をマイグレーションファイルが行ってくれている。なので絶対忘れずにマイグレーションしよう!!

[参考]ちなみによく見る『schema.rb』とは

マイグレーションした際に作成されるファイルで、テーブルのカラムやそのデータ型などマイグレーションした結果が書かれている。マイグレーションするたびに最新の状態へ更新。実際ファイルを見てみるといままでのマイグレーションの履歴が見れる。

まとめ

モデルを作成したら、すぐにマイグレーション実行しよう!!

ちなみにマイグレーションファイルを勝手に削除すると大変な事に・・・・・:scream:

消さないでね:u7981:

今後も学習で気づきや参考になるものがあれば、アップしていきます。
もし参考になったらいいね!!よろしくお願いします:bow_tone1:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails console で日付操作をするときのメモ

データの日付情報を変更したいときの備忘録。

準備

まずはじめにdateという情報を持ってるHogeテーブルがあるとする。
何度も入力するのが手間なので、変更したいdateが入ってるidを持ったHogeを指定し、変数に格納します。

hoge = Hoge.find(1234)

agoとsinceを使う

agoは◯日前、sinceは◯日後、というふうになります。ただしこの場合、「今この瞬間から2日前」というふうになるので注意しないとだめです。

hoge.update(date: 2.days.ago)
hoge.update(date: 2.days.since)

おわりに

よくつかうのでメモに残しておきました⭐

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails6 のちょい足しな新機能を試す89(Event object編)

はじめに

Rails 6 に追加された新機能を試す第89段。 今回は、 Event object 編です。
Rails 6 では、ActiveSupport::Notifications.subscribe で、ブロックパラメータを1つにすると、そのブロックパラメータに ActiveSupport::Notifications::Event オブジェクトが設定されるようになりました。
これで、ブロックの中で ActiveSupport::Notifications::Event.new を使う必要がなくなりました。

Ruby 2.6.4, Rails 6.0.0 で確認しました。

$ rails --version
Rails 6.0.0

今回は、User の CRUD を作り、各コントローラのアクションを実行したときの情報を取得してログに出力してみます。

プロジェクトを作る

rails new rails_sandbox
cd rails_sandbox

User の CRUD を作る

name をもつ User の CRUD を作ります

bin/rails g scaffold User name

Subscribe する

config/initializers/notification_subscriber.rb を作成します。
ActiveSupport::Notifications.subscribe を呼び出すときに、ブロックパラメータを event 1つだけにします。

config/initializers/notification_subscriber.rb
ActiveSupport::Notifications.subscribe 'process_action.action_controller' do |event|
  action = "#{event.payload[:controller]}##{event.payload[:action]}"
  Rails.logger.info("#{action} cpu_time=#{event.cpu_time} duration=#{event.duration} allocations=#{event.allocations}")
end

User の登録などをしてログを確認する

実際に rails server を実行してブラウザから http://localhost:3000/users にアクセスします。

以下のようにコンソールに出力されているはずです。

...
UsersController#index cpu_time=833.013591 duration=840.9244199865498 allocations=1237733

試したソース

試したソースは以下にあります。
https://github.com/suketa/rails_sandbox/tree/try089_subscriber_event

参考情報

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ゲストログイン機能の実装方法(ポートフォリオ用)

ポートフォリオ用のWebサイトに必須と言われるゲストログイン機能

特にDeviseconfirmableを使用している場合は,ゲストログイン機能が無いと,新規登録が面倒という理由でポートフォリオを見てすらもらえない可能性が高くなります。

ところが,普通のWebサイトには絶対に導入しないためか,記事が見当たりませんでした。そこで,私のたどりついた実装方法を備忘録としてアップしておきます。

ゲストログイン機能の実装方法

以下,Deviseを使用している前提とします。

config/routes.rb
  post '/homes/guest_sign_in', to: 'homes#guest_sign_in'
app/controllers/homes_controller.rb
class HomesController < ApplicationController
  def guest_sign_in
    user = User.find_or_create_by(email: 'guest@example.com') do |user|
      user.password = SecureRandom.urlsafe_base64
      user.confirmed_at = Time.now  # Devise の confirmable を使用している場合は必須
    end
    sign_in user
    redirect_to root_path, notice: 'ゲストユーザーとしてログインしました。'
  end
end
app/views/homes/index.html.erb
<%= link_to 'ゲストログイン(閲覧用)', homes_guest_sign_in_path, method: :post %>
  • ポイントはDevisesign_inメソッドを利用することです。

  • ゲストユーザーをあらかじめ作成する手間を省くため,find_byではなく,find_or_create_byを利用しています。パスワードを把握する必要はないので,ランダム文字列にしています。

備考

  • メールアドレス,パスワードを変更できるように設定する場合は,ゲストユーザーのメールアドレスなどを変更されないように注意する必要があります。

  • 実は,最初,devise/sessions/new.html.erbの真似をして,hidden_fieldを加えて……という手順で実装しようとしました。ところが,この方法ではuser_email, user_passwordなどのidセレクタが重複するエラーが出てしまいます。そこで,sign_inメソッドの存在を思い出し,このような実装方法にたどりつきました。

おまけ

ゲストユーザーのデータを定期的に初期化するため,実際にはこのような形式で実装しました。
nameカラムはニックネーム,content自己紹介文の想定です。

app/controllers/homes_controller.rb
class HomesController < ApplicationController
  def guest_sign_in
    new_user_check = false
    user = User.find_or_create_by(email: 'guest@example.com') do |user|
      user.password = SecureRandom.urlsafe_base64
      user.confirmed_at = Time.now
      new_user_check = true
    end
    # 新規ユーザー作成時と,前回のゲストログインor更新から30分以上経過している場合はデータを初期化
    data_initialization(user) if new_user_check || Time.now - user.updated_at > 1800
    sign_in user
    redirect_to root_path, notice: 'ゲストユーザーとしてログインしました。'
  end

  private

  def data_initialization(user)
    user_params = {
      # ゲストログイン時にデータを初期化するかどうか判断できるようにupdated_atを更新
      updated_at: Time.now,
      name: 'ゲスト',
      content: 'ゲストユーザーの各種データは30分後に初期化されます。ご了承下さい。',
    }
    user.update(user_params)
  end
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby の fork と spawn を試す

/proc/{PID}/ 配下の情報について Ruby で fork/spawn をした時の違いを確認した時のメモ
(あくまで個人用のメモです)

環境

  • OS:amzn2-ami-hvm-2.0.20180810-x86_64-gp2 (ami-08847abae18baa040)
$sudo amazon-linux-extras install ruby

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]

/proc{PID} 配下の情報について

以下にまとまった情報あり

E.3. /proc/ 内のディレクトリ

  • /proc/配下には様々な情報があるが、/proc/{PID} という形式でプロセスが生成されるとディレクトリが作成され、配下にプロセスに関わる情報が格納されている
  • {PID} = プロセス ID。プロセスを生成すると生成される ID
  • プロセスが終了すると、そのプロセスの /proc/ プロセスディレクトリはなくなる

なお、今回確認したかった cmdline はい以下のように書いてある

cmdline — プロセスを起動した際に発行されたコマンドが格納されています。

fork してみる

module function Kernel.#fork

fork(2) システムコールを使ってプロセスの複製を作 ります。親プロセスでは子プロセスのプロセスIDを、子プロセスでは nil を返します。ブロックを指定して呼び出した場合には、生成し た子プロセスでブロックを評価します。

内部的には fork(2)のシステムコールを呼び出しているようであり、確認。
上記のサンプルコードを少し修正。

fork.rb
child_pid = fork do
  puts "child process. pid: #{Process.pid}"
  sleep(1)
end

puts "parent process. pid: #{Process.pid}, child pid: #{child_pid}"
r = `cat /proc/#{child_pid}/cmdline`
puts r

Process.waitpid(child_pid)

実行すると以下のような感じ

$ruby fork.rb
parent process. pid: 2794, child pid: 2795
child process. pid: 2795
rubyfork.rb

/proc/{PID}/cmdlinerubyfork.rbとなっている。
上記のコードの場合、処理しているのは sleep だが、実行しているのは Ruby のコードという為だと思われる(違ったらご指摘下さい)

spawn してみる

module Process

関数 Kernel.#spawn と同じです。

module function Kernel.#spawn

引数を外部コマンドとして実行しますが、生成した 子プロセスの終了を待ち合わせません。生成した子プロセスのプロセスIDを返します。

こっちの場合どうなるのか。

spawn.rb
child_pid = spawn({"FOO"=>"BAR", "BAZ"=>nil}, 'sleep 10')
puts "parent process. pid: #{Process.pid}, child pid: #{child_pid}"
r = `cat /proc/#{child_pid}/cmdline`
puts r

Process.waitpid(child_pid)
$ruby spawn.rb
parent process. pid: 2894, child pid: 2895
sleep10

こうなる。
こっちの場合、spawn(env,command)でよばれて command が cmdline に指定されているという点が違う。
親プロセスID と生成された子プロセスIDが違うのは同じ。

spawn は Linux のコマンドとして存在する。
一方、 fork はシステムコールという所が違うのだろうか。

TODO

以下辺りの情報など関連する情報を集めてもう少しまとめたい。。。
(fork,spawn,thread...)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsでプロジェクトを丸々コピーする

この記事でやること

  • railsで作ったプロジェクトを丸々コピーする

手順

  • githubからレポジトリのクローン
  • dbの作成(rails db:migrate)
  • configに含まれているapplication.rbとroutes.rbのアプリ名の変更
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rubyのselenium-webdriverで同じclass名の要素を取る!

ど素人がrubyのselenium-webdriverを使って七転八倒した記録です。
ただの忘れやすい個人の備忘録です。

selenium-webdriver 3.142.2を使ってます。

同じclass名の要素を取ろうとするとき、
driver.find_element(:class, 'クラス名').text
では、最初の要素しか取れない。

https://www.rubydoc.info/gems/selenium-webdriver/0.0.28/Selenium/WebDriver/Find

にあるように
driver.find_elements(:class, 'クラス名').text
なら引数が配列で複数取れる。

これに数日かかった模様w

xpathやcssとかでも取れるけど、タグは模様替えで簡単に変わってしまうのでできるだけ避けたかった

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rubyのselenium-webdriverで同じclass名の要素を取る!class名の中の空白も

ど素人がrubyのselenium-webdriverを使って七転八倒した記録です。
ただの忘れやすい個人の備忘録です。生暖かい目でご覧ください。
アドバイスとかあればよろしくお願いいたします。
selenium-webdriver 3.142.2を使ってます。

同じclass名の要素を取ろうとするとき、
driver.find_element(:class, 'クラス名').text
では、最初の要素しか取れない。

https://www.rubydoc.info/gems/selenium-webdriver/0.0.28/Selenium/WebDriver/Find

にあるように
driver.find_elements(:class, 'クラス名').text
なら戻り値が配列で取れる。

これに数日かかった模様w

xpathやcssとかでも取れるけど、タグは模様替えで簡単に変わってしまうのでできるだけ避けたかった

もう一つ。class名に空白がある場合エラーがでる。
driver.find_element(:class, '空白までのクラス名').find_element(:class, '空白後のクラス名').text

↑のようにメソッドチェーンで分けるとうまく行く

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RailsチュートリアルエラーExpected response to be a <3XX: redirect>, but was a <200: OK>

エラー発生!

Railsチュートリアルも13章に差し掛かり、早く終わりたいと取り組んでいると
エラーが重なり迷走していると、またエラーが!

FAIL["test_should_not_allow_the_not_activated_attribute", UsersControllerTest, 1.5222024869999586]
 test_should_not_allow_the_not_activated_attribute#UsersControllerTest (1.52s)
        Expected response to be a <3XX: redirect>, but was a <200: OK>
        test/controllers/users_controller_test.rb:66:in `block in <class:UsersControllerTest>'
users_contrloller_test.rb
require 'test_helper'

class UsersControllerTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:michael)
    @other_user = users(:archer)
    #11.3演習テスト
    @non_activated_user = users(:non_activated)
  end

  test "should redirect index when not logged in" do
    get users_path
    assert_redirected_to login_url
  end

  test "should get new" do
    get signup_path
    assert_response :success
  end

  test "should redirect edit when not logged in" do
    get edit_user_path(@user)
    assert_not flash.empty?
    assert_redirected_to login_url
  end

  test "should redirect update when not logged in" do
    patch user_path(@user), params: { user: { name: @user.name,
                                              email: @user.email } }
    assert_not flash.empty?
    assert_redirected_to login_url
  end

  test "should not allow the admin attribute to be edited via the web" do
    log_in_as(@other_user)
    assert_not @other_user.admin?
    patch user_path(@other_user), params: { user: { password:              @other_user.password,
                                                    password_confirmation: @other_user.password_confirmation,
                                                    admin: true } }
    assert_not @other_user.reload.admin?
  end

  test "should redirect destroy when not logged in" do
    assert_no_difference 'User.count' do
      delete user_path(@user)
    end
    assert_redirected_to login_url
  end

  test "should redirect destroy when logged in as a non-admin" do
    log_in_as(@other_user)
    assert_no_difference 'User.count' do
      delete user_path(@user)
    end
    assert_redirected_to root_url
  end

  #11.3演習テスト https://www.yokoyan.net/entry/2017/09/14/070000
  test "should not allow the not activated attribute" do
    log_in_as(@non_activated_user)
    assert_not @non_activated_user.activated?
    get users_path
    assert_select "a[href=?]", user_path(@non_activated_user), count: 0
    get user_path(@non_activated_user)
    assert_redirected_to root_url #<=ここがエラー
  end
end

assert_redirected_to root_url が問題のようだ
users_controller.rbを見てみると…
redirect_to root_url and return unless @user.activated?
が抜けてました!

users_controller.rb
class UsersController < ApplicationController
  before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
  before_action :correct_user,   only: [:edit, :update]
  before_action :admin_user,     only: :destroy

  def index
    @users = User.where(activated: true).paginate(page: params[:page])
  end

  def show
    @user = User.find(params[:id])
    redirect_to root_url and return unless @user.activated?
    @microposts = @user.microposts.paginate(page: params[:page]) #13.23
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
      @user.send_activation_email
      flash[:info] = "Please check your email to activate your account."
      redirect_to root_url
    else
      render 'new'
    end
  end

  def edit
    @user = User.find(params[:id])
  end

  def update
    @user = User.find(params[:id])
    if @user.update_attributes(user_params)
      flash[:success] = "Profile updated"
      redirect_to @user
    else
      render 'edit'
    end
  end

  def destroy
    User.find(params[:id]).destroy
    flash[:success] = "User deleted"
    redirect_to users_url
  end

  private

    def user_params
      params.require(:user).permit(:name, :email, :password,
                                   :password_confirmation)
    end

    # 正しいユーザーかどうか確認
    def correct_user
      @user = User.find(params[:id])
      redirect_to(root_url) unless current_user?(@user)
    end

    # 管理者かどうか確認
    def admin_user
      redirect_to(root_url) unless current_user.admin?
    end
end

そうだよね、redirect_to root_urlってテストに書いているのに、コードに無いと困るよねRailsさん。ごめんなさいそしてありがとう( ^∀^)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む