Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/controllers/news_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class NewsController < ApplicationController
def index
@title = '☯️ CoderDojo ニュース 📰'
@desc = 'CoderDojo に関するお知らせの一覧ページです。'
@url = request.url

# データベースからニュースデータを取得(最新順)
@news_items = News.recent
end
end
30 changes: 3 additions & 27 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def prefecture_name_in_english(prefecture_name)
# 都道府県名の英語表記を返す簡易マッピング
# 「都」「府」「県」を除去してから検索
name_without_suffix = prefecture_name.gsub(/[都府県]$/, '')

prefecture_names = {
'北海道' => 'Hokkaido',
'青森' => 'Aomori',
Expand Down Expand Up @@ -157,7 +157,7 @@ def prefecture_name_in_english(prefecture_name)
'鹿児島' => 'Kagoshima',
'沖縄' => 'Okinawa'
}

prefecture_names[name_without_suffix] || prefecture_name
end

Expand Down Expand Up @@ -208,32 +208,8 @@ def translate_dojo_tag(tag_name)
'HTML' => 'HTML',
'CSS' => 'CSS'
}

tag_translations[tag_name] || tag_name
end

def format_news_title(news)
has_custom_emoji = news.title[0]&.match?(/[\p{Emoji}&&[^0-9#*]]/)
return news.title if has_custom_emoji

# Add preset Emoji to its prefix if news.title does not have Emoji.
emoji = case news.url
when %r{/podcasts/\d+}
'📻'
when %r{prtimes\.jp}
'📢'
else
'📰'
end
"#{emoji} #{news.title}"
tag_translations[tag_name] || tag_name
end

def news_link_url(news)
# Convert absolute podcast URLs to relative paths for local development
if news.url.match?(%r{^https://coderdojo\.jp/podcasts/\d+$})
news.url.sub('https://coderdojo.jp', '')
else
news.url
end
end
end
25 changes: 25 additions & 0 deletions app/models/news.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,29 @@ class News < ApplicationRecord
uniqueness: true,
format: { with: /\Ahttps?:\/\/.*\z/i }
validates :published_at, presence: true

def formatted_title
has_custom_emoji = title[0]&.match?(/[\p{Emoji}&&[^0-9#*]]/)
return title if has_custom_emoji

# Add preset Emoji to its prefix if title does not have Emoji.
emoji = case url
when %r{/podcasts/\d+}
'📻'
when %r{prtimes\.jp}
'📢'
else
'📰'
end
"#{emoji} #{title}"
end

def link_url
# Convert absolute podcast URLs to relative paths for local development
if url.match?(%r{^https://coderdojo\.jp/podcasts/\d+$})
url.sub('https://coderdojo.jp', '')
else
url
end
end
end
23 changes: 4 additions & 19 deletions app/views/home/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -194,30 +194,15 @@
<ul class="list" style="list-style: none;">
<% @news_items.each do |news| %>
<li>
<%= link_to format_news_title(news), news_link_url(news), target: '_blank' %>
<%= link_to news.formatted_title, news.link_url, target: '_blank' %>
</li>
<% end %>
</ul>
<p style="margin-top: 30px;">
最新情報はメールで受け取れます。
<br class="ignore-pc">
<small>(毎月配信)</small>
</p>

<div id="mc_embed_signup">
<form id="mc-embedded-subscribe-form" class="validate" action="https://coderdojo.us17.list-manage.com/subscribe/post?u=39a5824e42ab56ec44bb4e84e&amp;id=097dfa6a14" method="post" name="mc-embedded-subscribe-form" novalidate="" target="_blank">
<div id="mc_embed_signup_scroll">
<input id="mce-EMAIL" type="email" name="EMAIL" class="email" placeholder="your@example.com">
<div style="position: absolute; left: -5000px;" aria-hidden="true">
<input tabindex="-1" name="b_39a5824e42ab56ec44bb4e84e_097dfa6a14" type="text" value="">
</div>
<input id="mc-embedded-subscribe" class="button" name="subscribe" type="submit" value="購読する">
</div>
</form>
</div>

<%= render 'shared/dojo_letter_signup' %>

<div class="btn-cover" style="margin-top: 40px;">
<a class="btn-blue" href="<%= news_url %>">
<a class="btn-blue" href="<%= news_index_path %>">
<i class="far fa-newspaper"></i>
過去の記事を読む
</a>
Expand Down
44 changes: 44 additions & 0 deletions app/views/news/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<% provide :title, strip_tags(@title) %>
<% provide :desc, strip_tags(@desc) %>
<% provide :url, @url %>
<% provide :meta_image, "/img/ogp-news.jpeg" %>

<div class='container'>
<section class='news' style='padding: 50px 0px 0px;'>
<h2 class='text-center'><%= @title.html_safe %></h2>

<p class='text-center' style='padding-top: 30px;'>
<%= @desc.html_safe %>
</p>
</section>

<section class="text-center list" style='margin-bottom: 30px;'>
<%= render 'shared/dojo_letter_signup' %>
<%= render 'shared/social_buttons' %>
</section>

<section class='news' style='padding: 50px 0px;'>
<% if @news_items.any? %>
<div class="news-list">
<% @news_items.each do |news| %>
<article class="news-item" style="margin-bottom: 30px; padding-bottom: 20px; border-bottom: 1px solid #e0e0e0;">
<h3 style="margin-bottom: 10px;">
<%= link_to news.formatted_title, news.link_url, target: '_blank', rel: 'noopener' %>
</h3>
<p class="news-meta" style="color: #666; font-size: 14px;">
<i class="fa fa-calendar"></i>
<%= l(news.published_at.to_date, format: :long) if news.published_at %>
</p>
</article>
<% end %>
</div>
<% else %>
<p class="text-center">現在、ニュース記事はありません。</p>
<% end %>
</section>

<section class="text-center list" style='margin-bottom: 100px;'>
<%= render 'shared/dojo_letter_signup' %>
<%= render 'shared/social_buttons' %>
</section>
</div>
17 changes: 17 additions & 0 deletions app/views/shared/_dojo_letter_signup.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<p style="margin-top: 30px;">
最新情報はメールで受け取れます。
<br class="ignore-pc">
<small>(毎月配信)</small>
</p>

<div id="mc_embed_signup">
<form id="mc-embedded-subscribe-form" class="validate" action="https://coderdojo.us17.list-manage.com/subscribe/post?u=39a5824e42ab56ec44bb4e84e&amp;id=097dfa6a14" method="post" name="mc-embedded-subscribe-form" novalidate="" target="_blank">
<div id="mc_embed_signup_scroll">
<input id="mce-EMAIL" type="email" name="EMAIL" class="email" placeholder="your@example.com">
<div style="position: absolute; left: -5000px;" aria-hidden="true">
<input tabindex="-1" name="b_39a5824e42ab56ec44bb4e84e_097dfa6a14" type="text" value="">
</div>
<input id="mc-embedded-subscribe" class="button" name="subscribe" type="submit" value="購読する">
</div>
</form>
</div>
4 changes: 2 additions & 2 deletions app/views/shared/_footer.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<h2 style="line-height: 30px;">
<a class="footer-link" href="/" title="トップページに戻ります">Top</a>
| <a class="footer-link" href="/kata" title="道場情報まとめ「Kata」を見る">Kata</a>
| <a class="footer-link" href="/#news" title="最近のニュース「DojoLetter」を見る">News</a>
| <a class="footer-link" href="/news" title="最近のニュース「DojoLetter」を見る">News</a>
| <a class="footer-link" href="/docs" title="公式資料まとめ「Docs」を見る">資料</a>
| <a class="footer-link" href="/charter" title="CoderDojo 憲章を見る">憲章</a>
| <a class="footer-link" href="/stats" title="CoderDojo の統計データを見る">統計</a>
Expand All @@ -16,7 +16,7 @@
</h2>

<h2>
<%= link_to news_url do %>
<%= link_to news_index_path do %>
<i class="fa fa-newspaper-o fa-3x fa-fw" aria-hidden="true" title="News"></i>
<% end %>
<%= link_to facebook_page_url do %>
Expand Down
2 changes: 1 addition & 1 deletion app/views/shared/_header.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<%= link_to '最近の活動', '/#news' %>
<span class="navbar-dd-l"></span>
<ul class="navbar-dd">
<li><%= link_to '📰 News', '/#news' %></li>
<li><%= link_to '📰 News', news_index_path %></li>
<li><%= link_to '📻 Podcast', '/podcasts' %></li>
<li><%= link_to '📺 YouTube', '/youtube' %></li>
<li><%= link_to '📚 その他', '/#kata' %></li>
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
end
end
resources :docs, only: %i(index show)
resources :news, only: %i(index)
resources :podcasts, only: %i(index show)
resources :spaces, only: %i(index)

Expand Down
40 changes: 2 additions & 38 deletions spec/helpers/application_helper_spec.rb
Original file line number Diff line number Diff line change
@@ -1,42 +1,6 @@
require 'rails_helper'

RSpec.describe ApplicationHelper, type: :helper do
describe '#format_news_title' do
it '先頭文字が絵文字ならそのまま返す' do
news = double('news', title: '🔔 新着', url: 'https://news.coderdojo.jp/123')
expect(helper.format_news_title(news)).to eq '🔔 新着'
end

context '先頭文字が絵文字でない場合' do
it 'ポッドキャストのURLには📻を付与する' do
news = double('news', title: 'エピソード33', url: 'https://coderdojo.jp/podcasts/33')
expect(helper.format_news_title(news)).to eq '📻 エピソード33'
end

it 'PR TIMESのURLには📢を付与する' do
news = double('news', title: 'プレスリリース', url: 'https://prtimes.jp/main/html/rd/p/000000001.000038935.html')
expect(helper.format_news_title(news)).to eq '📢 プレスリリース'
end

it 'その他のURLには📰を付与する' do
news = double('news', title: '更新情報', url: 'https://news.coderdojo.jp/2025/12/06/dojoletter')
expect(helper.format_news_title(news)).to eq '📰 更新情報'
end
end
end

describe '#news_link_url' do
it 'ポッドキャストの絶対URLを相対パスに変換する' do
news = double('news', url: 'https://coderdojo.jp/podcasts/33')
expect(helper.news_link_url(news)).to eq '/podcasts/33'
end

it 'その他のURLはそのまま返す' do
news = double('news', url: 'https://news.coderdojo.jp/2025/12/06/dojoletter')
expect(helper.news_link_url(news)).to eq 'https://news.coderdojo.jp/2025/12/06/dojoletter'

news2 = double('news', url: 'https://prtimes.jp/main/html/rd/p/000000001.000038935.html')
expect(helper.news_link_url(news2)).to eq 'https://prtimes.jp/main/html/rd/p/000000001.000038935.html'
end
end
# News関連のメソッドはNewsモデルに移動しました
# spec/models/news_spec.rb を参照
end
39 changes: 39 additions & 0 deletions spec/models/news_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,43 @@
end
end
end

describe '#formatted_title' do
it '先頭文字が絵文字ならそのまま返す' do
news = build(:news, title: '🔔 新着', url: 'https://news.coderdojo.jp/123')
expect(news.formatted_title).to eq '🔔 新着'
end

context '先頭文字が絵文字でない場合' do
it 'ポッドキャストのURLには📻を付与する' do
news = build(:news, title: 'エピソード33', url: 'https://coderdojo.jp/podcasts/33')
expect(news.formatted_title).to eq '📻 エピソード33'
end

it 'PR TIMESのURLには📢を付与する' do
news = build(:news, title: 'プレスリリース', url: 'https://prtimes.jp/main/html/rd/p/000000001.000038935.html')
expect(news.formatted_title).to eq '📢 プレスリリース'
end

it 'その他のURLには📰を付与する' do
news = build(:news, title: '更新情報', url: 'https://news.coderdojo.jp/2025/12/06/dojoletter')
expect(news.formatted_title).to eq '📰 更新情報'
end
end
end

describe '#link_url' do
it 'ポッドキャストの絶対URLを相対パスに変換する' do
news = build(:news, url: 'https://coderdojo.jp/podcasts/33')
expect(news.link_url).to eq '/podcasts/33'
end

it 'その他のURLはそのまま返す' do
news = build(:news, url: 'https://news.coderdojo.jp/2025/12/06/dojoletter')
expect(news.link_url).to eq 'https://news.coderdojo.jp/2025/12/06/dojoletter'

news2 = build(:news, url: 'https://prtimes.jp/main/html/rd/p/000000001.000038935.html')
expect(news2.link_url).to eq 'https://prtimes.jp/main/html/rd/p/000000001.000038935.html'
end
end
end
66 changes: 66 additions & 0 deletions spec/requests/news_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
require 'rails_helper'

RSpec.describe "News", type: :request do
describe "GET /news" do
before do
# テスト用のニュースデータを作成
@news1 = News.create!(
title: "テストニュース1",
url: "https://example.com/news1",
published_at: 2.days.ago
)
@news2 = News.create!(
title: "テストニュース2",
url: "https://example.com/news2",
published_at: 1.day.ago
)
@news3 = News.create!(
title: "テストニュース3",
url: "https://example.com/news3",
published_at: 3.days.ago
)
end

it "正常にレスポンスを返す" do
get news_index_path
expect(response).to have_http_status(:success)
end

it "適切なタイトルを表示する" do
get news_index_path
expect(response.body).to include("CoderDojo ニュース")
end

it "ニュース記事を新しい順に表示する" do
get news_index_path

# 新しい順に表示されることを確認
# format_news_title によってプリセット絵文字が追加される可能性があるため、
# タイトルの主要部分で位置を確認
news2_pos = response.body.index("テストニュース2")
news1_pos = response.body.index("テストニュース1")
news3_pos = response.body.index("テストニュース3")

expect(news2_pos).to be < news1_pos
expect(news1_pos).to be < news3_pos
end

it "ニュースのタイトルとリンクを表示する" do
get news_index_path

# format_news_title によってプリセット絵文字が追加される可能性があるため、
# タイトルの主要部分が含まれていることを確認
expect(response.body).to include("テストニュース1")
expect(response.body).to include(@news1.url)
expect(response.body).to include("テストニュース2")
expect(response.body).to include(@news2.url)
end

it "ニュースがない場合は適切なメッセージを表示する" do
News.destroy_all
get news_index_path

expect(response.body).to include("現在、ニュース記事はありません")
end
end
end