cooklang 0.1.0 → 1.0.0

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.
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cooklang
4
+ class Recipe
5
+ attr_reader :ingredients, :cookware, :timers, :steps, :metadata, :sections, :notes
6
+
7
+ def initialize(ingredients:, cookware:, timers:, steps:, metadata:, sections: [], notes: [])
8
+ @ingredients = ingredients.freeze
9
+ @cookware = cookware.freeze
10
+ @timers = timers.freeze
11
+ @steps = steps.freeze
12
+ @metadata = metadata
13
+ @sections = sections.freeze
14
+ @notes = notes.freeze
15
+ end
16
+
17
+ def ingredients_hash
18
+ @ingredients.each_with_object({}) do |ingredient, hash|
19
+ hash[ingredient.name] = {
20
+ quantity: ingredient.quantity,
21
+ unit: ingredient.unit
22
+ }.compact
23
+ end
24
+ end
25
+
26
+ def steps_text
27
+ @steps.map(&:to_text)
28
+ end
29
+
30
+ def to_h
31
+ {
32
+ ingredients: @ingredients.map(&:to_h),
33
+ cookware: @cookware.map(&:to_h),
34
+ timers: @timers.map(&:to_h),
35
+ steps: @steps.map(&:to_h),
36
+ metadata: @metadata.to_h,
37
+ sections: @sections.map(&:to_h),
38
+ notes: @notes.map(&:to_h)
39
+ }
40
+ end
41
+
42
+ def ==(other)
43
+ return false unless other.is_a?(Recipe)
44
+
45
+ ingredients == other.ingredients &&
46
+ cookware == other.cookware &&
47
+ timers == other.timers &&
48
+ steps == other.steps &&
49
+ metadata == other.metadata &&
50
+ sections == other.sections &&
51
+ notes == other.notes
52
+ end
53
+
54
+ def eql?(other)
55
+ self == other
56
+ end
57
+
58
+ def hash
59
+ [ingredients, cookware, timers, steps, metadata].hash
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cooklang
4
+ class Section
5
+ attr_reader :name, :steps
6
+
7
+ def initialize(name:, steps: [])
8
+ @name = name&.to_s&.freeze
9
+ @steps = steps.freeze
10
+ end
11
+
12
+ def to_s
13
+ name || "Section"
14
+ end
15
+
16
+ def to_h
17
+ {
18
+ name: name,
19
+ steps: steps.map(&:to_h)
20
+ }.compact
21
+ end
22
+
23
+ def ==(other)
24
+ other.is_a?(Section) &&
25
+ name == other.name &&
26
+ steps == other.steps
27
+ end
28
+
29
+ def hash
30
+ [name, steps].hash
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cooklang
4
+ class Step
5
+ attr_reader :segments
6
+
7
+ def initialize(segments:)
8
+ @segments = segments.freeze
9
+ end
10
+
11
+ def to_text
12
+ @segments.map do |segment|
13
+ case segment
14
+ when Hash
15
+ case segment[:type]
16
+ when :ingredient
17
+ segment[:name]
18
+ when :cookware
19
+ segment[:name]
20
+ when :timer
21
+ segment[:name] || "timer"
22
+ else
23
+ segment[:value] || ""
24
+ end
25
+ when String
26
+ segment
27
+ when Ingredient
28
+ segment.name
29
+ when Cookware
30
+ segment.name
31
+ when Timer
32
+ if segment.name
33
+ "#{segment.name} for #{segment.duration} #{segment.unit}"
34
+ elsif segment.duration && segment.unit
35
+ "for #{segment.duration} #{segment.unit}"
36
+ else
37
+ "timer"
38
+ end
39
+ else
40
+ segment.to_s
41
+ end
42
+ end.join.rstrip
43
+ end
44
+
45
+ def to_h
46
+ {
47
+ segments: @segments
48
+ }
49
+ end
50
+
51
+ def ==(other)
52
+ return false unless other.is_a?(Step)
53
+
54
+ segments == other.segments
55
+ end
56
+
57
+ def eql?(other)
58
+ self == other
59
+ end
60
+
61
+ def hash
62
+ segments.hash
63
+ end
64
+
65
+ def ingredients_used
66
+ @segments.filter_map { |segment| segment[:name] if segment.is_a?(Hash) && segment[:type] == :ingredient }
67
+ end
68
+
69
+ def cookware_used
70
+ @segments.filter_map { |segment| segment[:name] if segment.is_a?(Hash) && segment[:type] == :cookware }
71
+ end
72
+
73
+ def timers_used
74
+ @segments.select { |segment| segment.is_a?(Hash) && segment[:type] == :timer }
75
+ end
76
+
77
+ def has_ingredients?
78
+ ingredients_used.any?
79
+ end
80
+
81
+ def has_cookware?
82
+ cookware_used.any?
83
+ end
84
+
85
+ def has_timers?
86
+ timers_used.any?
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cooklang
4
+ class Timer
5
+ attr_reader :name, :duration, :unit
6
+
7
+ def initialize(duration:, unit:, name: nil)
8
+ @name = name&.to_s&.freeze
9
+ @duration = duration
10
+ @unit = unit.to_s.freeze
11
+ end
12
+
13
+ def to_s
14
+ result = ""
15
+ result += "#{@name}: " if @name
16
+ result += "#{@duration} #{@unit}"
17
+ result
18
+ end
19
+
20
+ def to_h
21
+ {
22
+ name: @name,
23
+ duration: @duration,
24
+ unit: @unit
25
+ }.compact
26
+ end
27
+
28
+ def ==(other)
29
+ return false unless other.is_a?(Timer)
30
+
31
+ name == other.name &&
32
+ duration == other.duration &&
33
+ unit == other.unit
34
+ end
35
+
36
+ def eql?(other)
37
+ self == other
38
+ end
39
+
40
+ def hash
41
+ [name, duration, unit].hash
42
+ end
43
+
44
+ def total_seconds
45
+ case @unit.downcase
46
+ when "second", "seconds", "sec", "s"
47
+ @duration
48
+ when "minute", "minutes", "min", "m"
49
+ @duration * 60
50
+ when "hour", "hours", "hr", "h"
51
+ @duration * 3600
52
+ when "day", "days", "d"
53
+ @duration * 86_400
54
+ else
55
+ @duration
56
+ end
57
+ end
58
+
59
+ def has_name?
60
+ !@name.nil?
61
+ end
62
+ end
63
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cooklang
4
- VERSION = "0.1.0"
4
+ VERSION = "1.0.0"
5
5
  end
