質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

URL

URL(ユニフォームリソースロケータ)とは、インターネット上のリソース(Webページや電子メールの宛先等)を特定するための形式的な記号の並びの事を言う。

ハイパーリンク

ハイパーリンクとは、ハイパーテキストにおいて、複数の文書を結び付ける役割を担う「参照」である。単にリンクとも呼びます

Q&A

1回答

1830閲覧

Rubyで掲示板の書き込みの中でハイパーリンクを作りたい

rubyrubyrubyrub

総合スコア0

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

URL

URL(ユニフォームリソースロケータ)とは、インターネット上のリソース(Webページや電子メールの宛先等)を特定するための形式的な記号の並びの事を言う。

ハイパーリンク

ハイパーリンクとは、ハイパーテキストにおいて、複数の文書を結び付ける役割を担う「参照」である。単にリンクとも呼びます

0グッド

0クリップ

投稿2021/01/17 13:09

前提・実現したいこと

Rubyで掲示板を作っています。

掲示板にコメントする際に、URLを含んでいる場合、そのURLをハイパーリンクにしたいです。
どのようにコードを書けばいいでしょうか?

どこを調べてもRailsでの実装しか出てきません。Rubyでの実装がしたいです。

bbs.logファイルは下のrubyファイルと同じところに作ってる状態です。

Ruby

1# coding: utf-8 2 3require "socket" 4require "uri" 5require "cgi/util" 6require "pathname" 7 8LOG = "bbs.log" 9 10ss = TCPServer.open(8160) 11 12loop do 13 Thread.start(ss.accept) do |s| 14 begin 15 path, params = s.gets.split[1].split("?") 16 17 header = "" 18 body = "" 19 myname = "名無し" 20 value = "" 21 color = "" 22 size = "" 23 24 if (params != nil) 25 params.split("&").each do |param| 26 pair = param.split("=") 27 pname = pair[0] 28 pvalue = URI.decode(pair[1] == nil ? "" : pair[1]) 29 myname = pvalue if (pname == "myname") 30 value = pvalue if (pname == "value") 31 color = pvalue if (pname == "color") 32 size = pvalue if (pname == "size") 33 34 end 35 end 36 37 38 39 if (path == "/") 40 status = "200 OK" 41 header = "Content-Type: text/html; charset=utf-8" 42 43 log = [] 44 message = "" 45 46 if value != "" 47 value=value.gsub(/(\r\n|\r|\n)/, "<br />") 48 log.unshift("<b>#{myname}</b> : (<I>#{Time.new}</I>)<br><font color=" + color + " size=" + size +">" + value + "</font></p><br>\n") 49 f = open(LOG, "a") 50 log.each{ 51 |line| 52 f.print line 53 } 54 f.close 55 end 56 57 f = open(LOG) 58 f.each{|line| 59 message = line + message 60 } 61 f.close 62 63 def text_url_to_link message 64 65 URI.extract(message, ['http']).uniq.each do |url| 66 sub_text = "" 67 sub_text << "<a href=" << url << " target=\"_blank\">" << url << "</a>" 68 69 text.gsub!(url, sub_text) 70 end 71 72 return message 73 end 74 75 body = "<html><body>こんにちは<br>" 76 body += "<form method=get>" 77 body += "name:<input type=text name=myname value=名無し>" 78 body += "<select name=color>\n" 79 body += "<option value=black" 80 body +=" selected" if (color=="black") 81 body += ">黒</option>\n" 82 body += "<option value=red" 83 body +=" selected" if (color=="red") 84 body += ">赤</option>\n" 85 body += "<option value=blue" 86 body +=" selected" if (color=="blue") 87 body += ">青</option>\n" 88 body += "<option value=green" 89 body +=" selected" if (color=="green") 90 body += ">緑</option>\n" 91 body += "<option value=yellow" 92 body +=" selected" if (color=="yellow") 93 body += ">黄</option>\n" 94 body += "</select>\n" 95 body += "<select name=size>\n" 96 body += "<option value=1" 97 body +=" selected" if (size=="1") 98 body += ">1</option>\n" 99 body += "<option value=2" 100 body +=" selected" if (size=="2") 101 body += ">2</option>\n" 102 body += "<option value=3" 103 body +=" selected" if (size=="3") 104 body += ">3</option>\n" 105 body += "<option value=4" 106 body +=" selected" if (size=="4") 107 body += ">4</option>\n" 108 body += "<option value=5" 109 body +=" selected" if (size=="5") 110 body += ">5</option>\n" 111 body += "</select><br>\n" 112 body += "comment:<textarea name=value cols=40 rows=3></textarea>" 113 body += "<input type=submit value=書き込み>" 114 body += "</form><hr>" 115 body = body+message 116 body = body + "</body></html>" 117 118 else 119 status = "302 Moved" 120 header = "Location: /" 121 end 122 123 124 s.write("HTTP/1.0 " + status + "\r\n") 125 s.write(header + "\r\n") if (header != "") 126 s.write("\r\n") 127 s.write(body) 128 puts Time.new.to_s + " " + status + " " + path 129 s.close 130 end 131 end 132end

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

