20191127のRubyに関する記事は18件です。

1-100までのカードをn-1枚置きに裏返す(Ruby)

1-100までの数字が書かれたカードが裏返しで横に並んでいます。(1, 2, 3, 4, 5... 99, 100)
①初めは2番目のカードから1枚置きにカードを裏返して行きます。
②次に3番目のカードから2枚置きにカードを裏返して行きます。
③次に4番目のカードから3枚置きにカードを裏返して行きます。



このようにn番目のカードからn-1枚置きにカードを裏返していって、これ以上カードの向きが変わらなくなるまで続けます。

その時に裏返っているカードは何番かを調べていきます。

考え方

カードを左から順に裏返す処理を書いていきます。カードが表か裏かはTrue(表), またはFalse(裏)で表すことが出来ます。

まずはカードを100枚用意します。

qiita.rb
n = 100
cards = Array.new(n, false)

配列はArray.newで作成することが出来ます。
また、その時に第2引数を指定すると、新しい配列をそのオブジェクトで埋めます。
ここでは100枚全てfalse(裏)にして配列を作成しました。

次にカードを裏返して行きます。

qiita.rb
(2..n).each do |i|
  j = i - 1
  while j < cards.length do
    cards[j] = !cards[j]
    j += i
  end
end

初めは2番目のカードから裏返していくので、(2..n)として2番目のカードから順番に処理をしています。
また、n-1枚置きにカードを裏返すために、jにn−1の値を格納します。
そうしたら、whileのループの中で、n-1枚置きにカードを裏返す処理を行います。

最後に裏返しのカードの番号を表示します。

qiita.rb
no = 1
cards.each do |i|
  puts no if !i
  no += 1
end

初めのカードから順番に、false(裏)だったらその番号(no)をputsしています。

答え

qiita.rb
1
4
9
16
25
36
49
64
81
100

ちなみに

カードの番号とその裏表の状態を同時にハッシュで保持出来るともっと簡単に記述出来るかもしれないと思ったり。。。

qiita.rb
n = 100
cards = Array.new(n, false)

(2..n).each do |i|
  j = i - 1
  while j < cards.length do
    cards[j] = !cards[j]
    j += i
  end
end

no = 1
cards.each do |i|
  puts no if !i
  no += 1
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IBM Watson "Personality Insights" を利用した性格分析(夏目漱石「こころ」を例に)

IBM Watson とは

日々の業務から生まれるデータをナレッジに変え、業務プロセスに組み込んで活用することで、プロセスの効率化や高付加価値化を実現できます。たとえば、人間には読みきれないような大量のデータの中からすばやく知見や洞察を見出したり、顧客に対してさまざまな場面で一貫した質の高い応対をすることを可能にします。また、情報と知見に基づく意思決定を支援したり、ビジネスのさまざまな業務や場面であなたをサポートします。
(引用 - IBM Watsonとは? - Japan)
https://www.ibm.com/watson/jp-ja/what-is-watson.html

  • クレジットカードを登録せずに利用可能

Personality Insights とは

  • IBM Watsonが提供しているAPIの1つ。
  • メール・ツイート・メッセージを分析して、個人のパーソナリティ特性を推論

パーソナリティ特性
ビッグ・ファイブ(big5)
- 個人が世界とどのように関与しているかを示す
- 協調性、誠実性、外向性、情緒不安定性、開放性の5つディメンションを含む。
- 各ディメンションには、そのディメンションに従って個人をさらに特徴付ける 6 つのファセットがある。

ニーズ(needs)
- 個人の共感を呼ぶ可能性がある商品の側面を説明するもの。
- 興奮、調和、好奇心、理想、親近感、自己表現、自由、愛、実用性、安定性、挑戦、構造の12個の特徴のニーズがある。

価値(value)
- 個人の意思決定に影響を与える動機を説明するもの。
- 自己超越/人助け、不変/伝統、快楽主義/人生を楽しむ、自己高揚/成功、変化に対して抵抗感を持たない/興奮 の5つを含む

上記内容は、製品情報 - IBM Watson Personality Insightsに詳細があります。

今回挑戦したこと

  • htmlファイルをPersonality Insightsで分析し、その著者の性格を分析する
  • CURLコマンドで、叩く
  • 分析結果(json)をデスクトップに出力・保存する
  • 保存した分析結果を、Rubyを使ってハッシュ化
  • ハッシュ化した結果を整理

実行環境

項目
PC MacBook Air
ブラウザ Google Chrome

前提条件

  • IBM Cloud のアカウントを持っている

今回の分析対象とする文章

青空文庫 - 夏目漱石「こころ」

青空文庫で公開されているページ(html)を、デスクトップに保存する。

保存する名前:kokoro.html

デスクトップに移動し、デスクトップにあるファイルを確認
cd desktop
ls
上記コマンド実行結果
kokoro.html

実際にやってみよう

下記のコマンドを実行する。

Watosonに分析させるコマンド
curl -X POST -u "apikey:YOUR_IBM_WATSON_API_KEY" \
--header "Content-Type: text/plain;charset=utf-8" \
--header "Accept: application/json" \
--data-binary @/User/YOUR_USER_NAME/Desktop/kokoro.html \
"https://gateway.watsonplatform.net/personality-insights/api/v3/profile?version=2017-10-13"


