I have some service objects that use Nokogiri to make AR instances. I created a rake task so that I can update the instances with a cron job. What I want to test is if it's adding items that weren't there before, ie:
- Create an
Importerwith aurlofspec/fixtures/feed.xml, feed.xml having 10 items. - Expect
Show.count == 1andEpisode.count == 10 - Edit
spec/fixtures/feed.xmlto have 11 items - Invoke rake task
- Expect
Show.count == 1andEpisode.count == 11
How could I test this in RSpec, or modify my code to be more testable?
# models/importer.rb
class Importer < ActiveRecord::Base
after_create :parse_importer
validates :title, presence: true
validates :url, presence: true
validates :feed_format, presence: true
private
def parse_importer
Parser.new(self)
end
end
# models/show.rb
class Show < ActiveRecord::Base
validates :title, presence: true
validates :title, uniqueness: true
has_many :episodes
attr_accessor :entries
end
# models/episode.rb
class Episode < ActiveRecord::Base
validates :title, presence: true
validates :title, uniqueness: true
belongs_to :show
end
#lib/tasks/admin.rake
namespace :admin do
desc "Checks all Importer URLs for new items."
task refresh: :environment do
@importers = Importer.all
@importers.each do |importer|
Parser.new(importer)
end
end
end
# services/parser.rb
class Parser
def initialize(importer)
feed = Feed.new(importer)
show = Show.where(rss_link: importer.url).first
if show # add new episodes
new_episodes = Itunes::Channel.refresh(feed.origin)
new_episodes.each do |new_episode|
show.episodes.create feed.episode(new_episode)
end
else # create a show and its episodes
new_show = Show.new(feed.show) if (feed && feed.show)
if (new_show.save && new_show.entries.any?)
new_show.entries.each do |entry|
new_show.episodes.create feed.episode(entry)
end
end
end
end
end
# services/feed.rb
class Feed
require "nokogiri"
require "open-uri"
require "formats/itunes"
attr_reader :params, :origin, :show, :episode
def initialize(params)
@params = params
end
def origin
@origin = Nokogiri::XML(open(params[:url]))
end
def format
@format = params[:feed_format]
end
def show
case format
when "iTunes"
Itunes::Channel.fresh(origin)
end
end
def episode(entry)
@entry = entry
case format
when "iTunes"
Itunes::Item.fresh(@entry)
end
end
end
# services/formats/itunes.rb
class Itunes
class Channel
def initialize(origin)
@origin = origin
end
def title
@origin.xpath("//channel/title").text
end
def description
@origin.xpath("//channel/description").text
end
def summary
@origin.xpath("//channel/*[name()='itunes:summary']").text
end
def subtitle
@origin.xpath("//channel/*[name()='itunes:subtitle']/text()").text
end
def rss_link
@origin.xpath("//channel/*[name()='atom:link']/@href").text
end
def main_link
@origin.xpath("//channel/link/text()").text
end
def docs_link
@origin.xpath("//channel/docs/text()").text
end
def release
@origin.xpath("//channel/pubDate/text()").text
end
def image
@origin.xpath("//channel/image/url/text()").text
end
def language
@origin.xpath("//channel/language/text()").text
end
def keywords
keywords_array(@origin)
end
def categories
category_array(@origin)
end
def explicit
explicit_check(@origin)
end
def entries
entry_array(@origin)
end
def self.fresh(origin)
@show = Itunes::Channel.new origin
return {
description: @show.description,
release: @show.release,
explicit: @show.explicit,
language: @show.language,
title: @show.title,
summary: @show.summary,
subtitle: @show.subtitle,
image: @show.image,
rss_link: @show.rss_link,
main_link: @show.main_link,
docs_link: @show.docs_link,
categories: @show.categories,
keywords: @show.keywords,
entries: @show.entries
}
end
def self.refresh(origin)
@show = Itunes::Channel.new origin
return @show.entries
end
private
def category_array(channel)
arr = []
channel.xpath("//channel/*[name()='itunes:category']/@text").each do |category|
arr.push(category.to_s)
end
return arr
end
def explicit_check(channel)
string = channel.xpath("//channel/*[name()='itunes:explicit']").text
if string === "yes" || string === "Yes"
true
else
false
end
end
def keywords_array(channel)
keywords = channel.xpath("//channel/*[name()='itunes:keywords']/text()").text
arr = keywords.split(",")
return arr
end
def entry_array(channel)
arr = []
channel.xpath("//item").each do |item|
arr.push(item)
end
return arr
end
end
class Item
def initialize(origin)
@origin = origin
end
def description
@origin.xpath("*[name()='itunes:subtitle']").text
end
def release
@origin.xpath("pubDate").text
end
def image
@origin.xpath("*[name()='itunes:image']/@href").text
end
def explicit
explicit_check(@origin)
end
def duration
@origin.xpath("*[name()='itunes:duration']").text
end
def title
@origin.xpath("title").text
end
def enclosure_url
@origin.xpath("enclosure/@url").text
end
def enclosure_length
@origin.xpath("enclosure/@length").text
end
def enclosure_type
@origin.xpath("enclosure/@type").text
end
def keywords
keywords_array(@origin.xpath("*[name()='itunes:keywords']").text)
end
def self.fresh(entry)
@episode = Itunes::Item.new entry
return {
description: @episode.description,
release: @episode.release,
image: @episode.image,
explicit: @episode.explicit,
duration: @episode.duration,
title: @episode.title,
enclosure_url: @episode.enclosure_url,
enclosure_length: @episode.enclosure_length,
enclosure_type: @episode.enclosure_type,
keywords: @episode.keywords
}
end
private
def explicit_check(item)
string = item.xpath("*[name()='itunes:explicit']").text
if string === "yes" || string === "Yes"
true
else
false
end
end
def keywords_array(item)
keywords = item.split(",")
return keywords
end
end
end
Aucun commentaire:
Enregistrer un commentaire