data/lib/cooklang.rb CHANGED
@@ -1,8 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "cooklang/version"
4
+ require_relative "cooklang/lexer"
5
+ require_relative "cooklang/parser"
6
+ require_relative "cooklang/recipe"
7
+ require_relative "cooklang/ingredient"
8
+ require_relative "cooklang/cookware"
9
+ require_relative "cooklang/timer"
10
+ require_relative "cooklang/step"
11
+ require_relative "cooklang/metadata"
12
+ require_relative "cooklang/section"
13
+ require_relative "cooklang/note"
14
+ require_relative "cooklang/formatter"
15
+ require_relative "cooklang/formatters/text"
4
16
 
5
17
  module Cooklang
6
18
  class Error < StandardError; end
7
- # Your code goes here...
19
+ class ParseError < Error; end
20
+
21
+ def self.parse(input)
22
+ parser = Parser.new
23
+ parser.parse(input)
24
+ end
25
+
26
+ def self.parse_file(file_path)
27
+ parse(File.read(file_path))
28
+ end
8
29
  end
metadata CHANGED
@@ -1,84 +1,46 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cooklang
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Brooks
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2022-03-08 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: rubocop
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1.23'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '1.23'
27
- - !ruby/object:Gem::Dependency
28
- name: rubocop-performance
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '1.12'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '1.12'
41
- - !ruby/object:Gem::Dependency
42
- name: rubocop-rspec
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '2.6'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '2.6'
55
- description:
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: Cooklang is a markup language for recipes that allows you to define ingredients,
13
+ cookware, timers, and metadata in a structured way. This gem provides a Ruby parser
14
+ for Cooklang files.
56
15
  email:
57
16
  - james@jamesbrooks.net
58
17
  executables: []
59
18
  extensions: []
60
19
  extra_rdoc_files: []
61
20
  files:
62
- - ".rubocop.yml"
63
- - ".ruby-version"
64
- - CHANGELOG.md
65
- - Gemfile
66
- - Gemfile.lock
67
- - LICENSE
21
+ - LICENSE.txt
68
22
  - README.md