YOUR_IBM_WATSON_API_KEY にはAPIキーを
YOUR_USER_NAME にはユーザー名を
それぞれ代入する。

分析結果

デスクトップに kokoro.json が保存されていることを確認する。

このjsonファイルには、パーソナリティーモデルに基づいた分析結果が格納される。

command
cd desktop
ls
出力結果
kokoro.json
kokoro.json
{"word_count":20314,"processed_language":"en","personality":[{"trait_id":"big5_openness","name":"Openness","category":"personality","percentile":0.4190447483892409,"significant":true,"children":[{"trait_id":"facet_adventurousness","name":"Adventurousness","category":"personality","percentile":0.32241762665507223,"significant":true},{"trait_id":"facet_artistic_interests","name":"Artistic interests","category":"personality","percentile":0.5521516749265605,"significant":true},{"trait_id":"facet_emotionality","name":"Emotionality","category":"personality","percentile":0.5132346262424734,"significant":true},{"trait_id":"facet_imagination","name":"Imagination","category":"personality","percentile":0.607365647158204,"significant":true},{"trait_id":"facet_intellect","name":"Intellect","category":"personality","percentile":0.7578837755541521,"significant":true},{"trait_id":"facet_liberalism","name":"Authority-challenging","category":"personality","percentile":0.8196241048763219,"significant":true}]},{"trait_id":"big5_conscientiousness","name":"Conscientiousness","category":"personality","percentile":0.22525820846421102,"significant":true,"children":[{"trait_id":"facet_achievement_striving","name":"Achievement striving","category":"personality","percentile":0.24103760626726628,"significant":true},{"trait_id":"facet_cautiousness","name":"Cautiousness","category":"personality","percentile":0.3967189883611717,"significant":true},{"trait_id":"facet_dutifulness","name":"Dutifulness","category":"personality","percentile":0.24845542781829882,"significant":true},{"trait_id":"facet_orderliness","name":"Orderliness","category":"personality","percentile":0.207879302126662,"significant":true},{"trait_id":"facet_self_discipline","name":"Self-discipline","category":"personality","percentile":0.19535876642618683,"significant":true},{"trait_id":"facet_self_efficacy","name":"Self-efficacy","category":"personality","percentile":0.21390998519773624,"significant":true}]},{"trait_id":"big5_extraversion","name":"Extraversion","category":"personality","percentile":0.34971430412544213,"significant":true,"children":[{"trait_id":"facet_activity_level","name":"Activity level","category":"personality","percentile":0.33897831044712423,"significant":true},{"trait_id":"facet_assertiveness","name":"Assertiveness","category":"personality","percentile":0.11746700558582457,"significant":true},{"trait_id":"facet_cheerfulness","name":"Cheerfulness","category":"personality","percentile":0.22972256751949427,"significant":true},{"trait_id":"facet_excitement_seeking","name":"Excitement-seeking","category":"personality","percentile":0.2357839101981965,"significant":true},{"trait_id":"facet_friendliness","name":"Outgoing","category":"personality","percentile":0.29853824637994103,"significant":true},{"trait_id":"facet_gregariousness","name":"Gregariousness","category":"personality","percentile":0.3643146929286043,"significant":true}]},{"trait_id":"big5_agreeableness","name":"Agreeableness","category":"personality","percentile":0.27663077925440716,"significant":true,"children":[{"trait_id":"facet_altruism","name":"Altruism","category":"personality","percentile":0.4020555962581259,"significant":true},{"trait_id":"facet_cooperation","name":"Cooperation","category":"personality","percentile":0.3603843552650475,"significant":true},{"trait_id":"facet_modesty","name":"Modesty","category":"personality","percentile":0.582131278592358,"significant":true},{"trait_id":"facet_morality","name":"Uncompromising","category":"personality","percentile":0.34577537905073386,"significant":true},{"trait_id":"facet_sympathy","name":"Sympathy","category":"personality","percentile":0.608321787997034,"significant":true},{"trait_id":"facet_trust","name":"Trust","category":"personality","percentile":0.1107532137251554,"significant":true}]},{"trait_id":"big5_neuroticism","name":"Emotional range","category":"personality","percentile":0.7558550985505927,"significant":true,"children":[{"trait_id":"facet_anger","name":"Fiery","category":"personality","percentile":0.5983433110620369,"significant":true},{"trait_id":"facet_anxiety","name":"Prone to worry","category":"personality","percentile":0.7269479416509153,"significant":true},{"trait_id":"facet_depression","name":"Melancholy","category":"personality","percentile":0.8142313580352654,"significant":true},{"trait_id":"facet_immoderation","name":"Immoderation","category":"personality","percentile":0.5798042693808653,"significant":true},{"trait_id":"facet_self_consciousness","name":"Self-consciousness","category":"personality","percentile":0.7577882331656556,"significant":true},{"trait_id":"facet_vulnerability","name":"Susceptible to stress","category":"personality","percentile":0.7631343989629844,"significant":true}]}],"needs":[{"trait_id":"need_challenge","name":"Challenge","category":"needs","percentile":0.29712717312254844,"significant":true},{"trait_id":"need_closeness","name":"Closeness","category":"needs","percentile":0.33880584971327,"significant":true},{"trait_id":"need_curiosity","name":"Curiosity","category":"needs","percentile":0.7000224484452974,"significant":true},{"trait_id":"need_excitement","name":"Excitement","category":"needs","percentile":0.38269688677373603,"significant":true},{"trait_id":"need_harmony","name":"Harmony","category":"needs","percentile":0.2638918250639486,"significant":true},{"trait_id":"need_ideal","name":"Ideal","category":"needs","percentile":0.3202722066143936,"significant":true},{"trait_id":"need_liberty","name":"Liberty","category":"needs","percentile":0.44282382289912187,"significant":true},{"trait_id":"need_love","name":"Love","category":"needs","percentile":0.3958365238399758,"significant":true},{"trait_id":"need_practicality","name":"Practicality","category":"needs","percentile":0.8563266321622546,"significant":true},{"trait_id":"need_self_expression","name":"Self-expression","category":"needs","percentile":0.5737335986056481,"significant":true},{"trait_id":"need_stability","name":"Stability","category":"needs","percentile":0.22173366620030177,"significant":true},{"trait_id":"need_structure","name":"Structure","category":"needs","percentile":0.4504728693366197,"significant":true}],"values":[{"trait_id":"value_conservation","name":"Conservation","category":"values","percentile":0.18307614327169447,"significant":true},{"trait_id":"value_openness_to_change","name":"Openness to change","category":"values","percentile":0.46170119182941183,"significant":true},{"trait_id":"value_hedonism","name":"Hedonism","category":"values","percentile":0.48338908358801075,"significant":true},{"trait_id":"value_self_enhancement","name":"Self-enhancement","category":"values","percentile":0.37303733233452196,"significant":true},{"trait_id":"value_self_transcendence","name":"Self-transcendence","category":"values","percentile":0.18354711061030066,"significant":true}],"warnings":[{"warning_id":"CONTENT_TRUNCATED","message":"For maximum accuracy while also optimizing processing time, only the first 250KB of input text (excluding markup) was analyzed. Accuracy levels off at approximately 3,000 words so this did not affect the accuracy of the profile."}]}
出力したjsonをハッシュに整形するコード(Ruby)
require "json"

