{"id":18690728,"url":"https://github.com/rap1ds/ruby-possibly","last_synced_at":"2025-04-06T12:09:03.943Z","repository":{"id":16409495,"uuid":"19160552","full_name":"rap1ds/ruby-possibly","owner":"rap1ds","description":"A maybe monad","archived":false,"fork":false,"pushed_at":"2016-07-24T20:03:45.000Z","size":67,"stargazers_count":151,"open_issues_count":6,"forks_count":11,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-30T11:06:58.542Z","etag":null,"topics":["maybe","option","pattern"],"latest_commit_sha":null,"homepage":null,"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/rap1ds.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-04-25T20:57:26.000Z","updated_at":"2025-01-02T05:03:02.000Z","dependencies_parsed_at":"2022-09-05T16:50:34.699Z","dependency_job_id":null,"html_url":"https://github.com/rap1ds/ruby-possibly","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rap1ds%2Fruby-possibly","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rap1ds%2Fruby-possibly/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rap1ds%2Fruby-possibly/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rap1ds%2Fruby-possibly/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rap1ds","download_url":"https://codeload.github.com/rap1ds/ruby-possibly/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247478323,"owners_count":20945266,"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":["maybe","option","pattern"],"created_at":"2024-11-07T10:50:31.346Z","updated_at":"2025-04-06T12:09:03.917Z","avatar_url":"https://github.com/rap1ds.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Possibly - Maybe monad for Ruby\n\n[![Travis CI](https://travis-ci.org/rap1ds/ruby-possibly.svg?branch=master)](https://travis-ci.org/rap1ds/ruby-possibly)\n[![Code Climate](https://codeclimate.com/github/rap1ds/ruby-possibly/badges/gpa.svg)](https://codeclimate.com/github/rap1ds/ruby-possibly)\n\nMaybe monad implementation for Ruby\n\n```ruby\nputs Maybe(User.find_by_id(\"123\")).username.downcase.or_else { \"N/A\" }\n\n=\u003e # puts downcased username if user \"123\" can be found, otherwise puts \"N/A\"\n```\n\n## Installation\n\n```ruby\ngem install possibly\n```\n\n## Getting started\n\n```\nrequire 'possibly'\n\nfirst_name = Maybe(deep_hash)[:account][:profile][:first_name].or_else { \"No first name available\" }\n```\n\n## Documentation\n\nMaybe monad is a programming pattern that allows to treat nil values that same way as non-nil values. This is done by wrapping the value, which may or may not be `nil` to, a wrapper class.\n\nThe implementation includes three different classes: `Maybe`, `Some` and `None`. `Some` represents a value, `None` represents a non-value and `Maybe` is a constructor, which results either `Some`, or `None`.\n\n```ruby\nMaybe(\"I'm a value\")    =\u003e #\u003cSome:0x007ff7a85621e0 @value=\"I'm a value\"\u003e\nMaybe(nil)              =\u003e #\u003cNone:0x007ff7a852bd20\u003e\n```\n\nBoth `Some` and `None` implement four trivial methods: `is_some?`, `is_none?`, `get` and `or_else`\n\n```ruby\nMaybe(\"I'm a value\").is_some?               =\u003e true\nMaybe(\"I'm a value\").is_none?               =\u003e false\nMaybe(nil).is_some?                         =\u003e false\nMaybe(nil).is_none?                         =\u003e true\nMaybe(\"I'm a value\").get                    =\u003e \"I'm a value\"\nMaybe(\"I'm a value\").or_else { \"No value\" } =\u003e \"I'm a value\"\nMaybe(nil).get                              =\u003e None::ValueExpectedException: `get` called to None. A value was expected.\nMaybe(nil).or_else { \"No value\" }           =\u003e \"No value\"\nMaybe(\"I'm a value\").or_raise               =\u003e \"I'm a value\"\nMaybe(nil).or_raise                         =\u003e None::ValueExpectedException: `or_raise` called to None. A value was expected.\nMaybe(nil).or_raise(ArgumentError)          =\u003e ArgumentError\nMaybe(\"I'm a value\").or_nil                 =\u003e \"I'm a value\"\nMaybe([]).or_nil                            =\u003e nil\n```\n\nIn addition, `Some` and `None` implement `Enumerable`, so all methods available for `Enumerable` are available for `Some` and `None`:\n\n```ruby\nMaybe(\"Print me!\").each { |v| puts v }      =\u003e it puts \"Print me!\"\nMaybe(nil).each { |v| puts v }              =\u003e puts nothing\nMaybe(4).map { |v| Math.sqrt(v) }           =\u003e #\u003cSome:0x007ff7ac8697b8 @value=2.0\u003e\nMaybe(nil).map { |v| Math.sqrt(v) }         =\u003e #\u003cNone:0x007ff7ac809b10\u003e\nMaybe(2).inject(3) { |a, b| a + b }         =\u003e 5\nNone().inject(3) { |a, b| a + b }           =\u003e 3\n```\n\nAll the other methods you call on `Some` are forwarded to the `value`.\n\n```ruby\nMaybe(\"I'm a value\").upcase                 =\u003e #\u003cSome:0x007ffe198e6128 @value=\"I'M A VALUE\"\u003e\nMaybe(nil).upcase                           =\u003e None\n```\n\n### Case expression\n\nMaybe implements threequals method `#===`, so it can be used in case expressions:\n\n```ruby\nvalue = Maybe([nil, 1, 2, 3, 4, 5, 6].sample)\n\ncase value\nwhen Some\n  puts \"Got Some: #{value.get}\"\nwhen None\n  puts \"Got None\"\nend\n```\n\nIf the type of Maybe is Some, you can also match the value:\n\n```ruby\nvalue = Maybe([nil, 0, 1, 2, 3, 4, 5, 6].sample)\n\ncase value\nwhen Some(0)\n  puts \"Got zero\"\nwhen Some((1..3))\n  puts \"Got a low number: #{value.get}\"\nwhen Some((4..6))\n  puts \"Got a high number: #{value.get}\"\nwhen None\n  puts \"Got nothing\"\nend\n```\n\nFor more complicated matching you can use Procs and lambdas. Proc class aliases #=== to the #call method. In practice this means that you can use Procs and lambdas in case expressions. It works also nicely with Maybe:\n\n```ruby\neven = -\u003e(a) { a % 2 == 0 }\nodd = -\u003e(a) { a % 2 != 0 }\n\nvalue = Maybe([nil, 1, 2, 3, 4, 5, 6].sample)\n\ncase value\nwhen Some(even)\n  puts \"Got even value: #{value.get}\"\nwhen Some(odd)\n  puts \"Got odd value: #{value.get}\"\nwhen None\n  puts \"Got None\"\nend\n```\n\n## Examples\n\nInstead of using if-clauses to define whether a value is a `nil`, you can wrap the value with `Maybe()` and threat it the same way whether or not it is a `nil`\n\nWithout Maybe():\n\n```ruby\nuser = User.find_by_id(user_id)\nnumber_of_friends = if user \u0026\u0026 user.friends\n  user.friends.count\nelse\n  0\nend\n```\n\nWith Maybe():\n\n```ruby\nnumber_of_friends = Maybe(User.find_by_id(user_id)).friends.count.or_else { 0 }\n```\n\nSame in HAML view, without Maybe():\n\n```haml\n- if @user \u0026\u0026 @user.friends\n  = @user.friends.count\n- else\n  0\n```\n\n```haml\n= Maybe(@user).friends.count.or_else { 0 }\n```\n\n## Tests\n\n`rspec spec/spec.rb`\n\n## License\n\n[MIT](LICENSE)\n\n## Author\n\n[Mikko Koski](https://github.com/rap1ds) / [@rap1ds](http://twitter.com/rap1ds)\n\n## Sponsored by\n\n[Sharetribe](https://github.com/sharetribe) / [@sharetribe](http://twitter.com/sharetribe) / [www.sharetribe.com](https://www.sharetribe.com)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frap1ds%2Fruby-possibly","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frap1ds%2Fruby-possibly","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frap1ds%2Fruby-possibly/lists"}