cooklang 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,187 +3,49 @@
3
3
  require "spec_helper"
4
4
 
5
5
  RSpec.describe Cooklang::Formatters::Text do
6
- let(:recipe) do
7
- Cooklang::Recipe.new(
8
- ingredients: ingredients,
9
- steps: steps,
10
- cookware: [],
11
- timers: [],
12
- metadata: {},
13
- sections: [],
14
- notes: []
15
- )
16
- end
17
- let(:formatter) { described_class.new(recipe) }
18
-
19
- describe "#to_s" do
20
- context "with ingredients only" do
21
- let(:ingredients) do
22
- [
23
- Cooklang::Ingredient.new(name: "flour", quantity: 125, unit: "g"),
24
- Cooklang::Ingredient.new(name: "milk", quantity: 250, unit: "ml"),
25
- Cooklang::Ingredient.new(name: "eggs", quantity: 3),
26
- Cooklang::Ingredient.new(name: "butter"),
27
- Cooklang::Ingredient.new(name: "sea salt", quantity: 1, unit: "pinch")
28
- ]
29
- end
30
- let(:steps) { [] }
31
-
32
- it "formats ingredients with aligned columns" do
33
- expected = <<~OUTPUT.strip
34
- Ingredients:
35
- flour 125 g
36
- milk 250 ml
37
- eggs 3
38
- butter some
39
- sea salt 1 pinch
40
- OUTPUT
41
-
42
- expect(formatter.to_s).to eq(expected)
43
- end
6
+ describe "#generate" do
7
+ let(:recipe) do
8
+ recipe_text = File.read(File.join(__dir__, "..", "fixtures", "comprehensive_recipe.cook"))
9
+ Cooklang.parse(recipe_text)
44
10
  end
11
+ let(:output) { described_class.new(recipe).generate }
45
12
 
46
- context "with steps only" do
47
- let(:ingredients) { [] }
48
- let(:steps) do
49
- [
50
- Cooklang::Step.new(segments: [
51
- { type: "text", value: "Crack the " },
52
- { type: "ingredient", value: "eggs", name: "eggs" },
53
- { type: "text", value: " into a blender" }
54
- ]),
55
- Cooklang::Step.new(segments: [
56
- { type: "text", value: "Pour into a bowl and leave to stand for " },
57
- { type: "timer", value: "15 minutes", name: nil },
58
- { type: "text", value: "." }
59
- ])
60
- ]
61
- end
62
-
63
- it "formats steps with numbered list" do
64
- expected = <<~OUTPUT.strip
65
- Steps:
66
- 1. Crack the eggs into a blender
67
- 2. Pour into a bowl and leave to stand for 15 minutes.
68
- OUTPUT
69
-
70
- expect(formatter.to_s).to eq(expected)
71
- end
13
+ it "includes Ingredients and Steps sections" do
14
+ expect(output).to include("Ingredients:")
15
+ expect(output).to include("Steps:")
72
16
  end
73
17
 