File.open("kokoro.json") do |json|
    hash = JSON.load(json)

    puts ""
    puts "------big5------"
    puts "Openness"
    puts hash["personality"][0]["children"]
    puts ""
    puts "Conscientiousness"
    puts hash["personality"][1]["children"]
    puts ""
    puts "Extraversion"
    puts hash["personality"][2]["children"]
    puts ""
    puts "Agreeableness"
    puts hash["personality"][3]["children"]
    puts ""
    puts "Emotional range"
    puts hash["personality"][4]["children"]
    puts ""
    puts "------values------"
    puts hash["values"]
end
整形するコードを実行した結果
------big5------
Openness
{"trait_id"=>"facet_adventurousness", "name"=>"Adventurousness", "category"=>"personality", "percentile"=>0.32241762665507223, "significant"=>true}
{"trait_id"=>"facet_artistic_interests", "name"=>"Artistic interests", "category"=>"personality", "percentile"=>0.5521516749265605, "significant"=>true}
{"trait_id"=>"facet_emotionality", "name"=>"Emotionality", "category"=>"personality", "percentile"=>0.5132346262424734, "significant"=>true}
{"trait_id"=>"facet_imagination", "name"=>"Imagination", "category"=>"personality", "percentile"=>0.607365647158204, "significant"=>true}
{"trait_id"=>"facet_intellect", "name"=>"Intellect", "category"=>"personality", "percentile"=>0.7578837755541521, "significant"=>true}
{"trait_id"=>"facet_liberalism", "name"=>"Authority-challenging", "category"=>"personality", "percentile"=>0.8196241048763219, "significant"=>true}

Conscientiousness
{"trait_id"=>"facet_achievement_striving", "name"=>"Achievement striving", "category"=>"personality", "percentile"=>0.24103760626726628, "significant"=>true}
{"trait_id"=>"facet_cautiousness", "name"=>"Cautiousness", "category"=>"personality", "percentile"=>0.3967189883611717, "significant"=>true}
{"trait_id"=>"facet_dutifulness", "name"=>"Dutifulness", "category"=>"personality", "percentile"=>0.24845542781829882, "significant"=>true}
{"trait_id"=>"facet_orderliness", "name"=>"Orderliness", "category"=>"personality", "percentile"=>0.207879302126662, "significant"=>true}
{"trait_id"=>"facet_self_discipline", "name"=>"Self-discipline", "category"=>"personality", "percentile"=>0.19535876642618683, "significant"=>true}
{"trait_id"=>"facet_self_efficacy", "name"=>"Self-efficacy", "category"=>"personality", "percentile"=>0.21390998519773624, "significant"=>true}

Extraversion
{"trait_id"=>"facet_activity_level", "name"=>"Activity level", "category"=>"personality", "percentile"=>0.33897831044712423, "significant"=>true}
{"trait_id"=>"facet_assertiveness", "name"=>"Assertiveness", "category"=>"personality", "percentile"=>0.11746700558582457, "significant"=>true}
{"trait_id"=>"facet_cheerfulness", "name"=>"Cheerfulness", "category"=>"personality", "percentile"=>0.22972256751949427, "significant"=>true}
{"trait_id"=>"facet_excitement_seeking", "name"=>"Excitement-seeking", "category"=>"personality", "percentile"=>0.2357839101981965, "significant"=>true}
{"trait_id"=>"facet_friendliness", "name"=>"Outgoing", "category"=>"personality", "percentile"=>0.29853824637994103, "significant"=>true}
{"trait_id"=>"facet_gregariousness", "name"=>"Gregariousness", "category"=>"personality", "percentile"=>0.3643146929286043, "significant"=>true}

