{"id":22725354,"url":"https://github.com/delonnewman/invokable","last_synced_at":"2025-04-13T20:27:17.680Z","repository":{"id":56877895,"uuid":"238530918","full_name":"delonnewman/invokable","owner":"delonnewman","description":"Objects are functions! Treat any Object or Class as a Proc (like Enumerable but for Procs).","archived":false,"fork":false,"pushed_at":"2024-03-01T17:15:45.000Z","size":121,"stargazers_count":43,"open_issues_count":6,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-11-09T04:19:23.084Z","etag":null,"topics":["array","composition","curry","currying","functional-programming","functions","hash","memoization","memoize","objects","ruby","ruby-gem","ruby-lambda","ruby-proc","set"],"latest_commit_sha":null,"homepage":"https://delonnewman.github.io/invokable","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/delonnewman.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2020-02-05T19:27:42.000Z","updated_at":"2024-01-03T01:07:56.000Z","dependencies_parsed_at":"2024-02-28T22:49:09.309Z","dependency_job_id":null,"html_url":"https://github.com/delonnewman/invokable","commit_stats":{"total_commits":92,"total_committers":2,"mean_commits":46.0,"dds":"0.021739130434782594","last_synced_commit":"d93ae46c68b8d7235094583e07c376ae65e004d7"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/delonnewman%2Finvokable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/delonnewman%2Finvokable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/delonnewman%2Finvokable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/delonnewman%2Finvokable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/delonnewman","download_url":"https://codeload.github.com/delonnewman/invokable/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228964237,"owners_count":17998861,"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":["array","composition","curry","currying","functional-programming","functions","hash","memoization","memoize","objects","ruby","ruby-gem","ruby-lambda","ruby-proc","set"],"created_at":"2024-12-10T16:09:25.400Z","updated_at":"2024-12-10T16:09:25.896Z","avatar_url":"https://github.com/delonnewman.png","language":"Ruby","readme":"![Ruby](https://github.com/delonnewman/invokable/workflows/Ruby/badge.svg)\n[![Gem Version](https://badge.fury.io/rb/invokable.svg)](https://badge.fury.io/rb/invokable)\n\n# Invokable\n\nObjects are functions! Treat any Object or Class as a Proc (like Enumerable but for Procs)\n\n## Synopsis\n\nObjects that enclose state, can be treated as automatically curried functions.\n\n```ruby\nrequire 'invokable'\n\nclass TwitterPoster\n  include Invokable\n\n  def initialize(model)\n    @model = model\n  end\n\n  def call(user)\n    # do the dirt\n    ...\n    TwitterStatus.new(user, data)\n  end\nend\n\nTwitterPoster.call(Model.find(1)) # =\u003e #\u003cTwitterPoster ...\u003e\nTwitterPoster.call(Model.find(1), current_user) # =\u003e #\u003cTwitterStatus ...\u003e\n\n# both the class and it's instances can be used anywhere Procs are.\n\nModel.where(created_at: Date.today).map(\u0026TwitterPoster) # =\u003e [#\u003cTwitterPoster ...\u003e, ...]\n```\n\nUse `memoize`, `\u003c\u003c` and `\u003e\u003e` for composition and other methods on Proc on your command objects\n\n```ruby\n# app/queries/filter_records.rb\nclass FilterRecords\n  include Invokable\n\n  def initialize(params)\n    @params = params\n  end\n\n  def call(records)\n    # do filtering return records\n  end\nend\n\n# app/queries/sort_records.rb\nclass SortRecords\n  include Invokable\n\n  def initialize(params)\n    @params = params\n  end\n\n  def call(records)\n    # do sorting return records\n  end\nend\n\nsort_and_filter = SortRecords.call(params) \u003c\u003c FilterRecords.call(params)\nsort_and_filter.call(records) # =\u003e sorted and filtered records\n```\n\nHelper methods that can be used with any object that responds to `call` or `to_proc`\n\n```ruby\nInvokable.juxtapose(:sum, -\u003e (xs) { xs.reduce(:*) }, :min, :max).([3, 4, 6]) # =\u003e [13, 72, 3, 6]\n\nInvokable.knit(:upcase, :downcase).(['FoO', 'BaR']) # =\u003e [\"FOO\", \"bar\"]\n```\n\nThey are also mixed into any class that includes the module\n\n```ruby\nclass Transformer\n  include Invokable\n\n  def call(array)\n    array.map(\u0026juxtapose(identity, compose(:to_s, :upcase))).to_h\n  end\nend\n\nTransformer.call([:a, :b, :c, :d]) # =\u003e {:a =\u003e \"A\", :b =\u003e \"B\", :c =\u003e \"C\", :d =\u003e \"D\"} \n```\n\nHashes can be treated as functions of their keys\n\n```ruby\nrequire 'invokable'\nrequire 'invokable/hash'\n\nnumber_names = { 1 =\u003e \"One\", 2 =\u003e \"Two\", 3 =\u003e \"Three\" }\n[1, 2, 3, 4].map(\u0026number_names) # =\u003e [\"One\", \"Two\", \"Three\", nil]\n```\n\nArrays can be treated as functions of their indexes\n\n```ruby\nrequire 'invokable'\nrequire 'invokable/array'\n\nalpha = ('a'..'z').to_a\n[1, 2, 3, 4].map(\u0026alpha) # =\u003e [\"b\", \"c\", \"d\", \"e\"]\n```\n\nSets can be treated as predicates\n\n```ruby\nrequire 'invokable'\nrequire 'invokable/set'\n\nfavorite_numbers = Set[3, Math::PI]\n[1, 2, 3, 4].select(\u0026favorite_numbers) # =\u003e [3]\n```\n\nUse as much or a little as you need\n\n```ruby\nrequire 'invokable'         # loads Invokable module\nrequire 'invokable/helpers' # loads Invokable::Helpers module\nrequire 'invokable/hash'    # loads hash patch\nrequire 'invokable/array'   # loads array patch\nrequire 'invokable/set'     # loads set patch\nrequire 'invokable/data'    # loads hash, set and array patches\n```\n\n## Why?\n\nA function is a mapping of one value to another with the additional constraint that for the one input value you will\nalways get the same output value. So, conceptually, Ruby Hashes, Arrays, and Sets are all functions. Also, there are\nmany one method objects out there (e.g. Service Objects) that are essentially functions. Why not treat them as such?\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'invokable'\n```\n\nAnd then execute:\n\n    \u003e bundle\n\nOr install it yourself as:\n\n    \u003e gem install invokable\n\n## API Documentation\n\n[https://www.rubydoc.info/gems/invokable](https://www.rubydoc.info/gems/invokable)\n\n## See Also\n\n  - [Closures and Objects are Equivalent](http://wiki.c2.com/?ClosuresAndObjectsAreEquivalent)\n  - [Clojure](https://clojure.org)\n  - [Arc](http://www.arclanguage.org)\n\n## Compatibility\n\nTested using MRI 2.4, 2.5, 2.6, 2.7, 3.0 and JRuby\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdelonnewman%2Finvokable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdelonnewman%2Finvokable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdelonnewman%2Finvokable/lists"}