74
- context "with both ingredients and steps" do
75
- let(:ingredients) do
76
- [
77
- Cooklang::Ingredient.new(name: "butter"),
78
- Cooklang::Ingredient.new(name: "eggs", quantity: 3),
79
- Cooklang::Ingredient.new(name: "flour", quantity: 125, unit: "g"),
80
- Cooklang::Ingredient.new(name: "milk", quantity: 250, unit: "ml"),
81
- Cooklang::Ingredient.new(name: "sea salt", quantity: 1, unit: "pinch")
82
- ]
83
- end
84
- let(:steps) do
85
- [
86
- Cooklang::Step.new(segments: [
87
- { type: "text", value: "Crack the " },
88
- { type: "ingredient", value: "eggs", name: "eggs" },
89
- { type: "text", value: " into a blender, then add the " },
90
- { type: "ingredient", value: "flour", name: "flour" },
91
- { type: "text", value: ", " },
92
- { type: "ingredient", value: "milk", name: "milk" },
93
- { type: "text", value: " and " },
94
- { type: "ingredient", value: "sea salt", name: "sea salt" },
95
- { type: "text", value: "." }
96
- ]),
97
- Cooklang::Step.new(segments: [
98
- { type: "text", value: "Pour into a bowl and leave to stand for " },
99
- { type: "timer", value: "15 minutes", name: nil },
100
- { type: "text", value: "." }
101
- ]),
102
- Cooklang::Step.new(segments: [
103
- { type: "text", value: "Melt the " },
104
- { type: "ingredient", value: "butter", name: "butter" },
105
- { type: "text", value: " in a large non-stick " },
106
- { type: "cookware", value: "frying pan", name: "frying pan" },
107
- { type: "text", value: "." }
108
- ])
109
- ]
110
- end
111
-
112
- it "formats complete recipe" do
113
- expected = <<~OUTPUT.strip
114
- Ingredients:
115
- butter some
116
- eggs 3
117
- flour 125 g
118
- milk 250 ml
119
- sea salt 1 pinch
120
-
121
- Steps:
122
- 1. Crack the eggs into a blender, then add the flour, milk and sea salt.
123
- 2. Pour into a bowl and leave to stand for 15 minutes.
124
- 3. Melt the butter in a large non-stick frying pan.
125
- OUTPUT
126
-
127
- expect(formatter.to_s).to eq(expected)
128
- end
18
+ it "formats ingredients with quantities and units" do
19
+ expect(output).to match(/shallots\s+2/)
20
+ expect(output).to match(/garlic\s+4 cloves/)
21
+ expect(output).to match(/msg\s+0\.5 tsp/)
22
+ expect(output).to match(/white pepper\s+some/)
129
23
  end
130
24
 
131
- context "with empty recipe" do
132
- let(:ingredients) { [] }
133
- let(:steps) { [] }
134
-
135
- it "returns empty string" do
136
- expect(formatter.to_s).to eq("")
137
- end
138
- end
139
-
140
- context "with various ingredient formats" do
141
- let(:ingredients) do
142
- [
143
- Cooklang::Ingredient.new(name: "onion", quantity: 1),
144
- Cooklang::Ingredient.new(name: "olive oil", unit: "drizzle"),
145
- Cooklang::Ingredient.new(name: "salt"),
146
- Cooklang::Ingredient.new(name: "pepper", quantity: "some", unit: "grinds")
147
- ]
148
- end
149
- let(:steps) { [] }
150
-
151
- it "handles missing quantities and units gracefully" do
152
- expected = <<~OUTPUT.strip
153
- Ingredients:
154
- onion 1
155
- olive oil drizzle
156
- salt some
157
- pepper some grinds
158
- OUTPUT
159
-
160
- expect(formatter.to_s).to eq(expected)
161
- end
162
- end
163
- end
164
-
165
- describe "#format_quantity_unit" do
166
- let(:formatter) { described_class.new(recipe) }
167
- let(:recipe) { double("recipe") }
25
+ it "formats steps with sequential numbering" do
26
+ lines = output.split("\n")
27
+ step_lines = lines.select { |line| line.match(/^\s*\d+\./) }
168
28
 
169
- it "formats quantity with unit" do
170
- ingredient = Cooklang::Ingredient.new(name: "flour", quantity: 125, unit: "g")
171
- expect(formatter.send(:format_quantity_unit, ingredient)).to eq("125 g")
29
+ expect(step_lines.size).to be >= 10
30
+ expect(step_lines.first).to match(/1\..*/)
31
+ expect(step_lines.last).to match(/\d+\..*/)
172
32
  end
173
33
 
174
- it "formats quantity without unit" do
175
- ingredient = Cooklang::Ingredient.new(name: "eggs", quantity: 3)
176
- expect(formatter.send(:format_quantity_unit, ingredient)).to eq("3")
34
+ it "includes ingredient names in step text" do
35
+ expect(output).to include("shallots")
36
+ expect(output).to include("garlic")
37
+ expect(output).to include("day-old rice")
177
38
  end
178
39
 
179
- it "formats unit without quantity" do
180
- ingredient = Cooklang::Ingredient.new(name: "olive oil", unit: "drizzle")
181
- expect(formatter.send(:format_quantity_unit, ingredient)).to eq("drizzle")
40
+ it "includes timer references in steps" do
41
+ expect(output).to include("2 minutes")
42
+ expect(output).to include("30 seconds")
43
+ expect(output).to include("quick scramble")
182
44
  end