m.ts10806

2021/01/17 21:17

>Railsでの実装しか出てきません Railsは結局Rubyで組まれてるので、ほぼ同じ考え方ができるのでは? 結局のところ正規表現による置換になると思いますし、 「Railsのやり方」がでてくるのでしたら、対応している機能など追っていくことで考え方を学ぶこともできるはずです。
rubyrubyrubyrub

2021/01/18 06:45

require "uri" def text_url_to_link text URI.extract(text, ['http']).uniq.each do |url| sub_text = "" sub_text << "<a href=" << url << " target=\"_blank\">" << url << "</a>" text.gsub!(url, sub_text) end return text end Railsのやり方はこれらしいのですが、これをRubyの正規表現で上のコードに入れることはできますでしょうか? Railsのこのコードの場合、textの中に代入された文の中からhttpで始まる部分を抽出していると思いますが、上のコードだと掲示板でコメントした際にどこにそのコメントが代入されているのかがわかりません。
m.ts10806

2021/01/18 07:33

URIモジュールはRuby由来のものっぽいですが(リファレンスマニュアルにも載ってる)、それは使えないということですか?
rubyrubyrubyrub

2021/01/18 07:42

いえ、URIモジュールは使えるので、このtext_url_to_linkをうまく上のコードに実装できていないだけだと思います。ここの実装がうまくいかなくて困っているところです。
guest

回答1

0

NOTE: 質問文のコードは(問題の本質に関係のない部分で)大量に修正したいところがあるのですが,この回答ではあまりそれらの問題点には触れていません.時間があれば追記します.
NOTE: 文中 [n]n 番目の註釈への参照です.

問題を細かくする

問題はいくつかの要素に分解できると思います.まず,次の2つに大別できます.

  • どのように文字列を変換するのか
  • どの箇所で文字列を変換するのか

ここで文字列の変換とは,「ある文字列があって,この中に含まれる(スキームが http の[1])URLの部分(ここでは一時的に[URL]とします)の全てについて,<a href="[URL]">[URL]</a>[2]に差し替える」ことを指します.

どの箇所で文字列を変換するのか

後者の問題は単純です.以下の行で,ログファイルに書き込む予定の文字列に,HTMLを組み立てて追記しています.このうち,掲示板へのコメントを表す変数は value でしょうから,この変数に対して変換すればよいことになります.

ruby

1log.unshift("<b>#{myname}</b> : (<I>#{Time.new}</I>)<br><font color=" + color + " size=" + size +">" + value + "</font></p><br>\n")

コード中にtext_url_to_linkメソッドが定義されています[3].このメソッドに型シグネチャが付くとすれば String -> String です.使うときには text_url_to_link(value) のようにします[4].

どのように文字列を変換するのか

それでは,今手元にある text_url_to_link メソッドはうまく動くのでしょうか.もしかしたらちゃんと動くのかもしれませんが, sub_text への破壊的変更が,(個人的に)めちゃくちゃ嫌な感じです.また,使用されている URI#extract英語のドキュメントでは特に但し書きはありませんが,日本語のドキュメントを見ると

このメソッドは Ruby 2.2 から obsolete です。

とあります.少なくとも URI#extract は使いたくないです.

それではどうするかということになるのですが,ここでさきほど読んだ日本語ドキュメントが役に立ちます.

ブロックが与えられた場合は String#scan と同様で、マッチした部分がみつかるたびに uri_str にその部分を代入してブロックを評価します。このときは nil を返します。

String#scan といえば正規表現です.URLモジュールがURIを表現する正規表現を提供していれば,String.gsubが使えるため一発です.

で,探しますと,あります.今回はオプション引数 schemes も活用できそうですね[1].

ruby

1value = "aaa https://teratail.com/questions/316630 bbb" 2 3value.gsub(URI.regexp(["https"]), '<a href="\&">\&</a>') 4#=> "aaa <a href=\"https://teratail.com/questions/316630\">https://teratail.com/questions/316630</a> bbb"

良さそうです.メソッドを作れば次回から再利用できます[4, 5]:

ruby

1def text_url_to_link(message) 2 message.gsub(URI.regexp(["https"]), '<a href="\&">\&</a>') 3end

註釈

  • [1] なぜなら,text_url_to_linkメソッドにURI.extract(message, ['http'])とあるからです.この記述を遵守すると,httpsは無視されてしまうのですが,ここでは元のコードの意味を変えないようにしておきます.ただし,一般にはこの点はhttpsも受け付けるようにするべきであると考えられます.
  • [2] なぜか質問文のコードではtarget属性が指定されているのですが,意図がよくわからなかったのでここでは省いています.
  • [3] ここでメソッドを定義することは推奨しません.トップレベルの位置で定義するべきです.
  • [4] 好みの問題ですが,私なら String に対してRefinementsします.書き捨てのコードであればモンキーパッチかもしれません.いずれにせよ,使い方を value.text_url_to_link にしたいからです.
  • [5] メソッド名がこれで最善とは思いません.

投稿2021/01/27 13:53

gemmaro

総合スコア358

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問