Class: RecipeScrapers::Sources::SchemaOrg

Inherits:
Object
  • Object
show all
Defined in:
lib/recipe_scrapers/sources/schema_org.rb,
lib/recipe_scrapers/sources/schema_org/step_labels.rb,
lib/recipe_scrapers/sources/schema_org/ingredient_list.rb,
lib/recipe_scrapers/sources/schema_org/nutrition_facts.rb

Overview

Reads the fields of the schema.org Recipe a page publishes, from JSON-LD or from microdata.

Every method answers the field of the same name on RecipeScrapers::Scraper. A site that structures its markup in its own way subclasses this class and overrides a method, then names the subclass with RecipeScrapers::Scraper.schema_reader. The steps of recipeInstructions are read by private methods that can be overridden one at a time: steps_in, section_steps, step_steps and step_heading.

Defined Under Namespace

Modules: IngredientList, NutritionFacts, StepLabels

Instance Method Summary collapse

Constructor Details

#initialize(*readers) ⇒ SchemaOrg



16
17
18
19
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 16

def initialize(*readers)
  @source = readers.find(&:recipe) || readers.first
  @data = @source.recipe || {}
end

Instance Method Details

#author ⇒ String?



45
46
47
48
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 45

def author
  names = [@data["author"]].flatten.filter_map { |entry| author_name(dereference(entry, :person)) }
  names.empty? ? nil : names.join(", ")
end

#category ⇒ String?



30
31
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 30

def category = first_text(@data["recipeCategory"])
# (see Models::Recipe#cuisine)

#cook_time ⇒ Integer?



40
41
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 40

def cook_time = Parsers::Durations.minutes(@data["cookTime"])
# (see Models::Recipe#prep_time)

#cooking_method ⇒ String?



34
35
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 34

def cooking_method = first_text(@data["cookingMethod"])
# (see Models::Recipe#yields)

#cuisine ⇒ String?



32
33
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 32

def cuisine = first_text(@data["recipeCuisine"])
# (see Models::Recipe#cooking_method)

#description ⇒ String?



24
25
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 24

def description = Text.normalize(@data["description"])
# (see Models::Recipe#language)

#dietary_restrictions ⇒ Array<String>?



75
76
77
78
79
80
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 75

def dietary_restrictions
  diets = Array(@data["suitableForDiet"]).filter_map do |diet|
    Text.normalize(diet.is_a?(Hash) ? diet["name"] : diet.to_s.split("/").last)
  end
  diets.empty? ? nil : diets
end

#image ⇒ String?



62
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 62

def image = Text.normalize(url_in(@data["image"]))

#ingredients ⇒ Array<String>?

The ingredient lines as the page writes them. A line with no letter and no digit, such as "*", is a separator and left out. A line that ends in a colon and has no digit, such as "For the sauce:", is a group heading and goes to Models::Recipe#ingredient_groups instead. See Models::Recipe#parsed_ingredients.



51
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 51

def ingredients = IngredientList.parse(@data["recipeIngredient"] || @data["ingredients"])

#instructions ⇒ String?



60
61
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 60

def instructions = instructions_list&.join("\n")
# (see Models::Recipe#image)

#instructions_list ⇒ Array<String>?



54
55
56
57
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 54

def instructions_list
  steps = steps_in(@data["recipeInstructions"])
  steps.empty? ? nil : steps
end

#keywords ⇒ Array<String>?



65
66
67
68
69
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 65

def keywords
  words = [@data["keywords"]].flatten.flat_map { |entry| entry.to_s.split(",") }
  cleaned = words.filter_map { |word| Text.normalize(word) }.grep_v(FACET)
  cleaned.empty? ? nil : cleaned
end

#language ⇒ String?



26
27
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 26

def language = Text.normalize(@data["inLanguage"])
# (see Models::Recipe#site_name)

#nutrients ⇒ Hash{String => String}?

The nutrition facts as the page publishes them, keyed by the schema.org property name. Nothing is parsed here. See Models::Recipe#parsed_nutrients.

Examples:

recipe.nutrients # => { "calories" => "219 kcal", "fatContent" => "7 g" }


72
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 72

def nutrients = NutritionFacts.parse(first_hash(@data["nutrition"]))

#prep_time ⇒ Integer?



42
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 42

def prep_time = Parsers::Durations.minutes(@data["prepTime"])

#ratings ⇒ Float?



83
84
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 83

def ratings = Parsers::Ratings.average(rating_node)
# (see Models::Recipe#ratings_count)

#ratings_count ⇒ Integer?



85
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 85

def ratings_count = Parsers::Ratings.count(rating_node)

#site_name ⇒ String?



28
29
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 28

def site_name = @source.website_name
# (see Models::Recipe#category)

#title ⇒ String?



22
23
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 22

def title = Text.normalize(@data["name"])
# (see Models::Recipe#description)

#total_time ⇒ Integer?



38
39
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 38

def total_time = Parsers::Durations.minutes(@data["totalTime"])
# (see Models::Recipe#cook_time)

#yields ⇒ String?



36
37
# File 'lib/recipe_scrapers/sources/schema_org.rb', line 36

def yields = Parsers::Yields.parse(@data["recipeYield"])
# (see Models::Recipe#total_time)