183
45
 
184
- it "handles missing quantity and unit" do
185
- ingredient = Cooklang::Ingredient.new(name: "salt")
186
- expect(formatter.send(:format_quantity_unit, ingredient)).to eq("some")
46
+ it "includes cookware references" do
47
+ expect(output).to include("wok")
48
+ expect(output).to include("mixing bowl")
187
49
  end
188
50
  end
189
51
  end
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe "Canonical Metadata Support" do
6
+ let(:parser) { Cooklang::Parser.new }
7
+
8
+ describe "Complex YAML frontmatter" do
9
+ it "supports arrays in metadata" do
10
+ recipe_text = <<~COOK
11
+ ---
12
+ title: Pasta Dish
13
+ tags: [italian, pasta, quick]
14
+ diet:
15
+ - vegetarian
16
+ - gluten-free
17
+ ---
18
+ @pasta{200%g}
19
+ COOK
20
+
21
+ recipe = parser.parse(recipe_text)
22
+
23
+ expect(recipe.metadata["tags"]).to eq(["italian", "pasta", "quick"])
24
+ expect(recipe.metadata["diet"]).to eq(["vegetarian", "gluten-free"])
25
+ end
26
+
27
+ it "supports nested hash structures" do
28
+ recipe_text = <<~COOK
29
+ ---
30
+ title: Recipe
31
+ source:
32
+ name: My Cookbook
33
+ url: https://example.com/recipe
34
+ author: Jane Doe
35
+ time:
36
+ prep: 15 minutes
37
+ cook: 30 minutes
38
+ ---
39
+ @flour{100%g}
40
+ COOK
41
+
42
+ recipe = parser.parse(recipe_text)
43
+
44
+ expect(recipe.metadata["source"]).to be_a(Hash)
45
+ expect(recipe.metadata["source"]["name"]).to eq("My Cookbook")
46
+ expect(recipe.metadata["source"]["url"]).to eq("https://example.com/recipe")
47
+ expect(recipe.metadata["source"]["author"]).to eq("Jane Doe")
48
+
49
+ expect(recipe.metadata["time"]).to be_a(Hash)
50
+ expect(recipe.metadata["time"]["prep"]).to eq("15 minutes")
51
+ expect(recipe.metadata["time"]["cook"]).to eq("30 minutes")
52
+ end
53
+
54
+ it "supports mixed data types" do
55
+ recipe_text = <<~COOK
56
+ ---
57
+ title: Test Recipe
58
+ servings: 4
59
+ rating: 4.5
60
+ published: true
61
+ custom_field: Some value
62
+ numbers: [1, 2, 3]
63
+ ---
64
+ @salt
65
+ COOK
66
+
67
+ recipe = parser.parse(recipe_text)
68
+
69
+ expect(recipe.metadata["title"]).to eq("Test Recipe")
70
+ expect(recipe.metadata["servings"]).to eq(4)
71
+ expect(recipe.metadata["rating"]).to eq(4.5)
72
+ expect(recipe.metadata["published"]).to eq(true)
73
+ expect(recipe.metadata["custom_field"]).to eq("Some value")
74
+ expect(recipe.metadata["numbers"]).to eq([1, 2, 3])
75
+ end
76
+
77
+ it "raises error on invalid YAML" do
78
+ recipe_text = <<~COOK
79
+ ---
80
+ title: Test
81
+ tags: [invalid
82
+ not closed
83
+ ---
84
+ @salt
85
+ COOK
86
+
87
+ expect { parser.parse(recipe_text) }.to raise_error(Psych::SyntaxError)
88
+ end
89
+ end
90
+
91
+ describe "Canonical fields work as plain Hash values" do
92
+ let(:metadata) { Cooklang::Metadata.new }
93
+
94
+ it "stores servings as number or string" do
95
+ metadata["servings"] = 4
96
+ expect(metadata["servings"]).to eq(4)
97
+
98
+ metadata["servings"] = "6 cups worth"
99
+ expect(metadata["servings"]).to eq("6 cups worth")
100
+ end
101
+
102
+ it "stores time fields as nested or flat" do
103
+ metadata["time"] = { "prep" => "10 min", "cook" => "20 min" }
104
+ expect(metadata["time"]["prep"]).to eq("10 min")
105
+ expect(metadata["time"]["cook"]).to eq("20 min")
106
+
107
+ metadata.clear
108
+ metadata["prep_time"] = "15 minutes"
109
+ metadata["cook_time"] = "25 minutes"
110
+ expect(metadata["prep_time"]).to eq("15 minutes")
111
+ expect(metadata["cook_time"]).to eq("25 minutes")
112
+ end
113
+
114
+ it "stores source as string or nested hash" do
115
+ metadata["source"] = {
116
+ "name" => "The Cookbook",
117
+ "url" => "https://example.com",
118
+ "author" => "Chef Name"
119
+ }
120
+ expect(metadata["source"]["name"]).to eq("The Cookbook")
121
+ expect(metadata["source"]["url"]).to eq("https://example.com")
122
+ expect(metadata["source"]["author"]).to eq("Chef Name")
123
+
124
+ metadata.clear
125
+ metadata["source"] = "Grandma's recipe box"
126
+ expect(metadata["source"]).to eq("Grandma's recipe box")
127
+ end
128
+
129
+ it "stores tags and diet as arrays" do
130
+ metadata["tags"] = ["pasta", "italian"]
131
+ expect(metadata["tags"]).to eq(["pasta", "italian"])
132
+
133
+ metadata["diet"] = ["vegan", "gluten-free"]
134
+ expect(metadata["diet"]).to eq(["vegan", "gluten-free"])
135
+ end
136
+
137
+ it "stores other canonical fields" do
138
+ metadata["category"] = "dessert"
139
+ metadata["description"] = "A delicious recipe"
140
+ metadata["difficulty"] = "easy"
141
+ metadata["cuisine"] = "Italian"
142
+ metadata["locale"] = "en_US"
143
+ metadata["images"] = ["url1", "url2"]
144
+
145
+ expect(metadata["category"]).to eq("dessert")
146
+ expect(metadata["description"]).to eq("A delicious recipe")
147
+ expect(metadata["difficulty"]).to eq("easy")
148
+ expect(metadata["cuisine"]).to eq("Italian")
149
+ expect(metadata["locale"]).to eq("en_US")
150
+ expect(metadata["images"]).to eq(["url1", "url2"])
151
+ end
152
+ end
153
+
154
+ describe "Custom metadata fields" do
155
+ it "allows any custom fields" do
156
+ metadata = Cooklang::Metadata.new(
157
+ "wine_pairing" => "Pinot Noir",
158
+ "my_rating" => 5,
159
+ "special_equipment" => ["thermometer", "stand mixer"],
160
+ "notes_to_self" => "Double the garlic next time"
161
+ )
162
+
163
+ expect(metadata["wine_pairing"]).to eq("Pinot Noir")
164
+ expect(metadata["my_rating"]).to eq(5)
165
+ expect(metadata["special_equipment"]).to eq(["thermometer", "stand mixer"])
166
+ expect(metadata["notes_to_self"]).to eq("Double the garlic next time")
167
+ end
168
+ end
169
+ end
@@ -81,161 +81,31 @@ RSpec.describe Cooklang::Metadata do
81
81
  end
82
82
  end
83
83
 
84
- describe "recipe-specific accessors" do
84
+ describe "direct access to metadata" do
85
85
  let(:metadata) { described_class.new }
86
86
 
87
- describe "#servings" do
88
- it "returns integer servings" do
89
- metadata["servings"] = "4"
87
+ it "stores any data types" do
88
+ metadata["servings"] = 4
89
+ metadata["tags"] = ["pasta", "italian"]
90
+ metadata["rating"] = 4.5
91
+ metadata["published"] = true
92
+ metadata["source"] = { "name" => "Cookbook", "url" => "https://example.com" }
90
93
 
91
- expect(metadata.servings).to eq(4)
92
- end
93
-
94
- it "returns nil when not set" do
95
- expect(metadata.servings).to be_nil
96
- end
97
- end
98
-
99
- describe "#servings=" do
100
- it "sets servings" do
101
- metadata.servings = 6
102
-
103
- expect(metadata["servings"]).to eq(6)
104
- end
105
- end
106
-
107
- describe "#prep_time" do
108
- it "returns prep_time" do
109
- metadata["prep_time"] = "15 minutes"
110
-
111
- expect(metadata.prep_time).to eq("15 minutes")
112
- end
113
-
114
- it "returns prep-time as fallback" do
115
- metadata["prep-time"] = "15 minutes"
116
-
117
- expect(metadata.prep_time).to eq("15 minutes")
118
- end
119
-
120
- it "returns nil when not set" do
121
- expect(metadata.prep_time).to be_nil
122
- end
123
- end
124
-
125
- describe "#prep_time=" do
126
- it "sets prep_time" do
127
- metadata.prep_time = "20 minutes"
128
-
129
- expect(metadata["prep_time"]).to eq("20 minutes")
130
- end
131
- end
132
-
133
- describe "#cook_time" do
134
- it "returns cook_time" do
135
- metadata["cook_time"] = "30 minutes"
136
-
137
- expect(metadata.cook_time).to eq("30 minutes")
138
- end
139
-
140
- it "returns cook-time as fallback" do
141
- metadata["cook-time"] = "30 minutes"
142
-
143
- expect(metadata.cook_time).to eq("30 minutes")
144
- end
145
- end
146
-
147
- describe "#cook_time=" do
148
- it "sets cook_time" do
149
- metadata.cook_time = "25 minutes"
150
-
151
- expect(metadata["cook_time"]).to eq("25 minutes")
152
- end
153
- end
154
-
155
- describe "#total_time" do
156
- it "returns total_time" do
157
- metadata["total_time"] = "45 minutes"
158
-
159
- expect(metadata.total_time).to eq("45 minutes")
160
- end
161
-
162
- it "returns total-time as fallback" do
163
- metadata["total-time"] = "45 minutes"
164
-
165
- expect(metadata.total_time).to eq("45 minutes")
166
- end
167
- end
168
-
169
- describe "#total_time=" do
170
- it "sets total_time" do
171
- metadata.total_time = "50 minutes"
172
-
173
- expect(metadata["total_time"]).to eq("50 minutes")
174
- end
175
- end
176
-
177
- describe "#title" do
178
- it "returns title" do
179
- metadata["title"] = "Chocolate Cake"
180
-
181
- expect(metadata.title).to eq("Chocolate Cake")
182
- end
183
- end
184
-
185
- describe "#title=" do
186
- it "sets title" do
187
- metadata.title = "Vanilla Cake"
188
-
189
- expect(metadata["title"]).to eq("Vanilla Cake")
190
- end
191
- end
192
-
193
- describe "#source" do
194
- it "returns source" do
195
- metadata["source"] = "cookbook.com"
196
-
197
- expect(metadata.source).to eq("cookbook.com")
198
- end
199
- end
200
-
201
- describe "#source=" do
202
- it "sets source" do
203
- metadata.source = "my-blog.com"
204
-
205
- expect(metadata["source"]).to eq("my-blog.com")
206
- end
207
- end
208
-
209
- describe "#tags" do
210
- it "returns array when tags is array" do
211
- metadata["tags"] = ["dessert", "chocolate"]
212
-
213
- expect(metadata.tags).to eq(["dessert", "chocolate"])
214
- end
215
-
216
- it "splits string tags on comma" do
217
- metadata["tags"] = "dessert, chocolate, sweet"
218
-
219
- expect(metadata.tags).to eq(["dessert", "chocolate", "sweet"])
220
- end
221
-
222
- it "returns empty array when not set" do
223
- expect(metadata.tags).to eq([])
224
- end
225
-
226
- it "returns empty array for non-string, non-array values" do
227
- metadata["tags"] = 123
228
-
229
- expect(metadata.tags).to eq([])
230
- end
94
+ expect(metadata["servings"]).to eq(4)
95
+ expect(metadata["tags"]).to eq(["pasta", "italian"])
96
+ expect(metadata["rating"]).to eq(4.5)
97
+ expect(metadata["published"]).to eq(true)
98
+ expect(metadata["source"]).to eq({ "name" => "Cookbook", "url" => "https://example.com" })
231
99
  end
232
100
 
233
- describe "#tags=" do
234
- it "sets tags" do
235
- metadata.tags = ["breakfast", "quick"]
101
+ it "allows any custom fields" do
102
+ metadata["wine_pairing"] = "Pinot Noir"
103
+ metadata["my_custom_field"] = { "nested" => "data" }
104
+ metadata["notes"] = ["note1", "note2"]
236
105
 
237
- expect(metadata["tags"]).to eq(["breakfast", "quick"])
238
- end
106
+ expect(metadata["wine_pairing"]).to eq("Pinot Noir")
107
+ expect(metadata["my_custom_field"]).to eq({ "nested" => "data" })
108
+ expect(metadata["notes"]).to eq(["note1", "note2"])
239
109
  end
240
110
  end
241
111
  end
@@ -143,7 +143,7 @@ RSpec.describe Cooklang::Recipe do
143
143
 
144
144
  let(:expected_hash) do
145
145
  {
146
- metadata: { map: { "title" => "Test Recipe", "servings" => "4", "prep_time" => "15 minutes" } },
146
+ metadata: { map: { "title" => "Test Recipe", "servings" => 4, "prep_time" => "15 minutes" } },
147
147
  sections: [
148
148
  {
149
149
  name: nil,
@@ -174,7 +174,7 @@ RSpec.describe Cooklang::Recipe do
174
174
 
175
175
  let(:expected_json) do
176
176
  {
177
- "metadata" => { "map" => { "title" => "Test Recipe", "servings" => "4", "prep_time" => "15 minutes" } },
177
+ "metadata" => { "map" => { "title" => "Test Recipe", "servings" => 4, "prep_time" => "15 minutes" } },
178
178
  "sections" => [
179
179
  {
180
180
  "name" => nil,
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cooklang
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Brooks
@@ -13,16 +13,16 @@ dependencies:
13
13
  name: bundler
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - "~>"
16
+ - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: '2.7'
18
+ version: '2.1'
19
19
  type: :development
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
- - - "~>"
23
+ - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: '2.7'
25
+ version: '2.1'
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: rake
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -124,7 +124,6 @@ files:
124
124
  - ".rspec"
125
125
  - ".rubocop.yml"
126
126
  - Gemfile
127
- - Gemfile.lock
128
127
  - LICENSE.txt
129
128
  - README.md
130
129
  - Rakefile
@@ -158,8 +157,13 @@ files:
158
157
  - spec/comprehensive_spec.rb
159
158
  - spec/cooklang_spec.rb
160
159
  - spec/fixtures/canonical.yaml
160
+ - spec/fixtures/comprehensive_recipe.cook
161
+ - spec/formatters/formatter_spec.rb
162
+ - spec/formatters/hash_spec.rb
163
+ - spec/formatters/json_spec.rb
161
164
  - spec/formatters/text_spec.rb
162
165
  - spec/integration/canonical_spec.rb
166
+ - spec/integration/metadata_canonical_spec.rb
163
167
  - spec/lexer_spec.rb
164
168
  - spec/models/cookware_spec.rb
165
169
  - spec/models/ingredient_spec.rb
@@ -186,7 +190,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
186
190
  requirements:
187
191
  - - ">="
188
192
  - !ruby/object:Gem::Version
189
- version: 3.2.0
193
+ version: 2.7.0
190
194
  required_rubygems_version: !ruby/object:Gem::Requirement
191
195
  requirements:
192
196
  - - ">="
@@ -200,8 +204,13 @@ test_files:
200
204
  - spec/comprehensive_spec.rb
201
205
  - spec/cooklang_spec.rb
202
206
  - spec/fixtures/canonical.yaml
207
+ - spec/fixtures/comprehensive_recipe.cook
208
+ - spec/formatters/formatter_spec.rb
209
+ - spec/formatters/hash_spec.rb
210
+ - spec/formatters/json_spec.rb
203
211
  - spec/formatters/text_spec.rb
204
212
  - spec/integration/canonical_spec.rb
213
+ - spec/integration/metadata_canonical_spec.rb
205
214
  - spec/lexer_spec.rb
206
215
  - spec/models/cookware_spec.rb
207
216
  - spec/models/ingredient_spec.rb