Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
30 changes: 14 additions & 16 deletions lib/tasks/fetch_news.rake
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ require 'active_support/broadcast_logger'

def safe_open(url)
uri = URI.parse(url)
return File.read(url) if uri.scheme.nil? || uri.scheme == 'file'
raise "不正なURLです: #{url}" unless uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)

Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
request = Net::HTTP::Get.new(uri)
response = http.request(request)
Expand All @@ -26,25 +26,19 @@ namespace :news do

logger.info('==== START news:fetch ====')

# 既存の news.yml を読み込み
yaml_path = Rails.root.join('db', 'news.yml')
# YAML出力先を環境変数で上書きできるようにする
yaml_path = ENV['NEWS_YAML_PATH'] ? Pathname.new(ENV['NEWS_YAML_PATH']) : Rails.root.join('db', 'news.yml')
feed_urls = ENV['NEWS_RSS_PATH'] ? [ENV['NEWS_RSS_PATH']] :
(Rails.env.test? || Rails.env.staging? ?
[Rails.root.join('spec', 'fixtures', 'sample_news.rss').to_s] :
['https://news.coderdojo.jp/feed/'])

existing_news = if File.exist?(yaml_path)
YAML.safe_load(File.read(yaml_path), permitted_classes: [Time], aliases: true)['news'] || []
else
[]
end

# テスト/ステージング環境ではサンプルファイル、本番は実サイトのフィード
feed_urls = if Rails.env.test? || Rails.env.staging?
[Rails.root.join('spec', 'fixtures', 'sample_news.rss').to_s]
else
[
'https://news.coderdojo.jp/feed/'
# 必要に応じて他 Dojo の RSS もここに追加可能
# 'https://coderdojotokyo.org/feed',
]
end

# RSS 取得&パース
new_items = feed_urls.flat_map do |url|
logger.info("Fetching RSS → #{url}")
Expand Down Expand Up @@ -107,7 +101,11 @@ namespace :news do
Time.parse(item['published_at'])
}.reverse

File.open('db/news.yml', 'w') do |f|
sorted_items.each_with_index do |item, index|
item['id'] = index + 1
end

File.open(yaml_path, 'w') do |f|
formatted_items = sorted_items.map do |item|
{
'id' => item['id'],
Expand All @@ -120,7 +118,7 @@ namespace :news do
f.write({ 'news' => formatted_items }.to_yaml)
end

logger.info("✅ Wrote #{sorted_items.size} items to db/news.yml (#{truly_new_items_sorted.size} new, #{updated_items.size} updated)")
logger.info("✅ Wrote #{sorted_items.size} items to #{yaml_path} (#{truly_new_items_sorted.size} new, #{updated_items.size} updated)")
logger.info('==== END news:fetch ====')
end
end
5 changes: 3 additions & 2 deletions lib/tasks/import_news.rake
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
require 'yaml'

namespace :news do
desc 'db/news.yml を読み込んで News テーブルを upsert する'
desc 'db/news.yml (またはENV指定のYAML)を読み込んで News テーブルを upsert する'
task import_from_yaml: :environment do
yaml_path = Rails.root.join('db', 'news.yml')
# ENVで上書き可能にする(なければデフォルト db/news.yml)
yaml_path = ENV['NEWS_YAML_PATH'] ? Pathname.new(ENV['NEWS_YAML_PATH']) : Rails.root.join('db', 'news.yml')
raw = YAML.safe_load(File.read(yaml_path), permitted_classes: [Time], aliases: true)

# entries を計算
Expand Down
64 changes: 63 additions & 1 deletion spec/models/news_spec.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,67 @@
require 'rails_helper'

RSpec.describe News, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
describe 'バリデーション' do
let(:news) { build(:news) }

it '有効なファクトリーを持つ' do
expect(news).to be_valid
end

describe 'title' do
it 'タイトルが空の場合は無効になる' do
news.title = nil
expect(news).not_to be_valid
expect(news.errors[:title]).not_to be_empty
end
end

describe 'url' do
it 'URL が空の場合は無効になる' do
news.url = nil
expect(news).not_to be_valid
expect(news.errors[:url]).not_to be_empty
end

it 'URL が重複している場合は無効になる' do
create(:news, url: 'https://example.com/test')
duplicate_news = build(:news, url: 'https://example.com/test')
expect(duplicate_news).not_to be_valid
expect(duplicate_news.errors[:url]).not_to be_empty
end

it 'URL形式であること' do
news.url = 'invalid-url'
expect(news).not_to be_valid
expect(news.errors[:url]).not_to be_empty
end

it 'HTTPSとHTTPを許可する' do
news.url = 'https://example.com'
expect(news).to be_valid

news.url = 'http://example.com'
expect(news).to be_valid
end
end

describe 'published_at' do
it '公開日時が空の場合は無効になる' do
news.published_at = nil
expect(news).not_to be_valid
expect(news.errors[:published_at]).not_to be_empty
end
end
end

describe 'スコープ' do
describe '.recent' do
it '公開日時の降順で並び替える' do
old_news = create(:news, published_at: 2.days.ago)
new_news = create(:news, published_at: 1.day.ago)

expect(News.recent).to eq([new_news, old_news])
end
end
end
end