{"id":18553971,"url":"https://github.com/bodrovis-learning/messages_dictionary","last_synced_at":"2025-04-09T23:30:53.082Z","repository":{"id":56883366,"uuid":"53687907","full_name":"bodrovis-learning/messages_dictionary","owner":"bodrovis-learning","description":"Store your messages anywhere and fetch them anytime.","archived":false,"fork":false,"pushed_at":"2024-06-13T17:37:51.000Z","size":79,"stargazers_count":7,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-24T13:21:23.738Z","etag":null,"topics":["gem","messages","ruby","ruby-gem"],"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/bodrovis-learning.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2016-03-11T18:24:27.000Z","updated_at":"2024-06-13T17:37:54.000Z","dependencies_parsed_at":"2023-11-19T14:24:32.861Z","dependency_job_id":"a195386f-0536-468c-8264-dcbe946402db","html_url":"https://github.com/bodrovis-learning/messages_dictionary","commit_stats":{"total_commits":59,"total_committers":4,"mean_commits":14.75,"dds":0.4067796610169492,"last_synced_commit":"4fcfa093e214350abf3e629dda849d2ff5486b0e"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bodrovis-learning%2Fmessages_dictionary","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bodrovis-learning%2Fmessages_dictionary/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bodrovis-learning%2Fmessages_dictionary/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bodrovis-learning%2Fmessages_dictionary/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bodrovis-learning","download_url":"https://codeload.github.com/bodrovis-learning/messages_dictionary/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248129554,"owners_count":21052593,"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":["gem","messages","ruby","ruby-gem"],"created_at":"2024-11-06T21:19:07.020Z","updated_at":"2025-04-09T23:30:52.736Z","avatar_url":"https://github.com/bodrovis-learning.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MessagesDictionary\n\n[![Gem Version](https://badge.fury.io/rb/messages_dictionary.svg)](https://badge.fury.io/rb/messages_dictionary)\n![CI](https://github.com/bodrovis-learning/messages_dictionary/actions/workflows/ci.yml/badge.svg)\n[![Coverage Status](https://coveralls.io/repos/github/bodrovis-learning/messages_dictionary/badge.svg?branch=master)](https://coveralls.io/github/bodrovis-learning/messages_dictionary?branch=master)\n![Downloads total](https://img.shields.io/gem/dt/messages_dictionary)\n\nThis gem started as an educational project for my student. The idea behind this gem is to organize\nvarious messages in a simple key-value format that can be fetched later. Messages support interpolation,\ncan be stored inside files or passed as hashes (nested hashes are supported as well). Custom fetching rules\ncan be specified as well.\n\n[Here is my article](https://www.sitepoint.com/learn-ruby-metaprogramming-for-great-good/) describing how this gem was actually written.\n\nThis gem requires Ruby 2.7+. Install it by running:\n\n```\ngem install messages_dictionary\n```\n\nRefer to the next sections to see it in action.\n\n## Use Cases\n\nWanna see it in action? Some use-cases can be found, in the [Guesser](https://github.com/bodrovis/Guesser) game:\n\n* [Messages are stored inside files](https://github.com/bodrovis/Guesser/tree/master/lib/guesser/messages)\n* [Displaying errors](https://github.com/bodrovis/Guesser/blob/master/lib/guesser.rb#L25)\n* [Displaying informational messages](https://github.com/bodrovis/Guesser/blob/master/lib/guesser/game.rb#L29)\n\nAnother, a bit more complex, use case in the [lessons_indexer gem](https://github.com/bodrovis/lessons_indexer):\n\n* [Messages are stored in a single file](https://github.com/bodrovis/lessons_indexer/blob/master/lib/lessons_indexer/messages/messages.yml)\n* [Messenger class equipped with messages_dictionary magic is defined](https://github.com/bodrovis/lessons_indexer/blob/master/lib/lessons_indexer.rb#L7)\n* [Other classes simply inherit from it](https://github.com/bodrovis/lessons_indexer/blob/master/lib/lessons_indexer/indexer.rb#L2)\n* [Messages are fetched easily](https://github.com/bodrovis/lessons_indexer/blob/master/lib/lessons_indexer/indexer.rb#L45)\n\n## Basic usage\n\nSuppose you have the following program:\n\n```ruby\nclass MyClass\n  def calculate(a)\n    result = a ** 2\n    puts \"The result is #{result}\"\n  end\nend\n\nclass MyOtherClass\n  def some_action(a, b)\n    puts \"The first value is #{a}, the second is #{b}\"\n  end\n\n  def greet\n    puts \"Welcome!\"\n  end\nend\n```\n\nThese messages are scattered all over the program and can be hard to maintain. With `messages_dictionary` you can transform it into\n\n```ruby\nrequire 'messages_dictionary' # For brevity this line will be omitted in other examples\n\nclass MyClass\n  include MessagesDictionary\n  has_messages_dictionary\n\n  def calculate(a)\n    result = a ** 2\n    pretty_output(:show_result, result: result)\n  end\nend\n\nclass MyOtherClass\n  include MessagesDictionary\n  has_messages_dictionary\n\n  def some_action(a, b)\n    pretty_output(:show_values, first: a, second: b)\n  end\n\n  def greet\n    pretty_output(:welcome)\n    # Or simply\n    pou :welcome\n  end\nend\n```\n\nThe only thing you have to do is create two *.yml* files named after your classes:\n\n*my_class.yml*\n\n```yaml\nshow_result: \"The result is {{result}}\"\n```\n\n*my_other_class.yml*\n\n```yaml\nshow_values: \"The first value is {{a}}, the second is {{b}}\"\nwelcome: \"Welcome!\"\n```\n\n**Please note**, that if your class is named `MyModule::MyClass`, then by default the program will search\nfor a file named `my_class.yml` inside `my_module` directory. This can be further customized, refer\nthe \"Further Customization\" section for more info.\n\nSo by saying `pretty_output(:show_result, result: result)` you are fetching a message under the key\n`show_result` and replace the `{{result}}` part with the value of the `result` variable. Simple, eh?\n\n### Nesting\n\nMessagesDictionary supports nesting (similar to localization files in Rails):\n\n*my_class.yml*\n\n```yaml\nshow_result: \"The result is {{result}}\"\nnested:\n  value: 'Nested value'\n```\n\nNested messages can be easily accessed with dot notation:\n\n```ruby\nclass MyClass\n  include MessagesDictionary\n  has_messages_dictionary\n\n  def do_something\n    pou('nested.value') # =\u003e 'Nested value'\n  end\nend\n```\n\n### Indifferent Access\n\nKeys can be passed to the `pou` method as symbols or strings - it does not really matter:\n\n```ruby\nclass MyClass\n  include MessagesDictionary\n  has_messages_dictionary\n\n  def calculate(a)\n    result = a ** 2\n    pou(:show_result, result: result)\n    # OR\n    pou('show_result', result: result)\n  end\nend\n```\n\n## Further Customization\n\n### Specifying File Name and Directory\n\nBy default `messages_dictionary` will search for a *.yml* file named after your class (converted to snake case,\nso for the `MyClass` the file should be named *my_class.yml*)\ninside the same directory. However, this behavior can be easily changed with the following options:\n\n* `:file` (`string`) - specifies the file name to load messages from (extension has to be provided).\n* `:dir` (`string`) - specifies the directory to load file from.\n\n```ruby\nclass MyClass\n  include MessagesDictionary\n  has_messages_dictionary file: 'some_file.yml', dir: 'my_docs'\nend\n```\n\nBoth of these options are not mandatory.\n\n### Providing a custom file loader\n\nBy default the gem a messages file in YAML format. However, you might want to use a different format: for example, JSON. In this case you'll have to provide a custom loader:\n\n```ruby\nclass MyClass\n  include MessagesDictionary\n  has_messages_dictionary file: 'test_file.json', dir: 'my_dir',\n                          file_loader: -\u003e(file_path) { JSON.parse(File.read(file_path)) }\nend\n```\n\nThe `:file_loader` option accepts a proc or a lambda that receives a path to your messages file as an argument. This lambda must return a hash object with keys and the corresponding values.\n\nThe default value for the `:file_loader` is `-\u003e(f) { YAML.load_file(f) }`.\n\n### Specifying Messages Hash\n\nInstead of loading messages from a file, you can pass hash to the `has_messages_dictionary` using `:messages` option:\n\n```ruby\nclass MyClass\n  include MessagesDictionary\n  has_messages_dictionary messages: {key: 'value'}\nend\n```\n\nNesting and all other features are supported as well.\n\n### Specifying Output and Display Method\n\nBy default all messages will be outputted to `STDOUT` using `puts` method, however this can be changed:\n\n* `:output` (`object`) - specify your own output. The object you provide has to implement `puts` method\nor any other method you provide for the `:method` option.\n* `:method` (`symbol` or `string`) - specify method to use (like `warn` or `abort`, for example).\n\n```ruby\nclass MyClass\n  include MessagesDictionary\n  has_messages_dictionary output: STDOUT, method: :warn\nend\n```\n\n### \"Lazy\" mode\n\nBy default this gem will load all messages from the given file. However, you can enable a \"lazy\" mode so that messages are not loaded until `pou` or `pretty_output` methods have been called. The \"lazy\" mode can only be enabled when the `:file` option is provided (in other words, `:lazy` has no effect with the `:messages` setting):\n\n```ruby\nclass MyClass\n  include MessagesDictionary\n  has_messages_dictionary lazy: true, file: 'my_file.yml'\n\n  def greet\n    pou :hi\n  end\nend\n\n# At this point no messages are loaded from the given file\n\nobj = MyClass.new\n\n# ... doing some other stuff ...\n\n# Messages are still not loaded at this point!\n\nobj.greet # Now all messages will be loaded from the YAML file\n```\n\n### Providing Custom Transformation Logic\n\nSuppose you want to transform your message somehow or even simply return it instead of printing on the screen.\n`pretty_output` method accepts an optional block for this purpose:\n\n```ruby\nclass MyClass\n  include MessagesDictionary\n  has_messages_dictionary\n\n  def greet\n    pou(:welcome) do |msg|\n      msg.upcase\n    end\n\n    # Or simply:\n\n    pou(:welcome, \u0026:upcase)\n  end\nend\n\nmy_object = MyClass.new\nmy_object.greet # Will return \"WELCOME\", nothing will be put on the screen\n```\n\nYou can also specify transformation logic globally by assigning a procedure or lambda to the `:transform`\noption:\n\n```ruby\nclass MyClass\n  include MessagesDictionary\n  has_messages_dictionary transform: -\u003e(msg) {msg.upcase}\n\n  def greet\n    pou(:welcome)\n  end\nend\n\nmy_object = MyClass.new\nmy_object.greet # Will return \"WELCOME\", nothing will be put on the screen\n```\n\nTransformation provided per method takes higher precedence than the one provided per class.\n\n**Please note** that by default MessagesDictionary **does not output anything** when you provide transformation\nblock. This is done to allow more control, because sometimes you may want to fetch a message, but not output\nit anywhere (for example, when raising a custom error - see use case [here](https://github.com/bodrovis/Guesser/blob/master/lib/guesser.rb#L25)).\n\nIf you do want to output your message after transformation, you have to do it explicitly:\n\n```ruby\ndef greet\n  pou(:welcome) do |msg|\n    puts msg.upcase # =\u003e Prints \"WELCOME\"\n  end\nend\n```\n\n### Handling missing keys\n\nBy default when a non-existent key is requested, an error will be raised:\n\n```ruby\nclass MyClass\n  include MessagesDictionary\n  has_messages_dictionary messages: {key: 'value'}\n\n  def greet\n    pou :unknown_key # trying to use some unknown key...\n  end\nend\n\nobj = MyClass.new\n\nobj.greet # KeyError is raised here!\n```\n\nHowever, you can adjust the `:on_key_missing` option and provide a custom proc or lambda to handle all missing keys:\n\n```ruby\nclass MyClass\n  include MessagesDictionary\n  has_messages_dictionary messages: {key: 'value'},\n                          on_key_missing: -\u003e(key) { key } # We simply return the requested key itself\n\n  def greet\n    pou :unknown_key\n  end\nend\n\nobj = MyClass.new\n\nobj.greet # Prints \"unknown_key\" to the screen, no errors will be raised\n```\n\nSo, in the example above we simply return the key itself if it was not found in the messages hash.\n\n## License\n\nLicensed under the [MIT License](https://github.com/bodrovis-learning/messages_dictionary/blob/master/LICENSE).\n\nCopyright (c) 2022 [Ilya Krukowski](http://bodrovis.tech)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbodrovis-learning%2Fmessages_dictionary","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbodrovis-learning%2Fmessages_dictionary","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbodrovis-learning%2Fmessages_dictionary/lists"}