{"id":13400351,"url":"https://github.com/davejacobs/letters","last_synced_at":"2025-03-14T06:31:39.078Z","repository":{"id":4865505,"uuid":"6020356","full_name":"davejacobs/letters","owner":"davejacobs","description":"A tiny debugging library for Ruby","archived":false,"fork":false,"pushed_at":"2019-02-01T18:31:57.000Z","size":71,"stargazers_count":271,"open_issues_count":5,"forks_count":9,"subscribers_count":18,"default_branch":"master","last_synced_at":"2024-07-31T19:24:15.756Z","etag":null,"topics":["debugger","pipeline","rails","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"ltk/neo4j-harvest-importer","license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davejacobs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-09-30T17:06:34.000Z","updated_at":"2024-03-26T02:48:46.000Z","dependencies_parsed_at":"2022-09-15T16:10:40.767Z","dependency_job_id":null,"html_url":"https://github.com/davejacobs/letters","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davejacobs%2Fletters","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davejacobs%2Fletters/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davejacobs%2Fletters/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davejacobs%2Fletters/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davejacobs","download_url":"https://codeload.github.com/davejacobs/letters/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221440182,"owners_count":16821599,"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":["debugger","pipeline","rails","ruby"],"created_at":"2024-07-30T19:00:51.017Z","updated_at":"2024-10-25T16:30:22.161Z","avatar_url":"https://github.com/davejacobs.png","language":"Ruby","readme":"**Letters** is a little alphabetical library that makes sophisticated debugging easy \u0026amp; fun.\n\nFor many of us, troubleshooting begins and ends with the `puts` statement. Others recruit the debugger, too. (Maybe you use `puts` statements to look at changes over time but the debugger to focus on a small bit of code.) These tools are good, but they are the lowest level of how we can debug in Ruby. Letters leverages `puts`, the debugger, control transfer, computer beeps, and other side-effects for more well-rounded visibility into code and state.\n\n## Installation ##\n\nIf you're using RubyGems, install Letters with:\n\n    gem install letters\n\nBy default, requiring `\"letters\"` monkey-patches `Object`. It goes without saying that if you're using Letters in an app that has environments, you probably only want to use it in development.\n\n## Debugging with letters ##\n\nWith Letters installed, you have a suite of methods available wherever you want them in your code -- at the end of any expression, in the middle of any pipeline. Most of these methods will output some form of information, though there are more sophisticated ones that pass around control of the application.\n\nThere are almost 20 Letters methods so far. You can find them [in the documentation](http://lettersrb.com/api).\n\nLet's use with the `o` method as an example. It is one of the most familiar methods. Calling it prints the receiver to STDOUT and returns the receiver:\n\n```ruby\n{ foo: \"bar\" }.o\n# =\u003e { foo: \"bar\" }\n# prints { foo: \"bar\" }\n```\n\nThat's simple enough, but not really useful. Things get interesting when you're in a pipeline:\n\n```ruby\nwords.grep(/interesting/).\n  map(\u0026:downcase).\n  group_by(\u0026:length).\n  values_at(5, 10).\n  slice(0..2).\n  join(\", \")\n```\n\nIf I want to know the state of your code after lines 3 and 5, all I have to do is add `.o` to each one:\n\n```ruby\nwords.grep(/interesting/).\n  map(\u0026:downcase).\n  group_by(\u0026:length).o.\n  values_at(5, 10).\n  slice(0..2).o.\n  join(\", \")\n```\n\nBecause the `o` method (and nearly every Letters method) returns the original object, introducing it is only ever for side effects -- it won't change the output of your code.\n\nThis is significantly easier than breaking apart the pipeline using variable assignment or a hefty `tap` block.\n\nThe `o` method takes options, too, so you can add a prefix message to the output or choose another output format -- like [YAML]() or [pretty print]().\n\n## The methods ##\n\nHere are the methods, listed with any options that can be passed in to modify their behavior.  Some options are available to all methods and are not listed in the table below:\n\n- `:message (string)`: Print out the specified message as the method is being called.\n- `:line_no (boolean)`: Print out the line number where a method is called as it is being called\n- `:disable (boolean)`: Disable this method's side effects\n\nYou can easily set these for an entire project using global configuration if you wish (see below).\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003eLetter\u003c/th\u003e\n    \u003cth\u003eCommand\u003c/th\u003e\n    \u003cth\u003eOptions\u003c/th\u003e\n    \u003cth\u003eDescription\u003c/th\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ca href=\"http://lettersrb.com/api#a\"\u003ea\u003c/a\u003e\u003c/td\u003e\n    \u003ctd\u003eAssert\u003c/td\u003e\n    \u003ctd\u003e\u003ccode\u003e:error_class\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003easserts in the context of its receiver or \u003ccode\u003eLetters::AssertionError\u003c/code\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ca href=\"http://lettersrb.com/api#b\"\u003eb\u003c/a\u003e\u003c/td\u003e\n    \u003ctd\u003eBeep\u003c/td\u003e\n    \u003ctd\u003e\u003c/td\u003e\n    \u003ctd\u003ecauses your terminal to beep\u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ca href=\"http://lettersrb.com/api#c\"\u003ec\u003c/a\u003e\u003c/td\u003e\n    \u003ctd\u003eCallstack\u003c/td\u003e\n    \u003ctd\u003e\u003c/td\u003e\n    \u003ctd\u003eprints the current callstack\u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ca href=\"http://lettersrb.com/api#d\"\u003ed\u003c/a\u003e\u003c/td\u003e\n    \u003ctd\u003eDebugger\u003c/td\u003e\n    \u003ctd\u003e\u003c/td\u003e\n    \u003ctd\u003epasses control to the debugger\u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ca href=\"http://lettersrb.com/api#d1/d2\"\u003ed1/d2\u003c/a\u003e\u003c/td\u003e\n    \u003ctd\u003eDiff\u003c/td\u003e\n    \u003ctd\u003e\u003ccode\u003e:format\u003c/code\u003e,\u003ccode\u003e:stream\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003eprints a diff between first and second receivers\u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ca href=\"http://lettersrb.com/api#e\"\u003ee\u003c/a\u003e\u003c/td\u003e\n    \u003ctd\u003eEmpty\u003c/td\u003e\n    \u003ctd\u003e\u003c/td\u003e\n    \u003ctd\u003eraises a \u003ccode\u003eLetters::EmptyError\u003c/code\u003e if its receiver is empty\u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ca href=\"http://lettersrb.com/api#f\"\u003ef\u003c/a\u003e\u003c/td\u003e\n    \u003ctd\u003eFile\u003c/td\u003e\n    \u003ctd\u003e\u003ccode\u003e:format\u003c/code\u003e, \u003ccode\u003e:name\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003ewrites its receiver into a file in a given format\u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ca href=\"http://lettersrb.com/api#j\"\u003ej\u003c/a\u003e\u003c/td\u003e\n    \u003ctd\u003eJump\u003c/td\u003e\n    \u003ctd\u003e(\u003ccode\u003e\u0026block\u003c/code\u003e)\u003c/td\u003e\n    \u003ctd\u003eexecutes its block in the context of its receiver\u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ca href=\"http://lettersrb.com/api#k\"\u003ek\u003c/a\u003e\u003c/td\u003e\n    \u003ctd\u003eKill\u003c/td\u003e\n    \u003ctd\u003e\u003ccode\u003e:on\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003eraises \u003ccode\u003eLetters::KillError\u003c/code\u003e at a specified number of calls\u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ca href=\"http://lettersrb.com/api#l\"\u003el\u003c/a\u003e\u003c/td\u003e\n    \u003ctd\u003eLogger\u003c/td\u003e\n    \u003ctd\u003e\u003ccode\u003e:format\u003c/code\u003e, \u003ccode\u003e:level\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003elogs its receivers on the available logger instance\u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ca href=\"http://lettersrb.com/api#m\"\u003em\u003c/a\u003e\u003c/td\u003e\n    \u003ctd\u003eMark as tainted\u003c/td\u003e\n    \u003ctd\u003e(\u003ccode\u003etrue\u003c/code\u003e|\u003ccode\u003efalse\u003c/code\u003e)\u003c/td\u003e\n    \u003ctd\u003etaints (or untaints) its receiver\u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ca href=\"http://lettersrb.com/api#n\"\u003en\u003c/a\u003e\u003c/td\u003e\n    \u003ctd\u003eNil\u003c/td\u003e\n    \u003ctd\u003e\u003c/td\u003e\n    \u003ctd\u003eraises a \u003ccode\u003eLetters::NilError\u003c/code\u003e if its receiver is nil\u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ca href=\"http://lettersrb.com/api#o\"\u003eo\u003c/a\u003e\u003c/td\u003e\n    \u003ctd\u003eOutput\u003c/td\u003e\n    \u003ctd\u003e\u003ccode\u003e:format\u003c/code\u003e, \u003ccode\u003e:stream\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003eprints its receiver to standard output\u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ca href=\"http://lettersrb.com/api#r\"\u003er\u003c/a\u003e\u003c/td\u003e\n    \u003ctd\u003eRi\u003c/td\u003e\n    \u003ctd\u003e(method name as symbol)\u003c/td\u003e\n    \u003ctd\u003eprints RI documentation of its receiver class\u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ca href=\"http://lettersrb.com/api#s\"\u003es\u003c/a\u003e\u003c/td\u003e\n    \u003ctd\u003eSafety\u003c/td\u003e\n    \u003ctd\u003e(level number)\u003c/td\u003e\n    \u003ctd\u003ebumps the safety level (by one or as specified)\u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ca href=\"http://lettersrb.com/api#t\"\u003et\u003c/a\u003e\u003c/td\u003e\n    \u003ctd\u003eTimestamp\u003c/td\u003e\n    \u003ctd\u003e\u003ccode\u003e:time_format\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003eprints out the current timestamp\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\nSee the [full documentation](http://lettersrb.com/api) for examples and more detailed explanations.\n\n## Configuration ##\n\nFor maximum productivity, you can tune and tweak each Letters method to fit your own tastes. Want to name put files somewhere else? No problem. Don't like YAML? Default `f` to use Pretty Print instead! The world of defaults is your oyster.\n\n```ruby\nLetters.config do\n  f :format =\u003e \"pp\", :name =\u003e \"my-special-file\"\nend\n```\n\nYou can also change options globally, for methods where the global option is appropriate. For example, if you want every Letters method to print out its line number when called, you can do this for all methods at once:\n\n```ruby\nLetters.config do\n  all :line_no =\u003e true\nend\n```\n\nTo disable all Letters, for example if you're worried about them getting into a production environment:\n\n```ruby\nLetters.config do\n  all :disable =\u003e true\nend\n```\n","funding_links":[],"categories":["Ruby"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavejacobs%2Fletters","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavejacobs%2Fletters","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavejacobs%2Fletters/lists"}