Agreeableness
{"trait_id"=>"facet_altruism", "name"=>"Altruism", "category"=>"personality", "percentile"=>0.4020555962581259, "significant"=>true}
{"trait_id"=>"facet_cooperation", "name"=>"Cooperation", "category"=>"personality", "percentile"=>0.3603843552650475, "significant"=>true}
{"trait_id"=>"facet_modesty", "name"=>"Modesty", "category"=>"personality", "percentile"=>0.582131278592358, "significant"=>true}
{"trait_id"=>"facet_morality", "name"=>"Uncompromising", "category"=>"personality", "percentile"=>0.34577537905073386, "significant"=>true}
{"trait_id"=>"facet_sympathy", "name"=>"Sympathy", "category"=>"personality", "percentile"=>0.608321787997034, "significant"=>true}
{"trait_id"=>"facet_trust", "name"=>"Trust", "category"=>"personality", "percentile"=>0.1107532137251554, "significant"=>true}

Emotional range
{"trait_id"=>"facet_anger", "name"=>"Fiery", "category"=>"personality", "percentile"=>0.5983433110620369, "significant"=>true}
{"trait_id"=>"facet_anxiety", "name"=>"Prone to worry", "category"=>"personality", "percentile"=>0.7269479416509153, "significant"=>true}
{"trait_id"=>"facet_depression", "name"=>"Melancholy", "category"=>"personality", "percentile"=>0.8142313580352654, "significant"=>true}
{"trait_id"=>"facet_immoderation", "name"=>"Immoderation", "category"=>"personality", "percentile"=>0.5798042693808653, "significant"=>true}
{"trait_id"=>"facet_self_consciousness", "name"=>"Self-consciousness", "category"=>"personality", "percentile"=>0.7577882331656556, "significant"=>true}
{"trait_id"=>"facet_vulnerability", "name"=>"Susceptible to stress", "category"=>"personality", "percentile"=>0.7631343989629844, "significant"=>true}

------values------
{"trait_id"=>"value_conservation", "name"=>"Conservation", "category"=>"values", "percentile"=>0.18307614327169447, "significant"=>true}
{"trait_id"=>"value_openness_to_change", "name"=>"Openness to change", "category"=>"values", "percentile"=>0.46170119182941183, "significant"=>true}
{"trait_id"=>"value_hedonism", "name"=>"Hedonism", "category"=>"values", "percentile"=>0.48338908358801075, "significant"=>true}
{"trait_id"=>"value_self_enhancement", "name"=>"Self-enhancement", "category"=>"values", "percentile"=>0.37303733233452196, "significant"=>true}
{"trait_id"=>"value_self_transcendence", "name"=>"Self-transcendence", "category"=>"values", "percentile"=>0.18354711061030066, "significant"=>true}

感想

  • 人工知能は思った以上に気軽に利用することができる。
  • カスタマーとの良い関係を維持することに利用出来そう。

参考資料

IBM Watson Personality Insights

青空文庫 - 夏目漱石「こころ」

パーソナリティーモデル - IBM Watson Personality Insights

GitHub - tanakadaichi1989:分析結果のjsonとハッシュに整形するコード

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

ABC088B - Card Game for Two

問題

https://atcoder.jp/contests/abs/tasks/abc088_b
スクリーンショット 2019-11-27 23.04.25.png

1回目

回答

N = gets.to_i
A = gets.split.map &:to_i

for i in 0..N-2 do
  for j in 0..N-2-i do
    if A[j] < A[j+1] 
      hoji = A[j]
      A[j] = A[j+1]
      A[j+1] = hoji
    end
  end
end

i = 0
aliceSum = 0
bobSum = 0
while i < N
  aliceSum += A[i]
  if i+1 < N
    bobSum += A[i+1]
  end
  i += 2
end

puts aliceSum - bobSum

結果

スクリーンショット 2019-11-27 23.06.57.png

2回目

回答

N = gets.to_i
A = gets.split.map(&:to_i).sort.reverse

result = 0

for i in 0..N-1
  if i % 2 == 0
    result += A[i]
  else
    result -= A[i]
  end
end

puts result

結果

スクリーンショット 2019-11-27 23.17.16.png

感想

sortあるんか〜〜〜〜〜い

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

Ruby学習ログ:その2【class, attr_accessor】

ハマりかけてます。
とりあえず整理するため書いていく!

【Ruby】クラスとかインスタンスについて改めて学習してみた

  クラス
=  初期処理インスタンス生成インスタンスメソッド定義・実行

ちなみにクラスの中は 初期処理インスタンスメソッド定義 が含まれていれば大丈夫!
...と思ったがattr_accessorというメソッドがないとクラスの外でインスタンス生成ができないらしい
(scivolaさんがコメントにて間違いを指摘してくださりました。
attr_accessorメソッドを定義するメソッドとのこと)

attr_accessor (Module)

詳しくは
attr_accessorメソッドの使い方

