class HomebrewAutomation::Formula

An in-memory, programmable representation of some Formula.rb Ruby source file, containing the definition of a Homebrew Bottle. See Homebrew docs for concepts: docs.brew.sh/Bottles.html

Instance methods produce new instances where sensible, leaving all instances free from mutation.

{#==} and {#hash} delegates to the underlying Parser::AST::Node.

Attributes

ast[R]

Public Class Methods

new(ast) click to toggle source

Take an post-parsing abstract syntax tree representation of a Homebrew Formula. This is mostly not intended for common use-cases.

@param ast [Parser::AST::Node]

# File lib/homebrew_automation/formula.rb, line 33
def initialize ast
  @ast = ast
end
parse_string(s) click to toggle source

Parse the string form of a Homebrew Formula source file into a {Formula}.

{#to_s} is the inverse of this method.

@return [Formula]

# File lib/homebrew_automation/formula.rb, line 25
def self.parse_string s
  Formula.new (Parser::CurrentRuby.parse s)
end

Public Instance Methods

==(o) click to toggle source

Both formulae are +==+, as per +Parser::AST::Node#==+.

In practice, I think this means both formulae are equivalent in terms of Ruby semantics.

@param o [Object] Expecting a {Formula}, really. @return [Boolean]

# File lib/homebrew_automation/formula.rb, line 106
def == o
  self.class == o.class && @ast == o.ast
end
Also aliased as: eql?
eql?(o)
Alias for: ==
hash() click to toggle source

Hash of the Parser::AST::Node

@return [Integer]

# File lib/homebrew_automation/formula.rb, line 113
def hash
  @ast.hash
end
put_bottle(os, sha256) click to toggle source

Insert or replace the Homebrew Bottle for a given OS

@param os [String] Operating system name, e.g. “yosemite”, as per Homebrew's conventions @param sha256 [String] Checksum of the binary “Bottle” tarball @return [Formula] a new instance of Formula with the changes applied

# File lib/homebrew_automation/formula.rb, line 78
def put_bottle os, sha256
  Formula.new update(
    @ast,
    bot_begin_path,
    put_bottle_version(os, sha256))
end
put_sdist(url, sha256) click to toggle source

Update two fields together

@param url [String] URL of source tarball @param sha256 [String] SHA256 sum of source tarball @return [Formula]

# File lib/homebrew_automation/formula.rb, line 68
def put_sdist url, sha256
  update_field("url", url).
  update_field("sha256", sha256)
end
rm_all_bottles() click to toggle source

Remove sha256 references to all bottles

This may leave the Formula in a non-standard state. Please add at lease one bottle referene back, otherwise why are you even declaring a bottles block at all.

@return [Formula] a new instance of Formula with the changes applied

# File lib/homebrew_automation/formula.rb, line 92
def rm_all_bottles
  Formula.new update(
    @ast,
    bot_begin_path,
    rm_all_bottle_sha256s)
end
to_s() click to toggle source

Produce Homebrew Formula source code as a string, suitable for saving as a Ruby source file.

This is the inverse of {.parse_string}.

@return [String]

# File lib/homebrew_automation/formula.rb, line 43
def to_s
  Unparser.unparse @ast
end
update_field(field, value) click to toggle source

Update a top-level field in the Formula

@param field [String] Name of the Formula field, e.g. url @param value [String] Value of the Formula field, e.g. https://github.com/easoncxz/homebrew-automation @return [Formula] a new instance of Formula with the changes applied

# File lib/homebrew_automation/formula.rb, line 52
def update_field field, value
  Formula.new update(
    @ast,
    [ by_type('begin'),
      by_both(
        by_type('send'),
        by_msg(field)),
      by_type('str')],
    -> (n) { n.updated(nil, [value]) })
end

Private Instance Methods

bot_begin_path() click to toggle source

Path to the :begin node

bot_begin_path
Choice

type Choice = Proc (Node -> Bool)

# File lib/homebrew_automation/formula.rb, line 127
def bot_begin_path
  [ by_type('begin'),
    by_both(
      by_type('block'),
      by_child(
        by_both(
          by_type('send'),
          by_msg('bottle')))),
    by_type('begin')]
end
by_both(p, q) click to toggle source

by_both

:: Proc (Node -> Bool)
-> Proc (Node -> Bool)
-> Proc (Node -> Bool)
# File lib/homebrew_automation/formula.rb, line 217
def by_both p, q
  -> (n) { p.(n) && q.(n) }
end
by_child(p) click to toggle source

Matches if one of the node's children matches the given p

by_child

Proc (Node -> Bool) -> Proc (Node -> Bool)

# File lib/homebrew_automation/formula.rb, line 237
def by_child p
  -> (n) {
    n &&
    n.is_a?(AST::Node) &&
    n.children.select(&p).size > 0
  }
end
by_msg(msg) click to toggle source
by_msg

String -> Proc (Node -> Bool)

# File lib/homebrew_automation/formula.rb, line 222
def by_msg msg
  -> (n) { n.children[1] == msg.to_sym }
end
by_os(os) click to toggle source

Matches if this :send node expresses the give sha256 sum

by_os

String -> Proc (Node -> Bool)

# File lib/homebrew_automation/formula.rb, line 247
def by_os os
  -> (n) {
    zoom_in(n, [
      by_type('hash'),
      by_type('pair'),
      by_type('sym')])
    .children[0] == os.to_sym
  }
end
by_type(type) click to toggle source
by_type

String -> Proc (Node -> Bool)

# File lib/homebrew_automation/formula.rb, line 227
def by_type type
  -> (n) {
    n &&
    n.is_a?(AST::Node) &&
    n.type == type.to_sym
  }
end
new_sha256(sha256, os) click to toggle source

Build a new AST Node String -> String -> Node

# File lib/homebrew_automation/formula.rb, line 168
def new_sha256 sha256, os
  # Unparser doesn't like Sexp, so let's bring
  # own own bit of "source code" inline.
  sha256_send = Parser::CurrentRuby.parse(
    'sha256 "checksum-here" => :some_os')
  with_sha256 = update(
    sha256_send,
    [ by_type('hash'),
      by_type('pair'),
      by_type('str') ],
    -> (n) { n.updated(nil, [sha256]) })
  with_sha256_and_os = update(
    with_sha256,
    [ by_type('hash'),
      by_type('pair'),
      by_type('sym') ],
    -> (n) { n.updated(nil, [os.to_sym]) })
  with_sha256_and_os
end
put_bottle_version(os, sha256) click to toggle source

Tricky: this is an insert-or-update

put_bottle_version

String -> String -> Proc (Node -> Node)

# File lib/homebrew_automation/formula.rb, line 140
def put_bottle_version os, sha256
  -> (bot_begin) {
    bot_begin.updated(
      nil,  # keep the node type unchanged
      bot_begin.children.reject(
        # Get rid of any existing matching ones
        &by_both(
          by_msg('sha256'),
          by_os(os))
      # Then add the one we want
      ).push(new_sha256(sha256, os)))
  }
end
rm_all_bottle_sha256s() click to toggle source

Drop all bottles

rm_all_bottle_sha256s

Proc (Node -> Node)

# File lib/homebrew_automation/formula.rb, line 156
def rm_all_bottle_sha256s
  -> (bot_begin) {
    bot_begin.updated(
      nil,  # keep the node type unchanged
      bot_begin.children.reject(
        # Get ride of all bottles condtionlessly
        &by_msg('sha256')))
  }
end
update(node, path, fn) click to toggle source
update

Node -> [Choice] -> Proc (Node -> Node) -> Node

# File lib/homebrew_automation/formula.rb, line 189
def update node, path, fn
  if path.length == 0 then
    fn.(node)
  else
    choose, *rest = path
    node.updated(
      nil,    # Don't change node type
      node.children.map do |c|
        choose.(c) ? update(c, rest, fn) : c
      end)
  end
end
zoom_in(node, path) click to toggle source
zoom_in

Node -> [Choice] -> Node

# File lib/homebrew_automation/formula.rb, line 203
def zoom_in node, path
  if path.length == 0 then
    node
  else
    choose, *rest = path
    chosen = node.children.select(&choose).first
    zoom_in chosen, rest
  end
end