我使用自定义类从Feedjirra制作AR实例。我无法让子实例与其父对象相关联。
Show has_many :episodes
-
Episode belongs_to :show
-
show_id
始终为nil
。
RSpec记录@show.id
和@episode.show_id
彼此相同。但是,当我在开发中运行导入后运行episode = Episode.first
时,该集的show_id
设置为nil
。
@show = Show.new
@show.name = @feed.title
@show.description = @feed.description
...
if @show.save
puts "@show.id: #{@show.id}"
end
@episodes = []
@feed.entries.each do |item|
@episodes.push(item)
end
@episodes.each do |item|
@episode = @show.episodes.new
@episode.name = item.title
@episode.description = item.summary
...
if @episode.save
puts "@episode.show_id: #{@episode.show_id}"
end
end
我尝试使用@episode = @show.episodes.create
,@episode = Episode.new
和@episode.show_id = @show.id
。它们都记录了匹配的ID,但实例上的show_id
仍为nil
。每隔一列都填写正确。
我认为这个问题可能与使用add_foreign_key
:
class AddShowToEpisodes < ActiveRecord::Migration
def change
add_reference :episodes, :show, index: true
add_foreign_key :episodes, :shows, column: :show_id
end
end
所以我删除了它并使用了标准foreign_key: true
,但它没有效果。
class RemoveShowFromEpisodes < ActiveRecord::Migration
def change
remove_column :episodes, :show_id
end
end
class AddShowBackToEpisodes < ActiveRecord::Migration
def change
add_reference :episodes, :show, index: true, foreign_key: true
end
end
以下是完整代码以防万一。
importers_controller.rb:
class Admin::ImportersController < Admin::ApplicationController
before_action :set_importer, only: [:show, :edit, :update, :destroy]
def index
@importers = policy_scope(Importer)
end
def show
end
def new
@importer = Importer.new
authorize @importer
end
def create
@importer = Importer.new(importer_params)
authorize @importer
if @importer.save
require "subscription_importer"
SubscriptionImporter.new(@importer)
flash[:notice] = "Importer added."
redirect_to admin_importers_path
else
flash[:error] = "Importer not added."
render "new"
end
end
def edit
end
def update
end
def destroy
end
private
def set_importer
@importer = Importer.find(params[:id])
authorize @importer
end
def importer_params
params.require(:importer).permit(:name, :url, :source)
end
end
subscription_importer.rb:
class SubscriptionImporter
def initialize(importer)
@importer = importer
@feed = Feedjira::Feed.fetch_and_parse @importer.url
if @importer.source === "iTunes"
itunes_parser(@importer)
end
end
def itunes_parser(importer)
@importer = importer
# Parser
@feed = Feedjira::Feed.fetch_and_parse @importer.url
# Show
@show = Show.new
@show.name = @feed.title
@show.description = @feed.description
@show.logo = @feed.itunes_image
@show.explicit = explicit_check(@feed.itunes_explicit)
@show.genre = @feed.itunes_categories
@show.tags = @feed.itunes_keywords
@show.url = @feed.url
@show.language = @feed.language
if @show.save
puts "Show import succeeded"
puts "@show.id: #{@show.id}"
else
puts "Show import failed"
end
# Episodes
@episodes = []
@feed.entries.each do |item|
@episodes.push(item)
end
@episodes.each do |item|
@episode = @show.episodes.new
@episode.name = item.title
@episode.description = item.summary
@episode.release = item.published
@episode.image = item.itunes_image
@episode.explicit = explicit_check(item.itunes_explicit)
@episode.tags = item.itunes_keywords
@episode.url = item.enclosure_url
@episode.duration = item.itunes_duration
if @episode.save
puts "Episode import succeeded"
puts "@episode.show_id: #{@episode.show_id}"
else
puts "Episode import failed"
end
end
end
def explicit_check(string)
if string == "yes" || "Yes"
true
else
false
end
end
end
create_importer_spec.rb:
require "rails_helper"
RSpec.feature "Admins can create importers" do
let(:user) { FactoryGirl.create(:user, :admin) }
context "admins" do
before do
login_as(user)
visit "/"
click_link "Admin"
click_link "Importers"
click_link "New Importer"
end
scenario "with valid credentials" do
fill_in "Name", with: "The Stack Exchange Podcast"
fill_in "Url", with: "https://blog.stackoverflow.com/feed/podcast/" # Needs stubbing
select "iTunes", from: "Source"
click_button "Create Importer"
expect(page).to have_content "Importer added"
expect(page).to have_content "The Stack Exchange Podcast"
end
scenario "with invalid credentials" do
fill_in "Name", with: ""
fill_in "Url", with: ""
click_button "Create Importer"
expect(page).to have_content "Importer not added"
end
end
end
答案 0 :(得分:0)
我认为episodes
类中的SubscriptionImporter
功能导致问题......
@episodes = []
@feed.entries.each do |item|
@episodes.push(item) #-> each "@episodes" is a FeedJirra object
end
@episodes.each do |episode|
#-> you're now creating an episode in the same call as show, which will either mean that show is not persisted or perhaps some other error
end
我个人会将SubscriptionImporter
功能限制为仅返回数据。您应该通过相应的模型解析该数据:
#app/controllers/admin/importers_controller.rb
class Admin::ImportersController < Admin::ApplicationController
def create
@import = Importer.new import_params
if @import.save
@import.parse_show if @import.itunes?
end
end
private
def import_params
params.require(:importer).permit(:name, :url, :source)
end
end
#app/models/importer.rb
class Importer < ActiveRecord::Base
def feed
return false unless itunes?
origin = Feedjirra::Feed.fetch_and_parse(self.url)
return {
name: origin.title,
description: origin.description,
logo: origin.itunes_image,
explicit: explicit_check(origin.itunes_explicit),
genre: origin.itunes_categories,
tags: origin.itunes_keywords,
url: origin.url,
language: origin.language,
entries: origin.entries
}
end
def parse_show
Show.create(feed)
end
def itunes?
self.source == "iTunes" #-> true/false
end
private
def explicit_check
string == "yes" || "Yes" #-> true/false
end
end
#app/models/show.rb
class Show < ActiveRecord::Base
has_many :episodes
attr_accessor :entries
after_create :create_episodes #-> might not persist entries
def create_episodes
if self.entries.any?
self.entries.each do |item|
self.episodes.create({
name: item.title
description: item.summary,
release: item.published,
image: item.itunes_image,
explicit: explicit_check?(item.itunes_explicit),
tags: item.itunes_keywords,
url: item.enclosure_url,
duration: item.itunes_duration
})
end
end
end
private
def explicit_check?
string == "yes" || "Yes"
end
end
以上内容允许您创建@importer
,从中提取feed
,然后填充Show
&amp; Episode
模型包含返回的数据。
虽然应解决您的问题,但您需要考虑OOP
- 将每个元素作为对象。
<强>更新强>
如果你想更加客观化,那么就有一个简单的模式:
Importer
就是你需要保存的 - 其他一切都应该围绕这个Show
+ Episode
可以为我所知道的所有类/表考虑到这一点,您可以执行以下操作:
#app/controllers/admin/importers_controller.rb
class Admin::ImportersController < Admin::ApplicationController
def create
@import = Importer.new import_params
@import.save
end
private
def import_params
params.require(:importer).permit(:name, :url, :source)
end
end
#app/services/feed.rb
class Feed
attr_reader :params, :show, :episode, :origin
def initialize(params)
@params = params
end
def origin
@origin = Feedjirra::Feed.fetch_and_parse params[:source]
end
def show
@show = ShowHelper.new @origin
end
def episodes
@show.episodes
end
end
#app/services/show_helper.rb
class ShowHelper
attr_reader :origin
def initialize(origin)
@origin = origin
end
def name
@origin.title
end
def description
@origin.summary || @origin.description
end
def logo
@origin.itunes_image
end
def explicit
%r{^yes$} =~ @origin.itunes_explicit
end
def genre
@origin.itunes_categories
end
def tags
@origin.itunes_keywords
end
def url
@origin.url
end
def language
@origin.language
end
def episodes
@origin.entries
end
end
#app/models/importer.rb
class Importer < ActiveRecord::Base
after_create :parse_show, if: "itunes?"
validates :source, :url, :name, presence: true
def itunes?
source == "iTunes"
end
def feed
@feed = Feed.new(self)
end
private
def parse_show
@show = Show.new(feed.show) if feed && feed.show
if @show.save && @show.entries.any?
@show.entries.each do |entry|
@show.episodes.create ShowHelper.new(entry)
end
end
end
end