↓これが一番理解しやすかった↓
アクセスメソッド(アクセサメソッド )について(Ruby)
より引用↓

class Pokemon
  def initialize(name)
    @name = name
  end

  def getName
    @name
  end

  def setName=(changed_name)
    @name = changed_name
  end
end

#クラス外部から値の参照が可能
pokemon = Pokemon.new('ピカチュウ')
puts pokemon.getName
=> ピカチュウ

#クラス外部から値の更新が可能
pokemon.setName = 'ギエピー'
puts pokemon.getName
=> ギエピー

これをattr_accessorで書き換えると

class Pokemon
  attr_reader :name

  def initialize(name)
    @name = name
  end
end

pokemon = Pokemon.new('ピカチュウ')

puts pokemon.name
=> ピカチュウ

やっと理解できた...!

引数処理に関して掘り下げると
ruby引数処理に使えるテクニック

クラスについてもっと知りたい!
【まとめ】インスタンス変数、クラス変数、クラスインスタンス変数

所感

アウトプット前提で学習すると効率よくインプットできますね!
個人的にはもっと質の高い記事を投稿したいが、学習ログという定に甘えてます:baby:

    

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

Railsチュートリアル 第10章 - 「div.paginationが無い」と言われてテストが通らない場合

何が起こったか

Railsチュートリアルも第10章まで進み、ユーザー一覧のテストを一通り書き終えました。しかし、以下のように「div.paginationが無い」と言われてテストが通りません。

# rails test test/integration/users_index_test.rb
Running via Spring preloader in process 12045
Started with run options --seed 11902

 FAIL["test_index_including_pagination", UsersIndexTest, 2.137264299992239]
 test_index_including_pagination#UsersIndexTest (2.14s)
        Expected at least 1 element matching "div.pagination", found 0..
        Expected 0 to be >= 1.
        test/integration/users_index_test.rb:12:in `block in <class:UsersIndexTest>'

  1/1: [===================================] 100% Time: 00:00:02, Time: 00:00:02

Finished in 2.14716s
1 tests, 2 assertions, 1 failures, 0 errors, 0 skips

解決

app/views/users/index.html.erbapp/controllers/users_controller.rbの記述に間違いがないのにテストが通らない場合、fixtureの記述を間違えているのが原因かもしれません。

私の場合は、fixtureにおける以下の記述間違いが原因でした。

test/fixtures/users.yml(抜粋)
  ...
  <% 30.times do |n| %>
- user_<% n %>:
+ user_<%= n %>:
    name:  <%= "User #{n}" %>
    email: <%= "user-#{n}@example.com" %>
    password_digest: <%= User.digest('password') %>
  <% end %>

これは気が付きにくい。

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

Railsチュートリアル 第10章 - 「div.paginationが無い」と言われてテストが失敗する場合

何が起こったか

Railsチュートリアルも第10章まで進み、ユーザー一覧のテストを一通り書き終えました。しかし、以下のように「div.paginationが無い」と言われてテストが通りません。

# rails test test/integration/users_index_test.rb
Running via Spring preloader in process 12045
Started with run options --seed 11902

 FAIL["test_index_including_pagination", UsersIndexTest, 2.137264299992239]
 test_index_including_pagination#UsersIndexTest (2.14s)
        Expected at least 1 element matching "div.pagination", found 0..
        Expected 0 to be >= 1.
        test/integration/users_index_test.rb:12:in `block in <class:UsersIndexTest>'

  1/1: [===================================] 100% Time: 00:00:02, Time: 00:00:02

Finished in 2.14716s
1 tests, 2 assertions, 1 failures, 0 errors, 0 skips

解決

app/views/users/index.html.erbapp/controllers/users_controller.rbの記述に間違いがないのにテストが通らない場合、fixtureの記述を間違えているのが原因かもしれません。

私の場合は、fixtureにおける以下の記述間違いが原因でした。

test/fixtures/users.yml(抜粋)
  ...
  <% 30.times do |n| %>
- user_<% n %>:
+ user_<%= n %>:
    name:  <%= "User #{n}" %>
    email: <%= "user-#{n}@example.com" %>
    password_digest: <%= User.digest('password') %>
  <% end %>

これは気が付きにくい。

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

【Rails】ユーザーと投稿を関連づける

※この記事はProgate Railsコースをやってみて開発する際使えそうなおおまかな手順をメモしただけです。

投稿にユーザの名前と画像を表示

  • 投稿を管理しているテーブルに「どのユーザが投稿したのか」という情報を管理するためのカラム(user_id)を追加
  • 「新規投稿の際にどのユーザーが投稿したのか」という情報を(user_id)に追加
  • あとは表示するときに使うアクションでuser_idを使ってユーザ情報を集めてビューで表示

ユーザ詳細にそのユーザの投稿一覧を作る

  • ユーザ詳細アクションでそのユーザと一致するuser_idの投稿をwhereメソッドで集めてビューで表示

投稿の編集と削除の制限

  • 投稿詳細表示のアクションででログインしているユーザのidと投稿のuser_idが一致したら「編集」と「削除」のリンクを表示
  • 投稿編集表示メソッド・投稿編集メソッド・投稿削除メソッドなどで現在ログインしているユーザのidと投稿のユーザidが一致しなければ投稿一覧へリダイレクト

