diff --git a/app/controllers/news_controller.rb b/app/controllers/news_controller.rb
new file mode 100644
index 00000000..8c2dba7e
--- /dev/null
+++ b/app/controllers/news_controller.rb
@@ -0,0 +1,10 @@
+class NewsController < ApplicationController
+ def index
+ @title = '☯️ CoderDojo ニュース 📰'
+ @desc = 'CoderDojo に関するお知らせの一覧ページです。'
+ @url = request.url
+
+ # データベースからニュースデータを取得(最新順)
+ @news_items = News.recent
+ end
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 47285052..44c9afa7 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -107,7 +107,7 @@ def prefecture_name_in_english(prefecture_name)
# 都道府県名の英語表記を返す簡易マッピング
# 「都」「府」「県」を除去してから検索
name_without_suffix = prefecture_name.gsub(/[都府県]$/, '')
-
+
prefecture_names = {
'北海道' => 'Hokkaido',
'青森' => 'Aomori',
@@ -157,7 +157,7 @@ def prefecture_name_in_english(prefecture_name)
'鹿児島' => 'Kagoshima',
'沖縄' => 'Okinawa'
}
-
+
prefecture_names[name_without_suffix] || prefecture_name
end
@@ -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
diff --git a/app/models/news.rb b/app/models/news.rb
index 0dd08cbc..0bd5fa84 100644
--- a/app/models/news.rb
+++ b/app/models/news.rb
@@ -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
diff --git a/app/views/home/show.html.erb b/app/views/home/show.html.erb
index 5b4626c1..38e5a0e8 100644
--- a/app/views/home/show.html.erb
+++ b/app/views/home/show.html.erb
@@ -194,30 +194,15 @@
<% @news_items.each do |news| %>
-
- <%= link_to format_news_title(news), news_link_url(news), target: '_blank' %>
+ <%= link_to news.formatted_title, news.link_url, target: '_blank' %>
<% end %>
-
- 最新情報はメールで受け取れます。
-
- (毎月配信)
-
-
-
+
+ <%= render 'shared/dojo_letter_signup' %>
-
+
過去の記事を読む
diff --git a/app/views/news/index.html.erb b/app/views/news/index.html.erb
new file mode 100644
index 00000000..5e95242e
--- /dev/null
+++ b/app/views/news/index.html.erb
@@ -0,0 +1,44 @@
+<% provide :title, strip_tags(@title) %>
+<% provide :desc, strip_tags(@desc) %>
+<% provide :url, @url %>
+<% provide :meta_image, "/img/ogp-news.jpeg" %>
+
+
+
+ <%= @title.html_safe %>
+
+
+ <%= @desc.html_safe %>
+
+
+
+
+ <%= render 'shared/dojo_letter_signup' %>
+ <%= render 'shared/social_buttons' %>
+
+
+
+ <% if @news_items.any? %>
+
+ <% @news_items.each do |news| %>
+
+
+ <%= link_to news.formatted_title, news.link_url, target: '_blank', rel: 'noopener' %>
+
+
+
+ <%= l(news.published_at.to_date, format: :long) if news.published_at %>
+
+
+ <% end %>
+
+ <% else %>
+ 現在、ニュース記事はありません。
+ <% end %>
+
+
+
+ <%= render 'shared/dojo_letter_signup' %>
+ <%= render 'shared/social_buttons' %>
+
+
diff --git a/app/views/shared/_dojo_letter_signup.html.erb b/app/views/shared/_dojo_letter_signup.html.erb
new file mode 100644
index 00000000..4d8c2920
--- /dev/null
+++ b/app/views/shared/_dojo_letter_signup.html.erb
@@ -0,0 +1,17 @@
+
+ 最新情報はメールで受け取れます。
+
+ (毎月配信)
+
+
+
\ No newline at end of file
diff --git a/app/views/shared/_footer.html.erb b/app/views/shared/_footer.html.erb
index 5b304963..fea784bb 100644
--- a/app/views/shared/_footer.html.erb
+++ b/app/views/shared/_footer.html.erb
@@ -2,7 +2,7 @@
|
- |
+ |
|
|
|
@@ -16,7 +16,7 @@
- <%= link_to news_url do %>
+ <%= link_to news_index_path do %>
<% end %>
<%= link_to facebook_page_url do %>
diff --git a/app/views/shared/_header.html.erb b/app/views/shared/_header.html.erb
index 96868778..e06800a0 100644
--- a/app/views/shared/_header.html.erb
+++ b/app/views/shared/_header.html.erb
@@ -21,7 +21,7 @@
<%= link_to '最近の活動', '/#news' %>
- - <%= link_to '📰 News', '/#news' %>
+ - <%= link_to '📰 News', news_index_path %>
- <%= link_to '📻 Podcast', '/podcasts' %>
- <%= link_to '📺 YouTube', '/youtube' %>
- <%= link_to '📚 その他', '/#kata' %>
diff --git a/config/routes.rb b/config/routes.rb
index f756ff20..7c83b0ee 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -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)
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 9304ff07..f1bbd147 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -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
diff --git a/spec/models/news_spec.rb b/spec/models/news_spec.rb
index 3beba335..e3691be4 100644
--- a/spec/models/news_spec.rb
+++ b/spec/models/news_spec.rb
@@ -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
diff --git a/spec/requests/news_spec.rb b/spec/requests/news_spec.rb
new file mode 100644
index 00000000..8fc928d9
--- /dev/null
+++ b/spec/requests/news_spec.rb
@@ -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
\ No newline at end of file