{"id":17942652,"url":"https://github.com/petercamilleri/format_engine","last_synced_at":"2026-01-19T09:34:37.513Z","repository":{"id":56847536,"uuid":"37821521","full_name":"PeterCamilleri/format_engine","owner":"PeterCamilleri","description":"A gem to support creating string formatting and parsing routines like strftime and strptime for any class that needs formatting and parsing.","archived":false,"fork":false,"pushed_at":"2021-05-19T18:55:57.000Z","size":114,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-04-24T21:41:44.879Z","etag":null,"topics":["engine","formatting","parser","parsing","parsing-routines","ruby","rubygem"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PeterCamilleri.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-06-21T18:56:41.000Z","updated_at":"2023-03-05T04:20:47.000Z","dependencies_parsed_at":"2022-09-12T11:44:28.054Z","dependency_job_id":null,"html_url":"https://github.com/PeterCamilleri/format_engine","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PeterCamilleri%2Fformat_engine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PeterCamilleri%2Fformat_engine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PeterCamilleri%2Fformat_engine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PeterCamilleri%2Fformat_engine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PeterCamilleri","download_url":"https://codeload.github.com/PeterCamilleri/format_engine/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247809992,"owners_count":20999821,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["engine","formatting","parser","parsing","parsing-routines","ruby","rubygem"],"created_at":"2024-10-29T03:06:38.924Z","updated_at":"2026-01-19T09:34:37.470Z","avatar_url":"https://github.com/PeterCamilleri.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# FormatEngine\n\nThe FormatEngine gem contains the common support code needed to support\nstring formatting and parsing routines like strftime and strptime.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'format_engine'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install format_engine\n\nThe format_engine gem itself is found at: ( https://rubygems.org/gems/format_engine )\n\n## Usage\n\nIn general the format engine allows for the easy creation of formatters and\nparsers. This is done using the attr_formatter and attr_parser methods that\nwork in a manner analogous to attr_reader etc for creating access to instance\ndata.\n\n#### attr_formatter\n\nCreates a data formatting facility for the specified class.\n\n_Prerequisites_\n\u003cbr\u003eThe class must 'extend FormatEngine::AttrFormatter'\n\n_Arguments_\n* method_symbol - the name of the formatter instance method created by this\nmethod\n* library - a hash mapping specifications to actions (lambda blocks)\n\n_Returns_\n\u003cbr\u003eThe formatting engine created for the formatter.\n\n_The created instance method_\n\u003cbr\u003eThis method creates an instance method that provides formatted text from\nan object. This takes the form:\n\n    an_object.method_name(format_string) -\u003e a_formatted_string\n\n#### attr_parse\n\nCreates a data parsing facility for the specified class.\n\n_Prerequisites_\n\u003cbr\u003eThe class must 'extend FormatEngine::AttrParser'\n\n_Arguments_\n* method_symbol - the name of the parser class method created by this method.\n* library - a hash mapping specifications to actions (lambda blocks)\n\n_Returns_\n\u003cbr\u003eThe formatting engine created for the parser.\n\n_The created class method_\n\u003cbr\u003eThis method creates an class method that parses formatted text into a new\ninstance of the class. This takes the form:\n\n    AClass.method_name(input_string, format_string) -\u003e an_object\n\n_Extras_\n\u003cbr\u003eAfter the parsing method is completed, the unparsed attribute of the parser\nengine contains the un-processed portion of the input string. If no parsing\nhas been performed, this is an empty string.\n\n#### Special entries\nThe library has two special entries keyed by symbols instead of strings.\nThese are\n\n* :before - references a block that is executed before the formatting or\nparsing process starts.\n* :after - references a block that is executed after the formatting or\nparsing process finishes.\n\nBy default, both of these entries take no action.\n\n### Example\nThe following example is found in the mocks folder:\n\n```ruby\nrequire 'format_engine'\n\n#A demo class for the format_engine gem.\n\nclass Customer\n  extend FormatEngine::AttrFormatter\n  extend FormatEngine::AttrParser\n\n  #Demo customer first name.\n  attr_reader :first_name\n\n  #Demo customer last name\n  attr_reader :last_name\n\n  #Demo customer age\n  attr_reader :age\n\n  #Demo defn of the strfmt method for formatted string output!\n  attr_formatter :strfmt,\n  {\"%a\" =\u003e lambda {cat \"%#{fmt.width_str}d\" % src.age },\n   \"%f\" =\u003e lambda {cat \"%#{fmt.width_str}s\" % src.first_name },\n   \"%l\" =\u003e lambda {cat \"%#{fmt.width_str}s\" % src.last_name  }\n  }\n\n  #Demo defn of the strprs method for formatted string input!\n  attr_parser :strprs,\n  {\"%a\"   =\u003e lambda { tmp[:age] = found.to_i if parse(/(\\d)+/ ) },\n   \"%f\"   =\u003e lambda { tmp[:fn] = found if parse(/(\\w)+/ ) },\n   \"%l\"   =\u003e lambda { tmp[:ln] = found if parse(/(\\w)+/ ) },\n   :after =\u003e lambda { set dst.new(tmp[:fn], tmp[:ln], tmp[:age]) }\n  }\n\n  #Create an instance of the demo customer.\n  def initialize(first_name, last_name, age)\n    @first_name, @last_name, @age = first_name, last_name, age\n  end\nend\n\n#Then later in the code...\n\ncust = Customer.strprs('Jane, Smith 22', \"%f, %l %a\")\n\n# and in another part of Gotham City...\n\nputs cust.strfmt('%f %l is %a years old.')\n\n# Prints out: Jane Smith is 22 years old.\n\n#Etc, etc, etc ...\n\n```\n\n## In the wild...\n\nAn example of the format_engine in action is the ruby_sscanf\ngem (https://github.com/PeterCamilleri/ruby_sscanf) where it is used to\nprovide a facility for parsing strings and extracting data along with a\ngenerous helping of performance enhancement.\n\n## Format Specification\n\nFormat String Specification Syntax (Regex):\n\nThe parsing of format specification strings is based on the following regular\nexpression. This expression is applied repeatedly until all the specifications\nhave been extracted from the input string.\n\n    PARSE_REGEX =\n      %r{(?\u003clead\u003e  (^|(?\u003c=[^\\\\]))%){0}\n         (?\u003cflags\u003e [~@#$^\u0026*=?_\u003c\u003e|!]*){0}\n\n         (?\u003cvar\u003e \\g\u003clead\u003e\\g\u003cflags\u003e[-+]?(\\d+(\\.\\d+)?)?[a-zA-Z]){0}\n         (?\u003cset\u003e \\g\u003clead\u003e\\g\u003cflags\u003e(\\d+(,\\d+)?)?\\[([^\\]\\\\]|\\\\.)+\\]){0}\n         (?\u003crgx\u003e \\g\u003clead\u003e\\g\u003cflags\u003e\\/([^\\\\ \\/]|\\\\.)*\\/([imx]*)){0}\n         (?\u003cper\u003e \\g\u003clead\u003e%){0}\n\n         \\g\u003cvar\u003e | \\g\u003cset\u003e | \\g\u003crgx\u003e | \\g\u003cper\u003e\n        }x\n\n### Var\nA format specification of the classical form:\n\n    %[flags][+/-][width[.precision]]letter\n\n### Set\nA regular expression set (or un-set), used for parsing only, of the form:\n\n    %[flags][[min_width,]max_width]\"[\"[^]set_chars\"]\"\n\n### Rgx\nA full blown regular expression, used for parsing only, of the form:\n\n    %[flags]/regex stuff goes here/[options]\n\nSupported options are i, m, and x.\n\n### Per\nA %% which evaluates to a literal character %. This is the old school\nalternative to \\\\% which is preferred.\n\n### Literal\nText in between the various format specifications is treated as literal text.\nWhen formatting this literal text is output verbatim. When parsing, this text\nis expected in the input string.\n\n###Format Internals:\n\nThe format specification:\n```ruby\n\"Elapsed = %*02H:%M:%-5.2S %d%% %@1[!?] %/a+b+c+/i\"\n```\ncreates the following format specification array:\n\n```ruby\n#\u003cFormatEngine::FormatSpec:0x1be2140\n @specs=\n  [Literal(\"Elapsed = \"),\n   Variable(\"%*H\", [\"02\"]),\n   Literal(\":\"),\n   Variable(\"%M\", nil),\n   Literal(\":\"),\n   Variable(\"%S\", [\"-5\", \"2\"]),\n   Literal(\" \"),\n   Variable(\"%d\", nil),\n   Literal(\"%\"),\n   Literal(\" \"),\n   Set(\"%@[!?]\", \"%@[\", /[!?]{1,1}/),\n   Literal(\" \"),\n   Regex(\"%/a+b+c+/i\", \"%/\", /a+b+c+/i)]\u003e\n```\nWhere literals are processed as themselves, except:\n* If that literal ends with a space, that space will parse zero or more spaces.\nWhen formatting, the trailing space is just another space.\n* A %% will be treated as a single % sign.\n* A backslash character will take the character that follows as a literal\ncharacter. Thus \\\\% is equivalent to %%.\n\nVariables are executed by looking up the format string (without the width or\nprecision fields) in the library and executing the corresponding block. The\nformat string can be seen in the above sample as the first string in the\nVariable\n\nSets and Regexes work by looking into the input string with their regular\nexpression. Both are only ever used when parsing, never for formatting.\nThey are executed by first looking up their long name, listed first, and\nthen if the long name is not found, their short name, listed second is tried.\n\n**Note:** If a format specification does not correspond to an entry in the\nlibrary, an exception occurs. For example\n\n    RuntimeError detected: Unsupported tag = \"%!!!d\"\n\n## Formatting / Parsing Blocks\n\nIn the context of a formatting / parsing block, the\n\"self\" of that block is an instance of SpecInfo. The\ncomponents of that object are:\n\n###When Formatting:\nAttributes:\n* src - The object that is the source of the data (RO).\n* dst - A string that receives the formatted output (RO).\n* fmt - The format specification currently being processed (RW).\n* engine - The formatting engine. Mostly for access to the library (RO).\n* tmp - A utility hash so that the formatting process can retain state (RO).\n\nMethods\n* cat - Append the string that follows to the formatted output. This is\n  equivalent to the code dst \u003c\u003c \"string\"\n\n###When Parsing:\nAttributes:\n* src - A string that is the source of formatted input (RO).\n* dst - The class of the object being created (RO).\n* fmt - The parse specification currently being processed (RW).\n* engine - The parsing engine. Mostly for access to the library (RO).\n* tmp - A utility hash so that the parsing process can retain state (RO).\n\nMethods\n* set - Set the return value of the parsing operation to the value that follows.\n* parse - Look for the string or regex parm that follows. Return the data found or nil.\n* parse! - Like parse but raises an exception (with optional msg) if not found.\n* grab - Grab some text based on the width of the format.\n* found? - Did the last parse succeed?\n* found - The text found by the last parse (or parse!) operation.\n\n###Variable Format Specifier Attributes\nThe format specifier, used in both formatting and parsing and accessed as the\nfmt attribute, has itself, the following attributes:\n* has_width? - Was a width specified?\n* width - The width parameter or 0 if not specified.\n* width_str - The actual width text or an empty string.\n* has_prec? - Was a precision specified?\n* prec - The precision parameter or 0 if not specified.\n* prec_str - The actual precision text or an empty string.\n* parm_str - The actual parameter (width and precision) text or an empty string.\n\n###Set and Regex Format Specifier Attributes\n* width - Always 0.\n* regex - The internal regex compiled for this specification\n\n## Contributing\n\n#### Plan A\n\n1. Fork it ( https://github.com/PeterCamilleri/format_engine/fork )\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create a new Pull Request\n\n#### Plan B\n\nGo to the GitHub repository and raise an issue calling attention to some\naspect that could use some TLC or a suggestion or idea.\n\n## License\n\nThe gem is available as open source under the terms of the\n[MIT License](./LICENSE.txt).\n\n## Code of Conduct\n\nEveryone interacting in the fully_freeze project’s codebases, issue trackers,\nchat rooms and mailing lists is expected to follow the\n[code of conduct](./CODE_OF_CONDUCT.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpetercamilleri%2Fformat_engine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpetercamilleri%2Fformat_engine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpetercamilleri%2Fformat_engine/lists"}