「いいね」機能の追加

  • Likeモデルと「いいねを押したユーザのID」と「いいねを押した投稿」を管理するためのlikesテーブルを作成
  • likesテーブルを用いて操作するためのlikesコントローラを作成してdestroyメソッドとcreateメソッドを作成する
  • いいねボタンの表示とcreateアクションとdestroyアクションの結びつけ
  • いいねの数を表示

ヒント

・いいねボタンの表示
link_toメソッドにHTML要素を含む場合

<% link_to("URL") do %>
  HTML要素
<% end %>

・いいねの数の表示
whereメソッドとcountメソッドを使用

いいねした投稿の表示

  • likes.html.erbとそのルートとコントローラを作成する
  • likesテーブルをもとにそのユーザがいいねした投稿を表示する

パスワードの暗号化

  • bcryptというgemをインストール
  • usersテーブルにpassword_digestカラムを追加してpasswordカラムを削除
  • 保存する際にパスワードを暗号化するためにhas_secure_passwordをuserモデルに追加

ヒント

・passwordカラムを削除
remove_columnメソッドを使用

暗号化されたパスワードでログインする

  • authenticateメソッドを使用して、入力されたパスワードと比較して、一致すればログインできるようにする

ヒント

authenticateメソッドは渡された引数を暗号化する

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

Rubyで長い文字列を書く時の備忘録

Rubyで複数行に渡る長い文字列を作成する際の備忘録です。

文字列を作成する際、シングル(or ダブル)クオーテーションで囲う書き方が一般的です。

ダブルクオーテーション
a = "長い文字列です。
     複数行にわたる文字列は
     読みにくいですね。"

こう書けばもっと読みやすいよ。

ヒアドキュメント
a = <<TEXT
長い文字列です。
複数行にわたる文字列は
読みにくいですね。
TEXT

TEXTはただの識別子なので、TEXTでなくても良いです。
識別子を指定できるので、文字列について何かわかりやすい文言を指定できるというのも
メリットの1つです!

以上、備忘録でした!

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

特定パス以下のActionController::RoutingErrorでJSONを返却する

環境

Ruby 2.5.7
Rails 5.2 (ActionPack 5.2)

コード

lib/middleware/routing_error_response_json.rb
module YourNameSpace::Middleware
  class RoutingErrorResponseJson
    RESPONSE_JSON_PATHS = %w[/api/v1]

    def initialize(app)
      @app = app
    end

    def call(env)
      request = ActionDispatch::Request.new(env)
      status, headers, response = @app.call(env)
      # NOTE: ActionController::RoutingError時に設定されるパラメータ
      # See https://github.com/rails/rails/blob/5-2-stable/actionpack/lib/action_dispatch/journey/router.rb#L64
      if enable?(request) && status == 404 && headers['X-Cascade'] == 'pass'
        status, headers, response = pass_response(status)
      end 

      [status, headers, response]
    rescue Exception => exception # rubocop:disable Lint/RescueException
      raise exception
    end

    private

    def pass_response(status)
      [
        status,
        { "Content-Type" => "application/json" },
        [
          JSON.generate(
            {
              title: 'Routing Error',
              detail: 'No route matches',
              invalid_params: []
            }
          )
        ]
      ]
    end

    def enable?(request)
      RESPONSE_JSON_PATHS.any? { |path| request.path.to_s.start_with?(path) }
    end
  end
end
config/initializers/middleware.rb
require_relative "../../lib/middleware/routing_error_response_json"
::Rails.application.config.middleware.use YourNameSpace::Middleware::RoutingErrorResponseJson

実現できる事

↑の例であれば /api/v1 以下へのリクエストでRoutingErrorとなった場合にJSONを返却する事ができる。
WebとApiが同居しているアプリケーションなどで利用価値があるかもしれない。

最後に

もっと良い方法があれば教えて欲しいです。

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

Rubyのreturn備忘録

Rubyでメソッドを定義する際、他の言語経験者だと戻り値を返すためにreturnを使うことが多いかと思うのですが、
Rubyの場合はreturnを使わない書き方の方が主流です。

メソッド定義(return版)
def add(a, b)
  return a + b
end

add(1, 2)
メソッド定義(主流版)
def add(a, b)
  a + b
end

add(1, 2)

returnはメソッドを途中で脱出する場合に使われることが多い。
以下の例は、引数がnilの場合はgreetingメソッドを抜けるようreturnを使っています。

returnの書き方
def greeting(country)
  return 'countryを入力してください' if country.nil?

  if country == 'japan'
    ・・・  
end

以上、備忘録でした!

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

クイズ:なぜバリデーションエラーになるでしょう

要約

  • railsのcallbackで中間テーブルを生成する方法をミスるとvalidationエラーになるアンチパターン

詳細

  • モデル構成
class User < ApplicationRecord
  has_many :notification_settings, dependent: :destroy
  has_many :notifications, through: :notification_settings, dependent: :destroy
  after_create :create_default_notifications
end

class NotificationSetting < ApplicationRecord
  belongs_to :user
  belongs_to :notification

  validates :user_id,  uniqueness: { scope: [:notification_id] }
end

class Notification < ApplicationRecord
  has_many :notification_settings, dependent: :destroy
  has_many :users, through: :notification_settings, dependent: :destroy
end

とかで、ユーザに通知設定が複数ひもづいている状態で、ユーザが作成されたタイミングでデフォルトの通知設定を作りたくてcreate_default_notificationsの中で