69
- - Rakefile
70
- - bin/console
71
- - bin/setup
72
23
  - lib/cooklang.rb
24
+ - lib/cooklang/cookware.rb
25
+ - lib/cooklang/formatter.rb
26
+ - lib/cooklang/formatters/text.rb
27
+ - lib/cooklang/ingredient.rb
28
+ - lib/cooklang/lexer.rb
29
+ - lib/cooklang/metadata.rb
30
+ - lib/cooklang/note.rb
31
+ - lib/cooklang/parser.rb
32
+ - lib/cooklang/recipe.rb
33
+ - lib/cooklang/section.rb
34
+ - lib/cooklang/step.rb
35
+ - lib/cooklang/timer.rb
73
36
  - lib/cooklang/version.rb
74
- homepage: https://github.com/jamesbrooks/cooklang-rb
75
- licenses: []
37
+ homepage: https://github.com/jamesbrooks/cooklang
38
+ licenses:
39
+ - MIT
76
40
  metadata:
77
- homepage_uri: https://github.com/jamesbrooks/cooklang-rb
78
- source_code_uri: https://github.com/jamesbrooks/cooklang-rb
79
- changelog_uri: https://github.com/jamesbrooks/cooklang-rb/CHANGELOG.md
41
+ homepage_uri: https://github.com/jamesbrooks/cooklang
42
+ source_code_uri: https://github.com/jamesbrooks/cooklang
80
43
  rubygems_mfa_required: 'true'
81
- post_install_message:
82
44
  rdoc_options: []
83
45
  require_paths:
84
46
  - lib
@@ -86,15 +48,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
86
48
  requirements:
87
49
  - - ">="
88
50
  - !ruby/object:Gem::Version
89
- version: 2.7.5
51
+ version: 3.2.0
90
52
  required_rubygems_version: !ruby/object:Gem::Requirement
91
53
  requirements:
92
54
  - - ">="
93
55
  - !ruby/object:Gem::Version
94
56
  version: '0'
95
57
  requirements: []
96
- rubygems_version: 3.1.6
97
- signing_key:
58
+ rubygems_version: 3.7.1
98
59
  specification_version: 4
99
- summary: Ruby library for working with Cooklang
60
+ summary: A Ruby parser for the Cooklang recipe markup language.
100
61
  test_files: []