def create_default_notifications
  self.notifications = Notification.all
end

てやっていると一見OKっぽいんだけど、validates :user_id, uniqueness: { scope: [:notification_id] }で引っかかってvalildationエラーになる。

理由

self.notifications = Notification.all

が評価されたタイミングでNotificationSettingにinsertが発行されてしまって、
その後autosaveも動くので、結果2回createされてしまってuniqueじゃなくなってエラーになる。
has_manyにassignすると即insertされるというのがわかりにくかったという問題。
=はsaveされないという思い込みでした。

修正

丁寧に1件1件buildする

Notification.all.each do |notification|
  self.notification_settings.build(notification: notification)
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyの丸め誤差備忘録

小数点での計算の際、思わぬバグが生じたりする。

例えば0.1かける3が0.3にならない。

丸め誤差発生
0.1 * 3.0

 => 0.30000000000000004

これはPCが10進数ではなく2進数で計算していることが原因なのですが、
上記コードから、以下のような場合が発生します。

思わぬバグ
0.1 * 3.0 == 0.3 #-> false
0.1 * 3.0 <= 0.3 #-> false

そのため、Rubyで小数点の計算を行うときは、Rationalクラスを使う!
数字の後ろに『r』をつけることで利用できる!

Rationalクラス利用
0.1r * 3.0r #->3/10

# 上記を利用すると・・・
0.1r * 3.0r == 0.3 #-> true
0.1r * 3.0r <= 0.3 #-> true

変数に小数点が入っている場合は、rationalizeメソッドを呼び出すことでRationalクラスの数値に変換することができる!

rationalizeメソッド
a = 0.1
b = 3.0
a.rationalize * b.rationalize #-> 3/10

『分数になってるやないか!』というツッコミが聞こえてくる・・・
その場合はto_fメソッドを使って、分数を少数にしてくださいませ!

to_fメソッド
(0.1r * 3.0r).to_f #-> 0.3

以上、備忘録でした!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

eachメソッドとifメソッドの組み合わせの例

はじめに

この記事では、個人的に感動したeachメソッドとifメソッドを組み合わせについて解説します。

実行

最初に今回使用するコードを提示します。

users = ["田中", "鈴木", "佐藤", "山田", "藤本"]

puts "誰をさがしますか?"
name = gets.chomp.to_s

num = 0

users.each do |user|
 if name == user
  num += 1
 end
end

if num > 0 
 puts "発見しました"
else
 puts "いませんでした"
end

このコードは配列users内に、入力した人名があった時に"発見しました"、無かった時に"いませんでした"と表示するプログラムを表しています。

まず最初に配列を定義します。

users = ["田中", "鈴木", "佐藤", "山田", "藤本"]

次に、文字を入力してnameに代入するようにします。

puts "誰をさがしますか?"
name = gets.chomp.to_s

次に、eachメソッドで使うための変数numを定義します。
ここで代入する数字はなんでもいいのですが、ここではnum = 0としています。

num = 0

そして、今回の狙いの核となるeachメソッドおよびifメソッドを書いていきます。

users.each do |user|
 if name == user
  num += 1
 end
end

eachメソッドは、指定した配列の要素全てに対して、特定の処理を行うメソッドです。
今回の場合、指定した配列はusers、特定の処理はifメソッドになります。
ではifメソッドだけを見てみると。

if name == user
 num += 1
end

となります。
これは、name == userの場合にnum += 1という処理を行う、という処理になります。
もう少しかみ砕くと、入力した値が、元からあった値と一致したらnumに1を足すという処理です。

これをusersの全ての要素一つ一つに行います。
usersの要素の中に。nameと一致するものが一つでもあれば、num > 0となるので、最後の部分の

if num > 0 
 puts "発見しました"
else
 puts "いませんでした"
end

puts "発見しました"が実行されます。
反対にnameと一致するものが一つもなければnum = 0となりnum > 0を満たせないので、ifメソッドelseの部分であるputs "いませんでした"が実行されます。

これらの処理を組み合わせることにより配列users内に、入力した人名があった時に"発見しました"、無かった時に"いませんでした"と表示するプログラムを実現できます。

追記

eachメソッドifメソッドを始めとした条件式を使えば、簡単なアプリが色々作れそうなので、思いついたら色々試してみようと思います。

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

みんな大好き @mattn_jp さんのホクイモRuby ver.の解説

概要

@mattn さんのホクイモ、Rubyでわからないところがあったので調べました。

説明

コード再掲

puts '%1$s%1$sの%2$s'%((%w[ホイクモ]*2).map{|x|x.chars.sample(2).join})

'%1$s%1$sの%2$s'%の最後の%はString#%で、Stringクラスのメソッドで、フォーマットに対して呼び出すと引数を適用した文字列を返してくれるそうです。

フォーマットの詳細はKernel::sprintfを見ろというので、見に行きます。

見てみると、%s$を使っているとわかります。
下記の事例などがわかりやすいですね。

sprintf("%1$*2$s %2$d %1$s", "hello", 8)   #=> "   hello 8 hello"

感想

元記事のコメント欄で、C++の人から強い言葉でコメントがついていて、(以下略)。

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

Mac環境でRails内で別プロセスが立ち上がる処理をすると落ちてしまうとき

概要

Railsでtask runnerを実行しようとすると以下のエラーが起こり、途中で処理が止まってしまいました。

$ bundle exec rails runner -e development [タスク]
objc[13178]: +[__NSPlaceholderDictionary initialize] may have been in progress in another thread when fork() was called.
objc[13178]: +[__NSPlaceholderDictionary initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug.

解決法

$ echo 'export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES' >> ~/.bash_profile
$ exec $SHELL -l
$ bin/spring stop
$ bundle exec rails runner -e development [タスク]

参照

macOS High Sierra で "__NSPlaceholderDictionary initialize" エラー

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

Rails6 のちょい足しな新機能を試す108(multi-db abort_if_pending_migrations編)

はじめに

Rails 6 に追加された新機能を試す第108段。 今回は、 multi-db abort_if_pending_migrations 編です。
Rails 6 では、 rails db:abort_if_pending_migrations が multi database に対応しました。

Ruby 2.6.5, Rails 6.0.0 で確認しました。 (Rails 6.0.1 がリリースされていますが、確認した時点ではリリースされていませんでした。)

$ rails --version
Rails 6.0.0

今回は、簡単なスクリプトを作って確認します。

Rails プロジェクトを作成する

$ rails new rails_sandbox
$ cd rails_sandbox

今回は複数のDBを作って、それぞれ、 User と Book のCRUD を作ってから、 rails db:abort_if_pending_migrations を実行して確認します。

config/database.yml を編集する

config/database.yml を編集して複数データベースにします。

config/database.yml
...
development:
  backbone:
    <<: *default
    database: backbone_development
  library:
    <<: *default
    database: library_development
    migrations_paths: db/library_migrate
...

User の CRUD を作成する

User の CRUD を作成します。

$ bin/rails g scaffold User name

Book の CRUD を作成する

Book の CRUD を作成します。

$ bin/rails g scaffold Book title --db library

Book モデルを編集する

データベースの接続先を library に変更します。

app/models/book.rb
class Book < ApplicationRecord
  connects_to database: { writing: :library, reading: :library }
end

マイグレーションを実行する

$ bin/rails db:create db:migrate

rails server を実行する

別に実行しなくても良いのですが、別の事象に遭遇したので、ここで実行します。

$ bin/rails s

マイグレーションを作成する

rails server を実行したのとは、別のコンソールから、books に published_at カラムを追加するマイグレーションを作成します。

$ bin/rails g migration add_published_at_to_books published_at:datetime --db library

db:abort_if_pending_migrations を実行する

rails db:abort_if_pending_migrations を実行すると、ちゃんと rails db:migrate を実行しろとメッセージが出ます。

$ rails db:abort_if_pending_migrations
You have 1 pending migration:
  20191022023755 AddPublishedAtToBooks
Run `rails db:migrate` to update your database then try again.

ブラウザでアクセスする

どういう訳か、http://localhost:3000/users または、 http://localhost:3000/books にブラウザからアクセスしても ActiveRecord::PendingMigrationError になりませんでした。
予想と違ったので、 Issue として報告 しておきました。

試したソース

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

参考情報

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

投稿に対してのコメントの削除した際にエラー ActiveRecord::RecordNotFound

投稿に対してコメントできる機能を実装しました。
コメントは投稿詳細ページでコメントできるようにしています。
そのコメントを削除する際にエラーが出ます。
ActiveRecord::RecordNotFound in TweetsController#destroy
Couldn't find Tweet with 'id'=27
とでます。
tweets.controller.rbのdestroyアクションに飛んでいる状態です
本来はcomments.controller.rbのdestroyアクションに飛ばしたいです。

この削除ボタンのlink先のurlが悪いのでしょうか?
= link_to "削除","/tweets/#{comment.id}", method: :delete, class: "image-delete"

教えてくださいよろしくお願いします。

ファイル名<comments_controller.rb>

class CommentsController < ApplicationController

  def create
    @comment = Comment.create(text: comment_params[:text], tweet_id: comment_params[:tweet_id], user_id: current_user.id)
    respond_to do |format|
      format.html { redirect_to tweet_path(params[:tweet_id])  }
      format.json
    end  
  end

  def destroy
    comment = Comment.find(params[:id])
    comment.destroy
  end


  private
  def comment_params
    params.require(:comment).permit(:text).merge(user_id: current_user.id, tweet_id: params[:tweet_id])

  end
end

ファイル名<views/comments/_comment.html.haml>

.comments
    %h4 <コメント一覧>
    - if @comments
      - @comments.each do |comment|
        %p
          %strong
            = link_to comment.user.nickname, "/users/#{comment.user_id}"
            :
          = comment.text
        - if user_signed_in? && current_user.id == comment.user_id
          = link_to "削除","/tweets/#{comment.id}", method: :delete, class: "image-delete"
ファイル名<routes.rb>

resources :tweets do
    resources :comments, only: [:create, :destroy]

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

RubyとPythonとmap

一方の言語でのイメージが邪魔して、もう一方の言語での書き方が思い出せない時がたまにあるんです。

Python
map(lambda n: str(n).rjust(3,"0"), [1,2,3])
>> ["001", "002", "003"]
Ruby
[1,2,3].map{|n| n.to_s.rjust(3,"0")}
>> ["001", "002", "003"]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む