data/.rubocop.yml DELETED
@@ -1,126 +0,0 @@
1
- require:
2
- - rubocop-performance
3
- - rubocop-rspec
4
-
5
- AllCops:
6
- TargetRubyVersion: 2.7.5
7
- NewCops: enable
8
-
9
- Bundler/OrderedGems:
10
- Enabled: false
11
-
12
- Layout/AccessModifierIndentation:
13
- EnforcedStyle: outdent
14
-
15
- Layout/CaseIndentation:
16
- EnforcedStyle: end
17
-
18
- Layout/EmptyLinesAroundAccessModifier:
19
- EnforcedStyle: only_before
20
-
21
- Layout/EndAlignment:
22
- EnforcedStyleAlignWith: variable
23
-
24
- Layout/FirstArrayElementIndentation:
25
- EnforcedStyle: consistent
26
-
27
- Layout/FirstHashElementIndentation:
28
- EnforcedStyle: consistent
29
-
30
- Layout/LineLength:
31
- Max: 120
32
-
33
- Layout/MultilineMethodCallIndentation:
34
- EnforcedStyle: indented
35
-
36
- Layout/SpaceInsideArrayLiteralBrackets:
37
- EnforcedStyle: space
38
-
39
- Metrics/AbcSize:
40
- Enabled: false
41
-
42
- Metrics/BlockLength:
43
- Enabled: false
44
-
45
- Metrics/ClassLength:
46
- Enabled: false
47
-
48
- Metrics/CyclomaticComplexity:
49
- Enabled: false
50
-
51
- Metrics/MethodLength:
52
- Enabled: false # with constructing a lot of bulky hashes this cop becomes noisy
53
-
54
- Metrics/ParameterLists:
55
- CountKeywordArgs: false
56
-
57
- Metrics/PerceivedComplexity:
58
- Enabled: false
59
-
60
- Naming/AccessorMethodName:
61
- Enabled: false
62
-
63
- Naming/InclusiveLanguage:
64
- Enabled: false
65
-
66
- RSpec/EmptyLineAfterSubject:
67
- Enabled: false
68
-
69
- RSpec/ExampleLength:
70
- Max: 10
71
-
72
- RSpec/MultipleSubjects:
73
- Enabled: false
74
-
75
- Style/AndOr:
76
- Enabled: false
77
-
78
- Style/ClassAndModuleChildren:
79
- Enabled: false
80
-
81
- Style/ConditionalAssignment:
82
- Enabled: false
83
-
84
- Style/Documentation:
85
- Enabled: false
86
-
87
- Style/FrozenStringLiteralComment:
88
- Enabled: false
89
-
90
- Style/GuardClause:
91
- Enabled: false
92
-
93
- Style/HashEachMethods:
94
- Enabled: true
95
-
96
- Style/HashTransformKeys:
97
- Enabled: false
98
-
99
- Style/HashTransformValues:
100
- Enabled: false
101
-
102
- Style/IfUnlessModifier:
103
- Enabled: false
104
-
105
- Style/IfWithBooleanLiteralBranches:
106
- Enabled: false
107
-
108
- Style/Lambda:
109
- Enabled: false
110
-
111
- Style/QuotedSymbols:
112
- Enabled: false
113
-
114
- Style/StringLiterals:
115
- Enabled: true
116
- EnforcedStyle: double_quotes
117
-
118
- Style/StringLiteralsInInterpolation:
119
- Enabled: true
120
- EnforcedStyle: double_quotes
121
-
122
- Style/SymbolArray:
123
- Enabled: false
124
-
125
- Style/WordArray:
126
- Enabled: false
data/.ruby-version DELETED
@@ -1 +0,0 @@
1
- 2.7.5
data/CHANGELOG.md DELETED
@@ -1,5 +0,0 @@
1
- ## [Unreleased]
2
-
3
- ## [0.1.0] - 2022-03-08
4
-
5
- - Initial release
data/Gemfile DELETED
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source "https://rubygems.org"
4
-
5
- # Specify your gem's dependencies in cooklang.gemspec
6
- gemspec
data/Gemfile.lock DELETED
@@ -1,45 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- cooklang (0.1.0)
5
-
6
- GEM
7
- remote: https://rubygems.org/
8
- specs:
9
- ast (2.4.2)
10
- parallel (1.21.0)
11
- parser (3.1.1.0)
12
- ast (~> 2.4.1)
13
- rainbow (3.1.1)
14
- regexp_parser (2.2.1)
15
- rexml (3.2.5)
16
- rubocop (1.25.1)
17
- parallel (~> 1.10)
18
- parser (>= 3.1.0.0)
19
- rainbow (>= 2.2.2, < 4.0)
20
- regexp_parser (>= 1.8, < 3.0)
21
- rexml
22
- rubocop-ast (>= 1.15.1, < 2.0)
23
- ruby-progressbar (~> 1.7)
24
- unicode-display_width (>= 1.4.0, < 3.0)
25
- rubocop-ast (1.16.0)
26
- parser (>= 3.1.1.0)
27
- rubocop-performance (1.13.3)
28
- rubocop (>= 1.7.0, < 2.0)
29
- rubocop-ast (>= 0.4.0)
30
- rubocop-rspec (2.9.0)
31
- rubocop (~> 1.19)
32
- ruby-progressbar (1.11.0)
33
- unicode-display_width (2.1.0)
34
-
35
- PLATFORMS
36
- x86_64-darwin-21
37
-
38
- DEPENDENCIES
39
- cooklang!
40
- rubocop (~> 1.23)
41
- rubocop-performance (~> 1.12)
42
- rubocop-rspec (~> 2.6)
43
-
44
- BUNDLED WITH
45
- 2.2.33
data/Rakefile DELETED
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rubocop/rake_task"
5
-
6
- RuboCop::RakeTask.new
7
-
8
- task default: :rubocop
data/bin/console DELETED
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require "bundler/setup"
5
- require "cooklang"
6
-
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
10
- # (If you use this, don't forget to add pry to your Gemfile!)
11
- # require "pry"
12
- # Pry.start
13
-
14
- require "irb"
15
- IRB.start(__FILE__)
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here